Merge branch 'main' into user-properties

This commit is contained in:
MihailRis 2024-11-24 12:58:09 +03:00
commit f095edda7a
50 changed files with 669 additions and 224 deletions

86
doc/en/3d-text.md Normal file
View File

@ -0,0 +1,86 @@
# 3D Text
2D text displayed in 3D space.
The appearance of 3D text, is configured via a table, like [particles](particles.md). All fields are optional.
| Field | Description | Default |
| -------------- | ---------------------------- | ---------------- |
| display | Display format | static_billboard |
| color | Text color | {1, 1, 1, 1} |
| scale | Text scale | 1 |
| renderDistance | Text rendering distance | 32 |
| xray_opacity | Visibility through obstacles | 0 |
| perspective | Perspective coefficient | 1 |
Available display formats:
| Format | Description |
| ----------------- | ----------------------------------------------------------------- |
| static_billboard | Simple 3D text in the world with manual size and rotation control |
| y_free_billboard | Freely rotating text on the Y axis facing the camera |
| xy_free_billboard | Freely rotating text facing the camera |
| projected | Projected text (displayed in screen coordinates) |
## *gfx.text3d* library
```lua
gfx.text3d.show(
-- text position
position: vec3,
-- text to display
text: str,
-- text display settings table
preset: table,
-- additional text display settings table
[optional] extension: table
) -> int
```
Creates 3D text, returning its id.
```lua
gfx.text3d.hide(id: int)
```
Removes 3D text.
```lua
gfx.text3d.get_text(id: int) -> str
gfx.text3d.set_text(id: int, text: str)
```
Text getter and setter.
```lua
gfx.text3d.get_pos(id: int) -> vec3
gfx.text3d.set_pos(id: int, pos: vec3)
```
Text position getter and setter.
```lua
gfx.text3d.get_axis_x(id: int) -> vec3
gfx.text3d.set_axis_x(id: int, pos: vec3)
```
Getter and setter of vector X.
```lua
gfx.text3d.get_axis_y(id: int) -> vec3
gfx.text3d.set_axis_y(id: int, pos: vec3)
```
Getter and setter of vector Y.
```lua
gfx.text3d.set_rotation(id: int, rotation: mat4)
```
Sets the text rotation (Sets the rotated vectors X,Y).
```lua
gfx.text3d.update_settings(id: int, preset: table)
```
Updates text display settings.

View File

