Merge branch 'main' into volume-renderer

This commit is contained in:
MihailRis 2026-01-29 22:34:34 +03:00
commit d9a7afad05
42 changed files with 539 additions and 196 deletions

View file

@ -5,6 +5,13 @@ A library for working with audio/visual assets.
## Functions ## Functions
```lua ```lua
-- Requests background texture loading
assets.request_texture(
-- Image file
filename: string,
-- Texture name after loading
name: string,
)
-- Loads a texture -- Loads a texture
assets.load_texture( assets.load_texture(
-- Array of bytes of an image file -- Array of bytes of an image file

View file

@ -167,10 +167,11 @@ Properties:
Properties: Properties:
| Name | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ------ | ------ | ---- | ----- | ---------------- | | -------- | ------ | ---- | ----- | ---------------- |
| src | string | yes | yes | texture name | | src | string | yes | yes | texture name |
| region | vec4 | yes | yes | image sub-region | | fallback | string | yes | yes | fallback texture |
| region | vec4 | yes | yes | image sub-region |
## Canvas ## Canvas

View file

@ -120,6 +120,7 @@ Inner text is a button text.
- `src` - name of an image stored in textures folder. Extension is not specified. Type: string. - `src` - name of an image stored in textures folder. Extension is not specified. Type: string.
Example: *gui/error* Example: *gui/error*
- `fallback` - a fallback texture displayed when the main texture is missing / loading.
- `region` - image region x1, y1, x2, y2 from 0.0, 0.0 (upper left corner), 1.0, 1.0 (lower right corner) - `region` - image region x1, y1, x2, y2 from 0.0, 0.0 (upper left corner), 1.0, 1.0 (lower right corner)
## *canvas* ## *canvas*

View file

@ -5,6 +5,14 @@
## Функции ## Функции
```lua ```lua
-- Запрашивает фоновую загрузку текстуры
assets.request_texture(
-- Файл изображения
filename: string,
-- Имя текстуры после загрузки
name: string,
)
-- Загружает текстуру -- Загружает текстуру
assets.load_texture( assets.load_texture(
-- Массив байт файла изображения -- Массив байт файла изображения

View file

@ -170,6 +170,7 @@ document["worlds-panel"]:clear()
| Название | Тип | Чтение | Запись | Описание | | Название | Тип | Чтение | Запись | Описание |
| -------- | ------ | ------ | ------ | ---------------------- | | -------- | ------ | ------ | ------ | ---------------------- |
| src | string | да | да | отображаемая текстура | | src | string | да | да | отображаемая текстура |
| fallback | string | да | да | резервная текстура |
| region | vec4 | да | да | под-регион изображения | | region | vec4 | да | да | под-регион изображения |
## Холст (canvas) ## Холст (canvas)

View file

@ -121,6 +121,7 @@
## Изображение - *image* ## Изображение - *image*
- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error` - `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
- `fallback` - резервная текстура, отображаемая при отсутствии / фоновой загрузке основной
- `region` - под-регион изображения x1, y1, x2, y2 от 0.0, 0.0 (левый верхний угол), 1.0, 1.0 (правый нижний угол) - `region` - под-регион изображения x1, y1, x2, y2 от 0.0, 0.0 (левый верхний угол), 1.0, 1.0 (правый нижний угол)
## Холст - *canvas* ## Холст - *canvas*

View file

@ -18,7 +18,7 @@ function ores.place(placements, x, z, w, d, seed, hmap, chunk_height)
for i=1,count do for i=1,count do
local sx = math.random() * w local sx = math.random() * w
local sz = math.random() * d local sz = math.random() * d
local sy = math.random() * (chunk_height * 0.5) local sy = math.random() * (chunk_height * 0.5) + 6
if sy < hmap:at(sx, sz) * chunk_height - 6 then if sy < hmap:at(sx, sz) * chunk_height - 6 then
table.insert(placements, {ore.struct, {sx, sy, sz}, math.random()*4, -1}) table.insert(placements, {ore.struct, {sx, sy, sz}, math.random()*4, -1})
end end

View file

@ -28,7 +28,9 @@ local function include(id, is_include)
end end
function apply() function apply()
core.reconfig_packs(add_packs, rem_packs) time.post_runnable(function ()
app.reconfig_packs(add_packs, rem_packs)
end)
if mode ~= "world" then if mode ~= "world" then
menu:back() menu:back()
end end

View file

@ -11,5 +11,5 @@
<label ifnot='%{error}' pos='80,28' size='355,50' multiline='true' color='#FFFFFFB2'> <label ifnot='%{error}' pos='80,28' size='355,50' multiline='true' color='#FFFFFFB2'>
%{description} %{description}
</label> </label>
<image pos='8,8' src='%{icon}' size='64'/> <image pos='8,8' src='%{icon}' fallback='gui/no_icon' size='64'/>
</container> </container>

View file

@ -4,7 +4,7 @@
hover-color='#162B3399' hover-color='#162B3399'
onclick='app.open_world("%{name}")' onclick='app.open_world("%{name}")'
> >
<image pos='1' src='%{icon}' size='96,64'></image> <image pos='1' src='%{icon}' fallback='gui/no_world_icon' size='96,64'></image>
<label pos='104,8'>%{name}</label> <label pos='104,8'>%{name}</label>
<button margin='4' <button margin='4'
gravity='center-right' gravity='center-right'

View file

@ -25,6 +25,7 @@ function tb_frame_tostring(frame)
end end
local __vc__app_script_coroutine local __vc__app_script_coroutine
local __vc__is_post_runnable = false
local function complete_app_lib(app) local function complete_app_lib(app)
local __app_load_content = app.load_content local __app_load_content = app.load_content
@ -44,6 +45,10 @@ local function complete_app_lib(app)
app.tick = __app_tick app.tick = __app_tick
local function call_in_app_script_co(func, ...) local function call_in_app_script_co(func, ...)
if __vc__is_post_runnable then
func(...)
return
end
local running = coroutine.running() local running = coroutine.running()
if not running or running ~= __vc__app_script_coroutine then if not running or running ~= __vc__app_script_coroutine then
error("content must be reload in application script coroutine") error("content must be reload in application script coroutine")
@ -513,7 +518,7 @@ local fn_audio_reset_fetch_buffer = audio.__reset_fetch_buffer
audio.__reset_fetch_buffer = nil audio.__reset_fetch_buffer = nil
core.get_core_token = audio.input.__get_core_token core.get_core_token = audio.input.__get_core_token
function __process_post_runnables() local function __vc__process_post_runnables()
if #__post_runnables then if #__post_runnables then
for _, func in ipairs(__post_runnables) do for _, func in ipairs(__post_runnables) do
local status, result = xpcall(func, __vc__error) local status, result = xpcall(func, __vc__error)
@ -547,6 +552,15 @@ function __process_post_runnables()
end end
end end
function __process_post_runnables()
__vc__is_post_runnable = true
local success, err = pcall(__vc__process_post_runnables)
if not success then
debug.error("an error ocurred while processing post-runnables: ".. err)
end
__vc__is_post_runnable = false
end
function time.post_runnable(runnable) function time.post_runnable(runnable)
table.insert(__post_runnables, runnable) table.insert(__post_runnables, runnable)
end end

View file

@ -319,28 +319,31 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
} }
bool AssetsLoader::loadExternalTexture( bool AssetsLoader::loadExternalTexture(
Assets* assets, AssetsLoader& loader,
const std::string& name, const std::string& name,
const std::vector<io::path>& alternatives const std::vector<io::path>& alternatives
) { ) {
if (assets->get<Texture>(name) != nullptr) { if (loader.getAssets().get<Texture>(name) != nullptr) {
return true; return true;
} }
for (auto& path : alternatives) { for (auto& path : alternatives) {
if (io::exists(path)) { if (io::exists(path)) {
try { loader.add(
auto image = imageio::read(path); AssetType::TEXTURE,
assets->store(Texture::from(image.get()), name); (path.parent() / path.stem()).string(),
return true; name,
} catch (const std::exception& err) { nullptr
logger.error() << "error while loading external " );
<< path.string() << ": " << err.what(); return true;
}
} }
} }
return false; return false;
} }
Assets& AssetsLoader::getAssets() {
return assets;
}
Engine& AssetsLoader::getEngine() { Engine& AssetsLoader::getEngine() {
return engine; return engine;
} }
@ -368,14 +371,24 @@ public:
} }
}; };
std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone) { std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone, int maxWorkers) {
auto pool = auto pool =
std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>( std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>(
"assets-loader-pool", "assets-loader-pool",
[=]() { return std::make_unique<LoaderWorker>(this); }, [=]() { return std::make_unique<LoaderWorker>(this); },
[this](assetload::postfunc&& func) { func(&assets); } [this](const assetload::postfunc& func) { func(&assets); },
maxWorkers
); );
pool->setOnComplete(std::move(onDone)); pool->setOnComplete(std::move(onDone));
pool->setJobsSource([this]() -> std::optional<aloader_entry> {
if (entries.empty()) {
return std::nullopt;
}
aloader_entry entry = std::move(entries.front());
entries.pop();
return entry;
});
while (!entries.empty()) { while (!entries.empty()) {
aloader_entry entry = std::move(entries.front()); aloader_entry entry = std::move(entries.front());
entries.pop(); entries.pop();

View file

@ -113,7 +113,7 @@ public:
/// @throws assetload::error /// @throws assetload::error
void loadNext(); void loadNext();
std::shared_ptr<Task> startTask(runnable onDone); std::shared_ptr<Task> startTask(runnable onDone, int maxWorkers);
const ResPaths& getPaths() const; const ResPaths& getPaths() const;
aloader_func getLoader(AssetType tag); aloader_func getLoader(AssetType tag);
@ -124,10 +124,11 @@ public:
static void addDefaults(AssetsLoader& loader, const Content* content); static void addDefaults(AssetsLoader& loader, const Content* content);
static bool loadExternalTexture( static bool loadExternalTexture(
Assets* assets, AssetsLoader& loader,
const std::string& name, const std::string& name,
const std::vector<io::path>& alternatives const std::vector<io::path>& alternatives
); );
Assets& getAssets();
Engine& getEngine(); Engine& getEngine();
}; };

View file

@ -441,7 +441,8 @@ static void load_script(const Content& content, T& def) {
def.name, def.name,
scriptfile, scriptfile,
def.scriptFile, def.scriptFile,
def.rt.funcsset def.rt.funcsset,
def.rt.eventNames
); );
} }
} }

