Merge branch 'main' into animation

This commit is contained in:
MihailRis 2026-08-04 23:39:49 +03:00
commit 282c9b3de2
68 changed files with 1419 additions and 488 deletions

View file

@ -25,7 +25,7 @@ const uint MIN_SURROUNDING = 9;
ChunksController::ChunksController(Level& level)
: level(level),
generator(std::make_unique<WorldGenerator>(
level.content.generators.require(level.getWorld()->getGenerator()),
level.content.generators.require(level.environment.generator),
level.content,
level.getWorld()->getSeed()
)) {}

View file

@ -179,7 +179,7 @@ void EngineController::onMissingContent(
if (engine.isHeadless()) {
throw std::runtime_error(
"missing content: " +
json::stringify(create_missing_content_report(report), true, " ")
util::quote(json::stringify(create_missing_content_report(report), true, " "))
);
} else {
engine.setScreen(std::make_shared<MenuScreen>(engine));
@ -268,7 +268,7 @@ inline uint64_t str2seed(const std::string& seedstr) {
void EngineController::createWorld(
const std::string& name,
const std::string& seedstr,
const std::string& generatorID
const std::string& environment
) {
uint64_t seed = str2seed(seedstr);
@ -279,17 +279,31 @@ void EngineController::createWorld(
paths.setCurrentWorldFolder(folder);
engine.getContentControl().loadContent();
});
const auto& generators = engine.getContentControl().get()->generators;
// generator-based environments must die in 1.0
bool genBasedEnv = false;
if (auto genDef = generators.find(environment)) {
genBasedEnv = true;
}
auto& contentControl = engine.getContentControl();
auto level = World::create(
name,
generatorID,
genBasedEnv ? "" : environment,
genBasedEnv ? environment : "",
folder,
seed,
engine.getSettings(),
*contentControl.get(),
contentControl.getContentPacks()
);
// generator-based environment initialization
if (genBasedEnv) {
level->environment.generator = environment;
}
if (!engine.isHeadless()) {
level->players->create(localPlayer);
}

View file

@ -149,7 +149,7 @@ void LevelController::saveWorld() {
world->wfile->createDirectories();
scripting::on_world_save();
level->onSave();
level->getWorld()->write(level.get());
level->getWorld()->write(*level);
}
void LevelController::onWorldQuit() {

View file

@ -20,8 +20,14 @@
#include "window/Window.hpp"
#include "world/Level.hpp"
#include <array>
using namespace scripting;
namespace {
static std::array<std::unique_ptr<Process>, MAX_SUBPROCESSES> processes;
}
/// @brief Check if content is loaded
static int l_is_content_loaded(lua::State* L) {
return lua::pushboolean(L, content != nullptr);
@ -337,6 +343,10 @@ static int l_start_debug_instance(lua::State* L) {
if (!engine->getProject().permissions.has(Permissions::DEBUGGING)) {
throw std::runtime_error("project has no debugging permission");
}
const auto& params = engine->getCoreParameters();
if (params.subProcessDepth >= MAX_SUBPROCESS_DEPTH) {
throw std::runtime_error("max subprocess depth exceeded");
}
int port = lua::tointeger(L, 1);
if (port == 0) {
@ -357,6 +367,8 @@ static int l_start_debug_instance(lua::State* L) {
"--res", paths.getResourcesFolder().u8string(),
"--dir", paths.getUserFilesFolder().u8string(),
"--dbg-server", "tcp:" + std::to_string(port),
"--sub-depth", std::to_string(engine->getCoreParameters()
.subProcessDepth + 1),
};
if (!projectPath.empty()) {
args.emplace_back("--project");
@ -365,11 +377,76 @@ static int l_start_debug_instance(lua::State* L) {
platform::new_engine_instance(
std::move(args),
outputPath.empty() ? "" : io::resolve(std::string(outputPath))
outputPath.empty() ? "" : io::resolve(std::string(outputPath)),
false
);
return lua::pushinteger(L, port);
}
static int l_start_background_instance(lua::State* L) {
if (!engine->getProject().permissions.has(Permissions::SUB_INSTANCES)) {
throw std::runtime_error("project has no sub-instances permission");
}
const auto& params = engine->getCoreParameters();
if (params.subProcessDepth >= MAX_SUBPROCESS_DEPTH) {
throw std::runtime_error("max subprocess depth exceeded");
}
auto scriptPath = lua::require_lstring(L, 1);
io::path outputPath = lua::isstring(L, 2) ? lua::require_lstring(L, 2) : "";
const auto& paths = engine->getPaths();
std::vector<std::string> args {
"--headless",
"--res", paths.getResourcesFolder().u8string(),
"--dir", paths.getUserFilesFolder().u8string(),
"--script", io::resolve(scriptPath).u8string(),
"--sub-depth", std::to_string(engine->getCoreParameters()
.subProcessDepth + 1),
"--log", io::resolve(outputPath).u8string(),
};
args.emplace_back("--project");
args.emplace_back(io::resolve(engine->getProject().path).u8string());
int handle = -1;
for (int i = 0; i < ::processes.size(); i++) {
if (!::processes[i] || !::processes[i]->isActive()) {
handle = i;
break;
}
}
if (handle == -1) {
throw std::runtime_error("sub-processes limit exceeded");
}
::processes[handle] =
platform::new_engine_instance(std::move(args), "", true);
return lua::pushinteger(L, handle);
}
static int l_is_instance_alive(lua::State* L) {
int handle = lua::tointeger(L, 1);
if (handle < 0 || handle >= ::processes.size()) {
throw std::runtime_error("invalid process handle");
}
auto& process = ::processes[handle];
return lua::pushboolean(L, process && process->isActive());
}
static int l_terminate_instance(lua::State* L) {
int handle = lua::tointeger(L, 1);
if (handle < 0 || handle >= ::processes.size()) {
throw std::runtime_error("invalid process handle");
}
auto& process = ::processes[handle];
if (process == nullptr) {
return lua::pushboolean(L, false);
}
bool active = process->isActive();
process.reset();
return lua::pushboolean(L, active);
}
const luaL_Reg applib[] = {
/// content
{"is_content_loaded", lua::wrap<l_is_content_loaded>},
@ -402,5 +479,8 @@ const luaL_Reg applib[] = {
{"get_version", lua::wrap<l_get_version>},
{"create_memory_device", lua::wrap<l_create_memory_device>},
{"start_debug_instance", lua::wrap<l_start_debug_instance>},
{"start_background_instance", lua::wrap<l_start_background_instance>},
{"is_instance_alive", lua::wrap<l_is_instance_alive>},
{"terminate_instance", lua::wrap<l_terminate_instance>},
{nullptr, nullptr}
};

View file

@ -125,7 +125,7 @@ static int l_is_night(lua::State* L) {
}
static int l_get_generator(lua::State* L) {
return lua::pushstring(L, require_world_info().generator);
return lua::pushstring(L, require_level().environment.generator);
}
static int l_get_chunk_data(lua::State* L) {

View file

@ -161,17 +161,13 @@ static LuaCanvas& require_canvas(State* L, int idx) {
static int l_clear(State* L) {
auto& canvas = require_canvas(L, 1);
auto& image = canvas.getData();
ubyte* data = image.getData();
RGBA rgba {};
size_t pixelscount = image.getWidth() * image.getHeight();
uint32_t* data = reinterpret_cast<uint32_t*>(image.getData());
if (gettop(L) == 1) {
std::fill(data, data + image.getDataSize(), 0);
return 0;
}
rgba = get_rgba(L, 2);
size_t pixels = image.getWidth() * image.getHeight();
const size_t channels = 4;
for (size_t i = 0; i < pixels * channels; i++) {
data[i] = rgba.arr[i % channels];
std::fill(data, data + pixelscount, 0);
} else {
std::fill(data, data + pixelscount, get_rgba(L, 2).rgba);
}
return 0;
}