Merge branch 'MihailRis:main' into main

This commit is contained in:
RomanDonw 2026-08-04 22:51:40 +10:00 • committed by GitHub
commit d328a2ba2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
139 changed files with 3015 additions and 1048 deletions

View file

@ -44,6 +44,7 @@ extern const luaL_Reg posteffectslib[]; // gfx.posteffects
extern const luaL_Reg quatlib[];
extern const luaL_Reg randomlib[];
extern const luaL_Reg compressionlib[];
extern const luaL_Reg testlib[];
extern const luaL_Reg text3dlib[]; // gfx.text3d
extern const luaL_Reg timelib[];
extern const luaL_Reg tomllib[];

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

@ -311,6 +311,10 @@ static int l_world_raycast(lua::State* L) {
entityRaycast.entityFilterExcludeMode = lua::toboolean(L, -1);
lua::pop(L);
}
if (lua::getfield(L, "nonselect_entities", 1)) {
entityRaycast.includeNonSelectable = lua::toboolean(L, -1);
lua::pop(L);
}
if (lua::getfield(L, "filter_blocks", 1)) {
load_blocks_filter(L, -1, filteredBlocks);
blocksRaycast.filter = &filteredBlocks;

View file

@ -10,7 +10,7 @@
#include "util/stringutil.hpp"
#include "api_lua.hpp"
#include "../lua_engine.hpp"
#include "logic/scripting/descriptors_manager.hpp"
#include "logic/scripting/io_descriptors.hpp"
namespace fs = std::filesystem;
using namespace scripting;
@ -263,13 +263,13 @@ static int l_open_descriptor(lua::State* L) {
std::vector<char> buffer;
if(wplusMode) {
int temp_descriptor = scripting::descriptors_manager::open_descriptor(path, false, true);
int temp_descriptor = io_descriptors::open_descriptor(path, false, true);
if (temp_descriptor == -1) {
throw std::runtime_error("failed to open descriptor for initial reading");
}
auto* in_stream = scripting::descriptors_manager::get_input(temp_descriptor);
auto* in_stream = io_descriptors::get_input(temp_descriptor);
in_stream->seekg(0, std::ios::end);
std::streamsize size = in_stream->tellg();
@ -278,17 +278,17 @@ static int l_open_descriptor(lua::State* L) {
buffer.resize(size);
in_stream->read(buffer.data(), size);
scripting::descriptors_manager::close(temp_descriptor);
io_descriptors::close(temp_descriptor);
}
int descriptor = scripting::descriptors_manager::open_descriptor(path, write, read);
int descriptor = io_descriptors::open_descriptor(path, write, read);
if(descriptor == -1) {
throw std::runtime_error("failed to open descriptor");
}
if(wplusMode) {
auto* out_stream = scripting::descriptors_manager::get_output(descriptor);
auto* out_stream = io_descriptors::get_output(descriptor);
out_stream->write(buffer.data(), buffer.size());
out_stream->flush();
}
@ -297,51 +297,31 @@ static int l_open_descriptor(lua::State* L) {
}
static int l_has_descriptor(lua::State* L) {
return lua::pushboolean(L, scripting::descriptors_manager::has_descriptor(lua::tointeger(L, 1)));
return lua::pushboolean(L, io_descriptors::has_descriptor(lua::tointeger(L, 1)));
}
static int l_read_descriptor(lua::State* L) {
int descriptor = lua::tointeger(L, 1);
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
throw std::runtime_error("unknown descriptor");
}
if (!scripting::descriptors_manager::is_readable(descriptor)) {
throw std::runtime_error("descriptor is not readable");
}
int maxlen = lua::tointeger(L, 2);
auto* stream = scripting::descriptors_manager::get_input(descriptor);
auto& stream = io_descriptors::require_input(descriptor);
if (stream.eof()) {
stream.clear();
}
util::Buffer<char> buffer(maxlen);
stream->read(buffer.data(), maxlen);
std::streamsize read_len = stream->gcount();
stream.read(buffer.data(), maxlen);
std::streamsize read_len = stream.gcount();
return lua::create_bytearray(L, buffer.data(), read_len);
}
static int l_write_descriptor(lua::State* L) {
int descriptor = lua::tointeger(L, 1);
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
throw std::runtime_error("unknown descriptor");
}
if (!scripting::descriptors_manager::is_writeable(descriptor)) {
throw std::runtime_error("descriptor is not writeable");
}
auto data = lua::bytearray_as_string(L, 2);
auto* stream = scripting::descriptors_manager::get_output(descriptor);
stream->write(data.data(), static_cast<std::streamsize>(data.size()));
if (!stream->good()) {
auto& stream = io_descriptors::require_output(descriptor);
stream.write(data.data(), static_cast<std::streamsize>(data.size()));
if (!stream.good()) {
throw std::runtime_error("failed to write to stream");
}
return 0;
@ -350,12 +330,13 @@ static int l_write_descriptor(lua::State* L) {
static int l_seek_descriptor(lua::State* L) {
int descriptor = lua::tointeger(L, 1);
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
if (!io_descriptors::has_descriptor(descriptor)) {
throw std::runtime_error("unknown descriptor");
}
std::string mode = lua::require_string(L, 2);
auto mode = lua::require_string(L, 2);
std::ios_base::seekdir dir;
auto position = lua::tointeger(L, 3);
switch (mode[0]) {
case 'b':
@ -371,45 +352,81 @@ static int l_seek_descriptor(lua::State* L) {
throw std::runtime_error("invalid seek mode");
}
auto* stream = scripting::descriptors_manager::get_output(descriptor);
if (io_descriptors::is_writeable(descriptor)) {
auto& stream = io_descriptors::require_output(descriptor);
stream.seekp(position, dir);
if (!stream.good()) {
throw std::runtime_error("failed to seek stream");
}
}
if (io_descriptors::is_readable(descriptor)) {
auto& stream = io_descriptors::require_input(descriptor);
stream.seekg(position, dir);
if (!stream.good()) {
throw std::runtime_error("failed to seek stream");
}
}
return 0;
}
stream->seekp(lua::tointeger(L, 3), dir);
static int l_tell_descriptor(lua::State* L) {
int descriptor = lua::tointeger(L, 1);
if (!stream->good()) {
throw std::runtime_error("failed to seek stream");
if (!io_descriptors::has_descriptor(descriptor)) {
throw std::runtime_error("unknown descriptor");
}
return 0;
if (io_descriptors::is_writeable(descriptor)) {
auto& stream = io_descriptors::require_output(descriptor);
return lua::pushinteger(L, stream.tellp());
} else {
auto& stream = io_descriptors::require_input(descriptor);
return lua::pushinteger(L, stream.tellg());
}
}
static int l_flush_descriptor(lua::State* L) {
int descriptor = lua::tointeger(L, 1);
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
if (!io_descriptors::has_descriptor(descriptor)) {
throw std::runtime_error("unknown descriptor");
}
if (!scripting::descriptors_manager::is_writeable(descriptor)) {
if (!io_descriptors::is_writeable(descriptor)) {
throw std::runtime_error("descriptor is not writeable");
}
scripting::descriptors_manager::flush(descriptor);
io_descriptors::flush(descriptor);
return 0;
}
static int l_available_descriptor(lua::State* L) {
int descriptor = lua::tointeger(L, 1);
if (!io_descriptors::has_descriptor(descriptor)) {
throw std::runtime_error("unknown descriptor");
}
if (!io_descriptors::is_readable(descriptor)) {
throw std::runtime_error("descriptor is not readable");
}
return lua::pushinteger(L, io_descriptors::available(descriptor));
}
static int l_close_descriptor(lua::State* L) {
int descriptor = lua::tointeger(L, 1);
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
if (!io_descriptors::has_descriptor(descriptor)) {
throw std::runtime_error("unknown descriptor");
}
scripting::descriptors_manager::close(descriptor);
io_descriptors::close(descriptor);
return 0;
}
static int l_close_all_descriptors(lua::State* L) {
scripting::descriptors_manager::close_all_descriptors();
io_descriptors::close_all_descriptors();
return 0;
}
@ -442,7 +459,9 @@ const luaL_Reg filelib[] = {
{"__read_descriptor", lua::wrap<l_read_descriptor>},
{"__write_descriptor", lua::wrap<l_write_descriptor>},
{"__seek_descriptor", lua::wrap<l_seek_descriptor>},
{"__tell_descriptor", lua::wrap<l_tell_descriptor>},
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
{"__available_descriptor", lua::wrap<l_available_descriptor>},
{"__close_descriptor", lua::wrap<l_close_descriptor>},
{"__close_all_descriptors", lua::wrap<l_close_all_descriptors>},
{nullptr, nullptr}

View file

@ -420,15 +420,19 @@ static int p_get_data(UINode* node, lua::State* L) {
return 0;
}
static const std::string& request_node_id(const DocumentNode& docnode) {
std::string id = docnode.node->getId();
static const std::string& request_node_id(UiDocument& document, UINode& node) {
const std::string& id = node.getId();
if (id.empty()) {
id = "#" + std::to_string(
reinterpret_cast<std::ptrdiff_t>(docnode.node.get()));
node.setId( "#" + std::to_string(
reinterpret_cast<std::ptrdiff_t>(&node)));
document.pushIndices(node.shared_from_this());
return node.getId();
}
docnode.node->setId(std::move(id));
docnode.document->pushIndices(docnode.node);
return docnode.node->getId();
return id;
}
static const std::string& request_node_id(const DocumentNode& docnode) {
return request_node_id(*docnode.document, *docnode.node);
}
/// @brief Push UI-document node object to stack
@ -443,15 +447,14 @@ static int push_document_node(lua::State* L, const std::string& id) {
static int p_get_parent(UINode* node, lua::State* L) {
auto parent = node->getParent();
if (!parent) {
if (parent == nullptr) {
return 0;
}
auto docname = lua::require_string(L, 1);
auto element = lua::require_string(L, 2);
auto docnode = get_document_node_impl(L, docname, element);
const auto& id = request_node_id(docnode);
const auto& id = request_node_id(*docnode.document, *parent);
return push_document_node(L, id);
}
@ -1144,7 +1147,7 @@ static int l_gui_load_document(lua::State* L) {
auto document = documentPtr.get();
engine->requireAssets().store(std::move(documentPtr), alias);
scripting::on_ui_open(document, {args});
scripting::on_ui_open(*document, {args});
return 0;
}

View file

@ -1,4 +1,5 @@
#include "coders/json.hpp"
#include "coders/commons.hpp"
#include "api_lua.hpp"
static int l_json_stringify(lua::State* L) {
@ -12,8 +13,12 @@ static int l_json_stringify(lua::State* L) {
static int l_json_parse(lua::State* L) {
auto string = lua::require_string(L, 1);
auto element = json::parse("[string]", string);
return lua::pushvalue(L, element);
try {
auto element = json::parse("[string]", string);
return lua::pushvalue(L, element);
} catch (const parsing_error& err) {
throw err.toRuntimeError();
}
}
const luaL_Reg jsonlib[] = {

View file

@ -0,0 +1,45 @@
#include "api_lua.hpp"
#include "engine/Engine.hpp"
#include "window/input.hpp"
#include <iostream>
using namespace scripting;
static int l_set_button_pressed(lua::State* L) {
int button = lua::touinteger(L, 1);
int x = lua::tointeger(L, 2);
int y = lua::tointeger(L, 3);
bool pressed = lua::toboolean(L, 4);
auto& input = engine->getInput();
input.simulateCursorPos(x, y);
input.simulateClick(button, pressed);
return 0;
}
static int l_set_key_pressed(lua::State* L) {
auto key = lua::require_string(L, 1);
bool pressed = lua::toboolean(L, 2);
auto& input = engine->getInput();
input.simulateKey(input_util::keycode_from(key), pressed);
return 0;
}
static int l_enter_text(lua::State* L) {
auto string = lua::require_wstring(L, 1);
auto& input = engine->getInput();
for (auto chr : string) {
input.simulateCodepoint(chr);
}
return 0;
}
const luaL_Reg testlib[] = {
{"set_button_pressed", lua::wrap<l_set_button_pressed>},
{"set_key_pressed", lua::wrap<l_set_key_pressed>},
{"enter_text", lua::wrap<l_enter_text>},
{nullptr, nullptr}
};

View file

@ -1,4 +1,5 @@
#include "coders/toml.hpp"
#include "coders/commons.hpp"
#include "api_lua.hpp"
using namespace scripting;
@ -16,8 +17,12 @@ static int l_toml_stringify(lua::State* L) {
static int l_toml_parse(lua::State* L) {
auto string = lua::require_string(L, 1);
auto element = toml::parse("[string]", string);
return lua::pushvalue(L, element);
try {
auto element = toml::parse("[string]", string);
return lua::pushvalue(L, element);
} catch (const parsing_error& err) {
throw err.toRuntimeError();
}
}
const luaL_Reg tomllib[] = {

View file

@ -125,10 +125,13 @@ 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) {
if (level == nullptr) {
throw std::runtime_error("world is not open");
}
int x = static_cast<int>(lua::tointeger(L, 1));
int z = static_cast<int>(lua::tointeger(L, 2));
const auto& chunk = level->chunks->getChunk(x, z);
@ -175,7 +178,7 @@ static void integrate_chunk_client(Chunk& chunk) {
static int l_set_chunk_data(lua::State* L) {
if (level == nullptr) {
throw std::runtime_error("no open world");
throw std::runtime_error("world is not open");
}
int x = static_cast<int>(lua::tointeger(L, 1));
@ -201,7 +204,7 @@ static int l_set_chunk_data(lua::State* L) {
static int l_save_chunk_data(lua::State* L) {
if (level == nullptr) {
throw std::runtime_error("no open world");
throw std::runtime_error("world is not open");
}
int x = static_cast<int>(lua::tointeger(L, 1));

View file

@ -1,8 +1,8 @@
#include "coders/xml.hpp"
#include "coders/commons.hpp"
#include "api_lua.hpp"
static const char* TAG_ATTR = "#";
static const char* DEFAULT_ROOT_TAG = "root";
static int push_xml(lua::State* L, const xml::xmlelement& elem) {
if (elem.isText()) {
@ -73,17 +73,26 @@ static int l_tostring(lua::State* L) {
static int l_parse(lua::State* L) {
auto string = lua::require_string(L, 1);
auto document = xml::parse("[string]", string);
return push_xml(L, *document->getRoot());
try {
auto document = xml::parse("[string]", string);
return push_xml(L, *document->getRoot());
} catch (const parsing_error& err) {
throw err.toRuntimeError();
}
}
static int l_parse_vcd(lua::State* L) {
auto string = lua::require_string(L, 1);
auto rootTag = lua::tostring(L, 2);
auto document = xml::parse_vcm(
"[string]", string, rootTag ? rootTag : ""
);
std::unique_ptr<xml::Document> document;
try {
document = xml::parse_vcm(
"[string]", string, rootTag ? rootTag : ""
);
} catch (const parsing_error& err) {
throw err.toRuntimeError();
}
const auto& root = *document->getRoot();
if (rootTag != nullptr) {
return push_xml(L, root);

View file

@ -1,4 +1,5 @@
#include "coders/yaml.hpp"
#include "coders/commons.hpp"
#include "api_lua.hpp"
static int l_stringify(lua::State* L) {
@ -9,8 +10,12 @@ static int l_stringify(lua::State* L) {
static int l_parse(lua::State* L) {
auto string = lua::require_string(L, 1);
auto element = yaml::parse("[string]", string);
return lua::pushvalue(L, element);
try {
auto element = yaml::parse("[string]", string);
return lua::pushvalue(L, element);
} catch (const parsing_error& err) {
throw err.toRuntimeError();
}
}
const luaL_Reg yamllib[] = {

View file

@ -19,6 +19,7 @@ namespace {
debug::Logger logger("lua-state");
lua::State* main_thread = nullptr;
bool headless_mode = false;
bool test_mode = false;
const std::unordered_map<std::string, std::string>* project_args;
}
@ -70,12 +71,12 @@ static void create_libs(State* L, StateType stateType) {
openlib(L, "yaml", yamllib);
openlib(L, "__vc_app", applib);
lua::getglobal(L, "__vc_app");
lua::setregistry(L, "app");
getglobal(L, "__vc_app");
setregistry(L, "app");
if (stateType == StateType::SCRIPT) {
lua::getregistry(L, "app");
lua::setglobal(L, "app");
getregistry(L, "app");
setglobal(L, "app");
}
if (stateType == StateType::BASE || stateType == StateType::SCRIPT) {
openlib(L, "assets", assetslib);
@ -100,6 +101,10 @@ static void create_libs(State* L, StateType stateType) {
openlib(L, "__transform", transformlib);
}
if (::test_mode) {
openlib(L, "test", testlib);
}
addfunc(L, "print", lua::wrap<l_print>);
addfunc(L, "crc32", lua::wrap<l_crc32>);
}
@ -192,6 +197,7 @@ void lua::initialize(const EnginePaths& paths, const CoreParameters& params) {
logger.info() << LUAJIT_VERSION;
headless_mode = params.headless;
test_mode = params.testMode;
project_args = &params.projectArgs;
main_thread = create_state(
paths, params.headless ? StateType::SCRIPT : StateType::BASE