View file

@ -0,0 +1,92 @@
#include "AssetsManagement.hpp"
#include "assets/AssetsLoader.hpp"
#include "coders/GLSLExtension.hpp"
#include "content/Content.hpp"
#include "debug/Logger.hpp"
#include "engine/Engine.hpp"
#include "EnginePaths.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/render/ModelsGenerator.hpp"
#include "graphics/ui/GUI.hpp"
static debug::Logger logger("assets-management");
AssetsManagement::AssetsManagement(Engine& engine)
: engine(engine), settings(engine.getSettings()) {
}
AssetsManagement::~AssetsManagement() {
finishBackgroundLoader();
}
const Assets* AssetsManagement::getStorage() const {
return assets.get();
}
Assets* AssetsManagement::getStorage() {
return assets.get();
}
AssetsLoader& AssetsManagement::acquireBackgroundLoader() {
if (backgroundLoader) {
return *backgroundLoader;
}
if (assets == nullptr) {
throw std::runtime_error("no assets storage available");
}
backgroundLoader = std::make_unique<AssetsLoader>(
engine, *assets, engine.getResPaths()
);
backgroundLoaderTask = backgroundLoader->startTask(
nullptr, settings.system.maxBgAssetLoaders.get()
);
return *backgroundLoader;
}
void AssetsManagement::loadAssets(Content* content) {
finishBackgroundLoader();
const auto& paths = engine.getPaths();
logger.info() << "loading assets";
Shader::preprocessor->setPaths(&paths.resPaths);
auto new_assets = std::make_unique<Assets>();
AssetsLoader loader(engine, *new_assets, paths.resPaths);
AssetsLoader::addDefaults(loader, content);
// no need
// correct log messages order is more useful
// todo: before setting to true, check if GLSLExtension thread safe
bool threading = false; // look at three upper lines
if (threading) {
auto task = loader.startTask(
[=]() {}, 0
);
task->waitForEnd();
} else {
while (loader.hasNext()) {
loader.loadNext();
}
}
assets = std::move(new_assets);
if (content) {
ModelsGenerator::prepare(*content, *assets);
}
assets->setup();
engine.getGUI().onAssetsLoad(assets.get());
}
void AssetsManagement::update() {
if (backgroundLoaderTask) {
backgroundLoaderTask->update();
}
}
void AssetsManagement::finishBackgroundLoader() {
if (backgroundLoaderTask == nullptr) {
return;
}
backgroundLoaderTask.reset();
backgroundLoader.reset();
}

View file

@ -0,0 +1,33 @@
#pragma once
#include "assets/Assets.hpp"
#include <memory>
class AssetsLoader;
class Engine;
class Content;
class Task;
struct EngineSettings;
class AssetsManagement final {
public:
AssetsManagement(Engine& engine);
~AssetsManagement();
void loadAssets(Content* content);
void update();
Assets* getStorage();
const Assets* getStorage() const;
AssetsLoader& acquireBackgroundLoader();
private:
void finishBackgroundLoader();
Engine& engine;
const EngineSettings& settings;
std::unique_ptr<Assets> assets;
std::unique_ptr<AssetsLoader> backgroundLoader;
std::shared_ptr<Task> backgroundLoaderTask;
};

View file

