From 4260742d6e8a13da916f59f68a58c4c80097b595 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 6 Apr 2025 22:22:35 +0300 Subject: [PATCH 01/19] add new Bytearray class --- res/modules/internal/bytearray.lua | 141 +++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 res/modules/internal/bytearray.lua diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua new file mode 100644 index 00000000..95d3dc20 --- /dev/null +++ b/res/modules/internal/bytearray.lua @@ -0,0 +1,141 @@ +local MIN_CAPACITY = 8 +local _type = type +local FFI = ffi + +FFI.cdef[[ + void* malloc(size_t); + void free(void*); + typedef struct { + unsigned char* bytes; + int size; + int capacity; + } bytearray_t; +]] + +local malloc = FFI.C.malloc +local free = FFI.C.free + +local function grow_buffer(self, elems) + local new_capacity = math.ceil(self.capacity / 0.75 + elems) + local prev = self.bytes + self.bytes = malloc(new_capacity) + FFI.copy(self.bytes, prev, self.size) + self.capacity = new_capacity + free(prev) +end + +local function count_elements(b) + local elems = 1 + if _type(b) ~= "number" then + elems = #b + end + return elems +end + +local bytearray_methods = { + append=function(self, b) + local elems = count_elements(b) + if self.size + elems > self.capacity then + grow_buffer(self, elems) + end + if _type(b) == "number" then + self.bytes[self.size] = b + else + for i=1, #b do + self.bytes[self.size + i - 1] = b[i] + end + end + self.size = self.size + elems + end, + insert=function(self, index, b) + local elems = count_elements(b) + if self.size + elems >= self.capacity then + grow_buffer(self, elems) + end + if _type(b) == "number" then + self.bytes[index] = b + else + for i=1, #b do + self.bytes[index + i - 1] = b[i] + end + end + self.size = self.size + elems + end, + remove=function(self, index, elems) + if index <= 0 or index > self.size then + return + end + if elems == nil then + elems = 1 + end + if index + elems > self.size then + elems = self.size - index + 1 + end + for i=index, self.size - elems do + self.bytes[i] = self.bytes[i + elems] + end + self.size = self.size - elems + end, + clear=function(self) + self.size = 0 + end, + reserve=function(self, new_capacity) + if new_capacity <= self.capacity then + return + end + local prev = self.bytes + self.bytes = malloc(new_capacity) + FFI.copy(self.bytes, prev, self.size) + self.capacity = new_capacity + free(prev) + end, +} + +local bytearray_mt = { + __index = function(self, key) + if _type(key) == "string" then + return bytearray_methods[key] + end + if key <= 0 or key > self.size then + return + end + return self.bytes[key - 1] + end, + __newindex = function(self, key, value) + if key <= 0 or key > self.size then + return + end + self.bytes[key - 1] = value + end, + __tostring = function(self) + return string.format("FFIBytearray[%s]{...}", tonumber(self.size)) + end, + __len = function(self) + return tonumber(self.size) + end, + __gc = function(self) + free(self.bytes) + end +} + +local utf8tostring = utf8.tostring + +function utf8.tostring(bytes) + local type = _type(bytes) + if type == "cdata" then + return FFI.string(bytes.bytes, bytes.size) + else + return utf8tostring(bytes) + end +end + +local bytearray_type = FFI.metatype("bytearray_t", bytearray_mt) + +return function (n) + n = n or 0 + if n < MIN_CAPACITY then + return bytearray_type(malloc(MIN_CAPACITY), n, MIN_CAPACITY) + else + return bytearray_type(malloc(n), n, n) + end +end From 36c2971fcfe7d98ee298cf364c4b226cd249f4c0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 6 Apr 2025 22:23:39 +0300 Subject: [PATCH 02/19] add FFIBytearray --- res/scripts/stdlib.lua | 2 ++ res/scripts/stdmin.lua | 15 ++++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index d881f3fa..29b74e64 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -263,6 +263,8 @@ entities.get_all = function(uids) return stdcomp.get_all(uids) end end +FFIBytearray = require "core:internal/bytearray" +ffi = nil math.randomseed(time.uptime() * 1536227939) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 8a2af237..06c86832 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -1,6 +1,3 @@ -local _ffi = ffi -ffi = nil - -- Lua has no parallelizm, also _set_data does not call any lua functions so -- may be reused one global ffi buffer per lua_State local canvas_ffi_buffer @@ -8,14 +5,14 @@ local canvas_ffi_buffer_size = 0 function __vc_Canvas_set_data(self, data) if type(data) == "cdata" then - self:_set_data(tostring(_ffi.cast("uintptr_t", data))) + self:_set_data(tostring(ffi.cast("uintptr_t", data))) end local width = self.width local height = self.height local size = width * height * 4 if size > canvas_ffi_buffer_size then - canvas_ffi_buffer = _ffi.new( + canvas_ffi_buffer = ffi.new( string.format("unsigned char[%s]", size) ) canvas_ffi_buffer_size = size @@ -23,7 +20,7 @@ function __vc_Canvas_set_data(self, data) for i=0, size - 1 do canvas_ffi_buffer[i] = data[i + 1] end - self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer))) + self:_set_data(tostring(ffi.cast("uintptr_t", canvas_ffi_buffer))) end function crc32(bytes, chksum) @@ -31,14 +28,14 @@ function crc32(bytes, chksum) local length = #bytes if type(bytes) == "table" then - local buffer_len = _ffi.new('int[1]', length) - local buffer = _ffi.new( + local buffer_len = ffi.new('int[1]', length) + local buffer = ffi.new( string.format("char[%s]", length) ) for i=1, length do buffer[i - 1] = bytes[i] end - bytes = _ffi.string(buffer, buffer_len[0]) + bytes = ffi.string(buffer, buffer_len[0]) end return _crc32(bytes, chksum) end From 794aa5b9cd941c23da97b8db7516a4065884e794 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 7 Apr 2025 20:14:46 +0300 Subject: [PATCH 03/19] add simple data_buffer test --- dev/tests/data_buffer.lua | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 dev/tests/data_buffer.lua diff --git a/dev/tests/data_buffer.lua b/dev/tests/data_buffer.lua new file mode 100644 index 00000000..5c6f4cf1 --- /dev/null +++ b/dev/tests/data_buffer.lua @@ -0,0 +1,4 @@ +local data_buffer = require "core:data_buffer" +local buffer = data_buffer({}, 'LE', true) +buffer:put_int16(2025) +debug.print(buffer) From 303e861fbb4bfb9ec48954d4bf5bae3c6e7f25ed Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 7 Apr 2025 20:15:48 +0300 Subject: [PATCH 04/19] replace Bytearray with FFI implementation --- res/modules/internal/bytearray.lua | 46 +++- res/scripts/stdlib.lua | 4 +- src/logic/scripting/lua/libs/libbase64.cpp | 21 +- src/logic/scripting/lua/libs/libbjson.cpp | 19 +- src/logic/scripting/lua/libs/libbyteutil.cpp | 6 +- src/logic/scripting/lua/libs/libcore.cpp | 8 +- src/logic/scripting/lua/libs/libfile.cpp | 18 +- src/logic/scripting/lua/libs/libnetwork.cpp | 15 +- src/logic/scripting/lua/libs/libutf8.cpp | 19 +- src/logic/scripting/lua/libs/libworld.cpp | 19 +- src/logic/scripting/lua/lua_custom_types.hpp | 20 -- src/logic/scripting/lua/lua_engine.cpp | 1 - src/logic/scripting/lua/lua_overrides.cpp | 10 +- src/logic/scripting/lua/lua_util.cpp | 20 +- src/logic/scripting/lua/lua_util.hpp | 26 ++- .../lua/usertypes/lua_type_bytearray.cpp | 206 ------------------ 16 files changed, 125 insertions(+), 333 deletions(-) delete mode 100644 src/logic/scripting/lua/usertypes/lua_type_bytearray.cpp diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index 95d3dc20..04eddc94 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -118,20 +118,22 @@ local bytearray_mt = { end } -local utf8tostring = utf8.tostring - -function utf8.tostring(bytes) - local type = _type(bytes) - if type == "cdata" then - return FFI.string(bytes.bytes, bytes.size) - else - return utf8tostring(bytes) - end -end - local bytearray_type = FFI.metatype("bytearray_t", bytearray_mt) -return function (n) +local function FFIBytearray (n) + local t = type(n) + if t == "string" then + local buffer = malloc(#n) + FFI.copy(buffer, n, #n) + return bytearray_type(buffer, #n, #n) + elseif t == "table" then + local capacity = math.max(#n, MIN_CAPACITY) + local buffer = malloc(capacity) + for i=1,#n do + buffer[i - 1] = n[i] + end + return bytearray_type(buffer, #n, capacity) + end n = n or 0 if n < MIN_CAPACITY then return bytearray_type(malloc(MIN_CAPACITY), n, MIN_CAPACITY) @@ -139,3 +141,23 @@ return function (n) return bytearray_type(malloc(n), n, n) end end + +local function FFIBytearray_as_string(bytes) + local t = type(bytes) + if t == "cdata" then + return FFI.string(bytes.bytes, bytes.size) + elseif t == "table" then + local buffer = FFI.new("unsigned char[?]", #bytes) + for i=1, #bytes do + buffer[i - 1] = bytes[i] + end + return FFI.string(buffer, #bytes) + else + error("Bytearray expected, got "..type(bytes)) + end +end + +return { + FFIBytearray = FFIBytearray, + FFIBytearray_as_string = FFIBytearray_as_string +} diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 29b74e64..558ba0f2 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -263,7 +263,9 @@ entities.get_all = function(uids) return stdcomp.get_all(uids) end end -FFIBytearray = require "core:internal/bytearray" +local bytearray = require "core:internal/bytearray" +Bytearray = bytearray.FFIBytearray +Bytearray_as_string = bytearray.FFIBytearray_as_string ffi = nil math.randomseed(time.uptime() * 1536227939) diff --git a/src/logic/scripting/lua/libs/libbase64.cpp b/src/logic/scripting/lua/libs/libbase64.cpp index 26f77eb6..5d50f1e8 100644 --- a/src/logic/scripting/lua/libs/libbase64.cpp +++ b/src/logic/scripting/lua/libs/libbase64.cpp @@ -16,14 +16,14 @@ static int l_encode(lua::State* L) { return lua::pushstring(L, util::base64_encode( reinterpret_cast(buffer.data()), buffer.size() )); - } else if (auto bytes = lua::touserdata(L, 1)) { - return lua::pushstring( - L, - util::base64_encode( - bytes->data().data(), - bytes->data().size() - ) + } else { + auto string = lua::bytearray_as_string(L, 1); + auto out = util::base64_encode( + reinterpret_cast(string.data()), + string.size() ); + lua::pop(L); + return lua::pushstring(L, std::move(out)); } throw std::runtime_error("array or ByteArray expected"); } @@ -36,13 +36,10 @@ static int l_decode(lua::State* L) { lua::pushinteger(L, buffer[i] & 0xFF); lua::rawseti(L, i+1); } + return 1; } else { - lua::newuserdata(L, buffer.size()); - auto bytearray = lua::touserdata(L, -1); - bytearray->data().reserve(buffer.size()); - std::memcpy(bytearray->data().data(), buffer.data(), buffer.size()); + return lua::create_bytearray(L, buffer.data(), buffer.size()); } - return 1; } const luaL_Reg base64lib[] = { diff --git a/src/logic/scripting/lua/libs/libbjson.cpp b/src/logic/scripting/lua/libs/libbjson.cpp index fa3dcfd7..f9acd077 100644 --- a/src/logic/scripting/lua/libs/libbjson.cpp +++ b/src/logic/scripting/lua/libs/libbjson.cpp @@ -9,9 +9,7 @@ static int l_tobytes(lua::State* L) { if (lua::gettop(L) >= 2) { compress = lua::toboolean(L, 2); } - return lua::newuserdata( - L, json::to_binary(value, compress) - ); + return lua::create_bytearray(L, json::to_binary(value, compress)); } static int l_frombytes(lua::State* L) { @@ -24,17 +22,18 @@ static int l_frombytes(lua::State* L) { lua::pop(L); } return lua::pushvalue(L, json::from_binary(buffer.data(), len)); - } else if (auto bytes = lua::touserdata(L, -1)) { - const auto& buffer = bytes->data(); - return lua::pushvalue( - L, json::from_binary(buffer.data(), buffer.size()) - ); } else { - throw std::runtime_error("table or Bytearray expected"); + auto string = lua::bytearray_as_string(L, 1); + auto out = json::from_binary( + reinterpret_cast(string.data()), string.size() + ); + lua::pop(L); + return lua::pushvalue(L, std::move(out)); } } const luaL_Reg bjsonlib[] = { {"tobytes", lua::wrap}, {"frombytes", lua::wrap}, - {NULL, NULL}}; + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/libs/libbyteutil.cpp b/src/logic/scripting/lua/libs/libbyteutil.cpp index d2c13175..76bd9d37 100644 --- a/src/logic/scripting/lua/libs/libbyteutil.cpp +++ b/src/logic/scripting/lua/libs/libbyteutil.cpp @@ -99,7 +99,7 @@ static int pack(lua::State* L, const char* format, bool usetable) { } return 1; } else { - return lua::newuserdata(L, builder.build()); + return lua::create_bytearray(L, builder.build()); } } @@ -130,8 +130,8 @@ static int count_elements(const char* format) { static int l_unpack(lua::State* L) { const char* format = lua::require_string(L, 1); int count = count_elements(format); - auto bytes = lua::require_bytearray(L, 2); - ByteReader reader(bytes); + auto bytes = lua::bytearray_as_string(L, 2); + ByteReader reader(reinterpret_cast(bytes.data()), bytes.size()); bool bigEndian = false; for (size_t i = 0; format[i]; i++) { diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 568802b3..7ebdbc38 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -248,12 +248,14 @@ static int l_load_texture(lua::State* L) { } lua::pop(L); load_texture(buffer.data(), buffer.size(), lua::require_string(L, 2)); - } else if (auto bytes = lua::touserdata(L, 1)) { + } else { + auto string = lua::bytearray_as_string(L, 1); load_texture( - bytes->data().data(), - bytes->data().size(), + reinterpret_cast(string.data()), + string.size(), lua::require_string(L, 2) ); + lua::pop(L); } return 0; } diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp index 51c63e82..fa2b2954 100644 --- a/src/logic/scripting/lua/libs/libfile.cpp +++ b/src/logic/scripting/lua/libs/libfile.cpp @@ -128,7 +128,7 @@ static int l_read_bytes(lua::State* L) { auto bytes = io::read_bytes(path); if (lua::gettop(L) < 2 || !lua::toboolean(L, 2)) { - lua::newuserdata(L, std::move(bytes)); + lua::create_bytearray(L, std::move(bytes)); } else { lua::createtable(L, length, 0); int newTable = lua::gettop(L); @@ -148,18 +148,12 @@ static int l_read_bytes(lua::State* L) { static int l_write_bytes(lua::State* L) { io::path path = get_writeable_path(L); - if (auto bytearray = lua::touserdata(L, 2)) { - auto& bytes = bytearray->data(); - return lua::pushboolean( - L, io::write_bytes(path, bytes.data(), bytes.size()) - ); - } - - std::vector bytes; - lua::read_bytes_from_table(L, 2, bytes); - return lua::pushboolean( - L, io::write_bytes(path, bytes.data(), bytes.size()) + auto string = lua::bytearray_as_string(L, 2); + bool res = io::write_bytes( + path, reinterpret_cast(string.data()), string.size() ); + lua::pop(L); + return lua::pushboolean(L, res); } static int l_list_all_res(lua::State* L, const std::string& path) { diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index ffad16ee..9a77b843 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -109,13 +109,13 @@ static int l_send(lua::State* L, network::Network& network) { } lua::pop(L); connection->send(buffer.data(), size); - } else if (auto bytes = lua::touserdata(L, 2)) { - connection->send( - reinterpret_cast(bytes->data().data()), bytes->data().size() - ); } else if (lua::isstring(L, 2)) { auto string = lua::tolstring(L, 2); connection->send(string.data(), string.length()); + } else { + auto string = lua::bytearray_as_string(L, 2); + connection->send(string.data(), string.length()); + lua::pop(L); } return 0; } @@ -140,13 +140,10 @@ static int l_recv(lua::State* L, network::Network& network) { lua::pushinteger(L, buffer[i] & 0xFF); lua::rawseti(L, i+1); } + return 1; } else { - lua::newuserdata(L, size); - auto bytearray = lua::touserdata(L, -1); - bytearray->data().reserve(size); - std::memcpy(bytearray->data().data(), buffer.data(), size); + return lua::create_bytearray(L, buffer.data(), size); } - return 1; } static int l_available(lua::State* L, network::Network& network) { diff --git a/src/logic/scripting/lua/libs/libutf8.cpp b/src/logic/scripting/lua/libs/libutf8.cpp index 4c1f484f..c0801b4d 100644 --- a/src/logic/scripting/lua/libs/libutf8.cpp +++ b/src/logic/scripting/lua/libs/libutf8.cpp @@ -14,13 +14,10 @@ static int l_tobytes(lua::State* L) { lua::pushinteger(L, string[i] & 0xFF); lua::rawseti(L, i+1); } + return 1; } else { - lua::newuserdata(L, string.length()); - auto bytearray = lua::touserdata(L, -1); - bytearray->data().reserve(string.length()); - std::memcpy(bytearray->data().data(), string.data(), string.length()); + return lua::create_bytearray(L, string.data(), string.length()); } - return 1; } static int l_tostring(lua::State* L) { @@ -35,16 +32,10 @@ static int l_tostring(lua::State* L) { } lua::pop(L); return lua::pushlstring(L, buffer.data(), size); - } else if (auto bytes = lua::touserdata(L, 1)) { - return lua::pushstring( - L, - std::string( - reinterpret_cast(bytes->data().data()), - bytes->data().size() - ) - ); + } else { + lua::bytearray_as_string(L, 1); + return 1; } - return 1; } static int l_length(lua::State* L) { diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index acf602c3..3e107f0e 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -144,7 +144,7 @@ static int l_get_chunk_data(lua::State* L) { } else { chunkData = compressed_chunks::encode(*chunk); } - return lua::newuserdata(L, std::move(chunkData)); + return lua::create_bytearray(L, std::move(chunkData)); } static void integrate_chunk_client(Chunk& chunk) { @@ -175,14 +175,17 @@ static int l_set_chunk_data(lua::State* L) { int x = static_cast(lua::tointeger(L, 1)); int z = static_cast(lua::tointeger(L, 2)); - auto buffer = lua::require_bytearray(L, 3); + auto buffer = lua::bytearray_as_string(L, 3); auto chunk = level->chunks->getChunk(x, z); if (chunk == nullptr) { return lua::pushboolean(L, false); } compressed_chunks::decode( - *chunk, buffer.data(), buffer.size(), *content->getIndices() + *chunk, + reinterpret_cast(buffer.data()), + buffer.size(), + *content->getIndices() ); if (controller->getChunksController()->lighting == nullptr) { return lua::pushboolean(L, true); @@ -198,10 +201,16 @@ static int l_save_chunk_data(lua::State* L) { int x = static_cast(lua::tointeger(L, 1)); int z = static_cast(lua::tointeger(L, 2)); - auto buffer = lua::require_bytearray(L, 3); + auto buffer = lua::bytearray_as_string(L, 3); compressed_chunks::save( - x, z, std::move(buffer), level->getWorld()->wfile->getRegions() + x, + z, + std::vector( + reinterpret_cast(buffer.data()), + reinterpret_cast(buffer.data()) + buffer.size() + ), + level->getWorld()->wfile->getRegions() ); return 0; } diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index e126ee72..1185ecbe 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -18,26 +18,6 @@ namespace lua { virtual const std::string& getTypeName() const = 0; }; - class LuaBytearray : public Userdata { - std::vector buffer; - public: - LuaBytearray(size_t capacity); - LuaBytearray(std::vector buffer); - LuaBytearray(const ubyte* data, size_t size); - virtual ~LuaBytearray(); - - const std::string& getTypeName() const override { - return TYPENAME; - } - inline std::vector& data() { - return buffer; - } - - static int createMetatable(lua::State*); - inline static std::string TYPENAME = "Bytearray"; - }; - static_assert(!std::is_abstract()); - class LuaHeightmap : public Userdata { std::shared_ptr map; std::unique_ptr noise; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 1ae35d39..ca335c49 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -117,7 +117,6 @@ void lua::init_state(State* L, StateType stateType) { initialize_libs_extends(L); - newusertype(L); newusertype(L); newusertype(L); newusertype(L); diff --git a/src/logic/scripting/lua/lua_overrides.cpp b/src/logic/scripting/lua/lua_overrides.cpp index 2040392c..3af2fa65 100644 --- a/src/logic/scripting/lua/lua_overrides.cpp +++ b/src/logic/scripting/lua/lua_overrides.cpp @@ -39,14 +39,16 @@ int l_crc32(lua::State* L) { string.length() ) ); - } else if (auto bytearray = lua::touserdata(L, 1)) { - auto& bytes = bytearray->data(); - return lua::pushinteger(L, crc32(value, bytes.data(), bytes.size())); } else if (lua::istable(L, 1)) { std::vector bytes; lua::read_bytes_from_table(L, 1, bytes); return lua::pushinteger(L, crc32(value, bytes.data(), bytes.size())); } else { - throw std::runtime_error("invalid argument 1"); + auto string = lua::bytearray_as_string(L, 1); + auto res = crc32( + value, reinterpret_cast(string.data()), string.size() + ); + lua::pop(L); + return lua::pushinteger(L, res); } } diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index 8cd77fb1..77937aef 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -61,7 +61,7 @@ int lua::pushvalue(State* L, const dv::value& value) { break; case value_type::bytes: { const auto& bytes = value.asBytes(); - newuserdata(L, bytes.data(), bytes.size()); + create_bytearray(L, bytes.data(), bytes.size()); break; } } @@ -128,18 +128,14 @@ dv::value lua::tovalue(State* L, int idx) { return map; } } - case LUA_TUSERDATA: { - if (auto bytes = touserdata(L, idx)) { - const auto& data = bytes->data(); - return std::make_shared(data.data(), data.size()); - } - [[fallthrough]]; - } - default: - throw std::runtime_error( - "lua type " + std::string(lua_typename(L, type)) + - " is not supported" + default: { + auto data = bytearray_as_string(L, idx); + auto bytes = std::make_shared( + reinterpret_cast(data.data()), data.size() ); + pop(L); + return bytes; + } } } diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index ab45edb1..ba0cd3d7 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -746,14 +746,22 @@ namespace lua { } } - inline std::vector require_bytearray(lua::State* L, int idx) { - if (auto* bytearray = lua::touserdata(L, idx)) { - return bytearray->data(); - } else if (lua::istable(L, idx)) { - std::vector bytes; - read_bytes_from_table(L, idx, bytes); - return bytes; - } - throw std::runtime_error("bytearray expected"); + inline int create_bytearray(lua::State* L, const void* bytes, size_t size) { + lua::requireglobal(L, "Bytearray"); + lua::pushlstring( + L, std::string_view(reinterpret_cast(bytes), size) + ); + return lua::call(L, 1, 1); + } + + inline int create_bytearray(lua::State* L, const std::vector& bytes) { + return create_bytearray(L, bytes.data(), bytes.size()); + } + + inline std::string_view bytearray_as_string(lua::State* L, int idx) { + lua::requireglobal(L, "Bytearray_as_string"); + lua::pushvalue(L, idx); + lua::call(L, 1, 1); + return lua::tolstring(L, -1); } } diff --git a/src/logic/scripting/lua/usertypes/lua_type_bytearray.cpp b/src/logic/scripting/lua/usertypes/lua_type_bytearray.cpp deleted file mode 100644 index 41b4ee59..00000000 --- a/src/logic/scripting/lua/usertypes/lua_type_bytearray.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "../lua_custom_types.hpp" - -#include - -#include "util/listutil.hpp" -#include "../lua_util.hpp" - -using namespace lua; - -LuaBytearray::LuaBytearray(size_t capacity) : buffer(capacity) { - buffer.resize(capacity); -} - -LuaBytearray::LuaBytearray(std::vector buffer) : buffer(std::move(buffer)) { -} - -LuaBytearray::LuaBytearray(const ubyte* data, size_t size) : buffer(data, data + size) { -} - -LuaBytearray::~LuaBytearray() { -} - -static int l_append(lua::State* L) { - if (auto buffer = touserdata(L, 1)) { - if (lua::isnumber(L, 2)) { - auto value = tointeger(L, 2); - buffer->data().push_back(static_cast(value)); - } else if (lua::istable(L, 2)) { - lua::read_bytes_from_table(L, 2, buffer->data()); - } else if (auto extension = lua::touserdata(L, 2)) { - util::concat(buffer->data(), extension->data()); - } else { - throw std::runtime_error("integer/table/Bytearray expected"); - } - } - return 0; -} - -static int l_insert(lua::State* L) { - auto buffer = touserdata(L, 1); - if (buffer == nullptr) { - return 0; - } - auto& data = buffer->data(); - auto index = tointeger(L, 2) - 1; - if (static_cast(index) > data.size()) { - return 0; - } - if (lua::isnumber(L, 3)) { - auto value = tointeger(L, 3); - data.insert(data.begin() + index, static_cast(value)); - } else if (lua::istable(L, 3)) { - std::vector temp; - lua::read_bytes_from_table(L, 3, temp); - data.insert(data.begin() + index, temp.begin(), temp.end()); - } else if (auto extension = lua::touserdata(L, 3)) { - const std::vector& src = extension->data(); - data.insert(data.begin() + index, src.begin(), src.end()); - } else { - throw std::runtime_error("integer/table/Bytearray expected"); - } - return 0; -} - -static int l_remove(lua::State* L) { - auto buffer = touserdata(L, 1); - if (buffer == nullptr) { - return 0; - } - auto& data = buffer->data(); - auto index = tointeger(L, 2) - 1; - if (static_cast(index) > data.size()) { - return 0; - } - data.erase(data.begin() + index); - return 0; -} - -static std::unordered_map methods { - {"append", lua::wrap}, - {"insert", lua::wrap}, - {"remove", lua::wrap}, -}; - -static int l_meta_meta_call(lua::State* L) { - if (lua_istable(L, 2)) { - size_t len = objlen(L, 2); - std::vector buffer(len); - buffer.resize(len); - for (size_t i = 0; i < len; i++) { - rawgeti(L, i + 1); - buffer[i] = static_cast(tointeger(L, -1)); - pop(L); - } - return newuserdata(L, std::move(buffer)); - } - auto size = tointeger(L, 2); - if (size < 0) { - throw std::runtime_error("size can not be less than 0"); - } - return newuserdata(L, static_cast(size)); -} - -static int l_meta_index(lua::State* L) { - auto buffer = touserdata(L, 1); - if (buffer == nullptr) { - return 0; - } - auto& data = buffer->data(); - if (isstring(L, 2)) { - auto found = methods.find(tostring(L, 2)); - if (found != methods.end()) { - return pushcfunction(L, found->second); - } - } - auto index = tointeger(L, 2) - 1; - if (static_cast(index) > data.size()) { - return 0; - } - return pushinteger(L, data.at(index)); -} - -static int l_meta_newindex(lua::State* L) { - auto buffer = touserdata(L, 1); - if (buffer == nullptr) { - return 0; - } - auto& data = buffer->data(); - auto index = static_cast(tointeger(L, 2) - 1); - auto value = tointeger(L, 3); - if (index >= data.size()) { - if (index == data.size()) { - data.push_back(static_cast(value)); - } - return 0; - } - data[index] = static_cast(value); - return 0; -} - -static int l_meta_len(lua::State* L) { - if (auto buffer = touserdata(L, 1)) { - return pushinteger(L, buffer->data().size()); - } - return 0; -} - -static int l_meta_tostring(lua::State* L) { - auto buffer = touserdata(L, 1); - if (buffer == nullptr) { - return 0; - } - auto& data = buffer->data(); - if (data.size() > 512) { - return pushstring( - L, "bytearray[" + std::to_string(data.size()) + "]{...}" - ); - } else { - std::stringstream ss; - ss << "bytearray[" << std::to_string(data.size()) << "]{"; - for (size_t i = 0; i < data.size(); i++) { - if (i > 0) { - ss << " "; - } - ss << static_cast(data[i]); - } - ss << "}"; - return pushstring(L, ss.str()); - } -} - -static int l_meta_add(lua::State* L) { - auto bufferA = touserdata(L, 1); - auto bufferB = touserdata(L, 2); - if (bufferA == nullptr || bufferB == nullptr) { - return 0; - } - auto& dataA = bufferA->data(); - auto& dataB = bufferB->data(); - - std::vector ab; - ab.reserve(dataA.size() + dataB.size()); - ab.insert(ab.end(), dataA.begin(), dataA.end()); - ab.insert(ab.end(), dataB.begin(), dataB.end()); - return newuserdata(L, std::move(ab)); -} - -int LuaBytearray::createMetatable(lua::State* L) { - createtable(L, 0, 6); - pushcfunction(L, lua::wrap); - setfield(L, "__index"); - pushcfunction(L, lua::wrap); - setfield(L, "__newindex"); - pushcfunction(L, lua::wrap); - setfield(L, "__len"); - pushcfunction(L, lua::wrap); - setfield(L, "__tostring"); - pushcfunction(L, lua::wrap); - setfield(L, "__add"); - - createtable(L, 0, 1); - pushcfunction(L, lua::wrap); - setfield(L, "__call"); - setmetatable(L); - return 1; -} From bf79f6bc75a7686d59fdd0dba8b9018d6191e980 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 10 Apr 2025 23:10:49 +0300 Subject: [PATCH 05/19] fix "upgrade square is not fully inside of area" error --- src/logic/ChunksController.cpp | 6 ++++-- src/world/generator/WorldGenerator.cpp | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index ccdd59b1..1c90b122 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -35,12 +35,14 @@ void ChunksController::update( int64_t maxDuration, int loadDistance, uint padding, Player& player ) const { const auto& position = player.getPosition(); - int centerX = floordiv(position.x); - int centerY = floordiv(position.z); + int centerX = floordiv(glm::floor(position.x)); + int centerY = floordiv(glm::floor(position.z)); if (player.isLoadingChunks()) { /// FIXME: one generator for multiple players generator->update(centerX, centerY, loadDistance); + } else { + return; } int64_t mcstotal = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 955e3195..9ebd9067 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -355,8 +355,9 @@ void WorldGenerator::generateHeightmap( void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); - // 2 is safety padding preventing ChunksController rounding problem - surroundMap.resize(loadDistance + 2); + // 3 is safety padding preventing ChunksController rounding problem + // FIXME + surroundMap.resize(loadDistance + 3); surroundMap.setCenter(centerX, centerY); } From c110fafbcfff0dba4ac7c82b56bbf616044bfcd7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 11 Apr 2025 19:25:13 +0300 Subject: [PATCH 06/19] update doc/*/scripting/ecs.md --- doc/en/scripting/ecs.md | 3 +++ doc/ru/scripting/ecs.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index 90a64828..f0a33a97 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -29,6 +29,9 @@ entity:has_component(name: str) -> bool -- Enables/disables the component entity:set_enabled(name: str, enable: bool) + +-- Returns id of player the entity is bound +entity:get_player() -> int or nil ``` ## Built-in components diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index db44a1ac..89af90fa 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -29,6 +29,9 @@ entity:has_component(name: str) -> bool -- Включает/выключает компонент по имени entity:set_enabled(name: str, enable: bool) + +-- Возвращает id игрока, к которому привязана сущность +entity:get_player() -> int или nil ``` ## Встроенные компоненты From 98813472a8c25b1de93dd5d843af38c5aec9b1d8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 11 Apr 2025 20:37:08 +0300 Subject: [PATCH 07/19] fix generator area centering --- src/logic/LevelController.cpp | 4 ++-- src/voxels/Chunks.cpp | 6 +++--- src/world/generator/WorldGenerator.cpp | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 4404fb3a..f1aca628 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -77,8 +77,8 @@ void LevelController::update(float delta, bool pause) { player->updateEntity(); glm::vec3 position = player->getPosition(); player->chunks->configure( - position.x, - position.z, + glm::floor(position.x), + glm::floor(position.z), settings.chunks.loadDistance.get() + settings.chunks.padding.get() ); chunks->update( diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 5b5a2cd9..ce533889 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -32,18 +32,18 @@ Chunks::Chunks( : events(events), indices(indices), areaMap(w, d) { - areaMap.setCenter(ox-w/2, oz-d/2); + areaMap.setCenter(ox - w / 2, oz - d / 2); areaMap.setOutCallback([this](int, int, const auto& chunk) { this->events->trigger(LevelEventType::CHUNK_HIDDEN, chunk.get()); }); } void Chunks::configure(int32_t x, int32_t z, uint32_t radius) { - setCenter(x, z); uint32_t diameter = radius * 2LL; if (getWidth() != diameter) { resize(diameter, diameter); } + setCenter(x, z); } voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { @@ -313,7 +313,7 @@ glm::vec3 Chunks::rayCastToObstacle( } void Chunks::setCenter(int32_t x, int32_t z) { - areaMap.setCenter(floordiv(x, CHUNK_W), floordiv(z, CHUNK_D)); + areaMap.setCenter(floordiv(x), floordiv(z)); } void Chunks::resize(uint32_t newW, uint32_t newD) { diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 9ebd9067..e2bf4a86 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -355,9 +355,7 @@ void WorldGenerator::generateHeightmap( void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); - // 3 is safety padding preventing ChunksController rounding problem - // FIXME - surroundMap.resize(loadDistance + 3); + surroundMap.resize(loadDistance); surroundMap.setCenter(centerX, centerY); } From d8feff56c0ed5598f80fc7d556251b2d41a599d8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 12 Apr 2025 12:32:31 +0300 Subject: [PATCH 08/19] feat: Lua-style array append support in Bytearray --- dev/tests/bytearray.lua | 8 +++++++ res/modules/internal/bytearray.lua | 34 +++++++++++++++++------------- 2 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 dev/tests/bytearray.lua diff --git a/dev/tests/bytearray.lua b/dev/tests/bytearray.lua new file mode 100644 index 00000000..9860b9fe --- /dev/null +++ b/dev/tests/bytearray.lua @@ -0,0 +1,8 @@ +local arr = Bytearray() +assert(#arr == 0) + +for i=1,10 do + arr[i] = 10 - i + assert(#arr == i) + assert(arr[i] == 10 - i) +end diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index 04eddc94..7178985e 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -32,21 +32,23 @@ local function count_elements(b) return elems end +local function append(self, b) + local elems = count_elements(b) + if self.size + elems > self.capacity then + grow_buffer(self, elems) + end + if _type(b) == "number" then + self.bytes[self.size] = b + else + for i=1, #b do + self.bytes[self.size + i - 1] = b[i] + end + end + self.size = self.size + elems +end + local bytearray_methods = { - append=function(self, b) - local elems = count_elements(b) - if self.size + elems > self.capacity then - grow_buffer(self, elems) - end - if _type(b) == "number" then - self.bytes[self.size] = b - else - for i=1, #b do - self.bytes[self.size + i - 1] = b[i] - end - end - self.size = self.size + elems - end, + append=append, insert=function(self, index, b) local elems = count_elements(b) if self.size + elems >= self.capacity then @@ -102,7 +104,9 @@ local bytearray_mt = { return self.bytes[key - 1] end, __newindex = function(self, key, value) - if key <= 0 or key > self.size then + if key == self.size + 1 then + return append(self, value) + elseif key <= 0 or key > self.size then return end self.bytes[key - 1] = value From 9169049530d2399d0bf05fd7e759c78463592ce1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 12 Apr 2025 12:55:29 +0300 Subject: [PATCH 09/19] feat: ipairs(...) support in Bytearray --- dev/tests/bytearray.lua | 4 ++++ res/modules/internal/bytearray.lua | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/dev/tests/bytearray.lua b/dev/tests/bytearray.lua index 9860b9fe..e20d4318 100644 --- a/dev/tests/bytearray.lua +++ b/dev/tests/bytearray.lua @@ -6,3 +6,7 @@ for i=1,10 do assert(#arr == i) assert(arr[i] == 10 - i) end + +for i, v in ipairs(arr) do + assert(v == 10 - i) +end diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index 7178985e..03a2578d 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -119,6 +119,15 @@ local bytearray_mt = { end, __gc = function(self) free(self.bytes) + end, + __ipairs = function(self) + local i = 0 + return function() + i = i + 1 + if i <= self.size then + return i, self.bytes[i - 1] + end + end end } From fb07c86ea690d07475c509106c6083fbbba6db5f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 12 Apr 2025 13:24:55 +0300 Subject: [PATCH 10/19] add static Lua-style functions: Bytearray.insert, Bytearray.remove, Bytearray.append --- dev/tests/bytearray.lua | 3 + res/modules/internal/bytearray.lua | 118 +++++++++++++++------------ res/scripts/stdlib.lua | 1 + src/logic/scripting/lua/lua_util.hpp | 2 +- 4 files changed, 73 insertions(+), 51 deletions(-) diff --git a/dev/tests/bytearray.lua b/dev/tests/bytearray.lua index e20d4318..79d8a89b 100644 --- a/dev/tests/bytearray.lua +++ b/dev/tests/bytearray.lua @@ -10,3 +10,6 @@ end for i, v in ipairs(arr) do assert(v == 10 - i) end + +Bytearray.remove(arr, 2) +assert(#arr == 9) diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index 03a2578d..fabfaee7 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -47,37 +47,50 @@ local function append(self, b) self.size = self.size + elems end +local function insert(self, index, b) + if index == nil then + index = self.size + 1 + end + if index <= 0 or index > self.size + 1 then + return + end + local elems = count_elements(b) + if self.size + elems > self.capacity then + grow_buffer(self, elems) + end + for i=self.size, index - 1, -1 do + self.bytes[i + elems] = self.bytes[i] + end + if _type(b) == "number" then + self.bytes[index - 1] = b + else + for i=1, #b do + self.bytes[index + i - 2] = b[i] + end + end + self.size = self.size + elems +end + +local function remove(self, index, elems) + if index <= 0 or index > self.size then + return + end + if elems == nil then + elems = 1 + end + if index + elems > self.size then + elems = self.size - index + 1 + end + for i=index, self.size - elems do + self.bytes[i] = self.bytes[i + elems] + end + self.size = self.size - elems +end + local bytearray_methods = { append=append, - insert=function(self, index, b) - local elems = count_elements(b) - if self.size + elems >= self.capacity then - grow_buffer(self, elems) - end - if _type(b) == "number" then - self.bytes[index] = b - else - for i=1, #b do - self.bytes[index + i - 1] = b[i] - end - end - self.size = self.size + elems - end, - remove=function(self, index, elems) - if index <= 0 or index > self.size then - return - end - if elems == nil then - elems = 1 - end - if index + elems > self.size then - elems = self.size - index + 1 - end - for i=index, self.size - elems do - self.bytes[i] = self.bytes[i + elems] - end - self.size = self.size - elems - end, + insert=insert, + remove=remove, clear=function(self) self.size = 0 end, @@ -133,27 +146,32 @@ local bytearray_mt = { local bytearray_type = FFI.metatype("bytearray_t", bytearray_mt) -local function FFIBytearray (n) - local t = type(n) - if t == "string" then - local buffer = malloc(#n) - FFI.copy(buffer, n, #n) - return bytearray_type(buffer, #n, #n) - elseif t == "table" then - local capacity = math.max(#n, MIN_CAPACITY) - local buffer = malloc(capacity) - for i=1,#n do - buffer[i - 1] = n[i] +local FFIBytearray = { + __call = function (n) + local t = type(n) + if t == "string" then + local buffer = malloc(#n) + FFI.copy(buffer, n, #n) + return bytearray_type(buffer, #n, #n) + elseif t == "table" then + local capacity = math.max(#n, MIN_CAPACITY) + local buffer = malloc(capacity) + for i=1,#n do + buffer[i - 1] = n[i] + end + return bytearray_type(buffer, #n, capacity) end - return bytearray_type(buffer, #n, capacity) - end - n = n or 0 - if n < MIN_CAPACITY then - return bytearray_type(malloc(MIN_CAPACITY), n, MIN_CAPACITY) - else - return bytearray_type(malloc(n), n, n) - end -end + n = n or 0 + if n < MIN_CAPACITY then + return bytearray_type(malloc(MIN_CAPACITY), n, MIN_CAPACITY) + else + return bytearray_type(malloc(n), n, n) + end + end, + append = append, + insert = insert, + remove = remove, +} local function FFIBytearray_as_string(bytes) local t = type(bytes) @@ -171,6 +189,6 @@ local function FFIBytearray_as_string(bytes) end return { - FFIBytearray = FFIBytearray, + FFIBytearray = setmetatable(FFIBytearray, FFIBytearray), FFIBytearray_as_string = FFIBytearray_as_string } diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 558ba0f2..5fa9ca29 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -266,6 +266,7 @@ end local bytearray = require "core:internal/bytearray" Bytearray = bytearray.FFIBytearray Bytearray_as_string = bytearray.FFIBytearray_as_string +Bytearray_construct = Bytearray.__call ffi = nil math.randomseed(time.uptime() * 1536227939) diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index ba0cd3d7..7d545d49 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -747,7 +747,7 @@ namespace lua { } inline int create_bytearray(lua::State* L, const void* bytes, size_t size) { - lua::requireglobal(L, "Bytearray"); + lua::requireglobal(L, "Bytearray_construct"); lua::pushlstring( L, std::string_view(reinterpret_cast(bytes), size) ); From 1ade910fa1bcd374db0d7df2850c7e9d6eae73e7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 12 Apr 2025 13:49:31 +0300 Subject: [PATCH 11/19] fix Bytearray.insert, Bytearray.remove --- dev/tests/bytearray.lua | 9 +++++++++ res/modules/internal/bytearray.lua | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/dev/tests/bytearray.lua b/dev/tests/bytearray.lua index 79d8a89b..7b407cba 100644 --- a/dev/tests/bytearray.lua +++ b/dev/tests/bytearray.lua @@ -13,3 +13,12 @@ end Bytearray.remove(arr, 2) assert(#arr == 9) + +Bytearray.insert(arr, {5, 3, 6}) + +assert(#arr == 12) +Bytearray.insert(arr, 2, 8) +assert(#arr == 13) +for i=1,10 do + assert(arr[i] == 10 - i) +end diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index fabfaee7..e319751b 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -48,7 +48,8 @@ local function append(self, b) end local function insert(self, index, b) - if index == nil then + if b == nil then + b = index index = self.size + 1 end if index <= 0 or index > self.size + 1 then @@ -81,7 +82,7 @@ local function remove(self, index, elems) if index + elems > self.size then elems = self.size - index + 1 end - for i=index, self.size - elems do + for i=index - 1, self.size - elems - 1 do self.bytes[i] = self.bytes[i + elems] end self.size = self.size - elems From 0a49e4fa92880bc1682337517ab30a132a4e1a0a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 12 Apr 2025 15:27:29 +0300 Subject: [PATCH 12/19] add Bytearray.trim, Bytearray.get_capacity --- dev/tests/bytearray.lua | 3 ++ res/modules/internal/bytearray.lua | 52 ++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/dev/tests/bytearray.lua b/dev/tests/bytearray.lua index 7b407cba..1a12bdeb 100644 --- a/dev/tests/bytearray.lua +++ b/dev/tests/bytearray.lua @@ -22,3 +22,6 @@ assert(#arr == 13) for i=1,10 do assert(arr[i] == 10 - i) end +print(#arr, arr:get_capacity()) +arr:trim() +assert(#arr == arr:get_capacity()) diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index e319751b..2481c4dd 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -24,6 +24,18 @@ local function grow_buffer(self, elems) free(prev) end +local function trim_buffer(self) + if self.size == self.capacity then + return + end + local size = self.size + local prev = self.bytes + self.bytes = malloc(size) + FFI.copy(self.bytes, prev, self.size) + self.capacity = size + free(prev) +end + local function count_elements(b) local elems = 1 if _type(b) ~= "number" then @@ -88,23 +100,33 @@ local function remove(self, index, elems) self.size = self.size - elems end +local function clear(self) + self.size = 0 +end + +local function reserve(self, new_capacity) + if new_capacity <= self.capacity then + return + end + local prev = self.bytes + self.bytes = malloc(new_capacity) + FFI.copy(self.bytes, prev, self.size) + self.capacity = new_capacity + free(prev) +end + +local function get_capacity(self) + return self.capacity +end + local bytearray_methods = { append=append, insert=insert, remove=remove, - clear=function(self) - self.size = 0 - end, - reserve=function(self, new_capacity) - if new_capacity <= self.capacity then - return - end - local prev = self.bytes - self.bytes = malloc(new_capacity) - FFI.copy(self.bytes, prev, self.size) - self.capacity = new_capacity - free(prev) - end, + trim=trim_buffer, + clear=clear, + reserve=reserve, + get_capacity=get_capacity, } local bytearray_mt = { @@ -172,6 +194,10 @@ local FFIBytearray = { append = append, insert = insert, remove = remove, + trim = trim_buffer, + clear = clear, + reserve = reserve, + get_capacity = get_capacity, } local function FFIBytearray_as_string(bytes) From da3f3d546fc84757791df6de801740fb72d941e8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 12 Apr 2025 15:32:26 +0300 Subject: [PATCH 13/19] cleanup --- res/modules/internal/bytearray.lua | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index 2481c4dd..35bc6cc6 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -191,14 +191,8 @@ local FFIBytearray = { return bytearray_type(malloc(n), n, n) end end, - append = append, - insert = insert, - remove = remove, - trim = trim_buffer, - clear = clear, - reserve = reserve, - get_capacity = get_capacity, } +table.merge(FFIBytearray, bytearray_methods) local function FFIBytearray_as_string(bytes) local t = type(bytes) From 2b753a1dd26337811c6f27a3c11280c6c6e7672f Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Sat, 12 Apr 2025 19:43:41 +0300 Subject: [PATCH 14/19] update chunks.lua test for debugging "upgrade square is not fully inside of area" (#511) * Update chunks.lua * Update chunks.lua * Update chunks.lua --- dev/tests/chunks.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/chunks.lua b/dev/tests/chunks.lua index fcbaeb1f..cd6109ca 100644 --- a/dev/tests/chunks.lua +++ b/dev/tests/chunks.lua @@ -1,8 +1,8 @@ local util = require "core:tests_util" util.create_demo_world() -app.set_setting("chunks.load-distance", 3) -app.set_setting("chunks.load-speed", 1) +app.set_setting("chunks.load-distance", 15) +app.set_setting("chunks.load-speed", 15) local pid1 = player.create("Xerxes") assert(player.get_name(pid1) == "Xerxes") From d888ddec4cd0813cb1ddc363862ae804972dfe33 Mon Sep 17 00:00:00 2001 From: clasher113 <61116084+clasher113@users.noreply.github.com> Date: Sat, 12 Apr 2025 20:30:55 +0300 Subject: [PATCH 15/19] fix: pause menu on hudVisible=false (#512) * fix: pause menu on hudVisible=false --- src/frontend/hud.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index f39c72c7..ced38093 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -314,6 +314,7 @@ void Hud::updateWorldGenDebug() { void Hud::update(bool visible) { const auto& chunks = *player.chunks; + bool is_menu_open = menu.hasOpenPage(); debugPanel->setVisible( debug && visible && !(inventoryOpen && inventoryView == nullptr) @@ -322,13 +323,13 @@ void Hud::update(bool visible) { if (!visible && inventoryOpen) { closeInventory(); } - if (pause && !menu.hasOpenPage()) { + if (pause && !is_menu_open) { setPause(false); } if (!gui.isFocusCaught()) { processInput(visible); } - if ((menu.hasOpenPage() || inventoryOpen) == input.getCursor().locked) { + if ((is_menu_open || inventoryOpen) == input.getCursor().locked) { input.toggleCursor(); } @@ -349,6 +350,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); if (visible) { for (auto& element : elements) { @@ -360,7 +363,7 @@ void Hud::update(bool visible) { } cleanup(); - debugMinimap->setVisible(debug && showGeneratorMinimap); + debugMinimap->setVisible(debug && showGeneratorMinimap && visible); if (debug && showGeneratorMinimap) { updateWorldGenDebug(); } @@ -593,10 +596,6 @@ void Hud::setDebug(bool flag) { void Hud::draw(const DrawContext& ctx){ const auto& viewport = ctx.getViewport(); - bool is_menu_open = menu.hasOpenPage(); - darkOverlay->setVisible(is_menu_open); - menu.setVisible(is_menu_open); - updateElementsPosition(viewport); uicamera->setFov(viewport.y); From 4360cd408bf3b543a1c4596adcb75b50747f21d3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 12 Apr 2025 21:14:18 +0300 Subject: [PATCH 16/19] refactor: rename coders/lua_parsing to coders/syntax_parser --- src/coders/{lua_parsing.cpp => syntax_parser.cpp} | 7 ++++--- src/coders/{lua_parsing.hpp => syntax_parser.hpp} | 4 ++-- src/devtools/syntax_highlighting.cpp | 4 ++-- test/coders/lua_parsing.cpp | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) rename src/coders/{lua_parsing.cpp => syntax_parser.cpp} (97%) rename src/coders/{lua_parsing.hpp => syntax_parser.hpp} (71%) diff --git a/src/coders/lua_parsing.cpp b/src/coders/syntax_parser.cpp similarity index 97% rename from src/coders/lua_parsing.cpp rename to src/coders/syntax_parser.cpp index 0840c369..fb26f622 100644 --- a/src/coders/lua_parsing.cpp +++ b/src/coders/syntax_parser.cpp @@ -1,10 +1,9 @@ -#include "lua_parsing.hpp" +#include "syntax_parser.hpp" #include #include "BasicParser.hpp" -using namespace lua; using namespace devtools; static std::set keywords { @@ -188,6 +187,8 @@ public: } }; -std::vector lua::tokenize(std::string_view file, std::wstring_view source) { +std::vector devtools::tokenize( + std::string_view file, std::wstring_view source +) { return Tokenizer(file, source).tokenize(); } diff --git a/src/coders/lua_parsing.hpp b/src/coders/syntax_parser.hpp similarity index 71% rename from src/coders/lua_parsing.hpp rename to src/coders/syntax_parser.hpp index 2d5e6349..d8166a0a 100644 --- a/src/coders/lua_parsing.hpp +++ b/src/coders/syntax_parser.hpp @@ -5,8 +5,8 @@ #include "devtools/syntax.hpp" -namespace lua { - std::vector tokenize( +namespace devtools { + std::vector tokenize( std::string_view file, std::wstring_view source ); } diff --git a/src/devtools/syntax_highlighting.cpp b/src/devtools/syntax_highlighting.cpp index 7aa95a23..748f9aaa 100644 --- a/src/devtools/syntax_highlighting.cpp +++ b/src/devtools/syntax_highlighting.cpp @@ -1,7 +1,7 @@ #include "syntax_highlighting.hpp" #include "coders/commons.hpp" -#include "coders/lua_parsing.hpp" +#include "coders/syntax_parser.hpp" #include "graphics/core/Font.hpp" using namespace devtools; @@ -60,7 +60,7 @@ std::unique_ptr devtools::syntax_highlight( ) { try { if (lang == "lua") { - auto tokens = lua::tokenize("", source); + auto tokens = tokenize("", source); return build_styles(tokens); } else { return nullptr; diff --git a/test/coders/lua_parsing.cpp b/test/coders/lua_parsing.cpp index dcfaeffc..2bc547c4 100644 --- a/test/coders/lua_parsing.cpp +++ b/test/coders/lua_parsing.cpp @@ -1,4 +1,4 @@ -#include "coders/lua_parsing.hpp" +#include "coders/syntax_parser.hpp" #include @@ -14,7 +14,7 @@ TEST(lua_parsing, Tokenizer) { auto filename = "res:scripts/stdlib.lua"; auto source = io::read_string(filename); try { - auto tokens = lua::tokenize(filename, util::str2wstr_utf8(source)); + auto tokens = devtools::tokenize(filename, util::str2wstr_utf8(source)); for (const auto& token : tokens) { std::cout << (int)token.tag << " " << util::quote(util::wstr2str_utf8(token.text)) From 5253be6c56227fda8b3cb315e444921f3670b19f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 13 Apr 2025 00:19:38 +0300 Subject: [PATCH 17/19] feat: custom languages syntax (WIP) --- res/devtools/syntax/glsl.toml | 28 ++++ res/devtools/syntax/lua.toml | 12 ++ res/layouts/console.xml | 1 - res/layouts/console.xml.lua | 1 + src/coders/BasicParser.inl | 3 + src/coders/syntax_parser.cpp | 122 +++++++++++++----- src/coders/syntax_parser.hpp | 18 ++- src/devtools/Editor.cpp | 29 +++++ src/devtools/Editor.hpp | 23 ++++ ...x_highlighting.cpp => SyntaxProcessor.cpp} | 30 +++-- src/devtools/SyntaxProcessor.hpp | 29 +++++ src/devtools/syntax_highlighting.hpp | 16 --- src/engine/Engine.cpp | 3 + src/engine/Engine.hpp | 9 ++ src/graphics/ui/GUI.cpp | 4 + src/graphics/ui/GUI.hpp | 5 + src/graphics/ui/elements/TextBox.cpp | 6 +- test/coders/lua_parsing.cpp | 27 ---- 18 files changed, 278 insertions(+), 88 deletions(-) create mode 100644 res/devtools/syntax/glsl.toml create mode 100644 res/devtools/syntax/lua.toml create mode 100644 src/devtools/Editor.cpp create mode 100644 src/devtools/Editor.hpp rename src/devtools/{syntax_highlighting.cpp => SyntaxProcessor.cpp} (76%) create mode 100644 src/devtools/SyntaxProcessor.hpp delete mode 100644 src/devtools/syntax_highlighting.hpp delete mode 100644 test/coders/lua_parsing.cpp diff --git a/res/devtools/syntax/glsl.toml b/res/devtools/syntax/glsl.toml new file mode 100644 index 00000000..a2acb4d2 --- /dev/null +++ b/res/devtools/syntax/glsl.toml @@ -0,0 +1,28 @@ +language = "GLSL" +extensions = ["glsl", "glslv", "glslf"] +line-comment = "//" +multiline-comment-start = "/*" +multiline-comment-end = "*/" +keywords = [ + "attribute", "break", "bvec2", "bvec3", "bvec4", "centroid", "continue", + "discard", "dmat2", "dmat2x2", "dmat2x3", "dmat2x4", "dmat3", "dmat3x2", + "dmat3x3", "dmat3x4", "dmat4", "dmat4x2", "dmat4x3", "dmat4x4", "dvec2", + "dvec3", "dvec4", "else", "flat", "float", "highp", "if", "in", "inout", + "int", "invariant", "isampler1D", "isampler1DArray", "isampler2D", + "isampler2DArray", "isampler2DMS", "isampler2DMSArray", "isampler2DRect", + "isampler3D", "isamplerBuffer", "isamplerCube", "isamplerCubeArray", + "ivec2", "ivec3", "ivec4", "layout", "lowp", "mat2", "mat2x2", "mat2x3", + "mat2x4", "mat3", "mat3x2", "mat3x3", "mat3x4", "mat4", "mat4x2", "mat4x3", + "mat4x4", "mediump", "noperspective", "out", "patch", "precision", "return", + "sample", "sampler1D", "sampler1DArray", "sampler1DArrayShadow", + "sampler1DShadow", "sampler2D", "sampler2DArray", "sampler2DArrayShadow", + "sampler2DMS", "sampler2DMSArray", "sampler2DRect", "sampler2DRectShadow", + "sampler2DShadow", "sampler3D", "samplerBuffer", "samplerCube", + "samplerCubeArray", "samplerCubeArrayShadow", "samplerCubeShadow", "smooth", + "subroutine", "uniform", "usampler1D", "usampler1DArray", "usampler2D", + "usampler2DArray", "usampler2DMS", "usampler2DMSArray", "usampler2DRect", + "usampler3D", "usamplerBuffer", "usamplerCube", "usamplerCubeArray", + "uvec2", "uvec3", "uvec4", "varying", "vec2", "vec3", "vec4", "void", + "while", +] + diff --git a/res/devtools/syntax/lua.toml b/res/devtools/syntax/lua.toml new file mode 100644 index 00000000..cbccce75 --- /dev/null +++ b/res/devtools/syntax/lua.toml @@ -0,0 +1,12 @@ +language = "Lua" +extensions = ["lua"] +line-comment = "--" +multiline-comment-start = "[==[" +multiline-comment-end = "]==]" +multiline-string-start = "[[" +multiline-string-end = "]]" +keywords = [ + "and", "break", "do", "else", "elseif", "end", "false", "for", "function", + "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", + "until", "while" +] diff --git a/res/layouts/console.xml b/res/layouts/console.xml index 9d015aab..bbfdcf7a 100644 --- a/res/layouts/console.xml +++ b/res/layouts/console.xml @@ -74,7 +74,6 @@ multiline='true' line-numbers='true' oncontrolkey='on_control_combination' - syntax='lua' size-func="-1,40" text-wrap='false' scroll-step='50' diff --git a/res/layouts/console.xml.lua b/res/layouts/console.xml.lua index 695408e1..c3fee31d 100644 --- a/res/layouts/console.xml.lua +++ b/res/layouts/console.xml.lua @@ -197,6 +197,7 @@ function open_file_in_editor(filename, line, mutable) editor.scroll = 0 editor.text = source editor.focused = true + editor.syntax = file.ext(filename) if line then time.post_runnable(function() editor.caret = editor:linePos(line) diff --git a/src/coders/BasicParser.inl b/src/coders/BasicParser.inl index 7ed9c10d..1bbe1278 100644 --- a/src/coders/BasicParser.inl +++ b/src/coders/BasicParser.inl @@ -166,6 +166,9 @@ size_t BasicParser::remain() const { template bool BasicParser::isNext(const std::basic_string& substring) { + if (substring.empty()) { + return false; + } if (source.length() - pos < substring.length()) { return false; } diff --git a/src/coders/syntax_parser.cpp b/src/coders/syntax_parser.cpp index fb26f622..4b56b607 100644 --- a/src/coders/syntax_parser.cpp +++ b/src/coders/syntax_parser.cpp @@ -2,26 +2,73 @@ #include +#include "data/dv.hpp" +#include "util/stringutil.hpp" #include "BasicParser.hpp" using namespace devtools; -static std::set keywords { - L"and", L"break", L"do", L"else", L"elseif", L"end", L"false", L"for", L"function", - L"if", L"in", L"local", L"nil", L"not", L"or", L"repeat", L"return", L"then", L"true", - L"until", L"while" -}; +dv::value Syntax::serialize() const { + auto map = dv::object(); + map["language"] = language; + map["line-comment"] = util::wstr2str_utf8(lineComment); + map["multiline-comment-start"] = util::wstr2str_utf8(multilineCommentStart); + map["multiline-comment-end"] = util::wstr2str_utf8(multilineCommentEnd); + map["multiline-string-start"] = util::wstr2str_utf8(multilineStringStart); + map["multiline-string-end"] = util::wstr2str_utf8(multilineStringEnd); -static bool is_lua_keyword(std::wstring_view view) { - return keywords.find(view) != keywords.end(); + auto& extsList = map.list("extensions"); + for (const auto& ext : extensions) { + extsList.add(ext); + } + + auto& keywordsList = map.list("keywords"); + for (const auto& keyword : keywords) { + keywordsList.add(util::wstr2str_utf8(keyword)); + } + return map; } -inline bool is_lua_identifier_start(int c) { +void Syntax::deserialize(const dv::value& src) { + src.at("language").get(language); + + std::string lineComment; + std::string multilineCommentStart; + std::string multilineCommentEnd; + std::string multilineStringStart; + std::string multilineStringEnd; + src.at("line-comment").get(lineComment); + src.at("multiline-comment-start").get(multilineCommentStart); + src.at("multiline-comment-end").get(multilineCommentEnd); + src.at("multiline-string-start").get(multilineStringStart); + src.at("multiline-string-end").get(multilineStringEnd); + this->lineComment = util::str2wstr_utf8(lineComment); + this->multilineCommentStart = util::str2wstr_utf8(multilineCommentStart); + this->multilineCommentEnd = util::str2wstr_utf8(multilineCommentEnd); + this->multilineStringStart = util::str2wstr_utf8(multilineStringStart); + this->multilineStringEnd = util::str2wstr_utf8(multilineStringEnd); + + if (src.has("extensions")) { + const auto& extsList = src["extensions"]; + for (const auto& ext : extsList) { + extensions.insert(ext.asString()); + } + } + + if (src.has("keywords")) { + const auto& keywordsList = src["keywords"]; + for (const auto& keyword : keywordsList) { + keywords.insert(util::str2wstr_utf8(keyword.asString())); + } + } +} + +inline bool is_common_identifier_start(int c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'; } -inline bool is_lua_identifier_part(int c) { - return is_lua_identifier_start(c) || is_digit(c); +inline bool is_common_identifier_part(int c) { + return is_common_identifier_start(c) || is_digit(c); } inline bool is_lua_operator_start(int c) { @@ -31,10 +78,13 @@ inline bool is_lua_operator_start(int c) { } class Tokenizer : BasicParser { + const Syntax& syntax; std::vector tokens; public: - Tokenizer(std::string_view file, std::wstring_view source) - : BasicParser(file, source) { + Tokenizer( + const Syntax& syntax, std::string_view file, std::wstring_view source + ) + : BasicParser(file, source), syntax(syntax) { } std::wstring parseLuaName() { @@ -110,9 +160,12 @@ public: } wchar_t c = peek(); auto start = currentLocation(); - if (is_lua_identifier_start(c)) { + if (is_common_identifier_start(c)) { auto name = parseLuaName(); - TokenTag tag = (is_lua_keyword(name) ? TokenTag::KEYWORD : TokenTag::NAME); + TokenTag tag = + (syntax.keywords.find(name) == syntax.keywords.end() + ? TokenTag::NAME + : TokenTag::KEYWORD); emitToken( tag, std::move(name), @@ -132,24 +185,29 @@ public: emitToken(tag, std::wstring(literal), start); continue; } + const auto& mcommentStart = syntax.multilineCommentStart; + if (!mcommentStart.empty() && c == mcommentStart[0] && + isNext(syntax.multilineCommentStart)) { + auto string = readUntil(syntax.multilineCommentEnd, true); + skip(syntax.multilineCommentEnd.length()); + emitToken( + TokenTag::COMMENT, + std::wstring(string) + syntax.multilineCommentEnd, + start + ); + continue; + } + const auto& mstringStart = syntax.multilineStringStart; + if (!mstringStart.empty() && c == mstringStart[0] && + isNext(syntax.multilineStringStart)) { + skip(mstringStart.length()); + auto string = readUntil(syntax.multilineStringEnd, true); + skip(syntax.multilineStringEnd.length()); + emitToken(TokenTag::STRING, std::wstring(string), start); + continue; + } switch (c) { case '(': case '[': case '{': - if (isNext(L"[==[")) { - auto string = readUntil(L"]==]", true); - skip(4); - emitToken( - TokenTag::COMMENT, - std::wstring(string) + L"]==]", - start - ); - continue; - } else if (isNext(L"[[")) { - skip(2); - auto string = readUntil(L"]]", true); - skip(2); - emitToken(TokenTag::STRING, std::wstring(string), start); - continue; - } emitToken(TokenTag::OPEN_BRACKET, std::wstring({c}), start, true); continue; case ')': case ']': case '}': @@ -188,7 +246,7 @@ public: }; std::vector devtools::tokenize( - std::string_view file, std::wstring_view source + const Syntax& syntax, std::string_view file, std::wstring_view source ) { - return Tokenizer(file, source).tokenize(); + return Tokenizer(syntax, file, source).tokenize(); } diff --git a/src/coders/syntax_parser.hpp b/src/coders/syntax_parser.hpp index d8166a0a..c7bd254c 100644 --- a/src/coders/syntax_parser.hpp +++ b/src/coders/syntax_parser.hpp @@ -1,12 +1,28 @@ #pragma once +#include #include #include #include "devtools/syntax.hpp" +#include "interfaces/Serializable.hpp" namespace devtools { + struct Syntax : Serializable { + std::string language; + std::set extensions; + std::set keywords; + std::wstring lineComment; + std::wstring multilineCommentStart; + std::wstring multilineCommentEnd; + std::wstring multilineStringStart; + std::wstring multilineStringEnd; + + dv::value serialize() const override; + void deserialize(const dv::value& src) override; + }; + std::vector tokenize( - std::string_view file, std::wstring_view source + const Syntax& syntax, std::string_view file, std::wstring_view source ); } diff --git a/src/devtools/Editor.cpp b/src/devtools/Editor.cpp new file mode 100644 index 00000000..82475ee4 --- /dev/null +++ b/src/devtools/Editor.cpp @@ -0,0 +1,29 @@ +#include "Editor.hpp" + +#include "engine/Engine.hpp" +#include "io/engine_paths.hpp" +#include "coders/syntax_parser.hpp" +#include "SyntaxProcessor.hpp" + +using namespace devtools; + +Editor::Editor(Engine& engine) + : engine(engine), syntaxProcessor(std::make_unique()) { +} + +Editor::~Editor() = default; + +void Editor::loadTools() { + const auto& paths = engine.getResPaths(); + auto files = paths.listdir("devtools/syntax"); + for (const auto& file : files) { + auto config = io::read_object(file); + auto syntax = std::make_unique(); + syntax->deserialize(config); + syntaxProcessor->addSyntax(std::move(syntax)); + } +} + +SyntaxProcessor& Editor::getSyntaxProcessor() { + return *syntaxProcessor; +} diff --git a/src/devtools/Editor.hpp b/src/devtools/Editor.hpp new file mode 100644 index 00000000..8b79dd54 --- /dev/null +++ b/src/devtools/Editor.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +class Engine; + +namespace devtools { + class SyntaxProcessor; + + class Editor { + public: + Editor(Engine& engine); + ~Editor(); + + void loadTools(); + + SyntaxProcessor& getSyntaxProcessor(); + private: + Engine& engine; + std::unique_ptr syntaxProcessor; + }; +} diff --git a/src/devtools/syntax_highlighting.cpp b/src/devtools/SyntaxProcessor.cpp similarity index 76% rename from src/devtools/syntax_highlighting.cpp rename to src/devtools/SyntaxProcessor.cpp index 748f9aaa..a4a1c7cf 100644 --- a/src/devtools/syntax_highlighting.cpp +++ b/src/devtools/SyntaxProcessor.cpp @@ -1,4 +1,4 @@ -#include "syntax_highlighting.hpp" +#include "SyntaxProcessor.hpp" #include "coders/commons.hpp" #include "coders/syntax_parser.hpp" @@ -55,16 +55,28 @@ static std::unique_ptr build_styles( return std::make_unique(std::move(styles)); } -std::unique_ptr devtools::syntax_highlight( - const std::string& lang, std::wstring_view source +void SyntaxProcessor::addSyntax( + std::unique_ptr syntax ) { + const auto ptr = syntax.get(); + langs.emplace_back(std::move(syntax)); + + for (auto& ext : ptr->extensions) { + langsExtensions[ext] = ptr; + } +} + +std::unique_ptr SyntaxProcessor::highlight( + const std::string& ext, std::wstring_view source +) const { + const auto& found = langsExtensions.find(ext); + if (found == langsExtensions.end()) { + return nullptr; + } + const auto& syntax = *found->second; try { - if (lang == "lua") { - auto tokens = tokenize("", source); - return build_styles(tokens); - } else { - return nullptr; - } + auto tokens = tokenize(syntax, "", source); + return build_styles(tokens); } catch (const parsing_error& err) { return nullptr; } diff --git a/src/devtools/SyntaxProcessor.hpp b/src/devtools/SyntaxProcessor.hpp new file mode 100644 index 00000000..afb975a8 --- /dev/null +++ b/src/devtools/SyntaxProcessor.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct FontStylesScheme; + +namespace devtools { + struct Syntax; + + enum SyntaxStyles { + DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR + }; + + class SyntaxProcessor { + public: + std::unique_ptr highlight( + const std::string& ext, std::wstring_view source + ) const; + + void addSyntax(std::unique_ptr syntax); + private: + std::vector> langs; + std::unordered_map langsExtensions; + }; +} diff --git a/src/devtools/syntax_highlighting.hpp b/src/devtools/syntax_highlighting.hpp deleted file mode 100644 index e30b0084..00000000 --- a/src/devtools/syntax_highlighting.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -struct FontStylesScheme; - -namespace devtools { - enum SyntaxStyles { - DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR - }; - - std::unique_ptr syntax_highlight( - const std::string& lang, std::wstring_view source - ); -} diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 112ece7f..fb53978a 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -12,6 +12,7 @@ #include "coders/json.hpp" #include "coders/toml.hpp" #include "coders/commons.hpp" +#include "devtools/Editor.hpp" #include "content/ContentControl.hpp" #include "core_defs.hpp" #include "io/io.hpp" @@ -73,6 +74,7 @@ Engine& Engine::getInstance() { void Engine::initialize(CoreParameters coreParameters) { params = std::move(coreParameters); settingsHandler = std::make_unique(settings); + editor = std::make_unique(*this); cmd = std::make_unique(); network = network::Network::create(settings.network); @@ -134,6 +136,7 @@ void Engine::initialize(CoreParameters coreParameters) { ); } content = std::make_unique(paths, *input, [this]() { + editor->loadTools(); langs::setup(langs::get_current(), paths.resPaths.collectRoots()); if (!isHeadless()) { for (auto& pack : content->getAllContentPacks()) { diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index f352d12b..5e854ae3 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -33,6 +33,10 @@ namespace network { class Network; } +namespace devtools { + class Editor; +} + class initialize_error : public std::runtime_error { public: initialize_error(const std::string& message) : std::runtime_error(message) {} @@ -63,6 +67,7 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr window; std::unique_ptr input; std::unique_ptr gui; + std::unique_ptr editor; PostRunnables postRunnables; Time time; OnWorldOpen levelConsumer; @@ -161,4 +166,8 @@ public: cmd::CommandsInterpreter& getCmd() { return *cmd; } + + devtools::Editor& getEditor() { + return *editor; + } }; diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 79883136..828315e6 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -364,3 +364,7 @@ Input& GUI::getInput() { Window& GUI::getWindow() { return engine.getWindow(); } + +devtools::Editor& GUI::getEditor() { + return engine.getEditor(); +} diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index ca8f5508..b5c70bdd 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -18,6 +18,10 @@ class Engine; class Input; class Window; +namespace devtools { + class Editor; +} + /* Some info about padding and margin. Padding is element inner space, margin is outer @@ -159,5 +163,6 @@ namespace gui { const Input& getInput() const; Input& getInput(); Window& getWindow(); + devtools::Editor& getEditor(); }; } diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 3ab4306c..f271cc23 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -8,7 +8,8 @@ #include "../markdown.hpp" #include "Label.hpp" #include "assets/Assets.hpp" -#include "devtools/syntax_highlighting.hpp" +#include "devtools/Editor.hpp" +#include "devtools/SyntaxProcessor.hpp" #include "engine/Engine.hpp" #include "graphics/core/Batch2D.hpp" #include "graphics/core/DrawContext.hpp" @@ -811,7 +812,8 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) { void TextBox::refreshSyntax() { if (!syntax.empty()) { - if (auto styles = devtools::syntax_highlight(syntax, input)) { + const auto& processor = gui.getEditor().getSyntaxProcessor(); + if (auto styles = processor.highlight(syntax, input)) { label->setStyles(std::move(styles)); } } diff --git a/test/coders/lua_parsing.cpp b/test/coders/lua_parsing.cpp deleted file mode 100644 index 2bc547c4..00000000 --- a/test/coders/lua_parsing.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "coders/syntax_parser.hpp" - -#include - -#include "coders/commons.hpp" -#include "io/io.hpp" -#include "io/devices/StdfsDevice.hpp" -#include "util/stringutil.hpp" - -namespace fs = std::filesystem; - -TEST(lua_parsing, Tokenizer) { - io::set_device("res", std::make_shared(fs::u8path("../../res"))); - auto filename = "res:scripts/stdlib.lua"; - auto source = io::read_string(filename); - try { - auto tokens = devtools::tokenize(filename, util::str2wstr_utf8(source)); - for (const auto& token : tokens) { - std::cout << (int)token.tag << " " - << util::quote(util::wstr2str_utf8(token.text)) - << std::endl; - } - } catch (const parsing_error& err) { - std::cerr << err.errorLog() << std::endl; - throw err; - } -} From 7749675a61724f7236c12cee6186c5b28343499f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 13 Apr 2025 13:53:59 +0300 Subject: [PATCH 18/19] add EnumMetadata --- src/coders/GLSLExtension.cpp | 29 +++------ src/content/Content.hpp | 38 ------------ src/content/ContentLoader.cpp | 28 ++++----- src/content/content_fwd.hpp | 14 +++++ src/data/StructLayout.hpp | 1 - src/graphics/core/PostEffect.hpp | 9 +++ src/logic/EngineController.cpp | 3 +- .../scripting/lua/libs/lib__rigidbody.cpp | 12 ++-- src/logic/scripting/lua/libs/libblock.cpp | 3 +- src/objects/Entities.cpp | 7 +-- src/objects/Player.hpp | 1 - src/physics/Hitbox.cpp | 20 ------ src/physics/Hitbox.hpp | 9 ++- src/presets/NotePreset.cpp | 34 ++--------- src/presets/NotePreset.hpp | 10 ++- src/util/EnumMetadata.hpp | 61 +++++++++++++++++++ src/voxels/Block.cpp | 56 ----------------- src/voxels/Block.hpp | 18 ++++-- src/world/World.cpp | 3 +- src/world/files/WorldFiles.cpp | 6 +- 20 files changed, 156 insertions(+), 206 deletions(-) create mode 100644 src/util/EnumMetadata.hpp diff --git a/src/coders/GLSLExtension.cpp b/src/coders/GLSLExtension.cpp index 8af419e0..c9e31e31 100644 --- a/src/coders/GLSLExtension.cpp +++ b/src/coders/GLSLExtension.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "GLSLExtension.hpp" #include @@ -95,22 +96,6 @@ inline void source_line(std::stringstream& ss, uint linenum) { ss << "#line " << linenum << "\n"; } -static std::optional param_type_from( - const std::string& name -) { - static const std::unordered_map typeNames { - {"float", Type::FLOAT}, - {"vec2", Type::VEC2}, - {"vec3", Type::VEC3}, - {"vec4", Type::VEC4}, - }; - const auto& found = typeNames.find(name); - if (found == typeNames.end()) { - return std::nullopt; - } - return found->second; -} - static Value default_value_for(Type type) { switch (type) { case Type::FLOAT: @@ -212,11 +197,13 @@ public: } bool processParamDirective() { + using Param = PostEffect::Param; + skipWhitespace(false); // Parse type name auto typeName = parseName(); - auto type = param_type_from(typeName); - if (!type.has_value()) { + Param::Type type; + if (!Param::TypeMeta.getItem(typeName, type)) { throw error("unsupported param type " + util::quote(typeName)); } skipWhitespace(false); @@ -228,17 +215,17 @@ public: skipWhitespace(false); ss << "uniform " << typeName << " " << paramName << ";\n"; - auto defValue = default_value_for(type.value()); + auto defValue = default_value_for(type); // Parse default value if (peekNoJump() == '=') { skip(1); skipWhitespace(false); - defValue = parseDefaultValue(type.value(), typeName); + defValue = parseDefaultValue(type, typeName); } skipLine(); - params[paramName] = PostEffect::Param(type.value(), std::move(defValue)); + params[paramName] = PostEffect::Param(type, std::move(defValue)); return false; } diff --git a/src/content/Content.hpp b/src/content/Content.hpp index bbdf2013..539325a8 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -25,23 +24,6 @@ namespace rigging { class SkeletonConfig; } -constexpr const char* ContentType_name(ContentType type) { - switch (type) { - case ContentType::NONE: - return "none"; - case ContentType::BLOCK: - return "block"; - case ContentType::ITEM: - return "item"; - case ContentType::ENTITY: - return "entity"; - case ContentType::GENERATOR: - return "generator"; - default: - return "unknown"; - } -} - class namereuse_error : public std::runtime_error { ContentType type; public: @@ -185,26 +167,6 @@ public: } }; -constexpr const char* to_string(ResourceType type) { - switch (type) { - case ResourceType::CAMERA: - return "camera"; - case ResourceType::POST_EFFECT_SLOT: - return "post-effect-slot"; - default: - return "unknown"; - } -} - -inline std::optional ResourceType_from(std::string_view str) { - if (str == "camera") { - return ResourceType::CAMERA; - } else if (str == "post-effect-slot") { - return ResourceType::POST_EFFECT_SLOT; - } - return std::nullopt; -} - using ResourceIndicesSet = ResourceIndices[RESOURCE_TYPES_COUNT]; /// @brief Content is a definitions repository diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 3fb2b8f6..9b4326b5 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "ContentLoader.hpp" #include @@ -259,28 +260,25 @@ void ContentLoader::loadBlock( } // block model - std::string modelTypeName = to_string(def.model); + std::string modelTypeName = BlockModelMeta.getNameString(def.model); root.at("model").get(modelTypeName); root.at("model-name").get(def.modelName); - if (auto model = BlockModel_from(modelTypeName)) { - if (*model == BlockModel::custom && def.customModelRaw == nullptr) { + if (BlockModelMeta.getItem(modelTypeName, def.model)) { + if (def.model == BlockModel::custom && def.customModelRaw == nullptr) { if (root.has("model-primitives")) { def.customModelRaw = root["model-primitives"]; } else if (def.modelName.empty()) { throw std::runtime_error(name + ": no 'model-primitives' or 'model-name' found"); } } - def.model = *model; } else if (!modelTypeName.empty()) { logger.error() << "unknown model: " << modelTypeName; def.model = BlockModel::none; } - std::string cullingModeName = to_string(def.culling); + std::string cullingModeName = CullingModeMeta.getNameString(def.culling); root.at("culling").get(cullingModeName); - if (auto mode = CullingMode_from(cullingModeName)) { - def.culling = *mode; - } else { + if (!CullingModeMeta.getItem(cullingModeName, def.culling)) { logger.error() << "unknown culling mode: " << cullingModeName; } @@ -518,9 +516,7 @@ void ContentLoader::loadEntity( std::string bodyTypeName; root.at("body-type").get(bodyTypeName); - if (auto bodyType = BodyType_from(bodyTypeName)) { - def.bodyType = *bodyType; - } + BodyTypeMeta.getItem(bodyTypeName, def.bodyType); root.at("skeleton-name").get(def.skeletonName); root.at("blocking").get(def.blocking); @@ -799,8 +795,9 @@ void ContentLoader::load() { if (io::exists(resourcesFile)) { auto resRoot = io::read_json(resourcesFile); for (const auto& [key, arr] : resRoot.asObject()) { - if (auto resType = ResourceType_from(key)) { - loadResources(*resType, arr); + ResourceType type; + if (ResourceTypeMeta.getItem(key, type)) { + loadResources(type, arr); } else { // Ignore unknown resources logger.warning() << "unknown resource type: " << key; @@ -813,8 +810,9 @@ void ContentLoader::load() { if (io::exists(aliasesFile)) { auto resRoot = io::read_json(aliasesFile); for (const auto& [key, arr] : resRoot.asObject()) { - if (auto resType = ResourceType_from(key)) { - loadResourceAliases(*resType, arr); + ResourceType type; + if (ResourceTypeMeta.getItem(key, type)) { + loadResourceAliases(type, arr); } else { // Ignore unknown resources logger.warning() << "unknown resource type: " << key; diff --git a/src/content/content_fwd.hpp b/src/content/content_fwd.hpp index f6e2ffb7..f3b4061a 100644 --- a/src/content/content_fwd.hpp +++ b/src/content/content_fwd.hpp @@ -1,12 +1,21 @@ #pragma once #include "typedefs.hpp" +#include "util/EnumMetadata.hpp" class Content; class ContentPackRuntime; enum class ContentType { NONE, BLOCK, ITEM, ENTITY, GENERATOR }; +VC_ENUM_METADATA(ContentType) + {"none", ContentType::NONE}, + {"block", ContentType::BLOCK}, + {"item", ContentType::ITEM}, + {"entity", ContentType::ENTITY}, + {"generator", ContentType::GENERATOR}, +VC_ENUM_END + enum class ResourceType : size_t { CAMERA, POST_EFFECT_SLOT, @@ -15,3 +24,8 @@ enum class ResourceType : size_t { inline constexpr auto RESOURCE_TYPES_COUNT = static_cast(ResourceType::LAST) + 1; + +VC_ENUM_METADATA(ResourceType) + {"camera", ResourceType::CAMERA}, + {"post-effect-slot", ResourceType::POST_EFFECT_SLOT}, +VC_ENUM_END diff --git a/src/data/StructLayout.hpp b/src/data/StructLayout.hpp index 47d59b3b..8d35a226 100644 --- a/src/data/StructLayout.hpp +++ b/src/data/StructLayout.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include "typedefs.hpp" #include "interfaces/Serializable.hpp" diff --git a/src/graphics/core/PostEffect.hpp b/src/graphics/core/PostEffect.hpp index 4624ec40..9c4bd84f 100644 --- a/src/graphics/core/PostEffect.hpp +++ b/src/graphics/core/PostEffect.hpp @@ -7,6 +7,7 @@ #include #include "data/dv_fwd.hpp" +#include "util/EnumMetadata.hpp" class Shader; @@ -14,6 +15,14 @@ class PostEffect { public: struct Param { enum class Type { FLOAT, VEC2, VEC3, VEC4 }; + + VC_ENUM_METADATA(Type) + {"float", Type::FLOAT}, + {"vec2", Type::VEC2}, + {"vec3", Type::VEC3}, + {"vec4", Type::VEC4}, + VC_ENUM_END + using Value = std::variant; Type type; diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index ec125909..9cfbba30 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "EngineController.hpp" #include @@ -164,7 +165,7 @@ static dv::value create_missing_content_report( auto root = dv::object(); auto& contentEntries = root.list("content"); for (auto& entry : report->getMissingContent()) { - std::string contentName = ContentType_name(entry.type); + std::string contentName = ContentTypeMeta.getNameString(entry.type); auto& contentEntry = contentEntries.object(); contentEntry["type"] = contentName; contentEntry["name"] = entry.name; diff --git a/src/logic/scripting/lua/libs/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp index a5d3563a..d0e66940 100644 --- a/src/logic/scripting/lua/libs/lib__rigidbody.cpp +++ b/src/logic/scripting/lua/libs/lib__rigidbody.cpp @@ -1,3 +1,5 @@ +#define VC_ENABLE_REFLECTION + #include "util/stringutil.hpp" #include "libentity.hpp" @@ -96,8 +98,8 @@ static int l_set_crouching(lua::State* L) { static int l_get_body_type(lua::State* L) { if (auto entity = get_entity(L, 1)) { - return lua::pushstring( - L, to_string(entity->getRigidbody().hitbox.type) + return lua::pushlstring( + L, BodyTypeMeta.getName(entity->getRigidbody().hitbox.type) ); } return 0; @@ -105,9 +107,9 @@ static int l_get_body_type(lua::State* L) { static int l_set_body_type(lua::State* L) { if (auto entity = get_entity(L, 1)) { - if (auto type = BodyType_from(lua::tostring(L, 2))) { - entity->getRigidbody().hitbox.type = *type; - } else { + if (!BodyTypeMeta.getItem( + lua::tostring(L, 2), entity->getRigidbody().hitbox.type + )) { throw std::runtime_error( "unknown body type " + util::quote(lua::tostring(L, 2)) ); diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 3c813bd8..8963926f 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "content/Content.hpp" #include "content/ContentLoader.hpp" #include "content/ContentControl.hpp" @@ -311,7 +312,7 @@ static int l_get_textures(lua::State* L) { static int l_get_model(lua::State* L) { if (auto def = require_block(L)) { - return lua::pushstring(L, to_string(def->model)); + return lua::pushlstring(L, BlockModelMeta.getName(def->model)); } return 0; } diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 4e9f8894..4cfe1b29 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "Entities.hpp" #include @@ -216,9 +217,7 @@ void Entities::loadEntity(const dv::value& map, Entity entity) { dv::get_vec(bodymap, "vel", body.hitbox.velocity); std::string bodyTypeName; map.at("type").get(bodyTypeName); - if (auto bodyType = BodyType_from(bodyTypeName)) { - body.hitbox.type = *bodyType; - } + BodyTypeMeta.getItem(bodyTypeName, body.hitbox.type); bodymap["crouch"].asBoolean(body.hitbox.crouching); bodymap["damping"].asNumber(body.hitbox.linearDamping); } @@ -329,7 +328,7 @@ dv::value Entities::serialize(const Entity& entity) { if (def.save.body.settings) { bodymap["damping"] = rigidbody.hitbox.linearDamping; if (hitbox.type != def.bodyType) { - bodymap["type"] = to_string(hitbox.type); + bodymap["type"] = BodyTypeMeta.getNameString(hitbox.type); } if (hitbox.crouching) { bodymap["crouch"] = hitbox.crouching; diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp index cc60eb3f..c4d847b3 100644 --- a/src/objects/Player.hpp +++ b/src/objects/Player.hpp @@ -2,7 +2,6 @@ #include #include -#include #include "interfaces/Serializable.hpp" #include "settings.hpp" diff --git a/src/physics/Hitbox.cpp b/src/physics/Hitbox.cpp index f80dc0c5..83ad823c 100644 --- a/src/physics/Hitbox.cpp +++ b/src/physics/Hitbox.cpp @@ -2,26 +2,6 @@ #include -std::optional BodyType_from(std::string_view str) { - if (str == "kinematic") { - return BodyType::KINEMATIC; - } else if (str == "dynamic") { - return BodyType::DYNAMIC; - } else if (str == "static") { - return BodyType::STATIC; - } - return std::nullopt; -} - -std::string to_string(BodyType type) { - switch (type) { - case BodyType::KINEMATIC: return "kinematic"; - case BodyType::DYNAMIC: return "dynamic"; - case BodyType::STATIC: return "static"; - default: return "unknown"; - } -} - Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize) : type(type), position(position), diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 2c5a4e78..e825cefe 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -2,10 +2,10 @@ #include "maths/aabb.hpp" #include "typedefs.hpp" +#include "util/EnumMetadata.hpp" #include #include -#include #include #include @@ -41,8 +41,11 @@ enum class BodyType { STATIC, KINEMATIC, DYNAMIC }; -std::optional BodyType_from(std::string_view str); -std::string to_string(BodyType type); +VC_ENUM_METADATA(BodyType) + {"static", BodyType::STATIC}, + {"kinematic", BodyType::KINEMATIC}, + {"dynamic", BodyType::DYNAMIC}, +VC_ENUM_END struct Hitbox { BodyType type; diff --git a/src/presets/NotePreset.cpp b/src/presets/NotePreset.cpp index e03fd741..59865e80 100644 --- a/src/presets/NotePreset.cpp +++ b/src/presets/NotePreset.cpp @@ -1,37 +1,13 @@ +#define VC_ENABLE_REFLECTION + #include "NotePreset.hpp" - -#include -#include - #include "data/dv_util.hpp" -std::string to_string(NoteDisplayMode mode) { - static std::vector names = { - "static_billboard", - "y_free_billboard", - "xy_free_billboard", - "projected" - }; - return names.at(static_cast(mode)); -} - -std::optional NoteDisplayMode_from(std::string_view s) { - static std::map> map { - {"static_billboard", NoteDisplayMode::STATIC_BILLBOARD}, - {"y_free_billboard", NoteDisplayMode::Y_FREE_BILLBOARD}, - {"xy_free_billboard", NoteDisplayMode::XY_FREE_BILLBOARD}, - {"projected", NoteDisplayMode::PROJECTED} - }; - const auto& found = map.find(s); - if (found == map.end()) { - return std::nullopt; - } - return found->second; -} +#include dv::value NotePreset::serialize() const { return dv::object({ - {"display", to_string(displayMode)}, + {"display", NoteDisplayModeMeta.getNameString(displayMode)}, {"color", dv::to_value(color)}, {"scale", scale}, {"render_distance", renderDistance}, @@ -42,7 +18,7 @@ dv::value NotePreset::serialize() const { void NotePreset::deserialize(const dv::value& src) { if (src.has("display")) { - displayMode = NoteDisplayMode_from(src["display"].asString()).value(); + NoteDisplayModeMeta.getItem(src["display"].asString(), displayMode); } if (src.has("color")) { dv::get_vec(src["color"], color); diff --git a/src/presets/NotePreset.hpp b/src/presets/NotePreset.hpp index bc478fb4..fecdaf18 100644 --- a/src/presets/NotePreset.hpp +++ b/src/presets/NotePreset.hpp @@ -2,10 +2,10 @@ #include #include -#include #include #include "interfaces/Serializable.hpp" +#include "util/EnumMetadata.hpp" enum class NoteDisplayMode { STATIC_BILLBOARD, @@ -14,8 +14,12 @@ enum class NoteDisplayMode { PROJECTED }; -std::string to_string(NoteDisplayMode mode); -std::optional NoteDisplayMode_from(std::string_view s); +VC_ENUM_METADATA(NoteDisplayMode) + {"static_billboard", NoteDisplayMode::STATIC_BILLBOARD}, + {"y_free_billboard", NoteDisplayMode::Y_FREE_BILLBOARD}, + {"xy_free_billboard", NoteDisplayMode::XY_FREE_BILLBOARD}, + {"projected", NoteDisplayMode::PROJECTED}, +VC_ENUM_END struct NotePreset : public Serializable { NoteDisplayMode displayMode = NoteDisplayMode::STATIC_BILLBOARD; diff --git a/src/util/EnumMetadata.hpp b/src/util/EnumMetadata.hpp new file mode 100644 index 00000000..35f2b0d1 --- /dev/null +++ b/src/util/EnumMetadata.hpp @@ -0,0 +1,61 @@ +#pragma once + +#ifdef VC_ENABLE_REFLECTION +#define VC_ENUM_METADATA(NAME) static inline util::EnumMetadata NAME##Meta { +#define VC_ENUM_END }; + +#include +#include +#include + +namespace util { + template + class EnumMetadata { + public: + EnumMetadata( + std::initializer_list> items + ) + : items(items) { + for (const auto& [name, item] : items) { + names[item] = name; + } + } + + std::string_view getName(EnumT item) const { + const auto& found = names.find(item); + if (found == names.end()) { + return ""; + } + return found->second; + } + + std::string getNameString(EnumT item) const { + return std::string(getName(item)); + } + + bool getItem(std::string_view name, EnumT& dst) const { + const auto& found = items.find(name); + if (found == items.end()) { + return false; + } + dst = found->second; + return true; + } + + size_t size() const { + return items.size(); + } + private: + std::map items; + std::map names; + }; +} + +#else +#include + +#define VC_ENUM_METADATA(NAME) \ + struct NAME##__PAIR {const char* n; NAME i;}; \ + [[maybe_unused]] static inline std::initializer_list NAME##_PAIRS { +#define VC_ENUM_END }; +#endif // VC_ENABLE_REFLECTION diff --git a/src/voxels/Block.cpp b/src/voxels/Block.cpp index 7f08b3a7..7ef9d317 100644 --- a/src/voxels/Block.cpp +++ b/src/voxels/Block.cpp @@ -18,62 +18,6 @@ dv::value BlockMaterial::serialize() const { }); } -std::string to_string(BlockModel model) { - switch (model) { - case BlockModel::none: - return "none"; - case BlockModel::block: - return "block"; - case BlockModel::xsprite: - return "X"; - case BlockModel::aabb: - return "aabb"; - case BlockModel::custom: - return "custom"; - default: - return "unknown"; - } -} - -std::optional BlockModel_from(std::string_view str) { - if (str == "none") { - return BlockModel::none; - } else if (str == "block") { - return BlockModel::block; - } else if (str == "X") { - return BlockModel::xsprite; - } else if (str == "aabb") { - return BlockModel::aabb; - } else if (str == "custom") { - return BlockModel::custom; - } - return std::nullopt; -} - -std::string to_string(CullingMode mode) { - switch (mode) { - case CullingMode::DEFAULT: - return "default"; - case CullingMode::OPTIONAL: - return "optional"; - case CullingMode::DISABLED: - return "disabled"; - default: - return "unknown"; - } -} - -std::optional CullingMode_from(std::string_view str) { - if (str == "default") { - return CullingMode::DEFAULT; - } else if (str == "optional") { - return CullingMode::OPTIONAL; - } else if (str == "disabled") { - return CullingMode::DISABLED; - } - return std::nullopt; -} - CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ) : axes({axisX, axisY, axisZ}) { fix = glm::ivec3(0); diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 972b405f..6776ffa5 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -10,6 +9,7 @@ #include "maths/UVRegion.hpp" #include "maths/aabb.hpp" #include "typedefs.hpp" +#include "util/EnumMetadata.hpp" struct ParticlesPreset; @@ -93,8 +93,13 @@ enum class BlockModel { custom }; -std::string to_string(BlockModel model); -std::optional BlockModel_from(std::string_view str); +VC_ENUM_METADATA(BlockModel) + {"none", BlockModel::none}, + {"block", BlockModel::block}, + {"X", BlockModel::xsprite}, + {"aabb", BlockModel::aabb}, + {"custom", BlockModel::custom}, +VC_ENUM_END enum class CullingMode { DEFAULT, @@ -102,8 +107,11 @@ enum class CullingMode { DISABLED, }; -std::string to_string(CullingMode mode); -std::optional CullingMode_from(std::string_view str); +VC_ENUM_METADATA(CullingMode) + {"default", CullingMode::DEFAULT}, + {"optional", CullingMode::OPTIONAL}, + {"disabled", CullingMode::DISABLED}, +VC_ENUM_END using BoxModel = AABB; diff --git a/src/world/World.cpp b/src/world/World.cpp index 68842ae3..96264336 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "World.hpp" #include @@ -49,7 +50,7 @@ void World::updateTimers(float delta) { void World::writeResources(const Content& content) { auto root = dv::object(); for (size_t typeIndex = 0; typeIndex < RESOURCE_TYPES_COUNT; typeIndex++) { - auto typeName = to_string(static_cast(typeIndex)); + auto typeName = ResourceTypeMeta.getNameString(static_cast(typeIndex)); auto& list = root.list(typeName); auto& indices = content.resourceIndices[typeIndex]; for (size_t i = 0; i < indices.size(); i++) { diff --git a/src/world/files/WorldFiles.cpp b/src/world/files/WorldFiles.cpp index 3617ad7c..e4137f03 100644 --- a/src/world/files/WorldFiles.cpp +++ b/src/world/files/WorldFiles.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "WorldFiles.hpp" #include @@ -182,8 +183,9 @@ bool WorldFiles::readResourcesData(const Content& content) { } auto root = io::read_json(file); for (const auto& [key, arr] : root.asObject()) { - if (auto resType = ResourceType_from(key)) { - read_resources_data(content, arr, *resType); + ResourceType type; + if (ResourceTypeMeta.getItem(key, type)) { + read_resources_data(content, arr, type); } else { logger.warning() << "unknown resource type: " << key; } From 3be8546bf4b3ada1694a7e21ca79c59941c7d0e6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 13 Apr 2025 14:18:50 +0300 Subject: [PATCH 19/19] refactor enums: CursorShape, InterpolationType, ParticleSpawnShape --- src/content/loading/GeneratorLoader.cpp | 9 ++---- src/graphics/core/commons.cpp | 39 ------------------------- src/graphics/core/commons.hpp | 16 ++++++++-- src/graphics/ui/gui_xml.cpp | 6 ++-- src/logic/scripting/lua/libs/libgui.cpp | 9 +++--- src/maths/Heightmap.cpp | 11 ------- src/maths/Heightmap.hpp | 7 ++++- src/presets/ParticlesPreset.cpp | 26 +++-------------- src/presets/ParticlesPreset.hpp | 12 +++++--- 9 files changed, 44 insertions(+), 91 deletions(-) delete mode 100644 src/graphics/core/commons.cpp diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index a845bc21..2f8f40d5 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "../ContentLoader.hpp" #include @@ -210,13 +211,9 @@ void ContentLoader::loadGenerator( map.at("heights-bpd").get(def.heightsBPD); std::string interpName; map.at("heights-interpolation").get(interpName); - if (auto interp = InterpolationType_from(interpName)) { - def.heightsInterpolation = *interp; - } + InterpolationTypeMeta.getItem(interpName, def.heightsInterpolation); map.at("biomes-interpolation").get(interpName); - if (auto interp = InterpolationType_from(interpName)) { - def.biomesInterpolation = *interp; - } + InterpolationTypeMeta.getItem(interpName, def.biomesInterpolation); map.at("sea-level").get(def.seaLevel); map.at("wide-structs-chunks-radius").get(def.wideStructsChunksRadius); diff --git a/src/graphics/core/commons.cpp b/src/graphics/core/commons.cpp deleted file mode 100644 index 98cc5c13..00000000 --- a/src/graphics/core/commons.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "commons.hpp" - -#include - -std::optional CursorShape_from(std::string_view name) { - static std::map shapes = { - {"arrow", CursorShape::ARROW}, - {"text", CursorShape::TEXT}, - {"crosshair", CursorShape::CROSSHAIR}, - {"pointer", CursorShape::POINTER}, - {"ew-resize", CursorShape::EW_RESIZE}, - {"ns-resize", CursorShape::NS_RESIZE}, - {"nwse-resize", CursorShape::NWSE_RESIZE}, - {"nesw-resize", CursorShape::NESW_RESIZE}, - {"all-resize", CursorShape::ALL_RESIZE}, - {"not-allowed", CursorShape::NOT_ALLOWED} - }; - const auto& found = shapes.find(name); - if (found == shapes.end()) { - return std::nullopt; - } - return found->second; -} - -std::string to_string(CursorShape shape) { - static std::string names[] = { - "arrow", - "text", - "crosshair", - "pointer", - "ew-resize", - "ns-resize", - "nwse-resize", - "nesw-resize", - "all-resize", - "not-allowed" - }; - return names[static_cast(shape)]; -} diff --git a/src/graphics/core/commons.hpp b/src/graphics/core/commons.hpp index 80819206..7c26b75b 100644 --- a/src/graphics/core/commons.hpp +++ b/src/graphics/core/commons.hpp @@ -3,6 +3,8 @@ #include #include +#include "util/EnumMetadata.hpp" + enum class DrawPrimitive { point = 0, line, @@ -48,8 +50,18 @@ enum class CursorShape { LAST=NOT_ALLOWED }; -std::optional CursorShape_from(std::string_view name); -std::string to_string(CursorShape shape); +VC_ENUM_METADATA(CursorShape) + {"arrow", CursorShape::ARROW}, + {"text", CursorShape::TEXT}, + {"crosshair", CursorShape::CROSSHAIR}, + {"pointer", CursorShape::POINTER}, + {"ew-resize", CursorShape::EW_RESIZE}, + {"ns-resize", CursorShape::NS_RESIZE}, + {"nwse-resize", CursorShape::NWSE_RESIZE}, + {"nesw-resize", CursorShape::NESW_RESIZE}, + {"all-resize", CursorShape::ALL_RESIZE}, + {"not-allowed", CursorShape::NOT_ALLOWED}, +VC_ENUM_END class Flushable { public: diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 61719f29..31c7bd56 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "gui_xml.hpp" #include @@ -168,8 +169,9 @@ static void read_uinode( node.setTooltipDelay(element.attr("tooltip-delay").asFloat()); } if (element.has("cursor")) { - if (auto cursor = CursorShape_from(element.attr("cursor").getText())) { - node.setCursor(*cursor); + CursorShape cursor; + if (CursorShapeMeta.getItem(element.attr("cursor").getText(), cursor)) { + node.setCursor(cursor); } } diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 4a5f4ec9..2df7f322 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "libgui.hpp" #include "assets/Assets.hpp" #include "engine/Engine.hpp" @@ -422,7 +423,7 @@ static int p_get_line_pos(UINode*, lua::State* L) { } static int p_get_cursor(UINode* node, lua::State* L) { - return lua::pushstring(L, to_string(node->getCursor())); + return lua::pushlstring(L, CursorShapeMeta.getName(node->getCursor())); } static int p_get_scroll(UINode* node, lua::State* L) { @@ -660,9 +661,9 @@ static void p_set_focused( } static void p_set_cursor(UINode* node, lua::State* L, int idx) { - if (auto cursor = CursorShape_from(lua::require_string(L, idx))) { - node->setCursor(*cursor); - } + auto cursor = CursorShape::ARROW; // reset to default + CursorShapeMeta.getItem(lua::require_string(L, idx), cursor); + node->setCursor(cursor); } static int p_set_scroll(UINode* node, lua::State* L, int idx) { diff --git a/src/maths/Heightmap.cpp b/src/maths/Heightmap.cpp index ab6ad153..99c27098 100644 --- a/src/maths/Heightmap.cpp +++ b/src/maths/Heightmap.cpp @@ -5,17 +5,6 @@ #include #include -std::optional InterpolationType_from(std::string_view str) { - if (str == "nearest") { - return InterpolationType::NEAREST; - } else if (str == "linear") { - return InterpolationType::LINEAR; - } else if (str == "cubic") { - return InterpolationType::CUBIC; - } - return std::nullopt; -} - static inline float sample_at( const float* buffer, uint width, diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp index a5bd4f4d..1c4c7ff0 100644 --- a/src/maths/Heightmap.hpp +++ b/src/maths/Heightmap.hpp @@ -6,6 +6,7 @@ #include "typedefs.hpp" #include "maths/Heightmap.hpp" +#include "util/EnumMetadata.hpp" enum class InterpolationType { NEAREST, @@ -13,7 +14,11 @@ enum class InterpolationType { CUBIC, }; -std::optional InterpolationType_from(std::string_view str); +VC_ENUM_METADATA(InterpolationType) + {"nearest", InterpolationType::NEAREST}, + {"linear", InterpolationType::LINEAR}, + {"cubic", InterpolationType::CUBIC}, +VC_ENUM_END class Heightmap { uint width, height; diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp index 8a007076..df129f1f 100644 --- a/src/presets/ParticlesPreset.cpp +++ b/src/presets/ParticlesPreset.cpp @@ -1,26 +1,8 @@ +#define VC_ENABLE_REFLECTION + #include "ParticlesPreset.hpp" - #include "data/dv_util.hpp" -std::string to_string(ParticleSpawnShape shape) { - static std::string names[] = { - "ball", - "sphere", - "box" - }; - return names[static_cast(shape)]; -} - -ParticleSpawnShape ParticleSpawnShape_from(std::string_view s) { - if (s == "ball") { - return ParticleSpawnShape::BALL; - } else if (s == "sphere") { - return ParticleSpawnShape::SPHERE; - } else { - return ParticleSpawnShape::BOX; - } -} - dv::value ParticlesPreset::serialize() const { auto root = dv::object(); if (frames.empty()) { @@ -47,7 +29,7 @@ dv::value ParticlesPreset::serialize() const { root["min_angular_vel"] = minAngularVelocity; root["max_angular_vel"] = maxAngularVelocity; root["spawn_spread"] = dv::to_value(size); - root["spawn_shape"] = to_string(spawnShape); + root["spawn_shape"] = ParticleSpawnShapeMeta.getName(spawnShape); root["random_sub_uv"] = randomSubUV; return root; } @@ -82,7 +64,7 @@ void ParticlesPreset::deserialize(const dv::value& src) { dv::get_vec(src["explosion"], explosion); } if (src.has("spawn_shape")) { - spawnShape = ParticleSpawnShape_from(src["spawn_shape"].asString()); + ParticleSpawnShapeMeta.getItem(src["spawn_shape"].asString(), spawnShape); } if (src.has("frames")) { for (const auto& frame : src["frames"]) { diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index 4b271849..dd5a66d5 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -5,8 +5,9 @@ #include #include "interfaces/Serializable.hpp" +#include "util/EnumMetadata.hpp" -enum ParticleSpawnShape { +enum class ParticleSpawnShape { /// @brief Coordinates are regulary distributed within /// the volume of a ball. BALL = 0, @@ -18,8 +19,11 @@ enum ParticleSpawnShape { BOX }; -std::string to_string(ParticleSpawnShape shape); -ParticleSpawnShape ParticleSpawnShape_from(std::string_view s); +VC_ENUM_METADATA(ParticleSpawnShape) + {"ball", ParticleSpawnShape::BALL}, + {"sphere", ParticleSpawnShape::SPHERE}, + {"box", ParticleSpawnShape::BOX}, +VC_ENUM_END struct ParticlesPreset : public Serializable { /// @brief Collision detection @@ -53,7 +57,7 @@ struct ParticlesPreset : public Serializable { /// @brief Maximum angular velocity float maxAngularVelocity = 0.0f; /// @brief Spawn spread shape - ParticleSpawnShape spawnShape = BALL; + ParticleSpawnShape spawnShape = ParticleSpawnShape::BALL; /// @brief Spawn spread glm::vec3 spawnSpread {}; /// @brief Texture name