mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-06 11:31:49 +00:00
Merge branch 'MihailRis:main' into main
This commit is contained in:
commit
b7295da941
31 changed files with 477 additions and 28 deletions
|
|
@ -60,13 +60,18 @@ Property used via [inventory.use](scripting/builtins/libinventory.md).
|
|||
|
||||
Property status is displayed in the inventory interface. Display method is defined via `uses-display`.
|
||||
|
||||
### Display of uses - `uses-display`
|
||||
### Display of remaining uses count - `uses-display`
|
||||
|
||||
- `none` - display disabled
|
||||
- `number` - number
|
||||
- `relation` - current value to initial value (x/y)
|
||||
- `vbar` - vertical scale (used by default)
|
||||
|
||||
### *script-name*
|
||||
|
||||
Defines the name of the script containing the item's event handlers. By default, equals to the item's string id (name).
|
||||
Allows you to reuse a single script for multiple items.
|
||||
|
||||
## Tags
|
||||
|
||||
Tags allow you to designate general properties of items. Names should be formatted as `prefix:tag_name`.
|
||||
|
|
|
|||
|
|
@ -58,9 +58,58 @@ not part of Lua syntax.
|
|||
- quat - array of four numbers - quaternion
|
||||
- matrix - array of 16 numbers - matrix
|
||||
|
||||
## Core functions
|
||||
## Namespaces
|
||||
|
||||
Currently, the engine uses a namespace hierarchy that minimizes conflicts between individual modules, scripts, and even packs.
|
||||
|
||||
The following structure is relevant:
|
||||
|
||||
- Global namespace (_G), which is the root namespace. Writing to it outside of engine core modules is highly undesirable, as it can waste hours of debugging time.
|
||||
- Pack namespace - created each time content is loaded for each pack included in the configuration. This namespace is used by modules, as well as item and block scripts.
|
||||
- [Component namespace](scripting/ecs.md#built-in-components).
|
||||
- [UI document namespace](scripting/ui.md).
|
||||
- Isolated world generator namespace.
|
||||
|
||||
## Modules
|
||||
|
||||
A module is a global object with a lifetime limited to the lifetime of the loaded content, used both for interaction between different packages and as a substitute for global variables within the package itself.
|
||||
|
||||
The module must be located in `contentpack/modules/**` (subfolders are allowed, but you will need to specify them)
|
||||
|
||||
```lua
|
||||
require "packid:module_name" -- load Lua module from pack-folder/modules/
|
||||
-- no extension included, just name
|
||||
local module_name = require "contentpack:module_name" -- imports the module
|
||||
|
||||
-- If the module is in the same pack in which it is imported, you can use the shortcut:
|
||||
local module_name = require "module_name"
|
||||
|
||||
-- When using nested folders:
|
||||
local module_name = require "contentpack:path/to/module_name" -- the path does not include `modules`
|
||||
local module_name = require "path/to/module_name" -- if in the same pack
|
||||
```
|
||||
|
||||
The module will have the namespace of the folder in which it is located, regardless of the namespace from which the first import was performed.
|
||||
|
||||
When creating a module, you should follow the following approach, which is what *require* expects:
|
||||
|
||||
```lua
|
||||
local this = {
|
||||
variable_name = ... -- public variables
|
||||
}
|
||||
|
||||
-- private module variables
|
||||
local variable_name = ...
|
||||
|
||||
-- private module functions
|
||||
local function function_name(...)
|
||||
...
|
||||
end
|
||||
|
||||
-- public module functions
|
||||
function this.function_name(...)
|
||||
...
|
||||
end
|
||||
|
||||
return this -- the result that require will return and cache
|
||||
```
|
||||
|
||||
When reimported, the module will not be reloaded; instead, require will return the cached result.
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ entities.def_hitbox(id: int) -> vec3
|
|||
-- Returns entity definition name by index (string ID).
|
||||
entities.def_name(id: int) -> str
|
||||
|
||||
-- Returns true if the entity is a solid obstacle.
|
||||
entities.def_solid(id: int) -> boolean
|
||||
|
||||
-- Returns entity definition index by name (integer ID).
|
||||
entities.def_index(name: str) -> int
|
||||
|
||||
|
|
@ -51,12 +54,12 @@ entities.get_all() -> table
|
|||
-- Returns a table of loaded entities based on the passed list of UIDs
|
||||
entities.get_all(uids: array<int>) -> table
|
||||
|
||||
-- Returns a list of UIDs of entities inside the rectangular area
|
||||
-- Returns a list of UIDs of entities with origin inside the rectangular area
|
||||
-- pos - minimal area corner
|
||||
-- size - area size
|
||||
entities.get_all_in_box(pos: vec3, size: vec3) -> array<int>
|
||||
|
||||
-- Returns a list of UIDs of entities inside the radius
|
||||
-- Returns a list of UIDs of entities with origin inside the radius
|
||||
-- center - center of the area
|
||||
-- radius - radius of the area
|
||||
entities.get_all_in_radius(center: vec3, radius: number) -> array<int>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,17 @@ entity:set_enabled(name: str, enable: bool)
|
|||
entity:get_player() -> int or nil
|
||||
```
|
||||
|
||||
## Custom Components
|
||||
|
||||
A component is defined as a script in `{pack}/scripts/components/{name}.lua`.
|
||||
The component will be available for use in the entity definition as `{pack}:{name}`.
|
||||
Example: `core:scripts/components/pathfinding.lua` -> `core:pathfinding`.
|
||||
|
||||
For **each** entity containing a component, a **separate instance** is created with **its own namespace** within the pack namespace.
|
||||
Global variables declared in it act as public fields of the component (same for global functions).
|
||||
|
||||
Entity components are specified via the ["components" list](../entity-properties.md#components)
|
||||
|
||||
## Built-in components
|
||||
|
||||
### Transform
|
||||
|
|
|
|||
|
|
@ -52,6 +52,60 @@ yaml.parse(code: str) -> table
|
|||
|
||||
Parses a YAML string into a table.
|
||||
|
||||
## XML Library
|
||||
|
||||
The library contains functions for serializing and deserializing XML:
|
||||
|
||||
Further, XML as a data type denotes a Lua table with the following structure:
|
||||
- Element tag is accessible by the key `#`
|
||||
- XML attributes are accessible as key = value pairs
|
||||
- Inner elements are accessible by index
|
||||
|
||||
The table is used both as a dictionary and an array.
|
||||
|
||||
Example:
|
||||
|
||||
```xml
|
||||
<panel size='400' color='0' interval='1' context='menu'>
|
||||
<button onclick='menu.page="worlds"'>@Worlds</button>
|
||||
</panel>
|
||||
```
|
||||
|
||||
Equivalent Lua table:
|
||||
|
||||
```lua
|
||||
{
|
||||
['#'] = "panel",
|
||||
size = "400",
|
||||
color = "0",
|
||||
interval = "1",
|
||||
context = "menu",
|
||||
{
|
||||
['#'] = "button",
|
||||
onclick = 'menu.page="worlds"',
|
||||
{
|
||||
"@Worlds"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Consequently, the button element is accessible as `panel_element[1]`.
|
||||
|
||||
```lua
|
||||
-- Serializes a tree in XML format
|
||||
-- multiline - use multiline formatting
|
||||
xml.tostring(data: XML, multiline: bool = true) -> str
|
||||
|
||||
-- Parses XML
|
||||
xml.parse(code: str) -> XML
|
||||
|
||||
-- Parses the VCD (VoxelCore Declaration) format used by the VCM format.
|
||||
-- root_tag - a root element with the specified tag will be implicitly created,
|
||||
-- containing all root elements
|
||||
xml.parse_vcd(code: str, root_tag: str = "root") -> XML
|
||||
```
|
||||
|
||||
## *bjson* library
|
||||
|
||||
The library contains functions for working with the binary data exchange format [vcbjson](../../specs/binary_json_spec.md).
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# UI properties and methods
|
||||
|
||||
UI elements in scripts are accessed through a Document instance
|
||||
(*document* variable) by id specified in xml.
|
||||
(global *document* variable) by id specified in xml.
|
||||
|
||||
Example: print the pos property of an element with id: "worlds-panel" to the console:
|
||||
```lua
|
||||
|
|
|
|||
|
|
@ -66,6 +66,10 @@
|
|||
- `relation` - отношение текущего значения к изначальному (x/y)
|
||||
- `vbar` - вертикальная шкала (используется по-умолчанию)
|
||||
|
||||
### Имя скрипта - `script-name`
|
||||
|
||||
Определяет имя скрипта, содержащего обработчики событий предмета. По-умолчанию соответствует строковому id (имени) предмета.
|
||||
Свойство обеспечивает возможность использования одного скрипта для нескольких предметов.
|
||||
|
||||
## Теги - *tags*
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,58 @@
|
|||
- quat - массив из четырех чисел - кватернион
|
||||
- matrix - массив из 16 чисел - матрица
|
||||
|
||||
## Пространства имён
|
||||
|
||||
В настоящее время, движок использует иерархию пространств имён, минимизирующую конфликты между отдельными модулями, скриптами, да и паками.
|
||||
|
||||
Актуальна следующая структура:
|
||||
|
||||
- Глобальное пространство (оно же - _G), являющееся корневым, запись в которое, вне модулей ядра движка, является крайне нежелательным для вашего же времени, что может уйти на лишние часы отладки.
|
||||
- Пространство пака - создаётся каждый раз при загрузке контента для каждого, включённого в конфигурацию, пака. Это пространство используют модули, а также скрипты предметов и блоков.
|
||||
- Пространство [компонента](scripting/ecs.md#пользовательские-компоненты).
|
||||
- Пространство [UI-документа](scripting/ui.md)
|
||||
- Изолированное пространство имён генератора мира.
|
||||
|
||||
## Модули
|
||||
|
||||
Модуль - глобальный объект со сроком жизни, ограниченным сроком жизни контента, используемый как для взаимодействия разных паков, так и для вместо глобальных переменных в пределах самого пака.
|
||||
|
||||
Модуль должен находиться в `контентпак/modules/**` (допускаются вложенные папки, что нужно будет указывать)
|
||||
|
||||
```lua
|
||||
require "контентпак:имя_модуля" -- загружает lua модуль из папки modules (расширение не указывается)
|
||||
local имя_модуля = require "контентпак:имя_модуля" -- импортирует модуль
|
||||
|
||||
-- если модуль находится в том же паке, в котором импортируется, можно использовать сокращённый вариант:
|
||||
local имя_модуля = require "имя_модуля"
|
||||
|
||||
-- при использовании вложенных папок:
|
||||
local имя_модуля = require "контентпак:путь/к/имя_модуля" -- путь не включает `modules`
|
||||
local имя_модуля = require "путь/к/имя_модуля" -- если в том же паке
|
||||
```
|
||||
|
||||
Модуль будет иметь пространство имён того пака, в котором находится, независимо от пространства имён, из которого был выполнен первый импорт.
|
||||
|
||||
При создании модуля следует придерживаться следующего подхода, которого и ожидает *require*:
|
||||
|
||||
```lua
|
||||
local this = {
|
||||
имя_переменной = ... -- публичные переменные
|
||||
}
|
||||
|
||||
-- приватные переменные модуля
|
||||
local имя_переменной = ...
|
||||
|
||||
-- приватные функции модуля
|
||||
local function имя_функции(...)
|
||||
...
|
||||
end
|
||||
|
||||
-- публичные функции модуля
|
||||
function this.имя_функции(...)
|
||||
...
|
||||
end
|
||||
|
||||
return this -- результат, который и будет возвращать и кешировать require
|
||||
```
|
||||
|
||||
При повторном импорте модуль не будет перезагружен - вместо этого require вернёт кешированный результат.
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ entities.def_name(id: int) -> string
|
|||
-- Возвращает значение свойства 'hitbox' сущности
|
||||
entities.def_hitbox(id: int) -> vec3
|
||||
|
||||
-- Возвращает true если сущность является осязаемым препятствием.
|
||||
entities.def_solid(id: int) -> boolean
|
||||
|
||||
-- Возвращает индекс определения сущности по имени (числовой ID).
|
||||
entities.def_index(name: string) -> int
|
||||
|
||||
|
|
@ -50,12 +53,12 @@ entities.get_all() -> table
|
|||
-- Возвращает таблицу загруженных сущностей по переданному списку UID
|
||||
entities.get_all(uids: table<int>) -> table
|
||||
|
||||
-- Возвращает список UID сущностей, попадающих в прямоугольную область
|
||||
-- Возвращает список UID сущностей, позиции которых попадают в прямоугольную область
|
||||
-- pos - минимальный угол области
|
||||
-- size - размер области
|
||||
entities.get_all_in_box(pos: vec3, size: vec3) -> table<int>
|
||||
|
||||
-- Возвращает список UID сущностей, попадающих в радиус
|
||||
-- Возвращает список UID сущностей, позиции которых попадают в радиус
|
||||
-- center - центр области
|
||||
-- radius - радиус области
|
||||
entities.get_all_in_radius(center: vec3, radius: number) -> table<int>
|
||||
|
|
|
|||
|
|
@ -180,6 +180,17 @@ rig:get_color() -> vec3
|
|||
rig:set_color(color: vec3)
|
||||
```
|
||||
|
||||
## Пользовательские компоненты
|
||||
|
||||
Компонент описывается в виде скрипта `{пак}/scripts/components/{имя}.lua`.
|
||||
Компонент будет доступен для использования в описании сущности как `{пак}:{имя}`.
|
||||
Пример: `core:scripts/components/pathfinding.lua` -> `core:pathfinding`.
|
||||
|
||||
Для **каждой** сущности, содержащей компонент, создаётся **отдельный экземпляр** со **своим пространством имен** внутри пространства имён пака.
|
||||
Глобальные переменные, объявляемые в нём, играют роль публичных полей компонента (как и глобальные функции).
|
||||
|
||||
Компоненты сущности указываются через [список "components"](../entity-properties.md#cписок-компонентов---components)
|
||||
|
||||
> [!WARNING]
|
||||
> При выходе сущности за пределы зоны прогрузки она удаляется, вызывая события.
|
||||
> При повторном попадании в зону прогрузки сущность спавнится заново.
|
||||
|
|
|
|||
|
|
@ -156,6 +156,8 @@ math.round(num: number, [опционально] places: number) -> number
|
|||
-- Возвращает сумму всех принимаемых аргументов. Если в качестве аргумента была передана таблица, метод вернёт сумму всех её элементов.
|
||||
math.sum(x: number, ... | t: table) -> number
|
||||
|
||||
-- Возвращает целое число, указывающее знак числа (-1/0/1)
|
||||
math.sign(x: number) -> int
|
||||
```
|
||||
|
||||
## Расширения для bit
|
||||
|
|
@ -200,4 +202,4 @@ await(co: coroutine) -> result, error
|
|||
|
||||
-- Константа, в которой хранится PID текущего инстанса движка.
|
||||
os.pid -> number
|
||||
```
|
||||
```
|
||||
|
|
|
|||
|
|
@ -52,6 +52,60 @@ yaml.parse(code: str) -> table
|
|||
|
||||
Парсит YAML строку в таблицу.
|
||||
|
||||
## Библиотека xml
|
||||
|
||||
Библиотека содержит функции для сериализации и десериализации XML:
|
||||
|
||||
Далее XML как тип данных обозначает Lua-таблицу со следующей структурой:
|
||||
- Тег элемента доступен по ключу `#`
|
||||
- XML-аттрибуты доступны как пары ключ = значение
|
||||
- Вложенные элементы доступны по индексу
|
||||
|
||||
Таблица используется как словарь, так и массив.
|
||||
|
||||
Пример:
|
||||
|
||||
```xml
|
||||
<panel size='400' color='0' interval='1' context='menu'>
|
||||
<button onclick='menu.page="worlds"'>@Worlds</button>
|
||||
</panel>
|
||||
```
|
||||
|
||||
Эквивалентная Lua-таблица:
|
||||
|
||||
```lua
|
||||
{
|
||||
['#'] = "panel",
|
||||
size = "400",
|
||||
color = "0",
|
||||
interval = "1",
|
||||
context = "menu",
|
||||
{
|
||||
['#'] = "button",
|
||||
onclick = 'menu.page="worlds"',
|
||||
{
|
||||
"@Worlds"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Соответственно, элемент button доступен как `элемент_panel[1]`.
|
||||
|
||||
```lua
|
||||
-- Сериализует дерево в формате XML
|
||||
-- multiline - использовать ли многострочное форматирование
|
||||
xml.tostring(data: XML, multiline: bool = true) -> str
|
||||
|
||||
-- Парсит XML
|
||||
xml.parse(code: str) -> XML
|
||||
|
||||
-- Парсит формат VCD (VoxelCore Declaration), используемый форматом VCM.
|
||||
-- root_tag - будет неявно создан корневой элемент с указанным тегом,
|
||||
-- содержащий все корневые элементы
|
||||
xml.parse_vcd(code: str, root_tag: str | nil = nil) -> XML
|
||||
```
|
||||
|
||||
## Библиотека bjson
|
||||
|
||||
Библиотека содержит функции для работы с двоичным форматом обмена данными [vcbjson](../../specs/binary_json_spec.md).
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Свойства и методы UI элементов
|
||||
|
||||
Обращение к UI элементов в скриптах произодится через экземпляр Document
|
||||
(переменная document) по id, указанному в xml.
|
||||
(глобальная переменная document) по id, указанному в xml.
|
||||
|
||||
Пример: вывод в консоль свойства pos элемента с id: "worlds-panel":
|
||||
```lua
|
||||
|
|
|
|||
|
|
@ -35,3 +35,7 @@ function math.sum(...)
|
|||
|
||||
return sum
|
||||
end
|
||||
|
||||
function math.sign(x)
|
||||
return (x > 0) and 1 or (x < 0 and -1 or 0)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -57,3 +57,7 @@ std::string parsing_error::errorLog() const {
|
|||
ss << "^";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::runtime_error parsing_error::toRuntimeError() const {
|
||||
return std::runtime_error("parsing error: " + errorLog());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,4 +73,6 @@ public:
|
|||
uint linestart
|
||||
);
|
||||
std::string errorLog() const;
|
||||
|
||||
std::runtime_error toRuntimeError() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -385,10 +385,22 @@ public:
|
|||
}
|
||||
if (c == '(') {
|
||||
nextChar();
|
||||
// TODO: replace with array parsing after moving to dv::value's
|
||||
std::string value = std::string(readUntil(')'));
|
||||
int depth = 1;
|
||||
size_t start = pos;
|
||||
while (hasNext()) {
|
||||
char c = nextChar();
|
||||
if (c == '(') {
|
||||
depth++;
|
||||
} else if (c == ')') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
goBack(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(')');
|
||||
return value;
|
||||
return std::string(source.substr(start, pos - start - 1));
|
||||
}
|
||||
return std::string(readUntilWhitespace());
|
||||
}
|
||||
|
|
@ -528,3 +540,11 @@ std::string xml::stringify(
|
|||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string xml::stringify(
|
||||
const Node& element, bool nice, const std::string& indentStr
|
||||
) {
|
||||
std::stringstream ss;
|
||||
stringifyElement(ss, element, nice, indentStr, 0);
|
||||
return ss.str();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,12 @@ namespace xml {
|
|||
const std::string& indentStr = " "
|
||||
);
|
||||
|
||||
std::string stringify(
|
||||
const Node& element,
|
||||
bool nice = true,
|
||||
const std::string& indentStr = " "
|
||||
);
|
||||
|
||||
/// @brief Read XML Document from string
|
||||
/// @param filename file name will be shown in error messages
|
||||
/// @param source xml source code string
|
||||
|
|
|
|||
|
|
@ -178,7 +178,16 @@ void ContentUnitLoader<DefT>::loadUnit(
|
|||
) {
|
||||
auto folder = pack.folder;
|
||||
auto configFile = folder / (defsDir + "/" + name + ".json");
|
||||
if (io::exists(configFile)) loadUnit(def, full, configFile);
|
||||
if (io::exists(configFile)) {
|
||||
try {
|
||||
loadUnit(def, full, configFile);
|
||||
} catch (const std::runtime_error& err) {
|
||||
throw std::runtime_error(
|
||||
"file " + util::quote(configFile.string()) + ": " +
|
||||
std::string(err.what())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadBlockMaterial(
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ static void load_variant(
|
|||
}
|
||||
} else if (root.has("texture-faces")) {
|
||||
const auto& texarr = root["texture-faces"];
|
||||
if (texarr.length() < 6) {
|
||||
throw std::runtime_error("texture-faces requires 6 elements");
|
||||
}
|
||||
for (uint i = 0; i < 6; i++) {
|
||||
variant.textureFaces[i] = texarr[i].asString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ class Font;
|
|||
struct FontMetrics {
|
||||
std::optional<std::weak_ptr<Font>> font;
|
||||
|
||||
int lineHeight;
|
||||
int yoffset;
|
||||
int lineHeight = 0;
|
||||
int yoffset = 0;
|
||||
int _glyphInterval = 8;
|
||||
|
||||
~FontMetrics();
|
||||
|
|
|
|||
|
|
@ -207,6 +207,9 @@ uint Label::getLinesNumber() const {
|
|||
void Label::draw(const DrawContext& pctx, const Assets& assets) {
|
||||
auto batch = pctx.getBatch2D();
|
||||
auto font = assets.getShared<Font>(fontName);
|
||||
if (font == nullptr) {
|
||||
return;
|
||||
}
|
||||
cache.prepare(
|
||||
font,
|
||||
font->getMetrics(),
|
||||
|
|
|
|||
|
|
@ -353,7 +353,9 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
|
||||
void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) {
|
||||
auto font = assets.getShared<Font>(label->getFontName());
|
||||
rawTextCache.prepare(font, font->getMetrics(), label->getSize().x);
|
||||
if (font != nullptr) {
|
||||
rawTextCache.prepare(font, font->getMetrics(), label->getSize().x);
|
||||
}
|
||||
|
||||
glm::vec2 pos = calcPos();
|
||||
|
||||
|
|
|
|||
|
|
@ -173,8 +173,12 @@ bool io::write_binary_json(
|
|||
}
|
||||
|
||||
dv::value io::read_json(const path& filename) {
|
||||
std::string text = io::read_string(filename);
|
||||
return json::parse(filename.string(), text);
|
||||
auto text = io::read_string(filename);
|
||||
try {
|
||||
return json::parse(filename.string(), text);
|
||||
} catch (const parsing_error& err) {
|
||||
throw err.toRuntimeError();
|
||||
}
|
||||
}
|
||||
|
||||
dv::value io::read_binary_json(const path& file) {
|
||||
|
|
@ -184,7 +188,12 @@ dv::value io::read_binary_json(const path& file) {
|
|||
}
|
||||
|
||||
dv::value io::read_toml(const path& file) {
|
||||
return toml::parse(file.string(), io::read_string(file));
|
||||
auto text = io::read_string(file);
|
||||
try {
|
||||
return toml::parse(file.string(), text);
|
||||
} catch (const parsing_error& err) {
|
||||
throw err.toRuntimeError();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> io::read_list(const io::path& filename) {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ extern const luaL_Reg vec3lib[]; // vecn.cpp
|
|||
extern const luaL_Reg vec4lib[]; // vecn.cpp
|
||||
extern const luaL_Reg weatherlib[]; // gfx.weather
|
||||
extern const luaL_Reg worldlib[];
|
||||
extern const luaL_Reg xmllib[];
|
||||
extern const luaL_Reg yamllib[];
|
||||
|
||||
// Components
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@ static int l_def_hitbox(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_def_solid(lua::State* L) {
|
||||
if (auto def = require_entity_def(L)) {
|
||||
return lua::pushboolean(L, def->solid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_defs_count(lua::State* L) {
|
||||
return lua::pushinteger(L, indices->entities.count());
|
||||
}
|
||||
|
|
@ -376,6 +383,7 @@ const luaL_Reg entitylib[] = {
|
|||
{"def_index", lua::wrap<l_def_index>},
|
||||
{"def_name", lua::wrap<l_def_name>},
|
||||
{"def_hitbox", lua::wrap<l_def_hitbox>},
|
||||
{"def_solid", lua::wrap<l_def_solid>},
|
||||
{"get_def", lua::wrap<l_get_def>},
|
||||
{"defs_count", lua::wrap<l_defs_count>},
|
||||
{"spawn", lua::wrap<l_spawn>},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "coders/json.hpp"
|
||||
#include "coders/commons.hpp"
|
||||
#include "api_lua.hpp"
|
||||
|
||||
static int l_json_stringify(lua::State* L) {
|
||||
|
|
@ -12,8 +13,12 @@ static int l_json_stringify(lua::State* L) {
|
|||
|
||||
static int l_json_parse(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
auto element = json::parse("[string]", string);
|
||||
return lua::pushvalue(L, element);
|
||||
try {
|
||||
auto element = json::parse("[string]", string);
|
||||
return lua::pushvalue(L, element);
|
||||
} catch (const parsing_error& err) {
|
||||
throw err.toRuntimeError();
|
||||
}
|
||||
}
|
||||
|
||||
const luaL_Reg jsonlib[] = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "coders/toml.hpp"
|
||||
#include "coders/commons.hpp"
|
||||
#include "api_lua.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
|
@ -16,8 +17,12 @@ static int l_toml_stringify(lua::State* L) {
|
|||
|
||||
static int l_toml_parse(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
auto element = toml::parse("[string]", string);
|
||||
return lua::pushvalue(L, element);
|
||||
try {
|
||||
auto element = toml::parse("[string]", string);
|
||||
return lua::pushvalue(L, element);
|
||||
} catch (const parsing_error& err) {
|
||||
throw err.toRuntimeError();
|
||||
}
|
||||
}
|
||||
|
||||
const luaL_Reg tomllib[] = {
|
||||
|
|
|
|||
114
src/logic/scripting/lua/libs/libxml.cpp
Normal file
114
src/logic/scripting/lua/libs/libxml.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#include "coders/xml.hpp"
|
||||
#include "coders/commons.hpp"
|
||||
#include "api_lua.hpp"
|
||||
|
||||
static const char* TAG_ATTR = "#";
|
||||
|
||||
static int push_xml(lua::State* L, const xml::xmlelement& elem) {
|
||||
if (elem.isText()) {
|
||||
return lua::pushlstring(L, elem.getInnerText());
|
||||
}
|
||||
|
||||
const auto& attrs = elem.getAttributes();
|
||||
const auto& elems = elem.getElements();
|
||||
const auto& tag = elem.getTag();
|
||||
|
||||
lua::createtable(L, elem.size(), attrs.size() + 1);
|
||||
lua::pushlstring(L, tag);
|
||||
lua::setfield(L, TAG_ATTR);
|
||||
|
||||
for (int i = 0; i < elems.size(); i++) {
|
||||
push_xml(L, *elems[i]);
|
||||
lua::rawseti(L, i + 1);
|
||||
}
|
||||
|
||||
for (const auto& [key, value] : attrs) {
|
||||
lua::pushlstring(L, value.getText());
|
||||
lua::setfield(L, key);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static std::unique_ptr<xml::xmlelement> toxml(lua::State* L) {
|
||||
lua::getfield(L, TAG_ATTR);
|
||||
auto tag = lua::require_lstring(L, -1);
|
||||
lua::pop(L);
|
||||
|
||||
auto elem = std::make_unique<xml::xmlelement>(std::string(tag));
|
||||
int length = lua::objlen(L, -1);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
lua::rawgeti(L, i + 1);
|
||||
elem->add(toxml(L));
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
lua::pushnil(L);
|
||||
while (lua::next(L, -2)) {
|
||||
lua::pushvalue(L, -2);
|
||||
do {
|
||||
if (!lua::isstring(L, -1)) {
|
||||
break;
|
||||
}
|
||||
auto key = lua::require_lstring(L, -1);
|
||||
if (key == "@") {
|
||||
break;
|
||||
}
|
||||
auto value = lua::require_lstring(L, -2);
|
||||
elem->set(std::string(key), std::string(value));
|
||||
} while (false);
|
||||
|
||||
lua::pop(L, 2);
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
static int l_tostring(lua::State* L) {
|
||||
bool nice = lua::isboolean(L, 2) ? lua::toboolean(L, 2) : true;
|
||||
lua::pushvalue(L, 1);
|
||||
auto xml = toxml(L);
|
||||
lua::pop(L);
|
||||
return lua::pushstring(L, xml::stringify(*xml, nice));
|
||||
}
|
||||
|
||||
static int l_parse(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
try {
|
||||
auto document = xml::parse("[string]", string);
|
||||
return push_xml(L, *document->getRoot());
|
||||
} catch (const parsing_error& err) {
|
||||
throw err.toRuntimeError();
|
||||
}
|
||||
}
|
||||
|
||||
static int l_parse_vcd(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
auto rootTag = lua::tostring(L, 2);
|
||||
|
||||
std::unique_ptr<xml::Document> document;
|
||||
try {
|
||||
document = xml::parse_vcm(
|
||||
"[string]", string, rootTag ? rootTag : ""
|
||||
);
|
||||
} catch (const parsing_error& err) {
|
||||
throw err.toRuntimeError();
|
||||
}
|
||||
const auto& root = *document->getRoot();
|
||||
if (rootTag != nullptr) {
|
||||
return push_xml(L, root);
|
||||
}
|
||||
int rootElements = root.size();
|
||||
if (rootElements != 1) {
|
||||
throw std::runtime_error(
|
||||
"one root element expected, got " + std::to_string(rootElements)
|
||||
);
|
||||
}
|
||||
return push_xml(L, *root.getElements()[0]);
|
||||
}
|
||||
|
||||
const luaL_Reg xmllib[] = {
|
||||
{"tostring", lua::wrap<l_tostring>},
|
||||
{"parse", lua::wrap<l_parse>},
|
||||
{"parse_vcd", lua::wrap<l_parse_vcd>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "coders/yaml.hpp"
|
||||
#include "coders/commons.hpp"
|
||||
#include "api_lua.hpp"
|
||||
|
||||
static int l_stringify(lua::State* L) {
|
||||
|
|
@ -9,8 +10,12 @@ static int l_stringify(lua::State* L) {
|
|||
|
||||
static int l_parse(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
auto element = yaml::parse("[string]", string);
|
||||
return lua::pushvalue(L, element);
|
||||
try {
|
||||
auto element = yaml::parse("[string]", string);
|
||||
return lua::pushvalue(L, element);
|
||||
} catch (const parsing_error& err) {
|
||||
throw err.toRuntimeError();
|
||||
}
|
||||
}
|
||||
|
||||
const luaL_Reg yamllib[] = {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ static void create_libs(State* L, StateType stateType) {
|
|||
openlib(L, "vec2", vec2lib);
|
||||
openlib(L, "vec3", vec3lib);
|
||||
openlib(L, "vec4", vec4lib);
|
||||
openlib(L, "xml", xmllib);
|
||||
openlib(L, "yaml", yamllib);
|
||||
|
||||
openlib(L, "__vc_app", applib);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue