Merge branch 'MihailRis:main' into main

This commit is contained in:
Xertis 2024-12-09 19:27:45 +03:00 committed by GitHub
commit ee147ec7bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
93 changed files with 2332 additions and 912 deletions

View File

@ -1,138 +1,96 @@
# 0.24 - 2024.11.07 # 0.25 - 2024.12.01
[Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/tree/release-0.24/doc/en/main-page.md) for 0.24 [Documentation](https://github.com/MihailRis/VoxelEngine-Cpp/tree/release-0.25/doc/en/main-page.md) for 0.25
Table of contents: Table of contents:
- [Added](#added) - [Added](#added)
- [Functions](#functions) - [Functions](#functions)
- [Changes](#changes)
- [Fixes](#fixes) - [Fixes](#fixes)
## Added ## Added
- particles - 3dtext
- VEC3 models support - blockwraps
- handhold item display - network (http requests and sockets)
- rules
- events:
- on_block_broken (documented)
- on_block_placed (documented)
- on_block_interact
- libraries: - libraries:
- gfx.particles - base64
- utf8 - gfx.text3d
- rules - gfx.blockwraps
- bindings: - network
- player.destroy - events:
- player.fast_interaction - on_replaced
- water overlay - on_block_replaced
- block models from OBJ or VEC3 - on_player_tick
- bicubic heightmaps interpolation method - structures 'lowering' property
- unicode escapes support - add 'hint' property to textbox
- fragments placements - add 'taking' and 'placing' properties to slot and slotsgrid
- console commands: - add 'scroll-step' property to container
- time.daycycle - add 'line-numbers' and 'text-color' to textbox
- fragment.place - modules:
- rule.list - base:util
- rule.set - uinode property 'id'
- text field 'subconsumer' - block.materials table
- shader uniforms: - block.properties table
- u_lightDir to main shader - item.properties table
- u_dayTime to skybox shader - add version to world info table
- block properties: - add 'sizeSpread' particles property
- overlay-texture - add user properties
- model-name
- item properties:
- model-name
- 'Open content folder' buttons
- 'Background framerate limit' setting
### Functions ### Functions
- core.open_folder - player.is_infinite_items
- world.get_generator - player.set_infinite_items
- world.is_open - player.is_instant_destruction
- item.placing_block - player.set_instant_destruction
- item.model_name - player.get_name
- item.emission - player.set_name
- entities.get_hitbox - hud.open
- utf8.tobytes - base64.encode
- utf8.tostring - base64.decode
- utf8.length - utf8.escape
- utf8.codepoint - string.escape
- utf8.encode - textbox:lineAt
- utf8.sub - textbox:linePos
- utf8.upper - network.get
- utf8.lower - network.get_binary
- file.read_combined_object - network.tcp_connect
- fragment:place - network.tcp_open
- rules.create - network.get_total_upload
- rules.listen - network.get_total_download
- rules.unlisten - gfx.text3d.show
- rules.get - gfx.text3d.hide
- rules.set - gfx.text3d.get_text
- rules.reset - gfx.text3d.set_text
- input.set_enabled - gfx.text3d.get_pos
- hud._is_content_access - gfx.text3d.set_pos
- hud._set_content_access - gfx.text3d.get_axis_x
- hud._set_debug_cheats - gfx.text3d.set_axis_x
- gfx.particles.emit - gfx.text3d.get_axis_y
- gfx.particles.stop - gfx.text3d.set_axis_y
- gfx.particles.get_origin - gfx.text3d.set_rotation
- gfx.particles.set_origin - gfx.text3d.update_settings
- assets.load_texture
Documented:
- file.read_combined_list
- file.list
- file.list_all_res
- input.is_active
- table.copy
- table.count_pairs
- table.random
- table.has
- table.index
- table.remove_value
- table.tostring
- string.explode
- string.split
- string.pattern_safe
- string.formatted_time
- string.replace
- string.trim
- string.trim_left
- string.trim_right
- string.starts_with
- string.ends_with
- math.clamp
- math.rand
- is_array
- parse_path
- timeit
- sleep
## Changes
- major skybox optimization
- chunks-renderer optimization
- libspng replaced with libpng on Windows
- console commands:
- blocks.fill
- fragment.save
- added 'def' to core.get_setting_info tables
- water texture
## Fixes ## Fixes
- [fix fatal error on editing texbox not having any consumer](https://github.com/MihailRis/VoxelEngine-Cpp/commit/22fa082fc6299ffa3196d62c67e01b849c35b8eb) - [fix translucent blocks render](https://github.com/MihailRis/VoxelEngine-Cpp/pull/370)
- [fix commands boolean type support](https://github.com/MihailRis/VoxelEngine-Cpp/commit/a50cb109c8e3ca0f7a591bf126f07aee36c962e6) - [fix blocks selection with semi-transparent blocks](https://github.com/MihailRis/VoxelEngine-Cpp/commit/171cbb48d099032d7e78c51a46c374104f96f0d1)
- [fix potential null dereferences on incorrect block.* functions use](https://github.com/MihailRis/VoxelEngine-Cpp/commit/961773c9f9745c15eb8d697c1538ac8e21f24da3) - [fix: commands repository not reset before world open](https://github.com/MihailRis/VoxelEngine-Cpp/commit/1a00a91b604399f3108aa995422d371e573e650b)
- [fix: draw-group not copied](https://github.com/MihailRis/VoxelEngine-Cpp/commit/dc8bad2af67e70b0b2346f516028e5795f597737) - [mip-mapping related fixes](https://github.com/MihailRis/VoxelEngine-Cpp/commit/d9277e1b31714632bd7f5f601b8362a9e7cb8819)
- [fix: generator-providing pack may be removed](https://github.com/MihailRis/VoxelEngine-Cpp/commit/6f2f365278eb1866c773890471b7269a5ef45305) - [fix disabled slots display](https://github.com/MihailRis/VoxelEngine-Cpp/commit/e8ee3e04b1398a3ada8445591267525304410571)
- [fix colision check on block place](https://github.com/MihailRis/VoxelEngine-Cpp/commit/726ee8ad703bc57530b881450b8839aaec6b97c9) - [fix attack](https://github.com/MihailRis/VoxelEngine-Cpp/commit/bc17abc8b3ee7ff9027f7e3c375ca0330bb8e7bc)
- [fix collision detection bug](https://github.com/MihailRis/VoxelEngine-Cpp/commit/7fcc34ba4cf14097dfda26054b028c5e8771d26c) - [fix: commands repository not reset before world open](https://github.com/MihailRis/VoxelEngine-Cpp/commit/1a00a91b604399f3108aa995422d371e573e650b)
- [fix: blocks lighting bug fix](https://github.com/MihailRis/VoxelEngine-Cpp/commit/9d3e872f88de2648f8c0f2e4611b30f5ce8999cf) - [fix stdlib.lua](https://github.com/MihailRis/VoxelEngine-Cpp/commit/6ec33ab98c78523eaececf40f113f2323d25a33a)
- [fix: inaccurate framerate limit on Windows](https://github.com/MihailRis/VoxelEngine-Cpp/commit/3f531bbf98da5ad751dce1220c5c5fdf35f86c92) - [fix file.write_bytes](https://github.com/MihailRis/VoxelEngine-Cpp/commit/0fec17a8b69ac81255b77022f3af5addf8fcc8f8)
- [fix block.get_hitbox again](https://github.com/MihailRis/VoxelEngine-Cpp/commit/edad594101e5808ccf14e0edefedbe87cb8f983b) - [fix World::nextInventoryId](https://github.com/MihailRis/VoxelEngine-Cpp/commit/371fdaedcef2c163edd226160f388068b2bf5e83)
- [fix string.replace](https://github.com/MihailRis/VoxelEngine-Cpp/commit/44fd5416a9a110a12f8b3f2d369e5638055b306e) - [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)

View File

@ -3,7 +3,7 @@
## Latest release ## Latest release
- [Download](https://github.com/MihailRis/VoxelEngine-Cpp/releases/latest) | [Скачать](https://github.com/MihailRis/VoxelEngine-Cpp/releases/latest) - [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.24/doc/en/main-page.md) | [Документация](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.24/doc/ru/main-page.md) - [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)
## Build project in Linux ## Build project in Linux
@ -62,6 +62,12 @@ If you use Wayland
sudo pacman -S glfw-wayland glew glm libpng libvorbis openal luajit libcurl sudo pacman -S glfw-wayland glew glm libpng libvorbis openal luajit libcurl
``` ```
And you need entt. In yay you can use
```sh
yay -S entt
```
### Build engine with CMake ### Build engine with CMake
```sh ```sh

View File

@ -1,8 +1,8 @@
# Documentation # Documentation
Documentation for the engine of in-development version 0.25. Documentation for in-development version 0.26.
[Documentation for stable release 0.24.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.24/doc/en/main-page.md) [Documentation for stable release 0.25.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/en/main-page.md)
## Sections ## Sections
@ -14,9 +14,10 @@ Documentation for the engine of in-development version 0.25.
- [Content-packs](content-packs.md) - [Content-packs](content-packs.md)
- [Engine usage recommendations](engine-use-recommendations.md) - [Engine usage recommendations](engine-use-recommendations.md)
- [Item properties](item-properties.md) - [Item properties](item-properties.md)
- [Particles](particles.md)
- [Resources (resources.json)](resources.md) - [Resources (resources.json)](resources.md)
- [Rigging](rigging.md) - [Rigging](rigging.md)
- [Scripting](scripting.md) - [Scripting](scripting.md)
- [Text styles](text-styles.md)
- [World generator engine](world-generator.md) - [World generator engine](world-generator.md)
- [XML UI building](xml-ui-layouts.md) - [XML UI building](xml-ui-layouts.md)
- [Particles](particles.md)

View File

@ -39,3 +39,25 @@ gui.get_locales_info() -> table of tables {
``` ```
Returns information about all loaded locales (res/texts/\*). Returns information about all loaded locales (res/texts/\*).
```lua
gui.clear_markup(
-- markup language ("md" - Markdown)
language: str,
-- text with markup
text: str
) -> str
```
Removes markup from text.
```lua
gui.escape_markup(
-- markup language ("md" - Markdown)
language: str,
-- text with markup
text: str
) -> str
```
Escapes markup in text.

View File

@ -39,7 +39,7 @@ The Socket class has the following methods:
```lua ```lua
-- Sends a byte array -- Sends a byte array
socket:send(table|ByteArray) socket:send(table|ByteArray|str)
-- Reads the received data -- Reads the received data
socket:recv( socket:recv(
@ -59,6 +59,9 @@ socket:is_alive() --> bool
-- Checks if the connection is present (using socket:send(...) is available). -- Checks if the connection is present (using socket:send(...) is available).
socket:is_connected() --> bool socket:is_connected() --> bool
-- Returns the address and port of the connection.
socket:get_address() --> str, int
``` ```
```lua ```lua
@ -80,6 +83,9 @@ server:close()
-- Checks if the TCP server exists and is open. -- Checks if the TCP server exists and is open.
server:is_open() --> bool server:is_open() --> bool
-- Returns the server port.
server:get_port() --> int
``` ```
## Analytics ## Analytics

View File

@ -108,6 +108,12 @@ function on_block_placed(blockid, x, y, z, playerid)
Called on block placed by player Called on block placed by player
```lua
function on_block_replaced(blockid, x, y, z, playerid)
```
Called on block replaced with other by player
```lua ```lua
function on_block_broken(blockid, x, y, z, playerid) function on_block_broken(blockid, x, y, z, playerid)
``` ```

View File

@ -32,7 +32,7 @@ document["worlds-panel"]:clear()
Properties that apply to all elements: Properties that apply to all elements:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ------------- | ------ | ---- | ----- | ------------------------------------------- | | ------------- | ------ | ---- | ----- | ------------------------------------------- |
| id | string | yes | *no* | element id | | id | string | yes | *no* | element id |
| pos | vec2 | yes | yes | element position inside a container | | pos | vec2 | yes | yes | element position inside a container |
@ -61,7 +61,7 @@ Common element methods:
Common methods for containers (elements: container, panel, button, pagebox): Common methods for containers (elements: container, panel, button, pagebox):
| Method | Description | | Method | Description |
| ------------------------------- | ------------------------------------------------------------------ | | ------------------------------- | -------------------------------------------------------------------------------------------- |
| clear() | clears content | | clear() | clears content |
| add(xml) | adds an element, creating it using xml code. Example: `container:add("<image src='test'/>")` | | add(xml) | adds an element, creating it using xml code. Example: `container:add("<image src='test'/>")` |
| setInterval(interval, callback) | assigns a function to be executed repeatedly at an interval specified in milliseconds | | setInterval(interval, callback) | assigns a function to be executed repeatedly at an interval specified in milliseconds |
@ -70,7 +70,7 @@ Common methods for containers (elements: container, panel, button, pagebox):
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----------- | ------ | ---- | ----- | ------------------------------------------------------------------------------------ | | ----------- | ------ | ---- | ----- | ------------------------------------------------------------------------------------ |
| text | string | yes | yes | entered text or placeholder | | text | string | yes | yes | entered text or placeholder |
| placeholder | string | yes | yes | placeholder (used if nothing has been entered) | | placeholder | string | yes | yes | placeholder (used if nothing has been entered) |
@ -82,6 +82,8 @@ Properties:
| textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") | | textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") |
| valid | bool | yes | no | is the entered text correct | | valid | bool | yes | no | is the entered text correct |
| textColor | vec4 | yes | yes | text color | | textColor | vec4 | yes | yes | text color |
| syntax | string | yes | yes | syntax highlighting ("lua" - Lua) |
| markup | string | yes | yes | text markup language ("md" - Markdown) |
Methods: Methods:
@ -95,7 +97,7 @@ Methods:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ---------- | ----- | ---- | ----- | --------------------- | | ---------- | ----- | ---- | ----- | --------------------- |
| value | float | yes | yes | current value | | value | float | yes | yes | current value |
| min | float | yes | yes | minimum value | | min | float | yes | yes | minimum value |
@ -108,7 +110,7 @@ Properties:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ | | ----- | ------ | ---- | ----- | ------------ |
| page | string | yes | yes | current page | | page | string | yes | yes | current page |
@ -123,7 +125,7 @@ Methods:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ------- | ---- | ---- | ----- | ----------- | | ------- | ---- | ---- | ----- | ----------- |
| checked | bool | yes | yes | mark status | | checked | bool | yes | yes | mark status |
@ -131,7 +133,7 @@ Properties:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ | | ----- | ------ | ---- | ----- | ------------ |
| text | string | yes | yes | button text | | text | string | yes | yes | button text |
@ -139,15 +141,16 @@ Properties:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ----------- | | ------ | ------ | ---- | ----- | -------------------------------------- |
| text | string | yes | yes | label text | | text | string | yes | yes | label text |
| markup | string | yes | yes | text markup language ("md" - Markdown) |
## Image ## Image
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ | | ----- | ------ | ---- | ----- | ------------ |
| src | string | yes | yes | texture name | | src | string | yes | yes | texture name |
@ -155,6 +158,6 @@ Properties:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| --------- | ---- | ---- | ----- | ------------------------------------------------- | | --------- | ---- | ---- | ----- | ------------------------------------------------- |
| inventory | int | yes | yes | id of the inventory to which the element is bound | | inventory | int | yes | yes | id of the inventory to which the element is bound |

21
doc/en/text-styles.md Normal file
View File

@ -0,0 +1,21 @@
# Text styles
A proprietary Markdown dialect is used to mark up text styles.
Formatting works on UI elements: label and textbox, if `markup="md"` is explicitly specified.
## Styles
| Style | Example | Output |
| ------------- | ------------------------ | -------------------------- |
| Bold | `**Bold font**` | **Bold font** |
| Italic | `*Text in italics*` | *Text in italics* |
| Underline | `__Underlined text__` | <ins>Underlined text</ins> |
| Strikethrough | `~~Strikethrough text~~` | ~~Strikethrough text~~ |
Styles can be combined. Example:
```md
***__Message__*** using *~~combed~~ combined* styles__~~.~~__
```
Output:
***<ins>Message</ins>*** using *~~combed~~ combined* styles<ins>~~.~~</ins>

View File

@ -87,6 +87,7 @@ Inner text is a button text.
- `autoresize` - automatic change of element size (default - false). Does not affect font size. - `autoresize` - automatic change of element size (default - false). Does not affect font size.
- `multiline` - allows display of multiline text. - `multiline` - allows display of multiline text.
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true"). - `text-wrap` - allows automatic text wrapping (works only with multiline: "true").
- `markup` - text markup language ("md" - Markdown).
## *image* ## *image*
@ -112,6 +113,8 @@ Inner text - initially entered text
- `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct. - `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct.
- `onup` - lua function called when the up arrow is pressed. - `onup` - lua function called when the up arrow is pressed.
- `ondown` - lua function called when the down arrow is pressed. - `ondown` - lua function called when the down arrow is pressed.
- `syntax` - syntax highlighting ("lua" - Lua).
- `markup` - text markup language ("md" - Markdown).
## *trackbar* ## *trackbar*

View File

@ -1,8 +1,8 @@
# Документация # Документация
Документация движка разрабатываемой версии 0.25. Документация разрабатываемой версии 0.26.
[Документация стабильной версии 0.24.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.24/doc/ru/main-page.md) [Документация стабильной версии 0.25.x.](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.25/doc/ru/main-page.md)
## Разделы ## Разделы
@ -19,4 +19,5 @@
- [Свойства блоков](block-properties.md) - [Свойства блоков](block-properties.md)
- [Свойства предметов](item-properties.md) - [Свойства предметов](item-properties.md)
- [Скриптинг](scripting.md) - [Скриптинг](scripting.md)
- [Стили текста](text-styles.md)
- [Частицы](particles.md) - [Частицы](particles.md)

View File

@ -36,3 +36,25 @@ gui.get_locales_info() -> таблица таблиц где
``` ```
Возвращает информацию о всех загруженных локалях (res/texts/\*). Возвращает информацию о всех загруженных локалях (res/texts/\*).
```lua
gui.clear_markup(
-- язык разметки ("md" - Markdown)
language: str,
-- текст с разметкой
text: str
) -> str
```
Удаляет разметку из текста.
```lua
gui.escape_markup(
-- язык разметки ("md" - Markdown)
language: str,
-- текст с разметкой
text: str
) -> str
```
Экранирует разметку в тексте.

View File

@ -39,7 +39,7 @@ network.tcp_connect(
```lua ```lua
-- Отправляет массив байт -- Отправляет массив байт
socket:send(table|ByteArray) socket:send(table|ByteArray|str)
-- Читает полученные данные -- Читает полученные данные
socket:recv( socket:recv(
@ -59,6 +59,9 @@ socket:is_alive() --> bool
-- Проверяет наличие соединения (доступно использование socket:send(...)). -- Проверяет наличие соединения (доступно использование socket:send(...)).
socket:is_connected() --> bool socket:is_connected() --> bool
-- Возвращает адрес и порт соединения.
socket:get_address() --> str, int
``` ```
```lua ```lua
@ -80,6 +83,9 @@ server:close()
-- Проверяет, существует и открыт ли TCP сервер. -- Проверяет, существует и открыт ли TCP сервер.
server:is_open() --> bool server:is_open() --> bool
-- Возвращает порт сервера.
server:get_port() --> int
``` ```
## Аналитика ## Аналитика

View File

@ -108,6 +108,12 @@ function on_block_placed(blockid, x, y, z, playerid)
Вызывается после установки блока игроком Вызывается после установки блока игроком
```lua
function on_block_replaced(blockid, x, y, z, playerid)
```
Вызывается после замены блока игроком
```lua ```lua
function on_block_broken(blockid, x, y, z, playerid) function on_block_broken(blockid, x, y, z, playerid)
``` ```

View File

@ -10,6 +10,12 @@ table.copy(t: table) -> table
Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной. Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной.
```lua
table.deep_copy(t: table) -> table
```
Функция глубокого копирования создает полную копию исходной таблицы, включая все её вложенные таблицы.
```lua ```lua
table.count_pairs(t: table) -> integer table.count_pairs(t: table) -> integer
``` ```

View File

@ -1,93 +1,98 @@
# Модуль core:bit_converter # Модуль core:bit_converter
## Доступные порядки байтов
**LE (Little-Endian)**
**BE (Big-Endian)**
По умолчанию используется **LE**
## Конвертация значений в байты и обратно ## Конвертация значений в байты и обратно
```lua ```lua
function bit_converter.string_to_bytes(string: str) -> table function bit_converter.string_to_bytes(str: string) -> table
``` ```
Конвертирует строку в байты Конвертирует строку в байты
```lua ```lua
function bit_converter.bool_to_byte(boolean: bool) -> integer function bit_converter.bool_to_byte(bool: boolean) -> integer
``` ```
Конвертирует логический булев в байт Конвертирует логический булев в байт
```lua ```lua
function bit_converter.single_to_bytes(number: single) -> table function bit_converter.float32_to_bytes(float: number, [опционально] order: string) -> table
``` ```
Конвертирует плавающее значение одинарной точности в байты Конвертирует плавающее значение одинарной точности в байты
```lua ```lua
function bit_converter.double_to_bytes(number: double) -> table function bit_converter.float64_to_bytes(float: number, [опционально] order: string) -> table
``` ```
Конвертирует плавающее значение двойной точности в байты Конвертирует плавающее значение двойной точности в байты
```lua ```lua
function bit_converter.uint16_to_bytes(integer: int) -> table function bit_converter.uint16_to_bytes(int: integer, [опционально] order: string) -> table
``` ```
Конвертирует беззнаковое 2-х байтовое целое число в байты Конвертирует беззнаковое 2-х байтовое целое число в байты
```lua ```lua
function bit_converter.uint32_to_bytes(integer: int) -> table function bit_converter.uint32_to_bytes(int: integer, [опционально] order: string) -> table
``` ```
Конвертирует беззнаковое 4-х байтовое целое число в байты Конвертирует беззнаковое 4-х байтовое целое число в байты
```lua ```lua
function bit_converter.int16_to_bytes(integer: int) -> table function bit_converter.sint16_to_bytes(int: integer, [опционально] order: string) -> table
``` ```
Конвертирует знаковое 2-х байтовое целое число в байты Конвертирует знаковое 2-х байтовое целое число в байты
```lua ```lua
function bit_converter.int32_to_bytes(integer: int) -> table function bit_converter.sint32_to_bytes(int: integer, [опционально] order: string) -> table
``` ```
Конвертирует знаковое 4-х байтовое целое число в байты Конвертирует знаковое 4-х байтовое целое число в байты
```lua ```lua
function bit_converter.int64_to_bytes(integer: int) -> table function bit_converter.int64_to_bytes(int: integer, [опционально] order: string) -> table
``` ```
Конвертирует знаковое 8-и байтовое целое число в байты Конвертирует знаковое 8-и байтовое целое число в байты
```lua ```lua
function bit_converter.bytes_to_string(table: bytes) -> string function bit_converter.bytes_to_string(bytes: table) -> string
``` ```
Конвертирует массив байтов в строку Конвертирует массив байтов в строку
```lua ```lua
function bit_converter.byte_to_bool(integer: byte) -> boolean function bit_converter.byte_to_bool(byte: integer) -> boolean
``` ```
Конвертирует байт в логическое булевое значение Конвертирует байт в логическое булевое значение
```lua ```lua
function bit_converter.bytes_to_single(table: bytes) -> number№ function bit_converter.bytes_to_float32(bytes: table|Bytearray, [опционально] order: string) -> number
``` ```
Конвертирует массив байтов в плавающее число одинарной точности Конвертирует массив байтов в плавающее число одинарной точности
```lua ```lua
function bit_converter.bytes_to_double(table: bytes) -> number function bit_converter.bytes_to_float64(bytes: table|Bytearray, [опционально] order: string) -> number
``` ```
Конвертирует массив байтов в плавающее число двойной точности Конвертирует массив байтов в плавающее число двойной точности
```lua ```lua
function bit_converter.bytes_to_uint16(table: bytes) -> integer function bit_converter.bytes_to_uint16(bytes: table|Bytearray, [опционально] order: string) -> integer
``` ```
Конвертирует массив байтов в 2-х байтовое беззнаковое число Конвертирует массив байтов в 2-х байтовое беззнаковое число
```lua ```lua
function bit_converter.bytes_to_uint32(table: bytes) -> integer function bit_converter.bytes_to_uint32(bytes: table|Bytearray, [опционально] order: string) -> integer
``` ```
Конвертирует массив байтов в 4-х байтовое беззнаковое число Конвертирует массив байтов в 4-х байтовое беззнаковое число
```lua ```lua
function bit_converter.bytes_to_int16(table: bytes) -> integer function bit_converter.bytes_to_sint16(bytes: table|Bytearray, [опционально] order: string) -> integer
``` ```
Конвертирует массив байтов в 2-х байтовое знаковое число Конвертирует массив байтов в 2-х байтовое знаковое число
```lua ```lua
function bit_converter.bytes_to_int32(table: bytes) -> integer function bit_converter.bytes_to_sint32(bytes: table|Bytearray, [опционально] order: string) -> integer
``` ```
Конвертирует массив байтов в 4-х байтовое знаковое число Конвертирует массив байтов в 4-х байтовое знаковое число
```lua ```lua
function bit_converter.bytes_to_int64(table: bytes) -> integer function bit_converter.bytes_to_int64(bytes: table|Bytearray, [опционально] order: string) -> integer
``` ```
Конвертирует массив байтов в 8-х байтовое знаковое число Конвертирует массив байтов в 8-х байтовое знаковое число

View File

@ -4,67 +4,78 @@
### Хранит в себе массив байтов и позволяет легко получать или добавлять разные значения ### Хранит в себе массив байтов и позволяет легко получать или добавлять разные значения
```lua ```lua
function data_buffer(bytes) function data_buffer(
[опционально] bytes: table,
[опционально] order: string,
[опционально] useBytearray: boolean
)
``` ```
Создаёт новый экземпляр data_buffer (параметр bytes необязательный) Создаёт новый экземпляр **data_buffer**.
Если **useBytearray** равен **true**, то байты буффера будут хранится ввиде **Bytearray**. Это может снизить производительность, но также и уменьшить размер буффера в памяти
```lua ```lua
function data_buffer:put_byte(integer: byte) function data_buffer:set_order(order: string)
```
Задаёт порядок байтов для чисел.
Должен равняться одному из перечисленных в [**bit_converter**](core_bit_converter.md)
```lua
function data_buffer:put_byte(byte: integer)
``` ```
Записывает байт в буффер Записывает байт в буффер
```lua ```lua
function data_buffer:put_bytes(table: bytes) function data_buffer:put_bytes(bytes: table|Bytearray)
``` ```
Записывает байты в буффер Записывает байты в буффер
```lua ```lua
function data_buffer:put_string(string: str) function data_buffer:put_string(str: string)
``` ```
Конвертирует строку в байты и записывает их в буффер Конвертирует строку в байты и записывает их в буффер
```lua ```lua
function data_buffer:put_bool(boolean: bool) function data_buffer:put_bool(bool: boolean)
``` ```
Конвертирует булевое значение в байт и записывает его в буффер Конвертирует булевое значение в байт и записывает его в буффер
```lua ```lua
function data_buffer:put_single(number: single) function data_buffer:put_float32(float: number)
``` ```
Конвертирует плавающее число одинарной точности в байты и записывает их в буффер Конвертирует плавающее число одинарной точности в байты и записывает их в буффер
```lua ```lua
function data_buffer:put_double(number: double) function data_buffer:put_float64(float: number)
``` ```
Конвертирует плавающее число двойной точности в байты и записывает их в буффер Конвертирует плавающее число двойной точности в байты и записывает их в буффер
```lua ```lua
function data_buffer:put_uint16(integer: int) function data_buffer:put_uint16(int: integer)
``` ```
Конвертирует беззнаковое 2-х байтовое число в байты и записывает их в буффер Конвертирует беззнаковое 2-х байтовое число в байты и записывает их в буффер
```lua ```lua
function data_buffer:put_uint32(integer: int) function data_buffer:put_uint32(int: integer)
``` ```
Конвертирует беззнаковое 4-х байтовое число в байты и записывает их в буффер Конвертирует беззнаковое 4-х байтовое число в байты и записывает их в буффер
```lua ```lua
function data_buffer:put_int16(integer: int) function data_buffer:put_sint16(int: integer)
``` ```
Конвертирует знаковое 2-х байтовое число в байты и записывает их в буффер Конвертирует знаковое 2-х байтовое число в байты и записывает их в буффер
```lua ```lua
function data_buffer:put_int32(integer: int) function data_buffer:put_sint32(int: integer)
``` ```
Конвертирует знаковое 4-х байтовое число в байты и записывает их в буффер Конвертирует знаковое 4-х байтовое число в байты и записывает их в буффер
```lua ```lua
function data_buffer:put_int64(integer: int) function data_buffer:put_int64(int: integer)
``` ```
Конвертирует знаковое 8-и байтовое число в байты и записывает их в буффер Конвертирует знаковое 8-и байтовое число в байты и записывает их в буффер
```lua ```lua
function data_buffer:put_number(number: num) function data_buffer:put_number(num: number)
``` ```
Конвертирует любое число в байты и записывает их в буффер; Конвертирует любое число в байты и записывает их в буффер;
@ -73,10 +84,10 @@ function data_buffer:put_number(number: num)
zero = 0 zero = 0
uint16 = 1 uint16 = 1
uint32 = 2 uint32 = 2
int16 = 3
int32 = 4
int64 = 5 int64 = 5
double = 6 float64 = 6
sint16 = 7
sint32 = 8
``` ```
```lua ```lua
@ -85,9 +96,9 @@ function data_buffer:get_byte() -> integer
Возвращает следующий байт из буффера Возвращает следующий байт из буффера
```lua ```lua
function data_buffer:get_bytes(n) -> table function data_buffer:get_bytes(n) -> table|Bytearray
``` ```
Возвращает n следующих байтов, если n равен nil или не указан, то возвращается массив всех байтов Возвращает **n** следующих байтов, если **n** равен **nil** или не указан, то возвращается массив всех байтов
```lua ```lua
function data_buffer:get_string() -> string function data_buffer:get_string() -> string
@ -100,12 +111,12 @@ function data_buffer:get_bool() -> boolean
Читает следующий логический булев из буффера Читает следующий логический булев из буффера
```lua ```lua
function data_buffer:get_single() -> number function data_buffer:get_float32() -> number
``` ```
Читает следующее плавающее число одинарной точности из буффера Читает следующее плавающее число одинарной точности из буффера
```lua ```lua
function data_buffer:get_double() -> number function data_buffer:get_float64() -> number
``` ```
Читает следующее плавающее число двойной точности из буффера Читает следующее плавающее число двойной точности из буффера
@ -120,12 +131,12 @@ function data_buffer:get_uint32() -> integer
Читает следующее 4-х байтовое беззнаковое целое число из буффера Читает следующее 4-х байтовое беззнаковое целое число из буффера
```lua ```lua
function data_buffer:get_int16() -> integer function data_buffer:get_sint16() -> integer
``` ```
Читает следующее 2-х байтовое знаковое целое число из буффера Читает следующее 2-х байтовое знаковое целое число из буффера
```lua ```lua
function data_buffer:get_int32() -> integer function data_buffer:get_sint32() -> integer
``` ```
Читает следующее 4-х байтовое знаковое целое число из буффера Читает следующее 4-х байтовое знаковое целое число из буффера

View File

@ -82,6 +82,8 @@ document["worlds-panel"]:clear()
| textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") | | textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") |
| valid | bool | да | нет | является ли введенный текст корректным | | valid | bool | да | нет | является ли введенный текст корректным |
| textColor | vec4 | да | да | цвет текста | | textColor | vec4 | да | да | цвет текста |
| syntax | string | да | да | подсветка синтаксиса ("lua" - Lua) |
| markup | string | да | да | язык разметки текста ("md" - Markdown) |
Методы: Методы:
@ -140,8 +142,9 @@ document["worlds-panel"]:clear()
Свойства: Свойства:
| Название | Тип | Чтение | Запись | Описание | | Название | Тип | Чтение | Запись | Описание |
| -------- | ------ | ------ | ------ | ----------- | | -------- | ------ | ------ | ------ | -------------------------------------- |
| text | string | да | да | текст метки | | text | string | да | да | текст метки |
| markup | string | да | да | язык разметки текста ("md" - Markdown) |
## Изображение (image) ## Изображение (image)

21
doc/ru/text-styles.md Normal file
View File

@ -0,0 +1,21 @@
# Стили текста
Для разметки стилей текста используется собственный диалект Markdown.
Форматирование работает на UI элементах: label и textbox, если явно указано `markup="md"`.
## Стили
| Стиль | Пример | Вывод |
| ------------ | ------------------------- | ----------------------------- |
| Жирный | `**Жирный шрифт**` | **Жирный шрифт** |
| Курсив | `*Текст курсивом*` | *Текст курсивом* |
| Подчеркнутый | `__Подчеркнутый текст__` | <ins>Подчеркнутый текст</ins> |
| Зачеркнутый | `~~Зачеркнутый текст~~` | ~~Зачеркнутый текст~~ |
Стили могут объединяться. Пример:
```md
***__Сообщение__***, демонстрирующее *~~обедненные~~ объединенные* стили__~~.~~__
```
Вывод:
***<ins>Сообщение</ins>***, демонстрирующее *~~обедненные~~ объединенные* стили<ins>~~.~~</ins>

View File

@ -89,6 +89,7 @@
- `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта. - `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта.
- `multiline` - разрешает отображение многострочного текста. - `multiline` - разрешает отображение многострочного текста.
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") - `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
- `markup` - язык разметки текста ("md" - Markdown).
## Изображение - *image* ## Изображение - *image*
@ -113,6 +114,8 @@
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен. - `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.
- `onup` - lua функция вызываемая при нажатии стрелки вверх. - `onup` - lua функция вызываемая при нажатии стрелки вверх.
- `ondown` - lua функция вызываемая при нажатии стрелки вниз. - `ondown` - lua функция вызываемая при нажатии стрелки вниз.
- `syntax` - подстветка синтаксиса ("lua" - Lua).
- `markup` - язык разметки текста ("md" - Markdown).
## Ползунок - *trackbar* ## Ползунок - *trackbar*

View File

@ -8,7 +8,7 @@
flake-utils.lib.eachDefaultSystem (system: { flake-utils.lib.eachDefaultSystem (system: {
devShells.default = with nixpkgs.legacyPackages.${system}; mkShell { devShells.default = with nixpkgs.legacyPackages.${system}; mkShell {
nativeBuildInputs = [ cmake pkg-config ]; nativeBuildInputs = [ cmake pkg-config ];
buildInputs = [ glm glfw glew zlib libpng libvorbis openal luajit ]; # libglvnd buildInputs = [ glm glfw glew zlib libpng libvorbis openal luajit curl ]; # libglvnd
packages = [ glfw mesa freeglut entt ]; packages = [ glfw mesa freeglut entt ];
LD_LIBRARY_PATH = "${wayland}/lib:$LD_LIBRARY_PATH"; LD_LIBRARY_PATH = "${wayland}/lib:$LD_LIBRARY_PATH";
}; };

View File

@ -1,6 +1,6 @@
{ {
"id": "base", "id": "base",
"title": "Base", "title": "Base",
"version": "0.25", "version": "0.26",
"description": "basic content package" "description": "basic content package"
} }

View File

@ -22,6 +22,7 @@
multiline='true' multiline='true'
size-func="gui.get_viewport()[1],40" size-func="gui.get_viewport()[1],40"
gravity="bottom-left" gravity="bottom-left"
markup="md"
></textbox> ></textbox>
</container> </container>
<container id="editorContainer" pos="0,60" color="#00000080" <container id="editorContainer" pos="0,60" color="#00000080"
@ -32,10 +33,9 @@
autoresize='true' autoresize='true'
margin='0' margin='0'
padding='5' padding='5'
editable='false'
multiline='true' multiline='true'
line-numbers='true' line-numbers='true'
text-color="#FFFFFFA0" syntax='lua'
size-func="gui.get_viewport()[1]-350,40" size-func="gui.get_viewport()[1]-350,40"
gravity="top-left" gravity="top-left"
text-wrap='false' text-wrap='false'
@ -55,6 +55,7 @@
<textbox id='prompt' <textbox id='prompt'
consumer='submit' consumer='submit'
margin='0' margin='0'
markup="md"
gravity='bottom-left' gravity='bottom-left'
size-func="gui.get_viewport()[1],40" size-func="gui.get_viewport()[1],40"
onup="on_history_up()" onup="on_history_up()"

View File

@ -12,17 +12,57 @@ local MIN_INT32 = -2147483648
local MAX_INT64 = 9223372036854775807 local MAX_INT64 = 9223372036854775807
local MIN_INT64 = -9223372036854775808 local MIN_INT64 = -9223372036854775808
local function intToByte(num) local function maskHighBytes(num)
return bit.band(num, 0xFF) return bit.band(num, 0xFF)
end end
local function reverse(tab) local function reverse(tbl)
for i = 1, math.floor(#tab, 2), 1 do for i=1, math.floor(#tbl / 2) do
tab[i], tab[#tab-i+1] = tab[#tab-i+1], tab[i] local tmp = tbl[i]
tbl[i] = tbl[#tbl - i + 1]
tbl[#tbl - i + 1] = tmp
end end
return tab return tbl
end end
local orders = { "LE", "BE" }
local fromLEConvertors =
{
LE = function(bytes) return bytes end,
BE = function(bytes) return reverse(bytes) end
}
local toLEConvertors =
{
LE = function(bytes) return bytes end,
BE = function(bytes) return reverse(bytes) end
}
bit_converter.default_order = "LE"
local function fromLE(bytes, orderTo)
if orderTo then
bit_converter.validate_order(orderTo)
return fromLEConvertors[orderTo](bytes)
else return bytes end
end
local function toLE(bytes, orderFrom)
if orderFrom then
bit_converter.validate_order(orderFrom)
return toLEConvertors[orderFrom](bytes)
else return bytes end
end
function bit_converter.validate_order(order)
if not bit_converter.is_valid_order(order) then
error("invalid order: "..order)
end
end
function bit_converter.is_valid_order(order) return table.has(orders, order) end
function bit_converter.string_to_bytes(str) function bit_converter.string_to_bytes(str)
local bytes = { } local bytes = { }
@ -83,19 +123,12 @@ local function floatOrDoubleToBytes(val, opt)
bytes[#bytes + 1] = math.floor(sign * 128 + val) % (2 ^ 8) bytes[#bytes + 1] = math.floor(sign * 128 + val) % (2 ^ 8)
val = math.floor((sign * 128 + val) / (2 ^ 8)) val = math.floor((sign * 128 + val) / (2 ^ 8))
if not endianness then
reverse(bytes)
end
return bytes return bytes
end end
local function bytesToFloatOrDouble(bytes, opt) local function bytesToFloatOrDouble(bytes, opt)
local n = (opt == 'd') and 8 or 4 local n = (opt == 'd') and 8 or 4
if not endianness then
reverse(bytes)
end
local sign = 1 local sign = 1
local mantissa = bytes[n - 1] % ((opt == 'd') and 16 or 128) local mantissa = bytes[n - 1] % ((opt == 'd') and 16 or 128)
for i = n - 2, 1, -1 do for i = n - 2, 1, -1 do
@ -117,80 +150,128 @@ end
-- --
function bit_converter.single_to_bytes(float) function bit_converter.float32_to_bytes(float, order)
return floatOrDoubleToBytes(float, 'f') return fromLE(floatOrDoubleToBytes(float, 'f'), order)
end end
function bit_converter.double_to_bytes(double) function bit_converter.float64_to_bytes(float, order)
return floatOrDoubleToBytes(double, 'd') return fromLE(floatOrDoubleToBytes(float, 'd'), order)
end end
function bit_converter.uint32_to_bytes(int) function bit_converter.single_to_bytes(float, order)
on_deprecated_call("bit_converter.float_to_bytes", "bit_converter.float32_to_bytes")
return bit_converter.float32_to_bytes(bytes, order)
end
function bit_converter.double_to_bytes(double, order)
on_deprecated_call("bit_converter.double_to_bytes", "bit_converter.float64_to_bytes")
return bit_converter.float64_to_bytes(bytes, order)
end
local function uint32ToBytes(int, order)
return fromLE({
maskHighBytes(bit.rshift(int, 24)),
maskHighBytes(bit.rshift(int, 16)),
maskHighBytes(bit.rshift(int, 8)),
maskHighBytes(int)
}, order)
end
local function uint16ToBytes(int, order)
return fromLE({
maskHighBytes(bit.rshift(int, 8)),
maskHighBytes(int)
}, order)
end
function bit_converter.uint32_to_bytes(int, order)
if int > MAX_UINT32 or int < MIN_UINT32 then if int > MAX_UINT32 or int < MIN_UINT32 then
error("invalid uint32") error("invalid uint32")
end end
return { return uint32ToBytes(int, order)
intToByte(bit.rshift(int, 24)),
intToByte(bit.rshift(int, 16)),
intToByte(bit.rshift(int, 8)),
intToByte(int)
}
end end
function bit_converter.uint16_to_bytes(int) function bit_converter.uint16_to_bytes(int, order)
if int > MAX_UINT16 or int < MIN_UINT16 then if int > MAX_UINT16 or int < MIN_UINT16 then
error("invalid uint16") error("invalid uint16")
end end
return { return uint16ToBytes(int, order)
intToByte(bit.rshift(int, 8)),
intToByte(int)
}
end end
function bit_converter.int64_to_bytes(int) function bit_converter.int64_to_bytes(int, order)
if int > MAX_INT64 or int < MIN_INT64 then if int > MAX_INT64 or int < MIN_INT64 then
error("invalid int64") error("invalid int64")
end end
return { return fromLE({
intToByte(bit.rshift(int, 56)), maskHighBytes(bit.rshift(int, 56)),
intToByte(bit.rshift(int, 48)), maskHighBytes(bit.rshift(int, 48)),
intToByte(bit.rshift(int, 40)), maskHighBytes(bit.rshift(int, 40)),
intToByte(bit.rshift(int, 32)), maskHighBytes(bit.rshift(int, 32)),
intToByte(bit.rshift(int, 24)), maskHighBytes(bit.rshift(int, 24)),
intToByte(bit.rshift(int, 16)), maskHighBytes(bit.rshift(int, 16)),
intToByte(bit.rshift(int, 8)), maskHighBytes(bit.rshift(int, 8)),
intToByte(int) maskHighBytes(int)
} }, order)
end end
function bit_converter.int32_to_bytes(int) function bit_converter.int32_to_bytes(int, order)
on_deprecated_call("bit_converter.int32_to_bytes", "bit_converter.sint32_to_bytes")
if int > MAX_INT32 or int < MIN_INT32 then if int > MAX_INT32 or int < MIN_INT32 then
error("invalid int32") error("invalid int32")
end end
return bit_converter.uint32_to_bytes(int + MAX_INT32) return uint32ToBytes(int + MAX_INT32, order)
end end
function bit_converter.int16_to_bytes(int) function bit_converter.int16_to_bytes(int, order)
on_deprecated_call("bit_converter.int32_to_bytes", "bit_converter.sint16_to_bytes")
if int > MAX_INT16 or int < MIN_INT16 then if int > MAX_INT16 or int < MIN_INT16 then
error("invalid int16") error("invalid int16")
end end
return bit_converter.uint16_to_bytes(int + MAX_INT16) return uint16ToBytes(int + MAX_INT16, order)
end end
function bit_converter.bytes_to_single(bytes) function bit_converter.sint32_to_bytes(int, order)
return bytesToFloatOrDouble(bytes, 'f') if int > MAX_INT32 or int < MIN_INT32 then
error("invalid sint32")
end
return uint32ToBytes(int + MAX_UINT32 + 1, order)
end end
function bit_converter.bytes_to_double(bytes) function bit_converter.sint16_to_bytes(int, order)
return bytesToFloatOrDouble(bytes, 'd') if int > MAX_INT16 or int < MIN_INT16 then
error("invalid sint16")
end
return uint16ToBytes(int + MAX_UINT16 + 1, order)
end end
function bit_converter.bytes_to_string(bytes) function bit_converter.bytes_to_float32(bytes, order)
return bytesToFloatOrDouble(toLE(bytes, order), 'f')
end
function bit_converter.bytes_to_float64(bytes, order)
return bytesToFloatOrDouble(toLE(bytes, order), 'd')
end
function bit_converter.bytes_to_single(bytes, order)
on_deprecated_call("bit_converter.bytes_to_single", "bit_converter.bytes_to_float32")
return bit_converter.bytes_to_float32(bytes, order)
end
function bit_converter.bytes_to_double(bytes, order)
on_deprecated_call("bit_converter.bytes_to_double", "bit_converter.bytes_to_float64")
return bit_converter.bytes_to_float64(bytes, order)
end
function bit_converter.bytes_to_string(bytes, order)
local len = bit_converter.bytes_to_uint16({ bytes[1], bytes[2] }) local len = bit_converter.bytes_to_uint16({ bytes[1], bytes[2] })
local str = "" local str = ""
@ -206,17 +287,13 @@ function bit_converter.byte_to_bool(byte)
return byte ~= 0 return byte ~= 0
end end
function bit_converter.bytes_to_float(bytes) function bit_converter.bytes_to_uint32(bytes, order)
if #bytes < 8 then
error("eof")
end
error("unsupported operation")
end
function bit_converter.bytes_to_uint32(bytes)
if #bytes < 4 then if #bytes < 4 then
error("eof") error("eof")
end end
bytes = toLE(bytes, order)
return return
bit.bor( bit.bor(
bit.bor( bit.bor(
@ -226,20 +303,26 @@ function bit_converter.bytes_to_uint32(bytes)
bit.lshift(bytes[3], 8)),bytes[4]) bit.lshift(bytes[3], 8)),bytes[4])
end end
function bit_converter.bytes_to_uint16(bytes) function bit_converter.bytes_to_uint16(bytes, order)
if #bytes < 2 then if #bytes < 2 then
error("eof") error("eof")
end end
bytes = toLE(bytes, order)
return return
bit.bor( bit.bor(
bit.lshift(bytes[1], 8), bit.lshift(bytes[1], 8),
bytes[2], 0) bytes[2], 0)
end end
function bit_converter.bytes_to_int64(bytes) function bit_converter.bytes_to_int64(bytes, order)
if #bytes < 8 then if #bytes < 8 then
error("eof") error("eof")
end end
bytes = toLE(bytes, order)
return return
bit.bor( bit.bor(
bit.bor( bit.bor(
@ -257,12 +340,26 @@ function bit_converter.bytes_to_int64(bytes)
bit.lshift(bit.band(bytes[7], 0xFF), 8)),bit.band(bytes[8], 0xFF)) bit.lshift(bit.band(bytes[7], 0xFF), 8)),bit.band(bytes[8], 0xFF))
end end
function bit_converter.bytes_to_int32(bytes) function bit_converter.bytes_to_int32(bytes, order)
return bit_converter.bytes_to_uint32(bytes) - MAX_INT32 on_deprecated_call("bit_converter.bytes_to_int32", "bit_converter.bytes_to_sint32")
return bit_converter.bytes_to_uint32(bytes, order) - MAX_INT32
end end
function bit_converter.bytes_to_int16(bytes) function bit_converter.bytes_to_int16(bytes, order)
return bit_converter.bytes_to_uint16(bytes) - MAX_INT16 on_deprecated_call("bit_converter.bytes_to_int16", "bit_converter.bytes_to_sint16")
return bit_converter.bytes_to_uint16(bytes, order) - MAX_INT16
end
function bit_converter.bytes_to_sint32(bytes, order)
local num = bit_converter.bytes_to_uint32(bytes, order)
return MIN_INT32 * (bit.band(MAX_INT32 + 1, num) ~= 0 and 1 or 0) + bit.band(MAX_INT32, num)
end
function bit_converter.bytes_to_sint16(bytes, order)
local num = bit_converter.bytes_to_uint16(bytes, order)
return MIN_INT16 * (bit.band(MAX_INT16 + 1, num) ~= 0 and 1 or 0) + bit.band(MAX_INT16, num)
end end
return bit_converter return bit_converter

View File

@ -18,22 +18,31 @@ local TYPE_UINT32 = 2
local TYPE_INT16 = 3 local TYPE_INT16 = 3
local TYPE_INT32 = 4 local TYPE_INT32 = 4
local TYPE_INT64 = 5 local TYPE_INT64 = 5
local TYPE_DOUBLE = 6 local TYPE_FLOAT64 = 6
local TYPE_SINT16 = 7
local TYPE_SINT32 = 8
-- Data buffer -- Data buffer
local data_buffer = local data_buffer =
{ {
__call = __call =
function(data_buffer, bytes) function(data_buffer, ...)
return data_buffer:new(bytes) return data_buffer:new(...)
end end
} }
function data_buffer:new(bytes) function data_buffer:new(bytes, order, useBytearray)
bytes = bytes or { }
if order then bit_converter.validate_order(order)
else order = bit_converter.default_order end
local obj = { local obj = {
pos = 1, pos = 1,
bytes = bytes or { } order = order,
useBytearray = useBytearray or false,
bytes = useBytearray and Bytearray(bytes) or bytes
} }
self.__index = self self.__index = self
@ -42,6 +51,13 @@ function data_buffer:new(bytes)
return obj return obj
end end
function data_buffer:set_order(order)
bit_converter.validate_order(order)
self.order = order
self.floatsOrder = order
end
-- Push functions -- Push functions
function data_buffer:put_byte(byte) function data_buffer:put_byte(byte)
@ -49,7 +65,8 @@ function data_buffer:put_byte(byte)
error("invalid byte") error("invalid byte")
end end
self.bytes[self.pos] = byte if self.useBytearray then self.bytes:insert(self.pos, byte)
else table.insert(self.bytes, self.pos, byte) end
self.pos = self.pos + 1 self.pos = self.pos + 1
end end
@ -61,11 +78,21 @@ function data_buffer:put_bytes(bytes)
end end
function data_buffer:put_single(single) function data_buffer:put_single(single)
self:put_bytes(bit_converter.single_to_bytes(single)) on_deprecated_call("data_buffer:put_single", "data_buffer:put_float32")
self:put_bytes(bit_converter.single_to_bytes(single, self.order))
end end
function data_buffer:put_double(double) function data_buffer:put_double(double)
self:put_bytes(bit_converter.double_to_bytes(double)) on_deprecated_call("data_buffer:put_single", "data_buffer:put_float64")
self:put_bytes(bit_converter.double_to_bytes(double, self.order))
end
function data_buffer:put_float32(single)
self:put_bytes(bit_converter.float32_to_bytes(single, self.order))
end
function data_buffer:put_float64(float)
self:put_bytes(bit_converter.float64_to_bytes(float, self.order))
end end
function data_buffer:put_string(str) function data_buffer:put_string(str)
@ -77,23 +104,33 @@ function data_buffer:put_bool(bool)
end end
function data_buffer:put_uint16(uint16) function data_buffer:put_uint16(uint16)
self:put_bytes(bit_converter.uint16_to_bytes(uint16)) self:put_bytes(bit_converter.uint16_to_bytes(uint16, self.order))
end end
function data_buffer:put_uint32(uint32) function data_buffer:put_uint32(uint32)
self:put_bytes(bit_converter.uint32_to_bytes(uint32)) self:put_bytes(bit_converter.uint32_to_bytes(uint32, self.order))
end end
function data_buffer:put_int16(int16) function data_buffer:put_int16(int16)
self:put_bytes(bit_converter.int16_to_bytes(int16)) on_deprecated_call("data_buffer:put_int16", "data_buffer:put_sint16")
self:put_bytes(bit_converter.int16_to_bytes(int16, self.order))
end end
function data_buffer:put_int32(int32) function data_buffer:put_int32(int32)
self:put_bytes(bit_converter.int32_to_bytes(int32)) on_deprecated_call("data_buffer:put_int32", "data_buffer:put_sint32")
self:put_bytes(bit_converter.int32_to_bytes(int32, self.order))
end
function data_buffer:put_sint16(int16)
self:put_bytes(bit_converter.sint16_to_bytes(int16, self.order))
end
function data_buffer:put_sint32(int32)
self:put_bytes(bit_converter.sint32_to_bytes(int32, self.order))
end end
function data_buffer:put_int64(int64) function data_buffer:put_int64(int64)
self:put_bytes(bit_converter.int64_to_bytes(int64)) self:put_bytes(bit_converter.int64_to_bytes(int64, self.order))
end end
function data_buffer:put_number(num) function data_buffer:put_number(num)
@ -101,8 +138,8 @@ function data_buffer:put_number(num)
local type local type
if math.floor(num) ~= num then if math.floor(num) ~= num then
type = TYPE_DOUBLE type = TYPE_FLOAT64
bytes = bit_converter.double_to_bytes(num) bytes = bit_converter.float64_to_bytes(num)
elseif num == 0 then elseif num == 0 then
type = TYPE_ZERO type = TYPE_ZERO
bytes = { } bytes = { }
@ -119,11 +156,11 @@ function data_buffer:put_number(num)
end end
elseif num < 0 then elseif num < 0 then
if num >= MIN_INT16 then if num >= MIN_INT16 then
type = TYPE_INT16 type = TYPE_SINT16
bytes = bit_converter.int16_to_bytes(num) bytes = bit_converter.sint16_to_bytes(num)
elseif num >= MIN_INT32 then elseif num >= MIN_INT32 then
type = TYPE_INT32 type = TYPE_SINT32
bytes = bit_converter.int32_to_bytes(num) bytes = bit_converter.sint32_to_bytes(num)
elseif num >= MIN_INT64 then elseif num >= MIN_INT64 then
type = TYPE_INT64 type = TYPE_INT64
bytes = bit_converter.int64_to_bytes(num) bytes = bit_converter.int64_to_bytes(num)
@ -155,9 +192,13 @@ function data_buffer:get_number()
return self:get_int16() return self:get_int16()
elseif type == TYPE_INT32 then elseif type == TYPE_INT32 then
return self:get_int32() return self:get_int32()
elseif type == TYPE_SINT16 then
return self:get_sint16()
elseif type == TYPE_SINT32 then
return self:get_sint32()
elseif type == TYPE_INT64 then elseif type == TYPE_INT64 then
return self:get_int64() return self:get_int64()
elseif type == TYPE_DOUBLE then elseif type == TYPE_FLOAT64 then
return self:get_double() return self:get_double()
else else
error("unknown lua number type: "..type) error("unknown lua number type: "..type)
@ -165,11 +206,21 @@ function data_buffer:get_number()
end end
function data_buffer:get_single() function data_buffer:get_single()
return bit_converter.bytes_to_single(self:get_bytes(4)) on_deprecated_call("data_buffer:get_single", "data_buffer:get_float32")
return bit_converter.bytes_to_single(self:get_bytes(4), self.order)
end end
function data_buffer:get_double() function data_buffer:get_double()
return bit_converter.bytes_to_double(self:get_bytes(8)) on_deprecated_call("data_buffer:get_double", "data_buffer:get_float64")
return bit_converter.bytes_to_double(self:get_bytes(8), self.order)
end
function data_buffer:get_float32()
return bit_converter.bytes_to_float32(self:get_bytes(4), self.order)
end
function data_buffer:get_float64()
return bit_converter.bytes_to_float64(self:get_bytes(8), self.order)
end end
function data_buffer:get_string() function data_buffer:get_string()
@ -193,23 +244,33 @@ function data_buffer:get_bool()
end end
function data_buffer:get_uint16() function data_buffer:get_uint16()
return bit_converter.bytes_to_uint16(self:get_bytes(2)) return bit_converter.bytes_to_uint16(self:get_bytes(2), self.order)
end end
function data_buffer:get_uint32() function data_buffer:get_uint32()
return bit_converter.bytes_to_uint32(self:get_bytes(4)) return bit_converter.bytes_to_uint32(self:get_bytes(4), self.order)
end end
function data_buffer:get_int16() function data_buffer:get_int16()
return bit_converter.bytes_to_int16(self:get_bytes(2)) on_deprecated_call("data_buffer:get_int16", "data_buffer:get_sint16")
return bit_converter.bytes_to_int16(self:get_bytes(2), self.order)
end end
function data_buffer:get_int32() function data_buffer:get_int32()
return bit_converter.bytes_to_int32(self:get_bytes(4)) on_deprecated_call("data_buffer:get_int32", "data_buffer:get_sint32")
return bit_converter.bytes_to_int32(self:get_bytes(4), self.order)
end
function data_buffer:get_sint16()
return bit_converter.bytes_to_sint16(self:get_bytes(2), self.order)
end
function data_buffer:get_sint32()
return bit_converter.bytes_to_sint32(self:get_bytes(4), self.order)
end end
function data_buffer:get_int64() function data_buffer:get_int64()
return bit_converter.bytes_to_int64(self:get_bytes(8)) return bit_converter.bytes_to_int64(self:get_bytes(8), self.order)
end end
function data_buffer:size() function data_buffer:size()

View File

@ -42,6 +42,7 @@ local Socket = {__index={
close=function(self) return network.__close(self.id) end, close=function(self) return network.__close(self.id) end,
is_alive=function(self) return network.__is_alive(self.id) end, is_alive=function(self) return network.__is_alive(self.id) end,
is_connected=function(self) return network.__is_connected(self.id) end, is_connected=function(self) return network.__is_connected(self.id) end,
get_address=function(self) return network.__get_address(self.id) end,
}} }}
network.tcp_connect = function(address, port, callback) network.tcp_connect = function(address, port, callback)
@ -55,6 +56,7 @@ end
local ServerSocket = {__index={ local ServerSocket = {__index={
close=function(self) return network.__closeserver(self.id) end, close=function(self) return network.__closeserver(self.id) end,
is_open=function(self) return network.__is_serveropen(self.id) end, is_open=function(self) return network.__is_serveropen(self.id) end,
get_port=function(self) return network.__get_serverport(self.id) end,
}} }}
network.tcp_open = function(port, handler) network.tcp_open = function(port, handler)

View File

@ -63,6 +63,20 @@ function table.copy(t)
return copied return copied
end end
function table.deep_copy(t)
local copied = {}
for k, v in pairs(t) do
if type(v) == "table" then
copied[k] = table.deep_copy(v)
else
copied[k] = v
end
end
return copied
end
function table.count_pairs(t) function table.count_pairs(t)
local count = 0 local count = 0

View File

@ -1,4 +1,4 @@
# Общее # Umumiy
Yes=Ha Yes=Ha
No=Yo'q No=Yo'q
Ok=Ок Ok=Ок
@ -10,18 +10,26 @@ Version=Versiya
Creator=Muallif Creator=Muallif
Dependencies=Bog'liqliklar Dependencies=Bog'liqliklar
Description=Tavsif Description=Tavsif
Converting world...=Dunyoni konvertatsiyalash amalga oshirilyapti... Converting world...=Dunyo konvertatsiya qilinmoqda...
Unlimited=Cheksiz Unlimited=Cheksiz
Chat=Suhbat
Console=Konsol
Log=log
Problems=Muammolar
Monitor=Monitoring
Debug=Xatolarni tuzatish (Debug)
File=Fayl
devtools.traceback=Chaqiruvlar steki (songgisidan boshlab)
error.pack-not-found=Paketni topib bo'lmadi error.pack-not-found=Paketni topib bo'lmadi
error.dependency-not-found=Amaldagi qaramliklar topilmadi error.dependency-not-found=Amaldagi qaramliklar topilmadi
pack.remove-confirm=Paketlar bilan taqdim etilgan barcha mazmunni dunyodan olib tashlash (qaytarib bo'lmaydi)? pack.remove-confirm=Paketlar bilan taqdim etilgan barcha contentni dunyodan olib tashlash (qaytarib bo'lmaydi)?
# Подсказки # Maslahatlar
graphics.gamma.tooltip=Yorug'lik yorqinligi egri chizig'i graphics.gamma.tooltip=Yorug'lik yorqinligi egri chizig'i
graphics.backlight.tooltip=To'liq zulmatni oldini oladigan orqa yorug'ligi graphics.backlight.tooltip=To'liq zulmatni oldini oladigan orqa yorug'ligi
# Меню # Menyu
menu.Apply=Qo'llash menu.Apply=Qo'llash
menu.Audio=Tovush menu.Audio=Tovush
menu.Back to Main Menu=Menyuga qaytish menu.Back to Main Menu=Menyuga qaytish
@ -31,60 +39,72 @@ menu.Continue=Davom ettirish
menu.Controls=Boshqaruv menu.Controls=Boshqaruv
menu.Display=Displey menu.Display=Displey
menu.Graphics=Grafika menu.Graphics=Grafika
menu.missing-content=Kontent etishmayapti! menu.missing-content=Kontent mavjud emas!
menu.New World=Yangi Dunyo menu.New World=Yangi Dunyo
menu.Worlds=Dunyo menu.Open worlds folder=Dunyolar papkasini ochish
menu.Open worlds folder=Dunyo papkasini ochish menu.Worlds=Dunyolar
menu.Page not found=Sahifa topilmadi menu.Page not found=Sahifa topilmadi
menu.Quit=Chiqish menu.Quit=Chiqish
menu.Save and Quit to Menu=Saqlash va menyuga chiqish menu.Save and Quit to Menu=Saqlash va Menyuga chiqish
menu.Settings=Sozlamalar menu.Settings=Sozlamalar
menu.Contents Menu=Kontent to'plamlari menyusi menu.Reset settings=Sozlamalarni tiklash
menu.Contents Menu=Kontent paklar menyusi
menu.Open data folder=Ma'lumotlar papkasini ochish
menu.Open content folder=[content] papkasini ochish
world.Seed=Don world.Seed=Don
world.Name=Nom world.Name=Nom
world.World generator=Dunyo yaratuvchi world.World generator=Dunyo generatori
world.generators.default=Oddiy world.generators.default=Standart bo'yicha
world.generators.flat=Yassi world.generators.flat=Tekis
world.Create World=Dunyoni Yaratish world.Create World=Dunyo yaratish
world.convert-request=Indekslarda ozgarishlar mavjud! Dunyoni konvertatsiya qilish kerakmi? world.convert-request=Indekslar bo'yicha o'zgarishlar bor! Dunyoni konvertatsiya qilasizmi?
world.delete-confirm=Dunyoni qaytarib bo'lmaydigan tarzda olib tashlaysizmi? world.upgrade-request=Dunyo formati eskirgan! Dunyoni konvertatsiya qilasizmi?
world.convert-with-loss=Dunyoni yo'qotishlar bilan konvertatsiya qilasizmi?
world.convert-block-layouts=Blok maydonlarida o'zgarishlar bor! Dunyoni konvertatsiya qilasizmi?
world.delete-confirm=Dunyoni doimiy o'chirasizmi?
# Настройки # Sozlamalar
settings.Ambient=Fon settings.Ambient=Fon
settings.Backlight=Yoritish settings.Backlight=Yoritish
settings.Camera Shaking=Kamera Silkinishi settings.Camera Shaking=Kamera titrashi
settings.Camera Inertia=Kamera Inertsiyasi settings.Camera Inertia=Kamera inertsiyasi
settings.Fog Curve=Tuman Egri Chizig'i settings.Camera FOV Effects=Ko'rish maydoni effektlari
settings.FOV=Ko'rish Maydoni settings.Fog Curve=Tuman egri chizig'i
settings.FOV=Ko'rish maydoni
settings.Fullscreen=To'liq ekran settings.Fullscreen=To'liq ekran
settings.Framerate=Kadr tezligi settings.Framerate=Kadrlar chastotasi
settings.Gamma=Gamma settings.Gamma=Gamma
settings.Language=Til settings.Language=Til
settings.Load Distance=Yuklash Masofasi settings.Load Distance=Yuklash masofasi
settings.Load Speed=Yuklash Tezligi settings.Load Speed=Yuklash tezligi
settings.Master Volume=Umumiy Ovoz Balandligi settings.Master Volume=Umumiy ovoz balansi
settings.Mouse Sensitivity=Sichqoncha Sezgirligi settings.Mouse Sensitivity=Sichqoncha sezgirligi
settings.Music=Musiqa settings.Music=Musiqa
settings.Regular Sounds=Oddiy Tovushlar settings.Regular Sounds=Oddiy tovushlar
settings.UI Sounds=Interfeys Tovushlari settings.UI Sounds=Interfeys tovushlari
settings.V-Sync=Vertikal Sinxronizatsiya settings.V-Sync=Vertikal sinxronizatsiya
settings.Key=Tugma
settings.Controls Search Mode=Tayinlangan boshqaruv tugmasi bo'yicha qidirish
settings.Limit Background FPS=Fon kadrlar chastotasini cheklash
# Управление # Boshqaruv
chunks.reload=Chanklarni qayta yuklash chunks.reload=Chanklarni qayta yuklash
devtools.console=Konsol devtools.console=Konsol
movement.forward=Oldinga movement.forward=Oldinga
movement.back=Orqaga movement.back=Orqaga
movement.left=Chapga movement.left=Chapga
movement.right=O'ng tomonda movement.right=O'ngga
movement.jump=Sakrash movement.jump=Sakrash
movement.sprint=Tezlanish movement.sprint=Tez yugurish
movement.crouch=Egilish movement.crouch=Egilish
movement.cheat=Chit movement.cheat=Hiyla (Chit)
hud.inventory=inventar hud.inventory=Inventar
player.pick=Blokni olish player.pick=Blokni olish
player.attack=Hujum Qilish / Sindirish player.attack=Hujum qilish
player.build=Blokni Joylashtiring player.destroy=Sindirish
player.build=Blokni qo'yish
player.fast_interaction=Tezkor o'zaro ta'sir
player.flight=Parvoz player.flight=Parvoz
player.drop=Narsani tashlash player.drop=Narsani tashlash
camera.zoom=Yaqinlashish camera.zoom=Yaqinlashish

View File

@ -214,6 +214,28 @@ std::string_view BasicParser::readUntil(char c) {
return source.substr(start, pos - start); return source.substr(start, pos - start);
} }
std::string_view BasicParser::readUntil(std::string_view s, bool nothrow) {
int start = pos;
size_t found = source.find(s, pos);
if (found == std::string::npos) {
if (nothrow) {
pos = source.size();
return source.substr(start);
}
throw error(util::quote(std::string(s))+" expected");
}
skip(found - pos);
return source.substr(start, pos - start);
}
std::string_view BasicParser::readUntilWhitespace() {
int start = pos;
while (hasNext() && !is_whitespace(source[pos])) {
pos++;
}
return source.substr(start, pos - start);
}
std::string_view BasicParser::readUntilEOL() { std::string_view BasicParser::readUntilEOL() {
int start = pos; int start = pos;
while (hasNext() && source[pos] != '\r' && source[pos] != '\n') { while (hasNext() && source[pos] != '\r' && source[pos] != '\n') {

View File

@ -105,6 +105,8 @@ protected:
parsing_error error(const std::string& message); parsing_error error(const std::string& message);
public: public:
std::string_view readUntil(char c); std::string_view readUntil(char c);
std::string_view readUntil(std::string_view s, bool nothrow);
std::string_view readUntilWhitespace();
std::string_view readUntilEOL(); std::string_view readUntilEOL();
std::string parseName(); std::string parseName();
std::string parseXmlName(); std::string parseXmlName();

View File

@ -11,15 +11,17 @@
using namespace json; using namespace json;
class Parser : BasicParser { namespace {
class Parser : BasicParser {
dv::value parseList(); dv::value parseList();
dv::value parseObject(); dv::value parseObject();
dv::value parseValue(); dv::value parseValue();
public: public:
Parser(std::string_view filename, std::string_view source); Parser(std::string_view filename, std::string_view source);
dv::value parse(); dv::value parse();
}; };
}
inline void newline( inline void newline(
std::stringstream& ss, bool nice, uint indent, const std::string& indentstr std::stringstream& ss, bool nice, uint indent, const std::string& indentstr

188
src/coders/lua_parsing.cpp Normal file
View File

@ -0,0 +1,188 @@
#include "lua_parsing.hpp"
#include <set>
#include "commons.hpp"
using namespace lua;
using namespace devtools;
static std::set<std::string_view> keywords {
"and", "break", "do", "else", "elseif", "end", "false", "for", "function",
"if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true",
"until", "while"
};
bool lua::is_lua_keyword(std::string_view view) {
return keywords.find(view) != keywords.end();
}
inline bool is_lua_identifier_start(int c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
}
inline bool is_lua_identifier_part(int c) {
return is_lua_identifier_start(c) || is_digit(c);
}
inline bool is_lua_operator_start(int c) {
return c == '=' || c == '~' || c == '+' || c == '-' || c == '/' || c == '*'
|| c == '%' || c == '^' || c == '#' || c == '<' || c == '>' || c == ':'
|| c == '.';
}
class Tokenizer : BasicParser {
std::vector<Token> tokens;
public:
Tokenizer(std::string_view file, std::string_view source)
: BasicParser(file, source) {
}
std::string parseLuaName() {
char c = peek();
if (!is_identifier_start(c)) {
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_identifier_part(source[pos])) {
pos++;
}
return std::string(source.substr(start, pos - start));
}
inline Location currentLocation() const {
return Location {
static_cast<int>(pos),
static_cast<int>(linestart),
static_cast<int>(line)};
}
void emitToken(
TokenTag tag, std::string name, Location start, bool standalone=false
) {
tokens.emplace_back(
tag,
std::move(name),
std::move(start),
currentLocation()
);
if (standalone) skip(1);
}
/// @brief Get next operator token without checking operator for existing
std::string parseOperator() {
int start = pos;
char first = peek();
switch (first) {
case '#': case '+': case '/': case '*': case '^':
case '%':
skip(1);
return std::string({first});
case '-':
skip(1);
if (peekNoJump() == '-') {
skip(1);
return "--";
}
return std::string({first});
}
skip(1);
char second = peekNoJump();
if ((first == '=' && second == '=') || (first == '~' && second == '=') ||
(first == '<' && second == '=') || (first == '>' && second == '=')) {
skip(1);
return std::string(source.substr(start, pos - start));
}
if (first == '.' && second == '.') {
skip(1);
if (peekNoJump() == '.') {
skip(1);
}
}
return std::string(source.substr(start, pos - start));
}
std::vector<Token> tokenize() {
skipWhitespace();
while (hasNext()) {
skipWhitespace();
if (!hasNext()) {
continue;
}
char c = peek();
auto start = currentLocation();
if (is_lua_identifier_start(c)) {
auto name = parseLuaName();
emitToken(
is_lua_keyword(name) ? TokenTag::KEYWORD : TokenTag::NAME,
std::move(name),
start
);
continue;
} else if (is_digit(c)) {
dv::value value;
auto tag = TokenTag::UNEXPECTED;
try {
value = parseNumber(1);
tag = value.isInteger() ? TokenTag::INTEGER
: TokenTag::NUMBER;
} catch (const parsing_error& err) {}
auto literal = source.substr(start.pos, pos - start.pos);
emitToken(tag, std::string(literal), start);
continue;
}
switch (c) {
case '(': case '[': case '{':
if (isNext("[==[")) {
auto string = readUntil("]==]", true);
skip(4);
emitToken(TokenTag::COMMENT, std::string(string)+"]==]", start);
continue;
} else if (isNext("[[")) {
skip(2);
auto string = readUntil("]]", true);
skip(2);
emitToken(TokenTag::STRING, std::string(string), start);
continue;
}
emitToken(TokenTag::OPEN_BRACKET, std::string({c}), start, true);
continue;
case ')': case ']': case '}':
emitToken(TokenTag::CLOSE_BRACKET, std::string({c}), start, true);
continue;
case ',':
emitToken(TokenTag::COMMA, std::string({c}), start, true);
continue;
case ';':
emitToken(TokenTag::SEMICOLON, std::string({c}), start, true);
continue;
case '\'': case '"': {
skip(1);
auto string = parseString(c, false);
emitToken(TokenTag::STRING, std::move(string), start);
continue;
}
default: break;
}
if (is_lua_operator_start(c)) {
auto text = parseOperator();
if (text == "--") {
auto string = readUntilEOL();
emitToken(TokenTag::COMMENT, std::string(string), start);
skipLine();
continue;
}
emitToken(TokenTag::OPERATOR, std::move(text), start);
continue;
}
auto text = readUntilWhitespace();
emitToken(TokenTag::UNEXPECTED, std::string(text), start);
}
return std::move(tokens);
}
};
std::vector<Token> lua::tokenize(std::string_view file, std::string_view source) {
return Tokenizer(file, source).tokenize();
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <string>
#include <vector>
#include "devtools/syntax.hpp"
namespace lua {
bool is_lua_keyword(std::string_view view);
std::vector<devtools::Token> tokenize(
std::string_view file, std::string_view source
);
}

View File

@ -13,7 +13,7 @@
static debug::Logger logger("png-coder"); static debug::Logger logger("png-coder");
// returns 0 if all-right, 1 otherwise // returns 0 if all-right, 1 otherwise
int _png_write( static int png_write(
const char* filename, uint width, uint height, const ubyte* data, bool alpha const char* filename, uint width, uint height, const ubyte* data, bool alpha
) { ) {
uint pixsize = alpha ? 4 : 3; uint pixsize = alpha ? 4 : 3;
@ -112,7 +112,7 @@ static void read_in_memory(png_structp pngPtr, png_bytep dst, png_size_t toread)
} }
std::unique_ptr<ImageData> png::load_image(const ubyte* bytes, size_t size) { std::unique_ptr<ImageData> png::load_image(const ubyte* bytes, size_t size) {
if (!png_check_sig(bytes, size)) { if (size < 8 || !png_check_sig(bytes, 8)) {
throw std::runtime_error("invalid png signature"); throw std::runtime_error("invalid png signature");
} }
png_structp pngPtr = nullptr; png_structp pngPtr = nullptr;
@ -223,7 +223,7 @@ std::unique_ptr<Texture> png::load_texture(const std::string& filename) {
} }
void png::write_image(const std::string& filename, const ImageData* image) { void png::write_image(const std::string& filename, const ImageData* image) {
_png_write( png_write(
filename.c_str(), filename.c_str(),
image->getWidth(), image->getWidth(),
image->getHeight(), image->getHeight(),

View File

@ -107,8 +107,8 @@ glm::vec4 Attribute::asColor() const {
Node::Node(std::string tag) : tag(std::move(tag)) { Node::Node(std::string tag) : tag(std::move(tag)) {
} }
void Node::add(const xmlelement& element) { void Node::add(std::unique_ptr<Node> element) {
elements.push_back(element); elements.push_back(std::move(element));
} }
void Node::set(const std::string& name, const std::string& text) { void Node::set(const std::string& name, const std::string& text) {
@ -119,7 +119,7 @@ const std::string& Node::getTag() const {
return tag; return tag;
} }
const xmlattribute& Node::attr(const std::string& name) const { const Attribute& Node::attr(const std::string& name) const {
auto found = attrs.find(name); auto found = attrs.find(name);
if (found == attrs.end()) { if (found == attrs.end()) {
throw std::runtime_error( throw std::runtime_error(
@ -129,7 +129,7 @@ const xmlattribute& Node::attr(const std::string& name) const {
return found->second; return found->second;
} }
xmlattribute Node::attr(const std::string& name, const std::string& def) const { Attribute Node::attr(const std::string& name, const std::string& def) const {
auto found = attrs.find(name); auto found = attrs.find(name);
if (found == attrs.end()) { if (found == attrs.end()) {
return Attribute(name, def); return Attribute(name, def);
@ -142,19 +142,23 @@ bool Node::has(const std::string& name) const {
return found != attrs.end(); return found != attrs.end();
} }
xmlelement Node::sub(size_t index) { Node& Node::sub(size_t index) {
return elements.at(index); return *elements.at(index);
}
const Node& Node::sub(size_t index) const {
return *elements.at(index);
} }
size_t Node::size() const { size_t Node::size() const {
return elements.size(); return elements.size();
} }
const std::vector<xmlelement>& Node::getElements() const { const std::vector<std::unique_ptr<Node>>& Node::getElements() const {
return elements; return elements;
} }
const xmlelements_map& Node::getAttributes() const { const std::unordered_map<std::string, Attribute>& Node::getAttributes() const {
return attrs; return attrs;
} }
@ -162,12 +166,12 @@ Document::Document(std::string version, std::string encoding)
: version(std::move(version)), encoding(std::move(encoding)) { : version(std::move(version)), encoding(std::move(encoding)) {
} }
void Document::setRoot(const xmlelement& element) { void Document::setRoot(std::unique_ptr<Node> element) {
this->root = element; root = std::move(element);
} }
xmlelement Document::getRoot() const { const Node* Document::getRoot() const {
return root; return root.get();
} }
const std::string& Document::getVersion() const { const std::string& Document::getVersion() const {
@ -178,13 +182,21 @@ const std::string& Document::getEncoding() const {
return encoding; return encoding;
} }
Parser::Parser(std::string_view filename, std::string_view source) inline bool is_xml_identifier_start(char c) {
: BasicParser(filename, source) { return is_identifier_start(c) || c == ':';
} }
xmlelement Parser::parseOpenTag() { inline bool is_xml_identifier_part(char c) {
return is_identifier_part(c) || c == '-' || c == '.' || c == ':';
}
namespace {
class Parser : BasicParser {
std::unique_ptr<Document> document;
std::unique_ptr<Node> parseOpenTag() {
std::string tag = parseXMLName(); std::string tag = parseXMLName();
auto node = std::make_shared<Node>(tag); auto node = std::make_unique<Node>(tag);
char c; char c;
while (true) { while (true) {
@ -208,76 +220,12 @@ xmlelement Parser::parseOpenTag() {
node->set(attrname, attrtext); node->set(attrname, attrtext);
} }
return node; return node;
} }
void Parser::parseDeclaration() { std::unique_ptr<Node> parseElement() {
std::string version = "1.0";
std::string encoding = "UTF-8";
expect('<');
if (peek() == '?') {
nextChar();
xmlelement node = parseOpenTag();
expect("?>");
if (node->getTag() != "xml") {
throw error("invalid declaration");
}
version = node->attr("version", version).getText();
encoding = node->attr("encoding", encoding).getText();
if (encoding != "utf-8" && encoding != "UTF-8") {
throw error("UTF-8 encoding is only supported");
}
} else {
goBack();
}
document = std::make_shared<Document>(version, encoding);
}
void Parser::parseComment() {
expect("!--");
if (skipTo("-->")) {
skip(3);
} else {
throw error("comment close missing");
}
}
std::string Parser::parseText() {
size_t start = pos;
while (hasNext()) {
char c = peek();
if (c == '<') {
break;
}
nextChar();
}
return Parser("[string]", std::string(source.substr(start, pos - start)))
.parseString('\0', false);
}
inline bool is_xml_identifier_start(char c) {
return is_identifier_start(c) || c == ':';
}
inline bool is_xml_identifier_part(char c) {
return is_identifier_part(c) || c == '-' || c == '.' || c == ':';
}
std::string Parser::parseXMLName() {
char c = peek();
if (!is_xml_identifier_start(c)) {
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_xml_identifier_part(source[pos])) {
pos++;
}
return std::string(source.substr(start, pos - start));
}
xmlelement Parser::parseElement() {
// text element // text element
if (peek() != '<') { if (peek() != '<') {
auto element = std::make_shared<Node>("#"); auto element = std::make_unique<Node>("#");
auto text = parseText(); auto text = parseText();
util::replaceAll(text, "&quot;", "\""); util::replaceAll(text, "&quot;", "\"");
util::replaceAll(text, "&apos;", "'"); util::replaceAll(text, "&apos;", "'");
@ -311,7 +259,7 @@ xmlelement Parser::parseElement() {
while (!isNext("</")) { while (!isNext("</")) {
auto sub = parseElement(); auto sub = parseElement();
if (sub) { if (sub) {
element->add(sub); element->add(std::move(sub));
} }
skipWhitespace(); skipWhitespace();
} }
@ -324,20 +272,84 @@ xmlelement Parser::parseElement() {
throw error("invalid syntax"); throw error("invalid syntax");
} }
return element; return element;
} }
xmldocument Parser::parse() { void parseDeclaration() {
std::string version = "1.0";
std::string encoding = "UTF-8";
expect('<');
if (peek() == '?') {
nextChar();
auto node = parseOpenTag();
expect("?>");
if (node->getTag() != "xml") {
throw error("invalid declaration");
}
version = node->attr("version", version).getText();
encoding = node->attr("encoding", encoding).getText();
if (encoding != "utf-8" && encoding != "UTF-8") {
throw error("UTF-8 encoding is only supported");
}
} else {
goBack();
}
document = std::make_unique<Document>(version, encoding);
}
void parseComment() {
expect("!--");
if (skipTo("-->")) {
skip(3);
} else {
throw error("comment close missing");
}
}
std::string parseText() {
size_t start = pos;
while (hasNext()) {
char c = peek();
if (c == '<') {
break;
}
nextChar();
}
return Parser("[string]", std::string(source.substr(start, pos - start)))
.parseString('\0', false);
}
std::string parseXMLName() {
char c = peek();
if (!is_xml_identifier_start(c)) {
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_xml_identifier_part(source[pos])) {
pos++;
}
return std::string(source.substr(start, pos - start));
}
public:
Parser(std::string_view filename, std::string_view source)
: BasicParser(filename, source) {
}
std::unique_ptr<Document> parse() {
parseDeclaration(); parseDeclaration();
xmlelement root = nullptr; std::unique_ptr<Node> root;
while (root == nullptr) { while (root == nullptr) {
root = parseElement(); root = parseElement();
} }
document->setRoot(root); document->setRoot(std::move(root));
return document; return std::move(document);
}
};
} }
xmldocument xml::parse(std::string_view filename, std::string_view source) { std::unique_ptr<Document> xml::parse(
std::string_view filename, std::string_view source
) {
Parser parser(filename, source); Parser parser(filename, source);
return parser.parse(); return parser.parse();
} }
@ -354,13 +366,13 @@ inline void newline(
static void stringifyElement( static void stringifyElement(
std::stringstream& ss, std::stringstream& ss,
const xmlelement& element, const Node& element,
bool nice, bool nice,
const std::string& indentStr, const std::string& indentStr,
int indent int indent
) { ) {
if (element->isText()) { if (element.isText()) {
std::string text = element->attr("#").getText(); std::string text = element.attr("#").getText();
util::replaceAll(text, "&", "&amp;"); util::replaceAll(text, "&", "&amp;");
util::replaceAll(text, "\"", "&quot;"); util::replaceAll(text, "\"", "&quot;");
util::replaceAll(text, "'", "&apos;"); util::replaceAll(text, "'", "&apos;");
@ -369,10 +381,10 @@ static void stringifyElement(
ss << text; ss << text;
return; return;
} }
const std::string& tag = element->getTag(); const std::string& tag = element.getTag();
ss << '<' << tag; ss << '<' << tag;
auto& attrs = element->getAttributes(); auto& attrs = element.getAttributes();
if (!attrs.empty()) { if (!attrs.empty()) {
ss << ' '; ss << ' ';
int count = 0; int count = 0;
@ -388,10 +400,10 @@ static void stringifyElement(
count++; count++;
} }
} }
auto& elements = element->getElements(); auto& elements = element.getElements();
if (elements.size() == 1 && elements[0]->isText()) { if (elements.size() == 1 && elements[0]->isText()) {
ss << ">"; ss << ">";
stringifyElement(ss, elements[0], nice, indentStr, indent + 1); stringifyElement(ss, *elements[0], nice, indentStr, indent + 1);
ss << "</" << tag << ">"; ss << "</" << tag << ">";
return; return;
} }
@ -399,7 +411,7 @@ static void stringifyElement(
ss << '>'; ss << '>';
for (auto& sub : elements) { for (auto& sub : elements) {
newline(ss, nice, indentStr, indent + 1); newline(ss, nice, indentStr, indent + 1);
stringifyElement(ss, sub, nice, indentStr, indent + 1); stringifyElement(ss, *sub, nice, indentStr, indent + 1);
} }
newline(ss, nice, indentStr, indent); newline(ss, nice, indentStr, indent);
ss << "</" << tag << ">"; ss << "</" << tag << ">";
@ -410,16 +422,16 @@ static void stringifyElement(
} }
std::string xml::stringify( std::string xml::stringify(
const xmldocument& document, bool nice, const std::string& indentStr const Document& document, bool nice, const std::string& indentStr
) { ) {
std::stringstream ss; std::stringstream ss;
// XML declaration // XML declaration
ss << "<?xml version=\"" << document->getVersion(); ss << "<?xml version=\"" << document.getVersion();
ss << "\" encoding=\"UTF-8\" ?>"; ss << "\" encoding=\"UTF-8\" ?>";
newline(ss, nice, indentStr, 0); newline(ss, nice, indentStr, 0);
stringifyElement(ss, document->getRoot(), nice, indentStr, 0); stringifyElement(ss, *document.getRoot(), nice, indentStr, 0);
return ss.str(); return ss.str();
} }

View File

@ -13,11 +13,6 @@ namespace xml {
class Attribute; class Attribute;
class Document; class Document;
using xmlattribute = Attribute;
using xmlelement = std::shared_ptr<Node>;
using xmldocument = std::shared_ptr<Document>;
using xmlelements_map = std::unordered_map<std::string, xmlattribute>;
class Attribute { class Attribute {
std::string name; std::string name;
std::string text; std::string text;
@ -40,13 +35,15 @@ namespace xml {
/// 'text' /// 'text'
class Node { class Node {
std::string tag; std::string tag;
std::unordered_map<std::string, xmlattribute> attrs; std::unordered_map<std::string, Attribute> attrs;
std::vector<xmlelement> elements; std::vector<std::unique_ptr<Node>> elements;
public: public:
Node(std::string tag); Node(std::string tag);
Node(const Node&) = delete;
/// @brief Add sub-element /// @brief Add sub-element
void add(const xmlelement& element); void add(std::unique_ptr<Node> element);
/// @brief Set attribute value. Creates attribute if does not exists /// @brief Set attribute value. Creates attribute if does not exists
/// @param name attribute name /// @param name attribute name
@ -67,15 +64,15 @@ namespace xml {
/// @brief Get attribute by name /// @brief Get attribute by name
/// @param name attribute name /// @param name attribute name
/// @throws std::runtime_error if element has no attribute /// @throws std::runtime_error if element has no attribute
/// @return xmlattribute - {name, value} /// @return xml attribute - {name, value}
const xmlattribute& attr(const std::string& name) const; const Attribute& attr(const std::string& name) const;
/// @brief Get attribute by name /// @brief Get attribute by name
/// @param name attribute name /// @param name attribute name
/// @param def default value will be returned wrapped in xmlattribute /// @param def default value will be returned wrapped in xmlattribute
/// if element has no attribute /// if element has no attribute
/// @return xmlattribute - {name, value} or {name, def} if not found*/ /// @return xml attribute - {name, value} or {name, def} if not found
xmlattribute attr(const std::string& name, const std::string& def) Attribute attr(const std::string& name, const std::string& def)
const; const;
/// @brief Check if element has attribute /// @brief Check if element has attribute
@ -86,51 +83,37 @@ namespace xml {
/// @param index sub-element index /// @param index sub-element index
/// @throws std::out_of_range if an invalid index given /// @throws std::out_of_range if an invalid index given
/// @return sub-element /// @return sub-element
xmlelement sub(size_t index); Node& sub(size_t index);
const Node& sub(size_t index) const;
/// @brief Get number of sub-elements /// @brief Get number of sub-elements
size_t size() const; size_t size() const;
const std::vector<xmlelement>& getElements() const; const std::vector<std::unique_ptr<Node>>& getElements() const;
const xmlelements_map& getAttributes() const; const std::unordered_map<std::string, Attribute>& getAttributes() const;
}; };
class Document { class Document {
xmlelement root = nullptr; std::unique_ptr<Node> root = nullptr;
std::string version; std::string version;
std::string encoding; std::string encoding;
public: public:
Document(std::string version, std::string encoding); Document(std::string version, std::string encoding);
void setRoot(const xmlelement& element); void setRoot(std::unique_ptr<Node> element);
xmlelement getRoot() const; const Node* getRoot() const;
const std::string& getVersion() const; const std::string& getVersion() const;
const std::string& getEncoding() const; const std::string& getEncoding() const;
}; };
class Parser : BasicParser {
xmldocument document;
xmlelement parseOpenTag();
xmlelement parseElement();
void parseDeclaration();
void parseComment();
std::string parseText();
std::string parseXMLName();
public:
Parser(std::string_view filename, std::string_view source);
xmldocument parse();
};
/// @brief Serialize XML Document to string /// @brief Serialize XML Document to string
/// @param document serializing document /// @param document serializing document
/// @param nice use human readable format (with indents and line-separators) /// @param nice use human readable format (with indents and line-separators)
/// @param indentStr indentation characters sequence (default - 4 spaces) /// @param indentStr indentation characters sequence (default - 4 spaces)
/// @return XML string /// @return XML string
extern std::string stringify( std::string stringify(
const xmldocument& document, const Document& document,
bool nice = true, bool nice = true,
const std::string& indentStr = " " const std::string& indentStr = " "
); );
@ -139,7 +122,9 @@ namespace xml {
/// @param filename file name will be shown in error messages /// @param filename file name will be shown in error messages
/// @param source xml source code string /// @param source xml source code string
/// @return xml document /// @return xml document
extern xmldocument parse( std::unique_ptr<Document> parse(
std::string_view filename, std::string_view source std::string_view filename, std::string_view source
); );
using xmlelement = Node;
} }

View File

@ -6,7 +6,7 @@
#include <string> #include <string>
inline constexpr int ENGINE_VERSION_MAJOR = 0; inline constexpr int ENGINE_VERSION_MAJOR = 0;
inline constexpr int ENGINE_VERSION_MINOR = 25; inline constexpr int ENGINE_VERSION_MINOR = 26;
#ifdef NDEBUG #ifdef NDEBUG
inline constexpr bool ENGINE_DEBUG_BUILD = false; inline constexpr bool ENGINE_DEBUG_BUILD = false;
@ -14,7 +14,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = false;
inline constexpr bool ENGINE_DEBUG_BUILD = true; inline constexpr bool ENGINE_DEBUG_BUILD = true;
#endif // NDEBUG #endif // NDEBUG
inline const std::string ENGINE_VERSION_STRING = "0.25"; inline const std::string ENGINE_VERSION_STRING = "0.26";
/// @brief world regions format version /// @brief world regions format version
inline constexpr uint REGION_FORMAT_VERSION = 3; inline constexpr uint REGION_FORMAT_VERSION = 3;

30
src/devtools/syntax.hpp Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <string>
namespace devtools {
struct Location {
int pos;
int lineStart;
int line;
};
enum class TokenTag {
KEYWORD, NAME, INTEGER, NUMBER, OPEN_BRACKET, CLOSE_BRACKET, STRING,
OPERATOR, COMMA, SEMICOLON, UNEXPECTED, COMMENT
};
struct Token {
TokenTag tag;
std::string text;
Location start;
Location end;
Token(TokenTag tag, std::string text, Location start, Location end)
: tag(tag),
text(std::move(text)),
start(std::move(start)),
end(std::move(end)) {
}
};
}

View File

@ -0,0 +1,72 @@
#include "syntax_highlighting.hpp"
#include "coders/commons.hpp"
#include "coders/lua_parsing.hpp"
#include "graphics/core/Font.hpp"
using namespace devtools;
static std::unique_ptr<FontStylesScheme> build_styles(
const std::vector<devtools::Token>& tokens
) {
using devtools::TokenTag;
FontStylesScheme styles {
{
{false, false, false, false, glm::vec4(0.8f, 0.8f, 0.8f, 1)}, // default
{true, false, false, false, glm::vec4(0.9, 0.6f, 0.4f, 1)}, // keyword
{false, false, false, false, glm::vec4(0.4, 0.8f, 0.5f, 1)}, // string
{false, false, false, false, glm::vec4(0.3, 0.3f, 0.3f, 1)}, // comment
{true, false, false, false, glm::vec4(1.0f, 0.2f, 0.1f, 1)}, // unexpected
},
{}
};
size_t offset = 0;
for (int i = 0; i < tokens.size(); i++) {
const auto& token = tokens.at(i);
if (token.tag != TokenTag::KEYWORD &&
token.tag != TokenTag::STRING &&
token.tag != TokenTag::INTEGER &&
token.tag != TokenTag::NUMBER &&
token.tag != TokenTag::COMMENT &&
token.tag != TokenTag::UNEXPECTED) {
continue;
}
if (token.start.pos > offset) {
int n = token.start.pos - offset;
styles.map.insert(styles.map.end(), token.start.pos - offset, 0);
}
offset = token.end.pos;
int styleIndex;
switch (token.tag) {
case TokenTag::KEYWORD: styleIndex = SyntaxStyles::KEYWORD; break;
case TokenTag::STRING:
case TokenTag::INTEGER:
case TokenTag::NUMBER: styleIndex = SyntaxStyles::LITERAL; break;
case TokenTag::COMMENT: styleIndex = SyntaxStyles::COMMENT; break;
case TokenTag::UNEXPECTED: styleIndex = SyntaxStyles::ERROR; break;
default:
styleIndex = 0;
break;
}
styles.map.insert(
styles.map.end(), token.end.pos - token.start.pos, styleIndex
);
}
styles.map.push_back(0);
return std::make_unique<FontStylesScheme>(std::move(styles));
}
std::unique_ptr<FontStylesScheme> devtools::syntax_highlight(
const std::string& lang, std::string_view source
) {
try {
if (lang == "lua") {
auto tokens = lua::tokenize("<string>", source);
return build_styles(tokens);
} else {
return nullptr;
}
} catch (const parsing_error& err) {
return nullptr;
}
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <string>
#include <memory>
struct FontStylesScheme;
namespace devtools {
enum SyntaxStyles {
DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR
};
std::unique_ptr<FontStylesScheme> syntax_highlight(
const std::string& lang, std::string_view source
);
}

View File

@ -206,7 +206,7 @@ void Engine::renderFrame(Batch2D& batch) {
Viewport viewport(Window::width, Window::height); Viewport viewport(Window::width, Window::height);
DrawContext ctx(nullptr, viewport, &batch); DrawContext ctx(nullptr, viewport, &batch);
gui->draw(&ctx, assets.get()); gui->draw(ctx, *assets);
} }
void Engine::processPostRunnables() { void Engine::processPostRunnables() {

View File

@ -67,9 +67,7 @@ std::unique_ptr<UiDocument> UiDocument::read(
: scripting::create_doc_environment(penv, name); : scripting::create_doc_environment(penv, name);
gui::UiXmlReader reader(env); gui::UiXmlReader reader(env);
auto view = reader.readXML( auto view = reader.readXML(file.u8string(), *xmldoc->getRoot());
file.u8string(), xmldoc->getRoot()
);
view->setId("root"); view->setId("root");
uidocscript script {}; uidocscript script {};
auto scriptFile = fs::path(file.u8string()+".lua"); auto scriptFile = fs::path(file.u8string()+".lua");

View File

@ -124,7 +124,7 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
}); });
InventoryBuilder builder; InventoryBuilder builder;
builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout); builder.addGrid(8, itemsCount-1, glm::vec2(), glm::vec4(8, 8, 12, 8), true, slotLayout);
auto view = builder.build(); auto view = builder.build();
view->bind(accessInventory, content); view->bind(accessInventory, content);
view->setMargin(glm::vec4()); view->setMargin(glm::vec4());
@ -137,7 +137,7 @@ std::shared_ptr<InventoryView> Hud::createHotbar() {
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr); SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr);
InventoryBuilder builder; InventoryBuilder builder;
builder.addGrid(10, 10, glm::vec2(), 4, true, slotLayout); builder.addGrid(10, 10, glm::vec2(), glm::vec4(4), true, slotLayout);
auto view = builder.build(); auto view = builder.build();
view->setId("hud.hotbar"); view->setId("hud.hotbar");
view->setOrigin(glm::vec2(view->getSize().x/2, 0)); view->setOrigin(glm::vec2(view->getSize().x/2, 0));
@ -346,9 +346,9 @@ void Hud::update(bool visible) {
element.getNode()->setVisible(visible); element.getNode()->setVisible(visible);
} }
glm::vec2 invSize = contentAccessPanel->getSize(); glm::vec2 caSize = contentAccessPanel->getSize();
contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel); contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel);
contentAccessPanel->setSize(glm::vec2(invSize.x, Window::height)); contentAccessPanel->setSize(glm::vec2(caSize.x, Window::height));
contentAccess->setMinSize(glm::vec2(1, Window::height)); contentAccess->setMinSize(glm::vec2(1, Window::height));
hotbarView->setVisible(visible && !(secondUI && !inventoryView)); hotbarView->setVisible(visible && !(secondUI && !inventoryView));

View File

@ -95,16 +95,16 @@ class Hud : public util::ObjectsKeeper {
/// @brief Inventories interaction agent (grabbed item) /// @brief Inventories interaction agent (grabbed item)
std::shared_ptr<gui::SlotView> exchangeSlot; std::shared_ptr<gui::SlotView> exchangeSlot;
/// @brief Exchange slot inventory (1 slot only) /// @brief Exchange slot inventory (1 slot only)
std::shared_ptr<Inventory> exchangeSlotInv = nullptr; std::shared_ptr<Inventory> exchangeSlotInv;
/// @brief List of all controlled hud elements /// @brief List of all controlled hud elements
std::vector<HudElement> elements; std::vector<HudElement> elements;
/// @brief Player inventory view /// @brief Player inventory view
std::shared_ptr<gui::InventoryView> inventoryView = nullptr; std::shared_ptr<gui::InventoryView> inventoryView;
/// @brief Block inventory view /// @brief Block inventory view
std::shared_ptr<gui::InventoryView> blockUI = nullptr; std::shared_ptr<gui::InventoryView> blockUI;
/// @brief Secondary inventory view /// @brief Secondary inventory view
std::shared_ptr<gui::InventoryView> secondInvView = nullptr; std::shared_ptr<gui::InventoryView> secondInvView;
/// @brief Position of the block open /// @brief Position of the block open
glm::ivec3 blockPos {}; glm::ivec3 blockPos {};
/// @brief Id of the block open (used to detect block destruction or replacement) /// @brief Id of the block open (used to detect block destruction or replacement)
@ -114,9 +114,9 @@ class Hud : public util::ObjectsKeeper {
/// @brief Provide cheat controllers to the debug panel /// @brief Provide cheat controllers to the debug panel
bool allowDebugCheats = true; bool allowDebugCheats = true;
/// @brief UI element will be dynamicly positioned near to inventory or in screen center /// @brief UI element will be dynamicly positioned near to inventory or in screen center
std::shared_ptr<gui::UINode> secondUI = nullptr; std::shared_ptr<gui::UINode> secondUI;
std::shared_ptr<gui::UINode> debugMinimap = nullptr; std::shared_ptr<gui::UINode> debugMinimap;
std::unique_ptr<ImageData> debugImgWorldGen; std::unique_ptr<ImageData> debugImgWorldGen;

View File

@ -261,6 +261,24 @@ void Batch2D::rect(
vertex(x+w, y+h, u+tx, v, r,g,b,a); vertex(x+w, y+h, u+tx, v, r,g,b,a);
} }
void Batch2D::parallelogram(
float x, float y, float w, float h, float skew,
float u, float v, float tx, float ty,
float r, float g, float b, float a
){
if (index + 6*B2D_VERTEX_SIZE >= capacity) {
flush();
}
setPrimitive(DrawPrimitive::triangle);
vertex(x-skew*w, y, u, v+ty, r,g,b,a);
vertex(x+(1+skew)*w, y+h, u+tx, v, r,g,b,a);
vertex(x+skew*w, y+h, u, v, r,g,b,a);
vertex(x-skew*w, y, u, v+ty, r,g,b,a);
vertex(x+w-skew*w, y, u+tx, v+ty, r,g,b,a);
vertex(x+(1+skew)*w, y+h, u+tx, v, r,g,b,a);
}
void Batch2D::rect( void Batch2D::rect(
float x, float y, float w, float h, float x, float y, float w, float h,
float r0, float g0, float b0, float r0, float g0, float b0,
@ -336,6 +354,22 @@ void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index
rect(x, y, w, h, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a); rect(x, y, w, h, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a);
} }
void Batch2D::sprite(
float x,
float y,
float w,
float h,
float skew,
int atlasRes,
int index,
glm::vec4 tint
) {
float scale = 1.0f / (float)atlasRes;
float u = (index % atlasRes) * scale;
float v = 1.0f - ((index / atlasRes) * scale) - scale;
parallelogram(x, y, w, h, skew, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a);
}
void Batch2D::flush() { void Batch2D::flush() {
if (index == 0) if (index == 0)
return; return;

View File

@ -45,6 +45,7 @@ public:
void setRegion(UVRegion region); void setRegion(UVRegion region);
void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint); void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint);
void sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint); void sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint);
void sprite(float x, float y, float w, float h, float skew, int atlasRes, int index, glm::vec4 tint);
void point(float x, float y, float r, float g, float b, float a); void point(float x, float y, float r, float g, float b, float a);
inline void setColor(glm::vec4 color) { inline void setColor(glm::vec4 color) {
@ -79,6 +80,12 @@ public:
float r, float g, float b, float a float r, float g, float b, float a
); );
void parallelogram(
float x, float y, float w, float h, float skew,
float u, float v, float tx, float ty,
float r, float g, float b, float a
);
void rect( void rect(
float x, float y, float w, float h, float x, float y, float w, float h,
float r0, float g0, float b0, float r0, float g0, float b0,

View File

@ -1,5 +1,6 @@
#include "Font.hpp" #include "Font.hpp"
#include <limits>
#include <utility> #include <utility>
#include "Texture.hpp" #include "Texture.hpp"
#include "Batch2D.hpp" #include "Batch2D.hpp"
@ -52,17 +53,21 @@ static inline void draw_glyph(
uint c, uint c,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up, const glm::vec3& up,
float glyphInterval float glyphInterval,
const FontStyle& style
) { ) {
for (int i = 0; i <= style.bold; i++) {
batch.sprite( batch.sprite(
pos.x + offset.x * right.x, pos.x + (offset.x + i / (right.x/glyphInterval/2.0f)) * right.x,
pos.y + offset.y * right.y, pos.y + offset.y * right.y,
right.x / glyphInterval, right.x / glyphInterval,
up.y, up.y,
-0.15f * style.italic,
16, 16,
c, c,
batch.getColor() batch.getColor() * style.color
); );
}
} }
static inline void draw_glyph( static inline void draw_glyph(
@ -72,17 +77,20 @@ static inline void draw_glyph(
uint c, uint c,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up, const glm::vec3& up,
float glyphInterval float glyphInterval,
const FontStyle& style
) { ) {
for (int i = 0; i <= style.bold; i++) {
batch.sprite( batch.sprite(
pos + right * offset.x + up * offset.y, pos + right * (offset.x + i) + up * offset.y,
up, right / glyphInterval, up, right / glyphInterval,
0.5f, 0.5f,
0.5f, 0.5f,
16, 16,
c, c,
batch.getColor() batch.getColor() * style.color
); );
}
} }
template <class Batch> template <class Batch>
@ -93,14 +101,32 @@ static inline void draw_text(
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up, const glm::vec3& up,
float glyphInterval float interval,
const FontStylesScheme* styles,
size_t styleMapOffset
) { ) {
static FontStylesScheme defStyles {{{}}, {0}};
if (styles == nullptr) {
styles = &defStyles;
}
uint page = 0; uint page = 0;
uint next = MAX_CODEPAGES; uint next = MAX_CODEPAGES;
int x = 0; int x = 0;
int y = 0; int y = 0;
bool hasLines = false;
do { do {
for (uint c : text){ for (size_t i = 0; i < text.length(); i++) {
uint c = text[i];
size_t styleIndex = styles->map.at(
std::min(styles->map.size() - 1, i + styleMapOffset)
);
const FontStyle& style = styles->palette.at(styleIndex);
hasLines |= style.strikethrough;
hasLines |= style.underline;
if (!font.isPrintableChar(c)) { if (!font.isPrintableChar(c)) {
x++; x++;
continue; continue;
@ -109,7 +135,7 @@ static inline void draw_text(
if (charpage == page){ if (charpage == page){
batch.texture(font.getPage(charpage)); batch.texture(font.getPage(charpage));
draw_glyph( draw_glyph(
batch, pos, glm::vec2(x, y), c, right, up, glyphInterval batch, pos, glm::vec2(x, y), c, right, up, interval, style
); );
} }
else if (charpage > page && charpage < next){ else if (charpage > page && charpage < next){
@ -121,6 +147,31 @@ static inline void draw_text(
next = MAX_CODEPAGES; next = MAX_CODEPAGES;
x = 0; x = 0;
} while (page < MAX_CODEPAGES); } while (page < MAX_CODEPAGES);
if (!hasLines) {
return;
}
batch.texture(font.getPage(0));
for (size_t i = 0; i < text.length(); i++) {
uint c = text[i];
size_t styleIndex = styles->map.at(
std::min(styles->map.size() - 1, i + styleMapOffset)
);
const FontStyle& style = styles->palette.at(styleIndex);
FontStyle lineStyle = style;
lineStyle.bold = true;
if (style.strikethrough) {
draw_glyph(
batch, pos, glm::vec2(x, y), '-', right, up, interval, lineStyle
);
}
if (style.underline) {
draw_glyph(
batch, pos, glm::vec2(x, y), '_', right, up, interval, lineStyle
);
}
x++;
}
} }
const Texture* Font::getPage(int charpage) const { const Texture* Font::getPage(int charpage) const {
@ -135,20 +186,30 @@ const Texture* Font::getPage(int charpage) const {
} }
void Font::draw( void Font::draw(
Batch2D& batch, std::wstring_view text, int x, int y, float scale Batch2D& batch,
std::wstring_view text,
int x,
int y,
const FontStylesScheme* styles,
size_t styleMapOffset,
float scale
) const { ) const {
draw_text( draw_text(
*this, batch, text, *this, batch, text,
glm::vec3(x, y, 0), glm::vec3(x, y, 0),
glm::vec3(glyphInterval*scale, 0, 0), glm::vec3(glyphInterval*scale, 0, 0),
glm::vec3(0, lineHeight*scale, 0), glm::vec3(0, lineHeight*scale, 0),
glyphInterval/static_cast<float>(lineHeight) glyphInterval/static_cast<float>(lineHeight),
styles,
styleMapOffset
); );
} }
void Font::draw( void Font::draw(
Batch3D& batch, Batch3D& batch,
std::wstring_view text, std::wstring_view text,
const FontStylesScheme* styles,
size_t styleMapOffset,
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up const glm::vec3& up
@ -157,6 +218,8 @@ void Font::draw(
*this, batch, text, pos, *this, batch, text, pos,
right * static_cast<float>(glyphInterval), right * static_cast<float>(glyphInterval),
up * static_cast<float>(lineHeight), up * static_cast<float>(lineHeight),
glyphInterval/static_cast<float>(lineHeight) glyphInterval/static_cast<float>(lineHeight),
styles,
styleMapOffset
); );
} }

View File

@ -11,10 +11,33 @@ class Batch2D;
class Batch3D; class Batch3D;
class Camera; class Camera;
enum class FontStyle { struct FontStyle {
none, bool bold = false;
shadow, bool italic = false;
outline bool strikethrough = false;
bool underline = false;
glm::vec4 color {1, 1, 1, 1};
FontStyle() = default;
FontStyle(
bool bold,
bool italic,
bool strikethrough,
bool underline,
glm::vec4 color
)
: bold(bold),
italic(italic),
strikethrough(strikethrough),
underline(underline),
color(std::move(color)) {
}
};
struct FontStylesScheme {
std::vector<FontStyle> palette;
std::vector<ubyte> map;
}; };
class Font { class Font {
@ -46,11 +69,21 @@ public:
/// @param codepoint character unicode codepoint /// @param codepoint character unicode codepoint
bool isPrintableChar(uint codepoint) const; bool isPrintableChar(uint codepoint) const;
void draw(Batch2D& batch, std::wstring_view text, int x, int y, float scale=1) const; void draw(
Batch2D& batch,
std::wstring_view text,
int x,
int y,
const FontStylesScheme* styles,
size_t styleMapOffset,
float scale = 1
) const;
void draw( void draw(
Batch3D& batch, Batch3D& batch,
std::wstring_view text, std::wstring_view text,
const FontStylesScheme* styles,
size_t styleMapOffset,
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right={1, 0, 0}, const glm::vec3& right={1, 0, 0},
const glm::vec3& up={0, 1, 0} const glm::vec3& up={0, 1, 0}

View File

@ -65,12 +65,7 @@ void TextsRenderer::renderNote(
xvec *= 1.0f + scale; xvec *= 1.0f + scale;
yvec *= 1.0f + scale; yvec *= 1.0f + scale;
} }
if (preset.displayMode != NoteDisplayMode::PROJECTED) { if (preset.displayMode == NoteDisplayMode::PROJECTED) {
if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f),
pos + xvec * (width * 0.5f))) {
return;
}
} else {
float scale = 1.0f; float scale = 1.0f;
if (glm::abs(preset.perspective) > 0.0001f) { if (glm::abs(preset.perspective) > 0.0001f) {
float scale2 = scale / float scale2 = scale /
@ -99,12 +94,17 @@ void TextsRenderer::renderNote(
pos = screenPos / screenPos.w; pos = screenPos / screenPos.w;
} }
} else if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f * preset.scale),
pos + xvec * (width * 0.5f * preset.scale))) {
return;
} }
auto color = preset.color; auto color = preset.color;
batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity)); batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity));
font.draw( font.draw(
batch, batch,
text, text,
nullptr,
0,
pos - xvec * (width * 0.5f) * preset.scale, pos - xvec * (width * 0.5f) * preset.scale,
xvec * preset.scale, xvec * preset.scale,
yvec * preset.scale yvec * preset.scale

View File

@ -197,18 +197,18 @@ void GUI::act(float delta, const Viewport& vp) {
} }
} }
void GUI::draw(const DrawContext* pctx, Assets* assets) { void GUI::draw(const DrawContext& pctx, const Assets& assets) {
auto& viewport = pctx->getViewport(); auto& viewport = pctx.getViewport();
glm::vec2 wsize = viewport.size(); glm::vec2 wsize = viewport.size();
menu->setPos((wsize - menu->getSize()) / 2.0f); menu->setPos((wsize - menu->getSize()) / 2.0f);
uicamera->setFov(wsize.y); uicamera->setFov(wsize.y);
auto uishader = assets->get<Shader>("ui"); auto uishader = assets.get<Shader>("ui");
uishader->use(); uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjView()); uishader->uniformMatrix("u_projview", uicamera->getProjView());
pctx->getBatch2D()->begin(); pctx.getBatch2D()->begin();
container->draw(pctx, assets); container->draw(pctx, assets);
} }

View File

@ -94,7 +94,7 @@ namespace gui {
/// @brief Draw all visible elements on main container /// @brief Draw all visible elements on main container
/// @param pctx parent graphics context /// @param pctx parent graphics context
/// @param assets active assets storage /// @param assets active assets storage
void draw(const DrawContext* pctx, Assets* assets); void draw(const DrawContext& pctx, const Assets& assets);
/// @brief Add element to the main container /// @brief Add element to the main container
/// @param node UI element /// @param node UI element

View File

@ -52,7 +52,7 @@ Button::Button(
void Button::setText(std::wstring text) { void Button::setText(std::wstring text) {
if (label) { if (label) {
label->setText(text); label->setText(std::move(text));
} }
} }
@ -77,9 +77,9 @@ void Button::refresh() {
} }
} }
void Button::drawBackground(const DrawContext* pctx, Assets*) { void Button::drawBackground(const DrawContext& pctx, const Assets&) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(calcColor()); batch->setColor(calcColor());
batch->rect(pos.x, pos.y, size.x, size.y); batch->rect(pos.x, pos.y, size.x, size.y);

View File

@ -7,7 +7,7 @@ namespace gui {
class Button : public Panel { class Button : public Panel {
protected: protected:
std::shared_ptr<Label> label = nullptr; std::shared_ptr<Label> label;
public: public:
Button(const std::shared_ptr<UINode>& content, Button(const std::shared_ptr<UINode>& content,
glm::vec4 padding=glm::vec4(2.0f)); glm::vec4 padding=glm::vec4(2.0f));
@ -17,7 +17,9 @@ namespace gui {
const onaction& action, const onaction& action,
glm::vec2 size=glm::vec2(-1)); glm::vec2 size=glm::vec2(-1));
virtual void drawBackground(const DrawContext* pctx, Assets* assets) override; virtual void drawBackground(
const DrawContext& pctx, const Assets& assets
) override;
virtual Align getTextAlign() const; virtual Align getTextAlign() const;
virtual void setTextAlign(Align align); virtual void setTextAlign(Align align);

View File

@ -13,12 +13,12 @@ CheckBox::CheckBox(bool checked) : UINode(glm::vec2(32.0f)), checked(checked) {
setHoverColor({0.05f, 0.1f, 0.2f, 0.75f}); setHoverColor({0.05f, 0.1f, 0.2f, 0.75f});
} }
void CheckBox::draw(const DrawContext* pctx, Assets*) { void CheckBox::draw(const DrawContext& pctx, const Assets&) {
if (supplier) { if (supplier) {
checked = supplier(); checked = supplier();
} }
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(checked ? checkColor : calcColor()); batch->setColor(checked ? checkColor : calcColor());
batch->rect(pos.x, pos.y, size.x, size.y); batch->rect(pos.x, pos.y, size.x, size.y);

View File

@ -15,7 +15,7 @@ namespace gui {
public: public:
CheckBox(bool checked=false); CheckBox(bool checked=false);
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void mouseRelease(GUI*, int x, int y) override; virtual void mouseRelease(GUI*, int x, int y) override;

View File

@ -17,11 +17,19 @@ Container::~Container() {
Container::clear(); Container::clear();
} }
std::shared_ptr<UINode> Container::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) { std::shared_ptr<UINode> Container::getAt(
const glm::vec2& pos, const std::shared_ptr<UINode>& self
) {
if (!isInteractive() || !isEnabled()) { if (!isInteractive() || !isEnabled()) {
return nullptr; return nullptr;
} }
if (!isInside(pos)) return nullptr; if (!isInside(pos)) {
return nullptr;
}
int diff = (actualLength-size.y);
if (scrollable && diff > 0 && pos.x > calcPos().x + getSize().x - scrollBarWidth) {
return UINode::getAt(pos, self);
}
for (int i = nodes.size()-1; i >= 0; i--) { for (int i = nodes.size()-1; i >= 0; i--) {
auto& node = nodes[i]; auto& node = nodes[i];
@ -35,6 +43,35 @@ std::shared_ptr<UINode> Container::getAt(glm::vec2 pos, std::shared_ptr<UINode>
return UINode::getAt(pos, self); return UINode::getAt(pos, self);
} }
void Container::mouseMove(GUI* gui, int x, int y) {
UINode::mouseMove(gui, x, y);
if (!scrollable) {
return;
}
auto pos = calcPos();
x -= pos.x;
y -= pos.y;
if (prevScrollY == -1) {
if (x >= size.x - scrollBarWidth) {
prevScrollY = y;
}
return;
}
int diff = (actualLength-size.y);
if (diff > 0) {
scroll -= (y - prevScrollY) / static_cast<float>(size.y) * actualLength;
scroll = -glm::min(
glm::max(static_cast<float>(-scroll), 0.0f), actualLength - size.y
);
}
prevScrollY = y;
}
void Container::mouseRelease(GUI* gui, int x, int y) {
UINode::mouseRelease(gui, x, y);
prevScrollY = -1;
}
void Container::act(float delta) { void Container::act(float delta) {
for (const auto& node : nodes) { for (const auto& node : nodes) {
if (node->isVisible()) { if (node->isVisible()) {
@ -80,38 +117,50 @@ void Container::setScrollable(bool flag) {
scrollable = flag; scrollable = flag;
} }
void Container::draw(const DrawContext* pctx, Assets* assets) { void Container::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
glm::vec2 size = getSize(); glm::vec2 size = getSize();
drawBackground(pctx, assets); drawBackground(pctx, assets);
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
if (!nodes.empty()) { if (!nodes.empty()) {
batch->flush(); batch->flush();
DrawContext ctx = pctx->sub(); DrawContext ctx = pctx.sub();
ctx.setScissors(glm::vec4(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y))); ctx.setScissors(glm::vec4(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y)));
for (const auto& node : nodes) { for (const auto& node : nodes) {
if (node->isVisible()) if (node->isVisible())
node->draw(pctx, assets); node->draw(pctx, assets);
} }
int diff = (actualLength-size.y);
if (scrollable && diff > 0) {
int h = glm::max(size.y / actualLength * size.y, scrollBarWidth / 2.0f);
batch->untexture();
batch->setColor(glm::vec4(1, 1, 1, 0.3f));
batch->rect(
pos.x + size.x - scrollBarWidth,
pos.y - scroll / static_cast<float>(diff) * (size.y - h),
scrollBarWidth, h
);
}
batch->flush(); batch->flush();
} }
} }
void Container::drawBackground(const DrawContext* pctx, Assets*) { void Container::drawBackground(const DrawContext& pctx, const Assets&) {
glm::vec4 color = calcColor(); glm::vec4 color = calcColor();
if (color.a <= 0.001f) if (color.a <= 0.001f)
return; return;
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(color); batch->setColor(color);
batch->rect(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y)); batch->rect(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y));
} }
void Container::add(const std::shared_ptr<UINode> &node) { void Container::add(const std::shared_ptr<UINode>& node) {
nodes.push_back(node); nodes.push_back(node);
node->setParent(this); node->setParent(this);
node->reposition(); node->reposition();

View File

@ -7,23 +7,29 @@
namespace gui { namespace gui {
class Container : public UINode { class Container : public UINode {
int prevScrollY = -1;
protected: protected:
std::vector<std::shared_ptr<UINode>> nodes; std::vector<std::shared_ptr<UINode>> nodes;
std::vector<IntervalEvent> intervalEvents; std::vector<IntervalEvent> intervalEvents;
int scroll = 0; int scroll = 0;
int scrollStep = 40; int scrollStep = 40;
int scrollBarWidth = 10;
int actualLength = 0; int actualLength = 0;
bool scrollable = true; bool scrollable = true;
bool isScrolling() {
return prevScrollY != -1;
}
public: public:
Container(glm::vec2 size); Container(glm::vec2 size);
virtual ~Container(); virtual ~Container();
virtual void act(float delta) override; virtual void act(float delta) override;
virtual void drawBackground(const DrawContext* pctx, Assets* assets); virtual void drawBackground(const DrawContext& pctx, const Assets& assets);
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override; virtual std::shared_ptr<UINode> getAt(const glm::vec2& pos, const std::shared_ptr<UINode>& self) override;
virtual void add(const std::shared_ptr<UINode> &node); virtual void add(const std::shared_ptr<UINode>& node);
virtual void add(const std::shared_ptr<UINode> &node, glm::vec2 pos); virtual void add(const std::shared_ptr<UINode>& node, glm::vec2 pos);
virtual void clear(); virtual void clear();
virtual void remove(const std::shared_ptr<UINode>& node); virtual void remove(const std::shared_ptr<UINode>& node);
virtual void remove(const std::string& id); virtual void remove(const std::string& id);
@ -36,6 +42,9 @@ namespace gui {
virtual void setScrollStep(int step); virtual void setScrollStep(int step);
virtual void refresh() override; virtual void refresh() override;
virtual void mouseMove(GUI*, int x, int y) override;
virtual void mouseRelease(GUI*, int x, int y) override;
const std::vector<std::shared_ptr<UINode>>& getNodes() const; const std::vector<std::shared_ptr<UINode>>& getNodes() const;
}; };
} }

View File

@ -15,21 +15,21 @@ Image::Image(std::string texture, glm::vec2 size) : UINode(size), texture(std::m
setInteractive(false); setInteractive(false);
} }
void Image::draw(const DrawContext* pctx, Assets* assets) { void Image::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
Texture* texture = nullptr; Texture* texture = nullptr;
auto separator = this->texture.find(':'); auto separator = this->texture.find(':');
if (separator == std::string::npos) { if (separator == std::string::npos) {
texture = assets->get<Texture>(this->texture); texture = assets.get<Texture>(this->texture);
batch->texture(texture); batch->texture(texture);
if (texture && autoresize) { if (texture && autoresize) {
setSize(glm::vec2(texture->getWidth(), texture->getHeight())); setSize(glm::vec2(texture->getWidth(), texture->getHeight()));
} }
} else { } else {
auto atlasName = this->texture.substr(0, separator); auto atlasName = this->texture.substr(0, separator);
if (auto atlas = assets->get<Atlas>(atlasName)) { if (auto atlas = assets.get<Atlas>(atlasName)) {
if (auto region = atlas->getIf(this->texture.substr(separator+1))) { if (auto region = atlas->getIf(this->texture.substr(separator+1))) {
texture = atlas->getTexture(); texture = atlas->getTexture();
batch->texture(atlas->getTexture()); batch->texture(atlas->getTexture());

View File

@ -10,7 +10,7 @@ namespace gui {
public: public:
Image(std::string texture, glm::vec2 size=glm::vec2(32,32)); Image(std::string texture, glm::vec2 size=glm::vec2(32,32));
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void setAutoResize(bool flag); virtual void setAutoResize(bool flag);
virtual bool isAutoResize() const; virtual bool isAutoResize() const;

View File

@ -9,15 +9,15 @@ using namespace gui;
InputBindBox::InputBindBox(Binding& binding, glm::vec4 padding) InputBindBox::InputBindBox(Binding& binding, glm::vec4 padding)
: Panel(glm::vec2(100,32), padding, 0), : Panel(glm::vec2(100,32), padding, 0),
binding(binding) { binding(binding),
label = std::make_shared<Label>(L""); label(std::make_shared<Label>(L"")) {
add(label); add(label);
setScrollable(false); setScrollable(false);
} }
void InputBindBox::drawBackground(const DrawContext* pctx, Assets*) { void InputBindBox::drawBackground(const DrawContext& pctx, const Assets&) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(isFocused() ? focusedColor : calcColor()); batch->setColor(isFocused() ? focusedColor : calcColor());
batch->rect(pos.x, pos.y, size.x, size.y); batch->rect(pos.x, pos.y, size.x, size.y);

View File

@ -13,7 +13,10 @@ namespace gui {
Binding& binding; Binding& binding;
public: public:
InputBindBox(Binding& binding, glm::vec4 padding=glm::vec4(6.0f)); InputBindBox(Binding& binding, glm::vec4 padding=glm::vec4(6.0f));
virtual void drawBackground(const DrawContext* pctx, Assets* assets) override;
virtual void drawBackground(
const DrawContext& pctx, const Assets& assets
) override;
virtual void clicked(GUI*, mousecode button) override; virtual void clicked(GUI*, mousecode button) override;
virtual void keyPressed(keycode key) override; virtual void keyPressed(keycode key) override;

View File

@ -54,7 +54,7 @@ InventoryBuilder::InventoryBuilder() {
void InventoryBuilder::addGrid( void InventoryBuilder::addGrid(
int cols, int count, int cols, int count,
glm::vec2 pos, glm::vec2 pos,
int padding, glm::vec4 padding,
bool addpanel, bool addpanel,
const SlotLayout& slotLayout const SlotLayout& slotLayout
) { ) {
@ -63,8 +63,8 @@ void InventoryBuilder::addGrid(
int rows = ceildiv(count, cols); int rows = ceildiv(count, cols);
uint width = cols * (slotSize + interval) - interval + padding*2; uint width = cols * (slotSize + interval) - interval + padding.x + padding.z;
uint height = rows * (slotSize + interval) - interval + padding*2; uint height = rows * (slotSize + interval) - interval + padding.y + padding.w;
glm::vec2 vsize = view->getSize(); glm::vec2 vsize = view->getSize();
if (pos.x + width > vsize.x) { if (pos.x + width > vsize.x) {
@ -87,8 +87,8 @@ void InventoryBuilder::addGrid(
break; break;
} }
glm::vec2 position ( glm::vec2 position (
col * (slotSize + interval) + padding, col * (slotSize + interval) + padding.x,
row * (slotSize + interval) + padding row * (slotSize + interval) + padding.y
); );
auto builtSlot = slotLayout; auto builtSlot = slotLayout;
builtSlot.index = row * cols + col; builtSlot.index = row * cols + col;
@ -115,7 +115,7 @@ SlotView::SlotView(
setTooltipDelay(0.0f); setTooltipDelay(0.0f);
} }
void SlotView::draw(const DrawContext* pctx, Assets* assets) { void SlotView::draw(const DrawContext& pctx, const Assets& assets) {
if (bound == nullptr) { if (bound == nullptr) {
return; return;
} }
@ -144,7 +144,7 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
color = glm::vec4(1, 1, 1, 0.2f); color = glm::vec4(1, 1, 1, 0.2f);
} }
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->setColor(color); batch->setColor(color);
if (color.a > 0.0) { if (color.a > 0.0) {
batch->texture(nullptr); batch->texture(nullptr);
@ -157,7 +157,7 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
batch->setColor(glm::vec4(1.0f)); batch->setColor(glm::vec4(1.0f));
auto previews = assets->get<Atlas>("block-previews"); auto previews = assets.get<Atlas>("block-previews");
auto indices = content->getIndices(); auto indices = content->getIndices();
auto& item = indices->items.require(stack.getItemId()); auto& item = indices->items.require(stack.getItemId());
@ -176,7 +176,7 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
} }
case ItemIconType::SPRITE: { case ItemIconType::SPRITE: {
auto textureRegion = auto textureRegion =
util::get_texture_region(*assets, item.icon, "blocks:notfound"); util::get_texture_region(assets, item.icon, "blocks:notfound");
batch->texture(textureRegion.texture); batch->texture(textureRegion.texture);
batch->rect( batch->rect(
@ -187,16 +187,16 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
} }
if (stack.getCount() > 1) { if (stack.getCount() > 1) {
auto font = assets->get<Font>("normal"); auto font = assets.get<Font>("normal");
std::wstring text = std::to_wstring(stack.getCount()); std::wstring text = std::to_wstring(stack.getCount());
int x = pos.x+slotSize-text.length()*8; int x = pos.x+slotSize-text.length()*8;
int y = pos.y+slotSize-16; int y = pos.y+slotSize-16;
batch->setColor({0, 0, 0, 1.0f}); batch->setColor({0, 0, 0, 1.0f});
font->draw(*batch, text, x+1, y+1); font->draw(*batch, text, x+1, y+1, nullptr, 0);
batch->setColor(glm::vec4(1.0f)); batch->setColor(glm::vec4(1.0f));
font->draw(*batch, text, x, y); font->draw(*batch, text, x, y, nullptr, 0);
} }
} }

View File

@ -64,7 +64,7 @@ namespace gui {
public: public:
SlotView(SlotLayout layout); SlotView(SlotLayout layout);
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
void setHighlighted(bool flag); void setHighlighted(bool flag);
bool isHighlighted() const; bool isHighlighted() const;
@ -136,7 +136,7 @@ namespace gui {
void addGrid( void addGrid(
int cols, int count, int cols, int count,
glm::vec2 pos, glm::vec2 pos,
int padding, glm::vec4 padding,
bool addpanel, bool addpanel,
const SlotLayout& slotLayout const SlotLayout& slotLayout
); );

View File

@ -6,6 +6,7 @@
#include "graphics/core/Font.hpp" #include "graphics/core/Font.hpp"
#include "assets/Assets.hpp" #include "assets/Assets.hpp"
#include "util/stringutil.hpp" #include "util/stringutil.hpp"
#include "../markdown.hpp"
using namespace gui; using namespace gui;
@ -66,6 +67,8 @@ Label::Label(const std::wstring& text, std::string fontName)
cache.update(this->text, multiline, textWrap); cache.update(this->text, multiline, textWrap);
} }
Label::~Label() = default;
glm::vec2 Label::calcSize() { glm::vec2 Label::calcSize() {
auto font = cache.font; auto font = cache.font;
uint lineHeight = font->getLineHeight(); uint lineHeight = font->getLineHeight();
@ -78,11 +81,16 @@ glm::vec2 Label::calcSize() {
); );
} }
void Label::setText(const std::wstring& text) { void Label::setText(std::wstring text) {
if (markup == "md") {
auto [processedText, styles] = markdown::process(text, true);
text = std::move(processedText);
setStyles(std::move(styles));
}
if (text == this->text && !cache.resetFlag) { if (text == this->text && !cache.resetFlag) {
return; return;
} }
this->text = text; this->text = std::move(text);
cache.update(this->text, multiline, textWrap); cache.update(this->text, multiline, textWrap);
if (cache.font && autoresize) { if (cache.font && autoresize) {
@ -156,9 +164,9 @@ uint Label::getLinesNumber() const {
return cache.lines.size(); return cache.lines.size();
} }
void Label::draw(const DrawContext* pctx, Assets* assets) { void Label::draw(const DrawContext& pctx, const Assets& assets) {
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
auto font = assets->get<Font>(fontName); auto font = assets.get<Font>(fontName);
cache.prepare(font, static_cast<size_t>(glm::abs(getSize().x))); cache.prepare(font, static_cast<size_t>(glm::abs(getSize().x)));
if (supplier) { if (supplier) {
@ -201,10 +209,10 @@ void Label::draw(const DrawContext* pctx, Assets* assets) {
if (i < cache.lines.size()-1) { if (i < cache.lines.size()-1) {
view = std::wstring_view(text.c_str()+offset, cache.lines.at(i+1).offset-offset); view = std::wstring_view(text.c_str()+offset, cache.lines.at(i+1).offset-offset);
} }
font->draw(*batch, view, pos.x, pos.y + i * totalLineHeight); font->draw(*batch, view, pos.x, pos.y + i * totalLineHeight, styles.get(), offset);
} }
} else { } else {
font->draw(*batch, text, pos.x, pos.y); font->draw(*batch, text, pos.x, pos.y, styles.get(), 0);
} }
} }
@ -239,3 +247,16 @@ void Label::setTextWrapping(bool flag) {
bool Label::isTextWrapping() const { bool Label::isTextWrapping() const {
return textWrap; return textWrap;
} }
void Label::setMarkup(std::string_view lang) {
markup = lang;
setText(text);
}
const std::string& Label::getMarkup() const {
return markup;
}
void Label::setStyles(std::unique_ptr<FontStylesScheme> styles) {
this->styles = std::move(styles);
}

View File

@ -3,6 +3,7 @@
#include "UINode.hpp" #include "UINode.hpp"
class Font; class Font;
struct FontStylesScheme;
namespace gui { namespace gui {
struct LineScheme { struct LineScheme {
@ -51,11 +52,18 @@ namespace gui {
/// @brief Auto resize label to fit text /// @brief Auto resize label to fit text
bool autoresize = false; bool autoresize = false;
/// @brief Text markup language
std::string markup;
std::unique_ptr<FontStylesScheme> styles;
public: public:
Label(const std::string& text, std::string fontName="normal"); Label(const std::string& text, std::string fontName="normal");
Label(const std::wstring& text, std::string fontName="normal"); Label(const std::wstring& text, std::string fontName="normal");
virtual void setText(const std::wstring& text); virtual ~Label();
virtual void setText(std::wstring text);
const std::wstring& getText() const; const std::wstring& getText() const;
virtual void setFontName(std::string name); virtual void setFontName(std::string name);
@ -95,7 +103,7 @@ namespace gui {
virtual uint getLinesNumber() const; virtual uint getLinesNumber() const;
virtual bool isFakeLine(size_t line) const; virtual bool isFakeLine(size_t line) const;
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void textSupplier(wstringsupplier supplier); virtual void textSupplier(wstringsupplier supplier);
@ -107,5 +115,10 @@ namespace gui {
virtual void setTextWrapping(bool flag); virtual void setTextWrapping(bool flag);
virtual bool isTextWrapping() const; virtual bool isTextWrapping() const;
virtual void setMarkup(std::string_view lang);
virtual const std::string& getMarkup() const;
virtual void setStyles(std::unique_ptr<FontStylesScheme> styles);
}; };
} }

View File

@ -7,7 +7,7 @@
namespace gui { namespace gui {
struct Page { struct Page {
std::string name; std::string name;
std::shared_ptr<UINode> panel = nullptr; std::shared_ptr<UINode> panel;
}; };
using page_loader_func = std::function<std::shared_ptr<UINode>(const std::string& name)>; using page_loader_func = std::function<std::shared_ptr<UINode>(const std::string& name)>;
@ -31,13 +31,13 @@ namespace gui {
/// @param history previous page will not be saved in history if false /// @param history previous page will not be saved in history if false
void setPage(const std::string &name, bool history=true); void setPage(const std::string &name, bool history=true);
void setPage(Page page, bool history=true); void setPage(Page page, bool history=true);
void addPage(const std::string& name, const std::shared_ptr<UINode> &panel); void addPage(const std::string& name, const std::shared_ptr<UINode>& panel);
std::shared_ptr<UINode> fetchPage(const std::string& name); std::shared_ptr<UINode> fetchPage(const std::string& name);
/// @brief Add page supplier used if page is not found /// @brief Add page supplier used if page is not found
/// @param name page name /// @param name page name
/// @param pageSupplier page supplier function /// @param pageSupplier page supplier function
void addSupplier(const std::string &name, const supplier<std::shared_ptr<UINode>> &pageSupplier); void addSupplier(const std::string& name, const supplier<std::shared_ptr<UINode>>& pageSupplier);
/// @brief Page loader is called if accessed page is not found /// @brief Page loader is called if accessed page is not found
void setPageLoader(page_loader_func loader); void setPageLoader(page_loader_func loader);

View File

@ -23,7 +23,7 @@ namespace gui {
virtual void setOrientation(Orientation orientation); virtual void setOrientation(Orientation orientation);
Orientation getOrientation() const; Orientation getOrientation() const;
virtual void add(const std::shared_ptr<UINode> &node) override; virtual void add(const std::shared_ptr<UINode>& node) override;
virtual void refresh() override; virtual void refresh() override;
virtual void fullRefresh() override; virtual void fullRefresh() override;

View File

@ -14,9 +14,9 @@ void Plotter::act(float delta) {
points[index % dmwidth] = std::min(value, dmheight); points[index % dmwidth] = std::min(value, dmheight);
} }
void Plotter::draw(const DrawContext* pctx, Assets* assets) { void Plotter::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->lineWidth(1); batch->lineWidth(1);
for (int i = index+1; i < index+dmwidth; i++) { for (int i = index+1; i < index+dmwidth; i++) {
@ -37,7 +37,7 @@ void Plotter::draw(const DrawContext* pctx, Assets* assets) {
} }
int current_point = static_cast<int>(points[index % dmwidth]); int current_point = static_cast<int>(points[index % dmwidth]);
auto font = assets->get<Font>("normal"); auto font = assets.get<Font>("normal");
for (int y = 0; y < dmheight; y += labelsInterval) { for (int y = 0; y < dmheight; y += labelsInterval) {
std::wstring string; std::wstring string;
if (current_point/16 == y/labelsInterval) { if (current_point/16 == y/labelsInterval) {
@ -47,6 +47,13 @@ void Plotter::draw(const DrawContext* pctx, Assets* assets) {
batch->setColor({1,1,1,0.2f}); batch->setColor({1,1,1,0.2f});
string = util::to_wstring(y / multiplier, 3); string = util::to_wstring(y / multiplier, 3);
} }
font->draw(*batch, string, pos.x+dmwidth+2, pos.y+dmheight-y-labelsInterval); font->draw(
*batch,
string,
pos.x + dmwidth + 2,
pos.y + dmheight - y - labelsInterval,
nullptr,
0
);
} }
} }

View File

@ -29,6 +29,6 @@ namespace gui {
} }
void act(float delta) override; void act(float delta) override;
void draw(const DrawContext* pctx, Assets* assets) override; void draw(const DrawContext& pctx, const Assets& assets) override;
}; };
} }

View File

@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include "Label.hpp" #include "Label.hpp"
#include "devtools/syntax_highlighting.hpp"
#include "graphics/core/DrawContext.hpp" #include "graphics/core/DrawContext.hpp"
#include "graphics/core/Batch2D.hpp" #include "graphics/core/Batch2D.hpp"
#include "graphics/core/Font.hpp" #include "graphics/core/Font.hpp"
@ -12,6 +13,7 @@
#include "util/stringutil.hpp" #include "util/stringutil.hpp"
#include "window/Events.hpp" #include "window/Events.hpp"
#include "window/Window.hpp" #include "window/Window.hpp"
#include "../markdown.hpp"
using namespace gui; using namespace gui;
@ -46,10 +48,10 @@ TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
scrollStep = 0; scrollStep = 0;
} }
void TextBox::draw(const DrawContext* pctx, Assets* assets) { void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
Container::draw(pctx, assets); Container::draw(pctx, assets);
font = assets->get<Font>(label->getFontName()); font = assets.get<Font>(label->getFontName());
if (!isFocused()) { if (!isFocused()) {
return; return;
@ -57,39 +59,67 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
glm::vec2 size = getSize(); glm::vec2 size = getSize();
auto subctx = pctx->sub(); auto subctx = pctx.sub();
subctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y)); subctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y));
const int lineHeight = font->getLineHeight() * label->getLineInterval(); const int lineHeight = font->getLineHeight() * label->getLineInterval();
glm::vec2 lcoord = label->calcPos(); glm::vec2 lcoord = label->calcPos();
lcoord.y -= 2; lcoord.y -= 2;
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(glm::vec4(1.0f));
if (editable && int((Window::time() - caretLastMove) * 2) % 2 == 0) { if (editable && int((Window::time() - caretLastMove) * 2) % 2 == 0) {
uint line = label->getLineByTextIndex(caret); uint line = label->getLineByTextIndex(caret);
uint lcaret = caret - label->getTextLineOffset(line); uint lcaret = caret - label->getTextLineOffset(line);
batch->setColor(glm::vec4(1.0f));
int width = font->calcWidth(input, lcaret); int width = font->calcWidth(input, lcaret);
batch->rect(lcoord.x + width, lcoord.y+label->getLineYOffset(line), 2, lineHeight); batch->rect(
lcoord.x + width,
lcoord.y + label->getLineYOffset(line),
2,
lineHeight
);
} }
if (selectionStart != selectionEnd) { if (selectionStart != selectionEnd) {
auto selectionCtx = subctx.sub(batch);
selectionCtx.setBlendMode(BlendMode::addition);
uint startLine = label->getLineByTextIndex(selectionStart); uint startLine = label->getLineByTextIndex(selectionStart);
uint endLine = label->getLineByTextIndex(selectionEnd); uint endLine = label->getLineByTextIndex(selectionEnd);
batch->setColor(glm::vec4(0.8f, 0.9f, 1.0f, 0.25f)); batch->setColor(glm::vec4(0.8f, 0.9f, 1.0f, 0.25f));
int start = font->calcWidth(input, selectionStart-label->getTextLineOffset(startLine)); int start = font->calcWidth(
int end = font->calcWidth(input, selectionEnd-label->getTextLineOffset(endLine)); input, selectionStart - label->getTextLineOffset(startLine)
);
int end = font->calcWidth(
input, selectionEnd - label->getTextLineOffset(endLine)
);
int lineY = label->getLineYOffset(startLine); int lineY = label->getLineYOffset(startLine);
if (startLine == endLine) { if (startLine == endLine) {
batch->rect(lcoord.x + start, lcoord.y+lineY, end-start, lineHeight); batch->rect(
lcoord.x + start, lcoord.y + lineY, end - start, lineHeight
);
} else { } else {
batch->rect(lcoord.x + start, lcoord.y+lineY, label->getSize().x-start-padding.z-padding.x-2, lineHeight); batch->rect(
for (uint i = startLine+1; i < endLine; i++) { lcoord.x + start,
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(i), label->getSize().x-padding.z-padding.x-2, lineHeight); lcoord.y + lineY,
label->getSize().x - start - padding.z - padding.x - 2,
lineHeight
);
for (uint i = startLine + 1; i < endLine; i++) {
batch->rect(
lcoord.x,
lcoord.y + label->getLineYOffset(i),
label->getSize().x - padding.z - padding.x - 2,
lineHeight
);
} }
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(endLine), end, lineHeight); batch->rect(
lcoord.x,
lcoord.y + label->getLineYOffset(endLine),
end,
lineHeight
);
} }
} }
@ -132,13 +162,13 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) {
} }
} }
void TextBox::drawBackground(const DrawContext* pctx, Assets*) { void TextBox::drawBackground(const DrawContext& pctx, const Assets&) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
auto subctx = pctx->sub(); auto subctx = pctx.sub();
subctx.setScissors(glm::vec4(pos.x, pos.y-0.5, size.x, size.y+1)); subctx.setScissors(glm::vec4(pos.x, pos.y-0.5, size.x, size.y+1));
if (valid) { if (valid) {
@ -162,7 +192,18 @@ void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
void TextBox::refreshLabel() { void TextBox::refreshLabel() {
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f)); label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
label->setText(input.empty() && !hint.empty() ? hint : getText());
const auto& displayText = input.empty() && !hint.empty() ? hint : getText();
if (markup == "md") {
auto [processedText, styles] = markdown::process(displayText, !focused);
label->setText(std::move(processedText));
label->setStyles(std::move(styles));
} else {
label->setText(displayText);
if (syntax.empty()) {
label->setStyles(nullptr);
}
}
if (showLineNumbers) { if (showLineNumbers) {
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) { if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
@ -202,7 +243,8 @@ void TextBox::refreshLabel() {
if (multiline && font) { if (multiline && font) {
setScrollable(true); setScrollable(true);
uint height = label->getLinesNumber() * font->getLineHeight() * label->getLineInterval(); uint height = label->getLinesNumber() * font->getLineHeight() *
label->getLineInterval();
label->setSize(glm::vec2(label->getSize().x, height)); label->setSize(glm::vec2(label->getSize().x, height));
actualLength = height; actualLength = height;
} else { } else {
@ -388,7 +430,7 @@ int TextBox::calcIndexAt(int x, int y) const {
return std::min(offset+label->getTextLineOffset(line), input.length()); return std::min(offset+label->getTextLineOffset(line), input.length());
} }
inline std::wstring get_alphabet(wchar_t c) { static inline std::wstring get_alphabet(wchar_t c) {
std::wstring alphabet {c}; std::wstring alphabet {c};
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') { if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') {
return L"abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; return L"abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@ -433,7 +475,11 @@ void TextBox::click(GUI*, int x, int y) {
selectionOrigin = index; selectionOrigin = index;
} }
void TextBox::mouseMove(GUI*, int x, int y) { void TextBox::mouseMove(GUI* gui, int x, int y) {
Container::mouseMove(gui, x, y);
if (isScrolling()) {
return;
}
ptrdiff_t index = calcIndexAt(x, y); ptrdiff_t index = calcIndexAt(x, y);
setCaret(index); setCaret(index);
extendSelection(index); extendSelection(index);
@ -529,10 +575,21 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) {
} }
} }
void TextBox::refreshSyntax() {
if (!syntax.empty()) {
if (auto styles = devtools::syntax_highlight(
syntax, util::wstr2str_utf8(input)
)) {
label->setStyles(std::move(styles));
}
}
}
void TextBox::onInput() { void TextBox::onInput() {
if (subconsumer) { if (subconsumer) {
subconsumer(input); subconsumer(input);
} }
refreshSyntax();
} }
void TextBox::performEditingKeyboardEvents(keycode key) { void TextBox::performEditingKeyboardEvents(keycode key) {
@ -632,7 +689,9 @@ size_t TextBox::getLinePos(uint line) const {
return label->getTextLineOffset(line); return label->getTextLineOffset(line);
} }
std::shared_ptr<UINode> TextBox::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) { std::shared_ptr<UINode> TextBox::getAt(
const glm::vec2& pos, const std::shared_ptr<UINode>& self
) {
return UINode::getAt(pos, self); return UINode::getAt(pos, self);
} }
@ -710,6 +769,7 @@ const std::wstring& TextBox::getText() const {
void TextBox::setText(const std::wstring& value) { void TextBox::setText(const std::wstring& value) {
this->input = value; this->input = value;
input.erase(std::remove(input.begin(), input.end(), '\r'), input.end()); input.erase(std::remove(input.begin(), input.end(), '\r'), input.end());
refreshSyntax();
} }
const std::wstring& TextBox::getPlaceholder() const { const std::wstring& TextBox::getPlaceholder() const {
@ -789,3 +849,24 @@ void TextBox::setShowLineNumbers(bool flag) {
bool TextBox::isShowLineNumbers() const { bool TextBox::isShowLineNumbers() const {
return showLineNumbers; return showLineNumbers;
} }
void TextBox::setSyntax(std::string_view lang) {
syntax = lang;
if (syntax.empty()) {
label->setStyles(nullptr);
} else {
refreshSyntax();
}
}
const std::string& TextBox::getSyntax() const {
return syntax;
}
void TextBox::setMarkup(std::string_view lang) {
markup = lang;
}
const std::string& TextBox::getMarkup() const {
return markup;
}

View File

@ -56,6 +56,8 @@ namespace gui {
bool editable = true; bool editable = true;
bool autoresize = false; bool autoresize = false;
bool showLineNumbers = false; bool showLineNumbers = false;
std::string markup;
std::string syntax;
void stepLeft(bool shiftPressed, bool breakSelection); void stepLeft(bool shiftPressed, bool breakSelection);
void stepRight(bool shiftPressed, bool breakSelection); void stepRight(bool shiftPressed, bool breakSelection);
@ -84,6 +86,8 @@ namespace gui {
void refreshLabel(); void refreshLabel();
void onInput(); void onInput();
void refreshSyntax();
public: public:
TextBox( TextBox(
std::wstring placeholder, std::wstring placeholder,
@ -212,12 +216,20 @@ namespace gui {
virtual void click(GUI*, int, int) override; virtual void click(GUI*, int, int) override;
virtual void mouseMove(GUI*, int x, int y) override; virtual void mouseMove(GUI*, int x, int y) override;
virtual bool isFocuskeeper() const override {return true;} virtual bool isFocuskeeper() const override {return true;}
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void drawBackground(const DrawContext* pctx, Assets* assets) override; virtual void drawBackground(const DrawContext& pctx, const Assets& assets) override;
virtual void typed(unsigned int codepoint) override; virtual void typed(unsigned int codepoint) override;
virtual void keyPressed(keycode key) override; virtual void keyPressed(keycode key) override;
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override; virtual std::shared_ptr<UINode> getAt(
const glm::vec2& pos, const std::shared_ptr<UINode>& self
) override;
virtual void setOnUpPressed(const runnable &callback); virtual void setOnUpPressed(const runnable &callback);
virtual void setOnDownPressed(const runnable &callback); virtual void setOnDownPressed(const runnable &callback);
virtual void setSyntax(std::string_view lang);
virtual const std::string& getSyntax() const;
virtual void setMarkup(std::string_view lang);
virtual const std::string& getMarkup() const;
}; };
} }

View File

@ -25,12 +25,12 @@ TrackBar::TrackBar(
setHoverColor(glm::vec4(0.01f, 0.02f, 0.03f, 0.5f)); setHoverColor(glm::vec4(0.01f, 0.02f, 0.03f, 0.5f));
} }
void TrackBar::draw(const DrawContext* pctx, Assets*) { void TrackBar::draw(const DrawContext& pctx, const Assets&) {
if (supplier) { if (supplier) {
value = supplier(); value = supplier();
} }
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(hover ? hoverColor : color); batch->setColor(hover ? hoverColor : color);
batch->rect(pos.x, pos.y, size.x, size.y); batch->rect(pos.x, pos.y, size.x, size.y);

View File

@ -21,7 +21,7 @@ namespace gui {
double value, double value,
double step=1.0, double step=1.0,
int trackWidth=12); int trackWidth=12);
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void setSupplier(doublesupplier); virtual void setSupplier(doublesupplier);
virtual void setConsumer(doubleconsumer); virtual void setConsumer(doubleconsumer);

View File

@ -111,11 +111,11 @@ bool UINode::isInside(glm::vec2 point) {
point.x < pos.x + size.x && point.y < pos.y + size.y); point.x < pos.x + size.x && point.y < pos.y + size.y);
} }
std::shared_ptr<UINode> UINode::getAt(glm::vec2 point, std::shared_ptr<UINode> self) { std::shared_ptr<UINode> UINode::getAt(const glm::vec2& point, const std::shared_ptr<UINode>& self) {
if (!isInteractive() || !enabled) { if (!isInteractive() || !enabled) {
return nullptr; return nullptr;
} }
return isInside(point) ? std::move(self) : nullptr; return isInside(point) ? self : nullptr;
} }
bool UINode::isInteractive() const { bool UINode::isInteractive() const {

View File

@ -120,7 +120,7 @@ namespace gui {
/// @brief Called every frame for all visible elements /// @brief Called every frame for all visible elements
/// @param delta delta timУ /// @param delta delta timУ
virtual void act(float delta) {}; virtual void act(float delta) {};
virtual void draw(const DrawContext* pctx, Assets* assets) = 0; virtual void draw(const DrawContext& pctx, const Assets& assets) = 0;
virtual void setVisible(bool flag); virtual void setVisible(bool flag);
bool isVisible() const; bool isVisible() const;
@ -190,7 +190,7 @@ namespace gui {
/// @param pos cursor screen position /// @param pos cursor screen position
/// @param self shared pointer to element /// @param self shared pointer to element
/// @return self, sub-element or nullptr if element is not interractive /// @return self, sub-element or nullptr if element is not interractive
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self); virtual std::shared_ptr<UINode> getAt(const glm::vec2& pos, const std::shared_ptr<UINode>& self);
/// @brief Check if element is opaque for cursor /// @brief Check if element is opaque for cursor
virtual bool isInteractive() const; virtual bool isInteractive() const;

View File

@ -56,8 +56,8 @@ static runnable create_runnable(
const xml::xmlelement& element, const xml::xmlelement& element,
const std::string& name const std::string& name
) { ) {
if (element->has(name)) { if (element.has(name)) {
std::string text = element->attr(name).getText(); std::string text = element.attr(name).getText();
if (!text.empty()) { if (!text.empty()) {
return scripting::create_runnable( return scripting::create_runnable(
reader.getEnvironment(), text, reader.getFilename() reader.getEnvironment(), text, reader.getFilename()
@ -83,78 +83,78 @@ static onaction create_action(
static void _readUINode( static void _readUINode(
const UiXmlReader& reader, const xml::xmlelement& element, UINode& node const UiXmlReader& reader, const xml::xmlelement& element, UINode& node
) { ) {
if (element->has("id")) { if (element.has("id")) {
node.setId(element->attr("id").getText()); node.setId(element.attr("id").getText());
} }
if (element->has("pos")) { if (element.has("pos")) {
node.setPos(element->attr("pos").asVec2()); node.setPos(element.attr("pos").asVec2());
} }
if (element->has("size")) { if (element.has("size")) {
node.setSize(element->attr("size").asVec2()); node.setSize(element.attr("size").asVec2());
} }
if (element->has("color")) { if (element.has("color")) {
glm::vec4 color = element->attr("color").asColor(); glm::vec4 color = element.attr("color").asColor();
glm::vec4 hoverColor = color; glm::vec4 hoverColor = color;
glm::vec4 pressedColor = color; glm::vec4 pressedColor = color;
if (element->has("hover-color")) { if (element.has("hover-color")) {
hoverColor = node.getHoverColor(); hoverColor = node.getHoverColor();
} }
if (element->has("pressed-color")) { if (element.has("pressed-color")) {
pressedColor = node.getPressedColor(); pressedColor = node.getPressedColor();
} }
node.setColor(color); node.setColor(color);
node.setHoverColor(hoverColor); node.setHoverColor(hoverColor);
node.setPressedColor(pressedColor); node.setPressedColor(pressedColor);
} }
if (element->has("margin")) { if (element.has("margin")) {
node.setMargin(element->attr("margin").asVec4()); node.setMargin(element.attr("margin").asVec4());
} }
if (element->has("z-index")) { if (element.has("z-index")) {
node.setZIndex(element->attr("z-index").asInt()); node.setZIndex(element.attr("z-index").asInt());
} }
if (element->has("interactive")) { if (element.has("interactive")) {
node.setInteractive(element->attr("interactive").asBool()); node.setInteractive(element.attr("interactive").asBool());
} }
if (element->has("visible")) { if (element.has("visible")) {
node.setVisible(element->attr("visible").asBool()); node.setVisible(element.attr("visible").asBool());
} }
if (element->has("enabled")) { if (element.has("enabled")) {
node.setEnabled(element->attr("enabled").asBool()); node.setEnabled(element.attr("enabled").asBool());
} }
if (element->has("position-func")) { if (element.has("position-func")) {
node.setPositionFunc(scripting::create_vec2_supplier( node.setPositionFunc(scripting::create_vec2_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("position-func").getText(), element.attr("position-func").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("size-func")) { if (element.has("size-func")) {
node.setSizeFunc(scripting::create_vec2_supplier( node.setSizeFunc(scripting::create_vec2_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("size-func").getText(), element.attr("size-func").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("hover-color")) { if (element.has("hover-color")) {
node.setHoverColor(element->attr("hover-color").asColor()); node.setHoverColor(element.attr("hover-color").asColor());
} }
if (element->has("pressed-color")) { if (element.has("pressed-color")) {
node.setPressedColor(element->attr("pressed-color").asColor()); node.setPressedColor(element.attr("pressed-color").asColor());
} }
std::string alignName = element->attr("align", "").getText(); std::string alignName = element.attr("align", "").getText();
node.setAlign(align_from_string(alignName, node.getAlign())); node.setAlign(align_from_string(alignName, node.getAlign()));
if (element->has("gravity")) { if (element.has("gravity")) {
node.setGravity(gravity_from_string( node.setGravity(gravity_from_string(
element->attr("gravity").getText() element.attr("gravity").getText()
)); ));
} }
if (element->has("tooltip")) { if (element.has("tooltip")) {
node.setTooltip(util::str2wstr_utf8(element->attr("tooltip").getText())); node.setTooltip(util::str2wstr_utf8(element.attr("tooltip").getText()));
} }
if (element->has("tooltip-delay")) { if (element.has("tooltip-delay")) {
node.setTooltipDelay(element->attr("tooltip-delay").asFloat()); node.setTooltipDelay(element.attr("tooltip-delay").asFloat());
} }
if (auto onclick = create_action(reader, element, "onclick")) { if (auto onclick = create_action(reader, element, "onclick")) {
@ -169,16 +169,16 @@ static void _readUINode(
static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element, Container& container) { static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element, Container& container) {
_readUINode(reader, element, container); _readUINode(reader, element, container);
if (element->has("scrollable")) { if (element.has("scrollable")) {
container.setScrollable(element->attr("scrollable").asBool()); container.setScrollable(element.attr("scrollable").asBool());
} }
if (element->has("scroll-step")) { if (element.has("scroll-step")) {
container.setScrollStep(element->attr("scroll-step").asInt()); container.setScrollStep(element.attr("scroll-step").asInt());
} }
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->isText()) if (sub->isText())
continue; continue;
auto subnode = reader.readUINode(sub); auto subnode = reader.readUINode(*sub);
if (subnode) { if (subnode) {
container.add(subnode); container.add(subnode);
} }
@ -189,15 +189,17 @@ void UiXmlReader::readUINode(UiXmlReader& reader, const xml::xmlelement& element
_readContainer(reader, element, container); _readContainer(reader, element, container);
} }
void UiXmlReader::readUINode(UiXmlReader& reader, const xml::xmlelement& element, UINode& node) { void UiXmlReader::readUINode(
const UiXmlReader& reader, const xml::xmlelement& element, UINode& node
) {
_readUINode(reader, element, node); _readUINode(reader, element, node);
} }
static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Panel& panel, bool subnodes=true) { static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Panel& panel, bool subnodes=true) {
_readUINode(reader, element, panel); _readUINode(reader, element, panel);
if (element->has("padding")) { if (element.has("padding")) {
glm::vec4 padding = element->attr("padding").asVec4(); glm::vec4 padding = element.attr("padding").asVec4();
panel.setPadding(padding); panel.setPadding(padding);
glm::vec2 size = panel.getSize(); glm::vec2 size = panel.getSize();
panel.setSize(glm::vec2( panel.setSize(glm::vec2(
@ -205,23 +207,23 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane
size.y + padding.y + padding.w size.y + padding.y + padding.w
)); ));
} }
if (element->has("size")) { if (element.has("size")) {
panel.setResizing(false); panel.setResizing(false);
} }
if (element->has("max-length")) { if (element.has("max-length")) {
panel.setMaxLength(element->attr("max-length").asInt()); panel.setMaxLength(element.attr("max-length").asInt());
} }
if (element->has("orientation")) { if (element.has("orientation")) {
auto &oname = element->attr("orientation").getText(); auto &oname = element.attr("orientation").getText();
if (oname == "horizontal") { if (oname == "horizontal") {
panel.setOrientation(Orientation::horizontal); panel.setOrientation(Orientation::horizontal);
} }
} }
if (subnodes) { if (subnodes) {
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->isText()) if (sub->isText())
continue; continue;
auto subnode = reader.readUINode(sub); auto subnode = reader.readUINode(*sub);
if (subnode) { if (subnode) {
panel.add(subnode); panel.add(subnode);
} }
@ -231,8 +233,8 @@ 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 readAndProcessInnerText(const xml::xmlelement& element, const std::string& context) {
std::wstring text = L""; std::wstring text = L"";
if (element->size() == 1) { if (element.size() == 1) {
std::string source = element->sub(0)->attr("#").getText(); std::string source = element.sub(0).attr("#").getText();
util::trim(source); util::trim(source);
text = util::str2wstr_utf8(source); text = util::str2wstr_utf8(source);
if (text[0] == '@') { if (text[0] == '@') {
@ -246,33 +248,38 @@ static std::wstring readAndProcessInnerText(const xml::xmlelement& element, cons
return text; return text;
} }
static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readLabel(
const UiXmlReader& reader, const xml::xmlelement& element
) {
std::wstring text = readAndProcessInnerText(element, reader.getContext()); std::wstring text = readAndProcessInnerText(element, reader.getContext());
auto label = std::make_shared<Label>(text); auto label = std::make_shared<Label>(text);
_readUINode(reader, element, *label); _readUINode(reader, element, *label);
if (element->has("valign")) { if (element.has("valign")) {
label->setVerticalAlign( label->setVerticalAlign(
align_from_string(element->attr("valign").getText(), label->getVerticalAlign()) align_from_string(element.attr("valign").getText(), label->getVerticalAlign())
); );
} }
if (element->has("supplier")) { if (element.has("supplier")) {
label->textSupplier(scripting::create_wstring_supplier( label->textSupplier(scripting::create_wstring_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("autoresize")) { if (element.has("autoresize")) {
label->setAutoResize(element->attr("autoresize").asBool()); label->setAutoResize(element.attr("autoresize").asBool());
} }
if (element->has("multiline")) { if (element.has("multiline")) {
label->setMultiline(element->attr("multiline").asBool()); label->setMultiline(element.attr("multiline").asBool());
if (!element->has("valign")) { if (!element.has("valign")) {
label->setVerticalAlign(Align::top); label->setVerticalAlign(Align::top);
} }
} }
if (element->has("text-wrap")) { if (element.has("text-wrap")) {
label->setTextWrapping(element->attr("text-wrap").asBool()); label->setTextWrapping(element.attr("text-wrap").asBool());
}
if (element.has("markup")) {
label->setMarkup(element.attr("markup").getText());
} }
return label; return label;
} }
@ -284,19 +291,19 @@ static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, const xml::xml
} }
static std::shared_ptr<UINode> readPanel(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readPanel(UiXmlReader& reader, const xml::xmlelement& element) {
float interval = element->attr("interval", "2").asFloat(); float interval = element.attr("interval", "2").asFloat();
auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval); auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval);
_readPanel(reader, element, *panel); _readPanel(reader, element, *panel);
return panel; return panel;
} }
static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlelement& element) {
glm::vec4 padding = element->attr("padding", "10").asVec4(); glm::vec4 padding = element.attr("padding", "10").asVec4();
std::shared_ptr<Button> button; std::shared_ptr<Button> button;
auto& elements = element->getElements(); auto& elements = element.getElements();
if (!elements.empty() && elements[0]->getTag() != "#") { if (!elements.empty() && elements[0]->getTag() != "#") {
auto inner = reader.readUINode(element->getElements().at(0)); auto inner = reader.readUINode(*elements.at(0));
if (inner != nullptr) { if (inner != nullptr) {
button = std::make_shared<Button>(inner, padding); button = std::make_shared<Button>(inner, padding);
} else { } else {
@ -308,30 +315,32 @@ static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlele
button = std::make_shared<Button>(text, padding, nullptr); button = std::make_shared<Button>(text, padding, nullptr);
_readPanel(reader, element, *button, true); _readPanel(reader, element, *button, true);
} }
if (element->has("text-align")) { if (element.has("text-align")) {
button->setTextAlign(align_from_string(element->attr("text-align").getText(), button->getTextAlign())); button->setTextAlign(align_from_string(
element.attr("text-align").getText(), button->getTextAlign()
));
} }
return button; return button;
} }
static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto text = readAndProcessInnerText(element, reader.getContext()); auto text = readAndProcessInnerText(element, reader.getContext());
bool checked = element->attr("checked", "false").asBool(); bool checked = element.attr("checked", "false").asBool();
auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(32), checked); auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(32), checked);
_readPanel(reader, element, *checkbox); _readPanel(reader, element, *checkbox);
if (element->has("consumer")) { if (element.has("consumer")) {
checkbox->setConsumer(scripting::create_bool_consumer( checkbox->setConsumer(scripting::create_bool_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("consumer").getText(), element.attr("consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
checkbox->setSupplier(scripting::create_bool_supplier( checkbox->setSupplier(scripting::create_bool_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
@ -339,15 +348,15 @@ static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmle
} }
static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto placeholder = util::str2wstr_utf8(element->attr("placeholder", "").getText()); auto placeholder = util::str2wstr_utf8(element.attr("placeholder", "").getText());
auto hint = util::str2wstr_utf8(element->attr("hint", "").getText()); auto hint = util::str2wstr_utf8(element.attr("hint", "").getText());
auto text = readAndProcessInnerText(element, reader.getContext()); auto text = readAndProcessInnerText(element, reader.getContext());
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f)); auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
textbox->setHint(hint); textbox->setHint(hint);
_readContainer(reader, element, *textbox); _readContainer(reader, element, *textbox);
if (element->has("padding")) { if (element.has("padding")) {
glm::vec4 padding = element->attr("padding").asVec4(); glm::vec4 padding = element.attr("padding").asVec4();
textbox->setPadding(padding); textbox->setPadding(padding);
glm::vec2 size = textbox->getSize(); glm::vec2 size = textbox->getSize();
textbox->setSize(glm::vec2( textbox->setSize(glm::vec2(
@ -357,55 +366,61 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
} }
textbox->setText(text); textbox->setText(text);
if (element->has("multiline")) { if (element.has("syntax")) {
textbox->setMultiline(element->attr("multiline").asBool()); textbox->setSyntax(element.attr("syntax").getText());
} }
if (element->has("text-wrap")) { if (element.has("multiline")) {
textbox->setTextWrapping(element->attr("text-wrap").asBool()); textbox->setMultiline(element.attr("multiline").asBool());
} }
if (element->has("editable")) { if (element.has("text-wrap")) {
textbox->setEditable(element->attr("editable").asBool()); textbox->setTextWrapping(element.attr("text-wrap").asBool());
} }
if (element->has("autoresize")) { if (element.has("editable")) {
textbox->setAutoResize(element->attr("autoresize").asBool()); textbox->setEditable(element.attr("editable").asBool());
} }
if (element->has("line-numbers")) { if (element.has("autoresize")) {
textbox->setShowLineNumbers(element->attr("line-numbers").asBool()); textbox->setAutoResize(element.attr("autoresize").asBool());
} }
if (element->has("consumer")) { if (element.has("line-numbers")) {
textbox->setShowLineNumbers(element.attr("line-numbers").asBool());
}
if (element.has("markup")) {
textbox->setMarkup(element.attr("markup").getText());
}
if (element.has("consumer")) {
textbox->setTextConsumer(scripting::create_wstring_consumer( textbox->setTextConsumer(scripting::create_wstring_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("consumer").getText(), element.attr("consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("sub-consumer")) { if (element.has("sub-consumer")) {
textbox->setTextSubConsumer(scripting::create_wstring_consumer( textbox->setTextSubConsumer(scripting::create_wstring_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("sub-consumer").getText(), element.attr("sub-consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
textbox->setTextSupplier(scripting::create_wstring_supplier( textbox->setTextSupplier(scripting::create_wstring_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("focused-color")) { if (element.has("focused-color")) {
textbox->setFocusedColor(element->attr("focused-color").asColor()); textbox->setFocusedColor(element.attr("focused-color").asColor());
} }
if (element->has("error-color")) { if (element.has("error-color")) {
textbox->setErrorColor(element->attr("error-color").asColor()); textbox->setErrorColor(element.attr("error-color").asColor());
} }
if (element->has("text-color")) { if (element.has("text-color")) {
textbox->setTextColor(element->attr("text-color").asColor()); textbox->setTextColor(element.attr("text-color").asColor());
} }
if (element->has("validator")) { if (element.has("validator")) {
textbox->setTextValidator(scripting::create_wstring_validator( textbox->setTextValidator(scripting::create_wstring_validator(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("validator").getText(), element.attr("validator").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
@ -418,61 +433,70 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
return textbox; return textbox;
} }
static std::shared_ptr<UINode> readImage(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readImage(
std::string src = element->attr("src", "").getText(); const UiXmlReader& reader, const xml::xmlelement& element
) {
std::string src = element.attr("src", "").getText();
auto image = std::make_shared<Image>(src); auto image = std::make_shared<Image>(src);
_readUINode(reader, element, *image); _readUINode(reader, element, *image);
return image; return image;
} }
static std::shared_ptr<UINode> readTrackBar(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readTrackBar(
const UiXmlReader& reader, const xml::xmlelement& element
) {
const auto& env = reader.getEnvironment(); const auto& env = reader.getEnvironment();
const auto& file = reader.getFilename(); const auto& file = reader.getFilename();
float minv = element->attr("min", "0.0").asFloat(); float minv = element.attr("min", "0.0").asFloat();
float maxv = element->attr("max", "1.0").asFloat(); float maxv = element.attr("max", "1.0").asFloat();
float def = element->attr("value", "0.0").asFloat(); float def = element.attr("value", "0.0").asFloat();
float step = element->attr("step", "1.0").asFloat(); float step = element.attr("step", "1.0").asFloat();
int trackWidth = element->attr("track-width", "12").asInt(); int trackWidth = element.attr("track-width", "12").asInt();
auto bar = std::make_shared<TrackBar>(minv, maxv, def, step, trackWidth); auto bar = std::make_shared<TrackBar>(minv, maxv, def, step, trackWidth);
_readUINode(reader, element, *bar); _readUINode(reader, element, *bar);
if (element->has("consumer")) { if (element.has("consumer")) {
bar->setConsumer(scripting::create_number_consumer( bar->setConsumer(scripting::create_number_consumer(
env, element->attr("consumer").getText(), file)); env, element.attr("consumer").getText(), file));
} }
if (element->has("sub-consumer")) { if (element.has("sub-consumer")) {
bar->setSubConsumer(scripting::create_number_consumer( bar->setSubConsumer(scripting::create_number_consumer(
env, element->attr("sub-consumer").getText(), file)); env, element.attr("sub-consumer").getText(), file));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
bar->setSupplier(scripting::create_number_supplier( bar->setSupplier(scripting::create_number_supplier(
env, element->attr("supplier").getText(), file)); env, element.attr("supplier").getText(), file));
} }
if (element->has("track-color")) { if (element.has("track-color")) {
bar->setTrackColor(element->attr("track-color").asColor()); bar->setTrackColor(element.attr("track-color").asColor());
} }
if (element->has("change-on-release")) { if (element.has("change-on-release")) {
bar->setChangeOnRelease(element->attr("change-on-release").asBool()); bar->setChangeOnRelease(element.attr("change-on-release").asBool());
} }
return bar; return bar;
} }
static std::shared_ptr<UINode> readInputBindBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readInputBindBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto bindname = element->attr("binding").getText(); auto bindname = element.attr("binding").getText();
auto found = Events::bindings.find(bindname); auto found = Events::bindings.find(bindname);
if (found == Events::bindings.end()) { if (found == Events::bindings.end()) {
throw std::runtime_error("binding does not exists "+util::quote(bindname)); throw std::runtime_error("binding does not exists "+util::quote(bindname));
} }
glm::vec4 padding = element->attr("padding", "6").asVec4(); glm::vec4 padding = element.attr("padding", "6").asVec4();
auto bindbox = std::make_shared<InputBindBox>(found->second, padding); auto bindbox = std::make_shared<InputBindBox>(found->second, padding);
_readPanel(reader, element, *bindbox); _readPanel(reader, element, *bindbox);
return bindbox; return bindbox;
} }
static slotcallback readSlotFunc(InventoryView* view, UiXmlReader& reader, xml::xmlelement& element, const std::string& attr) { static slotcallback readSlotFunc(
InventoryView* view,
const UiXmlReader& reader,
const xml::xmlelement& element,
const std::string& attr
) {
auto consumer = scripting::create_int_array_consumer( auto consumer = scripting::create_int_array_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr(attr).getText() element.attr(attr).getText()
); );
return [=](uint slot, ItemStack&) { return [=](uint slot, ItemStack&) {
int args[] {int(view->getInventory()->getId()), int(slot)}; int args[] {int(view->getInventory()->getId()), int(slot)};
@ -480,22 +504,24 @@ static slotcallback readSlotFunc(InventoryView* view, UiXmlReader& reader, xml::
}; };
} }
static void readSlot(InventoryView* view, UiXmlReader& reader, xml::xmlelement element) { static void readSlot(
int index = element->attr("index", "0").asInt(); InventoryView* view, UiXmlReader& reader, const xml::xmlelement& element
bool itemSource = element->attr("item-source", "false").asBool(); ) {
bool taking = element->attr("taking", "true").asBool(); int index = element.attr("index", "0").asInt();
bool placing = element->attr("placing", "true").asBool(); bool itemSource = element.attr("item-source", "false").asBool();
bool taking = element.attr("taking", "true").asBool();
bool placing = element.attr("placing", "true").asBool();
SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr); SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr);
if (element->has("pos")) { if (element.has("pos")) {
layout.position = element->attr("pos").asVec2(); layout.position = element.attr("pos").asVec2();
} }
if (element->has("updatefunc")) { if (element.has("updatefunc")) {
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc"); layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
} }
if (element->has("sharefunc")) { if (element.has("sharefunc")) {
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc"); layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
} }
if (element->has("onrightclick")) { if (element.has("onrightclick")) {
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick"); layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
} }
layout.taking = taking; layout.taking = taking;
@ -505,19 +531,21 @@ static void readSlot(InventoryView* view, UiXmlReader& reader, xml::xmlelement e
view->add(slot); view->add(slot);
} }
static void readSlotsGrid(InventoryView* view, UiXmlReader& reader, xml::xmlelement element) { static void readSlotsGrid(
int startIndex = element->attr("start-index", "0").asInt(); InventoryView* view, const UiXmlReader& reader, const xml::xmlelement& element
int rows = element->attr("rows", "0").asInt(); ) {
int cols = element->attr("cols", "0").asInt(); int startIndex = element.attr("start-index", "0").asInt();
int count = element->attr("count", "0").asInt(); int rows = element.attr("rows", "0").asInt();
int cols = element.attr("cols", "0").asInt();
int count = element.attr("count", "0").asInt();
const int slotSize = InventoryView::SLOT_SIZE; const int slotSize = InventoryView::SLOT_SIZE;
bool taking = element->attr("taking", "true").asBool(); bool taking = element.attr("taking", "true").asBool();
bool placing = element->attr("placing", "true").asBool(); bool placing = element.attr("placing", "true").asBool();
int interval = element->attr("interval", "-1").asInt(); int interval = element.attr("interval", "-1").asInt();
if (interval < 0) { if (interval < 0) {
interval = InventoryView::SLOT_INTERVAL; interval = InventoryView::SLOT_INTERVAL;
} }
int padding = element->attr("padding", "-1").asInt(); int padding = element.attr("padding", "-1").asInt();
if (padding < 0) { if (padding < 0) {
padding = interval; padding = interval;
} }
@ -528,18 +556,18 @@ static void readSlotsGrid(InventoryView* view, UiXmlReader& reader, xml::xmlelem
} else if (count == 0) { } else if (count == 0) {
count = rows * cols; count = rows * cols;
} }
bool itemSource = element->attr("item-source", "false").asBool(); bool itemSource = element.attr("item-source", "false").asBool();
SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr); SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr);
if (element->has("pos")) { if (element.has("pos")) {
layout.position = element->attr("pos").asVec2(); layout.position = element.attr("pos").asVec2();
} }
if (element->has("updatefunc")) { if (element.has("updatefunc")) {
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc"); layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
} }
if (element->has("sharefunc")) { if (element.has("sharefunc")) {
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc"); layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
} }
if (element->has("onrightclick")) { if (element.has("onrightclick")) {
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick"); layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
} }
layout.padding = padding; layout.padding = padding;
@ -571,11 +599,11 @@ static std::shared_ptr<UINode> readInventory(UiXmlReader& reader, const xml::xml
reader.addIgnore("slots-grid"); reader.addIgnore("slots-grid");
reader.readUINode(reader, element, *view); reader.readUINode(reader, element, *view);
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->getTag() == "slot") { if (sub->getTag() == "slot") {
readSlot(view.get(), reader, sub); readSlot(view.get(), reader, *sub);
} else if (sub->getTag() == "slots-grid") { } else if (sub->getTag() == "slots-grid") {
readSlotsGrid(view.get(), reader, sub); readSlotsGrid(view.get(), reader, *sub);
} }
} }
return view; return view;
@ -618,18 +646,18 @@ void UiXmlReader::addIgnore(const std::string& tag) {
} }
std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element) { std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element) {
if (element->has("if")) { if (element.has("if")) {
const auto& cond = element->attr("if").getText(); const auto& cond = element.attr("if").getText();
if (cond.empty() || cond == "false" || cond == "nil") if (cond.empty() || cond == "false" || cond == "nil")
return nullptr; return nullptr;
} }
if (element->has("ifnot")) { if (element.has("ifnot")) {
const auto& cond = element->attr("ifnot").getText(); const auto& cond = element.attr("ifnot").getText();
if (!(cond.empty() || cond == "false" || cond == "nil")) if (!(cond.empty() || cond == "false" || cond == "nil"))
return nullptr; return nullptr;
} }
const std::string& tag = element->getTag(); const std::string& tag = element.getTag();
auto found = readers.find(tag); auto found = readers.find(tag);
if (found == readers.end()) { if (found == readers.end()) {
if (ignored.find(tag) != ignored.end()) { if (ignored.find(tag) != ignored.end()) {
@ -638,9 +666,9 @@ std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element)
throw std::runtime_error("unsupported element '"+tag+"'"); throw std::runtime_error("unsupported element '"+tag+"'");
} }
bool hascontext = element->has("context"); bool hascontext = element.has("context");
if (hascontext) { if (hascontext) {
contextStack.push(element->attr("context").getText()); contextStack.push(element.attr("context").getText());
} }
auto node = found->second(*this, element); auto node = found->second(*this, element);
if (hascontext) { if (hascontext) {
@ -655,8 +683,7 @@ std::shared_ptr<UINode> UiXmlReader::readXML(
) { ) {
this->filename = filename; this->filename = filename;
auto document = xml::parse(filename, source); auto document = xml::parse(filename, source);
auto root = document->getRoot(); return readUINode(*document->getRoot());
return readUINode(root);
} }
std::shared_ptr<UINode> UiXmlReader::readXML( std::shared_ptr<UINode> UiXmlReader::readXML(

View File

@ -11,7 +11,8 @@
namespace gui { namespace gui {
class UiXmlReader; class UiXmlReader;
using uinode_reader = std::function<std::shared_ptr<UINode>(UiXmlReader&, xml::xmlelement)>; using uinode_reader = std::function<
std::shared_ptr<UINode>(UiXmlReader&, const xml::xmlelement&)>;
class UiXmlReader { class UiXmlReader {
std::unordered_map<std::string, uinode_reader> readers; std::unordered_map<std::string, uinode_reader> readers;
@ -29,7 +30,7 @@ namespace gui {
std::shared_ptr<UINode> readUINode(const xml::xmlelement& element); std::shared_ptr<UINode> readUINode(const xml::xmlelement& element);
void readUINode( void readUINode(
UiXmlReader& reader, const UiXmlReader& reader,
const xml::xmlelement& element, const xml::xmlelement& element,
UINode& node UINode& node
); );

View File

@ -0,0 +1,111 @@
#include "markdown.hpp"
#include "graphics/core/Font.hpp"
using namespace markdown;
template <typename CharT>
static inline void emit(
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
) {
ss << c;
styles.map.emplace_back(styles.palette.size()-1);
}
template <typename CharT>
static inline void emit_md(
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
) {
ss << c;
styles.map.emplace_back(0);
}
template <typename CharT>
static inline void restyle(
CharT c,
FontStyle& style,
FontStylesScheme& styles,
std::basic_stringstream<CharT>& ss,
int& pos,
bool eraseMarkdown
) {
styles.palette.push_back(style);
if (!eraseMarkdown) {
emit_md(c, styles, ss);
}
pos++;
}
template <typename CharT>
Result<CharT> process_markdown(
std::basic_string_view<CharT> source, bool eraseMarkdown
) {
std::basic_stringstream<CharT> ss;
FontStylesScheme styles {
// markdown default
{{false, false, false, false, glm::vec4(1,1,1,0.5f)}, {}},
{}
};
FontStyle style;
int pos = 0;
while (pos < source.size()) {
CharT first = source[pos];
if (first == '\\') {
if (pos + 1 < source.size()) {
CharT second = source[++pos];
switch (second) {
case '*':
case '_':
case '~':
if (!eraseMarkdown) {
emit_md(first, styles, ss);
}
emit(second, styles, ss);
pos++;
continue;
}
pos--;
}
} else if (first == '*') {
if (pos + 1 < source.size() && source[pos+1] == '*') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.bold = !style.bold;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
}
style.italic = !style.italic;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
} else if (first == '_' && pos + 1 < source.size() && source[pos+1] == '_') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.underline = !style.underline;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
} else if (first == '~' && pos + 1 < source.size() && source[pos+1] == '~') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.strikethrough = !style.strikethrough;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
}
if (first == '\n') {
styles.palette.push_back(styles.palette.at(1));
}
emit(first, styles, ss);
pos++;
}
return {ss.str(), std::make_unique<FontStylesScheme>(std::move(styles))};
}
Result<char> markdown::process(std::string_view source, bool eraseMarkdown) {
return process_markdown(source, eraseMarkdown);
}
Result<wchar_t> markdown::process(std::wstring_view source, bool eraseMarkdown) {
return process_markdown(source, eraseMarkdown);
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <string>
#include <memory>
#include <sstream>
struct FontStylesScheme;
// VoxelCore Markdown dialect
namespace markdown {
template <typename CharT>
struct Result {
/// @brief Text with erased markdown
std::basic_string<CharT> text;
/// @brief Text styles scheme
std::unique_ptr<FontStylesScheme> styles;
};
Result<char> process(std::string_view source, bool eraseMarkdown);
Result<wchar_t> process(std::wstring_view source, bool eraseMarkdown);
template <typename CharT>
inline std::basic_string<CharT> escape(std::string_view source) {
std::basic_stringstream<CharT> ss;
int pos = 0;
while (pos < source.size()) {
CharT first = source[pos];
if (first == '\\' && pos + 1 < source.size()) {
CharT second = source[++pos];
ss << first << second;
pos++;
continue;
} else if (first == '*' || first == '~' || first == '_') {
ss << '\\';
}
ss << first;
pos++;
}
return ss.str();
}
}

View File

@ -20,7 +20,9 @@ std::shared_ptr<Inventory> Inventories::create(size_t size) {
std::shared_ptr<Inventory> Inventories::createVirtual(size_t size) { std::shared_ptr<Inventory> Inventories::createVirtual(size_t size) {
int64_t id; int64_t id;
do { do {
id = -std::max<int64_t>(1LL, std::llabs(random.rand64())); // lua does not support long integers because Number is floating-point
// type. Changing int_consumer to use 64 bit integer does not change anything
id = -std::max<int64_t>(1LL, std::llabs(random.rand64() % 1000'000'000));
} while (map.find(id) != map.end()); } while (map.find(id) != map.end());
auto inv = std::make_shared<Inventory>(id, size); auto inv = std::make_shared<Inventory>(id, size);

View File

@ -5,7 +5,7 @@
Inventory::Inventory(int64_t id, size_t size) : id(id), slots(size) { Inventory::Inventory(int64_t id, size_t size) : id(id), slots(size) {
} }
Inventory::Inventory(const Inventory& orig) { Inventory::Inventory(const Inventory& orig) : id(0) {
this->slots = orig.slots; this->slots = orig.slots;
} }

View File

@ -31,9 +31,16 @@ static int l_add_command(lua::State* L) {
static int l_execute(lua::State* L) { static int l_execute(lua::State* L) {
auto prompt = lua::require_string(L, 1); auto prompt = lua::require_string(L, 1);
try {
auto result = engine->getCommandsInterpreter()->execute(prompt); auto result = engine->getCommandsInterpreter()->execute(prompt);
lua::pushvalue(L, result); lua::pushvalue(L, result);
return 1; return 1;
} catch (const parsing_error& err) {
if (std::string(err.what()).find("unknown command ") == 0) {
throw;
}
throw std::runtime_error(err.errorLog());
}
} }
static int l_set(lua::State* L) { static int l_set(lua::State* L) {

View File

@ -12,6 +12,8 @@
#include "graphics/ui/elements/TrackBar.hpp" #include "graphics/ui/elements/TrackBar.hpp"
#include "graphics/ui/elements/UINode.hpp" #include "graphics/ui/elements/UINode.hpp"
#include "graphics/ui/gui_util.hpp" #include "graphics/ui/gui_util.hpp"
#include "graphics/ui/markdown.hpp"
#include "graphics/core/Font.hpp"
#include "items/Inventories.hpp" #include "items/Inventories.hpp"
#include "util/stringutil.hpp" #include "util/stringutil.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
@ -299,6 +301,22 @@ static int p_get_line_numbers(UINode* node, lua::State* L) {
return 0; return 0;
} }
static int p_get_syntax(UINode* node, lua::State* L) {
if (auto box = dynamic_cast<TextBox*>(node)) {
return lua::pushstring(L, box->getSyntax());
}
return 0;
}
static int p_get_markup(UINode* node, lua::State* L) {
if (auto box = dynamic_cast<TextBox*>(node)) {
return lua::pushstring(L, box->getMarkup());
} else if (auto label = dynamic_cast<Label*>(node)) {
return lua::pushstring(L, label->getMarkup());
}
return 0;
}
static int p_get_src(UINode* node, lua::State* L) { static int p_get_src(UINode* node, lua::State* L) {
if (auto image = dynamic_cast<Image*>(node)) { if (auto image = dynamic_cast<Image*>(node)) {
return lua::pushstring(L, image->getTexture()); return lua::pushstring(L, image->getTexture());
@ -420,6 +438,8 @@ static int l_gui_getattr(lua::State* L) {
{"lineNumbers", p_get_line_numbers}, {"lineNumbers", p_get_line_numbers},
{"lineAt", p_get_line_at}, {"lineAt", p_get_line_at},
{"linePos", p_get_line_pos}, {"linePos", p_get_line_pos},
{"syntax", p_get_syntax},
{"markup", p_get_markup},
{"src", p_get_src}, {"src", p_get_src},
{"value", p_get_value}, {"value", p_get_value},
{"min", p_get_min}, {"min", p_get_min},
@ -512,6 +532,18 @@ static void p_set_line_numbers(UINode* node, lua::State* L, int idx) {
box->setShowLineNumbers(lua::toboolean(L, idx)); box->setShowLineNumbers(lua::toboolean(L, idx));
} }
} }
static void p_set_syntax(UINode* node, lua::State* L, int idx) {
if (auto box = dynamic_cast<TextBox*>(node)) {
box->setSyntax(lua::require_string(L, idx));
}
}
static void p_set_markup(UINode* node, lua::State* L, int idx) {
if (auto box = dynamic_cast<TextBox*>(node)) {
box->setMarkup(lua::require_string(L, idx));
} else if (auto label = dynamic_cast<Label*>(node)) {
label->setMarkup(lua::require_string(L, idx));
}
}
static void p_set_src(UINode* node, lua::State* L, int idx) { static void p_set_src(UINode* node, lua::State* L, int idx) {
if (auto image = dynamic_cast<Image*>(node)) { if (auto image = dynamic_cast<Image*>(node)) {
image->setTexture(lua::require_string(L, idx)); image->setTexture(lua::require_string(L, idx));
@ -612,6 +644,8 @@ static int l_gui_setattr(lua::State* L) {
{"text", p_set_text}, {"text", p_set_text},
{"editable", p_set_editable}, {"editable", p_set_editable},
{"lineNumbers", p_set_line_numbers}, {"lineNumbers", p_set_line_numbers},
{"syntax", p_set_syntax},
{"markup", p_set_markup},
{"src", p_set_src}, {"src", p_set_src},
{"caret", p_set_caret}, {"caret", p_set_caret},
{"value", p_set_value}, {"value", p_set_value},
@ -694,6 +728,25 @@ static int l_gui_getviewport(lua::State* L) {
return lua::pushvec2(L, engine->getGUI()->getContainer()->getSize()); return lua::pushvec2(L, engine->getGUI()->getContainer()->getSize());
} }
static int l_gui_clear_markup(lua::State* L) {
auto lang = lua::require_string(L, 1);
std::string text = lua::require_string(L, 2);
if (std::strcmp(lang, "md") == 0) {
auto [processed, _] = markdown::process(text, true);
text = std::move(processed);
}
return lua::pushstring(L, text);
}
static int l_gui_escape_markup(lua::State* L) {
auto lang = lua::require_string(L, 1);
std::string text = lua::require_string(L, 2);
if (std::strcmp(lang, "md") == 0) {
text = std::move(markdown::escape<char>(text));
}
return lua::pushstring(L, text);
}
const luaL_Reg guilib[] = { const luaL_Reg guilib[] = {
{"get_viewport", lua::wrap<l_gui_getviewport>}, {"get_viewport", lua::wrap<l_gui_getviewport>},
{"getattr", lua::wrap<l_gui_getattr>}, {"getattr", lua::wrap<l_gui_getattr>},
@ -701,5 +754,8 @@ const luaL_Reg guilib[] = {
{"get_env", lua::wrap<l_gui_get_env>}, {"get_env", lua::wrap<l_gui_get_env>},
{"str", lua::wrap<l_gui_str>}, {"str", lua::wrap<l_gui_str>},
{"get_locales_info", lua::wrap<l_gui_get_locales_info>}, {"get_locales_info", lua::wrap<l_gui_get_locales_info>},
{"clear_markup", lua::wrap<l_gui_clear_markup>},
{"escape_markup", lua::wrap<l_gui_escape_markup>},
{"__reindex", lua::wrap<l_gui_reindex>}, {"__reindex", lua::wrap<l_gui_reindex>},
{NULL, NULL}}; {NULL, NULL}
};

View File

@ -87,6 +87,9 @@ static int l_send(lua::State* L) {
connection->send( connection->send(
reinterpret_cast<char*>(bytes->data().data()), bytes->data().size() reinterpret_cast<char*>(bytes->data().data()), bytes->data().size()
); );
} else if (lua::isstring(L, 2)) {
auto string = lua::tolstring(L, 2);
connection->send(string.data(), string.length());
} }
return 0; return 0;
} }
@ -98,7 +101,8 @@ static int l_recv(lua::State* L) {
if (connection == nullptr) { if (connection == nullptr) {
return 0; return 0;
} }
util::Buffer<char> buffer(glm::min(length, connection->available())); length = glm::min(length, connection->available());
util::Buffer<char> buffer(length);
int size = connection->recv(buffer.data(), length); int size = connection->recv(buffer.data(), length);
if (size == -1) { if (size == -1) {
@ -151,6 +155,16 @@ static int l_is_connected(lua::State* L) {
return lua::pushboolean(L, false); return lua::pushboolean(L, false);
} }
static int l_get_address(lua::State* L) {
u64id_t id = lua::tointeger(L, 1);
if (auto connection = engine->getNetwork().getConnection(id)) {
lua::pushstring(L, connection->getAddress());
lua::pushinteger(L, connection->getPort());
return 2;
}
return 0;
}
static int l_is_serveropen(lua::State* L) { static int l_is_serveropen(lua::State* L) {
u64id_t id = lua::tointeger(L, 1); u64id_t id = lua::tointeger(L, 1);
if (auto server = engine->getNetwork().getServer(id)) { if (auto server = engine->getNetwork().getServer(id)) {
@ -159,6 +173,14 @@ static int l_is_serveropen(lua::State* L) {
return lua::pushboolean(L, false); return lua::pushboolean(L, false);
} }
static int l_get_serverport(lua::State* L) {
u64id_t id = lua::tointeger(L, 1);
if (auto server = engine->getNetwork().getServer(id)) {
return lua::pushinteger(L, server->getPort());
}
return 0;
}
static int l_get_total_upload(lua::State* L) { static int l_get_total_upload(lua::State* L) {
return lua::pushinteger(L, engine->getNetwork().getTotalUpload()); return lua::pushinteger(L, engine->getNetwork().getTotalUpload());
} }
@ -180,6 +202,8 @@ const luaL_Reg networklib[] = {
{"__recv", lua::wrap<l_recv>}, {"__recv", lua::wrap<l_recv>},
{"__is_alive", lua::wrap<l_is_alive>}, {"__is_alive", lua::wrap<l_is_alive>},
{"__is_connected", lua::wrap<l_is_connected>}, {"__is_connected", lua::wrap<l_is_connected>},
{"__get_address", lua::wrap<l_get_address>},
{"__is_serveropen", lua::wrap<l_is_serveropen>}, {"__is_serveropen", lua::wrap<l_is_serveropen>},
{"__get_serverport", lua::wrap<l_get_serverport>},
{NULL, NULL} {NULL, NULL}
}; };

View File

@ -1,16 +1,20 @@
#include <cmath> #include <cmath>
#include <stdexcept>
#include <filesystem> #include <filesystem>
#include <stdexcept>
#include "assets/Assets.hpp" #include "api_lua.hpp"
#include "assets/AssetsLoader.hpp" #include "assets/AssetsLoader.hpp"
#include "coders/compression.hpp"
#include "coders/gzip.hpp"
#include "coders/json.hpp" #include "coders/json.hpp"
#include "engine.hpp" #include "engine.hpp"
#include "files/files.hpp"
#include "files/engine_paths.hpp" #include "files/engine_paths.hpp"
#include "files/files.hpp"
#include "lighting/Lighting.hpp"
#include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "world/World.hpp" #include "world/World.hpp"
#include "api_lua.hpp"
using namespace scripting; using namespace scripting;
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -36,12 +40,12 @@ static int l_get_list(lua::State* L) {
const auto& folder = worlds[i]; const auto& folder = worlds[i];
auto root = json::parse(files::read_string(folder/fs::u8path("world.json"))); auto root =
json::parse(files::read_string(folder / fs::u8path("world.json")));
const auto& versionMap = root["version"]; const auto& versionMap = root["version"];
int versionMajor = versionMap["major"].asInteger(); int versionMajor = versionMap["major"].asInteger();
int versionMinor = versionMap["minor"].asInteger(); int versionMinor = versionMap["minor"].asInteger();
auto name = folder.filename().u8string(); auto name = folder.filename().u8string();
lua::pushstring(L, name); lua::pushstring(L, name);
lua::setfield(L, "name"); lua::setfield(L, "name");
@ -115,6 +119,118 @@ static int l_get_generator(lua::State* L) {
return lua::pushstring(L, require_world_info().generator); return lua::pushstring(L, require_world_info().generator);
} }
static int l_get_chunk_data(lua::State* L) {
int x = (int)lua::tointeger(L, 1);
int y = (int)lua::tointeger(L, 2);
const auto& chunk = level->chunks->getChunk(x, y);
if (chunk == nullptr) {
lua::pushnil(L);
return 0;
}
bool compress = false;
if (lua::gettop(L) >= 3) {
compress = lua::toboolean(L, 3);
}
std::vector<ubyte> chunk_data;
if (compress) {
size_t rle_compressed_size;
size_t gzip_compressed_size;
const auto& data_ptr = chunk->encode();
ubyte* data = data_ptr.get();
const auto& rle_compressed_data_ptr = compression::compress(
data,
CHUNK_DATA_LEN,
rle_compressed_size,
compression::Method::EXTRLE16
);
const auto& gzip_compressed_data = compression::compress(
rle_compressed_data_ptr.get(),
rle_compressed_size,
gzip_compressed_size,
compression::Method::GZIP
);
auto tmp = dataio::h2le(rle_compressed_size);
chunk_data.reserve(gzip_compressed_size + sizeof(tmp));
chunk_data.insert(
chunk_data.begin() + 0, (char*)&tmp, ((char*)&tmp) + sizeof(tmp)
);
chunk_data.insert(
chunk_data.begin() + sizeof(tmp),
gzip_compressed_data.get(),
gzip_compressed_data.get() + gzip_compressed_size
);
} else {
const auto& data = chunk->encode();
chunk_data.reserve(CHUNK_DATA_LEN);
chunk_data.insert(
chunk_data.begin(), data.get(), data.get() + CHUNK_DATA_LEN
);
}
return lua::newuserdata<lua::LuaBytearray>(L, chunk_data);
}
static int l_set_chunk_data(lua::State* L) {
int x = (int)lua::tointeger(L, 1);
int y = (int)lua::tointeger(L, 2);
auto buffer = lua::touserdata<lua::LuaBytearray>(L, 3);
bool is_compressed = false;
if (lua::gettop(L) >= 4) {
is_compressed = lua::toboolean(L, 4);
}
auto chunk = level->chunks->getChunk(x, y);
if(chunk== nullptr){
return 0;
}
if (is_compressed) {
std::vector<ubyte>& raw_data = buffer->data();
size_t gzip_decompressed_size =
dataio::le2h(*(size_t*)(raw_data.data()));
const auto& rle_data = compression::decompress(
raw_data.data() + sizeof(gzip_decompressed_size),
buffer->data().size() - sizeof(gzip_decompressed_size),
gzip_decompressed_size,
compression::Method::GZIP
);
const auto& data = compression::decompress(
rle_data.get(),
gzip_decompressed_size,
CHUNK_DATA_LEN,
compression::Method::EXTRLE16
);
chunk->decode(data.get());
} else {
chunk->decode(buffer->data().data());
}
chunk->updateHeights();
level->lighting->buildSkyLight(x, y);
chunk->flags.modified = true;
level->lighting->onChunkLoaded(x, y, true);
chunk = level->chunks->getChunk(x - 1, y);
if (chunk != nullptr) {
chunk->flags.modified = true;
level->lighting->onChunkLoaded(x - 1, y, true);
}
chunk = level->chunks->getChunk(x + 1, y);
if (chunk != nullptr) {
chunk->flags.modified = true;
level->lighting->onChunkLoaded(x + 1, y, true);
}
chunk = level->chunks->getChunk(x, y - 1);
if (chunk != nullptr) {
chunk->flags.modified = true;
level->lighting->onChunkLoaded(x, y - 1, true);
}
chunk = level->chunks->getChunk(x, y + 1);
if (chunk != nullptr) {
chunk->flags.modified = true;
level->lighting->onChunkLoaded(x, y + 1, true);
}
return 1;
}
const luaL_Reg worldlib[] = { const luaL_Reg worldlib[] = {
{"is_open", lua::wrap<l_is_open>}, {"is_open", lua::wrap<l_is_open>},
{"get_list", lua::wrap<l_get_list>}, {"get_list", lua::wrap<l_get_list>},
@ -128,4 +244,7 @@ const luaL_Reg worldlib[] = {
{"is_day", lua::wrap<l_is_day>}, {"is_day", lua::wrap<l_is_day>},
{"is_night", lua::wrap<l_is_night>}, {"is_night", lua::wrap<l_is_night>},
{"exists", lua::wrap<l_exists>}, {"exists", lua::wrap<l_exists>},
{NULL, NULL}}; {"get_chunk_data", lua::wrap<l_get_chunk_data>},
{"set_chunk_data", lua::wrap<l_set_chunk_data>},
{NULL, NULL}
};

View File

@ -191,7 +191,7 @@ void scripting::on_content_load(Content* content) {
lua::pop(L); lua::pop(L);
} }
if (lua::getglobal(L, "item")) { if (lua::getglobal(L, "item")) {
push_properties_tables(L, indices.blocks); push_properties_tables(L, indices.items);
lua::setfield(L, "properties"); lua::setfield(L, "properties");
lua::pop(L); lua::pop(L);
} }

View File

@ -244,10 +244,11 @@ static inline int sendsocket(
return send(descriptor, buf, len, flags); return send(descriptor, buf, len, flags);
} }
static std::string to_string(const sockaddr_in* addr) { static std::string to_string(const sockaddr_in& addr, bool port=true) {
char ip[INET_ADDRSTRLEN]; char ip[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &(addr->sin_addr), ip, INET_ADDRSTRLEN)) { if (inet_ntop(AF_INET, &(addr.sin_addr), ip, INET_ADDRSTRLEN)) {
return std::string(ip)+":"+std::to_string(htons(addr->sin_port)); return std::string(ip) +
(port ? (":" + std::to_string(htons(addr.sin_port))) : "");
} }
return ""; return "";
} }
@ -266,7 +267,7 @@ class SocketConnection : public Connection {
void connectSocket() { void connectSocket() {
state = ConnectionState::CONNECTING; state = ConnectionState::CONNECTING;
logger.info() << "connecting to " << to_string(&addr); logger.info() << "connecting to " << to_string(addr);
int res = connectsocket(descriptor, (const sockaddr*)&addr, sizeof(sockaddr_in)); int res = connectsocket(descriptor, (const sockaddr*)&addr, sizeof(sockaddr_in));
if (res < 0) { if (res < 0) {
auto error = handle_socket_error("Connect failed"); auto error = handle_socket_error("Connect failed");
@ -275,7 +276,7 @@ class SocketConnection : public Connection {
logger.error() << error.what(); logger.error() << error.what();
return; return;
} }
logger.info() << "connected to " << to_string(&addr); logger.info() << "connected to " << to_string(addr);
state = ConnectionState::CONNECTED; state = ConnectionState::CONNECTED;
} }
public: public:
@ -292,22 +293,17 @@ public:
} }
} }
void connect(runnable callback) override { void startListen() {
thread = std::make_unique<std::thread>([this, callback]() {
connectSocket();
if (state == ConnectionState::CONNECTED) {
callback();
}
while (state == ConnectionState::CONNECTED) { while (state == ConnectionState::CONNECTED) {
int size = recvsocket(descriptor, buffer.data(), buffer.size()); int size = recvsocket(descriptor, buffer.data(), buffer.size());
if (size == 0) { if (size == 0) {
logger.info() << "closed connection with " << to_string(&addr); logger.info() << "closed connection with " << to_string(addr);
closesocket(descriptor); closesocket(descriptor);
state = ConnectionState::CLOSED; state = ConnectionState::CLOSED;
break; break;
} else if (size < 0) { } else if (size < 0) {
logger.info() << "an error ocurred while receiving from " logger.warning() << "an error ocurred while receiving from "
<< to_string(&addr); << to_string(addr);
auto error = handle_socket_error("recv(...) error"); auto error = handle_socket_error("recv(...) error");
closesocket(descriptor); closesocket(descriptor);
state = ConnectionState::CLOSED; state = ConnectionState::CLOSED;
@ -321,8 +317,22 @@ public:
} }
totalDownload += size; totalDownload += size;
} }
logger.info() << "read " << size << " bytes from " << to_string(&addr); logger.debug() << "read " << size << " bytes from " << to_string(addr);
} }
}
void startClient() {
state = ConnectionState::CONNECTED;
thread = std::make_unique<std::thread>([this]() { startListen();});
}
void connect(runnable callback) override {
thread = std::make_unique<std::thread>([this, callback]() {
connectSocket();
if (state == ConnectionState::CONNECTED) {
callback();
}
startListen();
}); });
} }
@ -380,6 +390,14 @@ public:
return size; return size;
} }
int getPort() const override {
return htons(addr.sin_port);
}
std::string getAddress() const override {
return to_string(addr, false);
}
static std::shared_ptr<SocketConnection> connect( static std::shared_ptr<SocketConnection> connect(
const std::string& address, int port, runnable callback const std::string& address, int port, runnable callback
) { ) {
@ -421,9 +439,10 @@ class SocketTcpSServer : public TcpServer {
std::mutex clientsMutex; std::mutex clientsMutex;
bool open = true; bool open = true;
std::unique_ptr<std::thread> thread = nullptr; std::unique_ptr<std::thread> thread = nullptr;
int port;
public: public:
SocketTcpSServer(Network* network, SOCKET descriptor) SocketTcpSServer(Network* network, SOCKET descriptor, int port)
: network(network), descriptor(descriptor) {} : network(network), descriptor(descriptor), port(port) {}
~SocketTcpSServer() { ~SocketTcpSServer() {
closeSocket(); closeSocket();
@ -445,10 +464,11 @@ public:
close(); close();
break; break;
} }
logger.info() << "client connected: " << to_string(&address); logger.info() << "client connected: " << to_string(address);
auto socket = std::make_shared<SocketConnection>( auto socket = std::make_shared<SocketConnection>(
clientDescriptor, address clientDescriptor, address
); );
socket->startClient();
u64id_t id = network->addConnection(socket); u64id_t id = network->addConnection(socket);
{ {
std::lock_guard lock(clientsMutex); std::lock_guard lock(clientsMutex);
@ -488,6 +508,11 @@ public:
bool isOpen() override { bool isOpen() override {
return open; return open;
} }
int getPort() const override {
return port;
}
static std::shared_ptr<SocketTcpSServer> openServer( static std::shared_ptr<SocketTcpSServer> openServer(
Network* network, int port, consumer<u64id_t> handler Network* network, int port, consumer<u64id_t> handler
) { ) {
@ -515,7 +540,8 @@ public:
throw std::runtime_error("could not bind port "+std::to_string(port)); throw std::runtime_error("could not bind port "+std::to_string(port));
} }
logger.info() << "opened server at port " << port; logger.info() << "opened server at port " << port;
auto server = std::make_shared<SocketTcpSServer>(network, descriptor); auto server =
std::make_shared<SocketTcpSServer>(network, descriptor, port);
server->startListen(std::move(handler)); server->startListen(std::move(handler));
return server; return server;
} }

View File

@ -46,6 +46,9 @@ namespace network {
virtual size_t pullUpload() = 0; virtual size_t pullUpload() = 0;
virtual size_t pullDownload() = 0; virtual size_t pullDownload() = 0;
virtual int getPort() const = 0;
virtual std::string getAddress() const = 0;
virtual ConnectionState getState() const = 0; virtual ConnectionState getState() const = 0;
}; };
@ -55,6 +58,7 @@ namespace network {
virtual void startListen(consumer<u64id_t> handler) = 0; virtual void startListen(consumer<u64id_t> handler) = 0;
virtual void close() = 0; virtual void close() = 0;
virtual bool isOpen() = 0; virtual bool isOpen() = 0;
virtual int getPort() const = 0;
}; };
class Network { class Network {

View File

@ -53,7 +53,7 @@ struct ParticlesPreset : public Serializable {
/// @brief Size of random sub-uv region /// @brief Size of random sub-uv region
float randomSubUV = 1.0f; float randomSubUV = 1.0f;
/// @brief Animation frames /// @brief Animation frames
std::vector<std::string> frames {}; std::vector<std::string> frames;
dv::value serialize() const override; dv::value serialize() const override;
void deserialize(const dv::value& src) override; void deserialize(const dv::value& src) override;

View File

@ -180,7 +180,7 @@ namespace util {
} }
TCoord getHeight() const { TCoord getHeight() const {
return sizeX; return sizeY;
} }
const std::vector<T>& getBuffer() const { const std::vector<T>& getBuffer() const {

View File

@ -349,7 +349,7 @@ std::string util::mangleid(uint64_t value) {
util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) { util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) {
util::Buffer<ubyte> bytes((size / 4) * 3); util::Buffer<ubyte> bytes((size / 4) * 3);
ubyte* dst = bytes.data(); ubyte* dst = bytes.data();
for (size_t i = 0; i < size;) { for (size_t i = 0; i < (size / 4) * 4;) {
ubyte a = base64_decode_char(ubyte(str[i++])); ubyte a = base64_decode_char(ubyte(str[i++]));
ubyte b = base64_decode_char(ubyte(str[i++])); ubyte b = base64_decode_char(ubyte(str[i++]));
ubyte c = base64_decode_char(ubyte(str[i++])); ubyte c = base64_decode_char(ubyte(str[i++]));

View File

@ -422,7 +422,7 @@ void Chunks::set(
if (lz == 0 && (chunk = getChunk(cx, cz - 1))) { if (lz == 0 && (chunk = getChunk(cx, cz - 1))) {
chunk->flags.modified = true; chunk->flags.modified = true;
} }
if (lx == CHUNK_W - 1 && (chunk = getChunk(cx, cz))) { if (lx == CHUNK_W - 1 && (chunk = getChunk(cx + 1, cz))) {
chunk->flags.modified = true; chunk->flags.modified = true;
} }
if (lz == CHUNK_D - 1 && (chunk = getChunk(cx, cz + 1))) { if (lz == CHUNK_D - 1 && (chunk = getChunk(cx, cz + 1))) {

View File

@ -0,0 +1,20 @@
#include <gtest/gtest.h>
#include "coders/commons.hpp"
#include "coders/lua_parsing.hpp"
#include "files/files.hpp"
#include "util/stringutil.hpp"
TEST(lua_parsing, Tokenizer) {
auto filename = "../../res/scripts/stdlib.lua";
auto source = files::read_string(std::filesystem::u8path(filename));
try {
auto tokens = lua::tokenize(filename, source);
for (const auto& token : tokens) {
std::cout << (int)token.tag << " " << util::quote(token.text) << std::endl;
}
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw err;
}
}