Merge branch 'dev' into pathfinding

This commit is contained in:
MihailRis
2025-08-19 19:47:10 +03:00
57 changed files with 818 additions and 207 deletions
-1
View File
@@ -174,7 +174,6 @@ std::unique_ptr<model::Model> vcm::parse(
"'model' tag expected as root, got '" + root.getTag() + "'"
);
}
std::cout << xml::stringify(*doc) << std::endl;
return load_model(root);
} catch (const parsing_error& err) {
throw std::runtime_error(err.errorLog());
+5 -4
View File
@@ -416,19 +416,20 @@ void ContentLoader::load() {
template <class T>
static void load_script(const Content& content, T& def) {
const auto& name = def.name;
size_t pos = name.find(':');
const auto& scriptName = def.scriptFile;
if (scriptName.empty()) return;
size_t pos = scriptName.find(':');
if (pos == std::string::npos) {
throw std::runtime_error("invalid content unit name");
}
const auto runtime = content.getPackRuntime(name.substr(0, pos));
const auto runtime = content.getPackRuntime(scriptName.substr(0, pos));
const auto& pack = runtime->getInfo();
const auto& folder = pack.folder;
auto scriptfile = folder / ("scripts/" + def.scriptName + ".lua");
if (io::is_regular_file(scriptfile)) {
scripting::load_content_script(
runtime->getEnvironment(),
name,
def.name,
scriptfile,
def.scriptFile,
def.rt.funcsset
+3
View File
@@ -1,6 +1,9 @@
#include "Project.hpp"
#include "data/dv_util.hpp"
#include "logic/scripting/scripting.hpp"
Project::~Project() = default;
dv::value Project::serialize() const {
return dv::object({
+8
View File
@@ -2,13 +2,21 @@
#include <string>
#include <vector>
#include <memory>
#include "interfaces/Serializable.hpp"
namespace scripting {
class IClientProjectScript;
}
struct Project : Serializable {
std::string name;
std::string title;
std::vector<std::string> basePacks;
std::unique_ptr<scripting::IClientProjectScript> clientScript;
~Project();
dv::value serialize() const override;
void deserialize(const dv::value& src) override;
+89 -56
View File
@@ -60,6 +60,17 @@ static std::unique_ptr<ImageData> load_icon() {
return nullptr;
}
static std::unique_ptr<scripting::IClientProjectScript> load_client_project_script() {
io::path scriptFile = "project:project_client.lua";
if (io::exists(scriptFile)) {
logger.info() << "starting project script";
return scripting::load_client_project_script(scriptFile);
} else {
logger.warning() << "project script does not exists";
}
return nullptr;
}
Engine::Engine() = default;
Engine::~Engine() = default;
@@ -72,6 +83,68 @@ Engine& Engine::getInstance() {
return *instance;
}
void Engine::onContentLoad() {
editor->loadTools();
langs::setup(langs::get_current(), paths.resPaths.collectRoots());
if (isHeadless()) {
return;
}
for (auto& pack : content->getAllContentPacks()) {
auto configFolder = pack.folder / "config";
auto bindsFile = configFolder / "bindings.toml";
if (io::is_regular_file(bindsFile)) {
input->getBindings().read(
toml::parse(
bindsFile.string(), io::read_string(bindsFile)
),
BindType::BIND
);
}
}
loadAssets();
}
void Engine::initializeClient() {
std::string title = project->title;
if (title.empty()) {
title = "VoxelCore v" +
std::to_string(ENGINE_VERSION_MAJOR) + "." +
std::to_string(ENGINE_VERSION_MINOR);
}
if (ENGINE_DEBUG_BUILD) {
title += " [debug]";
}
auto [window, input] = Window::initialize(&settings.display, title);
if (!window || !input){
throw initialize_error("could not initialize window");
}
window->setFramerate(settings.display.framerate.get());
time.set(window->time());
if (auto icon = load_icon()) {
icon->flipY();
window->setIcon(icon.get());
}
this->window = std::move(window);
this->input = std::move(input);
loadControls();
gui = std::make_unique<gui::GUI>(*this);
if (ENGINE_DEBUG_BUILD) {
menus::create_version_label(*gui);
}
keepAlive(settings.display.fullscreen.observe(
[this](bool value) {
if (value != this->window->isFullscreen()) {
this->window->toggleFullscreen();
}
},
true
));
}
void Engine::initialize(CoreParameters coreParameters) {
params = std::move(coreParameters);
settingsHandler = std::make_unique<SettingsHandler>(settings);
@@ -100,78 +173,28 @@ void Engine::initialize(CoreParameters coreParameters) {
controller = std::make_unique<EngineController>(*this);
if (!params.headless) {
std::string title = project->title;
if (title.empty()) {
title = "VoxelCore v" +
std::to_string(ENGINE_VERSION_MAJOR) + "." +
std::to_string(ENGINE_VERSION_MINOR);
}
if (ENGINE_DEBUG_BUILD) {
title += " [debug]";
}
auto [window, input] = Window::initialize(&settings.display, title);
if (!window || !input){
throw initialize_error("could not initialize window");
}
window->setFramerate(settings.display.framerate.get());
time.set(window->time());
if (auto icon = load_icon()) {
icon->flipY();
window->setIcon(icon.get());
}
this->window = std::move(window);
this->input = std::move(input);
loadControls();
gui = std::make_unique<gui::GUI>(*this);
if (ENGINE_DEBUG_BUILD) {
menus::create_version_label(*gui);
}
keepAlive(settings.display.fullscreen.observe(
[this](bool value) {
if (value != this->window->isFullscreen()) {
this->window->toggleFullscreen();
}
},
true
));
initializeClient();
}
audio::initialize(!params.headless, settings.audio);
bool langNotSet = settings.ui.language.get() == "auto";
if (langNotSet) {
if (settings.ui.language.get() == "auto") {
settings.ui.language.set(
langs::locale_by_envlocale(platform::detect_locale())
);
}
content = std::make_unique<ContentControl>(*project, paths, *input, [this]() {
editor->loadTools();
langs::setup(langs::get_current(), paths.resPaths.collectRoots());
if (!isHeadless()) {
for (auto& pack : content->getAllContentPacks()) {
auto configFolder = pack.folder / "config";
auto bindsFile = configFolder / "bindings.toml";
if (io::is_regular_file(bindsFile)) {
input->getBindings().read(
toml::parse(
bindsFile.string(), io::read_string(bindsFile)
),
BindType::BIND
);
}
}
loadAssets();
}
onContentLoad();
});
scripting::initialize(this);
if (!isHeadless()) {
gui->setPageLoader(scripting::create_page_loader());
}
keepAlive(settings.ui.language.observe([this](auto lang) {
langs::setup(lang, paths.resPaths.collectRoots());
}, true));
project->clientScript = load_client_project_script();
}
void Engine::loadSettings() {
@@ -286,6 +309,7 @@ void Engine::close() {
audio::close();
network.reset();
clearKeepedObjects();
project.reset();
scripting::close();
logger.info() << "scripting finished";
if (!params.headless) {
@@ -345,10 +369,19 @@ void Engine::loadProject() {
}
void Engine::setScreen(std::shared_ptr<Screen> screen) {
if (project->clientScript && this->screen) {
project->clientScript->onScreenChange(this->screen->getName(), false);
}
// reset audio channels (stop all sources)
audio::reset_channel(audio::get_channel_index("regular"));
audio::reset_channel(audio::get_channel_index("ambient"));
this->screen = std::move(screen);
if (this->screen) {
this->screen->onOpen();
}
if (project->clientScript && this->screen) {
project->clientScript->onScreenChange(this->screen->getName(), true);
}
}
void Engine::onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer) {
+7
View File
@@ -82,6 +82,9 @@ class Engine : public util::ObjectsKeeper {
void updateHotkeys();
void loadAssets();
void loadProject();
void initializeClient();
void onContentLoad();
public:
Engine();
~Engine();
@@ -174,4 +177,8 @@ public:
devtools::Editor& getEditor() {
return *editor;
}
const Project& getProject() {
return *project;
}
};
+4
View File
@@ -2,10 +2,13 @@
#include "Engine.hpp"
#include "debug/Logger.hpp"
#include "devtools/Project.hpp"
#include "frontend/screens/MenuScreen.hpp"
#include "frontend/screens/LevelScreen.hpp"
#include "window/Window.hpp"
#include "world/Level.hpp"
#include "graphics/ui/GUI.hpp"
#include "graphics/ui/elements/Container.hpp"
static debug::Logger logger("mainloop");
@@ -36,6 +39,7 @@ void Mainloop::run() {
while (!window.isShouldClose()){
time.update(window.time());
engine.updateFrontend();
if (!window.isIconified()) {
engine.renderFrame();
}
+3 -1
View File
@@ -14,11 +14,13 @@ UiDocument::UiDocument(
const std::shared_ptr<gui::UINode>& root,
scriptenv env
) : id(std::move(id)), script(script), root(root), env(std::move(env)) {
gui::UINode::getIndices(root, map);
rebuildIndices();
}
void UiDocument::rebuildIndices() {
map.clear();
gui::UINode::getIndices(root, map);
map["root"] = root;
}
const UINodesMap& UiDocument::getMap() const {
+9 -5
View File
@@ -324,7 +324,7 @@ void Hud::updateWorldGenDebug() {
void Hud::update(bool visible) {
const auto& chunks = *player.chunks;
bool is_menu_open = menu.hasOpenPage();
bool isMenuOpen = menu.hasOpenPage();
debugPanel->setVisible(
debug && visible && !(inventoryOpen && inventoryView == nullptr)
@@ -333,13 +333,13 @@ void Hud::update(bool visible) {
if (!visible && inventoryOpen) {
closeInventory();
}
if (pause && !is_menu_open) {
if (pause && !isMenuOpen) {
setPause(false);
}
if (!gui.isFocusCaught()) {
processInput(visible);
}
if ((is_menu_open || inventoryOpen) == input.getCursor().locked) {
if ((isMenuOpen || inventoryOpen) == input.getCursor().locked) {
input.toggleCursor();
}
@@ -360,8 +360,8 @@ void Hud::update(bool visible) {
contentAccessPanel->setSize(glm::vec2(caSize.x, windowSize.y));
contentAccess->setMinSize(glm::vec2(1, windowSize.y));
hotbarView->setVisible(visible && !(secondUI && !inventoryView));
darkOverlay->setVisible(is_menu_open);
menu.setVisible(is_menu_open);
darkOverlay->setVisible(isMenuOpen);
menu.setVisible(isMenuOpen);
if (visible) {
for (auto& element : elements) {
@@ -538,6 +538,7 @@ void Hud::closeInventory() {
exchangeSlotInv = nullptr;
inventoryOpen = false;
inventoryView = nullptr;
secondInvView = nullptr;
secondUI = nullptr;
for (auto& element : elements) {
@@ -597,6 +598,9 @@ void Hud::remove(const std::shared_ptr<UINode>& node) {
}
}
cleanup();
if (node == secondUI) {
closeInventory();
}
}
void Hud::setDebug(bool flag) {
+4 -1
View File
@@ -97,7 +97,6 @@ LevelScreen::LevelScreen(
animator->addAnimations(assets.getAnimations());
loadDecorations();
initializeContent();
}
LevelScreen::~LevelScreen() {
@@ -112,6 +111,10 @@ LevelScreen::~LevelScreen() {
engine.getPaths().setCurrentWorldFolder("");
}
void LevelScreen::onOpen() {
initializeContent();
}
void LevelScreen::initializeContent() {
auto& content = controller->getLevel()->content;
for (auto& entry : content.getPacks()) {
+5
View File
@@ -53,8 +53,13 @@ public:
);
~LevelScreen();
void onOpen() override;
void update(float delta) override;
void draw(float delta) override;
void onEngineShutdown() override;
const char* getName() const override {
return "level";
}
};
+7 -29
View File
@@ -13,12 +13,6 @@
#include "engine/Engine.hpp"
MenuScreen::MenuScreen(Engine& engine) : Screen(engine) {
engine.getContentControl().resetContent();
auto menu = engine.getGUI().getMenu();
menu->reset();
menu->setPage("main");
uicamera =
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
uicamera->perspective = false;
@@ -29,33 +23,17 @@ MenuScreen::MenuScreen(Engine& engine) : Screen(engine) {
MenuScreen::~MenuScreen() = default;
void MenuScreen::onOpen() {
engine.getContentControl().resetContent();
auto menu = engine.getGUI().getMenu();
menu->reset();
}
void MenuScreen::update(float delta) {
}
void MenuScreen::draw(float delta) {
auto assets = engine.getAssets();
display::clear();
display::setBgColor(glm::vec3(0.2f));
const auto& size = engine.getWindow().getSize();
uint width = size.x;
uint height = size.y;
uicamera->setFov(height);
uicamera->setAspectRatio(width / static_cast<float>(height));
auto uishader = assets->get<Shader>("ui");
uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjView());
auto bg = assets->get<Texture>("gui/menubg");
batch->begin();
batch->texture(bg);
batch->rect(
0, 0,
width, height, 0, 0, 0,
UVRegion(0, 0, width / bg->getWidth(), height / bg->getHeight()),
false, false, glm::vec4(1.0f)
);
batch->flush();
}
+6
View File
@@ -13,6 +13,12 @@ public:
MenuScreen(Engine& engine);
~MenuScreen();
void onOpen() override;
void update(float delta) override;
void draw(float delta) override;
const char* getName() const override {
return "menu";
}
};
+2
View File
@@ -13,7 +13,9 @@ protected:
public:
Screen(Engine& engine);
virtual ~Screen();
virtual void onOpen() = 0;
virtual void update(float delta) = 0;
virtual void draw(float delta) = 0;
virtual void onEngineShutdown() {};
virtual const char* getName() const = 0;
};
+9 -1
View File
@@ -126,7 +126,15 @@ void Batch3D::sprite(
float scale = 1.0f / static_cast<float>(atlasRes);
float u = (index % atlasRes) * scale;
float v = 1.0f - ((index / atlasRes) * scale) - scale;
sprite(pos, up, right, w, h, UVRegion(u, v, u+scale, v+scale), tint);
sprite(
pos + right * w + up * h, // revert centering
up,
right,
w,
h,
UVRegion(u, v, u + scale, v + scale),
tint
);
}
void Batch3D::sprite(
+3
View File
@@ -171,6 +171,9 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
if (mesh == nullptr) {
return nullptr;
}
if (chunk->flags.dirtyHeights) {
chunk->updateHeights();
}
if (culling) {
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
glm::vec3 max(
+6 -2
View File
@@ -51,6 +51,7 @@ void TextsRenderer::renderNote(
glm::vec3 yvec = note.getAxisY();
int width = font.calcWidth(text, text.length());
int height = font.getLineHeight();
if (preset.displayMode == NoteDisplayMode::Y_FREE_BILLBOARD ||
preset.displayMode == NoteDisplayMode::XY_FREE_BILLBOARD) {
xvec = camera.position - pos;
@@ -96,8 +97,11 @@ void TextsRenderer::renderNote(
pos = screenPos / screenPos.w;
}
} else if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f * preset.scale),
pos + xvec * (width * 0.5f * preset.scale))) {
} else if (!frustum.isBoxVisible(
pos - xvec * (width * 0.5f) * preset.scale,
pos + xvec * (width * 0.5f) * preset.scale +
yvec * static_cast<float>(height) * preset.scale
)) {
return;
}
auto color = preset.color;
+10 -9
View File
@@ -56,6 +56,13 @@ GUI::GUI(Engine& engine)
store("tooltip", tooltip);
store("tooltip.label", UINode::find(tooltip, "tooltip.label"));
container->add(tooltip);
rootDocument = std::make_unique<UiDocument>(
"core:root",
uidocscript {},
std::dynamic_pointer_cast<gui::UINode>(container),
nullptr
);
}
GUI::~GUI() = default;
@@ -74,15 +81,8 @@ std::shared_ptr<Menu> GUI::getMenu() {
}
void GUI::onAssetsLoad(Assets* assets) {
assets->store(
std::make_unique<UiDocument>(
"core:root",
uidocscript {},
std::dynamic_pointer_cast<gui::UINode>(container),
nullptr
),
"core:root"
);
rootDocument->rebuildIndices();
assets->store(rootDocument, "core:root");
}
void GUI::resetTooltip() {
@@ -302,6 +302,7 @@ bool GUI::isFocusCaught() const {
}
void GUI::add(std::shared_ptr<UINode> node) {
UINode::getIndices(node, rootDocument->getMapWriteable());
container->add(std::move(node));
}
+3
View File
@@ -22,6 +22,8 @@ namespace devtools {
class Editor;
}
class UiDocument;
/*
Some info about padding and margin.
Padding is element inner space, margin is outer
@@ -70,6 +72,7 @@ namespace gui {
std::shared_ptr<UINode> pressed;
std::shared_ptr<UINode> focus;
std::shared_ptr<UINode> tooltip;
std::shared_ptr<UiDocument> rootDocument;
std::unordered_map<std::string, std::shared_ptr<UINode>> storage;
std::unique_ptr<Camera> uicamera;
+80 -1
View File
@@ -810,6 +810,85 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) {
}
}
static int calc_indent(int linestart, std::wstring_view input) {
int indent = 0;
while (linestart + indent < input.length() &&
input[linestart + indent] == L' ')
indent++;
return indent;
}
void TextBox::onTab(bool shiftPressed) {
std::wstring indentStr = L" ";
if (!shiftPressed && getSelectionLength() == 0) {
paste(indentStr);
return;
}
if (getSelectionLength() == 0) {
selectionStart = caret;
selectionEnd = caret;
selectionOrigin = caret;
}
int lineA = getLineAt(selectionStart);
int lineB = getLineAt(selectionEnd);
int caretLine = getLineAt(caret);
size_t lineAStart = getLinePos(lineA);
size_t lineBStart = getLinePos(lineB);
size_t caretLineStart = getLinePos(caretLine);
size_t caretIndent = calc_indent(caretLineStart, input);
size_t aIndent = calc_indent(lineAStart, input);
size_t bIndent = calc_indent(lineBStart, input);
int lastSelectionStart = selectionStart;
int lastSelectionEnd = selectionEnd;
size_t lastCaret = caret;
auto combination = history->beginCombination();
resetSelection();
for (int line = lineA; line <= lineB; line++) {
size_t linestart = getLinePos(line);
int indent = calc_indent(linestart, input);
if (shiftPressed) {
if (indent >= indentStr.length()) {
setCaret(linestart);
select(linestart, linestart + indentStr.length());
eraseSelected();
}
} else {
setCaret(linestart);
paste(indentStr);
}
refreshLabel(); // todo: replace with textbox cache
}
int linestart = getLinePos(caretLine);
int linestartA = getLinePos(lineA);
int linestartB = getLinePos(lineB);
int la = lastSelectionStart - lineAStart;
int lb = lastSelectionEnd - lineBStart;
if (shiftPressed) {
setCaret(lastCaret - caretLineStart + linestart - std::min<int>(caretIndent, indentStr.length()));
selectionStart = la + linestartA - std::min<int>(std::min<int>(la, aIndent), indentStr.length());
selectionEnd = lb + linestartB - std::min<int>(std::min<int>(lb, bIndent), indentStr.length());
} else {
setCaret(lastCaret - caretLineStart + linestart + indentStr.length());
selectionStart = la + linestartA + indentStr.length();
selectionEnd = lb + linestartB + indentStr.length();
}
if (selectionOrigin == lastSelectionStart) {
selectionOrigin = selectionStart;
} else {
selectionOrigin = selectionEnd;
}
historian->sync();
}
void TextBox::refreshSyntax() {
if (!syntax.empty()) {
const auto& processor = gui.getEditor().getSyntaxProcessor();
@@ -868,7 +947,7 @@ void TextBox::performEditingKeyboardEvents(Keycode key) {
}
}
} else if (key == Keycode::TAB) {
paste(L" ");
onTab(shiftPressed);
} else if (key == Keycode::LEFT) {
stepLeft(shiftPressed, breakSelection);
} else if (key == Keycode::RIGHT) {
+2
View File
@@ -71,6 +71,8 @@ namespace gui {
void stepDefaultDown(bool shiftPressed, bool breakSelection);
void stepDefaultUp(bool shiftPressed, bool breakSelection);
void onTab(bool shiftPressed);
size_t normalizeIndex(int index);
int calcIndexAt(int x, int y) const;
+1
View File
@@ -89,6 +89,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
builder.section("debug");
builder.add("generator-test-mode", &settings.debug.generatorTestMode);
builder.add("do-write-lights", &settings.debug.doWriteLights);
builder.add("enable-experimental", &settings.debug.enableExperimental);
}
dv::value SettingsHandler::getValue(const std::string& name) const {
@@ -54,7 +54,7 @@ static int l_get_gravity_scale(lua::State* L) {
static int l_set_gravity_scale(lua::State* L) {
if (auto entity = get_entity(L, 1)) {
entity->getRigidbody().hitbox.gravityScale = lua::tonumber(L, 2);
entity->getRigidbody().hitbox.gravityScale = lua::tovec3(L, 2).y;
}
return 0;
}
@@ -23,6 +23,9 @@ static void load_texture(
}
static int l_load_texture(lua::State* L) {
if (lua::isstring(L, 3) && lua::require_lstring(L, 3) != "png") {
throw std::runtime_error("unsupportd image format");
}
if (lua::istable(L, 1)) {
lua::pushvalue(L, 1);
size_t size = lua::objlen(L, 1);
+1 -4
View File
@@ -101,12 +101,9 @@ static int l_set(lua::State* L) {
if (static_cast<size_t>(id) >= indices->blocks.count()) {
return 0;
}
int cx = floordiv<CHUNK_W>(x);
int cz = floordiv<CHUNK_D>(z);
if (!blocks_agent::get_chunk(*level->chunks, cx, cz)) {
if (!blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state))) {
return 0;
}
blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state));
auto chunksController = controller->getChunksController();
if (chunksController == nullptr) {
+5 -2
View File
@@ -79,9 +79,12 @@ static int l_textbox_paste(lua::State* L) {
static int l_container_add(lua::State* L) {
auto docnode = get_document_node(L);
if (docnode.document == nullptr) {
throw std::runtime_error("target document not found");
}
auto node = dynamic_cast<Container*>(docnode.node.get());
if (node == nullptr) {
return 0;
throw std::runtime_error("target container not found");
}
auto xmlsrc = lua::require_string(L, 2);
try {
@@ -99,7 +102,7 @@ static int l_container_add(lua::State* L) {
UINode::getIndices(subnode, docnode.document->getMapWriteable());
node->add(std::move(subnode));
} catch (const std::exception& err) {
throw std::runtime_error(err.what());
throw std::runtime_error("container:add(...): " + std::string(err.what()));
}
return 0;
}
+1 -1
View File
@@ -46,7 +46,7 @@ static int l_open(lua::State* L) {
}
return lua::pushinteger(L, hud->openInventory(
layout,
level->inventories->get(invid),
lua::isnoneornil(L, 3) ? nullptr : level->inventories->get(invid),
playerInventory
)->getId());
}
@@ -45,6 +45,12 @@ namespace {
template <SlotFunc func>
int wrap_slot(lua::State* L) {
if (lua::isnoneornil(L, 1)) {
throw std::runtime_error("inventory id is nil");
}
if (lua::isnoneornil(L, 2)) {
throw std::runtime_error("slot index is nil");
}
auto invid = lua::tointeger(L, 1);
auto slotid = lua::tointeger(L, 2);
auto& inv = get_inventory(invid);
+44 -3
View File
@@ -25,6 +25,7 @@
#include "voxels/Block.hpp"
#include "voxels/Chunk.hpp"
#include "world/Level.hpp"
#include "world/World.hpp"
#include "interfaces/Process.hpp"
using namespace scripting;
@@ -112,11 +113,47 @@ public:
}
};
std::unique_ptr<Process> scripting::start_coroutine(
class LuaProjectScript : public IClientProjectScript {
public:
LuaProjectScript(lua::State* L, scriptenv env) : L(L), env(std::move(env)) {}
void onScreenChange(const std::string& name, bool show) override {
if (!lua::pushenv(L, *env)) {
return;
}
if (!lua::getfield(L, "on_" + name + (show ? "_setup" : "_clear"))) {
lua::pop(L);
return;
}
lua::call_nothrow(L, 0, 0);
lua::pop(L);
}
private:
lua::State* L;
scriptenv env;
};
std::unique_ptr<IClientProjectScript> scripting::load_client_project_script(
const io::path& script
) {
auto L = lua::get_main_state();
if (lua::getglobal(L, "__vc_start_coroutine")) {
auto source = io::read_string(script);
auto env = create_environment(nullptr);
lua::pushenv(L, *env);
if (lua::getglobal(L, "__vc_app")) {
lua::setfield(L, "app");
}
lua::pop(L);
lua::loadbuffer(L, *env, source, script.name());
lua::call(L, 0);
return std::make_unique<LuaProjectScript>(L, std::move(env));
}
std::unique_ptr<Process> scripting::start_coroutine(const io::path& script) {
auto L = lua::get_main_state();
auto method = "__vc_start_coroutine";
if (lua::getglobal(L, method)) {
auto source = io::read_string(script);
lua::loadbuffer(L, 0, source, script.name());
if (lua::call(L, 1)) {
@@ -259,7 +296,11 @@ void scripting::on_world_load(LevelController* controller) {
}
for (auto& pack : content_control->getAllContentPacks()) {
lua::emit_event(L, pack.id + ":.worldopen");
lua::emit_event(L, pack.id + ":.worldopen", [](auto L) {
return lua::pushboolean(
L, !scripting::level->getWorld()->getInfo().isLoaded
);
});
}
}
+10 -1
View File
@@ -65,10 +65,19 @@ namespace scripting {
void process_post_runnables();
std::unique_ptr<Process> start_coroutine(
class IClientProjectScript {
public:
virtual ~IClientProjectScript() {}
virtual void onScreenChange(const std::string& name, bool show) = 0;
};
std::unique_ptr<IClientProjectScript> load_client_project_script(
const io::path& script
);
std::unique_ptr<Process> start_coroutine(const io::path& script);
void on_world_load(LevelController* controller);
void on_world_tick(int tps);
void on_world_save();
+2
View File
@@ -95,6 +95,8 @@ struct DebugSettings {
FlagSetting generatorTestMode {false};
/// @brief Write lights cache
FlagSetting doWriteLights {true};
/// @brief Enable experimental optimizations and features
FlagSetting enableExperimental {false};
};
struct UiSettings {
+1
View File
@@ -14,6 +14,7 @@ Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos) {
}
void Chunk::updateHeights() {
flags.dirtyHeights = false;
for (uint i = 0; i < CHUNK_VOL; i++) {
if (voxels[i].id != 0) {
bottom = i / (CHUNK_D * CHUNK_W);
+1
View File
@@ -37,6 +37,7 @@ public:
bool loadedLights : 1;
bool entities : 1;
bool blocksData : 1;
bool dirtyHeights : 1;
} flags {};
/// @brief Block inventories map where key is index of block in voxels array
+10 -8
View File
@@ -7,7 +7,7 @@
using namespace blocks_agent;
template <class Storage>
static inline void set_block(
static inline bool set_block(
Storage& chunks,
int32_t x,
int32_t y,
@@ -16,14 +16,14 @@ static inline void set_block(
blockstate state
) {
if (y < 0 || y >= CHUNK_H) {
return;
return false;
}
const auto& indices = chunks.getContentIndices();
int cx = floordiv<CHUNK_W>(x);
int cz = floordiv<CHUNK_D>(z);
Chunk* chunk = get_chunk(chunks, cx, cz);
if (chunk == nullptr) {
return;
return false;
}
int lx = x - cx * CHUNK_W;
int lz = z - cz * CHUNK_D;
@@ -60,7 +60,8 @@ static inline void set_block(
else if (y + 1 > chunk->top)
chunk->top = y + 1;
else if (id == 0)
chunk->updateHeights();
chunk->flags.dirtyHeights = true;
if (lx == 0 && (chunk = get_chunk(chunks, cx - 1, cz))) {
chunk->flags.modified = true;
@@ -74,9 +75,10 @@ static inline void set_block(
if (lz == CHUNK_D - 1 && (chunk = get_chunk(chunks, cx, cz + 1))) {
chunk->flags.modified = true;
}
return true;
}
void blocks_agent::set(
bool blocks_agent::set(
Chunks& chunks,
int32_t x,
int32_t y,
@@ -84,10 +86,10 @@ void blocks_agent::set(
uint32_t id,
blockstate state
) {
set_block(chunks, x, y, z, id, state);
return set_block(chunks, x, y, z, id, state);
}
void blocks_agent::set(
bool blocks_agent::set(
GlobalChunks& chunks,
int32_t x,
int32_t y,
@@ -95,7 +97,7 @@ void blocks_agent::set(
uint32_t id,
blockstate state
) {
set_block(chunks, x, y, z, id, state);
return set_block(chunks, x, y, z, id, state);
}
template <class Storage>
+2 -2
View File
@@ -119,7 +119,7 @@ inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32
/// @param z block position Z
/// @param id new block id
/// @param state new block state
void set(
bool set(
Chunks& chunks,
int32_t x,
int32_t y,
@@ -135,7 +135,7 @@ void set(
/// @param z block position Z
/// @param id new block id
/// @param state new block state
void set(
bool set(
GlobalChunks& chunks,
int32_t x,
int32_t y,
+2
View File
@@ -116,6 +116,8 @@ std::unique_ptr<Level> World::load(
if (!info.has_value()) {
throw world_load_error("could not to find world.json");
}
info->isLoaded = true;
logger.info() << "loading world " << info->name << " ("
<< worldFilesPtr->getFolder().string() << ")";
logger.info() << "world version: " << info->major << "." << info->minor
+2
View File
@@ -45,6 +45,8 @@ struct WorldInfo : public Serializable {
int major = 0, minor = -1;
bool isLoaded = false;
dv::value serialize() const override;
void deserialize(const dv::value& src) override;
};