@ -4,47 +4,45 @@
#define GLEW_STATIC #define GLEW_STATIC
#endif #endif
#include "debug/Logger.hpp" #include "AssetsManagement.hpp"
#include "assets/AssetsLoader.hpp"
#include "audio/audio.hpp" #include "audio/audio.hpp"
#include "coders/commons.hpp"
#include "coders/GLSLExtension.hpp" #include "coders/GLSLExtension.hpp"
#include "coders/toml.hpp" #include "coders/toml.hpp"
#include "coders/commons.hpp"
#include "devtools/Editor.hpp"
#include "devtools/Project.hpp"
#include "devtools/DebuggingServer.hpp"
#include "content/ContentControl.hpp" #include "content/ContentControl.hpp"
#include "core_defs.hpp" #include "core_defs.hpp"
#include "io/io.hpp" #include "debug/Logger.hpp"
#include "io/settings_io.hpp" #include "devtools/DebuggingServer.hpp"
#include "devtools/Editor.hpp"
#include "devtools/Project.hpp"
#include "EnginePaths.hpp"
#include "frontend/locale.hpp" #include "frontend/locale.hpp"
#include "frontend/menu.hpp" #include "frontend/menu.hpp"
#include "frontend/screens/Screen.hpp" #include "frontend/screens/Screen.hpp"
#include "graphics/render/ModelsGenerator.hpp"
#include "graphics/core/DrawContext.hpp" #include "graphics/core/DrawContext.hpp"
#include "graphics/core/Shader.hpp" #include "graphics/core/Shader.hpp"
#include "graphics/ui/GUI.hpp"
#include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/elements/Menu.hpp"
#include "logic/EngineController.hpp" #include "graphics/ui/GUI.hpp"
#include "io/io.hpp"
#include "io/settings_io.hpp"
#include "logic/CommandsInterpreter.hpp" #include "logic/CommandsInterpreter.hpp"
#include "logic/scripting/scripting.hpp" #include "logic/EngineController.hpp"
#include "logic/scripting/scripting_hud.hpp" #include "logic/scripting/scripting_hud.hpp"
#include "logic/scripting/scripting.hpp"
#include "Mainloop.hpp"
#include "network/Network.hpp" #include "network/Network.hpp"
#include "ServerMainloop.hpp"
#include "util/platform.hpp" #include "util/platform.hpp"
#include "util/stringutil.hpp"
#include "window/input.hpp" #include "window/input.hpp"
#include "window/Window.hpp" #include "window/Window.hpp"
#include "world/Level.hpp"
#include "Mainloop.hpp"
#include "ServerMainloop.hpp"
#include "WindowControl.hpp" #include "WindowControl.hpp"
#include "EnginePaths.hpp" #include "world/Level.hpp"
#include <iostream>
#include <assert.h> #include <assert.h>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <unordered_set> #include <unordered_set>
#include <functional> #include <functional>
#include <utility>
static debug::Logger logger("engine"); static debug::Logger logger("engine");
@ -84,6 +82,7 @@ void Engine::onContentLoad() {
} }
void Engine::initializeClient() { void Engine::initializeClient() {
assets = std::make_unique<AssetsManagement>(*this);
windowControl = std::make_unique<WindowControl>(*this); windowControl = std::make_unique<WindowControl>(*this);
auto [window, input] = windowControl->initialize(); auto [window, input] = windowControl->initialize();
@ -264,6 +263,7 @@ void Engine::applicationTick() {
void Engine::updateFrontend() { void Engine::updateFrontend() {
double delta = time.getDelta(); double delta = time.getDelta();
assets->update();
updateHotkeys(); updateHotkeys();
audio::update(delta); audio::update(delta);
gui->act(delta, window->getSize()); gui->act(delta, window->getSize());
@ -305,7 +305,7 @@ void Engine::renderFrame() {
screen->draw(time.getDelta()); screen->draw(time.getDelta());
DrawContext ctx(nullptr, *window, nullptr); DrawContext ctx(nullptr, *window, nullptr);
gui->draw(ctx, *assets); gui->draw(ctx, *assets->getStorage());
} }
void Engine::saveSettings() { void Engine::saveSettings() {
@ -363,33 +363,7 @@ void Engine::setLevelConsumer(OnWorldOpen levelConsumer) {
} }
void Engine::loadAssets() { void Engine::loadAssets() {
logger.info() << "loading assets"; assets->loadAssets(content->get());
Shader::preprocessor->setPaths(&paths->resPaths);
auto content = this->content->get();
auto new_assets = std::make_unique<Assets>();
AssetsLoader loader(*this, *new_assets, paths->resPaths);
AssetsLoader::addDefaults(loader, content);
// no need
// correct log messages order is more useful
// todo: before setting to true, check if GLSLExtension thread safe
bool threading = false; // look at three upper lines
if (threading) {
auto task = loader.startTask([=](){});
task->waitForEnd();
} else {
while (loader.hasNext()) {
loader.loadNext();
}
}
assets = std::move(new_assets);
if (content) {
ModelsGenerator::prepare(*content, *assets);
}
assets->setup();
gui->onAssetsLoad(assets.get());
} }
void Engine::loadProject() { void Engine::loadProject() {
@ -442,7 +416,11 @@ EngineSettings& Engine::getSettings() {
} }
Assets* Engine::getAssets() { Assets* Engine::getAssets() {
return assets.get(); return assets->getStorage();
}
AssetsLoader& Engine::acquireBackgroundLoader() {
return assets->acquireBackgroundLoader();
} }
EnginePaths& Engine::getPaths() { EnginePaths& Engine::getPaths() {

View file

@ -10,6 +10,8 @@
#include <string> #include <string>
class Assets; class Assets;
class AssetsLoader;
class AssetsManagement;
class ContentControl; class ContentControl;
class EngineController; class EngineController;
class EnginePaths; class EnginePaths;
@ -52,7 +54,7 @@ class Engine : public util::ObjectsKeeper {
std::unique_ptr<EnginePaths> paths; std::unique_ptr<EnginePaths> paths;
std::unique_ptr<Project> project; std::unique_ptr<Project> project;
std::unique_ptr<SettingsHandler> settingsHandler; std::unique_ptr<SettingsHandler> settingsHandler;
std::unique_ptr<Assets> assets; std::unique_ptr<AssetsManagement> assets;
std::shared_ptr<Screen> screen; std::shared_ptr<Screen> screen;
std::unique_ptr<ContentControl> content; std::unique_ptr<ContentControl> content;
std::unique_ptr<EngineController> controller; std::unique_ptr<EngineController> controller;
@ -109,6 +111,8 @@ public:
/// @brief Get active assets storage instance /// @brief Get active assets storage instance
Assets* getAssets(); Assets* getAssets();
AssetsLoader& acquireBackgroundLoader();
/// @brief Get writeable engine settings structure instance /// @brief Get writeable engine settings structure instance
EngineSettings& getSettings(); EngineSettings& getSettings();

View file

@ -274,6 +274,9 @@ ResPaths::ResPaths(std::vector<PathsRoot> roots)
} }
io::path ResPaths::find(const std::string& filename) const { io::path ResPaths::find(const std::string& filename) const {
if (filename.find(':') != std::string::npos) {
return filename;
}
for (int i = roots.size() - 1; i >= 0; i--) { for (int i = roots.size() - 1; i >= 0; i--) {
auto& root = roots[i]; auto& root = roots[i];
auto file = root.path / filename; auto file = root.path / filename;

View file

@ -741,7 +741,7 @@ void BlocksRenderer::build(
size_t endIndex = indexCount; size_t endIndex = indexCount;
denseRender = true; denseRender = true;
densePass = true; densePass = settings.graphics.denseRender.get();
render(voxels, beginEnds); render(voxels, beginEnds);
denseIndexCount = indexCount; denseIndexCount = indexCount;

View file

@ -3,6 +3,7 @@
#include <utility> #include <utility>
#include "assets/Assets.hpp" #include "assets/Assets.hpp"
#include "assets/assets_util.hpp"
#include "graphics/core/Atlas.hpp" #include "graphics/core/Atlas.hpp"
#include "graphics/core/Batch2D.hpp" #include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp" #include "graphics/core/DrawContext.hpp"
@ -16,37 +17,25 @@ Image::Image(GUI& gui, std::string texture, glm::vec2 size)
setInteractive(false); setInteractive(false);
} }
util::TextureRegion Image::refreshTexture(const Assets& assets) {
auto region = util::get_texture_region(assets, texture, fallback);
if (region.texture && autoresize) {
setSize(glm::vec2(
region.texture->getWidth() * region.region.getWidth(),
region.texture->getHeight() * region.region.getHeight()
));
}
return region;
}
void Image::draw(const DrawContext& pctx, const Assets& assets) { void Image::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx.getBatch2D(); auto batch = pctx.getBatch2D();
Texture* texture = nullptr; auto textureRegion = refreshTexture(assets);
auto separator = this->texture.find(':');
if (separator == std::string::npos) { batch->setRegion(textureRegion.region);
texture = assets.get<Texture>(this->texture); batch->texture(textureRegion.texture);
batch->texture(texture);
if (texture && autoresize) {
setSize(glm::vec2(texture->getWidth(), texture->getHeight()));
}
} else {
auto atlasName = this->texture.substr(0, separator);
if (auto atlas = assets.get<Atlas>(atlasName)) {
if (auto region =
atlas->getIf(this->texture.substr(separator + 1))) {
texture = atlas->getTexture();
batch->texture(atlas->getTexture());
batch->setRegion(*region);
if (autoresize) {
setSize(glm::vec2(
texture->getWidth() * region->getWidth(),
texture->getHeight() * region->getHeight()
));
}
} else {
batch->texture(nullptr);
}
}
}
batch->rect( batch->rect(
pos.x, pos.x,
pos.y, pos.y,
@ -73,10 +62,18 @@ const std::string& Image::getTexture() const {
return texture; return texture;
} }
const std::string& Image::getFallback() const {
return fallback;
}
void Image::setTexture(const std::string& name) { void Image::setTexture(const std::string& name) {
texture = name; texture = name;
} }
void Image::setFallback(const std::string& name) {
fallback = name;
}
void Image::setRegion(const UVRegion& region) { void Image::setRegion(const UVRegion& region) {
this->region = region; this->region = region;
} }

View file

@ -3,21 +3,30 @@
#include "UINode.hpp" #include "UINode.hpp"
#include "maths/UVRegion.hpp" #include "maths/UVRegion.hpp"
namespace util {
struct TextureRegion;
}
namespace gui { namespace gui {
class Image : public UINode { class Image final : public UINode {
protected: protected:
std::string texture; std::string texture;
std::string fallback;
UVRegion region {}; UVRegion region {};
bool autoresize = false; bool autoresize = false;
util::TextureRegion refreshTexture(const Assets& assets);
public: public:
Image(GUI& gui, std::string texture, glm::vec2 size=glm::vec2(32,32)); Image(GUI& gui, std::string texture, glm::vec2 size=glm::vec2(32,32));
virtual void draw(const DrawContext& pctx, const Assets& assets) override; void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void setAutoResize(bool flag); void setAutoResize(bool flag);
virtual bool isAutoResize() const; bool isAutoResize() const;
virtual const std::string& getTexture() const; const std::string& getTexture() const;
virtual void setTexture(const std::string& name); const std::string& getFallback() const;
void setTexture(const std::string& name);
void setFallback(const std::string& name);
void setRegion(const UVRegion& region); void setRegion(const UVRegion& region);
const UVRegion& getRegion() const; const UVRegion& getRegion() const;
}; };

View file

@ -632,8 +632,10 @@ static std::shared_ptr<UINode> read_image(
const UiXmlReader& reader, const xml::xmlelement& element const UiXmlReader& reader, const xml::xmlelement& element
) { ) {
std::string src = element.attr("src", "").getText(); std::string src = element.attr("src", "").getText();
auto image = std::make_shared<Image>(reader.getGUI(), src); std::string fallback = element.attr("fallback", "").getText();
auto image = std::make_shared<Image>(reader.getGUI(), std::move(src));
read_uinode(reader, element, *image); read_uinode(reader, element, *image);
image->setFallback(std::move(fallback));
if (element.has("region")) { if (element.has("region")) {
auto vec = element.attr("region").asVec4(); auto vec = element.attr("region").asVec4();

View file

@ -94,6 +94,9 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
builder.add("do-write-lights", &settings.debug.doWriteLights); builder.add("do-write-lights", &settings.debug.doWriteLights);
builder.add("do-trace-shaders", &settings.debug.doTraceShaders); builder.add("do-trace-shaders", &settings.debug.doTraceShaders);
builder.add("enable-experimental", &settings.debug.enableExperimental); builder.add("enable-experimental", &settings.debug.enableExperimental);
builder.addSection("system");
builder.add("max-bg-asset-loaders", &settings.system.maxBgAssetLoaders);
} }
dv::value SettingsHandler::getValue(const std::string& name) const { dv::value SettingsHandler::getValue(const std::string& name) const {

View file

@ -15,6 +15,9 @@ struct ItemFuncsSet {
bool on_block_break_by : 1; bool on_block_break_by : 1;
}; };
struct ItemFuncNamesCache {
};
enum class ItemIconType { enum class ItemIconType {
NONE, // invisible (core:empty) must not be rendered NONE, // invisible (core:empty) must not be rendered
SPRITE, // textured quad: icon is `atlas_name:texture_name` SPRITE, // textured quad: icon is `atlas_name:texture_name`
@ -75,6 +78,8 @@ struct ItemDef {
bool emissive = false; bool emissive = false;
std::set<int> tags; std::set<int> tags;
ItemFuncNamesCache eventNames;
} rt {}; } rt {};
ItemDef(const std::string& name); ItemDef(const std::string& name);

View file

@ -1,6 +1,3 @@
#include <iostream>
#include <assert.h>
#include "LightSolver.hpp" #include "LightSolver.hpp"
#include "Lightmap.hpp" #include "Lightmap.hpp"
#include "content/Content.hpp" #include "content/Content.hpp"
@ -9,6 +6,8 @@
#include "voxels/voxel.hpp" #include "voxels/voxel.hpp"
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include <assert.h>
LightSolver::LightSolver(const ContentIndices& contentIds, Chunks& chunks, int channel) LightSolver::LightSolver(const ContentIndices& contentIds, Chunks& chunks, int channel)
: blockDefs(contentIds.blocks.getDefs()), : blockDefs(contentIds.blocks.getDefs()),
chunks(chunks), chunks(chunks),
@ -55,7 +54,7 @@ void LightSolver::remove(int x, int y, int z) {
lightmap.set(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel, 0); lightmap.set(x-chunk->x*CHUNK_W, y, z-chunk->z*CHUNK_D, channel, 0);
} }
void LightSolver::solve(){ void LightSolver::solve(Chunk* prevailingChunk) {
const int coords[] = { const int coords[] = {
0, 0, 1, 0, 0, 1,
0, 0,-1, 0, 0,-1,
@ -66,47 +65,54 @@ void LightSolver::solve(){
}; };
while (!remqueue.empty()){ while (!remqueue.empty()){
const lightentry entry = remqueue.front(); lightentry entry = std::move(remqueue.front());
remqueue.pop(); remqueue.pop();
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
int imul3 = i*3; int imul3 = i * 3;
int x = entry.x+coords[imul3]; int x = entry.x+coords[imul3];
int y = entry.y+coords[imul3+1]; int y = entry.y+coords[imul3+1];
int z = entry.z+coords[imul3+2]; int z = entry.z+coords[imul3+2];
Chunk* chunk = chunks.getChunkByVoxel(x,y,z); Chunk* chunk = prevailingChunk;
if (chunk) { if (chunk == nullptr || !chunk->isBlockInside(x, z)) {
int lx = x - chunk->x * CHUNK_W; chunk = chunks.getChunkByVoxel(x,y,z);
int lz = z - chunk->z * CHUNK_D; if (chunk == nullptr) {
chunk->flags.modified = true; continue;
}
} else if (y < 0 || y >= CHUNK_H) {
continue;
}
assert(chunk->lightmap != nullptr); int lx = x - chunk->x * CHUNK_W;
auto& lightmap = *chunk->lightmap; int lz = z - chunk->z * CHUNK_D;
chunk->flags.modified = true;
ubyte light = lightmap.get(lx,y,lz, channel); assert(chunk->lightmap != nullptr);
if (light != 0 && light == entry.light-1){ auto& lightmap = *chunk->lightmap;
voxel* vox = chunks.get(x, y, z);
if (vox && vox->id != 0) { ubyte light = lightmap.get(lx,y,lz, channel);
const Block* block = blockDefs[vox->id]; if (light != 0 && light == entry.light-1) {
if (uint8_t emission = block->emission[channel]) { voxel* vox = chunks.get(x, y, z);
addqueue.push(lightentry {x, y, z, emission}); if (vox && vox->id != 0) {
lightmap.set(lx, y, lz, channel, emission); const Block* block = blockDefs[vox->id];
} if (uint8_t emission = block->emission[channel]) {
else lightmap.set(lx, y, lz, channel, 0); addqueue.push(lightentry {x, y, z, emission});
lightmap.set(lx, y, lz, channel, emission);
} }
else lightmap.set(lx, y, lz, channel, 0); else lightmap.set(lx, y, lz, channel, 0);
remqueue.push(lightentry {x, y, z, light});
}
else if (light >= entry.light){
addqueue.push(lightentry {x, y, z, light});
} }
else lightmap.set(lx, y, lz, channel, 0);
remqueue.push(lightentry {x, y, z, light});
}
else if (light >= entry.light) {
addqueue.push(lightentry {x, y, z, light});
} }
} }
} }
while (!addqueue.empty()){ while (!addqueue.empty()){
const lightentry entry = addqueue.front(); lightentry entry = std::move(addqueue.front());
addqueue.pop(); addqueue.pop();
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
@ -115,10 +121,16 @@ void LightSolver::solve(){
int y = entry.y+coords[imul3+1]; int y = entry.y+coords[imul3+1];
int z = entry.z+coords[imul3+2]; int z = entry.z+coords[imul3+2];
Chunk* chunk = chunks.getChunkByVoxel(x,y,z); Chunk* chunk = prevailingChunk;
if (chunk == nullptr) { if (chunk == nullptr || !chunk->isBlockInside(x, z)) {
chunk = chunks.getChunkByVoxel(x,y,z);
if (chunk == nullptr) {
continue;
}
} else if (y < 0 || y >= CHUNK_H) {
continue; continue;
} }
assert(chunk->lightmap != nullptr); assert(chunk->lightmap != nullptr);
auto& lightmap = *chunk->lightmap; auto& lightmap = *chunk->lightmap;
int lx = x - chunk->x * CHUNK_W; int lx = x - chunk->x * CHUNK_W;

View file

@ -1,7 +1,8 @@
#pragma once #pragma once
#include <queue> #include "util/array_queue.hpp"
class Chunk;
class Chunks; class Chunks;
class ContentIndices; class ContentIndices;
class Block; class Block;
@ -14,8 +15,8 @@ struct lightentry {
}; };
class LightSolver { class LightSolver {
std::queue<lightentry> addqueue; util::array_queue<lightentry> addqueue;
std::queue<lightentry> remqueue; util::array_queue<lightentry> remqueue;
const Block* const* blockDefs; const Block* const* blockDefs;
Chunks& chunks; Chunks& chunks;
int channel; int channel;
@ -25,5 +26,5 @@ public:
void add(int x, int y, int z); void add(int x, int y, int z);
void add(int x, int y, int z, int emission); void add(int x, int y, int z, int emission);
void remove(int x, int y, int z); void remove(int x, int y, int z);
void solve(); void solve(Chunk* prevailingChunk = nullptr);
}; };

View file

@ -167,10 +167,10 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand) {
} }
} }
} }
solverR.solve(); solverR.solve(chunk);
solverG.solve(); solverG.solve(chunk);
solverB.solve(); solverB.solve(chunk);
solverS.solve(); solverS.solve(chunk);
} }
void Lighting::onBlockSet(int x, int y, int z, blockid_t id){ void Lighting::onBlockSet(int x, int y, int z, blockid_t id){
@ -180,9 +180,10 @@ void Lighting::onBlockSet(int x, int y, int z, blockid_t id){
solverB->remove(x,y,z); solverB->remove(x,y,z);
if (id == 0){ if (id == 0){
solverR->solve(); auto chunk = chunks.getChunkByVoxel(glm::ivec3{x, y, z});
solverG->solve(); solverR->solve(chunk);
solverB->solve(); solverG->solve(chunk);
solverB->solve(chunk);
if (chunks.getLight(x,y+1,z, 3) == 0xF){ if (chunks.getLight(x,y+1,z, 3) == 0xF){
for (int i = y; i >= 0; i--){ for (int i = y; i >= 0; i--){
voxel* vox = chunks.get(x,i,z); voxel* vox = chunks.get(x,i,z);
@ -191,17 +192,30 @@ void Lighting::onBlockSet(int x, int y, int z, blockid_t id){
solverS->add(x,i,z, 0xF); solverS->add(x,i,z, 0xF);
} }
} }
solverR->add(x,y+1,z); solverG->add(x,y+1,z); solverB->add(x,y+1,z); solverS->add(x,y+1,z); const int coords[] = {
solverR->add(x,y-1,z); solverG->add(x,y-1,z); solverB->add(x,y-1,z); solverS->add(x,y-1,z); 0, 0, 1,
solverR->add(x+1,y,z); solverG->add(x+1,y,z); solverB->add(x+1,y,z); solverS->add(x+1,y,z); 0, 0,-1,
solverR->add(x-1,y,z); solverG->add(x-1,y,z); solverB->add(x-1,y,z); solverS->add(x-1,y,z); 0, 1, 0,
solverR->add(x,y,z+1); solverG->add(x,y,z+1); solverB->add(x,y,z+1); solverS->add(x,y,z+1); 0,-1, 0,
solverR->add(x,y,z-1); solverG->add(x,y,z-1); solverB->add(x,y,z-1); solverS->add(x,y,z-1); 1, 0, 0,
solverR->solve(); -1, 0, 0
solverG->solve(); };
solverB->solve(); for (int i = 0; i < 6; i++) {
solverS->solve(); int lx = x + coords[i * 3];
int ly = y + coords[i * 3 + 1];
int lz = z + coords[i * 3 + 2];
solverR->add(lx, ly, lz);
solverG->add(lx, ly, lz);
solverB->add(lx, ly, lz);
solverS->add(lx, ly, lz);
solverR->solve(chunk);
solverG->solve(chunk);
solverB->solve(chunk);
solverS->solve(chunk);
}
} else { } else {
auto chunk = chunks.getChunkByVoxel(glm::ivec3{x, y, z});
if (!block.skyLightPassing){ if (!block.skyLightPassing){
solverS->remove(x,y,z); solverS->remove(x,y,z);
for (int i = y-1; i >= 0; i--){ for (int i = y-1; i >= 0; i--){
@ -210,19 +224,19 @@ void Lighting::onBlockSet(int x, int y, int z, blockid_t id){
break; break;
} }
} }
solverS->solve(); solverS->solve(chunk);
} }
solverR->solve(); solverR->solve(chunk);
solverG->solve(); solverG->solve(chunk);
solverB->solve(); solverB->solve(chunk);
if (block.emission[0] || block.emission[1] || block.emission[2]){ if (block.emission[0] || block.emission[1] || block.emission[2]){
solverR->add(x,y,z,block.emission[0]); solverR->add(x,y,z,block.emission[0]);
solverG->add(x,y,z,block.emission[1]); solverG->add(x,y,z,block.emission[1]);
solverB->add(x,y,z,block.emission[2]); solverB->add(x,y,z,block.emission[2]);
solverR->solve(); solverR->solve(chunk);
solverG->solve(); solverG->solve(chunk);
solverB->solve(); solverB->solve(chunk);
} }
} }
} }

View file

@ -1,7 +1,5 @@
#include "BlocksController.hpp" #include "BlocksController.hpp"
#include <set>
#include "content/Content.hpp" #include "content/Content.hpp"
#include "items/Inventories.hpp" #include "items/Inventories.hpp"
#include "items/Inventory.hpp" #include "items/Inventory.hpp"
@ -175,12 +173,8 @@ void BlocksController::randomTick(
void BlocksController::randomTick(int tickid, int parts, uint padding) { void BlocksController::randomTick(int tickid, int parts, uint padding) {
auto indices = level.content.getIndices(); auto indices = level.content.getIndices();
std::set<uint64_t> chunksIterated;
for (const auto& [pid, player] : *level.players) { for (const auto& [pid, player] : *level.players) {
const auto& chunks = *player->chunks; const auto& chunks = *player->chunks;
int offsetX = chunks.getOffsetX();
int offsetY = chunks.getOffsetY();
int width = chunks.getWidth(); int width = chunks.getWidth();
int height = chunks.getHeight(); int height = chunks.getHeight();
int segments = 4; int segments = 4;
@ -195,20 +189,15 @@ void BlocksController::randomTick(int tickid, int parts, uint padding) {
if (chunk == nullptr || !chunk->flags.lighted) { if (chunk == nullptr || !chunk->flags.lighted) {
continue; continue;
} }
union { if (chunk->lastRandomTickId == randomTickId) {
int32_t pos[2];
uint64_t key;
} posU;
posU.pos[0] = x + offsetX;
posU.pos[1] = z + offsetY;
if (chunksIterated.find(posU.key) != chunksIterated.end()) {
continue; continue;
} }
chunksIterated.insert(posU.key); chunk->lastRandomTickId = randomTickId;
randomTick(*chunk, segments, indices); randomTick(*chunk, segments, indices);
} }
} }
} }
randomTickId++;
} }
int64_t BlocksController::createBlockInventory(int x, int y, int z) { int64_t BlocksController::createBlockInventory(int x, int y, int z) {

View file

@ -33,6 +33,7 @@ class BlocksController {
util::Clock worldTickClock; util::Clock worldTickClock;
FastRandom random {}; FastRandom random {};
std::vector<on_block_interaction> blockInteractionCallbacks; std::vector<on_block_interaction> blockInteractionCallbacks;
uint64_t randomTickId = 0;
public: public:
BlocksController(const Level& level, Lighting* lighting); BlocksController(const Level& level, Lighting* lighting);

View file

@ -276,8 +276,8 @@ void EngineController::createWorld(
auto folder = paths.getWorldsFolder() / name; auto folder = paths.getWorldsFolder() / name;
call(engine, [this, &paths, folder]() { call(engine, [this, &paths, folder]() {
engine.getContentControl().loadContent();
paths.setCurrentWorldFolder(folder); paths.setCurrentWorldFolder(folder);
engine.getContentControl().loadContent();
}); });
auto& contentControl = engine.getContentControl(); auto& contentControl = engine.getContentControl();

View file

@ -1,6 +1,7 @@
#include "api_lua.hpp" #include "api_lua.hpp"
#include "assets/Assets.hpp" #include "assets/Assets.hpp"
#include "assets/AssetsLoader.hpp"
#include "coders/png.hpp" #include "coders/png.hpp"
#include "coders/vcm.hpp" #include "coders/vcm.hpp"
#include "debug/Logger.hpp" #include "debug/Logger.hpp"
@ -24,6 +25,14 @@ static void load_texture(
} }
} }
static int l_request_texture(lua::State* L) {
std::string filename = lua::require_string(L, 1);
std::string alias = lua::require_string(L, 2);
auto& loader = engine->acquireBackgroundLoader();
loader.add(AssetType::TEXTURE, filename, alias);
return 0;
}
static int l_load_texture(lua::State* L) { static int l_load_texture(lua::State* L) {
if (lua::isstring(L, 3) && lua::require_lstring(L, 3) != "png") { if (lua::isstring(L, 3) && lua::require_lstring(L, 3) != "png") {
throw std::runtime_error("unsupportd image format"); throw std::runtime_error("unsupportd image format");
@ -108,6 +117,7 @@ static int l_to_canvas(lua::State* L) {
} }
const luaL_Reg assetslib[] = { const luaL_Reg assetslib[] = {
{"request_texture", lua::wrap<l_request_texture>},
{"load_texture", lua::wrap<l_load_texture>}, {"load_texture", lua::wrap<l_load_texture>},
{"parse_model", lua::wrap<l_parse_model>}, {"parse_model", lua::wrap<l_parse_model>},
{"to_canvas", lua::wrap<l_to_canvas>}, {"to_canvas", lua::wrap<l_to_canvas>},

View file

@ -397,6 +397,13 @@ static int p_get_src(UINode* node, lua::State* L) {
return 0; return 0;
} }
static int p_get_fallback(UINode* node, lua::State* L) {
if (auto image = dynamic_cast<Image*>(node)) {
return lua::pushstring(L, image->getFallback());
}
return 0;
}
static int p_get_region(UINode* node, lua::State* L) { static int p_get_region(UINode* node, lua::State* L) {
if (auto image = dynamic_cast<Image*>(node)) { if (auto image = dynamic_cast<Image*>(node)) {
const auto& region = image->getRegion(); const auto& region = image->getRegion();
@ -663,6 +670,7 @@ static int l_gui_getattr(lua::State* L) {
{"syntax", p_get_syntax}, {"syntax", p_get_syntax},
{"markup", p_get_markup}, {"markup", p_get_markup},
{"src", p_get_src}, {"src", p_get_src},
{"fallback", p_get_fallback},
{"value", p_get_value}, {"value", p_get_value},
{"min", p_get_min}, {"min", p_get_min},
{"max", p_get_max}, {"max", p_get_max},
@ -790,6 +798,11 @@ static void p_set_src(UINode* node, lua::State* L, int idx) {
modelviewer->setModel(lua::require_string(L, idx)); modelviewer->setModel(lua::require_string(L, idx));
} }
} }
static void p_set_fallback(UINode* node, lua::State* L, int idx) {
if (auto image = dynamic_cast<Image*>(node)) {
image->setFallback(lua::require_string(L, idx));
}
}
static void p_set_region(UINode* node, lua::State* L, int idx) { static void p_set_region(UINode* node, lua::State* L, int idx) {
if (auto image = dynamic_cast<Image*>(node)) { if (auto image = dynamic_cast<Image*>(node)) {
auto vec = lua::tovec4(L, idx); auto vec = lua::tovec4(L, idx);
@ -962,6 +975,7 @@ static int l_gui_setattr(lua::State* L) {
{"syntax", p_set_syntax}, {"syntax", p_set_syntax},
{"markup", p_set_markup}, {"markup", p_set_markup},
{"src", p_set_src}, {"src", p_set_src},
{"fallback", p_set_fallback},
{"caret", p_set_caret}, {"caret", p_set_caret},
{"value", p_set_value}, {"value", p_set_value},
{"min", p_set_min}, {"min", p_set_min},

View file

@ -87,10 +87,10 @@ static int l_pack_get_info(
lua::setfield(L, "path"); lua::setfield(L, "path");
if (!engine->isHeadless()) { if (!engine->isHeadless()) {
auto assets = engine->getAssets(); auto& assetsLoader = engine->acquireBackgroundLoader();
std::string icon = pack.id + ".icon"; std::string icon = pack.id + ".icon";
if (!AssetsLoader::loadExternalTexture( if (!AssetsLoader::loadExternalTexture(
assets, icon, {pack.folder / "icon.png"} assetsLoader, icon, {pack.folder / "icon.png"}
)) { )) {
icon = "gui/no_icon"; icon = "gui/no_icon";
} }

View file

@ -56,10 +56,9 @@ static int l_get_list(lua::State* L) {
lua::pushstring(L, name); lua::pushstring(L, name);
lua::setfield(L, "name"); lua::setfield(L, "name");
auto assets = engine->getAssets();
std::string icon = "world#" + name + ".icon"; std::string icon = "world#" + name + ".icon";
if (!engine->isHeadless() && !AssetsLoader::loadExternalTexture( if (!engine->isHeadless() && !AssetsLoader::loadExternalTexture(
assets, engine->acquireBackgroundLoader(),
icon, icon,
{worlds[i] / "icon.png", {worlds[i] / "icon.png",
worlds[i] / "preview.png"} worlds[i] / "preview.png"}

View file

@ -1,8 +1,5 @@
#include "scripting.hpp" #include "scripting.hpp"
#include <iostream>
#include <stdexcept>
#include "scripting_commons.hpp" #include "scripting_commons.hpp"
#include "content/Content.hpp" #include "content/Content.hpp"
#include "content/ContentPack.hpp" #include "content/ContentPack.hpp"
@ -28,6 +25,9 @@
#include "world/World.hpp" #include "world/World.hpp"
#include "interfaces/Process.hpp" #include "interfaces/Process.hpp"
#include <iostream>
#include <stdexcept>
using namespace scripting; using namespace scripting;
static debug::Logger logger("scripting"); static debug::Logger logger("scripting");
@ -391,15 +391,15 @@ void scripting::on_blocks_tick(const Block& block, int tps) {
} }
void scripting::update_block(const Block& block, const glm::ivec3& pos) { void scripting::update_block(const Block& block, const glm::ivec3& pos) {
std::string name = block.name + ".update"; lua::emit_event(lua::get_main_state(), block.rt.eventNames.update,
lua::emit_event(lua::get_main_state(), name, [pos](auto L) { [pos](auto L) {
return lua::pushivec_stack(L, pos); return lua::pushivec_stack(L, pos);
}); });
} }
void scripting::random_update_block(const Block& block, const glm::ivec3& pos) { void scripting::random_update_block(const Block& block, const glm::ivec3& pos) {
std::string name = block.name + ".randupdate"; lua::emit_event(lua::get_main_state(), block.rt.eventNames.randomUpdate,
lua::emit_event(lua::get_main_state(), name, [pos](auto L) { [pos](auto L) {
return lua::pushivec_stack(L, pos); return lua::pushivec_stack(L, pos);
}); });
} }
@ -660,7 +660,8 @@ void scripting::load_content_script(
const std::string& prefix, const std::string& prefix,
const io::path& file, const io::path& file,
const std::string& fileName, const std::string& fileName,
BlockFuncsSet& funcsset BlockFuncsSet& funcsset,
BlockFuncNamesCache& namesCache
) { ) {
int env = *senv; int env = *senv;
lua::pop(lua::get_main_state(), load_script(env, "block", file, fileName)); lua::pop(lua::get_main_state(), load_script(env, "block", file, fileName));
@ -686,6 +687,9 @@ void scripting::load_content_script(
register_event(env, "on_block_present", prefix + ".blockpresent"); register_event(env, "on_block_present", prefix + ".blockpresent");
funcsset.onblockremoved = funcsset.onblockremoved =
register_event(env, "on_block_removed", prefix + ".blockremoved"); register_event(env, "on_block_removed", prefix + ".blockremoved");
namesCache.randomUpdate = prefix + ".update";
namesCache.randomUpdate = prefix + ".randupdate";
} }
void scripting::load_content_script( void scripting::load_content_script(
@ -693,7 +697,8 @@ void scripting::load_content_script(
const std::string& prefix, const std::string& prefix,
const io::path& file, const io::path& file,
const std::string& fileName, const std::string& fileName,
ItemFuncsSet& funcsset ItemFuncsSet& funcsset,
ItemFuncNamesCache& namesCache
) { ) {
int env = *senv; int env = *senv;
lua::pop(lua::get_main_state(), load_script(env, "item", file, fileName)); lua::pop(lua::get_main_state(), load_script(env, "item", file, fileName));

View file

@ -23,7 +23,9 @@ struct ItemDef;
class Inventory; class Inventory;
class UiDocument; class UiDocument;
struct BlockFuncsSet; struct BlockFuncsSet;
struct BlockFuncNamesCache;
struct ItemFuncsSet; struct ItemFuncsSet;
struct ItemFuncNamesCache;
struct WorldFuncsSet; struct WorldFuncsSet;
struct UserComponent; struct UserComponent;
struct uidocscript; struct uidocscript;
@ -168,7 +170,8 @@ namespace scripting {
const std::string& prefix, const std::string& prefix,
const io::path& file, const io::path& file,
const std::string& fileName, const std::string& fileName,
BlockFuncsSet& funcsset BlockFuncsSet& funcsset,
BlockFuncNamesCache& namesCache
); );
/// @brief Load script associated with an Item /// @brief Load script associated with an Item
@ -182,7 +185,8 @@ namespace scripting {
const std::string& prefix, const std::string& prefix,
const io::path& file, const io::path& file,
const std::string& fileName, const std::string& fileName,
ItemFuncsSet& funcsset ItemFuncsSet& funcsset,
ItemFuncNamesCache& namesCache
); );
/// @brief Load component script /// @brief Load component script

View file

@ -115,6 +115,10 @@ struct UiSettings {
struct NetworkSettings { struct NetworkSettings {
}; };
struct SystemSettings {
IntegerSetting maxBgAssetLoaders {3, -4, 16};
};
struct EngineSettings { struct EngineSettings {
AudioSettings audio; AudioSettings audio;
DisplaySettings display; DisplaySettings display;
@ -125,4 +129,5 @@ struct EngineSettings {
UiSettings ui; UiSettings ui;
NetworkSettings network; NetworkSettings network;
PathfindingSettings pathfinding; PathfindingSettings pathfinding;
SystemSettings system;
}; };

View file

@ -2,6 +2,7 @@
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <optional>
#include <condition_variable> #include <condition_variable>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
@ -48,6 +49,7 @@ namespace util {
std::atomic<int> busyWorkers = 0; std::atomic<int> busyWorkers = 0;
std::atomic<uint> jobsDone = 0; std::atomic<uint> jobsDone = 0;
std::atomic<bool> working = true; std::atomic<bool> working = true;
supplier<std::optional<T>> jobsSource = nullptr;
bool failed = false; bool failed = false;
bool standaloneResults = true; bool standaloneResults = true;
bool stopOnFail = true; bool stopOnFail = true;
@ -151,6 +153,10 @@ namespace util {
return working; return working;
} }
void setJobsSource(supplier<std::optional<T>>&& source) {
jobsSource = std::move(source);
}
void terminate() override { void terminate() override {
if (!working) { if (!working) {
return; return;
@ -227,6 +233,22 @@ namespace util {
} }
} }
} }
if (jobsSource) {
bool jobsAdded = false;
std::lock_guard<std::mutex> jobsLock(jobsMutex);
while (true) {
auto job = jobsSource();
if (job.has_value()) {
jobs.push(std::move(job.value()));
jobsAdded = true;
} else {
break;
}
}
if (jobsAdded) {
jobsMutexCondition.notify_one();
}
}
if (failed) { if (failed) {
throw std::runtime_error("some job failed"); throw std::runtime_error("some job failed");
} }

76
src/util/array_queue.hpp Normal file
View file

@ -0,0 +1,76 @@
#pragma once
#include <memory>
#include <cstring>
#include <stdexcept>
namespace util {
template <typename T>
class array_queue {
public:
array_queue(size_t initCapacity = 8)
: _capacity(initCapacity),
_size(0),
_front(0),
_back(0),
_data(std::make_unique<T[]>(_capacity)) {
if (initCapacity == 0 || (initCapacity & (initCapacity - 1)) != 0) {
throw std::invalid_argument("initCapacity must be positive power of 2");
}
}
~array_queue() {
while (!empty()) {
pop();
}
}
void push(T&& value) {
if (_back == _front && _size) {
grow();
}
_data[_back] = std::forward<T>(value);
_back = (_back + 1) & (_capacity - 1);
++_size;
}
T& front() {
return _data[_front];
}
void pop() {
_data[_front].~T();
_front = (_front + 1) & (_capacity - 1);
--_size;
}
bool empty() const {
return _size == 0;
}
size_t size() const {
return _size;
}
private:
size_t _capacity;
size_t _size;
size_t _front;
size_t _back;
std::unique_ptr<T[]> _data;
void grow() {
size_t newCapacity = _capacity * 2;
auto newData = std::make_unique<T[]>(newCapacity);
std::move(_data.get() + _front, _data.get() + _capacity, newData.get());
std::move(_data.get(), _data.get() + _front, newData.get() + (_capacity - _front));
_back = _size;
_front = 0;
_data.reset(newData.release());
_capacity = newCapacity;
}
};
}

View file

@ -54,6 +54,11 @@ struct BlockFuncsSet {
bool onblockremoved : 1; bool onblockremoved : 1;
}; };
struct BlockFuncNamesCache {
std::string update;
std::string randomUpdate;
};
struct CoordSystem { struct CoordSystem {
std::array<glm::ivec3, 3> axes; std::array<glm::ivec3, 3> axes;
/// @brief Grid 3d position fix offset (for negative vectors) /// @brief Grid 3d position fix offset (for negative vectors)
@ -296,6 +301,8 @@ public:
blockid_t surfaceReplacement = 0; blockid_t surfaceReplacement = 0;
std::set<int> tags; std::set<int> tags;
BlockFuncNamesCache eventNames;
} rt {}; } rt {};
Block(const std::string& name); Block(const std::string& name);

View file

@ -40,6 +40,8 @@ public:
bool dirtyHeights : 1; bool dirtyHeights : 1;
} flags {}; } flags {};
uint64_t lastRandomTickId = -1;
/// @brief Block inventories map where key is index of block in voxels array /// @brief Block inventories map where key is index of block in voxels array
ChunkInventoriesMap inventories; ChunkInventoriesMap inventories;
/// @brief Blocks metadata heap /// @brief Blocks metadata heap
@ -81,4 +83,10 @@ public:
glm::vec3((x + 1) * CHUNK_W, INFINITY, (z + 1) * CHUNK_D) glm::vec3((x + 1) * CHUNK_W, INFINITY, (z + 1) * CHUNK_D)
); );
} }
bool isBlockInside(int x, int z) const {
x -= this->x * CHUNK_W;
z -= this->z * CHUNK_D;
return x >= 0 && z >= 0 && x < CHUNK_W && z < CHUNK_D;
}
}; };