Merge branch 'MihailRis:main' into main

This commit is contained in:
Xertis
2025-01-27 16:35:11 +03:00
committed by GitHub
69 changed files with 639 additions and 285 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 ../..
+136 -79
View File
@@ -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)
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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
```
+7
View File
@@ -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 -3
View File
@@ -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
+12 -1
View File
@@ -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(
+9
View File
@@ -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
+1 -1
View File
@@ -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
+10 -1
View File
@@ -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
)
```
+3
View File
@@ -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
+7
View File
@@ -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 -3
View File
@@ -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.
## Разделы
+11
View File
@@ -59,6 +59,17 @@ gui.escape_markup(
Экранирует разметку в тексте.
```lua
gui.alert(
-- сообщение (не переводится автоматически, используйте gui.str(...))
message: str,
-- функция, вызываемая при закрытии
on_ok: function() -> nil
)
```
Выводит окно с сообщением. **Не** останавливает выполнение кода.
```lua
gui.confirm(
-- сообщение (не переводится автоматически, используйте gui.str(...))
+9
View File
@@ -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
+10 -1
View File
@@ -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
)
```
+3
View File
@@ -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)
```
## Встроенные компоненты
@@ -0,0 +1,5 @@
{
"steps-sound": "steps/snow",
"place-sound": "blocks/snow_place",
"break-sound": "blocks/snow_break"
}
+1
View File
@@ -2,5 +2,6 @@
"texture": "lamp",
"emission": [15, 14, 13],
"shadeless": true,
"material": "base:glass",
"base:durability": 0.3
}
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.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -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>
+13 -7
View File
@@ -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)
+6 -11
View File
@@ -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)
+2 -2
View File
@@ -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)
+7 -7
View File
@@ -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
+1 -1
View File
@@ -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
+21 -2
View File
@@ -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
}
+22 -7
View File
@@ -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
+24 -5
View File
@@ -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();
}
+2 -1
View File
@@ -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
+1
View 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;
+3
View File
@@ -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;
}
+26 -25
View File
@@ -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() {
+8 -2
View File
@@ -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();
+3 -1
View File
@@ -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();
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -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();
}
+1
View File
@@ -53,6 +53,7 @@ void Panel::cropToContent() {
void Panel::fullRefresh() {
refresh();
cropToContent();
reposition();
Container::fullRefresh();
}
+1 -1
View File
@@ -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) {
+10 -9
View File
@@ -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(
+81 -57
View File
@@ -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) {
+4 -3
View File
@@ -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);
+5
View File
@@ -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>},
+7 -1
View File
@@ -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;
}
+7 -1
View File
@@ -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++) {
+3 -1
View File
@@ -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;
+38 -7
View File
@@ -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}
};
+3
View File
@@ -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();
}
+12 -5
View File
@@ -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);
+12 -2
View File
@@ -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;
}
+3 -3
View File
@@ -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;
}
}
+1 -1
View File
@@ -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 {};
+2 -2
View File
@@ -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);
+1
View File
@@ -14,6 +14,7 @@
#include "maths/voxmaths.hpp"
#include <set>
#include <algorithm>
#include <stdint.h>
#include <stdexcept>
#include <glm/glm.hpp>
+54 -18
View File
@@ -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);
}
}
+9 -1
View File
@@ -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);
}
+2 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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(