Merge branch 'MihailRis:main' into main

This commit is contained in:
Xertis 2025-01-27 16:35:11 +03:00 committed by GitHub
commit bec7e59e33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 639 additions and 285 deletions

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

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 ../..

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)

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)

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
```

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:

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

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(

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

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

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
)
```

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

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.
## Создание команд
Для создания команды консоли используется следующая функция:

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.
## Разделы

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(...))

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

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
)
```

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)
```
## Встроенные компоненты

View File

@ -0,0 +1,5 @@
{
"steps-sound": "steps/snow",
"place-sound": "blocks/snow_place",
"break-sound": "blocks/snow_break"
}

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.

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>

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)

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)

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)

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

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

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
}

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

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();
}

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

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;

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;
}

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() {

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();

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();

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);

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();
}

View File

@ -53,6 +53,7 @@ void Panel::cropToContent() {
void Panel::fullRefresh() {
refresh();
cropToContent();
reposition();
Container::fullRefresh();
}

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) {

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(

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) {

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);

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]);
}

View File

@ -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>},

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;
}

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++) {

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;

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}
};

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();
}

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);

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;
}

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;
}
}

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 {};

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);

View File

@ -14,6 +14,7 @@
#include "maths/voxmaths.hpp"
#include <set>
#include <algorithm>
#include <stdint.h>
#include <stdexcept>
#include <glm/glm.hpp>

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);
}
}

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);
}

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;
}

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(