diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index cb599308..14805655 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -32,7 +32,7 @@ jobs: # install EnTT git clone https://github.com/skypjack/entt.git cd entt/build - cmake -DCMAKE_BUILD_TYPE=Release .. + cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=on .. sudo make install cd ../.. - name: Configure diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 69fb0a46..ddaa95f8 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -46,7 +46,7 @@ jobs: # install EnTT git clone https://github.com/skypjack/entt.git cd entt/build - cmake -DCMAKE_BUILD_TYPE=Release .. + cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=on .. sudo make install cd ../.. diff --git a/CHANGELOG.md b/CHANGELOG.md index 845cedc9..d4030492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,96 +1,153 @@ -# 0.25 - 2024.12.01 +# 0.26 - 2025.01.27 -[Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/tree/release-0.25/doc/en/main-page.md) for 0.25 +[Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/tree/release-0.26/doc/en/main-page.md) for 0.26 Table of contents: - [Added](#added) + - [Changes](#changes) - [Functions](#functions) - [Fixes](#fixes) ## Added -- 3dtext -- blockwraps -- network (http requests and sockets) +- headless mode `--headless` +- script execution mode `--headless --script filename` +- test execution mode `--headless --test filename` +- vctest console application - libraries: - - base64 - - gfx.text3d - - gfx.blockwraps - - network + - app + - byteutil +- in-game chat +- text markup: Markdown +- syntax-highlighting: Lua +- http post requests +- `Scripts` menu page for app scripts +- binding `hud.chat` +- user-defined console.submit - events: - - on_replaced - - on_block_replaced - - on_player_tick -- structures 'lowering' property -- add 'hint' property to textbox -- add 'taking' and 'placing' properties to slot and slotsgrid -- add 'scroll-step' property to container -- add 'line-numbers' and 'text-color' to textbox -- modules: - - base:util -- uinode property 'id' -- block.materials table -- block.properties table -- item.properties table -- add version to world info table -- add 'sizeSpread' particles property -- add user properties + - on_chunk_present + - on_chunk_remove + - on_inventory_open + - on_inventory_closed +- [canvas](https://github.com/MihailRis/VoxelEngine-Cpp/pull/444) ui node +- settings: + - `graphics.dense-render` +- block properties: + - `culling` +- particles properties: + - `angle_spread` + - `min_angular_vel`, `max_angular_vel` +- bytearray support in serializers +- ui properties: + - uinode: `cursor` + - textbox: `markup`, `syntax`, `text-color` + - label: `markup` +- base pack: + - add transparent leaves render mode + - add falling leaves particles + - 'states' parameter in base:falling_block + - added/updated sounds +- nameless worlds +- SIGTERM handler +- project: + - clang Windows workflow + - engine tests + +### Changes + +- moved `devtools.console` binding handler to Lua +- move `key:escape` binding handler to Lua +- upgrade dead emitters garbage collection +- reserved player entity ids: `0` - none (example: dead), `-1` - auto (spawns new one) +- input.add_callback("key:name") support and add optional `owner` argument ### Functions -- player.is_infinite_items -- player.set_infinite_items -- player.is_instant_destruction -- player.set_instant_destruction -- player.get_name -- player.set_name -- hud.open -- base64.encode -- base64.decode -- utf8.escape -- string.escape -- textbox:lineAt -- textbox:linePos -- network.get -- network.get_binary -- network.tcp_connect -- network.tcp_open -- network.get_total_upload -- network.get_total_download -- gfx.text3d.show -- gfx.text3d.hide -- gfx.text3d.get_text -- gfx.text3d.set_text -- gfx.text3d.get_pos -- gfx.text3d.set_pos -- gfx.text3d.get_axis_x -- gfx.text3d.set_axis_x -- gfx.text3d.get_axis_y -- gfx.text3d.set_axis_y -- gfx.text3d.set_rotation -- gfx.text3d.update_settings +- app.tick +- app.sleep +- app.sleep_until +- app.new_world +- app.open_world +- app.save_world +- app.close_world +- app.reopen_world +- app.delete_world +- app.config_packs +- app.reconfig_packs +- app.get_setting +- app.set_setting +- app.get_version +- app.get_setting_info +- app.load_content +- app.reset_content +- app.is_content_loaded +- app.quit +- entity:get_player +- start_coroutine +- gui.clear_markup +- gui.escape_markup +- gui.alert +- gui.confirm +- gui.load_document +- console.get +- world.get_chunk_data +- world.set_chunk_data +- world.save_chunk_data +- world.count_chunks +- player.create +- player.delete +- player.is_suspended +- player.set_suspended +- player.is_loaded_chunks +- player.set_loading_chunks +- network.post +- table.shuffle +- table.deep_copy +- math.normalize +- math.round +- byteutil.pack +- byteutil.unpack +- file.name +- file.stem +- file.ext +- file.prefix +- hud.set_allow_pause + +Methods: +- uinode:reposition +- socket:available + +New overloads: +- block.get_X, block.get_Y, block.get_Z +- player.get_rot +- Bytearray:append ## Fixes - -- [fix translucent blocks render](https://github.com/MihailRis/VoxelEngine-Cpp/pull/370) -- [fix blocks selection with semi-transparent blocks](https://github.com/MihailRis/VoxelEngine-Cpp/commit/171cbb48d099032d7e78c51a46c374104f96f0d1) -- [fix: commands repository not reset before world open](https://github.com/MihailRis/VoxelEngine-Cpp/commit/1a00a91b604399f3108aa995422d371e573e650b) -- [mip-mapping related fixes](https://github.com/MihailRis/VoxelEngine-Cpp/commit/d9277e1b31714632bd7f5f601b8362a9e7cb8819) -- [fix disabled slots display](https://github.com/MihailRis/VoxelEngine-Cpp/commit/e8ee3e04b1398a3ada8445591267525304410571) -- [fix attack](https://github.com/MihailRis/VoxelEngine-Cpp/commit/bc17abc8b3ee7ff9027f7e3c375ca0330bb8e7bc) -- [fix: commands repository not reset before world open](https://github.com/MihailRis/VoxelEngine-Cpp/commit/1a00a91b604399f3108aa995422d371e573e650b) -- [fix stdlib.lua](https://github.com/MihailRis/VoxelEngine-Cpp/commit/6ec33ab98c78523eaececf40f113f2323d25a33a) -- [fix file.write_bytes](https://github.com/MihailRis/VoxelEngine-Cpp/commit/0fec17a8b69ac81255b77022f3af5addf8fcc8f8) -- [fix World::nextInventoryId](https://github.com/MihailRis/VoxelEngine-Cpp/commit/371fdaedcef2c163edd226160f388068b2bf5e83) -- [fix block inventory unbinding](https://github.com/MihailRis/VoxelEngine-Cpp/commit/6f6c2a916afd6b9b79221111fc72b1a86109be13) -- [fix xml text escapes handling](https://github.com/MihailRis/VoxelEngine-Cpp/commit/53c54dc91d132c221ff5fea2f7e9fb4568db9a0f) -- [fix `\'` escape parsing](https://github.com/MihailRis/VoxelEngine-Cpp/commit/2bc6cbda2e809b14fa6cffe09161b53c1636675f) -- [fix crosshair look](https://github.com/MihailRis/VoxelEngine-Cpp/commit/e034bda477c35efe96548e78ecc722966a7a2197) -- [fix: actual block inventory size not updating on inventory-size property update](https://github.com/MihailRis/VoxelEngine-Cpp/commit/1ba5b0ce33103e539ccb199ee1cd52095e286a1f) -- [fix falling block hitbox](https://github.com/MihailRis/VoxelEngine-Cpp/commit/352ef6485a4b796d1cdc8dd0e00ab1a1d72a2c0a) -- [fix console position](https://github.com/MihailRis/VoxelEngine-Cpp/commit/3ea213e8d3cee7be55ec39ffb18dc557dec7557b) -- [fix: fatal error on pack removal when no world open](https://github.com/MihailRis/VoxelEngine-Cpp/commit/78d5ab02c2ba8a3d05cf5639eb10a49c9ca14ec3) -- [fix custom model lighting](https://github.com/MihailRis/VoxelEngine-Cpp/commit/a333cadfcaeb485a30833343d55faf01b28a5c5f) -- [fix: emitter does not skip particles](https://github.com/MihailRis/VoxelEngine-Cpp/commit/983e516fb4ebc1f2def592f2b7f3195d968deed2) -- [fix old custom models render](https://github.com/MihailRis/VoxelEngine-Cpp/commit/82733d38011b52a426cb74560521949c1cd43cc1) +- [fix on_block_interact & fix segfault after engine finished](https://github.com/MihailRis/VoxelEngine-Cpp/commit/d1f92c21d0bbdf2df0eb3b31c5637bdf7110444c) +- [fix item.properties](https://github.com/MihailRis/VoxelEngine-Cpp/commit/92fb19ba5e2307fdbcbf5d0e55f9c0712be45f72) +- [fix base:bazalt durability](https://github.com/MihailRis/VoxelEngine-Cpp/commit/a036c5e383135dc0f9b086e244188d1ceb3f0bf2) +- [fix camera-related bugs](https://github.com/MihailRis/VoxelEngine-Cpp/commit/0d071ab0141edbf087f3ec03505792740023c01e) +- [fix: grabbed item is deleted on inventory close](https://github.com/MihailRis/VoxelEngine-Cpp/commit/2787f2fc5495004f6029644ed5221f3abfc0c68f) +- [fix block overriding](https://github.com/MihailRis/VoxelEngine-Cpp/commit/cda34e3975a42696ea31a1b0018731e746cd13bb) +- [fix faces culling when 'light-passing' is false](https://github.com/MihailRis/VoxelEngine-Cpp/commit/954724c8378da525fc7349c018e9351c5bdfdf8f) +- [fix particles lighting](https://github.com/MihailRis/VoxelEngine-Cpp/commit/6be640458d6b4ae46866b342ca0f26e561ead125) +- [fix non-skipping particles](https://github.com/MihailRis/VoxelEngine-Cpp/pull/421/commits/f1c7317c5ab2a148e5188e091cd1aa3490dc8b4d) +- [fix content stats](https://github.com/MihailRis/VoxelEngine-Cpp/commit/97eef3ef1900157a9648bade8e06b203b99ee6f6) +- [fix byte manipulation functions](https://github.com/MihailRis/VoxelEngine-Cpp/commit/9490d1f7eacb00f56112dfdd1ea12bb9c3ca528d) +- [fix error handling in events and runnables](https://github.com/MihailRis/VoxelEngine-Cpp/commit/03a3062940ebfc4e8f0b3efc5930c71f8d07b604) +- [fix small dumb legacy memory leak](https://github.com/MihailRis/VoxelEngine-Cpp/commit/4d0b9f049b79322959e4aefd95eedc665e87d087) +- [fix grass lighting](https://github.com/MihailRis/VoxelEngine-Cpp/commit/9d7816a286fb3a7269b5220502354720e4d2726b) +- [small fixes in translation.](https://github.com/MihailRis/VoxelEngine-Cpp/commit/d25452784d68be19821dc917ad15bc0a92d81bd9) +- [fix errors handling in event handlers](https://github.com/MihailRis/VoxelEngine-Cpp/commit/f62fc5a039dca70219fb2b38f61fc53a2542adf7) +- [fix lua stack manipulations](https://github.com/MihailRis/VoxelEngine-Cpp/commit/e7555448cf0df86995b40d67fa58de1ca78f8105) +- [fix lua::create_lambda](https://github.com/MihailRis/VoxelEngine-Cpp/commit/40cdebb175014736e35bc31ecc93ae72fb00a6e9) +- [fix some UB](https://github.com/MihailRis/VoxelEngine-Cpp/commit/b5999fe36420d116674abc353ed3dad739ac5f70) +- [fix rigidbody:is_enabled](https://github.com/MihailRis/VoxelEngine-Cpp/commit/2adfbdb19226b2685848131073a56b354706433d) +- [fix panel elements removal](https://github.com/MihailRis/VoxelEngine-Cpp/commit/c6951e09651149463528bdffbc2cba4ea41de4a4) +- [fix infinite block fields conversion requests](https://github.com/MihailRis/VoxelEngine-Cpp/commit/0494db91872abff500cfc153a32035ee3f2745ae) +- [fix data_buffer:put_number](https://github.com/MihailRis/VoxelEngine-Cpp/commit/e247902cc6ffdaa6beab391fcfdaea7f021ab063) +- [fix textbox horizontal scroll & fix console log width](https://github.com/MihailRis/VoxelEngine-Cpp/commit/13fde2116d095b9393c4f5804ba23071e5f56ad6) +- [fix is_array](https://github.com/MihailRis/VoxelEngine-Cpp/pull/420) +- [fix neighbour chunk update](https://github.com/MihailRis/VoxelEngine-Cpp/pull/404) +- [fix lamp material](https://github.com/MihailRis/VoxelEngine-Cpp/commit/57356e1d64d6d9d7e8d59b078543b290e998ad00) diff --git a/CMakeLists.txt b/CMakeLists.txt index abfd430e..9f773002 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,7 +63,7 @@ else() endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie") endif() if(WIN32) diff --git a/README.md b/README.md index e31e1436..caae42ad 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Latest release - [Download](https://github.com/MihailRis/VoxelEngine-Cpp/releases/latest) | [Скачать](https://github.com/MihailRis/VoxelEngine-Cpp/releases/latest) -- [Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/en/main-page.md) | [Документация](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/ru/main-page.md) +- [Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.26/doc/en/main-page.md) | [Документация](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.26/doc/ru/main-page.md) ## Build project in Linux @@ -14,7 +14,7 @@ ```sh git clone https://github.com/skypjack/entt.git cd entt/build -cmake -DCMAKE_BUILD_TYPE=Release .. +cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=on .. sudo make install ``` diff --git a/doc/en/console.md b/doc/en/console.md index 04d3d48d..fff57c76 100644 --- a/doc/en/console.md +++ b/doc/en/console.md @@ -2,6 +2,13 @@ To work with the command interpreter, use the **console** library. +When sending a command via the standard console (core:console layout): +1. the `allow-cheats` rule is checked +2. the `player`, `pos.x|y|z`, `entity.id`, `entity.selected` variables are automatically set. +3. the command handler is called - console.submit or the default one. + +The default handler calls console.execute, passing the result to the console.log call. + ## Commands creation To create a console command, use the following function: diff --git a/doc/en/main-page.md b/doc/en/main-page.md index 0daa4729..0a4748b4 100644 --- a/doc/en/main-page.md +++ b/doc/en/main-page.md @@ -1,8 +1,6 @@ # Documentation -Documentation for in-development version 0.26. - -[Documentation for stable release 0.25.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/en/main-page.md) +Documentation for release 0.26. ## Sections diff --git a/doc/en/scripting/builtins/libgui.md b/doc/en/scripting/builtins/libgui.md index 64b5005b..cf6839db 100644 --- a/doc/en/scripting/builtins/libgui.md +++ b/doc/en/scripting/builtins/libgui.md @@ -62,6 +62,17 @@ gui.escape_markup( Escapes markup in text. +```lua +gui.alert( + -- message (not automatically translated, use gui.str(...)) + message: str, + -- function called on close + on_ok: function() -> nil +) +``` + +Displays a message box. **Non-blocking**. + ```lua gui.confirm( -- message (does not translate automatically, use gui.str(...)) @@ -78,7 +89,7 @@ gui.confirm( ) ``` -Requests confirmation from the user for an action. **Does not** stop code execution. +Requests confirmation from the user for an action. **Non-blocking**. ```lua gui.load_document( diff --git a/doc/en/scripting/builtins/libplayer.md b/doc/en/scripting/builtins/libplayer.md index e235377a..fa799198 100644 --- a/doc/en/scripting/builtins/libplayer.md +++ b/doc/en/scripting/builtins/libplayer.md @@ -96,6 +96,15 @@ player.get_spawnpoint(playerid: int) -> number, number, number Spawn point setter and getter +```lua +player.is_suspended(pid: int) -> bool +player.set_suspended(pid: int, suspended: bool) +``` + +Setter and getter for the player's suspended status. + +When suspended, the entity is deleted and the player is disabled from the world simulation. + ```lua player.set_name(playerid: int, name: str) player.get_name(playerid: int) -> str diff --git a/doc/en/scripting/builtins/librules.md b/doc/en/scripting/builtins/librules.md index c4b1c377..acdf11d8 100644 --- a/doc/en/scripting/builtins/librules.md +++ b/doc/en/scripting/builtins/librules.md @@ -18,7 +18,7 @@ Creates a rule. If a handler is specified, returns the id for deletion. > Rules that have not been created can be used, but resetting via rules.reset will result in setting the value to nil. ```lua - rules.listen( +rules.listen( -- rule name name: str, -- value change handler function diff --git a/doc/en/scripting/builtins/libworld.md b/doc/en/scripting/builtins/libworld.md index 97c6fdee..b2453b34 100644 --- a/doc/en/scripting/builtins/libworld.md +++ b/doc/en/scripting/builtins/libworld.md @@ -49,10 +49,11 @@ world.is_night() -> bool world.count_chunks() -> int -- Returns the compressed chunk data to send. +-- If the chunk is not loaded, returns the saved data. -- Currently includes: -- 1. Voxel data (id and state) -- 2. Voxel metadata (fields) -world.get_chunk_data(x: int, z: int) -> Bytearray +world.get_chunk_data(x: int, z: int) -> Bytearray or nil -- Modifies the chunk based on the compressed data. -- Returns true if the chunk exists. @@ -61,4 +62,12 @@ world.set_chunk_data( -- compressed chunk data data: Bytearray ) -> bool + +-- Saves chunk data to region. +-- Changes will be written to file only on world save. +world.save_chunk_data( + x: int, z: int, + -- compressed chunk data + data: Bytearray +) ``` diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index 8763d981..90a64828 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -26,6 +26,9 @@ entity:get_uid() -> int entity:get_component(name: str) -> component or nil -- Checks for the presence of a component by name entity:has_component(name: str) -> bool + +-- Enables/disables the component +entity:set_enabled(name: str, enable: bool) ``` ## Built-in components diff --git a/doc/ru/console.md b/doc/ru/console.md index a142f84f..b5f931f6 100644 --- a/doc/ru/console.md +++ b/doc/ru/console.md @@ -2,6 +2,13 @@ Для работы с командным интерпретатором предоставляется библиотека **console**. +При отправке команды через стандартную консоль (макет core:console): +1. проверяется правило `allow-cheats` +2. автоматически устанавливаются переменные `player`, `pos.x|y|z`, `entity.id`, `entity.selected`. +3. вызывается обработчик команд - console.submit или по-умолчанию. + +Обработчик по-умолчанию вызывает console.execute, передавая результат в вызов console.log. + ## Создание команд Для создания команды консоли используется следующая функция: diff --git a/doc/ru/main-page.md b/doc/ru/main-page.md index 10dff292..52e0faa1 100644 --- a/doc/ru/main-page.md +++ b/doc/ru/main-page.md @@ -1,8 +1,6 @@ # Документация -Документация разрабатываемой версии 0.26. - -[Документация стабильной версии 0.25.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/ru/main-page.md) +Документация версии 0.26. ## Разделы diff --git a/doc/ru/scripting/builtins/libgui.md b/doc/ru/scripting/builtins/libgui.md index 66e67809..aea2f44a 100644 --- a/doc/ru/scripting/builtins/libgui.md +++ b/doc/ru/scripting/builtins/libgui.md @@ -59,6 +59,17 @@ gui.escape_markup( Экранирует разметку в тексте. +```lua +gui.alert( + -- сообщение (не переводится автоматически, используйте gui.str(...)) + message: str, + -- функция, вызываемая при закрытии + on_ok: function() -> nil +) +``` + +Выводит окно с сообщением. **Не** останавливает выполнение кода. + ```lua gui.confirm( -- сообщение (не переводится автоматически, используйте gui.str(...)) diff --git a/doc/ru/scripting/builtins/libplayer.md b/doc/ru/scripting/builtins/libplayer.md index 35dda747..2228f42c 100644 --- a/doc/ru/scripting/builtins/libplayer.md +++ b/doc/ru/scripting/builtins/libplayer.md @@ -96,6 +96,15 @@ player.get_spawnpoint(playerid: int) -> number, number, number Сеттер и геттер точки спавна игрока +```lua +player.is_suspended(pid: int) -> bool +player.set_suspended(pid: int, suspended: bool) +``` + +Сеттер и геттер статуса "заморозки" игрока. + +При "заморозке" удаляется сущность, а игрок выключается из симуляции мира. + ```lua player.set_name(playerid: int, name: str) player.get_name(playerid: int) -> str diff --git a/doc/ru/scripting/builtins/libworld.md b/doc/ru/scripting/builtins/libworld.md index a7e0e5a6..5713a328 100644 --- a/doc/ru/scripting/builtins/libworld.md +++ b/doc/ru/scripting/builtins/libworld.md @@ -48,10 +48,11 @@ world.is_night() -> bool world.count_chunks() -> int -- Возвращает сжатые данные чанка для отправки. +-- Если чанк не загружен, возвращает сохранённые данные. -- На данный момент включает: -- 1. Данные вокселей (id и состояние) -- 2. Метаданные (поля) вокселей -world.get_chunk_data(x: int, z: int) -> Bytearray +world.get_chunk_data(x: int, z: int) -> Bytearray или nil -- Изменяет чанк на основе сжатых данных. -- Возвращает true если чанк существует. @@ -60,4 +61,12 @@ world.set_chunk_data( -- сжатые данные чанка data: Bytearray ) -> bool + +-- Сохраняет данные чанка в регион. +-- Изменения будет записаны в файл только после сохранения мира. +world.save_chunk_data( + x: int, z: int, + -- сжатые данные чанка + data: Bytearray +) ``` diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index 3e8e00e5..db44a1ac 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -26,6 +26,9 @@ entity:get_uid() -> int entity:get_component(name: str) -> компонент или nil -- Проверяет наличие компонента по имени entity:has_component(name: str) -> bool + +-- Включает/выключает компонент по имени +entity:set_enabled(name: str, enable: bool) ``` ## Встроенные компоненты diff --git a/res/content/base/block_materials/snow.json b/res/content/base/block_materials/snow.json new file mode 100644 index 00000000..42359b2e --- /dev/null +++ b/res/content/base/block_materials/snow.json @@ -0,0 +1,5 @@ +{ + "steps-sound": "steps/snow", + "place-sound": "blocks/snow_place", + "break-sound": "blocks/snow_break" +} diff --git a/res/content/base/blocks/lamp.json b/res/content/base/blocks/lamp.json index 4a1f1ce7..b8f91100 100644 --- a/res/content/base/blocks/lamp.json +++ b/res/content/base/blocks/lamp.json @@ -2,5 +2,6 @@ "texture": "lamp", "emission": [15, 14, 13], "shadeless": true, + "material": "base:glass", "base:durability": 0.3 } diff --git a/res/content/base/sounds/blocks/snow_3.ogg b/res/content/base/sounds/blocks/snow_3.ogg new file mode 100644 index 00000000..72af9661 Binary files /dev/null and b/res/content/base/sounds/blocks/snow_3.ogg differ diff --git a/res/content/base/sounds/blocks/snow_break.ogg b/res/content/base/sounds/blocks/snow_break.ogg new file mode 100644 index 00000000..8f2df1a8 Binary files /dev/null and b/res/content/base/sounds/blocks/snow_break.ogg differ diff --git a/res/content/base/sounds/blocks/snow_place.ogg b/res/content/base/sounds/blocks/snow_place.ogg new file mode 100644 index 00000000..9595ba20 Binary files /dev/null and b/res/content/base/sounds/blocks/snow_place.ogg differ diff --git a/res/content/base/sounds/blocks/wood_break.ogg b/res/content/base/sounds/blocks/wood_break.ogg index d0fa78cc..e6c9a47f 100644 Binary files a/res/content/base/sounds/blocks/wood_break.ogg and b/res/content/base/sounds/blocks/wood_break.ogg differ diff --git a/res/content/base/sounds/blocks/wood_place.ogg b/res/content/base/sounds/blocks/wood_place.ogg index fea80562..6fbeee5e 100644 Binary files a/res/content/base/sounds/blocks/wood_place.ogg and b/res/content/base/sounds/blocks/wood_place.ogg differ diff --git a/res/content/base/sounds/steps/snow_0.ogg b/res/content/base/sounds/steps/snow_0.ogg index e50bd5fc..603946af 100644 Binary files a/res/content/base/sounds/steps/snow_0.ogg and b/res/content/base/sounds/steps/snow_0.ogg differ diff --git a/res/content/base/sounds/steps/snow_1.ogg b/res/content/base/sounds/steps/snow_1.ogg index c7409c15..8be6751e 100644 Binary files a/res/content/base/sounds/steps/snow_1.ogg and b/res/content/base/sounds/steps/snow_1.ogg differ diff --git a/res/content/base/sounds/steps/snow_2.ogg b/res/content/base/sounds/steps/snow_2.ogg index b4f44e98..415775bc 100644 Binary files a/res/content/base/sounds/steps/snow_2.ogg and b/res/content/base/sounds/steps/snow_2.ogg differ diff --git a/res/content/base/sounds/steps/snow_3.ogg b/res/content/base/sounds/steps/snow_3.ogg index 33f0161d..9e0a1c31 100644 Binary files a/res/content/base/sounds/steps/snow_3.ogg and b/res/content/base/sounds/steps/snow_3.ogg differ diff --git a/res/content/base/sounds/steps/snow_4.ogg b/res/content/base/sounds/steps/snow_4.ogg index 5ad40d6d..12f811ab 100644 Binary files a/res/content/base/sounds/steps/snow_4.ogg and b/res/content/base/sounds/steps/snow_4.ogg differ diff --git a/res/layouts/console.xml b/res/layouts/console.xml index 43e72dd8..18d72163 100644 --- a/res/layouts/console.xml +++ b/res/layouts/console.xml @@ -12,7 +12,7 @@ + size-func="unpack(vec2.add(gui.get_viewport(), {-350,-100}))"> diff --git a/res/layouts/console.xml.lua b/res/layouts/console.xml.lua index 7e41ddf6..bb36863b 100644 --- a/res/layouts/console.xml.lua +++ b/res/layouts/console.xml.lua @@ -97,11 +97,12 @@ end) function setup_variables() local pid = hud.get_player() local x,y,z = player.get_pos(pid) + console.set("player", pid) console.set('pos.x', x) console.set('pos.y', y) console.set('pos.z', z) local pentity = player.get_entity(pid) - if pentity ~= 0 then + if pentity > 0 then console.set('entity.id', pentity) end local sentity = player.get_selected_entity(pid) @@ -148,8 +149,6 @@ function submit(text) text = text:sub(2) end end - - setup_variables() local name for s in text:gmatch("%S+") do @@ -167,12 +166,19 @@ function submit(text) end document.log.caret = -1 - local status, result = pcall(console.execute, text) - if result then - console.log(result) - end document.prompt.text = "" document.prompt.focused = true + + setup_variables() + + if console.submit then + console.submit(text) + else + local status, result = pcall(console.execute, text) + if result then + console.log(result) + end + end end function set_mode(mode) diff --git a/res/layouts/ingame_chat.xml.lua b/res/layouts/ingame_chat.xml.lua index 52f38ce7..dce62988 100644 --- a/res/layouts/ingame_chat.xml.lua +++ b/res/layouts/ingame_chat.xml.lua @@ -7,16 +7,11 @@ local initialized = false local max_lines = 15 local animation_fps = 30 -local function remove_line(line) - document[line[1]]:destruct() - time.post_runnable(function() document.root:reposition() end) -end - local function update_line(line, uptime) local diff = uptime - line[2] if diff > timeout then - remove_line(line) - table.insert(dead_lines, i) + document[line[1]]:destruct() + table.insert(dead_lines, table.index(lines, line)) elseif diff > timeout-fadeout then local opacity = (timeout - diff) / fadeout document[line[1]].color = {0, 0, 0, opacity * 80} @@ -25,16 +20,16 @@ local function update_line(line, uptime) end events.on("core:chat", function(message) + while #lines >= max_lines do + document[lines[1][1]]:destruct() + table.remove(lines, 1) + end local current_time = time.uptime() local id = 'l'..tostring(nextid) document.root:add(gui.template("chat_line", {id=id})) document.root:reposition() document[id.."L"].text = message nextid = nextid + 1 - if #lines == max_lines then - remove_line(lines[1]) - table.remove(lines, 1) - end table.insert(lines, {id, current_time}) end) diff --git a/res/modules/bit_converter.lua b/res/modules/bit_converter.lua index c2ada355..b72dc311 100644 --- a/res/modules/bit_converter.lua +++ b/res/modules/bit_converter.lua @@ -313,8 +313,8 @@ function bit_converter.bytes_to_uint16(bytes, order) return bit.bor( - bit.lshift(bytes[1], 8), - bytes[2], 0) + bit.lshift(bytes[2], 8), + bytes[1], 0) end function bit_converter.bytes_to_int64(bytes, order) diff --git a/res/modules/data_buffer.lua b/res/modules/data_buffer.lua index 91dce29c..ad9ad486 100644 --- a/res/modules/data_buffer.lua +++ b/res/modules/data_buffer.lua @@ -144,31 +144,31 @@ function data_buffer:put_number(num) if math.floor(num) ~= num then type = TYPE_FLOAT64 - bytes = bit_converter.float64_to_bytes(num) + bytes = bit_converter.float64_to_bytes(num, self.order) elseif num == 0 then type = TYPE_ZERO bytes = { } elseif num > 0 then if num <= MAX_UINT16 then type = TYPE_UINT16 - bytes = bit_converter.uint16_to_bytes(num) + bytes = bit_converter.uint16_to_bytes(num, self.order) elseif num <= MAX_UINT32 then type = TYPE_UINT32 - bytes = bit_converter.uint32_to_bytes(num) + bytes = bit_converter.uint32_to_bytes(num, self.order) elseif num <= MAX_INT64 then type = TYPE_INT64 - bytes = bit_converter.int64_to_bytes(num) + bytes = bit_converter.int64_to_bytes(num, self.order) end elseif num < 0 then if num >= MIN_INT16 then type = TYPE_SINT16 - bytes = bit_converter.sint16_to_bytes(num) + bytes = bit_converter.sint16_to_bytes(num, self.order) elseif num >= MIN_INT32 then type = TYPE_SINT32 - bytes = bit_converter.sint32_to_bytes(num) + bytes = bit_converter.sint32_to_bytes(num, self.order) elseif num >= MIN_INT64 then type = TYPE_INT64 - bytes = bit_converter.int64_to_bytes(num) + bytes = bit_converter.int64_to_bytes(num, self.order) end end diff --git a/res/modules/internal/gui_util.lua b/res/modules/internal/gui_util.lua index 3cf84a33..294a7c79 100644 --- a/res/modules/internal/gui_util.lua +++ b/res/modules/internal/gui_util.lua @@ -47,7 +47,7 @@ function gui_util.add_page_dispatcher(dispatcher) table.insert(gui_util.local_dispatchers, dispatcher) end -function gui_util.reset_local() +function gui_util.__reset_local() gui_util.local_dispatchers = {} end diff --git a/res/modules/internal/stdcomp.lua b/res/modules/internal/stdcomp.lua index 9e208038..a62989a9 100644 --- a/res/modules/internal/stdcomp.lua +++ b/res/modules/internal/stdcomp.lua @@ -68,6 +68,22 @@ local Entity = {__index={ def_index=function(self) return entities.get_def(self.eid) end, def_name=function(self) return entities.def_name(entities.get_def(self.eid)) end, get_player=function(self) return entities.get_player(self.eid) end, + set_enabled=function(self, name, flag) + local comp = self.components[name] + if comp then + if flag then + if comp.__disabled and comp.on_enable then + comp.on_enable() + end + comp.__disabled = nil + else + if not comp.__disabled and comp.on_disable then + comp.on_disable() + end + comp.__disabled = true + end + end + end, }} local entities = {} @@ -99,7 +115,7 @@ return { end for _, component in pairs(entity.components) do local callback = component.on_update - if callback then + if not component.__disabled and callback then local result, err = pcall(callback, tps) if err then debug.error(err) @@ -113,7 +129,7 @@ return { for _,entity in pairs(entities) do for _, component in pairs(entity.components) do local callback = component.on_render - if callback then + if not component.__disabled and callback then local result, err = pcall(callback, delta) if err then debug.error(err) @@ -132,5 +148,8 @@ return { end return values end + end, + __reset = function() + entities = {} end } diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 78228254..9d29b873 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -37,7 +37,10 @@ local function complete_app_lib(app) app.tick = coroutine.yield app.get_version = core.get_version app.get_setting_info = core.get_setting_info - app.load_content = core.load_content + app.load_content = function() + core.load_content() + app.tick() + end app.reset_content = core.reset_content app.is_content_loaded = core.is_content_loaded @@ -191,8 +194,8 @@ function gui.template(name, params) text = text:gsub("if%s*=%s*'%%{%w+}'", "if=''") text = text:gsub("if%s*=%s*\"%%{%w+}\"", "if=\"\"") -- remove unsolved properties: attr='%{var}' - text = text:gsub("%w+%s*=%s*'%%{%w+}'%s?", "") - text = text:gsub("%w+%s*=%s*\"%%{%w+}\"%s?", "") + text = text:gsub("%s*%S+='%%{[^}]+}'%s*", " ") + text = text:gsub('%s*%S+="%%{[^}]+}"%s*', " ") return text end @@ -343,8 +346,8 @@ function __vc_on_hud_open() end) end) input.add_callback("key:escape", function() - if hud.is_paused() then - hud.resume() + if menu.page ~= "" then + menu:reset() elseif hud.is_inventory_open() then hud.close_inventory() else @@ -375,7 +378,8 @@ end function __vc_on_world_quit() _rules.clear() - gui_util:reset_local() + gui_util:__reset_local() + stdcomp.__reset() end local __vc_coroutines = {} @@ -415,7 +419,18 @@ end function start_coroutine(chunk, name) local co = coroutine.create(function() - local status, error = xpcall(chunk, __vc__error) + local status, error = xpcall(chunk, function(...) + gui.alert(debug.traceback(), function() + if world.is_open() then + __vc_app.close_world() + else + __vc_app.reset_content() + menu:reset() + menu.page = "main" + end + end) + return ... + end) if not status then debug.error(error) end diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 522e3705..8ac8b713 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -9,19 +9,21 @@ #include "AL/ALAudio.hpp" #include "NoAudio.hpp" #include "debug/Logger.hpp" +#include "util/ObjectsKeeper.hpp" static debug::Logger logger("audio"); -namespace audio { +using namespace audio; + +namespace { static speakerid_t nextId = 1; static Backend* backend; static std::unordered_map> speakers; static std::unordered_map> streams; static std::vector> channels; + static util::ObjectsKeeper objects_keeper {}; } -using namespace audio; - Channel::Channel(std::string name) : name(std::move(name)) { } @@ -148,7 +150,8 @@ public: } }; -void audio::initialize(bool enabled) { +void audio::initialize(bool enabled, AudioSettings& settings) { + enabled = enabled && settings.enabled.get(); if (enabled) { logger.info() << "initializing ALAudio backend"; backend = ALAudio::create().release(); @@ -160,7 +163,22 @@ void audio::initialize(bool enabled) { logger.info() << "initializing NoAudio backend"; backend = NoAudio::create().release(); } - create_channel("master"); + struct { + std::string name; + NumberSetting* setting; + } builtin_channels[] { + {"master", &settings.volumeMaster}, + {"regular", &settings.volumeRegular}, + {"music", &settings.volumeMusic}, + {"ambient", &settings.volumeAmbient}, + {"ui", &settings.volumeUI} + }; + for (auto& channel : builtin_channels) { + create_channel(channel.name); + objects_keeper.keepAlive(channel.setting->observe([=](auto value) { + audio::get_channel(channel.name)->setVolume(value * value); + }, true)); + } } std::unique_ptr audio::load_PCM(const fs::path& file, bool headerOnly) { @@ -442,4 +460,5 @@ void audio::close() { speakers.clear(); delete backend; backend = nullptr; + objects_keeper.clearKeepedObjects(); } diff --git a/src/audio/audio.hpp b/src/audio/audio.hpp index a37ebeea..37ae8e00 100644 --- a/src/audio/audio.hpp +++ b/src/audio/audio.hpp @@ -6,6 +6,7 @@ #include #include "typedefs.hpp" +#include "settings.hpp" namespace fs = std::filesystem; @@ -357,7 +358,7 @@ namespace audio { /// @brief Initialize audio system or use no audio mode /// @param enabled try to initialize actual audio - void initialize(bool enabled); + void initialize(bool enabled, AudioSettings& settings); /// @brief Load audio file info and PCM data /// @param file audio file diff --git a/src/constants.hpp b/src/constants.hpp index 6f4a6ce6..8755331d 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -27,6 +27,7 @@ inline constexpr blockid_t BLOCK_OBSTACLE = 1; inline constexpr blockid_t BLOCK_STRUCT_AIR = 2; inline constexpr itemid_t ITEM_EMPTY = 0; inline constexpr entityid_t ENTITY_NONE = 0; +inline constexpr entityid_t ENTITY_AUTO = std::numeric_limits::max(); inline constexpr int CHUNK_W = 16; inline constexpr int CHUNK_H = 256; diff --git a/src/data/dv.cpp b/src/data/dv.cpp index 5709fd84..cccf348e 100644 --- a/src/data/dv.cpp +++ b/src/data/dv.cpp @@ -104,6 +104,9 @@ namespace dv { } boolean_t value::asBoolean() const { + if (type == value_type::none) { + return false; + } check_type(type, value_type::boolean); return val.boolean; } diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index ce29e8c1..73704468 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -52,15 +52,6 @@ static debug::Logger logger("engine"); namespace fs = std::filesystem; -static void create_channel(Engine* engine, std::string name, NumberSetting& setting) { - if (name != "master") { - audio::create_channel(name); - } - engine->keepAlive(setting.observe([=](auto value) { - audio::get_channel(name)->setVolume(value*value); - }, true)); -} - static std::unique_ptr load_icon(const fs::path& resdir) { try { auto file = resdir / fs::u8path("textures/misc/icon.png"); @@ -73,12 +64,23 @@ static std::unique_ptr load_icon(const fs::path& resdir) { return nullptr; } -Engine::Engine(CoreParameters coreParameters) - : params(std::move(coreParameters)), - settings(), - settingsHandler({settings}), - interpreter(std::make_unique()), - network(network::Network::create(settings.network)) { +Engine::Engine() = default; + +static std::unique_ptr engine; + +Engine& Engine::getInstance() { + if (!engine) { + engine = std::make_unique(); + } + return *engine; +} + +void Engine::initialize(CoreParameters coreParameters) { + params = std::move(coreParameters); + settingsHandler = std::make_unique(settings); + interpreter = std::make_unique(); + network = network::Network::create(settings.network); + logger.info() << "engine version: " << ENGINE_VERSION_STRING; if (params.headless) { logger.info() << "headless mode is enabled"; @@ -110,12 +112,7 @@ Engine::Engine(CoreParameters coreParameters) menus::create_version_label(*this); } } - audio::initialize(settings.audio.enabled.get() && !params.headless); - create_channel(this, "master", settings.audio.volumeMaster); - create_channel(this, "regular", settings.audio.volumeRegular); - create_channel(this, "music", settings.audio.volumeMusic); - create_channel(this, "ambient", settings.audio.volumeAmbient); - create_channel(this, "ui", settings.audio.volumeUI); + audio::initialize(!params.headless, settings.audio); bool langNotSet = settings.ui.language.get() == "auto"; if (langNotSet) { @@ -140,7 +137,7 @@ void Engine::loadSettings() { logger.info() << "loading settings"; std::string text = files::read_string(settings_file); try { - toml::parse(settingsHandler, settings_file.string(), text); + toml::parse(*settingsHandler, settings_file.string(), text); } catch (const parsing_error& err) { logger.error() << err.errorLog(); throw; @@ -199,6 +196,7 @@ void Engine::updateFrontend() { audio::update(delta); gui->act(delta, Viewport(Window::width, Window::height)); screen->update(delta); + gui->postAct(); } void Engine::nextFrame() { @@ -217,12 +215,11 @@ void Engine::renderFrame() { Viewport viewport(Window::width, Window::height); DrawContext ctx(nullptr, viewport, nullptr); gui->draw(ctx, *assets); - gui->postAct(); } void Engine::saveSettings() { logger.info() << "saving settings"; - files::write_string(paths.getSettingsFile(), toml::stringify(settingsHandler)); + files::write_string(paths.getSettingsFile(), toml::stringify(*settingsHandler)); if (!params.headless) { logger.info() << "saving bindings"; files::write_string(paths.getControlsFile(), Events::writeBindings()); @@ -255,6 +252,10 @@ Engine::~Engine() { logger.info() << "engine finished"; } +void Engine::terminate() { + engine.reset(); +} + EngineController* Engine::getController() { return controller.get(); } @@ -511,7 +512,7 @@ std::shared_ptr Engine::getScreen() { } SettingsHandler& Engine::getSettingsHandler() { - return settingsHandler; + return *settingsHandler; } network::Network& Engine::getNetwork() { diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index 406d420a..d4cbf40a 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -58,9 +58,9 @@ using OnWorldOpen = std::function, int64_t)>; class Engine : public util::ObjectsKeeper { CoreParameters params; EngineSettings settings; - SettingsHandler settingsHandler; EnginePaths paths; + std::unique_ptr settingsHandler; std::unique_ptr assets; std::shared_ptr screen; std::vector contentPacks; @@ -82,9 +82,15 @@ class Engine : public util::ObjectsKeeper { void updateHotkeys(); void loadAssets(); public: - Engine(CoreParameters coreParameters); + Engine(); ~Engine(); + static Engine& getInstance(); + + void initialize(CoreParameters coreParameters); + + static void terminate(); + /// @brief Start the engine void run(); diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 0dce2bc0..0e9eb609 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -117,7 +117,9 @@ void LevelScreen::initializePack(ContentPackRuntime* pack) { } LevelScreen::~LevelScreen() { - saveWorldPreview(); + if (!controller->getLevel()->getWorld()->isNameless()) { + saveWorldPreview(); + } scripting::on_frontend_close(); // unblock all bindings Events::enableBindings(); diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index bc05fc72..5cddcb57 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -9,7 +9,7 @@ #include "lighting/Lightmap.hpp" #include "frontend/ContentGfxCache.hpp" -const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f); +const glm::vec3 BlocksRenderer::SUN_VECTOR (0.2275f,0.9388f,-0.1005f); BlocksRenderer::BlocksRenderer( size_t capacity, @@ -129,7 +129,7 @@ void BlocksRenderer::faceAO( float s = 0.5f; if (lights) { float d = glm::dot(glm::normalize(Z), SUN_VECTOR); - d = 0.8f + d * 0.2f; + d = 0.7f + d * 0.3f; auto axisX = glm::normalize(X); auto axisY = glm::normalize(Y); @@ -167,7 +167,7 @@ void BlocksRenderer::face( float s = 0.5f; if (lights) { float d = glm::dot(glm::normalize(Z), SUN_VECTOR); - d = 0.8f + d * 0.2f; + d = 0.7f + d * 0.3f; tint *= d; } vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint); diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 2cc28024..08485201 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -202,7 +202,7 @@ void GUI::act(float delta, const Viewport& vp) { void GUI::postAct() { while (!postRunnables.empty()) { - runnable callback = postRunnables.back(); + runnable callback = postRunnables.front(); postRunnables.pop(); callback(); } diff --git a/src/graphics/ui/elements/Panel.cpp b/src/graphics/ui/elements/Panel.cpp index d90b8830..d76866f0 100644 --- a/src/graphics/ui/elements/Panel.cpp +++ b/src/graphics/ui/elements/Panel.cpp @@ -53,6 +53,7 @@ void Panel::cropToContent() { void Panel::fullRefresh() { refresh(); cropToContent(); + reposition(); Container::fullRefresh(); } diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index f1ed6608..12f262f1 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -826,7 +826,7 @@ void TextBox::setCaret(size_t position) { scrolled(-glm::ceil(offset/static_cast(scrollStep)+0.5f)); } uint lcaret = caret - label->getTextLineOffset(line); - int realoffset = font->calcWidth(input, lcaret)-int(textOffset)+2; + int realoffset = font->calcWidth(input, lcaret)-int(textOffset) - padding.x; if (realoffset-width > 0) { setTextOffset(textOffset + realoffset-width); } else if (realoffset < 0) { diff --git a/src/graphics/ui/gui_util.cpp b/src/graphics/ui/gui_util.cpp index b84dcfd0..04026132 100644 --- a/src/graphics/ui/gui_util.cpp +++ b/src/graphics/ui/gui_util.cpp @@ -34,13 +34,14 @@ void guiutil::alert( auto panel = std::make_shared(glm::vec2(500, 300), glm::vec4(4.0f), 4.0f); panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f)); - auto menu = engine.getGUI()->getMenu(); - runnable on_hidden_final = [on_hidden, menu, &engine]() { - menu->removePage(""); + auto menuPtr = engine.getGUI()->getMenu(); + auto& menu = *menuPtr; + runnable on_hidden_final = [on_hidden, &menu, &engine]() { + menu.removePage(""); if (on_hidden) { on_hidden(); } else { - menu->back(); + menu.back(); } }; @@ -50,21 +51,21 @@ void guiutil::alert( panel->add(label); panel->add(std::make_shared