Merge branch 'main' into debugging-client
This commit is contained in:
commit
4d82fb7873
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
Callbacks specified in block script.
|
Callbacks specified in block script.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> events such as on_block_tick, on_block_present, and on_block_removed
|
||||||
|
> can cause performance issues if used carelessly or excessively.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_placed(x, y, z, playerid)
|
function on_placed(x, y, z, playerid)
|
||||||
```
|
```
|
||||||
@ -53,6 +57,21 @@ function on_block_tick(x, y, z, tps: number)
|
|||||||
Called tps (20 / tick-interval) times per second for a block.
|
Called tps (20 / tick-interval) times per second for a block.
|
||||||
Use 1/tps instead of `time.delta()`.
|
Use 1/tps instead of `time.delta()`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_block_present(x, y, z)
|
||||||
|
```
|
||||||
|
|
||||||
|
Called for a specific block when it appears in the world (generated/loaded/placed).
|
||||||
|
The call occurs within a time period that may depend on the event queue load.
|
||||||
|
Under light load, it occurs during the first tick interval of the block.
|
||||||
|
on_block_tick is not called until the event is called.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_block_removed(x, y, z)
|
||||||
|
```
|
||||||
|
|
||||||
|
Called when chunk containing the block unloads.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_player_tick(playerid: int, tps: int)
|
function on_player_tick(playerid: int, tps: int)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -201,6 +201,14 @@ Here, *color* can be specified in the following ways:
|
|||||||
| data:mul(*color* or Canvas) | multiplies a color by the specified color or canvas |
|
| data:mul(*color* or Canvas) | multiplies a color by the specified color or canvas |
|
||||||
| data:add(*color* or Canvas) | adds a color or another canvas to a color |
|
| data:add(*color* or Canvas) | adds a color or another canvas to a color |
|
||||||
| data:sub(*color* or Canvas) | subtracts a color or another canvas to a color |
|
| data:sub(*color* or Canvas) | subtracts a color or another canvas to a color |
|
||||||
|
| data:encode(format: str) | encodes image to specified format and returns bytearray |
|
||||||
|
|
||||||
|
To decode a byte array into a Canvas, use the static method:
|
||||||
|
```lua
|
||||||
|
Canvas.decode(data: Bytearray, format: str) -> Canvas
|
||||||
|
```
|
||||||
|
|
||||||
|
Currently, only png is supported.
|
||||||
|
|
||||||
## Inline frame (iframe)
|
## Inline frame (iframe)
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
Функции для обработки событий, прописываемые в скрипте блока.
|
Функции для обработки событий, прописываемые в скрипте блока.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Mass events such as on_block_tick, on_block_present, and on_block_removed,
|
||||||
|
> if used carelessly or excessively, can lead to performance issues.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_placed(x, y, z, playerid)
|
function on_placed(x, y, z, playerid)
|
||||||
```
|
```
|
||||||
@ -53,6 +57,21 @@ function on_block_tick(x, y, z, tps: number)
|
|||||||
Вызывается tps (20 / tick-interval) раз в секунду для конкретного блока.
|
Вызывается tps (20 / tick-interval) раз в секунду для конкретного блока.
|
||||||
Используйте 1/tps вместо `time.delta()`.
|
Используйте 1/tps вместо `time.delta()`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_block_present(x, y, z)
|
||||||
|
```
|
||||||
|
|
||||||
|
Вызывается для конкретного блока при появлении (генерации/загрузке/размещении).
|
||||||
|
Вызов происходит в течение времени, которое может зависеть от нагрузки очереди событий.
|
||||||
|
При малой нагрузке происходит в течение первого такта блока (tick-interval).
|
||||||
|
До вызова события on_block_tick не вызывается.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_block_removed(x, y, z)
|
||||||
|
```
|
||||||
|
|
||||||
|
Вызывается при выгрузке чанка, в котором находится блок.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_player_tick(playerid: int, tps: int)
|
function on_player_tick(playerid: int, tps: int)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -186,21 +186,29 @@ document["worlds-panel"]:clear()
|
|||||||
- r: int, g: int, b: int
|
- r: int, g: int, b: int
|
||||||
- r: int, g: int, b: int, a: int
|
- r: int, g: int, b: int, a: int
|
||||||
|
|
||||||
| Метод | Описание |
|
| Метод | Описание |
|
||||||
|----------------------------------------------------------|------------------------------------------------------|
|
|----------------------------------------------------------|-----------------------------------------------------------------|
|
||||||
| data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам |
|
| data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам |
|
||||||
| data:set(x: int, y: int, *цвет*) | изменяет RGBA пиксель по указанным координатам |
|
| data:set(x: int, y: int, *цвет*) | изменяет RGBA пиксель по указанным координатам |
|
||||||
| data:line(x1: int, y1: int, x2: int, y2: int, *цвет*) | рисует линию с указанным RGBA цветом |
|
| data:line(x1: int, y1: int, x2: int, y2: int, *цвет*) | рисует линию с указанным RGBA цветом |
|
||||||
| data:blit(src: Canvas, dst_x: int, dst_y: int) | рисует src-холст на указанных координатах |
|
| data:blit(src: Canvas, dst_x: int, dst_y: int) | рисует src-холст на указанных координатах |
|
||||||
| data:clear() | очищает холст |
|
| data:clear() | очищает холст |
|
||||||
| data:clear(*цвет*) | заполняет холст указанным RGBA цветом |
|
| data:clear(*цвет*) | заполняет холст указанным RGBA цветом |
|
||||||
| data:update() | применяет изменения и загружает холст в видеопамять |
|
| data:update() | применяет изменения и загружает холст в видеопамять |
|
||||||
| data:set_data(data: table<int>) | заменяет данные пикселей (ширина * высота * 4 чисел) |
|
| data:set_data(data: table<int>) | заменяет данные пикселей (ширина * высота * 4 чисел) |
|
||||||
| data:create_texture(name: str) | создаёт и делится текстурой с рендерером |
|
| data:create_texture(name: str) | создаёт и делится текстурой с рендерером |
|
||||||
| data:unbind_texture() | отвязывает текстуру от холста |
|
| data:unbind_texture() | отвязывает текстуру от холста |
|
||||||
| data:mul(*цвет* или Canvas) | умножает увет на указанный цвет или холст |
|
| data:mul(*цвет* или Canvas) | умножает увет на указанный цвет или холст |
|
||||||
| data:add(*цвет* или Canvas) | прибавляет цвет или другой холст к цвету |
|
| data:add(*цвет* или Canvas) | прибавляет цвет или другой холст к цвету |
|
||||||
| data:sub(*цвет* или Canvas) | вычитает цвет или другой холст к цвету |
|
| data:sub(*цвет* или Canvas) | вычитает цвет или другой холст к цвету |
|
||||||
|
| data:encode(format: str) | кодирует изображение в указанный формат и возращает массив байт |
|
||||||
|
|
||||||
|
Для декодирования массива байт в Canvas используйте статический метод:
|
||||||
|
```lua
|
||||||
|
Canvas.decode(data: Bytearray, format: str) -> Canvas
|
||||||
|
```
|
||||||
|
|
||||||
|
На данный момент, из форматов поддерживается только png.
|
||||||
|
|
||||||
## Рамка встраивания (iframe)
|
## Рамка встраивания (iframe)
|
||||||
|
|
||||||
|
|||||||
@ -181,73 +181,6 @@ local function clean(iterable, checkFun, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local updating_blocks = {}
|
|
||||||
local TYPE_REGISTER = 0
|
|
||||||
local TYPE_UNREGISTER = 1
|
|
||||||
|
|
||||||
block.__perform_ticks = function(delta)
|
|
||||||
for id, entry in pairs(updating_blocks) do
|
|
||||||
entry.timer = entry.timer + delta
|
|
||||||
local steps = math.floor(entry.timer / entry.delta * #entry / 3)
|
|
||||||
if steps == 0 then
|
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
entry.timer = 0.0
|
|
||||||
local event = entry.event
|
|
||||||
local tps = entry.tps
|
|
||||||
for i=1, steps do
|
|
||||||
local x = entry[entry.pointer + 1]
|
|
||||||
local y = entry[entry.pointer + 2]
|
|
||||||
local z = entry[entry.pointer + 3]
|
|
||||||
entry.pointer = (entry.pointer + 3) % #entry
|
|
||||||
events.emit(event, x, y, z, tps)
|
|
||||||
end
|
|
||||||
::continue::
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
block.__process_register_events = function()
|
|
||||||
local register_events = block.__pull_register_events()
|
|
||||||
if not register_events then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
for i=1, #register_events, 4 do
|
|
||||||
local header = register_events[i]
|
|
||||||
local type = bit.band(header, 0xFFFF)
|
|
||||||
local id = bit.rshift(header, 16)
|
|
||||||
local x = register_events[i + 1]
|
|
||||||
local y = register_events[i + 2]
|
|
||||||
local z = register_events[i + 3]
|
|
||||||
|
|
||||||
local list = updating_blocks[id]
|
|
||||||
if type == TYPE_REGISTER then
|
|
||||||
if not list then
|
|
||||||
list = {}
|
|
||||||
list.event = block.name(id) .. ".blocktick"
|
|
||||||
list.tps = 20 / (block.properties[id]["tick-interval"] or 1)
|
|
||||||
list.delta = 1.0 / list.tps
|
|
||||||
list.timer = 0.0
|
|
||||||
list.pointer = 0
|
|
||||||
updating_blocks[id] = list
|
|
||||||
end
|
|
||||||
table.insert(list, x)
|
|
||||||
table.insert(list, y)
|
|
||||||
table.insert(list, z)
|
|
||||||
elseif type == TYPE_UNREGISTER then
|
|
||||||
if list then
|
|
||||||
for j=1, #list, 3 do
|
|
||||||
if list[j] == x and list[j + 1] == y and list[j + 2] == z then
|
|
||||||
for k=1,3 do
|
|
||||||
table.remove(list, j)
|
|
||||||
end
|
|
||||||
j = j - 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
network.__process_events = function()
|
network.__process_events = function()
|
||||||
local CLIENT_CONNECTED = 1
|
local CLIENT_CONNECTED = 1
|
||||||
local CONNECTED_TO_SERVER = 2
|
local CONNECTED_TO_SERVER = 2
|
||||||
|
|||||||
144
res/scripts/internal_events.lua
Normal file
144
res/scripts/internal_events.lua
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
local updating_blocks = {}
|
||||||
|
local present_queues = {}
|
||||||
|
local REGISTER_BIT = 0x1
|
||||||
|
local UPDATING_BIT = 0x2
|
||||||
|
local PRESENT_BIT = 0x4
|
||||||
|
local REMOVED_BIT = 0x8
|
||||||
|
|
||||||
|
block.__perform_ticks = function(delta)
|
||||||
|
for id, entry in pairs(updating_blocks) do
|
||||||
|
entry.timer = entry.timer + delta
|
||||||
|
local steps = math.floor(entry.timer / entry.delta * #entry / 3)
|
||||||
|
if steps == 0 then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
entry.timer = 0.0
|
||||||
|
local event = entry.event
|
||||||
|
local tps = entry.tps
|
||||||
|
for i=1, steps do
|
||||||
|
local x = entry[entry.pointer + 1]
|
||||||
|
local y = entry[entry.pointer + 2]
|
||||||
|
local z = entry[entry.pointer + 3]
|
||||||
|
entry.pointer = (entry.pointer + 3) % #entry
|
||||||
|
events.emit(event, x, y, z, tps)
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
for id, queue in pairs(present_queues) do
|
||||||
|
queue.timer = queue.timer + delta
|
||||||
|
local steps = math.floor(queue.timer / queue.delta * #queue / 3)
|
||||||
|
if steps == 0 then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
queue.timer = 0.0
|
||||||
|
local event = queue.event
|
||||||
|
local update_list = updating_blocks[id]
|
||||||
|
for i=1, steps do
|
||||||
|
local index = #queue - 2
|
||||||
|
if index <= 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
local x = queue[index]
|
||||||
|
local y = queue[index + 1]
|
||||||
|
local z = queue[index + 2]
|
||||||
|
|
||||||
|
for j=1,3 do
|
||||||
|
table.remove(queue, index)
|
||||||
|
end
|
||||||
|
events.emit(event, x, y, z)
|
||||||
|
|
||||||
|
if queue.updating then
|
||||||
|
table.insert(update_list, x)
|
||||||
|
table.insert(update_list, y)
|
||||||
|
table.insert(update_list, z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local block_pull_register_events = block.__pull_register_events
|
||||||
|
block.__pull_register_events = nil
|
||||||
|
|
||||||
|
block.__process_register_events = function()
|
||||||
|
local register_events = block_pull_register_events()
|
||||||
|
if not register_events then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local emit_event = events.emit
|
||||||
|
local removed_events = {}
|
||||||
|
|
||||||
|
for i=1, #register_events, 4 do
|
||||||
|
local header = register_events[i]
|
||||||
|
local event_bits = bit.band(header, 0xFFFF)
|
||||||
|
local id = bit.rshift(header, 16)
|
||||||
|
local x = register_events[i + 1]
|
||||||
|
local y = register_events[i + 2]
|
||||||
|
local z = register_events[i + 3]
|
||||||
|
|
||||||
|
local is_register = bit.band(event_bits, REGISTER_BIT) ~= 0
|
||||||
|
local is_updating = bit.band(event_bits, UPDATING_BIT) ~= 0
|
||||||
|
local is_present = bit.band(event_bits, PRESENT_BIT) ~= 0
|
||||||
|
local is_removed = bit.band(event_bits, REMOVED_BIT) ~= 0
|
||||||
|
local list = updating_blocks[id]
|
||||||
|
|
||||||
|
if not is_register and is_removed then
|
||||||
|
local rm_event = removed_events[id]
|
||||||
|
if not rm_event then
|
||||||
|
rm_event = block.name(id) .. ".blockremoved"
|
||||||
|
removed_events[id] = rm_event
|
||||||
|
end
|
||||||
|
emit_event(rm_event, x, y, z)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not list and is_register and is_updating then
|
||||||
|
list = {}
|
||||||
|
list.event = block.name(id) .. ".blocktick"
|
||||||
|
list.tps = 20 / (block.properties[id]["tick-interval"] or 1)
|
||||||
|
list.delta = 1.0 / list.tps
|
||||||
|
list.timer = 0.0
|
||||||
|
list.pointer = 0
|
||||||
|
updating_blocks[id] = list
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_register and is_present then
|
||||||
|
local present_queue = present_queues[id]
|
||||||
|
if not present_queue then
|
||||||
|
present_queue = {}
|
||||||
|
present_queue.event = block.name(id) .. ".blockpresent"
|
||||||
|
present_queue.tps = 20 / (block.properties[id]["tick-interval"] or 1)
|
||||||
|
present_queue.delta = 1.0 / present_queue.tps
|
||||||
|
present_queue.timer = 0.0
|
||||||
|
present_queue.pointer = 0
|
||||||
|
present_queue.updating = is_updating
|
||||||
|
present_queues[id] = present_queue
|
||||||
|
end
|
||||||
|
table.insert(present_queue, x)
|
||||||
|
table.insert(present_queue, y)
|
||||||
|
table.insert(present_queue, z)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
if not is_updating then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
if is_register then
|
||||||
|
table.insert(list, x)
|
||||||
|
table.insert(list, y)
|
||||||
|
table.insert(list, z)
|
||||||
|
else
|
||||||
|
if not list then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
for j=1, #list, 3 do
|
||||||
|
if list[j] == x and list[j + 1] == y and list[j + 2] == z then
|
||||||
|
for k=1,3 do
|
||||||
|
table.remove(list, j)
|
||||||
|
end
|
||||||
|
j = j - 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -358,6 +358,10 @@ function __vc_on_world_tick(tps)
|
|||||||
time.schedules.world:tick(1.0 / tps)
|
time.schedules.world:tick(1.0 / tps)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function __vc_process_before_quit()
|
||||||
|
block.__process_register_events()
|
||||||
|
end
|
||||||
|
|
||||||
function __vc_on_world_save()
|
function __vc_on_world_save()
|
||||||
local rule_values = {}
|
local rule_values = {}
|
||||||
for name, rule in pairs(rules.rules) do
|
for name, rule in pairs(rules.rules) do
|
||||||
|
|||||||
@ -112,7 +112,7 @@ target_compile_options(
|
|||||||
-Wno-sign-compare
|
-Wno-sign-compare
|
||||||
-Wno-unknown-pragmas
|
-Wno-unknown-pragmas
|
||||||
>
|
>
|
||||||
$<$<CXX_COMPILER_ID:Clang>:
|
$<$<CXX_COMPILER_ID:GNU>:
|
||||||
-Wduplicated-branches
|
-Wduplicated-branches
|
||||||
-Wduplicated-cond
|
-Wduplicated-cond
|
||||||
>)
|
>)
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#define VC_ENABLE_REFLECTION
|
||||||
#include "imageio.hpp"
|
#include "imageio.hpp"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -7,28 +8,34 @@
|
|||||||
#include "io/io.hpp"
|
#include "io/io.hpp"
|
||||||
#include "png.hpp"
|
#include "png.hpp"
|
||||||
|
|
||||||
|
using namespace imageio;
|
||||||
|
|
||||||
using image_reader =
|
using image_reader =
|
||||||
std::function<std::unique_ptr<ImageData>(const ubyte*, size_t)>;
|
std::function<std::unique_ptr<ImageData>(const ubyte*, size_t)>;
|
||||||
using image_writer = std::function<void(const std::string&, const ImageData*)>;
|
using image_writer = std::function<void(const std::string&, const ImageData*)>;
|
||||||
|
|
||||||
static std::unordered_map<std::string, image_reader> readers {
|
static std::unordered_map<ImageFileFormat, image_reader> readers {
|
||||||
{".png", png::load_image},
|
{ImageFileFormat::PNG, png::load_image},
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unordered_map<std::string, image_writer> writers {
|
static std::unordered_map<ImageFileFormat, image_writer> writers {
|
||||||
{".png", png::write_image},
|
{ImageFileFormat::PNG, png::write_image},
|
||||||
};
|
};
|
||||||
|
|
||||||
bool imageio::is_read_supported(const std::string& extension) {
|
bool imageio::is_read_supported(const std::string& extension) {
|
||||||
return readers.find(extension) != readers.end();
|
return extension == ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool imageio::is_write_supported(const std::string& extension) {
|
bool imageio::is_write_supported(const std::string& extension) {
|
||||||
return writers.find(extension) != writers.end();
|
return extension == ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ImageData> imageio::read(const io::path& file) {
|
std::unique_ptr<ImageData> imageio::read(const io::path& file) {
|
||||||
auto found = readers.find(file.extension());
|
ImageFileFormat format;
|
||||||
|
if (!ImageFileFormatMeta.getItem(file.extension().substr(1), format)) {
|
||||||
|
throw std::runtime_error("unsupported image format");
|
||||||
|
}
|
||||||
|
auto found = readers.find(format);
|
||||||
if (found == readers.end()) {
|
if (found == readers.end()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"file format is not supported (read): " + file.string()
|
"file format is not supported (read): " + file.string()
|
||||||
@ -44,8 +51,25 @@ std::unique_ptr<ImageData> imageio::read(const io::path& file) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ImageData> imageio::decode(
|
||||||
|
ImageFileFormat format, util::span<ubyte> src
|
||||||
|
) {
|
||||||
|
auto found = readers.find(format);
|
||||||
|
try {
|
||||||
|
return std::unique_ptr<ImageData>(found->second(src.data(), src.size()));
|
||||||
|
} catch (const std::runtime_error& err) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"could not to decode image: " + std::string(err.what())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void imageio::write(const io::path& file, const ImageData* image) {
|
void imageio::write(const io::path& file, const ImageData* image) {
|
||||||
auto found = writers.find(file.extension());
|
ImageFileFormat format;
|
||||||
|
if (!ImageFileFormatMeta.getItem(file.extension().substr(1), format)) {
|
||||||
|
throw std::runtime_error("unsupported image format");
|
||||||
|
}
|
||||||
|
auto found = writers.find(format);
|
||||||
if (found == writers.end()) {
|
if (found == writers.end()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"file format is not supported (write): " + file.string()
|
"file format is not supported (write): " + file.string()
|
||||||
@ -53,3 +77,14 @@ void imageio::write(const io::path& file, const ImageData* image) {
|
|||||||
}
|
}
|
||||||
return found->second(io::resolve(file).u8string(), image);
|
return found->second(io::resolve(file).u8string(), image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
util::Buffer<unsigned char> imageio::encode(
|
||||||
|
ImageFileFormat format, const ImageData& image
|
||||||
|
) {
|
||||||
|
switch (format) {
|
||||||
|
case ImageFileFormat::PNG:
|
||||||
|
return png::encode_image(image);
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("file format is not supported for encoding");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,10 +4,22 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "io/fwd.hpp"
|
#include "io/fwd.hpp"
|
||||||
|
#include "util/Buffer.hpp"
|
||||||
|
#include "util/EnumMetadata.hpp"
|
||||||
|
#include "util/span.hpp"
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
class ImageData;
|
class ImageData;
|
||||||
|
|
||||||
namespace imageio {
|
namespace imageio {
|
||||||
|
enum class ImageFileFormat {
|
||||||
|
PNG
|
||||||
|
};
|
||||||
|
|
||||||
|
VC_ENUM_METADATA(ImageFileFormat)
|
||||||
|
{"png", ImageFileFormat::PNG},
|
||||||
|
VC_ENUM_END
|
||||||
|
|
||||||
inline const std::string PNG = ".png";
|
inline const std::string PNG = ".png";
|
||||||
|
|
||||||
bool is_read_supported(const std::string& extension);
|
bool is_read_supported(const std::string& extension);
|
||||||
@ -15,4 +27,6 @@ namespace imageio {
|
|||||||
|
|
||||||
std::unique_ptr<ImageData> read(const io::path& file);
|
std::unique_ptr<ImageData> read(const io::path& file);
|
||||||
void write(const io::path& file, const ImageData* image);
|
void write(const io::path& file, const ImageData* image);
|
||||||
|
std::unique_ptr<ImageData> decode(ImageFileFormat format, util::span<ubyte> src);
|
||||||
|
util::Buffer<unsigned char> encode(ImageFileFormat format, const ImageData& image);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,60 @@
|
|||||||
|
|
||||||
static debug::Logger logger("png-coder");
|
static debug::Logger logger("png-coder");
|
||||||
|
|
||||||
|
static util::Buffer<ubyte> write_to_memory(uint width, uint height, const ubyte* data, bool alpha) {
|
||||||
|
uint pixsize = alpha ? 4 : 3;
|
||||||
|
|
||||||
|
std::vector<ubyte> buffer;
|
||||||
|
png_structp png_ptr = png_create_write_struct(
|
||||||
|
PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr
|
||||||
|
);
|
||||||
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
|
||||||
|
png_set_write_fn(
|
||||||
|
png_ptr,
|
||||||
|
&buffer,
|
||||||
|
[](png_structp pngPtr, png_bytep data, png_size_t length) {
|
||||||
|
auto& buf = *reinterpret_cast<std::vector<ubyte>*>(png_get_io_ptr(pngPtr));
|
||||||
|
buf.insert(
|
||||||
|
buf.end(),
|
||||||
|
reinterpret_cast<ubyte*>(data),
|
||||||
|
reinterpret_cast<ubyte*>(data) + length
|
||||||
|
);
|
||||||
|
},
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
png_set_IHDR(
|
||||||
|
png_ptr,
|
||||||
|
info_ptr,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
8,
|
||||||
|
alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
|
||||||
|
PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_BASE,
|
||||||
|
PNG_FILTER_TYPE_BASE
|
||||||
|
);
|
||||||
|
|
||||||
|
png_write_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
auto row = std::make_unique<png_byte[]>(pixsize * width);
|
||||||
|
for (uint y = 0; y < height; y++) {
|
||||||
|
for (uint x = 0; x < width; x++) {
|
||||||
|
for (uint i = 0; i < pixsize; i++) {
|
||||||
|
row[x * pixsize + i] =
|
||||||
|
(png_byte)data[(y * width + x) * pixsize + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
png_write_row(png_ptr, row.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
png_write_end(png_ptr, nullptr);
|
||||||
|
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
|
||||||
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
|
return util::Buffer<ubyte>(buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
// returns 0 if all-right, 1 otherwise
|
// returns 0 if all-right, 1 otherwise
|
||||||
static int png_write(
|
static int png_write(
|
||||||
const char* filename, uint width, uint height, const ubyte* data, bool alpha
|
const char* filename, uint width, uint height, const ubyte* data, bool alpha
|
||||||
@ -230,3 +284,13 @@ void png::write_image(const std::string& filename, const ImageData* image) {
|
|||||||
image->getFormat() == ImageFormat::rgba8888
|
image->getFormat() == ImageFormat::rgba8888
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
util::Buffer<ubyte> png::encode_image(const ImageData& image) {
|
||||||
|
auto format = image.getFormat();
|
||||||
|
return write_to_memory(
|
||||||
|
image.getWidth(),
|
||||||
|
image.getHeight(),
|
||||||
|
image.getData(),
|
||||||
|
format == ImageFormat::rgba8888
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
#include "util/Buffer.hpp"
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
class ImageData;
|
class ImageData;
|
||||||
@ -11,6 +12,7 @@ class ImageData;
|
|||||||
namespace png {
|
namespace png {
|
||||||
std::unique_ptr<ImageData> load_image(const ubyte* bytes, size_t size);
|
std::unique_ptr<ImageData> load_image(const ubyte* bytes, size_t size);
|
||||||
void write_image(const std::string& filename, const ImageData* image);
|
void write_image(const std::string& filename, const ImageData* image);
|
||||||
|
util::Buffer<ubyte> encode_image(const ImageData& image);
|
||||||
std::unique_ptr<Texture> load_texture(const ubyte* bytes, size_t size);
|
std::unique_ptr<Texture> load_texture(const ubyte* bytes, size_t size);
|
||||||
std::unique_ptr<Texture> load_texture(const std::string& filename);
|
std::unique_ptr<Texture> load_texture(const std::string& filename);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,7 +87,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
fpsMax = fps;
|
fpsMax = fps;
|
||||||
});
|
});
|
||||||
|
|
||||||
panel->listenInterval(1.0f, [&engine, &gui]() {
|
panel->listenInterval(1.0f, [&engine]() {
|
||||||
const auto& network = engine.getNetwork();
|
const auto& network = engine.getNetwork();
|
||||||
size_t totalDownload = network.getTotalDownload();
|
size_t totalDownload = network.getTotalDownload();
|
||||||
size_t totalUpload = network.getTotalUpload();
|
size_t totalUpload = network.getTotalUpload();
|
||||||
|
|||||||
@ -109,6 +109,7 @@ LevelScreen::~LevelScreen() {
|
|||||||
scripting::on_frontend_close();
|
scripting::on_frontend_close();
|
||||||
// unblock all bindings
|
// unblock all bindings
|
||||||
input.getBindings().enableAll();
|
input.getBindings().enableAll();
|
||||||
|
playerController->getPlayer()->chunks->saveAndClear();
|
||||||
controller->onWorldQuit();
|
controller->onWorldQuit();
|
||||||
engine.getPaths().setCurrentWorldFolder("");
|
engine.getPaths().setCurrentWorldFolder("");
|
||||||
}
|
}
|
||||||
@ -278,5 +279,6 @@ void LevelScreen::onEngineShutdown() {
|
|||||||
if (hud->isInventoryOpen()) {
|
if (hud->isInventoryOpen()) {
|
||||||
hud->closeInventory();
|
hud->closeInventory();
|
||||||
}
|
}
|
||||||
|
controller->processBeforeQuit();
|
||||||
controller->saveWorld();
|
controller->saveWorld();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "voxels/GlobalChunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
#include "world/LevelEvents.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
#include "world/generator/WorldGenerator.hpp"
|
#include "world/generator/WorldGenerator.hpp"
|
||||||
|
|
||||||
@ -172,13 +173,12 @@ void ChunksController::createChunk(const Player& player, int x, int z) const {
|
|||||||
auto chunk = level.chunks->create(x, z);
|
auto chunk = level.chunks->create(x, z);
|
||||||
player.chunks->putChunk(chunk);
|
player.chunks->putChunk(chunk);
|
||||||
auto& chunkFlags = chunk->flags;
|
auto& chunkFlags = chunk->flags;
|
||||||
|
|
||||||
if (!chunkFlags.loaded) {
|
if (!chunkFlags.loaded) {
|
||||||
generator->generate(chunk->voxels, x, z);
|
generator->generate(chunk->voxels, x, z);
|
||||||
chunkFlags.unsaved = true;
|
chunkFlags.unsaved = true;
|
||||||
}
|
}
|
||||||
chunk->updateHeights();
|
chunk->updateHeights();
|
||||||
|
level.events->trigger(LevelEventType::CHUNK_PRESENT, chunk.get());
|
||||||
if (!chunkFlags.loadedLights) {
|
if (!chunkFlags.loadedLights) {
|
||||||
Lighting::prebuildSkyLight(*chunk, *level.content.getIndices());
|
Lighting::prebuildSkyLight(*chunk, *level.content.getIndices());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -366,7 +366,9 @@ void EngineController::reconfigPacks(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto world = controller->getLevel()->getWorld();
|
auto level = controller->getLevel();
|
||||||
|
auto world = level->getWorld();
|
||||||
|
controller->processBeforeQuit();
|
||||||
controller->saveWorld();
|
controller->saveWorld();
|
||||||
|
|
||||||
auto names = PacksManager::getNames(world->getPacks());
|
auto names = PacksManager::getNames(world->getPacks());
|
||||||
|
|||||||
@ -27,7 +27,8 @@ LevelController::LevelController(
|
|||||||
: settings(engine->getSettings()),
|
: settings(engine->getSettings()),
|
||||||
level(std::move(levelPtr)),
|
level(std::move(levelPtr)),
|
||||||
chunks(std::make_unique<ChunksController>(*level)),
|
chunks(std::make_unique<ChunksController>(*level)),
|
||||||
playerTickClock(20, 3) {
|
playerTickClock(20, 3),
|
||||||
|
localPlayer(clientPlayer) {
|
||||||
|
|
||||||
level->events->listen(LevelEventType::CHUNK_PRESENT, [](auto, Chunk* chunk) {
|
level->events->listen(LevelEventType::CHUNK_PRESENT, [](auto, Chunk* chunk) {
|
||||||
scripting::on_chunk_present(*chunk, chunk->flags.loaded);
|
scripting::on_chunk_present(*chunk, chunk->flags.loaded);
|
||||||
@ -121,6 +122,13 @@ void LevelController::update(float delta, bool pause) {
|
|||||||
level->entities->clean();
|
level->entities->clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LevelController::processBeforeQuit() {
|
||||||
|
if (localPlayer) {
|
||||||
|
localPlayer->chunks->saveAndClear();
|
||||||
|
}
|
||||||
|
scripting::process_before_quit();
|
||||||
|
}
|
||||||
|
|
||||||
void LevelController::saveWorld() {
|
void LevelController::saveWorld() {
|
||||||
auto world = level->getWorld();
|
auto world = level->getWorld();
|
||||||
if (world->isNameless()) {
|
if (world->isNameless()) {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ class LevelController {
|
|||||||
std::unique_ptr<ChunksController> chunks;
|
std::unique_ptr<ChunksController> chunks;
|
||||||
|
|
||||||
util::Clock playerTickClock;
|
util::Clock playerTickClock;
|
||||||
|
Player* localPlayer;
|
||||||
public:
|
public:
|
||||||
LevelController(Engine* engine, std::unique_ptr<Level> level, Player* clientPlayer);
|
LevelController(Engine* engine, std::unique_ptr<Level> level, Player* clientPlayer);
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ public:
|
|||||||
/// @param pause is world and player simulation paused
|
/// @param pause is world and player simulation paused
|
||||||
void update(float delta, bool pause);
|
void update(float delta, bool pause);
|
||||||
|
|
||||||
|
void processBeforeQuit();
|
||||||
void saveWorld();
|
void saveWorld();
|
||||||
|
|
||||||
void onWorldQuit();
|
void onWorldQuit();
|
||||||
|
|||||||
@ -730,7 +730,7 @@ static int l_pull_register_events(lua::State* L) {
|
|||||||
lua::createtable(L, events.size() * 4, 0);
|
lua::createtable(L, events.size() * 4, 0);
|
||||||
for (int i = 0; i < events.size(); i++) {
|
for (int i = 0; i < events.size(); i++) {
|
||||||
const auto& event = events[i];
|
const auto& event = events[i];
|
||||||
lua::pushinteger(L, static_cast<int>(event.type) | event.id << 16);
|
lua::pushinteger(L, static_cast<int>(event.bits) | event.id << 16);
|
||||||
lua::rawseti(L, i * 4 + 1);
|
lua::rawseti(L, i * 4 + 1);
|
||||||
|
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
|
|||||||
@ -120,6 +120,7 @@ static int l_close_world(lua::State* L) {
|
|||||||
if (controller == nullptr) {
|
if (controller == nullptr) {
|
||||||
throw std::runtime_error("no world open");
|
throw std::runtime_error("no world open");
|
||||||
}
|
}
|
||||||
|
controller->processBeforeQuit();
|
||||||
bool save_world = lua::toboolean(L, 1);
|
bool save_world = lua::toboolean(L, 1);
|
||||||
if (save_world) {
|
if (save_world) {
|
||||||
controller->saveWorld();
|
controller->saveWorld();
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
|
#define VC_ENABLE_REFLECTION
|
||||||
#include "lua_type_canvas.hpp"
|
#include "lua_type_canvas.hpp"
|
||||||
|
|
||||||
#include "graphics/core/ImageData.hpp"
|
#include "graphics/core/ImageData.hpp"
|
||||||
#include "graphics/core/Texture.hpp"
|
#include "graphics/core/Texture.hpp"
|
||||||
#include "logic/scripting/lua/lua_util.hpp"
|
#include "logic/scripting/lua/lua_util.hpp"
|
||||||
|
#include "coders/imageio.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
#include "assets/Assets.hpp"
|
#include "assets/Assets.hpp"
|
||||||
|
|
||||||
@ -284,6 +286,23 @@ static int l_sub(State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_encode(State* L) {
|
||||||
|
auto canvas = touserdata<LuaCanvas>(L, 1);
|
||||||
|
if (canvas == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto format = imageio::ImageFileFormat::PNG;
|
||||||
|
if (lua::isstring(L, 2)) {
|
||||||
|
auto name = lua::require_string(L, 2);
|
||||||
|
if (!imageio::ImageFileFormatMeta.getItem(name, format)) {
|
||||||
|
throw std::runtime_error("unsupported image file format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buffer = imageio::encode(format, canvas->getData());
|
||||||
|
return lua::create_bytearray(L, buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
static std::unordered_map<std::string, lua_CFunction> methods {
|
static std::unordered_map<std::string, lua_CFunction> methods {
|
||||||
{"at", lua::wrap<l_at>},
|
{"at", lua::wrap<l_at>},
|
||||||
{"set", lua::wrap<l_set>},
|
{"set", lua::wrap<l_set>},
|
||||||
@ -296,6 +315,7 @@ static std::unordered_map<std::string, lua_CFunction> methods {
|
|||||||
{"mul", lua::wrap<l_mul>},
|
{"mul", lua::wrap<l_mul>},
|
||||||
{"add", lua::wrap<l_add>},
|
{"add", lua::wrap<l_add>},
|
||||||
{"sub", lua::wrap<l_sub>},
|
{"sub", lua::wrap<l_sub>},
|
||||||
|
{"encode", lua::wrap<l_encode>},
|
||||||
{"_set_data", lua::wrap<l_set_data>},
|
{"_set_data", lua::wrap<l_set_data>},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -354,6 +374,23 @@ static int l_meta_meta_call(lua::State* L) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_canvas_decode(lua::State* L) {
|
||||||
|
auto bytes = bytearray_as_string(L, 1);
|
||||||
|
auto formatName = require_lstring(L, 2);
|
||||||
|
imageio::ImageFileFormat format;
|
||||||
|
if (!imageio::ImageFileFormatMeta.getItem(formatName, format)) {
|
||||||
|
throw std::runtime_error("unsupported image format");
|
||||||
|
}
|
||||||
|
return newuserdata<LuaCanvas>(
|
||||||
|
L,
|
||||||
|
nullptr,
|
||||||
|
imageio::decode(
|
||||||
|
format,
|
||||||
|
{reinterpret_cast<const unsigned char*>(bytes.data()), bytes.size()}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
int LuaCanvas::createMetatable(State* L) {
|
int LuaCanvas::createMetatable(State* L) {
|
||||||
createtable(L, 0, 3);
|
createtable(L, 0, 3);
|
||||||
pushcfunction(L, lua::wrap<l_meta_index>);
|
pushcfunction(L, lua::wrap<l_meta_index>);
|
||||||
@ -365,5 +402,8 @@ int LuaCanvas::createMetatable(State* L) {
|
|||||||
pushcfunction(L, lua::wrap<l_meta_meta_call>);
|
pushcfunction(L, lua::wrap<l_meta_meta_call>);
|
||||||
setfield(L, "__call");
|
setfield(L, "__call");
|
||||||
setmetatable(L);
|
setmetatable(L);
|
||||||
|
|
||||||
|
pushcfunction(L, lua::wrap<l_canvas_decode>);
|
||||||
|
setfield(L, "decode");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,7 @@ void scripting::initialize(Engine* engine) {
|
|||||||
|
|
||||||
load_script(io::path("stdlib.lua"), true);
|
load_script(io::path("stdlib.lua"), true);
|
||||||
load_script(io::path("classes.lua"), true);
|
load_script(io::path("classes.lua"), true);
|
||||||
|
load_script(io::path("internal_events.lua"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LuaCoroutine : public Process {
|
class LuaCoroutine : public Process {
|
||||||
@ -340,6 +341,13 @@ void scripting::on_world_save() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scripting::process_before_quit() {
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
if (lua::getglobal(L, "__vc_process_before_quit")) {
|
||||||
|
lua::call_nothrow(L, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void scripting::on_world_quit() {
|
void scripting::on_world_quit() {
|
||||||
auto L = lua::get_main_state();
|
auto L = lua::get_main_state();
|
||||||
for (auto& pack : content_control->getAllContentPacks()) {
|
for (auto& pack : content_control->getAllContentPacks()) {
|
||||||
@ -674,6 +682,10 @@ void scripting::load_content_script(
|
|||||||
register_event(env, "on_block_tick", prefix + ".blocktick");
|
register_event(env, "on_block_tick", prefix + ".blocktick");
|
||||||
funcsset.onblockstick =
|
funcsset.onblockstick =
|
||||||
register_event(env, "on_blocks_tick", prefix + ".blockstick");
|
register_event(env, "on_blocks_tick", prefix + ".blockstick");
|
||||||
|
funcsset.onblockpresent =
|
||||||
|
register_event(env, "on_block_present", prefix + ".blockpresent");
|
||||||
|
funcsset.onblockremoved =
|
||||||
|
register_event(env, "on_block_removed", prefix + ".blockremoved");
|
||||||
}
|
}
|
||||||
|
|
||||||
void scripting::load_content_script(
|
void scripting::load_content_script(
|
||||||
|
|||||||
@ -81,6 +81,7 @@ namespace scripting {
|
|||||||
void on_world_load(LevelController* controller);
|
void on_world_load(LevelController* controller);
|
||||||
void on_world_tick(int tps);
|
void on_world_tick(int tps);
|
||||||
void on_world_save();
|
void on_world_save();
|
||||||
|
void process_before_quit();
|
||||||
void on_world_quit();
|
void on_world_quit();
|
||||||
void cleanup(const std::vector<std::string>& nonReset);
|
void cleanup(const std::vector<std::string>& nonReset);
|
||||||
void on_blocks_tick(const Block& block, int tps);
|
void on_blocks_tick(const Block& block, int tps);
|
||||||
|
|||||||
@ -5,8 +5,6 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
/// @brief Thread-safe pool of same-sized buffers
|
/// @brief Thread-safe pool of same-sized buffers
|
||||||
/// @tparam T array type
|
/// @tparam T array type
|
||||||
|
|||||||
64
src/util/ObjectsPool.hpp
Normal file
64
src/util/ObjectsPool.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
struct AlignedDeleter {
|
||||||
|
void operator()(void* p) const {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
_aligned_free(p);
|
||||||
|
#else
|
||||||
|
std::free(p);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class ObjectsPool {
|
||||||
|
public:
|
||||||
|
ObjectsPool(size_t preallocated = 0) {
|
||||||
|
for (size_t i = 0; i < preallocated; i++) {
|
||||||
|
allocateNew();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
std::shared_ptr<T> create(Args&&... args) {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
if (freeObjects.empty()) {
|
||||||
|
allocateNew();
|
||||||
|
}
|
||||||
|
auto ptr = freeObjects.front();
|
||||||
|
freeObjects.pop();
|
||||||
|
new (ptr)T(std::forward<Args>(args)...);
|
||||||
|
return std::shared_ptr<T>(reinterpret_cast<T*>(ptr), [this](T* ptr) {
|
||||||
|
ptr->~T();
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
freeObjects.push(ptr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<void, AlignedDeleter>> objects;
|
||||||
|
std::queue<void*> freeObjects;
|
||||||
|
std::mutex mutex;
|
||||||
|
|
||||||
|
void allocateNew() {
|
||||||
|
std::unique_ptr<void, AlignedDeleter> ptr(
|
||||||
|
#if defined(_WIN32)
|
||||||
|
_aligned_malloc(sizeof(T), alignof(T))
|
||||||
|
#else
|
||||||
|
std::aligned_alloc(alignof(T), sizeof(T))
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
freeObjects.push(ptr.get());
|
||||||
|
objects.push_back(std::move(ptr));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -50,6 +50,8 @@ struct BlockFuncsSet {
|
|||||||
bool randupdate : 1;
|
bool randupdate : 1;
|
||||||
bool onblocktick : 1;
|
bool onblocktick : 1;
|
||||||
bool onblockstick : 1;
|
bool onblockstick : 1;
|
||||||
|
bool onblockpresent : 1;
|
||||||
|
bool onblockremoved : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CoordSystem {
|
struct CoordSystem {
|
||||||
|
|||||||
@ -2,22 +2,23 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "content/Content.hpp"
|
#include "Block.hpp"
|
||||||
|
#include "Chunk.hpp"
|
||||||
#include "coders/json.hpp"
|
#include "coders/json.hpp"
|
||||||
|
#include "content/Content.hpp"
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
#include "world/files/WorldFiles.hpp"
|
|
||||||
#include "items/Inventories.hpp"
|
#include "items/Inventories.hpp"
|
||||||
#include "lighting/Lightmap.hpp"
|
#include "lighting/Lightmap.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
#include "objects/Entity.hpp"
|
#include "objects/Entity.hpp"
|
||||||
#include "voxels/blocks_agent.hpp"
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "world/LevelEvents.hpp"
|
#include "util/ObjectsPool.hpp"
|
||||||
|
#include "voxels/blocks_agent.hpp"
|
||||||
|
#include "world/files/WorldFiles.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
#include "world/LevelEvents.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
#include "Block.hpp"
|
|
||||||
#include "Chunk.hpp"
|
|
||||||
|
|
||||||
static debug::Logger logger("chunks-storage");
|
static debug::Logger logger("chunks-storage");
|
||||||
|
|
||||||
@ -89,13 +90,15 @@ static inline auto load_inventories(
|
|||||||
return invs;
|
return invs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static util::ObjectsPool<Chunk> chunks_pool(1'024);
|
||||||
|
|
||||||
std::shared_ptr<Chunk> GlobalChunks::create(int x, int z) {
|
std::shared_ptr<Chunk> GlobalChunks::create(int x, int z) {
|
||||||
const auto& found = chunksMap.find(keyfrom(x, z));
|
const auto& found = chunksMap.find(keyfrom(x, z));
|
||||||
if (found != chunksMap.end()) {
|
if (found != chunksMap.end()) {
|
||||||
return found->second;
|
return found->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto chunk = std::make_shared<Chunk>(x, z);
|
auto chunk = chunks_pool.create(x, z);
|
||||||
chunksMap[keyfrom(x, z)] = chunk;
|
chunksMap[keyfrom(x, z)] = chunk;
|
||||||
|
|
||||||
World& world = *level.getWorld();
|
World& world = *level.getWorld();
|
||||||
@ -127,8 +130,6 @@ std::shared_ptr<Chunk> GlobalChunks::create(int x, int z) {
|
|||||||
chunk->flags.loadedLights = true;
|
chunk->flags.loadedLights = true;
|
||||||
}
|
}
|
||||||
chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z);
|
chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z);
|
||||||
|
|
||||||
level.events->trigger(LevelEventType::CHUNK_PRESENT, chunk.get());
|
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,39 +14,58 @@ std::vector<BlockRegisterEvent> blocks_agent::pull_register_events() {
|
|||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t get_events_bits(const Block& def) {
|
||||||
|
uint8_t bits = 0;
|
||||||
|
auto funcsset = def.rt.funcsset;
|
||||||
|
bits |= BlockRegisterEvent::UPDATING_BIT * funcsset.onblocktick;
|
||||||
|
bits |= BlockRegisterEvent::PRESENT_EVENT_BIT * funcsset.onblockpresent;
|
||||||
|
bits |= BlockRegisterEvent::REMOVED_EVENT_BIT * funcsset.onblockremoved;
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
static void on_chunk_register_event(
|
static void on_chunk_register_event(
|
||||||
const ContentIndices& indices,
|
const ContentIndices& indices,
|
||||||
const Chunk& chunk,
|
const Chunk& chunk,
|
||||||
BlockRegisterEvent::Type type
|
bool present
|
||||||
) {
|
) {
|
||||||
for (int i = 0; i < CHUNK_VOL; i++) {
|
const auto& voxels = chunk.voxels;
|
||||||
const auto& def =
|
|
||||||
indices.blocks.require(chunk.voxels[i].id);
|
int totalBegin = chunk.bottom * (CHUNK_W * CHUNK_D);
|
||||||
if (def.rt.funcsset.onblocktick) {
|
int totalEnd = chunk.top * (CHUNK_W * CHUNK_D);
|
||||||
int x = i % CHUNK_W + chunk.x * CHUNK_W;
|
|
||||||
int z = (i / CHUNK_W) % CHUNK_D + chunk.z * CHUNK_D;
|
uint8_t flagsCache[1024] {};
|
||||||
int y = (i / CHUNK_W / CHUNK_D);
|
|
||||||
block_register_events.push_back(BlockRegisterEvent {
|
for (int i = totalBegin; i <= totalEnd; i++) {
|
||||||
type, def.rt.id, {x, y, z}
|
blockid_t id = voxels[i].id;
|
||||||
});
|
uint8_t bits = id < sizeof(flagsCache) ? flagsCache[id] : 0;
|
||||||
|
if ((bits & 0x80) == 0) {
|
||||||
|
const auto& def = indices.blocks.require(id);
|
||||||
|
bits = get_events_bits(def);
|
||||||
|
flagsCache[id] = bits | 0x80;
|
||||||
}
|
}
|
||||||
|
bits &= 0x7F;
|
||||||
|
if (bits == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int x = i % CHUNK_W + chunk.x * CHUNK_W;
|
||||||
|
int z = (i / CHUNK_W) % CHUNK_D + chunk.z * CHUNK_D;
|
||||||
|
int y = (i / CHUNK_W / CHUNK_D);
|
||||||
|
block_register_events.push_back(BlockRegisterEvent {
|
||||||
|
static_cast<uint8_t>(bits | (present ? 1 : 0)), id, {x, y, z}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blocks_agent::on_chunk_present(
|
void blocks_agent::on_chunk_present(
|
||||||
const ContentIndices& indices, const Chunk& chunk
|
const ContentIndices& indices, const Chunk& chunk
|
||||||
) {
|
) {
|
||||||
on_chunk_register_event(
|
on_chunk_register_event(indices, chunk, true);
|
||||||
indices, chunk, BlockRegisterEvent::Type::REGISTER_UPDATING
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void blocks_agent::on_chunk_remove(
|
void blocks_agent::on_chunk_remove(
|
||||||
const ContentIndices& indices, const Chunk& chunk
|
const ContentIndices& indices, const Chunk& chunk
|
||||||
) {
|
) {
|
||||||
on_chunk_register_event(
|
on_chunk_register_event(indices, chunk, false);
|
||||||
indices, chunk, BlockRegisterEvent::Type::UNREGISTER_UPDATING
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Storage>
|
template <class Storage>
|
||||||
@ -101,11 +120,14 @@ static void finalize_block(
|
|||||||
chunk.flags.blocksData = true;
|
chunk.flags.blocksData = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (def.rt.funcsset.onblocktick) {
|
|
||||||
block_register_events.push_back(BlockRegisterEvent {
|
uint8_t bits = get_events_bits(def);
|
||||||
BlockRegisterEvent::Type::UNREGISTER_UPDATING, def.rt.id, {x, y, z}
|
if (bits == 0) {
|
||||||
});
|
return;
|
||||||
}
|
}
|
||||||
|
block_register_events.push_back(BlockRegisterEvent {
|
||||||
|
bits, def.rt.id, {x, y, z}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Storage>
|
template <class Storage>
|
||||||
@ -131,9 +153,17 @@ static void initialize_block(
|
|||||||
refresh_chunk_heights(chunk, id == BLOCK_AIR, y);
|
refresh_chunk_heights(chunk, id == BLOCK_AIR, y);
|
||||||
mark_neighboirs_modified(chunks, cx, cz, lx, lz);
|
mark_neighboirs_modified(chunks, cx, cz, lx, lz);
|
||||||
|
|
||||||
|
uint8_t bits = get_events_bits(def);
|
||||||
|
if (bits == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
block_register_events.push_back(BlockRegisterEvent {
|
||||||
|
static_cast<uint8_t>(bits | 1), def.rt.id, {x, y, z}
|
||||||
|
});
|
||||||
|
|
||||||
if (def.rt.funcsset.onblocktick) {
|
if (def.rt.funcsset.onblocktick) {
|
||||||
block_register_events.push_back(BlockRegisterEvent {
|
block_register_events.push_back(BlockRegisterEvent {
|
||||||
BlockRegisterEvent::Type::REGISTER_UPDATING, def.rt.id, {x, y, z}
|
bits, def.rt.id, {x, y, z}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,11 +25,11 @@ struct AABB;
|
|||||||
namespace blocks_agent {
|
namespace blocks_agent {
|
||||||
|
|
||||||
struct BlockRegisterEvent {
|
struct BlockRegisterEvent {
|
||||||
enum class Type : uint16_t {
|
static inline constexpr uint8_t REGISTER_BIT = 0x1;
|
||||||
REGISTER_UPDATING,
|
static inline constexpr uint8_t UPDATING_BIT = 0x2;
|
||||||
UNREGISTER_UPDATING,
|
static inline constexpr uint8_t PRESENT_EVENT_BIT = 0x4;
|
||||||
};
|
static inline constexpr uint8_t REMOVED_EVENT_BIT = 0x8;
|
||||||
Type type;
|
uint8_t bits;
|
||||||
blockid_t id;
|
blockid_t id;
|
||||||
glm::ivec3 coord;
|
glm::ivec3 coord;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,22 +16,34 @@ struct blockstate {
|
|||||||
uint8_t userbits : 8; // bits for use in block script
|
uint8_t userbits : 8; // bits for use in block script
|
||||||
};
|
};
|
||||||
static_assert(sizeof(blockstate) == 2);
|
static_assert(sizeof(blockstate) == 2);
|
||||||
|
static_assert(alignof(blockstate) == 1);
|
||||||
|
static_assert(sizeof(blockstate) == sizeof(blockstate_t));
|
||||||
|
|
||||||
/// @brief blockstate cast to an integer (optimized out in most cases)
|
/// @brief blockstate cast to an integer (optimized out in most cases)
|
||||||
|
#ifdef _WIN32
|
||||||
|
inline blockstate_t blockstate2int(blockstate b) {
|
||||||
|
return *reinterpret_cast<blockstate_t*>(&b);
|
||||||
|
#else
|
||||||
inline constexpr blockstate_t blockstate2int(blockstate b) {
|
inline constexpr blockstate_t blockstate2int(blockstate b) {
|
||||||
return static_cast<blockstate_t>(b.rotation) |
|
return static_cast<blockstate_t>(b.rotation) |
|
||||||
static_cast<blockstate_t>(b.segment) << 3 |
|
static_cast<blockstate_t>(b.segment) << 3 |
|
||||||
static_cast<blockstate_t>(b.reserved) << 6 |
|
static_cast<blockstate_t>(b.reserved) << 6 |
|
||||||
static_cast<blockstate_t>(b.userbits) << 8;
|
static_cast<blockstate_t>(b.userbits) << 8;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief integer cast to a blockstate (optimized out in most cases)
|
/// @brief integer cast to a blockstate (optimized out in most cases)
|
||||||
|
#ifdef _WIN32
|
||||||
|
inline blockstate int2blockstate(blockstate_t i) {
|
||||||
|
return *reinterpret_cast<blockstate*>(&i);
|
||||||
|
#else
|
||||||
inline constexpr blockstate int2blockstate(blockstate_t i) {
|
inline constexpr blockstate int2blockstate(blockstate_t i) {
|
||||||
return {
|
return {
|
||||||
static_cast<uint8_t>(i & 0b111),
|
static_cast<uint8_t>(i & 0b111),
|
||||||
static_cast<uint8_t>((i >> 3) & 0b111),
|
static_cast<uint8_t>((i >> 3) & 0b111),
|
||||||
static_cast<uint8_t>((i >> 6) & 0b11),
|
static_cast<uint8_t>((i >> 6) & 0b11),
|
||||||
static_cast<uint8_t>((i >> 8) & 0xFF)};
|
static_cast<uint8_t>((i >> 8) & 0xFF)};
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
struct voxel {
|
struct voxel {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user