Merge branch 'MihailRis:main' into main
This commit is contained in:
commit
bec7e59e33
2
.github/workflows/appimage.yml
vendored
2
.github/workflows/appimage.yml
vendored
@ -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
|
||||
|
||||
2
.github/workflows/cmake.yml
vendored
2
.github/workflows/cmake.yml
vendored
@ -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 ../..
|
||||
|
||||
|
||||
215
CHANGELOG.md
215
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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
)
|
||||
```
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
## Создание команд
|
||||
|
||||
Для создания команды консоли используется следующая функция:
|
||||
|
||||
@ -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.
|
||||
|
||||
## Разделы
|
||||
|
||||
|
||||
@ -59,6 +59,17 @@ gui.escape_markup(
|
||||
|
||||
Экранирует разметку в тексте.
|
||||
|
||||
```lua
|
||||
gui.alert(
|
||||
-- сообщение (не переводится автоматически, используйте gui.str(...))
|
||||
message: str,
|
||||
-- функция, вызываемая при закрытии
|
||||
on_ok: function() -> nil
|
||||
)
|
||||
```
|
||||
|
||||
Выводит окно с сообщением. **Не** останавливает выполнение кода.
|
||||
|
||||
```lua
|
||||
gui.confirm(
|
||||
-- сообщение (не переводится автоматически, используйте gui.str(...))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
)
|
||||
```
|
||||
|
||||
@ -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)
|
||||
```
|
||||
|
||||
## Встроенные компоненты
|
||||
|
||||
5
res/content/base/block_materials/snow.json
Normal file
5
res/content/base/block_materials/snow.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"steps-sound": "steps/snow",
|
||||
"place-sound": "blocks/snow_place",
|
||||
"break-sound": "blocks/snow_break"
|
||||
}
|
||||
@ -2,5 +2,6 @@
|
||||
"texture": "lamp",
|
||||
"emission": [15, 14, 13],
|
||||
"shadeless": true,
|
||||
"material": "base:glass",
|
||||
"base:durability": 0.3
|
||||
}
|
||||
|
||||
BIN
res/content/base/sounds/blocks/snow_3.ogg
Normal file
BIN
res/content/base/sounds/blocks/snow_3.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/blocks/snow_break.ogg
Normal file
BIN
res/content/base/sounds/blocks/snow_break.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/blocks/snow_place.ogg
Normal file
BIN
res/content/base/sounds/blocks/snow_place.ogg
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -12,7 +12,7 @@
|
||||
</container>
|
||||
|
||||
<container id="logContainer" pos="0,60"
|
||||
size-func="unpack(vec2.add(gui.get_viewport(), {0,-100}))">
|
||||
size-func="unpack(vec2.add(gui.get_viewport(), {-350,-100}))">
|
||||
<textbox
|
||||
id='log'
|
||||
color='0'
|
||||
@ -20,7 +20,7 @@
|
||||
margin='0'
|
||||
editable='false'
|
||||
multiline='true'
|
||||
size-func="gui.get_viewport()[1],40"
|
||||
size-func="gui.get_viewport()[1]-350,40"
|
||||
gravity="bottom-left"
|
||||
markup="md"
|
||||
></textbox>
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<speakerid_t, std::unique_ptr<Speaker>> speakers;
|
||||
static std::unordered_map<speakerid_t, std::shared_ptr<Stream>> streams;
|
||||
static std::vector<std::unique_ptr<Channel>> 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<PCM> 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();
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
|
||||
@ -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<entityid_t>::max();
|
||||
|
||||
inline constexpr int CHUNK_W = 16;
|
||||
inline constexpr int CHUNK_H = 256;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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<ImageData> load_icon(const fs::path& resdir) {
|
||||
try {
|
||||
auto file = resdir / fs::u8path("textures/misc/icon.png");
|
||||
@ -73,12 +64,23 @@ static std::unique_ptr<ImageData> load_icon(const fs::path& resdir) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Engine::Engine(CoreParameters coreParameters)
|
||||
: params(std::move(coreParameters)),
|
||||
settings(),
|
||||
settingsHandler({settings}),
|
||||
interpreter(std::make_unique<cmd::CommandsInterpreter>()),
|
||||
network(network::Network::create(settings.network)) {
|
||||
Engine::Engine() = default;
|
||||
|
||||
static std::unique_ptr<Engine> engine;
|
||||
|
||||
Engine& Engine::getInstance() {
|
||||
if (!engine) {
|
||||
engine = std::make_unique<Engine>();
|
||||
}
|
||||
return *engine;
|
||||
}
|
||||
|
||||
void Engine::initialize(CoreParameters coreParameters) {
|
||||
params = std::move(coreParameters);
|
||||
settingsHandler = std::make_unique<SettingsHandler>(settings);
|
||||
interpreter = std::make_unique<cmd::CommandsInterpreter>();
|
||||
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<Screen> Engine::getScreen() {
|
||||
}
|
||||
|
||||
SettingsHandler& Engine::getSettingsHandler() {
|
||||
return settingsHandler;
|
||||
return *settingsHandler;
|
||||
}
|
||||
|
||||
network::Network& Engine::getNetwork() {
|
||||
|
||||
@ -58,9 +58,9 @@ using OnWorldOpen = std::function<void(std::unique_ptr<Level>, int64_t)>;
|
||||
class Engine : public util::ObjectsKeeper {
|
||||
CoreParameters params;
|
||||
EngineSettings settings;
|
||||
SettingsHandler settingsHandler;
|
||||
EnginePaths paths;
|
||||
|
||||
std::unique_ptr<SettingsHandler> settingsHandler;
|
||||
std::unique_ptr<Assets> assets;
|
||||
std::shared_ptr<Screen> screen;
|
||||
std::vector<ContentPack> 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();
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ void Panel::cropToContent() {
|
||||
void Panel::fullRefresh() {
|
||||
refresh();
|
||||
cropToContent();
|
||||
reposition();
|
||||
Container::fullRefresh();
|
||||
}
|
||||
|
||||
|
||||
@ -826,7 +826,7 @@ void TextBox::setCaret(size_t position) {
|
||||
scrolled(-glm::ceil(offset/static_cast<double>(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) {
|
||||
|
||||
@ -34,13 +34,14 @@ void guiutil::alert(
|
||||
auto panel = std::make_shared<Panel>(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("<alert>");
|
||||
auto menuPtr = engine.getGUI()->getMenu();
|
||||
auto& menu = *menuPtr;
|
||||
runnable on_hidden_final = [on_hidden, &menu, &engine]() {
|
||||
menu.removePage("<alert>");
|
||||
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<Button>(
|
||||
langs::get(L"Ok"), glm::vec4(10.f),
|
||||
[=](GUI*) {
|
||||
[on_hidden_final](GUI*) {
|
||||
on_hidden_final();
|
||||
}
|
||||
));
|
||||
panel->refresh();
|
||||
panel->keepAlive(Events::keyCallbacks[keycode::ENTER].add([=](){
|
||||
panel->keepAlive(Events::keyCallbacks[keycode::ENTER].add([on_hidden_final](){
|
||||
on_hidden_final();
|
||||
return true;
|
||||
}));
|
||||
panel->keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([=](){
|
||||
panel->keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([on_hidden_final](){
|
||||
on_hidden_final();
|
||||
return true;
|
||||
}));
|
||||
menu->addPage("<alert>", panel, true);
|
||||
menu->setPage("<alert>");
|
||||
menu.addPage("<alert>", panel, true);
|
||||
menu.setPage("<alert>");
|
||||
}
|
||||
|
||||
void guiutil::confirm(
|
||||
|
||||
@ -82,8 +82,8 @@ static onaction create_action(
|
||||
return [callback](GUI*) {callback();};
|
||||
}
|
||||
|
||||
/* Read basic UINode properties */
|
||||
static void _readUINode(
|
||||
/// @brief Read basic UINode properties
|
||||
static void read_uinode(
|
||||
const UiXmlReader& reader, const xml::xmlelement& element, UINode& node
|
||||
) {
|
||||
if (element.has("id")) {
|
||||
@ -177,8 +177,8 @@ static void _readUINode(
|
||||
}
|
||||
}
|
||||
|
||||
static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element, Container& container) {
|
||||
_readUINode(reader, element, container);
|
||||
static void read_container_impl(UiXmlReader& reader, const xml::xmlelement& element, Container& container) {
|
||||
read_uinode(reader, element, container);
|
||||
|
||||
if (element.has("scrollable")) {
|
||||
container.setScrollable(element.attr("scrollable").asBool());
|
||||
@ -197,17 +197,22 @@ static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element,
|
||||
}
|
||||
|
||||
void UiXmlReader::readUINode(UiXmlReader& reader, const xml::xmlelement& element, Container& container) {
|
||||
_readContainer(reader, element, container);
|
||||
read_container_impl(reader, element, container);
|
||||
}
|
||||
|
||||
void UiXmlReader::readUINode(
|
||||
const UiXmlReader& reader, const xml::xmlelement& element, UINode& node
|
||||
) {
|
||||
_readUINode(reader, element, node);
|
||||
read_uinode(reader, element, node);
|
||||
}
|
||||
|
||||
static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Panel& panel, bool subnodes=true) {
|
||||
_readUINode(reader, element, panel);
|
||||
static void read_panel_impl(
|
||||
UiXmlReader& reader,
|
||||
const xml::xmlelement& element,
|
||||
Panel& panel,
|
||||
bool subnodes = true
|
||||
) {
|
||||
read_uinode(reader, element, panel);
|
||||
|
||||
if (element.has("padding")) {
|
||||
glm::vec4 padding = element.attr("padding").asVec4();
|
||||
@ -245,7 +250,9 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane
|
||||
}
|
||||
}
|
||||
|
||||
static std::wstring readAndProcessInnerText(const xml::xmlelement& element, const std::string& context) {
|
||||
static std::wstring parse_inner_text(
|
||||
const xml::xmlelement& element, const std::string& context
|
||||
) {
|
||||
std::wstring text = L"";
|
||||
if (element.size() == 1) {
|
||||
std::string source = element.sub(0).attr("#").getText();
|
||||
@ -265,9 +272,9 @@ static std::wstring readAndProcessInnerText(const xml::xmlelement& element, cons
|
||||
static std::shared_ptr<UINode> readLabel(
|
||||
const UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
std::wstring text = readAndProcessInnerText(element, reader.getContext());
|
||||
std::wstring text = parse_inner_text(element, reader.getContext());
|
||||
auto label = std::make_shared<Label>(text);
|
||||
_readUINode(reader, element, *label);
|
||||
read_uinode(reader, element, *label);
|
||||
if (element.has("valign")) {
|
||||
label->setVerticalAlign(
|
||||
align_from_string(element.attr("valign").getText(), label->getVerticalAlign())
|
||||
@ -298,20 +305,26 @@ static std::shared_ptr<UINode> readLabel(
|
||||
return label;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||
static std::shared_ptr<UINode> read_container(
|
||||
UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
auto container = std::make_shared<Container>(glm::vec2());
|
||||
_readContainer(reader, element, *container);
|
||||
read_container_impl(reader, element, *container);
|
||||
return container;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readPanel(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||
static std::shared_ptr<UINode> read_panel(
|
||||
UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
float interval = element.attr("interval", "2").asFloat();
|
||||
auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval);
|
||||
_readPanel(reader, element, *panel);
|
||||
read_panel_impl(reader, element, *panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||
static std::shared_ptr<UINode> read_button(
|
||||
UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
glm::vec4 padding = element.attr("padding", "10").asVec4();
|
||||
|
||||
std::shared_ptr<Button> button;
|
||||
@ -323,11 +336,11 @@ static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlele
|
||||
} else {
|
||||
button = std::make_shared<Button>(L"", padding, nullptr);
|
||||
}
|
||||
_readPanel(reader, element, *button, false);
|
||||
read_panel_impl(reader, element, *button, false);
|
||||
} else {
|
||||
std::wstring text = readAndProcessInnerText(element, reader.getContext());
|
||||
std::wstring text = parse_inner_text(element, reader.getContext());
|
||||
button = std::make_shared<Button>(text, padding, nullptr);
|
||||
_readPanel(reader, element, *button, true);
|
||||
read_panel_impl(reader, element, *button, true);
|
||||
}
|
||||
if (element.has("text-align")) {
|
||||
button->setTextAlign(align_from_string(
|
||||
@ -337,11 +350,13 @@ static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlele
|
||||
return button;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||
auto text = readAndProcessInnerText(element, reader.getContext());
|
||||
static std::shared_ptr<UINode> read_check_box(
|
||||
UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
auto text = parse_inner_text(element, reader.getContext());
|
||||
bool checked = element.attr("checked", "false").asBool();
|
||||
auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(32), checked);
|
||||
_readPanel(reader, element, *checkbox);
|
||||
read_panel_impl(reader, element, *checkbox);
|
||||
|
||||
if (element.has("consumer")) {
|
||||
checkbox->setConsumer(scripting::create_bool_consumer(
|
||||
@ -361,14 +376,16 @@ static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmle
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||
static std::shared_ptr<UINode> read_text_box(
|
||||
UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
auto placeholder = util::str2wstr_utf8(element.attr("placeholder", "").getText());
|
||||
auto hint = util::str2wstr_utf8(element.attr("hint", "").getText());
|
||||
auto text = readAndProcessInnerText(element, reader.getContext());
|
||||
auto text = parse_inner_text(element, reader.getContext());
|
||||
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
|
||||
textbox->setHint(hint);
|
||||
|
||||
_readContainer(reader, element, *textbox);
|
||||
read_container_impl(reader, element, *textbox);
|
||||
if (element.has("padding")) {
|
||||
glm::vec4 padding = element.attr("padding").asVec4();
|
||||
textbox->setPadding(padding);
|
||||
@ -447,16 +464,16 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
|
||||
return textbox;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readImage(
|
||||
static std::shared_ptr<UINode> read_image(
|
||||
const UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
std::string src = element.attr("src", "").getText();
|
||||
auto image = std::make_shared<Image>(src);
|
||||
_readUINode(reader, element, *image);
|
||||
read_uinode(reader, element, *image);
|
||||
return image;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readCanvas(
|
||||
static std::shared_ptr<UINode> read_canvas(
|
||||
const UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
auto size = glm::uvec2{32, 32};
|
||||
@ -464,11 +481,11 @@ static std::shared_ptr<UINode> readCanvas(
|
||||
size = element.attr("size").asVec2();
|
||||
}
|
||||
auto image = std::make_shared<Canvas>(ImageFormat::rgba8888, size);
|
||||
_readUINode(reader, element, *image);
|
||||
read_uinode(reader, element, *image);
|
||||
return image;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readTrackBar(
|
||||
static std::shared_ptr<UINode> read_track_bar(
|
||||
const UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
const auto& env = reader.getEnvironment();
|
||||
@ -479,7 +496,7 @@ static std::shared_ptr<UINode> readTrackBar(
|
||||
float step = element.attr("step", "1.0").asFloat();
|
||||
int trackWidth = element.attr("track-width", "12").asInt();
|
||||
auto bar = std::make_shared<TrackBar>(minv, maxv, def, step, trackWidth);
|
||||
_readUINode(reader, element, *bar);
|
||||
read_uinode(reader, element, *bar);
|
||||
if (element.has("consumer")) {
|
||||
bar->setConsumer(scripting::create_number_consumer(
|
||||
env, element.attr("consumer").getText(), file));
|
||||
@ -501,7 +518,9 @@ static std::shared_ptr<UINode> readTrackBar(
|
||||
return bar;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readInputBindBox(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||
static std::shared_ptr<UINode> read_input_bind_box(
|
||||
UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
auto bindname = element.attr("binding").getText();
|
||||
auto found = Events::bindings.find(bindname);
|
||||
if (found == Events::bindings.end()) {
|
||||
@ -509,12 +528,12 @@ static std::shared_ptr<UINode> readInputBindBox(UiXmlReader& reader, const xml::
|
||||
}
|
||||
glm::vec4 padding = element.attr("padding", "6").asVec4();
|
||||
auto bindbox = std::make_shared<InputBindBox>(found->second, padding);
|
||||
_readPanel(reader, element, *bindbox);
|
||||
read_panel_impl(reader, element, *bindbox);
|
||||
|
||||
return bindbox;
|
||||
}
|
||||
|
||||
static slotcallback readSlotFunc(
|
||||
static slotcallback read_slot_func(
|
||||
InventoryView* view,
|
||||
const UiXmlReader& reader,
|
||||
const xml::xmlelement& element,
|
||||
@ -542,13 +561,13 @@ static void readSlot(
|
||||
layout.position = element.attr("pos").asVec2();
|
||||
}
|
||||
if (element.has("updatefunc")) {
|
||||
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
|
||||
layout.updateFunc = read_slot_func(view, reader, element, "updatefunc");
|
||||
}
|
||||
if (element.has("sharefunc")) {
|
||||
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
|
||||
layout.shareFunc = read_slot_func(view, reader, element, "sharefunc");
|
||||
}
|
||||
if (element.has("onrightclick")) {
|
||||
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
|
||||
layout.rightClick = read_slot_func(view, reader, element, "onrightclick");
|
||||
}
|
||||
layout.taking = taking;
|
||||
layout.placing = placing;
|
||||
@ -588,13 +607,13 @@ static void readSlotsGrid(
|
||||
layout.position = element.attr("pos").asVec2();
|
||||
}
|
||||
if (element.has("updatefunc")) {
|
||||
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
|
||||
layout.updateFunc = read_slot_func(view, reader, element, "updatefunc");
|
||||
}
|
||||
if (element.has("sharefunc")) {
|
||||
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
|
||||
layout.shareFunc = read_slot_func(view, reader, element, "sharefunc");
|
||||
}
|
||||
if (element.has("onrightclick")) {
|
||||
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
|
||||
layout.rightClick = read_slot_func(view, reader, element, "onrightclick");
|
||||
}
|
||||
layout.padding = padding;
|
||||
layout.taking = taking;
|
||||
@ -618,7 +637,9 @@ static void readSlotsGrid(
|
||||
}
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readInventory(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||
static std::shared_ptr<UINode> read_inventory(
|
||||
UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
auto view = std::make_shared<InventoryView>();
|
||||
view->setColor(glm::vec4(0.122f, 0.122f, 0.122f, 0.878f)); // todo: fixme
|
||||
reader.addIgnore("slot");
|
||||
@ -633,31 +654,34 @@ static std::shared_ptr<UINode> readInventory(UiXmlReader& reader, const xml::xml
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readPageBox(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||
static std::shared_ptr<UINode> read_page_box(
|
||||
UiXmlReader& reader, const xml::xmlelement& element
|
||||
) {
|
||||
auto menu = std::make_shared<Menu>();
|
||||
// FIXME
|
||||
menu->setPageLoader(scripting::engine->getGUI()->getMenu()->getPageLoader());
|
||||
_readContainer(reader, element, *menu);
|
||||
menu->setPageLoader(
|
||||
Engine::getInstance().getGUI()->getMenu()->getPageLoader()
|
||||
);
|
||||
read_container_impl(reader, element, *menu);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
UiXmlReader::UiXmlReader(const scriptenv& env) : env(env) {
|
||||
contextStack.emplace("");
|
||||
add("image", readImage);
|
||||
add("canvas", readCanvas);
|
||||
add("image", read_image);
|
||||
add("canvas", read_canvas);
|
||||
add("label", readLabel);
|
||||
add("panel", readPanel);
|
||||
add("button", readButton);
|
||||
add("textbox", readTextBox);
|
||||
add("pagebox", readPageBox);
|
||||
add("checkbox", readCheckBox);
|
||||
add("trackbar", readTrackBar);
|
||||
add("container", readContainer);
|
||||
add("bindbox", readInputBindBox);
|
||||
add("inventory", readInventory);
|
||||
add("panel", read_panel);
|
||||
add("button", read_button);
|
||||
add("textbox", read_text_box);
|
||||
add("pagebox", read_page_box);
|
||||
add("checkbox", read_check_box);
|
||||
add("trackbar", read_track_bar);
|
||||
add("container", read_container);
|
||||
add("bindbox", read_input_bind_box);
|
||||
add("inventory", read_inventory);
|
||||
}
|
||||
|
||||
void UiXmlReader::add(const std::string& tag, uinode_reader reader) {
|
||||
|
||||
@ -46,9 +46,10 @@ static glm::vec4 parse_color(const std::basic_string_view<CharT>& color_code) {
|
||||
|
||||
template <typename CharT>
|
||||
static inline void apply_color(
|
||||
const std::basic_string_view<CharT>& color_code, FontStylesScheme& styles
|
||||
const std::basic_string_view<CharT>& color_code,
|
||||
FontStylesScheme& styles,
|
||||
FontStyle& style
|
||||
) {
|
||||
FontStyle style = styles.palette.back();
|
||||
style.color = parse_color(color_code);
|
||||
styles.palette.push_back(style);
|
||||
}
|
||||
@ -116,7 +117,7 @@ Result<CharT> process_markdown(
|
||||
}
|
||||
} else if (first == '[' && pos + 9 <= source.size() && source[pos + 1] == '#' && source[pos + 8] == ']') {
|
||||
std::basic_string_view<CharT> color_code = source.substr(pos + 1, 8);
|
||||
apply_color(color_code, styles);
|
||||
apply_color(color_code, styles, style);
|
||||
if (!eraseMarkdown) {
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
emit_md(source[pos + i], styles, ss);
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "typedefs.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
inline constexpr int LIGHTMAP_DATA_LEN = CHUNK_VOL/2;
|
||||
|
||||
@ -17,6 +18,10 @@ public:
|
||||
|
||||
void set(const light_t* map);
|
||||
|
||||
void clear() {
|
||||
std::memset(map, 0, sizeof(map));
|
||||
}
|
||||
|
||||
inline unsigned short get(int x, int y, int z) const {
|
||||
return (map[y*CHUNK_D*CHUNK_W+z*CHUNK_W+x]);
|
||||
}
|
||||
|
||||
@ -43,6 +43,11 @@ static int l_execute(lua::State* L) {
|
||||
}
|
||||
}
|
||||
|
||||
static int l_get(lua::State* L) {
|
||||
auto name = lua::require_string(L, 1);
|
||||
return lua::pushvalue(L, (*engine->getCommandsInterpreter())[name]);
|
||||
}
|
||||
|
||||
static int l_set(lua::State* L) {
|
||||
auto name = lua::require_string(L, 1);
|
||||
auto value = lua::tovalue(L, 2);
|
||||
@ -119,6 +124,7 @@ static int l_get_command_info(lua::State* L) {
|
||||
const luaL_Reg consolelib[] = {
|
||||
{"add_command", lua::wrap<l_add_command>},
|
||||
{"execute", lua::wrap<l_execute>},
|
||||
{"get", lua::wrap<l_get>},
|
||||
{"set", lua::wrap<l_set>},
|
||||
{"get_commands_list", lua::wrap<l_get_commands_list>},
|
||||
{"get_command_info", lua::wrap<l_get_command_info>},
|
||||
|
||||
@ -148,7 +148,13 @@ static int l_reconfig_packs(lua::State* L) {
|
||||
lua::pop(L);
|
||||
}
|
||||
auto engineController = engine->getController();
|
||||
engineController->reconfigPacks(controller, addPacks, remPacks);
|
||||
try {
|
||||
engineController->reconfigPacks(controller, addPacks, remPacks);
|
||||
} catch (const contentpack_error& err) {
|
||||
throw std::runtime_error(
|
||||
std::string(err.what()) + " [" + err.getPackId() + " ]"
|
||||
);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -231,7 +231,13 @@ static int l_pack_assemble(lua::State* L) {
|
||||
}
|
||||
auto manager = engine->createPacksManager(worldFolder);
|
||||
manager.scan();
|
||||
ids = std::move(manager.assemble(ids));
|
||||
try {
|
||||
ids = std::move(manager.assemble(ids));
|
||||
} catch (const contentpack_error& err) {
|
||||
throw std::runtime_error(
|
||||
std::string(err.what()) + " [" + err.getPackId() + "]"
|
||||
);
|
||||
}
|
||||
|
||||
lua::createtable(L, ids.size(), 0);
|
||||
for (size_t i = 0; i < ids.size(); i++) {
|
||||
|
||||
@ -229,7 +229,9 @@ static int l_set_entity(lua::State* L) {
|
||||
if (player == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (auto entity = get_entity(L, 2)) {
|
||||
if (lua::isnumber(L, 2)) {
|
||||
player->setEntity(lua::tointeger(L, 2));
|
||||
} else if (auto entity = get_entity(L, 2)) {
|
||||
player->setEntity(entity->getUID());
|
||||
}
|
||||
return 0;
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "assets/AssetsLoader.hpp"
|
||||
#include "coders/json.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "files/WorldFiles.hpp"
|
||||
#include "files/engine_paths.hpp"
|
||||
#include "files/files.hpp"
|
||||
#include "lighting/Lighting.hpp"
|
||||
@ -54,7 +55,7 @@ static int l_get_list(lua::State* L) {
|
||||
|
||||
auto assets = engine->getAssets();
|
||||
std::string icon = "world#" + name + ".icon";
|
||||
if (!AssetsLoader::loadExternalTexture(
|
||||
if (!engine->isHeadless() && !AssetsLoader::loadExternalTexture(
|
||||
assets,
|
||||
icon,
|
||||
{worlds[i] / fs::path("icon.png"),
|
||||
@ -125,11 +126,21 @@ static int l_get_chunk_data(lua::State* L) {
|
||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||
const auto& chunk = level->chunks->getChunk(x, z);
|
||||
|
||||
std::vector<ubyte> chunkData;
|
||||
if (chunk == nullptr) {
|
||||
lua::pushnil(L);
|
||||
return 0;
|
||||
auto& regions = level->getWorld()->wfile->getRegions();
|
||||
auto voxelData = regions.getVoxels(x, z);
|
||||
if (voxelData == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
static util::Buffer<ubyte> rleBuffer(CHUNK_DATA_LEN * 2);
|
||||
auto metadata = regions.getBlocksData(x, z);
|
||||
chunkData =
|
||||
compressed_chunks::encode(voxelData.get(), metadata, rleBuffer);
|
||||
} else {
|
||||
chunkData = compressed_chunks::encode(*chunk);
|
||||
}
|
||||
auto chunkData = compressed_chunks::encode(*chunk);
|
||||
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
|
||||
}
|
||||
|
||||
@ -137,12 +148,12 @@ static void integrate_chunk_client(Chunk& chunk) {
|
||||
int x = chunk.x;
|
||||
int z = chunk.z;
|
||||
auto chunksController = controller->getChunksController();
|
||||
|
||||
Lighting& lighting = *chunksController->lighting;
|
||||
chunk.flags.loadedLights = false;
|
||||
chunk.flags.lighted = false;
|
||||
|
||||
chunk.lightmap.clear();
|
||||
Lighting::prebuildSkyLight(chunk, *indices);
|
||||
lighting.onChunkLoaded(x, z, true);
|
||||
|
||||
for (int lz = -1; lz <= 1; lz++) {
|
||||
for (int lx = -1; lx <= 1; lx++) {
|
||||
@ -151,16 +162,20 @@ static void integrate_chunk_client(Chunk& chunk) {
|
||||
}
|
||||
if (auto other = level->chunks->getChunk(x + lx, z + lz)) {
|
||||
other->flags.modified = true;
|
||||
lighting.onChunkLoaded(x - 1, z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int l_set_chunk_data(lua::State* L) {
|
||||
if (level == nullptr) {
|
||||
throw std::runtime_error("no open world");
|
||||
}
|
||||
|
||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||
auto buffer = lua::require_bytearray(L, 3);
|
||||
|
||||
auto chunk = level->chunks->getChunk(x, z);
|
||||
if (chunk == nullptr) {
|
||||
return lua::pushboolean(L, false);
|
||||
@ -175,6 +190,21 @@ static int l_set_chunk_data(lua::State* L) {
|
||||
return lua::pushboolean(L, true);
|
||||
}
|
||||
|
||||
static int l_save_chunk_data(lua::State* L) {
|
||||
if (level == nullptr) {
|
||||
throw std::runtime_error("no open world");
|
||||
}
|
||||
|
||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||
auto buffer = lua::require_bytearray(L, 3);
|
||||
|
||||
compressed_chunks::save(
|
||||
x, z, std::move(buffer), level->getWorld()->wfile->getRegions()
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_count_chunks(lua::State* L) {
|
||||
if (level == nullptr) {
|
||||
return 0;
|
||||
@ -197,6 +227,7 @@ const luaL_Reg worldlib[] = {
|
||||
{"exists", lua::wrap<l_exists>},
|
||||
{"get_chunk_data", lua::wrap<l_get_chunk_data>},
|
||||
{"set_chunk_data", lua::wrap<l_set_chunk_data>},
|
||||
{"save_chunk_data", lua::wrap<l_save_chunk_data>},
|
||||
{"count_chunks", lua::wrap<l_count_chunks>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -22,6 +22,9 @@ namespace lua {
|
||||
|
||||
inline void pop(lua::State* L, int n = 1) {
|
||||
#ifndef NDEBUG
|
||||
if (n < 0) {
|
||||
abort();
|
||||
}
|
||||
if (lua_gettop(L) < n) {
|
||||
abort();
|
||||
}
|
||||
|
||||
@ -86,7 +86,10 @@ public:
|
||||
}
|
||||
|
||||
void update() override {
|
||||
if (lua::getglobal(L, "__vc_resume_coroutine")) {
|
||||
if (id == 0) {
|
||||
return;
|
||||
}
|
||||
if (lua::requireglobal(L, "__vc_resume_coroutine")) {
|
||||
lua::pushinteger(L, id);
|
||||
if (lua::call(L, 1)) {
|
||||
alive = lua::toboolean(L, -1);
|
||||
@ -102,10 +105,10 @@ public:
|
||||
}
|
||||
|
||||
void terminate() override {
|
||||
if (lua::getglobal(L, "__vc_stop_coroutine")) {
|
||||
lua::pushinteger(L, id);
|
||||
lua::pop(L, lua::call(L, 1));
|
||||
}
|
||||
lua::requireglobal(L, "__vc_stop_coroutine");
|
||||
lua::pushinteger(L, id);
|
||||
lua::pop(L, lua::call(L, 1));
|
||||
id = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -614,6 +617,10 @@ static void process_entity_callback(
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::pushenv(L, *env);
|
||||
if (lua::hasfield(L, "__disabled")) {
|
||||
lua::pop(L);
|
||||
return;
|
||||
}
|
||||
if (lua::getfield(L, name)) {
|
||||
if (args) {
|
||||
lua::call_nothrow(L, args(L), 0);
|
||||
|
||||
14
src/main.cpp
14
src/main.cpp
@ -3,11 +3,16 @@
|
||||
#include "util/command_line.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
static debug::Logger logger("main");
|
||||
|
||||
static void sigterm_handler(int signum) {
|
||||
Engine::getInstance().quit();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
CoreParameters coreParameters;
|
||||
try {
|
||||
@ -18,11 +23,15 @@ int main(int argc, char** argv) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::signal(SIGTERM, sigterm_handler);
|
||||
|
||||
debug::Logger::init(coreParameters.userFolder.string()+"/latest.log");
|
||||
platform::configure_encoding();
|
||||
|
||||
auto& engine = Engine::getInstance();
|
||||
try {
|
||||
Engine(std::move(coreParameters)).run();
|
||||
engine.initialize(std::move(coreParameters));
|
||||
engine.run();
|
||||
} catch (const initialize_error& err) {
|
||||
logger.error() << "could not to initialize engine\n" << err.what();
|
||||
}
|
||||
@ -33,5 +42,6 @@ int main(int argc, char** argv) {
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
Engine::terminate();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ Player::Player(
|
||||
Player::~Player() = default;
|
||||
|
||||
void Player::updateEntity() {
|
||||
if (eid == 0) {
|
||||
if (eid == ENTITY_AUTO) {
|
||||
auto& def = level.content.entities.require("base:player");
|
||||
eid = level.entities->spawn(def, getPosition());
|
||||
if (auto entity = level.entities->get(eid)) {
|
||||
@ -73,10 +73,10 @@ void Player::updateEntity() {
|
||||
if (auto entity = level.entities->get(eid)) {
|
||||
entity->setPlayer(id);
|
||||
}
|
||||
} else if (chunks->getChunkByVoxel(position)) {
|
||||
} else if (chunks->getChunkByVoxel(position) && eid != ENTITY_NONE) {
|
||||
logger.error() << "player entity despawned or deleted; "
|
||||
"will be respawned";
|
||||
eid = 0;
|
||||
eid = ENTITY_AUTO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ class Player : public Serializable {
|
||||
bool infiniteItems = true;
|
||||
bool instantDestruction = true;
|
||||
bool loadingChunks = true;
|
||||
entityid_t eid;
|
||||
entityid_t eid = ENTITY_AUTO;
|
||||
entityid_t selectedEid = 0;
|
||||
|
||||
glm::vec3 rotation {};
|
||||
|
||||
@ -37,7 +37,7 @@ Player* Players::create(int64_t id) {
|
||||
glm::vec3(0, DEF_PLAYER_Y, 0),
|
||||
DEF_PLAYER_SPEED,
|
||||
level.inventories->create(DEF_PLAYER_INVENTORY_SIZE),
|
||||
0
|
||||
ENTITY_AUTO
|
||||
);
|
||||
auto player = playerPtr.get();
|
||||
add(std::move(playerPtr));
|
||||
@ -92,7 +92,7 @@ void Players::deserialize(const dv::value& src) {
|
||||
glm::vec3(0, DEF_PLAYER_Y, 0),
|
||||
DEF_PLAYER_SPEED,
|
||||
level.inventories->create(DEF_PLAYER_INVENTORY_SIZE),
|
||||
0
|
||||
ENTITY_AUTO
|
||||
);
|
||||
auto player = playerPtr.get();
|
||||
player->deserialize(playerMap);
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include "maths/voxmaths.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
#include <stdexcept>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
@ -2,27 +2,24 @@
|
||||
|
||||
#include "coders/rle.hpp"
|
||||
#include "coders/gzip.hpp"
|
||||
#include "coders/byte_utils.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
|
||||
#include "files/WorldFiles.hpp"
|
||||
|
||||
inline constexpr int HAS_VOXELS = 0x1;
|
||||
inline constexpr int HAS_METADATA = 0x2;
|
||||
|
||||
std::vector<ubyte> compressed_chunks::encode(const Chunk& chunk) {
|
||||
auto data = chunk.encode();
|
||||
|
||||
/// world.get_chunk_data is only available in the main Lua state
|
||||
static util::Buffer<ubyte> rleBuffer;
|
||||
if (rleBuffer.size() < CHUNK_DATA_LEN * 2) {
|
||||
rleBuffer = util::Buffer<ubyte>(CHUNK_DATA_LEN * 2);
|
||||
}
|
||||
std::vector<ubyte> compressed_chunks::encode(
|
||||
const ubyte* data,
|
||||
const BlocksMetadata& metadata,
|
||||
util::Buffer<ubyte>& rleBuffer
|
||||
) {
|
||||
size_t rleCompressedSize =
|
||||
extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data());
|
||||
extrle::encode16(data, CHUNK_DATA_LEN, rleBuffer.data());
|
||||
|
||||
const auto gzipCompressedData = gzip::compress(
|
||||
rleBuffer.data(), rleCompressedSize
|
||||
);
|
||||
auto metadataBytes = chunk.blocksMetadata.serialize();
|
||||
auto metadataBytes = metadata.serialize();
|
||||
|
||||
ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size());
|
||||
builder.put(HAS_VOXELS | HAS_METADATA); // flags
|
||||
@ -34,6 +31,23 @@ std::vector<ubyte> compressed_chunks::encode(const Chunk& chunk) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
std::vector<ubyte> compressed_chunks::encode(const Chunk& chunk) {
|
||||
auto data = chunk.encode();
|
||||
|
||||
/// world.get_chunk_data is only available in the main Lua state
|
||||
static util::Buffer<ubyte> rleBuffer(CHUNK_DATA_LEN * 2);
|
||||
return encode(data.get(), chunk.blocksMetadata, rleBuffer);
|
||||
}
|
||||
|
||||
static void read_voxel_data(ByteReader& reader, util::Buffer<ubyte>& dst) {
|
||||
size_t gzipCompressedSize = reader.getInt32();
|
||||
|
||||
auto rleData = gzip::decompress(reader.pointer(), gzipCompressedSize);
|
||||
reader.skip(gzipCompressedSize);
|
||||
|
||||
extrle::decode16(rleData.data(), rleData.size(), dst.data());
|
||||
}
|
||||
|
||||
void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) {
|
||||
ByteReader reader(src, size);
|
||||
|
||||
@ -41,14 +55,9 @@ void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) {
|
||||
reader.skip(1); // reserved byte
|
||||
|
||||
if (flags & HAS_VOXELS) {
|
||||
size_t gzipCompressedSize = reader.getInt32();
|
||||
|
||||
auto rleData = gzip::decompress(reader.pointer(), gzipCompressedSize);
|
||||
reader.skip(gzipCompressedSize);
|
||||
|
||||
/// world.get_chunk_data is only available in the main Lua state
|
||||
static util::Buffer<ubyte> voxelData (CHUNK_DATA_LEN);
|
||||
extrle::decode16(rleData.data(), rleData.size(), voxelData.data());
|
||||
read_voxel_data(reader, voxelData);
|
||||
chunk.decode(voxelData.data());
|
||||
chunk.updateHeights();
|
||||
}
|
||||
@ -59,3 +68,30 @@ void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) {
|
||||
}
|
||||
chunk.setModifiedAndUnsaved();
|
||||
}
|
||||
|
||||
void compressed_chunks::save(
|
||||
int x, int z, std::vector<ubyte> bytes, WorldRegions& regions
|
||||
) {
|
||||
ByteReader reader(bytes.data(), bytes.size());
|
||||
|
||||
ubyte flags = reader.get();
|
||||
reader.skip(1); // reserved byte
|
||||
if (flags & HAS_VOXELS) {
|
||||
util::Buffer<ubyte> voxelData (CHUNK_DATA_LEN);
|
||||
read_voxel_data(reader, voxelData);
|
||||
regions.put(
|
||||
x, z, REGION_LAYER_VOXELS, voxelData.release(), CHUNK_DATA_LEN
|
||||
);
|
||||
}
|
||||
if (flags & HAS_METADATA) {
|
||||
size_t metadataSize = reader.getInt32();
|
||||
regions.put(
|
||||
x,
|
||||
z,
|
||||
REGION_LAYER_BLOCKS_DATA,
|
||||
util::Buffer<ubyte>(reader.pointer(), metadataSize).release(),
|
||||
metadataSize
|
||||
);
|
||||
reader.skip(metadataSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "Chunk.hpp"
|
||||
#include "coders/byte_utils.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Chunk;
|
||||
class WorldRegions;
|
||||
|
||||
namespace compressed_chunks {
|
||||
std::vector<ubyte> encode(
|
||||
const ubyte* voxelData,
|
||||
const BlocksMetadata& metadata,
|
||||
util::Buffer<ubyte>& rleBuffer
|
||||
);
|
||||
std::vector<ubyte> encode(const Chunk& chunk);
|
||||
void decode(Chunk& chunk, const ubyte* src, size_t size);
|
||||
void save(int x, int z, std::vector<ubyte> bytes, WorldRegions& regions);
|
||||
}
|
||||
|
||||
@ -133,11 +133,12 @@ static inline const Biome* choose_biome(
|
||||
score += glm::abs((params[i] - biome.parameters[i].value) /
|
||||
biome.parameters[i].weight);
|
||||
}
|
||||
if (score < chosenScore) {
|
||||
if (score < chosenScore || std::isinf(chosenScore)) {
|
||||
chosenScore = score;
|
||||
chosenBiome = &biome;
|
||||
}
|
||||
}
|
||||
assert(chosenBiome != nullptr);
|
||||
return chosenBiome;
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,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()
|
||||
|
||||
target_include_directories(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user