diff --git a/doc/en/scripting/builtins/libplayer.md b/doc/en/scripting/builtins/libplayer.md
index 99482c68..f71b8c95 100644
--- a/doc/en/scripting/builtins/libplayer.md
+++ b/doc/en/scripting/builtins/libplayer.md
@@ -77,6 +77,13 @@ player.get_spawnpoint(playerid: int) -> number, number, number
Spawn point setter and getter
+```lua
+player.set_name(playerid: int, name: str)
+player.get_name(playerid: int) -> str
+```
+
+Player name setter and getter
+
```lua
player.get_selected_block(playerid: int) -> x,y,z
```
diff --git a/doc/en/scripting/builtins/libworld.md b/doc/en/scripting/builtins/libworld.md
index 349e9106..5bf01abb 100644
--- a/doc/en/scripting/builtins/libworld.md
+++ b/doc/en/scripting/builtins/libworld.md
@@ -10,6 +10,8 @@ world.get_list() -> tables array {
name: str,
-- world icon/preview (loading automatically)
icon: str
+ -- engine version the world was saved on
+ version: {int, int}
}
-- Returns current day time in range \[0.0-1.0\]
diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md
index 8dcd1a06..4b13ec7b 100644
--- a/doc/en/scripting/events.md
+++ b/doc/en/scripting/events.md
@@ -16,6 +16,12 @@ function on_broken(x, y, z, playerid)
Called on block broken by player
+```lua
+function on_replaced(x, y, z, playerid)
+```
+
+Called on block replaced with other by player
+
```lua
function on_interact(x, y, z, playerid) -> bool
```
diff --git a/doc/ru/scripting/builtins/libplayer.md b/doc/ru/scripting/builtins/libplayer.md
index 179e6b18..916fd4ab 100644
--- a/doc/ru/scripting/builtins/libplayer.md
+++ b/doc/ru/scripting/builtins/libplayer.md
@@ -77,6 +77,13 @@ player.get_spawnpoint(playerid: int) -> number, number, number
Сеттер и геттер точки спавна игрока
+```lua
+player.set_name(playerid: int, name: str)
+player.get_name(playerid: int) -> str
+```
+
+Сеттер и геттер имени игрока
+
```lua
player.get_selected_block(playerid: int) -> x,y,z
```
diff --git a/doc/ru/scripting/builtins/libworld.md b/doc/ru/scripting/builtins/libworld.md
index b82f3b53..9d7d635e 100644
--- a/doc/ru/scripting/builtins/libworld.md
+++ b/doc/ru/scripting/builtins/libworld.md
@@ -9,7 +9,9 @@ world.get_list() -> массив таблиц {
-- название мира
name: str,
-- предпросмотр (автоматически загружаемая текстура)
- icon: str
+ icon: str,
+ -- версия движка, на которой был сохранен мир
+ version: {int, int}
}
-- Возвращает текущее игровое время от 0.0 до 1.0, где 0.0 и 1.0 - полночь, 0.5 - полдень.
diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md
index 6d908459..08192cfa 100644
--- a/doc/ru/scripting/events.md
+++ b/doc/ru/scripting/events.md
@@ -16,6 +16,12 @@ function on_broken(x, y, z, playerid)
Вызывается после разрушения блока игроком
+```lua
+function on_replaced(x, y, z, playerid)
+```
+
+Вызывается после замены блока игроком
+
```lua
function on_interact(x, y, z, playerid) -> bool
```
diff --git a/res/layouts/pages/worlds.xml.lua b/res/layouts/pages/worlds.xml.lua
index 37c348a9..1cbcd53d 100644
--- a/res/layouts/pages/worlds.xml.lua
+++ b/res/layouts/pages/worlds.xml.lua
@@ -1,6 +1,13 @@
function on_open()
local worlds = world.get_list()
for _, info in ipairs(worlds) do
+ local major, minor = core.get_version()
+ if info.version[1] > major or info.version[2] > minor then
+ info.versionColor = "#A02010"
+ else
+ info.versionColor = "#808080"
+ end
+ info.versionString = string.format("%s.%s", unpack(info.version))
document.worlds:add(gui.template("world", info))
end
end
diff --git a/res/layouts/templates/world.xml b/res/layouts/templates/world.xml
index c20223fc..a258604b 100644
--- a/res/layouts/templates/world.xml
+++ b/res/layouts/templates/world.xml
@@ -14,4 +14,5 @@
onclick='core.delete_world("%{name}")'>
+
diff --git a/res/presets/text3d/player_name.toml b/res/presets/text3d/player_name.toml
new file mode 100644
index 00000000..7942ff48
--- /dev/null
+++ b/res/presets/text3d/player_name.toml
@@ -0,0 +1,3 @@
+display = "projected"
+xray_opacity = 0.3
+render_distance = 128
diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp
index b790d0e7..857f4fe0 100644
--- a/src/content/ContentPack.hpp
+++ b/src/content/ContentPack.hpp
@@ -97,6 +97,7 @@ struct ContentPackStats {
struct world_funcs_set {
bool onblockplaced : 1;
+ bool onblockreplaced : 1;
bool onblockbroken : 1;
bool onblockinteract : 1;
bool onplayertick : 1;
diff --git a/src/engine.hpp b/src/engine.hpp
index 891606a5..04a221df 100644
--- a/src/engine.hpp
+++ b/src/engine.hpp
@@ -18,7 +18,6 @@
#include
#include
-class Level;
class Screen;
class EnginePaths;
class ResPaths;
diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp
index 1acd9200..34d1db67 100644
--- a/src/frontend/screens/LevelScreen.cpp
+++ b/src/frontend/screens/LevelScreen.cpp
@@ -50,7 +50,7 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr)
hud = std::make_unique(engine, *frontend, controller->getPlayer());
decorator = std::make_unique(
- *controller, *worldRenderer->particles, assets
+ *engine, *controller, *worldRenderer, assets
);
keepAlive(settings.graphics.backlight.observe([=](bool) {
diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp
index 78ed45bf..9a72916c 100644
--- a/src/graphics/render/ChunksRenderer.cpp
+++ b/src/graphics/render/ChunksRenderer.cpp
@@ -251,14 +251,16 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
continue;
}
- glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
- glm::vec3 max(
- chunk->x * CHUNK_W + CHUNK_W,
- chunk->top,
- chunk->z * CHUNK_D + CHUNK_D
- );
+ if (culling) {
+ glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
+ glm::vec3 max(
+ chunk->x * CHUNK_W + CHUNK_W,
+ chunk->top,
+ chunk->z * CHUNK_D + CHUNK_D
+ );
- if (!frustum.isBoxVisible(min, max)) continue;
+ if (!frustum.isBoxVisible(min, max)) continue;
+ }
auto& chunkEntries = found->second.sortingMeshData.entries;
diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp
index 4bac77af..b7bd4218 100644
--- a/src/graphics/render/Decorator.cpp
+++ b/src/graphics/render/Decorator.cpp
@@ -1,13 +1,22 @@
#include "Decorator.hpp"
#include "ParticlesRenderer.hpp"
+#include "WorldRenderer.hpp"
+#include "TextsRenderer.hpp"
+#include "TextNote.hpp"
#include "assets/assets_util.hpp"
#include "content/Content.hpp"
#include "voxels/Chunks.hpp"
#include "voxels/Block.hpp"
#include "world/Level.hpp"
#include "window/Camera.hpp"
+#include "objects/Players.hpp"
#include "logic/LevelController.hpp"
+#include "util/stringutil.hpp"
+#include "engine.hpp"
+#include "files/files.hpp"
+
+namespace fs = std::filesystem;
/// @brief Not greather than 64 for this BIG_PRIME value
inline constexpr int UPDATE_AREA_DIAMETER = 32;
@@ -20,15 +29,32 @@ inline constexpr int ITERATIONS = 512;
inline constexpr int BIG_PRIME = 666667;
Decorator::Decorator(
- LevelController& controller, ParticlesRenderer& particles, const Assets& assets
+ Engine& engine, LevelController& controller, WorldRenderer& renderer, const Assets& assets
)
- : level(*controller.getLevel()), particles(particles), assets(assets) {
+ : engine(engine),
+ level(*controller.getLevel()),
+ renderer(renderer),
+ assets(assets),
+ player(*controller.getPlayer()) {
controller.getBlocksController()->listenBlockInteraction(
[this](auto player, const auto& pos, const auto& def, BlockInteraction type) {
if (type == BlockInteraction::placing && def.particles) {
addParticles(def, pos);
}
});
+ for (const auto& [id, player] : *level.players) {
+ if (id == controller.getPlayer()->getId()) {
+ continue;
+ }
+ playerTexts[id] = renderer.texts->add(std::make_unique(
+ util::str2wstr_utf8(player->getName()),
+ playerNamePreset,
+ player->getPosition()
+ ));
+ }
+ playerNamePreset.deserialize(engine.getResPaths()->readCombinedObject(
+ "presets/text3d/player_name.toml"
+ ));
}
void Decorator::addParticles(const Block& def, const glm::ivec3& pos) {
@@ -37,7 +63,7 @@ void Decorator::addParticles(const Block& def, const glm::ivec3& pos) {
auto treg = util::get_texture_region(
assets, def.particles->texture, ""
);
- blockEmitters[pos] = particles.add(std::make_unique(
+ blockEmitters[pos] = renderer.particles->add(std::make_unique(
level,
glm::vec3{pos.x + 0.5, pos.y + 0.5, pos.z + 0.5},
*def.particles,
@@ -81,7 +107,7 @@ void Decorator::update(float delta, const Camera& camera) {
const auto& indices = *level.content->getIndices();
auto iter = blockEmitters.begin();
while (iter != blockEmitters.end()) {
- auto emitter = particles.getEmitter(iter->second);
+ auto emitter = renderer.particles->getEmitter(iter->second);
if (emitter == nullptr) {
iter = blockEmitters.erase(iter);
continue;
@@ -108,4 +134,28 @@ void Decorator::update(float delta, const Camera& camera) {
}
iter++;
}
+
+ for (const auto& [id, player] : *level.players) {
+ if (id == this->player.getId() ||
+ playerTexts.find(id) != playerTexts.end()) {
+ continue;
+ }
+ playerTexts[id] = renderer.texts->add(std::make_unique(
+ util::str2wstr_utf8(player->getName()),
+ playerNamePreset,
+ player->getPosition()
+ ));
+ }
+
+ auto textsIter = playerTexts.begin();
+ while (textsIter != playerTexts.end()) {
+ auto note = renderer.texts->get(textsIter->second);
+ auto player = level.players->get(textsIter->first);
+ if (player == nullptr) {
+ textsIter = playerTexts.erase(textsIter);
+ } else {
+ note->setPosition(player->getPosition() + glm::vec3(0, 1, 0));
+ ++textsIter;
+ }
+ }
}
diff --git a/src/graphics/render/Decorator.hpp b/src/graphics/render/Decorator.hpp
index 32f9540a..fbc196aa 100644
--- a/src/graphics/render/Decorator.hpp
+++ b/src/graphics/render/Decorator.hpp
@@ -6,20 +6,29 @@
#include
+#include "typedefs.hpp"
+#include "presets/NotePreset.hpp"
+
class Level;
class Chunks;
class Camera;
class Assets;
+class Player;
struct Block;
+class Engine;
class LevelController;
-class ParticlesRenderer;
+class WorldRenderer;
class Decorator {
+ Engine& engine;
const Level& level;
const Assets& assets;
- ParticlesRenderer& particles;
+ Player& player;
+ WorldRenderer& renderer;
std::unordered_map blockEmitters;
+ std::unordered_map playerTexts;
int currentIndex = 0;
+ NotePreset playerNamePreset {};
void update(
float delta, const glm::ivec3& areaStart, const glm::ivec3& areaCenter
@@ -27,7 +36,10 @@ class Decorator {
void addParticles(const Block& def, const glm::ivec3& pos);
public:
Decorator(
- LevelController& level, ParticlesRenderer& particles, const Assets& assets
+ Engine& engine,
+ LevelController& level,
+ WorldRenderer& renderer,
+ const Assets& assets
);
void update(float delta, const Camera& camera);
diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp
index 92b628f8..676ca349 100644
--- a/src/graphics/render/TextsRenderer.cpp
+++ b/src/graphics/render/TextsRenderer.cpp
@@ -61,6 +61,10 @@ void TextsRenderer::renderNote(
if (preset.displayMode == NoteDisplayMode::XY_FREE_BILLBOARD) {
yvec = camera.up;
}
+ float scale =
+ (1.0f - preset.perspective) * glm::pow(glm::distance(camera.position, pos), 1.0f-preset.perspective);
+ xvec *= 1.0f + scale;
+ yvec *= 1.0f + scale;
}
if (preset.displayMode != NoteDisplayMode::PROJECTED) {
if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f),
diff --git a/src/interfaces/Object.hpp b/src/interfaces/Object.hpp
deleted file mode 100644
index bb21fedb..00000000
--- a/src/interfaces/Object.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-
-class Level;
-
-class Object {
-private:
-
-public:
- uint64_t objectUID;
- bool shouldUpdate = true;
-
-public:
- ~Object() { destroyed(); }
-
-public:
- virtual void spawned() { }
- virtual void update(float delta) { }
- virtual void destroyed() { }
-};
diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp
index 01679546..980c8bfe 100644
--- a/src/logic/BlocksController.cpp
+++ b/src/logic/BlocksController.cpp
@@ -75,6 +75,13 @@ void BlocksController::breakBlock(
void BlocksController::placeBlock(
Player* player, const Block& def, blockstate state, int x, int y, int z
) {
+ auto voxel = chunks.get(x, y, z);
+ if (voxel == nullptr) {
+ return;
+ }
+ const auto& prevDef = level.content->getIndices()->blocks.require(voxel->id);
+ scripting::on_block_replaced(player, prevDef, {x, y, z});
+
onBlockInteraction(
player, glm::ivec3(x, y, z), def, BlockInteraction::placing
);
diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp
index bd8af15c..5d2ddd9b 100644
--- a/src/logic/LevelController.cpp
+++ b/src/logic/LevelController.cpp
@@ -5,7 +5,6 @@
#include "debug/Logger.hpp"
#include "engine.hpp"
#include "files/WorldFiles.hpp"
-#include "interfaces/Object.hpp"
#include "objects/Entities.hpp"
#include "physics/Hitbox.hpp"
#include "settings.hpp"
@@ -26,7 +25,7 @@ LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr
*level, settings.chunks.padding.get()
)),
player(std::make_unique(
- settings, this->level.get(), blocks.get()
+ settings, level.get(), blocks.get()
)) {
scripting::on_world_load(this);
}
@@ -45,11 +44,6 @@ void LevelController::update(float delta, bool input, bool pause) {
if (!pause) {
// update all objects that needed
- for (const auto& obj : level->objects) {
- if (obj && obj->shouldUpdate) {
- obj->update(delta);
- }
- }
blocks->update(delta);
player->update(delta, input, pause);
level->entities->updatePhysics(delta);
@@ -57,17 +51,6 @@ void LevelController::update(float delta, bool input, bool pause) {
}
level->entities->clean();
player->postUpdate(delta, input, pause);
-
- // erease null pointers
- auto& objects = level->objects;
- objects.erase(
- std::remove_if(
- objects.begin(),
- objects.end(),
- [](auto obj) { return obj == nullptr; }
- ),
- objects.end()
- );
}
void LevelController::saveWorld() {
diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp
index 0ad5ef6b..0b23942e 100644
--- a/src/logic/PlayerController.cpp
+++ b/src/logic/PlayerController.cpp
@@ -13,6 +13,7 @@
#include "lighting/Lighting.hpp"
#include "objects/Entities.hpp"
#include "objects/Player.hpp"
+#include "objects/Players.hpp"
#include "physics/Hitbox.hpp"
#include "physics/PhysicsSolver.hpp"
#include "settings.hpp"
@@ -40,7 +41,7 @@ const float C_ZOOM = 0.1f;
const float CROUCH_SHIFT_Y = -0.2f;
CameraControl::CameraControl(
- const std::shared_ptr& player, const CameraSettings& settings
+ Player* player, const CameraSettings& settings
)
: player(player),
camera(player->fpCamera),
@@ -193,7 +194,7 @@ PlayerController::PlayerController(
BlocksController* blocksController
)
: settings(settings), level(level),
- player(level->getObject(0)),
+ player(level->players->get(0)),
camControl(player, settings.camera),
blocksController(blocksController),
playerTickClock(20, 3) {
@@ -215,7 +216,7 @@ void PlayerController::onFootstep(const Hitbox& hitbox) {
continue;
}
blocksController->onBlockInteraction(
- player.get(),
+ player,
glm::ivec3(x, y, z),
def,
BlockInteraction::step
@@ -254,7 +255,7 @@ void PlayerController::update(float delta, bool input, bool pause) {
if (playerTickClock.update(delta)) {
if (player->getId() % playerTickClock.getParts() ==
playerTickClock.getPart()) {
- scripting::on_player_tick(player.get(), playerTickClock.getTickRate());
+ scripting::on_player_tick(player, playerTickClock.getTickRate());
}
}
}
@@ -390,12 +391,12 @@ voxel* PlayerController::updateSelection(float maxDistance) {
if (selection.entity != prevEntity) {
if (prevEntity != ENTITY_NONE) {
if (auto pentity = level->entities->get(prevEntity)) {
- scripting::on_aim_off(*pentity, player.get());
+ scripting::on_aim_off(*pentity, player);
}
}
if (selection.entity != ENTITY_NONE) {
if (auto pentity = level->entities->get(selection.entity)) {
- scripting::on_aim_on(*pentity, player.get());
+ scripting::on_aim_on(*pentity, player);
}
}
}
@@ -432,8 +433,8 @@ void PlayerController::processRightClick(const Block& def, const Block& target)
if (!input.shift && target.rt.funcsset.oninteract) {
if (scripting::on_block_interact(
- player.get(), target, selection.actualPosition
- )) {
+ player, target, selection.actualPosition
+ )) {
return;
}
}
@@ -474,7 +475,7 @@ void PlayerController::processRightClick(const Block& def, const Block& target)
slot.setCount(slot.getCount()-1);
}
blocksController->placeBlock(
- player.get(), def, state, coord.x, coord.y, coord.z
+ player, def, state, coord.x, coord.y, coord.z
);
}
}
@@ -488,10 +489,10 @@ void PlayerController::updateEntityInteraction(
}
auto entity = *entityOpt;
if (lclick) {
- scripting::on_attacked(entity, player.get(), player->getEntity());
+ scripting::on_attacked(entity, player, player->getEntity());
}
if (rclick) {
- scripting::on_entity_used(entity, player.get());
+ scripting::on_entity_used(entity, player);
}
}
@@ -523,7 +524,7 @@ void PlayerController::updateInteraction(float delta) {
auto vox = updateSelection(maxDistance);
if (vox == nullptr) {
if (rclick && item.rt.funcsset.on_use) {
- scripting::on_item_use(player.get(), item);
+ scripting::on_item_use(player, item);
}
if (selection.entity) {
updateEntityInteraction(selection.entity, lattack, rclick);
@@ -534,7 +535,7 @@ void PlayerController::updateInteraction(float delta) {
auto iend = selection.position;
if (lclick && !input.shift && item.rt.funcsset.on_block_break_by) {
if (scripting::on_item_break_block(
- player.get(), item, iend.x, iend.y, iend.z
+ player, item, iend.x, iend.y, iend.z
)) {
return;
}
@@ -543,7 +544,7 @@ void PlayerController::updateInteraction(float delta) {
if (lclick) {
if (player->isInstantDestruction() && target.breakable) {
blocksController->breakBlock(
- player.get(), target, iend.x, iend.y, iend.z
+ player, target, iend.x, iend.y, iend.z
);
}
}
@@ -551,10 +552,10 @@ void PlayerController::updateInteraction(float delta) {
bool preventDefault = false;
if (item.rt.funcsset.on_use_on_block) {
preventDefault = scripting::on_item_use_on_block(
- player.get(), item, iend, selection.normal
+ player, item, iend, selection.normal
);
} else if (item.rt.funcsset.on_use) {
- preventDefault = scripting::on_item_use(player.get(), item);
+ preventDefault = scripting::on_item_use(player, item);
}
if (preventDefault) {
return;
@@ -566,10 +567,10 @@ void PlayerController::updateInteraction(float delta) {
}
if (Events::jactive(BIND_PLAYER_PICK)) {
auto coord = selection.actualPosition;
- pick_block(indices, chunks, player.get(), coord.x, coord.y, coord.z);
+ pick_block(indices, chunks, player, coord.x, coord.y, coord.z);
}
}
Player* PlayerController::getPlayer() {
- return player.get();
+ return player;
}
diff --git a/src/logic/PlayerController.hpp b/src/logic/PlayerController.hpp
index 3fe3decf..8d82a3f9 100644
--- a/src/logic/PlayerController.hpp
+++ b/src/logic/PlayerController.hpp
@@ -18,7 +18,7 @@ struct CameraSettings;
struct EngineSettings;
class CameraControl {
- std::shared_ptr player;
+ Player* player;
std::shared_ptr camera;
const CameraSettings& settings;
glm::vec3 offset;
@@ -40,7 +40,7 @@ class CameraControl {
void switchCamera();
public:
CameraControl(
- const std::shared_ptr& player, const CameraSettings& settings
+ Player* player, const CameraSettings& settings
);
void updateMouse(PlayerInput& input);
void update(PlayerInput input, float delta, Chunks* chunks);
@@ -50,7 +50,7 @@ public:
class PlayerController {
const EngineSettings& settings;
Level* level;
- std::shared_ptr player;
+ Player* player;
PlayerInput input {};
CameraControl camControl;
BlocksController* blocksController;
diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp
index 20d3253d..2f24e0fe 100644
--- a/src/logic/scripting/lua/libs/libblock.cpp
+++ b/src/logic/scripting/lua/libs/libblock.cpp
@@ -2,6 +2,7 @@
#include "lighting/Lighting.hpp"
#include "logic/BlocksController.hpp"
#include "logic/LevelController.hpp"
+#include "objects/Players.hpp"
#include "voxels/Block.hpp"
#include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp"
@@ -350,9 +351,9 @@ static int l_place(lua::State* L) {
"there is no block with index " + std::to_string(id)
);
}
- auto player = level->getObject(playerid);
+ auto player = level->players->get(playerid);
controller->getBlocksController()->placeBlock(
- player ? player.get() : nullptr, *def, int2blockstate(state), x, y, z
+ player, *def, int2blockstate(state), x, y, z
);
return 0;
}
@@ -367,10 +368,8 @@ static int l_destruct(lua::State* L) {
return 0;
}
auto& def = level->content->getIndices()->blocks.require(voxel->id);
- auto player = level->getObject(playerid);
- controller->getBlocksController()->breakBlock(
- player ? player.get() : nullptr, def, x, y, z
- );
+ auto player = level->players->get(playerid);
+ controller->getBlocksController()->breakBlock(player, def, x, y, z);
return 0;
}
diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp
index 5a100b9c..c656d0e6 100644
--- a/src/logic/scripting/lua/libs/libcore.cpp
+++ b/src/logic/scripting/lua/libs/libcore.cpp
@@ -20,6 +20,12 @@
using namespace scripting;
+static int l_get_version(lua::State* L) {
+ return lua::pushvec_stack(
+ L, glm::vec2(ENGINE_VERSION_MAJOR, ENGINE_VERSION_MINOR)
+ );
+}
+
/// @brief Creating new world
/// @param name Name world
/// @param seed Seed world
@@ -239,6 +245,7 @@ static int l_quit(lua::State*) {
}
const luaL_Reg corelib[] = {
+ {"get_version", lua::wrap},
{"new_world", lua::wrap},
{"open_world", lua::wrap},
{"reopen_world", lua::wrap},
diff --git a/src/logic/scripting/lua/libs/libplayer.cpp b/src/logic/scripting/lua/libs/libplayer.cpp
index 18b29405..0628c5af 100644
--- a/src/logic/scripting/lua/libs/libplayer.cpp
+++ b/src/logic/scripting/lua/libs/libplayer.cpp
@@ -4,6 +4,7 @@
#include "items/Inventory.hpp"
#include "objects/Entities.hpp"
#include "objects/Player.hpp"
+#include "objects/Players.hpp"
#include "physics/Hitbox.hpp"
#include "window/Camera.hpp"
#include "world/Level.hpp"
@@ -11,8 +12,8 @@
using namespace scripting;
-inline std::shared_ptr get_player(lua::State* L, int idx) {
- return level->getObject(lua::tointeger(L, idx));
+inline Player* get_player(lua::State* L, int idx) {
+ return level->players->get(lua::tointeger(L, idx));
}
static int l_get_pos(lua::State* L) {
@@ -235,6 +236,20 @@ static int l_set_camera(lua::State* L) {
return 0;
}
+static int l_get_name(lua::State* L) {
+ if (auto player = get_player(L, 1)) {
+ return lua::pushstring(L, player->getName());
+ }
+ return 0;
+}
+
+static int l_set_name(lua::State* L) {
+ if (auto player = get_player(L, 1)) {
+ player->setName(lua::require_string(L, 2));
+ }
+ return 0;
+}
+
const luaL_Reg playerlib[] = {
{"get_pos", lua::wrap},
{"set_pos", lua::wrap},
@@ -260,5 +275,7 @@ const luaL_Reg playerlib[] = {
{"set_entity", lua::wrap},
{"get_camera", lua::wrap},
{"set_camera", lua::wrap},
+ {"get_name", lua::wrap},
+ {"set_name", lua::wrap},
{NULL, NULL}
};
diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp
index 06da877c..5422c873 100644
--- a/src/logic/scripting/lua/libs/libworld.cpp
+++ b/src/logic/scripting/lua/libs/libworld.cpp
@@ -4,7 +4,9 @@
#include "assets/Assets.hpp"
#include "assets/AssetsLoader.hpp"
+#include "coders/json.hpp"
#include "engine.hpp"
+#include "files/files.hpp"
#include "files/engine_paths.hpp"
#include "world/Level.hpp"
#include "world/World.hpp"
@@ -32,22 +34,34 @@ static int l_get_list(lua::State* L) {
for (size_t i = 0; i < worlds.size(); i++) {
lua::createtable(L, 0, 1);
- auto name = worlds[i].filename().u8string();
+ const auto& folder = worlds[i];
+
+ auto root = json::parse(files::read_string(folder/fs::u8path("world.json")));
+ const auto& versionMap = root["version"];
+ int versionMajor = versionMap["major"].asInteger();
+ int versionMinor = versionMap["minor"].asInteger();
+
+
+ auto name = folder.filename().u8string();
lua::pushstring(L, name);
lua::setfield(L, "name");
auto assets = engine->getAssets();
std::string icon = "world#" + name + ".icon";
if (!AssetsLoader::loadExternalTexture(
- assets,
- icon,
- {worlds[i] / fs::path("icon.png"),
- worlds[i] / fs::path("preview.png")}
- )) {
+ assets,
+ icon,
+ {worlds[i] / fs::path("icon.png"),
+ worlds[i] / fs::path("preview.png")}
+ )) {
icon = "gui/no_world_icon";
}
lua::pushstring(L, icon);
lua::setfield(L, "icon");
+
+ lua::pushvec2(L, {versionMajor, versionMinor});
+ lua::setfield(L, "version");
+
lua::rawseti(L, i + 1);
}
return 1;
diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp
index 0778fd45..055ecf34 100644
--- a/src/logic/scripting/scripting.cpp
+++ b/src/logic/scripting/scripting.cpp
@@ -284,6 +284,32 @@ void scripting::on_block_placed(
}
}
+void scripting::on_block_replaced(
+ Player* player, const Block& block, const glm::ivec3& pos
+) {
+ if (block.rt.funcsset.onreplaced) {
+ std::string name = block.name + ".replaced";
+ lua::emit_event(lua::get_main_state(), name, [pos, player](auto L) {
+ lua::pushivec_stack(L, pos);
+ lua::pushinteger(L, player ? player->getId() : -1);
+ return 4;
+ });
+ }
+ auto args = [&](lua::State* L) {
+ lua::pushinteger(L, block.rt.id);
+ lua::pushivec_stack(L, pos);
+ lua::pushinteger(L, player ? player->getId() : -1);
+ return 5;
+ };
+ for (auto& [packid, pack] : content->getPacks()) {
+ if (pack->worldfuncsset.onblockreplaced) {
+ lua::emit_event(
+ lua::get_main_state(), packid + ":.blockreplaced", args
+ );
+ }
+ }
+}
+
void scripting::on_block_broken(
Player* player, const Block& block, const glm::ivec3& pos
) {
@@ -701,6 +727,8 @@ void scripting::load_content_script(
register_event(env, "on_random_update", prefix + ".randupdate");
funcsset.onbroken = register_event(env, "on_broken", prefix + ".broken");
funcsset.onplaced = register_event(env, "on_placed", prefix + ".placed");
+ funcsset.onreplaced =
+ register_event(env, "on_replaced", prefix + ".replaced");
funcsset.oninteract =
register_event(env, "on_interact", prefix + ".interact");
funcsset.onblockstick =
@@ -752,6 +780,8 @@ void scripting::load_world_script(
register_event(env, "on_block_placed", prefix + ":.blockplaced");
funcsset.onblockbroken =
register_event(env, "on_block_broken", prefix + ":.blockbroken");
+ funcsset.onblockreplaced =
+ register_event(env, "on_block_replaced", prefix + ":.blockreplaced");
funcsset.onblockinteract =
register_event(env, "on_block_interact", prefix + ":.blockinteract");
funcsset.onplayertick =
diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp
index 3cd8a022..0ab3c035 100644
--- a/src/logic/scripting/scripting.hpp
+++ b/src/logic/scripting/scripting.hpp
@@ -71,6 +71,9 @@ namespace scripting {
void on_block_placed(
Player* player, const Block& block, const glm::ivec3& pos
);
+ void on_block_replaced(
+ Player* player, const Block& block, const glm::ivec3& pos
+ );
void on_block_broken(
Player* player, const Block& block, const glm::ivec3& pos
);
diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp
index 5c2a2b85..ae8c0139 100644
--- a/src/objects/Player.cpp
+++ b/src/objects/Player.cpp
@@ -29,12 +29,16 @@ constexpr int SPAWN_ATTEMPTS_PER_UPDATE = 64;
Player::Player(
Level* level,
+ int64_t id,
+ const std::string& name,
glm::vec3 position,
float speed,
std::shared_ptr inv,
entityid_t eid
)
: level(level),
+ id(id),
+ name(name),
speed(speed),
chosenSlot(0),
position(position),
@@ -49,8 +53,7 @@ Player::Player(
tpCamera->setFov(glm::radians(90.0f));
}
-Player::~Player() {
-}
+Player::~Player() = default;
void Player::updateEntity() {
if (eid == 0) {
@@ -268,6 +271,14 @@ entityid_t Player::getSelectedEntity() const {
return selectedEid;
}
+void Player::setName(const std::string& name) {
+ this->name = name;
+}
+
+const std::string& Player::getName() const {
+ return name;
+}
+
const std::shared_ptr& Player::getInventory() const {
return inventory;
}
@@ -283,6 +294,9 @@ glm::vec3 Player::getSpawnPoint() const {
dv::value Player::serialize() const {
auto root = dv::object();
+ root["id"] = id;
+ root["name"] = name;
+
root["position"] = dv::to_value(position);
root["rotation"] = dv::to_value(cam);
root["spawnpoint"] = dv::to_value(spawnpoint);
@@ -304,6 +318,9 @@ dv::value Player::serialize() const {
}
void Player::deserialize(const dv::value& src) {
+ src.at("id").get(id);
+ src.at("name").get(name);
+
const auto& posarr = src["position"];
dv::get_vec(posarr, position);
diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp
index 2badd9dc..920e06e4 100644
--- a/src/objects/Player.hpp
+++ b/src/objects/Player.hpp
@@ -4,7 +4,6 @@
#include
#include
-#include "interfaces/Object.hpp"
#include "interfaces/Serializable.hpp"
#include "settings.hpp"
#include "voxels/voxel.hpp"
@@ -40,8 +39,10 @@ struct CursorSelection {
entityid_t entity = ENTITY_NONE;
};
-class Player : public Object, public Serializable {
+class Player : public Serializable {
Level* level;
+ int64_t id;
+ std::string name;
float speed;
int chosenSlot;
glm::vec3 position;
@@ -52,7 +53,7 @@ class Player : public Object, public Serializable {
bool infiniteItems = true;
bool instantDestruction = true;
entityid_t eid;
- entityid_t selectedEid;
+ entityid_t selectedEid = 0;
public:
std::shared_ptr fpCamera, spCamera, tpCamera;
std::shared_ptr currentCamera;
@@ -62,6 +63,8 @@ public:
Player(
Level* level,
+ int64_t id,
+ const std::string& name,
glm::vec3 position,
float speed,
std::shared_ptr inv,
@@ -99,9 +102,12 @@ public:
entityid_t getSelectedEntity() const;
+ void setName(const std::string& name);
+ const std::string& getName() const;
+
const std::shared_ptr& getInventory() const;
- glm::vec3 getPosition() const {
+ const glm::vec3& getPosition() const {
return position;
}
@@ -115,7 +121,7 @@ public:
static void convert(dv::value& data, const ContentReport* report);
- inline int getId() const {
- return objectUID;
+ inline u64id_t getId() const {
+ return id;
}
};
diff --git a/src/objects/Players.cpp b/src/objects/Players.cpp
new file mode 100644
index 00000000..90031991
--- /dev/null
+++ b/src/objects/Players.cpp
@@ -0,0 +1,73 @@
+#include "Players.hpp"
+
+#include "Player.hpp"
+#include "items/Inventories.hpp"
+#include "world/Level.hpp"
+#include "world/World.hpp"
+
+Players::Players(Level* level) : level(level) {}
+
+void Players::addPlayer(std::unique_ptr player) {
+ players[player->getId()] = std::move(player);
+}
+
+Player* Players::get(int64_t id) const {
+ const auto& found = players.find(id);
+ if (found == players.end()) {
+ return nullptr;
+ }
+ return found->second.get();
+}
+
+Player* Players::create() {
+ auto playerPtr = std::make_unique(
+ level,
+ level->getWorld()->getInfo().nextPlayerId++,
+ "",
+ glm::vec3(0, DEF_PLAYER_Y, 0),
+ DEF_PLAYER_SPEED,
+ level->inventories->create(DEF_PLAYER_INVENTORY_SIZE),
+ 0
+ );
+ auto player = playerPtr.get();
+ addPlayer(std::move(playerPtr));
+
+ level->inventories->store(player->getInventory());
+ return player;
+}
+
+dv::value Players::serialize() const {
+ auto root = dv::object();
+ auto& list = root.list("players");
+
+ for (const auto& [id, player] : players) {
+ list.add(player->serialize());
+ }
+ return root;
+}
+
+void Players::deserialize(const dv::value& src) {
+ players.clear();
+
+ const auto& players = src["players"];
+ for (auto& playerMap : players) {
+ auto playerPtr = std::make_unique(
+ level,
+ 0,
+ "",
+ glm::vec3(0, DEF_PLAYER_Y, 0),
+ DEF_PLAYER_SPEED,
+ level->inventories->create(DEF_PLAYER_INVENTORY_SIZE),
+ 0
+ );
+ auto player = playerPtr.get();
+ player->deserialize(playerMap);
+ addPlayer(std::move(playerPtr));
+ auto& inventory = player->getInventory();
+ // invalid inventory id pre 0.25
+ if (inventory->getId() == 0) {
+ inventory->setId(level->getWorld()->getNextInventoryId());
+ }
+ level->inventories->store(player->getInventory());
+ }
+}
diff --git a/src/objects/Players.hpp b/src/objects/Players.hpp
new file mode 100644
index 00000000..f66d181f
--- /dev/null
+++ b/src/objects/Players.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+#include
+
+#include "typedefs.hpp"
+#include "interfaces/Serializable.hpp"
+
+inline constexpr float DEF_PLAYER_Y = 100.0f;
+inline constexpr float DEF_PLAYER_SPEED = 4.0f;
+inline constexpr int DEF_PLAYER_INVENTORY_SIZE = 40;
+
+class Level;
+class Player;
+
+class Players : public Serializable {
+ Level* level;
+ std::unordered_map> players;
+
+ void addPlayer(std::unique_ptr player);
+public:
+ Players(Level* level);
+
+ Player* get(int64_t id) const;
+
+ Player* create();
+
+ dv::value serialize() const override;
+
+ void deserialize(const dv::value& src) override;
+
+ auto begin() const {
+ return players.begin();
+ }
+
+ auto end() const {
+ return players.end();
+ }
+};
diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp
index 4fe51b0d..95768c88 100644
--- a/src/voxels/Block.hpp
+++ b/src/voxels/Block.hpp
@@ -39,6 +39,7 @@ struct block_funcs_set {
bool update : 1;
bool onplaced : 1;
bool onbroken : 1;
+ bool onreplaced : 1;
bool oninteract : 1;
bool randupdate : 1;
bool onblockstick : 1;
diff --git a/src/world/Level.cpp b/src/world/Level.cpp
index 9c58d0dc..ed370d08 100644
--- a/src/world/Level.cpp
+++ b/src/world/Level.cpp
@@ -7,6 +7,7 @@
#include "lighting/Lighting.hpp"
#include "objects/Entities.hpp"
#include "objects/Player.hpp"
+#include "objects/Players.hpp"
#include "physics/Hitbox.hpp"
#include "physics/PhysicsSolver.hpp"
#include "settings.hpp"
@@ -28,8 +29,9 @@ Level::Level(
physics(std::make_unique(glm::vec3(0, -22.6f, 0))),
events(std::make_unique()),
entities(std::make_unique(this)),
+ players(std::make_unique(this)),
settings(settings) {
- auto& worldInfo = world->getInfo();
+ const auto& worldInfo = world->getInfo();
auto& cameraIndices = content->getIndices(ResourceType::CAMERA);
for (size_t i = 0; i < cameraIndices.size(); i++) {
auto camera = std::make_shared();
@@ -51,12 +53,6 @@ Level::Level(
if (worldInfo.nextEntityId) {
entities->setNextID(worldInfo.nextEntityId);
}
- auto inv = std::make_shared(
- world->getNextInventoryId(), DEF_PLAYER_INVENTORY_SIZE
- );
- auto player = spawnObject(
- this, glm::vec3(0, DEF_PLAYER_Y, 0), DEF_PLAYER_SPEED, inv, 0
- );
uint matrixSize =
(settings.chunks.loadDistance.get() + settings.chunks.padding.get()) *
@@ -71,14 +67,9 @@ Level::Level(
});
inventories = std::make_unique(*this);
- inventories->store(player->getInventory());
}
-Level::~Level() {
- for (auto obj : objects) {
- obj.reset();
- }
-}
+Level::~Level() = default;
void Level::loadMatrix(int32_t x, int32_t z, uint32_t radius) {
chunks->setCenter(x, z);
diff --git a/src/world/Level.hpp b/src/world/Level.hpp
index 767844cf..1f3ef183 100644
--- a/src/world/Level.hpp
+++ b/src/world/Level.hpp
@@ -2,12 +2,10 @@
#include
#include
+#include
+#include
-#include "interfaces/Object.hpp"
-
-inline constexpr float DEF_PLAYER_Y = 100.0f;
-inline constexpr float DEF_PLAYER_SPEED = 4.0f;
-inline constexpr int DEF_PLAYER_INVENTORY_SIZE = 40;
+#include "typedefs.hpp"
class Content;
class World;
@@ -19,6 +17,7 @@ class Lighting;
class PhysicsSolver;
class ChunksStorage;
class Camera;
+class Players;
struct EngineSettings;
/// @brief A level, contains chunks and objects
@@ -26,7 +25,7 @@ class Level {
std::unique_ptr world;
public:
const Content* const content;
- std::vector> objects;
+
std::unique_ptr chunks;
std::unique_ptr chunksStorage;
std::unique_ptr inventories;
@@ -35,6 +34,7 @@ public:
std::unique_ptr lighting;
std::unique_ptr events;
std::unique_ptr entities;
+ std::unique_ptr players;
std::vector> cameras; // move somewhere?
const EngineSettings& settings;
@@ -52,36 +52,6 @@ public:
const World* getWorld() const;
- /// Spawns object of class T and returns pointer to it.
- /// @param T class that derives the Object class
- /// @param args pass arguments needed for T class constructor
- template
- std::shared_ptr spawnObject(Args&&... args) {
- static_assert(
- std::is_base_of