@ -9,10 +9,14 @@ Subsections:
- [UI properties and methods](scripting/ui.md)
- [Entities and components](scripting/ecs.md)
- [Libraries](#)
- [base64](scripting/builtins/libbase64.md)
- [bjson, json, toml](scripting/filesystem.md)
- [block](scripting/builtins/libblock.md)
- [cameras](scripting/builtins/libcameras.md)
- [entities](scripting/builtins/libentities.md)
- [file](scripting/builtins/libfile.md)
- [gfx.particles](particles.md#gfxparticles-library)
- [gfx.text3d](3d-text.md#gfxtext3d-library)
- [gui](scripting/builtins/libgui.md)
- [hud](scripting/builtins/libhud.md)
- [inventory](scripting/builtins/libinventory.md)

View File

@ -0,0 +1,11 @@
# *base64* library
Library for base64 encoding/decoding.
```lua
-- Encode bytes to base64 string
base64.encode(bytes: table|ByteArray) -> str
-- Decode base64 string to ByteArray or lua table if second argument is set to true
base64.decode(base64string: str, [optional]usetable: bool=false) -> table|ByteArray
```

View File

@ -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
```

View File

@ -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\]

View File

@ -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
```

86
doc/ru/3d-text.md Normal file
View File

@ -0,0 +1,86 @@
# 3D Текст
2D текст отображаемый в 3D пространстве.
Вид 3D текста, как и [частицы](particles.md), настраивается через таблицу, все поля которой опциональны.
| Поле | Описание | По-умолчанию |
| --------------- | ------------------------------------------------------- | ----------------- |
| display | Формат отображения | static_billboard |
| color | Цвет текста | {1, 1, 1, 1} |
| scale | Масштаб | 1 |
| renderDistance | Дистанция отрисовки текста | 32 |
| xray_opacity | Коэффициент видимости через препятствия (просвечивание) | 0 |
| perspective | Коэффициент перспективы | 1 |
Доступные форматы отображения:
| Формат | Описание |
| ----------------- | ----------------------------------------------------------------- |
| static_billboard | Простой 3D текст в мире с ручным управлением размером и вращением |
| y_free_billboard | Свободно вращающийся по оси Y текст, направляющийся на камеру |
| xy_free_billboard | Свободно вращающийся текст, направляющийся на камеру |
| projected | Проецируемый текст (отображается в экранной системе координат) |
## Библиотека *gfx.text3d*
```lua
gfx.text3d.show(
-- позиция текста
position: vec3,
-- отображаемый текст
text: str,
-- таблица настроек отображение текста
preset: table,
-- дополнительная таблица настроек отображения текста
[опционально] extension: table
) -> int
```
Создаёт 3D текст, возвращая его id.
```lua
gfx.text3d.hide(id: int)
```
Удаляет 3D текст.
```lua
gfx.text3d.get_text(id: int) -> str
gfx.text3d.set_text(id: int, text: str)
```
Геттер и сеттер текста.
```lua
gfx.text3d.get_pos(id: int) -> vec3
gfx.text3d.set_pos(id: int, pos: vec3)
```
Геттер и сеттер позиции текста.
```lua
gfx.text3d.get_axis_x(id: int) -> vec3
gfx.text3d.set_axis_x(id: int, pos: vec3)
```
Геттер и сеттер вектора X.
```lua
gfx.text3d.get_axis_y(id: int) -> vec3
gfx.text3d.set_axis_y(id: int, pos: vec3)
```
Геттер и сеттер вектора Y.
```lua
gfx.text3d.set_rotation(id: int, rotation: mat4)
```
Устанавливает вращение текста (Устанавливает повернутые вектора X,Y).
```lua
gfx.text3d.update_settings(id: int, preset: table)
```
Обновляет настройки отображения текста.

View File

@ -9,10 +9,14 @@
- [Свойства и методы UI элементов](scripting/ui.md)
- [Сущности и компоненты](scripting/ecs.md)
- [Библиотеки](#)
- [base64](scripting/builtins/libbase64.md)
- [bjson, json, toml](scripting/filesystem.md)
- [block](scripting/builtins/libblock.md)
- [cameras](scripting/builtins/libcameras.md)
- [entities](scripting/builtins/libentities.md)
- [file](scripting/builtins/libfile.md)
- [gfx.particles](particles.md#библиотека-gfxparticles)
- [gfx.text3d](3d-text.md#библиотека-gfxtext3d)
- [gui](scripting/builtins/libgui.md)
- [hud](scripting/builtins/libhud.md)
- [inventory](scripting/builtins/libinventory.md)

View File

@ -0,0 +1,11 @@
# Библиотека *base64*
Библиотека для base64 кодирования/декодирования.
```lua
-- Кодирует массив байт в base64 строку
base64.encode(bytes: table|ByteArray) -> str
-- Декодирует base64 строку в ByteArray или таблицу чисел, если второй аргумент установлен на true
base64.decode(base64string: str, [опционально]usetable: bool=false) -> table|ByteArray
```

View File

@ -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
```

View File

@ -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 - полдень.

View File

@ -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
```

View File

@ -84,13 +84,11 @@ events.on("core:open_traceback", function(traceback_b64)
framestr = framestr.."("..tostring(frame.name)..")"
end
local color = "#FFFFFF"
if frame.source:starts_with("core:") then
color = "#C0D0C5"
end
tb_list:add(gui.template("stack_frame", {
location=framestr,
color=color,
callback=callback
callback=callback,
enabled=file.exists(frame.source)
}))
end
tb_list.size = srcsize

View File

@ -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

View File

@ -1,3 +1,3 @@
<label hover-color="#A0A0FF" interactive="true" onclick="%{callback}" color="%{color}">
<label hover-color="#A0A0FF" interactive="true" onclick="%{callback}" color="%{color}" enabled="%{enabled}">
%{location}
</label>

View File

@ -14,4 +14,5 @@
onclick='core.delete_world("%{name}")'>
<image src='gui/delete_icon' size='32,32' color='#FFFFFF50'/>
</button>
<label color='%{versionColor}' pos='317,44'>%{versionString}</label>
</container>

View File

@ -0,0 +1,4 @@
display = "projected"
xray_opacity = 0.3
render_distance = 128
perspective = 0.0

View File

@ -248,7 +248,7 @@ function _rules.create(name, value, handler)
end
if _rules.get(name) == nil then
_rules.set(name, value)
else
elseif handler then
handler(_rules.get(name))
end
return handlerid
@ -265,11 +265,11 @@ end
function _rules.clear()
_rules.rules = {}
_rules.nextid = 1
_rules.create("allow-cheats", true)
end
function __vc_on_hud_open()
_rules.create("allow-cheats", true)
_rules.create("allow-content-access", hud._is_content_access(), function(value)
hud._set_content_access(value)
input.set_enabled("player.pick", value)

View File

@ -185,8 +185,16 @@ assetload::postfunc assetload::layout(
return [=](auto assets) {
try {
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
size_t pos = name.find(':');
auto prefix = name.substr(0, pos);
assets->store(
UiDocument::read(cfg->env, name, file, "abs:" + file), name
UiDocument::read(
cfg->env,
name,
file,
prefix + ":layouts/" + name.substr(pos + 1) + ".xml"
),
name
);
} catch (const parsing_error& err) {
throw std::runtime_error(

View File

@ -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;

View File

@ -362,6 +362,7 @@ void Engine::loadContent() {
load_configs(pack.folder);
}
content = contentBuilder.build();
interpreter->reset();
scripting::on_content_load(content.get());
ContentLoader::loadScripts(*content);

View File

@ -18,7 +18,6 @@
#include <vector>
#include <mutex>
class Level;
class Screen;
class EnginePaths;
class ResPaths;

View File

@ -50,7 +50,7 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> levelPtr)
hud = std::make_unique<Hud>(engine, *frontend, controller->getPlayer());
decorator = std::make_unique<Decorator>(
*controller, *worldRenderer->particles, assets
*engine, *controller, *worldRenderer, assets
);
keepAlive(settings.graphics.backlight.observe([=](bool) {

View File

@ -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;

View File

@ -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<TextNote>(
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<Emitter>(
blockEmitters[pos] = renderer.particles->add(std::make_unique<Emitter>(
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<TextNote>(
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;
}
}
}

View File

@ -6,20 +6,29 @@
#include <unordered_map>
#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<glm::ivec3, uint64_t> blockEmitters;
std::unordered_map<int64_t, u64id_t> 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);

View File

@ -34,12 +34,11 @@ void TextsRenderer::renderNote(
util::sqr(preset.renderDistance / camera.zoom)) {
return;
}
// Projected notes are displayed on the front layer only
if ((preset.displayMode == NoteDisplayMode::PROJECTED) != projected) {
return;
}
float opacity = 1.0f;
if (frontLayer && preset.displayMode != NoteDisplayMode::PROJECTED) {
if (frontLayer) {
if (preset.xrayOpacity <= 0.0001f) {
return;
}
@ -61,6 +60,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),
@ -77,15 +80,25 @@ void TextsRenderer::renderNote(
scale = scale2 * preset.perspective +
scale * (1.0f - preset.perspective);
}
auto projpos = camera.getProjView() * glm::vec4(pos, 1.0f);
pos = projpos;
if (pos.z < 0.0f) {
return;
if (frontLayer) {
auto projpos = camera.getProjView() * glm::vec4(pos, 1.0f);
pos = projpos;
if (pos.z < 0.0f) {
return;
}
pos /= projpos.w;
pos.z = 0;
xvec = {2.0f/Window::width*scale, 0, 0};
yvec = {0, 2.0f/Window::height*scale, 0};
} else {
auto matrix = camera.getProjView();
auto screenPos = matrix * glm::vec4(pos, 1.0f);
xvec = glm::vec3(2.0f/Window::width*scale, 0, 0);
yvec = glm::vec3(0, 2.0f/Window::height*scale, 0);
pos = screenPos / screenPos.w;
}
pos /= pos.z;
pos.z = 0;
xvec = {2.0f/Window::width*scale, 0, 0};
yvec = {0, 2.0f/Window::height*scale, 0};
}
auto color = preset.color;
batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity));
@ -115,16 +128,11 @@ void TextsRenderer::render(
renderNote(*note, context, camera, settings, hudVisible, frontLayer, false);
}
batch.flush();
if (frontLayer) {
shader.uniformMatrix(
"u_projview",
glm::mat4(1.0f)
);
for (const auto& [_, note] : notes) {
renderNote(*note, context, camera, settings, hudVisible, true, true);
}
batch.flush();
shader.uniformMatrix("u_projview", glm::mat4(1.0f));
for (const auto& [_, note] : notes) {
renderNote(*note, context, camera, settings, hudVisible, frontLayer, true);
}
batch.flush();
}
u64id_t TextsRenderer::add(std::unique_ptr<TextNote> note) {

View File

@ -1,23 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <iostream>
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() { }
};

View File

@ -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
);

View File

@ -125,6 +125,10 @@ namespace cmd {
const std::unordered_map<std::string, Command>& getCommands() const {
return commands;
}
void clear() {
commands.clear();
}
};
class CommandsInterpreter {
@ -158,5 +162,10 @@ namespace cmd {
CommandsRepository* getRepository() const {
return repository.get();
}
void reset() {
repository->clear();
variables.clear();
}
};
}

View File

@ -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<Level> levelPtr
*level, settings.chunks.padding.get()
)),
player(std::make_unique<PlayerController>(
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() {

View File

@ -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>& 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<Player>(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;
}

View File

@ -18,7 +18,7 @@ struct CameraSettings;
struct EngineSettings;
class CameraControl {
std::shared_ptr<Player> player;
Player* player;
std::shared_ptr<Camera> camera;
const CameraSettings& settings;
glm::vec3 offset;
@ -40,7 +40,7 @@ class CameraControl {
void switchCamera();
public:
CameraControl(
const std::shared_ptr<Player>& 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* player;
PlayerInput input {};
CameraControl camControl;
BlocksController* blocksController;

View File

@ -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<Player>(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<Player>(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;
}

View File

@ -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<l_get_version>},
{"new_world", lua::wrap<l_new_world>},
{"open_world", lua::wrap<l_open_world>},
{"reopen_world", lua::wrap<l_reopen_world>},

View File

@ -19,7 +19,7 @@ static fs::path resolve_path(const std::string& path) {
static fs::path resolve_path_soft(const std::string& path) {
if (path.find(':') == std::string::npos) {
return path;
return fs::u8path("");
}
return engine->getPaths()->resolve(path, false);
}

View File

@ -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<Player> get_player(lua::State* L, int idx) {
return level->getObject<Player>(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<l_get_pos>},
{"set_pos", lua::wrap<l_set_pos>},
@ -260,5 +275,7 @@ const luaL_Reg playerlib[] = {
{"set_entity", lua::wrap<l_set_entity>},
{"get_camera", lua::wrap<l_get_camera>},
{"set_camera", lua::wrap<l_set_camera>},
{"get_name", lua::wrap<l_get_name>},
{"set_name", lua::wrap<l_set_name>},
{NULL, NULL}
};

View File

@ -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;

View File

@ -68,7 +68,6 @@ void scripting::initialize(Engine* engine) {
lua::initialize(*engine->getPaths());
load_script(fs::path("stdlib.lua"), true);
load_script(fs::path("stdcmd.lua"), true);
load_script(fs::path("classes.lua"), true);
}
@ -196,6 +195,7 @@ void scripting::on_content_load(Content* content) {
lua::setfield(L, "properties");
lua::pop(L);
}
load_script(fs::path("stdcmd.lua"), true);
}
void scripting::on_world_load(LevelController* controller) {
@ -308,6 +308,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
) {
@ -725,6 +751,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 =
@ -776,6 +804,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 =

View File

@ -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
);

View File

@ -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<Inventory> 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<Inventory>& 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);

View File

@ -4,7 +4,6 @@
#include <memory>
#include <optional>
#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<Camera> fpCamera, spCamera, tpCamera;
std::shared_ptr<Camera> currentCamera;
@ -62,6 +63,8 @@ public:
Player(
Level* level,
int64_t id,
const std::string& name,
glm::vec3 position,
float speed,
std::shared_ptr<Inventory> 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<Inventory>& 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;
}
};

73
src/objects/Players.cpp Normal file
View File

@ -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> 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<Player>(
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<Player>(
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());
}
}

39
src/objects/Players.hpp Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <memory>
#include <unordered_map>
#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<int64_t, std::unique_ptr<Player>> players;
void addPlayer(std::unique_ptr<Player> 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();
}
};

View File

@ -20,9 +20,9 @@ struct NotePreset : public Serializable {
NoteDisplayMode displayMode = NoteDisplayMode::STATIC_BILLBOARD;
glm::vec4 color {1.0f};
float scale = 1.0f;
float renderDistance = 10.0f;
float renderDistance = 32.0f;
float xrayOpacity = 0.0f;
float perspective = 0.0f;
float perspective = 1.0f;
dv::value serialize() const override;
void deserialize(const dv::value& src) override;

View File

@ -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;

View File

@ -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<PhysicsSolver>(glm::vec3(0, -22.6f, 0))),
events(std::make_unique<LevelEvents>()),
entities(std::make_unique<Entities>(this)),
players(std::make_unique<Players>(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<Camera>();
@ -51,12 +53,6 @@ Level::Level(
if (worldInfo.nextEntityId) {
entities->setNextID(worldInfo.nextEntityId);
}
auto inv = std::make_shared<Inventory>(
world->getNextInventoryId(), DEF_PLAYER_INVENTORY_SIZE
);
auto player = spawnObject<Player>(
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<Inventories>(*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);

View File

@ -2,12 +2,10 @@
#include <memory>
#include <vector>
#include <string>
#include <unordered_map>
#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> world;
public:
const Content* const content;
std::vector<std::shared_ptr<Object>> objects;
std::unique_ptr<Chunks> chunks;
std::unique_ptr<ChunksStorage> chunksStorage;
std::unique_ptr<Inventories> inventories;
@ -35,6 +34,7 @@ public:
std::unique_ptr<Lighting> lighting;
std::unique_ptr<LevelEvents> events;
std::unique_ptr<Entities> entities;
std::unique_ptr<Players> players;
std::vector<std::shared_ptr<Camera>> 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 <class T, typename... Args>
std::shared_ptr<T> spawnObject(Args&&... args) {
static_assert(
std::is_base_of<Object, T>::value,
"T must be a derived of Object class"
);
std::shared_ptr<T> tObj = std::make_shared<T>(args...);
std::shared_ptr<Object> obj =
std::dynamic_pointer_cast<Object, T>(tObj);
obj->objectUID = objects.size();
objects.push_back(obj);
obj->spawned();
return tObj;
}
template <class T>
std::shared_ptr<T> getObject(uint64_t id) const {
static_assert(
std::is_base_of<Object, T>::value,
"T must be a derived of Object class"
);
if (id >= objects.size()) return nullptr;
std::shared_ptr<T> object = std::dynamic_pointer_cast<T>(objects[id]);
return object;
}
void onSave();
std::shared_ptr<Camera> getCamera(const std::string& name);

View File

@ -11,6 +11,7 @@
#include "items/Inventories.hpp"
#include "objects/Entities.hpp"
#include "objects/Player.hpp"
#include "objects/Players.hpp"
#include "settings.hpp"
#include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp"
@ -69,13 +70,7 @@ void World::write(Level* level) {
info.nextEntityId = level->entities->peekNextID();
wfile->write(this, content);
auto playerFile = dv::object();
auto& players = playerFile.list("players");
for (const auto& object : level->objects) {
if (auto player = std::dynamic_pointer_cast<Player>(object)) {
players.add(player->serialize());
}
}
auto playerFile = level->players->serialize();
files::write_json(wfile->getPlayerFile(), playerFile);
writeResources(content);
@ -100,7 +95,9 @@ std::unique_ptr<Level> World::create(
content,
packs
);
return std::make_unique<Level>(std::move(world), content, settings);
auto level = std::make_unique<Level>(std::move(world), content, settings);
level->players->create();
return level;
}
std::unique_ptr<Level> World::load(
@ -121,45 +118,23 @@ std::unique_ptr<Level> World::load(
<< " generator: " << info->generator;
auto world = std::make_unique<World>(
info.value(),
std::move(worldFilesPtr),
content,
packs
info.value(), std::move(worldFilesPtr), content, packs
);
auto& wfile = world->wfile;
wfile->readResourcesData(content);
auto level = std::make_unique<Level>(std::move(world), content, settings);
{
fs::path file = wfile->getPlayerFile();
if (!fs::is_regular_file(file)) {
logger.warning() << "player.json does not exists";
} else {
auto playerRoot = files::read_json(file);
if (playerRoot.has("players")) {
level->objects.clear();
const auto& players = playerRoot["players"];
for (auto& playerMap : players) {
auto player = level->spawnObject<Player>(
level.get(),
glm::vec3(0, DEF_PLAYER_Y, 0),
DEF_PLAYER_SPEED,
level->inventories->create(DEF_PLAYER_INVENTORY_SIZE),
0
);
player->deserialize(playerMap);
auto& inventory = player->getInventory();
// invalid inventory id pre 0.25
if (inventory->getId() == 0) {
inventory->setId(level->getWorld()->getNextInventoryId());
}
level->inventories->store(player->getInventory());
}
} else {
auto player = level->getObject<Player>(0);
player->deserialize(playerRoot);
level->inventories->store(player->getInventory());
}
fs::path file = wfile->getPlayerFile();
if (!fs::is_regular_file(file)) {
logger.warning() << "player.json does not exists";
level->players->create();
} else {
auto playerRoot = files::read_json(file);
level->players->deserialize(playerRoot);
if (!playerRoot["players"][0].has("id")) {
level->getWorld()->getInfo().nextPlayerId++;
}
}
return level;
@ -231,6 +206,7 @@ void WorldInfo::deserialize(const dv::value& root) {
}
nextInventoryId = root["next-inventory-id"].asInteger(2);
nextEntityId = root["next-entity-id"].asInteger(1);
root.at("next-player-id").get(nextPlayerId);
}
dv::value WorldInfo::serialize() const {
@ -254,5 +230,6 @@ dv::value WorldInfo::serialize() const {
root["next-inventory-id"] = nextInventoryId;
root["next-entity-id"] = nextEntityId;
root["next-player-id"] = nextPlayerId;
return root;
}

View File

@ -28,6 +28,7 @@ struct WorldInfo : public Serializable {
std::string generator;
uint64_t seed;
int64_t nextInventoryId = 1;
int64_t nextPlayerId = 0;
/// @brief Day/night loop timer in range 0..1 where
/// 0.0 - is midnight and