diff --git a/dev/tests/bytearray.lua b/dev/tests/bytearray.lua index 415768c65..71e895d37 100644 --- a/dev/tests/bytearray.lua +++ b/dev/tests/bytearray.lua @@ -18,19 +18,86 @@ Bytearray.insert(arr, {5, 3, 6}) assert(#arr == 12) Bytearray.insert(arr, 2, 8) -assert(#arr == 13) +asserts.equals(13, #arr) for i=1,10 do assert(arr[i] == 10 - i) end print(#arr, arr:get_capacity()) arr:trim() -assert(#arr == arr:get_capacity()) +asserts.equals(#arr, arr:get_capacity()) arr = Bytearray({0, 2, 7, 1, 16, 75, 25}) assert(arr[6] == 75) arr:insert(2, {5, 6}) -assert(arr[#arr] == 25) -assert(arr[2] == 5) -assert(arr[3] == 6) +asserts.equals(25, arr[#arr]) +asserts.equals(5, arr[2]) +asserts.equals(6, arr[3]) + +-- ============================= + +arr = Bytearray({254, 255, 255, 255, 255, 255, 255, 255}) +asserts.equals(U64view(arr)[1], ctypes.uint64(2) ^ 64 - 2) +asserts.equals(I64view(arr)[1], -2) +asserts.equals(U32view(arr)[1], 2 ^ 32 - 2) +asserts.equals(I32view(arr)[1], -2) +asserts.equals(U16view(arr)[1], 2 ^ 16 - 2) +asserts.equals(I16view(arr)[1], -2) +asserts.equals(I8view(arr)[1], -2) + +asserts.equals(I8view(arr)[2], -1) +asserts.equals(I32view(arr)[2], -1) + +arr = Bytearray({0xFF, 0x7F}) +local arri16 = I16view(arr) +local arru16 = U16view(arr) +asserts.equals(arri16[1], 32767) + +arru16[1] = arru16[1] + 1 +asserts.equals(arri16[1], -32768) +asserts.equals(arru16[1], 32768) + +arr = Bytearray({0x9A, 0x99, 0x59, 0x40}) -- approx. equal 3.4 in hexadecimal representation of single-precision float number. +local arrflt = FLTview(arr) +local arru32 = U32view(arr) +asserts.equals(arrflt[1], 3.4000000953674316) +asserts.equals(arru32[1], 0x4059999A) + +arrflt[1] = arrflt[1] + 0.1 +asserts.equals(arrflt[1], 3.5) +asserts.equals(arru32[1], 0x40600000) -- hexadecimal representation of 3.5 single-precision float number. + +arr = Bytearray({0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 9, 64}) -- hexadecimal representation of 3.141592653589793 double-precision float number. +assert(DBLview(arr)[1] == 3.141592653589793) + +-- ============================================= + +function barrtostr(barr) + local outstr = "" + for i = 1, #barr do + local lastfmt = "" + if i ~= #barr then lastfmt = "|" end + outstr = outstr .. barr[i] .. lastfmt + end + return outstr +end + +arr = Bytearray({0, 5, 34, 87, 21, 0, 210}) +asserts.equals(barrtostr(arr), "0|5|34|87|21|0|210") +arr:move(2, 3, 4) +asserts.equals(barrtostr(arr), "0|5|5|34|87|21|210") + +local arr2 = Bytearray({1, 2, 3, 4, 6, 7, 8}) +asserts.equals(barrtostr(arr2), "1|2|3|4|6|7|8") +arr:copy(4, arr2, 2, 3) +asserts.equals(barrtostr(arr2), "1|34|87|21|6|7|8") + +arr:fill(nil, nil, 192) +asserts.equals(barrtostr(arr), "192|192|192|192|192|192|192") +arr:fill(nil, 3, 233) +asserts.equals(barrtostr(arr), "233|233|233|233|233|233|233") +arr:fill(2, nil, 254) +asserts.equals(barrtostr(arr), "254|254|254|254|254|254|254") +arr:fill(2, 2, 66) +asserts.equals(barrtostr(arr), "254|66|66|254|254|254|254") diff --git a/doc/en/scripting.md b/doc/en/scripting.md index 09cf40ef4..2072d4353 100644 --- a/doc/en/scripting.md +++ b/doc/en/scripting.md @@ -16,6 +16,7 @@ Subsections: - [block](scripting/builtins/libblock.md) - [byteutil](scripting/builtins/libbyteutil.md) - [cameras](scripting/builtins/libcameras.md) + - [ctypes](scripting/builtins/libctypes.md) - [entities](scripting/builtins/libentities.md) - [file](scripting/builtins/libfile.md) - [gfx.blockwraps](scripting/builtins/libgfx-blockwraps.md) diff --git a/doc/en/scripting/builtins/libctypes.md b/doc/en/scripting/builtins/libctypes.md new file mode 100644 index 000000000..5517af0fa --- /dev/null +++ b/doc/en/scripting/builtins/libctypes.md @@ -0,0 +1,28 @@ +# *ctypes* Library + +A library for safe work with C types. + +| ctypes.\ | C-Type | Minimum | Maximum | +| --------------- | -------- | -------------------- | -------------------- | +| uint8 | uint8_t | 0 | 255 | +| uint16 | uint16_t | 0 | 65535 | +| uint32 | uint32_t | 0 | 4294967295 | +| uint64 | uint64_t | 0 | 18446744073709551615 | +| int8 | int8_t | -128 | 127 | +| int16 | int16_t | -32768 | 32767 | +| int32 | int32_t | -2147483648 | 2147483647 | +| int64 | int64_t | -9223372036854775808 | 9223372036854775807 | + + +Usage examples: + +```lua +local x = ctypes.uint64(1234) +print(x) --> 1234ULL (cdata) +local y = ctypes.uint64("18446744073709551615") +print(y) --> 18446744073709551615ULL (cdata) +print(x + y) --> 1233ULL + +local z = tonumber(x) +print(z) --> 1234 (number) +``` diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index db3fcd921..1e6d73098 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -16,6 +16,7 @@ - [block](scripting/builtins/libblock.md) - [byteutil](scripting/builtins/libbyteutil.md) - [cameras](scripting/builtins/libcameras.md) + - [ctypes](scripting/builtins/libctypes.md) - [entities](scripting/builtins/libentities.md) - [file](scripting/builtins/libfile.md) - [gfx.blockwraps](scripting/builtins/libgfx-blockwraps.md) diff --git a/doc/ru/scripting/builtins/libctypes.md b/doc/ru/scripting/builtins/libctypes.md new file mode 100644 index 000000000..d6c985b44 --- /dev/null +++ b/doc/ru/scripting/builtins/libctypes.md @@ -0,0 +1,28 @@ +# Библиотека *ctypes* + +Библиотека для безопасной работы с типами языка С. + +| ctypes.\ | С-тип | Минимум | Максимум | +| ------------- | -------- | -------------------- | -------------------- | +| uint8 | uint8_t | 0 | 255 | +| uint16 | uint16_t | 0 | 65535 | +| uint32 | uint32_t | 0 | 4294967295 | +| uint64 | uint64_t | 0 | 18446744073709551615 | +| int8 | int8_t | -128 | 127 | +| int16 | int16_t | -32768 | 32767 | +| int32 | int32_t | -2147483648 | 2147483647 | +| int64 | int64_t | -9223372036854775808 | 9223372036854775807 | + + +Примеры использования: + +```lua +local x = ctypes.uint64(1234) +print(x) --> 1234ULL (cdata) +local y = ctypes.uint64("18446744073709551615") +print(y) --> 18446744073709551615ULL (cdata) +print(x + y) --> 1233ULL + +local z = tonumber(x) +print(z) --> 1234 (number) +``` diff --git a/doc/ru/scripting/bytearray.md b/doc/ru/scripting/bytearray.md index c90b6d67e..d15cd6a58 100644 --- a/doc/ru/scripting/bytearray.md +++ b/doc/ru/scripting/bytearray.md @@ -2,6 +2,8 @@ *Bytearray* - динамический байтовый массив, реализованный через LuaJIT FFI. По нему можно итерироваться стандартными функциями **pairs** и **ipairs** +Примечание касаемо условных обозначений в документации: псведотип `posint` означает натуральное (положительное ненулевое целое) число. + ## Основное ### Создание массива ```lua @@ -22,29 +24,64 @@ bytes:insert(index: int, value: int) -- Удаляет элемент(ы), если передан `count`, удалит `count` кол-во элементов с выбранного индекса. bytes:remove(index: int, [опционально] count: int) --- Очищает массив +-- Очищает массив. bytes:clear() +--[[ +Заполняет массив определённым байтом полностью либо в определённом диапазоне. +Логика работы следующая: если параметр size или index равны nil, то указанным байтом будет заполнен весь массив, + иначе будет заполнен диапазон внутри массива начиная с индекса index и размером size. +В случае выхода заполняемого блока за границы массива генерирует соответствующую ошибку. +]] +bytes:fill(index: posint/nil, size: posint/nil, byte: uint8) + -- Создаёт новый Bytearray, содержащий копию части данных с offset до offset+length bytes:slice(offset: int, length: int) -> Bytearray + +--[[ +Копирует блок байт из исходного массива, начиная с индекса srcindex, в массив dst, начиная в нём с индекса dstindex. +В случае выхода за границы исходного, конечного массива, либо при некорректном значении параметра size генерирует соответствующую ошибку. +]] +bytes:copy(srcindex: posint, dst: Bytearray, dstindex: posint, size: posint) + +--[[ +Безопасно копирует перекрывающиеся области памяти (байт) в пределах одного массива. +В случае выхода за пределы массива начального или конечного диапазона байт генерирует соответствующую ошибку. +]] +bytes:move(fromindex: posint, toindex: posint, size: posint) ``` ## View Это "вьюшки" поверх Bytearray, которые интерпретируют его байты как массив чисел другого размера - **без копирования данных**. -Не имеет своих методов, является просто объектной ссылкой на оригинальный Bytearray. +Практически не имеют своих методов, являются простыми объектными ссылками на оригинальный Bytearray. -| Класс | Тип элементов | Размер | -| ------------ | ------------------ | ------- | -| `I16view` | `int16_t` | 2 байта | -| `U16view` | `uint16_t` | 2 байта | -| `I32view` | `int32_t` | 4 байта | -| `U32view` | `uint32_t` | 4 байта | +| Класс | Тип элементов | Размер | +| ------------ | ------------------ | --------- | +| `I8view` | `int8_t` | 1 байт | +| `I16view` | `int16_t` | 2 байта | +| `U16view` | `uint16_t` | 2 байта | +| `I32view` | `int32_t` | 4 байта | +| `U32view` | `uint32_t` | 4 байта | +| `I64view` | `int64_t` | 8 байт | +| `U64view` | `uint64_t` | 8 байт | +| `FLTview` | `float` | 4 байта* | +| `DBLview` | `double` | 8 байт* | ---- +* Размеры "оригинальных" C-шных типов являются платформозависимыми и их точный размер не описан в стандарте языка. В данной таблице предоставлены наиболее распространённые размеры типов. -### Пример использования +### Специфические методы "вьюшек" + +```lua +--[[ +Возвращает размер типа вьюшки. Результат тот же, что и от применения оператора sizeof() из языка C для данного типа. + Полезно для вьюшек, использующих C-шные типы нефиксированного размера, например те же float или double. +]] +view:typesize() -> posint +``` + +## Пример использования ```lua local bytes = Bytearray({1, 0, 2, 0, 250, 255}) @@ -59,4 +96,4 @@ print(i16[3]) -- -6 (signed вьюшка) for _, num in ipairs(i16) do print(num) end -- 1; 2; -6 -``` \ No newline at end of file +``` diff --git a/res/content/base/modules/util.lua b/res/content/base/modules/util.lua index b1a97ad06..874e6d3d6 100644 --- a/res/content/base/modules/util.lua +++ b/res/content/base/modules/util.lua @@ -12,14 +12,14 @@ function util.drop(ppos, itemid, count, data, pickup_delay) }}) end -local function calc_loot(loot_table) +function util.calc_loot(loot_table) local results = {} for _, loot in ipairs(loot_table) do local chance = loot.chance or 1 local count = loot.count or 1 - + local roll = math.random() - + if roll < chance then if loot.min and loot.max then count = math.random(loot.min, loot.max) @@ -37,7 +37,7 @@ end function util.block_loot(blockid) local lootscheme = block.properties[blockid]["base:loot"] if lootscheme then - return calc_loot(lootscheme) + return util.calc_loot(lootscheme) end return {{item=block.get_picking_item(blockid), count=1}} end diff --git a/res/content/base/scripts/grass_block.lua b/res/content/base/scripts/grass_block.lua index fb7482c20..b5965de43 100644 --- a/res/content/base/scripts/grass_block.lua +++ b/res/content/base/scripts/grass_block.lua @@ -1,3 +1,6 @@ +local dirtid = block.index("base:dirt") +local grass_blockid = block.index("base:grass_block") + local offsets = {} for lx=-1,1 do for ly=-1,1 do @@ -8,18 +11,17 @@ for lx=-1,1 do end function on_random_update(x, y, z) - local dirtid = block.index('base:dirt'); if block.is_solid_at(x, y+1, z) then block.set(x, y, z, dirtid, 0) return end - local grassblockid = block.index('base:grass_block') + for _, offset in ipairs(offsets) do local nx, ny, nz = x + offset[1], y + offset[2], z + offset[3] if block.get(nx, ny, nz) == dirtid and not block.is_solid_at(nx, ny + 1, nz) then - block.set(nx, ny, nz, grassblockid, 0) + block.set(nx, ny, nz, grass_blockid, 0) return end end diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index cb63a96c7..6d684b130 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -179,40 +179,40 @@ function place_pack(panel, packinfo, callback, position_func) end end -local Version = {}; +local Version = {} function Version.matches_pattern(version) - for _, letter in string.gmatch(version, "%.+") do - if type(letter) ~= "number" or letter ~= "." then - return false; - end + local t = string.split(version, ".") + if #t ~= 2 and #t ~= 3 then return false end - local t = string.split(version, "."); - - return #t == 2 or #t == 3; + for i = 1, #t do + local matched = string.match(t[i], "^%d+$") + if not matched then return false end end + + return true end function Version.__equal(ver1, ver2) - return ver1[1] == ver2[1] and ver1[2] == ver2[2] and ver1[3] == ver2[3]; + return ver1[1] == ver2[1] and ver1[2] == ver2[2] and ver1[3] == ver2[3] end function Version.__greater(ver1, ver2) - if ver1[1] ~= ver2[1] then return ver1[1] > ver2[1] end; - if ver1[2] ~= ver2[2] then return ver1[2] > ver2[2] end; - return ver1[3] > ver2[3]; + if ver1[1] ~= ver2[1] then return ver1[1] > ver2[1] end + if ver1[2] ~= ver2[2] then return ver1[2] > ver2[2] end + return ver1[3] > ver2[3] end function Version.__less(ver1, ver2) - return Version.__greater(ver2, ver1); + return Version.__greater(ver2, ver1) end function Version.__greater_or_equal(ver1, ver2) - return not Version.__less(ver1, ver2); + return not Version.__less(ver2, ver1) end function Version.__less_or_equal(ver1, ver2) - return not Version.__greater(ver1, ver2); + return not Version.__greater(ver1, ver2) end Version.operators = { @@ -224,40 +224,39 @@ Version.operators = { } function Version.compare(op, ver1, ver2) - ver1 = string.split(ver1, "."); - ver2 = string.split(ver2, "."); + ver1 = string.split(ver1, ".") + ver2 = string.split(ver2, ".") local comparison_func = Version.operators[op]; if comparison_func then - return comparison_func(ver1, ver2); + return comparison_func(ver1, ver2) else - return false; + return false end end function Version.parse(version) - local op = string.sub(version, 1, 2); + local op = string.sub(version, 1, 2) if op == ">=" or op == "<=" then - return op, string.sub(version, #op + 1); + return op, string.sub(version, #op + 1) end op = string.sub(version, 1, 1); if op == ">" or op == "<" then - return op, string.sub(version, #op + 1); + return op, string.sub(version, #op + 1) end - return "=", version; + return "=", version end -local function compare_version(dependent_version, actual_version) +local function compare_version(op, dependent_version, actual_version) if Version.matches_pattern(dependent_version) and Version.matches_pattern(actual_version) then - local op, dep_ver = Version.parse_version(dependent_version); - Version.compare(op, dep_ver, actual_version); + return Version.compare(op, dep_ver, actual_version) elseif dependent_version == "*" or dependent_version == actual_version then - return true; + return true else - return false; + return false end end @@ -277,15 +276,15 @@ function check_dependencies(packinfo) ) end - local dep_pack = pack.get_info(depid); - - if not compare_version(depver, dep_pack.version) then - local op, ver = Version.parse(depver) + local dep_pack = pack.get_info(depid) + local op, ver = Version.parse(depver) + + if not compare_version(op, ver, dep_pack.version) then return string.format( "%s: %s != %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, ver, depid - ); + ) end if table.has(packs_installed, packinfo.id) then diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index 58b33a520..896f43188 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -4,12 +4,14 @@ local FFI = ffi FFI.cdef[[ void* malloc(size_t); + void* realloc(void*, size_t); void free(void*); typedef struct { unsigned char* bytes; int size; int capacity; } bytearray_t; + void* memmove(void*, const void*, size_t); ]] local free = FFI.C.free @@ -22,12 +24,33 @@ local GC_THRESHOLD = 50 * 1024 * 1024 local function malloc(size) local raw = FFI.C.malloc(size) - if raw == nil then - return nil + if not raw then + error("failed memory allocation of " .. size .. " bytes") end allocated_bytes = allocated_bytes + size + if allocated_bytes >= GC_THRESHOLD then + collectgarbage("step", 200) + allocated_bytes = 0 + end + return raw +end + +local function realloc(buff, size, throwerror) + if throwerror == nil then throwerror = true end + + if size <= 0 then + free(buff) + return + end + + local raw = FFI.C.realloc(buff, size) + if not raw and throwerror then + error("failed memory reallocation of block " .. buff .. " to new size " .. size) + end + + allocated_bytes = allocated_bytes + size if allocated_bytes >= GC_THRESHOLD then collectgarbage("step", 200) allocated_bytes = 0 @@ -38,23 +61,17 @@ end 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.bytes = realloc(self.bytes, new_capacity) self.capacity = new_capacity - free(prev) end local function trim_buffer(self) - if self.size == self.capacity then - return + if self.size == self.capacity then return end + local new_bytes = realloc(self.bytes, self.size, false) + if new_bytes then + self.bytes = new_bytes + self.capacity = self.size 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) @@ -125,15 +142,21 @@ local function clear(self) self.size = 0 end -local function reserve(self, new_capacity) - if new_capacity <= self.capacity then - return +local function fill(self, index, size, byte) + if index ~= nil and size ~= nil and size > 0 then + if index < 1 or index + size - 1 > self.size then + error("selected fill range is out of array bounds") + end + FFI.fill(self.bytes + index - 1, size, byte) + else + FFI.fill(self.bytes, self.size, byte) end - local prev = self.bytes - self.bytes = malloc(new_capacity) - FFI.copy(self.bytes, prev, self.size) +end + +local function reserve(self, new_capacity) + if new_capacity <= self.capacity then return end + self.bytes = realloc(self.bytes, new_capacity) self.capacity = new_capacity - free(prev) end local function get_capacity(self) @@ -150,22 +173,46 @@ local function slice(self, offset, length) length = self.size - offset + 1 end local buffer = malloc(length) - if not buffer then - error("malloc(" .. length .. ") returned NULL") - end FFI.copy(buffer, self.bytes + (offset - 1), length) return bytearray_type(buffer, length, length) end +local function copy(self, srcindex, dst, dstindex, size) + if size <= 0 then error("size of byte range must be positive non-zero integer") end + if srcindex < 1 or srcindex + size - 1 > self.size then + error("specified source byte range is out of source array bounds") + end + if dstindex < 1 or dstindex + size - 1 > dst.size then + error("specified destination byte range is out of destination array bounds") + end + + FFI.copy(dst.bytes + dstindex - 1, self.bytes + srcindex - 1, size) +end + +local function move(self, fromindex, toindex, size) + if size <= 0 then error("size of byte range must be positive non-zero integer") end + if fromindex < 1 or fromindex + size - 1 > self.size then + error("specified source byte range is out of array bounds") + end + if toindex < 1 or toindex + size - 1 > self.size then + error("specified destination byte range is out of array bounds") + end + + FFI.C.memmove(self.bytes + toindex - 1, self.bytes + fromindex - 1, size) +end + local bytearray_methods = { - append=append, - insert=insert, - remove=remove, - trim=trim_buffer, - clear=clear, - reserve=reserve, - get_capacity=get_capacity, - slice=slice, + append = append, + insert = insert, + remove = remove, + trim = trim_buffer, + clear = clear, + reserve = reserve, + get_capacity = get_capacity, + slice = slice, + fill = fill, + copy = copy, + move = move } local bytearray_mt = { @@ -256,7 +303,8 @@ local function FFIBytearray_as_string(bytes) end end -local function create_FFIview_class(name, typename, typesize) +local function create_FFIview_class(name, typename) + local typesize = FFI.sizeof(typename) local ptrtype = typename .. "*" local FFIview_mt = { __index = function(self, key) @@ -291,27 +339,44 @@ local function create_FFIview_class(name, typename, typesize) return i, ptr[i - 1] end end - end + end, } - return function (bytes) - local x = setmetatable({ - bytes=bytes, - }, FFIview_mt) - return x - end + return setmetatable({}, { + __call = function (self, bytes) + local x = setmetatable({ + bytes=bytes, + }, FFIview_mt) + return x + end, + __index = { + typesize = function() + return typesize + end + } + }) end -local FFII16view = create_FFIview_class("FFII16view", "int16_t", 2) -local FFIU16view = create_FFIview_class("FFIU16view", "uint16_t", 2) -local FFII32view = create_FFIview_class("FFII32view", "int32_t", 4) -local FFIU32view = create_FFIview_class("FFIU32view", "uint32_t", 4) +local FFII8view = create_FFIview_class("FFII8view", "int8_t") +local FFII16view = create_FFIview_class("FFII16view", "int16_t") +local FFIU16view = create_FFIview_class("FFIU16view", "uint16_t") +local FFII32view = create_FFIview_class("FFII32view", "int32_t") +local FFIU32view = create_FFIview_class("FFIU32view", "uint32_t") +local FFII64view = create_FFIview_class("FFII64view", "int64_t") +local FFIU64view = create_FFIview_class("FFIU64view", "uint64_t") +local FFIFLTview = create_FFIview_class("FFIFLTview", "float") +local FFIDBLview = create_FFIview_class("FFIDBLview", "double") return { FFIBytearray = setmetatable(FFIBytearray, FFIBytearray), FFIBytearray_as_string = FFIBytearray_as_string, FFIBytearray_as_ptr = FFIBytearray_as_ptr, + FFII8view = FFII8view, FFIU16view = FFIU16view, FFII16view = FFII16view, FFIU32view = FFIU32view, FFII32view = FFII32view, + FFIU64view = FFIU64view, + FFII64view = FFII64view, + FFIFLTview = FFIFLTview, + FFIDBLview = FFIDBLview, } diff --git a/res/modules/internal/ctypes.lua b/res/modules/internal/ctypes.lua new file mode 100644 index 000000000..032ea34b5 --- /dev/null +++ b/res/modules/internal/ctypes.lua @@ -0,0 +1,38 @@ +local FFI = ffi + +FFI.cdef[[ + unsigned long long strtoull(const char* nptr, char** endptr, int base); + long long strtoll(const char* nptr, char** endptr, int base); +]] + +local this = {} + +local function create_integral_type(ctype_name, signed) + local parse_func = signed and FFI.C.strtoll or FFI.C.strtoull + return function(value, base) + value = value or 0 + local typename = type(value) + if typename == "number" then + return FFI.cast(ctype_name, value) + elseif typename == "string" then + if type(base) ~= "number" then + base = 10 + end + return parse_func(value, nil, base) + end + error(string.format("invalid value type: %s", typename)) + end +end + + +this.uint8 = create_integral_type("uint8_t", false) +this.uint16 = create_integral_type("uint16_t", false) +this.uint32 = create_integral_type("uint32_t", false) +this.uint64 = create_integral_type("uint64_t", false) + +this.int8 = create_integral_type("int8_t", true) +this.int16 = create_integral_type("int16_t", true) +this.int32 = create_integral_type("int32_t", true) +this.int64 = create_integral_type("int64_t", true) + +return this diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index e341f68a1..03de8cbaa 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -311,12 +311,19 @@ local bytearray = require "core:internal/bytearray" Bytearray = bytearray.FFIBytearray Bytearray_as_string = bytearray.FFIBytearray_as_string Bytearray_as_ptr = bytearray.FFIBytearray_as_ptr +I8view = bytearray.FFII8view U16view = bytearray.FFIU16view I16view = bytearray.FFII16view U32view = bytearray.FFIU32view I32view = bytearray.FFII32view +U64view = bytearray.FFIU64view +I64view = bytearray.FFII64view +FLTview = bytearray.FFIFLTview +DBLview = bytearray.FFIDBLview Bytearray_construct = function(...) return Bytearray(...) end +ctypes = require "core:internal/ctypes" + bit.compile = require "core:bitwise/compiler" bit.execute = require "core:bitwise/executor" diff --git a/src/engine/CoreParameters.hpp b/src/engine/CoreParameters.hpp index b5d8074ce..9668ce1cd 100644 --- a/src/engine/CoreParameters.hpp +++ b/src/engine/CoreParameters.hpp @@ -12,6 +12,7 @@ struct CoreParameters { std::filesystem::path userFolder = "."; std::filesystem::path scriptFile; std::filesystem::path projectFolder; + std::filesystem::path logFile; std::string debugServerString; int tps = 20; int subProcessDepth = 0; diff --git a/src/logic/scripting/lua/libs/libapp.cpp b/src/logic/scripting/lua/libs/libapp.cpp index 9157b33a6..84f319a9c 100644 --- a/src/logic/scripting/lua/libs/libapp.cpp +++ b/src/logic/scripting/lua/libs/libapp.cpp @@ -403,6 +403,7 @@ static int l_start_background_instance(lua::State* L) { "--script", io::resolve(scriptPath).u8string(), "--sub-depth", std::to_string(engine->getCoreParameters() .subProcessDepth + 1), + "--log", io::resolve(outputPath).u8string(), }; args.emplace_back("--project"); args.emplace_back(io::resolve(engine->getProject().path).u8string()); @@ -417,11 +418,9 @@ static int l_start_background_instance(lua::State* L) { if (handle == -1) { throw std::runtime_error("sub-processes limit exceeded"); } - ::processes[handle] = platform::new_engine_instance( - std::move(args), - outputPath.empty() ? "" : io::resolve(outputPath), - true - ); + ::processes[handle] = + platform::new_engine_instance(std::move(args), "", true); + return lua::pushinteger(L, handle); } diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index c670e23df..928515e74 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -161,17 +161,13 @@ static LuaCanvas& require_canvas(State* L, int idx) { static int l_clear(State* L) { auto& canvas = require_canvas(L, 1); auto& image = canvas.getData(); - ubyte* data = image.getData(); - RGBA rgba {}; + size_t pixelscount = image.getWidth() * image.getHeight(); + uint32_t* data = reinterpret_cast(image.getData()); + if (gettop(L) == 1) { - std::fill(data, data + image.getDataSize(), 0); - return 0; - } - rgba = get_rgba(L, 2); - size_t pixels = image.getWidth() * image.getHeight(); - const size_t channels = 4; - for (size_t i = 0; i < pixels * channels; i++) { - data[i] = rgba.arr[i % channels]; + std::fill(data, data + pixelscount, 0); + } else { + std::fill(data, data + pixelscount, get_rgba(L, 2).rgba); } return 0; } diff --git a/src/main.cpp b/src/main.cpp index 402b829b3..9d8d8e551 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include #include +using namespace std::literals; static debug::Logger logger("main"); static void sigterm_handler(int signum) { @@ -36,7 +37,14 @@ int main(int argc, char** argv) { #ifdef NDEBUG std::signal(SIGINT, sigterm_handler); #endif - debug::Logger::init(coreParameters.userFolder.string() + "/latest.log"); + std::filesystem::path logFile = coreParameters.logFile; + if (logFile.empty()) { + logFile = coreParameters.userFolder.string() + "/latest"s + + (coreParameters.subProcessDepth > 0 + ? ".sub" + std::to_string(coreParameters.subProcessDepth) + : "") + ".log"s; + } + debug::Logger::init(logFile.u8string()); platform::configure_encoding(); auto& engine = Engine::getInstance(); diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index f05231821..d5435e955 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -82,6 +82,10 @@ static bool perform_keyword( params.subProcessDepth = reader.nextInt(); return true; }, "", "sub-process depth"), + ArgC("--log", [](auto& params, auto& reader) -> bool { + params.logFile = reader.next(); + return true; + }, "", "target log file"), ArgC("--help", [](auto&, auto&) -> bool { std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n"; std::cout << "Command-line arguments:\n"; diff --git a/src/util/platform.cpp b/src/util/platform.cpp index 7b08639e9..5d5e489bc 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -28,6 +28,8 @@ #include #endif +using namespace std::literals; + namespace platform::internal { std::filesystem::path get_executable_path(); } @@ -356,7 +358,7 @@ std::unique_ptr platform::new_engine_instance( if (!outputFile.empty()) { si.dwFlags |= STARTF_USESTDHANDLES; si.hStdOutput = CreateFileW( - toWString(outputFile.u8string()).data(), + toWString(outputFile.empty() ? outputFile.u8string() : "NUL"s).data(), GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, nullptr, @@ -434,9 +436,12 @@ std::unique_ptr platform::new_engine_instance( if (subProcess) { pid_t pid = fork(); + if (outputFile.empty()) { + outputFile = "/dev/null"; + } int fd = open( outputFile.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644 - ); + ); if (pid == 0) { prctl(PR_SET_PDEATHSIG, SIGTERM);