diff --git a/doc/en/entity-properties.md b/doc/en/entity-properties.md index b0aee9aea..e25c6d71d 100644 --- a/doc/en/entity-properties.md +++ b/doc/en/entity-properties.md @@ -67,9 +67,10 @@ If true - the entity blocks the placement of a block that intersects with the en Default value: *true*. -### *selectable* +### *selectable* (raycast opacity) If set to `false` the cursor will ignore the entity, passing the ray. +Applies to all uses of raycast without the include_non_selectable flag. ### *sensors* diff --git a/doc/en/scripting/builtins/libapp.md b/doc/en/scripting/builtins/libapp.md index 48d79bf8e..06365fc99 100644 --- a/doc/en/scripting/builtins/libapp.md +++ b/doc/en/scripting/builtins/libapp.md @@ -9,21 +9,22 @@ local filename = "script:"..app.script..".lua" Since the control script may not belong to any of the packs, it does not belongs to its own package and has its own global namespace in which all global functions and tables are available, as well as the `app` library. -## Functions + +## Common functions ```lua +-- Performs one tick of the main engine loop. app.tick() -``` -Performs one tick of the main engine loop. - -```lua +-- Waits for the specified time in seconds, performing the main engine loop. app.sleep(time: number) -``` -Waits for the specified time in seconds, performing the main engine loop. +-- Terminates the engine, printing the call stack +-- to trace the function call location. +app.quit() -```lua +-- Waits for the condition checked by the function to be true, +-- performing the main engine loop. app.sleep_until( -- function that checks the condition for ending the wait predicate: function() -> bool, @@ -36,51 +37,54 @@ app.sleep_until( ) ``` -Waits for the condition checked by the function to be true, performing the main engine loop. +## Content ```lua -app.quit() -``` +-- Checks if content is loaded. +app.is_content_loaded() -> bool -Terminates the engine, printing the call stack to trace the function call location. +-- Returns the current content configuration +-- (list of packs IDs in loading order) +app.get_content() -> table -```lua +-- Loads content based on the current configuration. +-- Cannot be used if content is already loaded (see app.reset_content). +app.load_content() + +-- Unloads all content, resetting it to a single core pack. +app.reset_content( + -- Packs for which modules, events, and environment will not be reset + [optional] non_reset_packs: table +) + +-- Updates the packs configuration, checking its correctness +-- (dependencies and availability of packs). +-- Automatically adds and reorders packs based on dependencies. app.reconfig_packs( -- packs to add add_packs: table, -- packs to remove remove_packs: table ) -``` -Updates the packs configuration, checking its correctness (dependencies and availability of packs). -Automatically adds dependencies. - -To remove all packs from the configuration, you can use `pack.get_installed()`: - -```lua +-- To remove all packs from the configuration,you can use `pack.get_installed()`: app.reconfig_packs({}, pack.get_installed()) -``` +-- In this case, `base` will also be removed from the configuration. -In this case, `base` will also be removed from the configuration. - -```lua +-- Updates the packs configuration, automatically removing unspecified ones, +-- adding those missing in the previous configuration. +-- Uses app.reconfig_packs. app.config_packs( -- expected set of packs (excluding dependencies) packs: table ) ``` -Updates the packs configuration, automatically removing unspecified ones, adding those missing in the previous configuration. -Uses app.reconfig_packs. - -```lua -app.is_content_loaded() -> bool -``` - -Checks if content is loaded. +## Worlds + ```lua +-- Creates a new world and opens it. app.new_world( -- world name, empty string will create a nameless world name: str, @@ -91,62 +95,42 @@ app.new_world( -- local player id [optional] local_player: int=0 ) -``` -Creates a new world and opens it. +-- Deletes a world by name. +app.delete_world(name: str) -```lua +-- Opens a world by name. app.open_world(name: str) -``` -Opens a world by name. - -```lua +-- Reopens the world. app.reopen_world() -``` -Reopens the world. - -```lua +-- Saves the world. app.save_world() -``` -Saves the world. - -```lua +-- Closes the world. app.close_world( -- save the world before closing [optional] save_world: bool=false ) ``` -Closes the world. - -```lua -app.delete_world(name: str) -``` - -Deletes a world by name. +## Engine settings and information ```lua +-- Returns the major and minor versions of the engine. app.get_version() -> int, int -``` -Returns the major and minor versions of the engine. - -```lua +-- Returns the value of a setting. +-- Throws an exception if the setting does not exist. app.get_setting(name: str) -> value -``` -Returns the value of a setting. Throws an exception if the setting does not exist. - -```lua +-- Sets the value of a setting. +-- Throws an exception if the setting does not exist. app.set_setting(name: str, value: value) -``` -Sets the value of a setting. Throws an exception if the setting does not exist. - -```lua +-- Returns a table with information about a setting. +-- Throws an exception if the setting does not exist. app.get_setting_info(name: str) -> { -- default value def: value, @@ -155,43 +139,30 @@ app.get_setting_info(name: str) -> { -- maximum value [only for numeric settings] max: number } -``` -Returns a table with information about a setting. Throws an exception if the setting does not exist. - -```lua +-- Brings the window to front and sets input focus. app.focus() ``` -Brings the window to front and sets input focus. +## Paths and entry points ```lua +-- Creates an in-memory filesystem. app.create_memory_device( -- entry-point name name: str ) -``` -Creates an in-memory filesystem. - -```lua +-- Returns a list of content sources (paths), in descending priority order. app.get_content_sources() -> table -``` -Returns a list of content sources (paths), in descending priority order. - -```lua +-- Sets a list of content sources (paths). Specified in descending priority order. app.set_content_sources(sources: table) -``` -Sets a list of content sources (paths). Specified in descending priority order. - -```lua +-- Resets content sources. app.reset_content_sources() ``` -Resets content sources. - ## Sub-instances ```lua @@ -209,5 +180,5 @@ app.is_instance_alive(handle: int) -> boolean -- Stops the engine sub-instance. -- Returns true if the instance was alive at the time of the call. -app.terminate(handle: int) -> boolean +app.terminate_instance(handle: int) -> boolean ``` diff --git a/doc/en/scripting/builtins/libtime.md b/doc/en/scripting/builtins/libtime.md index 51e125e9c..320e330d1 100644 --- a/doc/en/scripting/builtins/libtime.md +++ b/doc/en/scripting/builtins/libtime.md @@ -10,6 +10,9 @@ time.delta() -> number -- Returns UTC time in seconds time.utc_time() -> int +-- Returns UTC time in seconds with millisecond precision +time.precise_utc_time() -> number + -- Returns local (system) time in seconds time.local_time() -> int diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index 9636cf763..f8bce6d3f 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -36,6 +36,11 @@ entity:set_enabled(name: str, enable: bool) -- At components initialization -1 is also returned, -- since the binding occurs after initialization. entity:get_player() -> int or nil + +-- Checks the 'selectable' property of an entity (raycast opacity) +body:is_selectable() -> bool +-- Sets the value of the 'selectable' property of an entity +body:set_selectable(flag: bool) ``` ## Custom Components diff --git a/doc/ru/entity-properties.md b/doc/ru/entity-properties.md index 2f5ca3791..71dc6dc88 100644 --- a/doc/ru/entity-properties.md +++ b/doc/ru/entity-properties.md @@ -67,9 +67,10 @@ Значение по-умолчанию: *true*. -### Выделяемость - *selectable* +### Непрозрачность для лучей - *selectable* При значении в `false` курсор будет игнорировать cущность, пропуская луч далее. +Распространяется на все использования raycast без флага include_non_selectable. ### Список сенсоров - *sensors* diff --git a/doc/ru/scripting/builtins/libapp.md b/doc/ru/scripting/builtins/libapp.md index bbdf5cd2f..53a2224f2 100644 --- a/doc/ru/scripting/builtins/libapp.md +++ b/doc/ru/scripting/builtins/libapp.md @@ -28,7 +28,8 @@ app.sleep(time: number) -- Завершает выполнение движка, выводя стек вызовов для ослеживания места вызова функции. app.quit() --- Ожидает истинности утверждения (условия), проверяемого функцией, выполняя основной цикл движка. +-- Ожидает истинности утверждения (условия), проверяемого функцией, +-- выполняя основной цикл движка. app.sleep_until( -- функция, проверяющее условия завершения ожидания predicate: function() -> boolean, @@ -46,7 +47,11 @@ app.sleep_until( -- Проверяет, загружен ли контент. app.is_content_loaded() -> boolean --- Загружает контент из конфига, нельзя использовать, если контент уже загружен +-- Возвращает текущую конфигурацию контента (список id паков в порядке загрузки) +app.get_content() -> table + +-- Загружает контент на основе текущей конфигурации. +-- Не может быть использован, если контент уже загружен (см. app.reset_content). app.load_content() -- Выгружает весь контент, сбрасывая до единственного пака ядра (`core`). @@ -56,7 +61,7 @@ app.reset_content( ) -- Обновляет конфигурацию паков, проверяя её корректность (зависимости и доступность паков). --- Автоматически добавляет зависимости. +-- Автоматически добавляет и упорядочивает паки в соответствии с зависимостями. -- Для удаления ВСЕХ паков из конфигурации можно использовать `pack.get_installed()` app.reconfig_packs( -- добавляемые паки @@ -107,19 +112,22 @@ app.close_world( ) ``` - ## Свойства и настройки движка + ```lua -- Возвращает мажорную и минорную версии движка. app.get_version() -> int, int --- Возвращает значение настройки. Бросает исключение, если настройки не существует. +-- Возвращает значение настройки. +-- Бросает исключение, если настройки не существует. app.get_setting(name: string) -> any --- Устанавливает значение настройки. Бросает исключение, если настройки не существует. +-- Устанавливает значение настройки. +-- Бросает исключение, если настройки не существует. app.set_setting(name: string, value: any) --- Возвращает таблицу с информацией о настройке. Бросает исключение, если настройки не существует. +-- Возвращает таблицу с информацией о настройке. +-- Бросает исключение, если настройки не существует. app.get_setting_info(name: string) -> table -- Переводит окно на передний план и устанавливает фокус ввода. @@ -127,6 +135,7 @@ app.focus() ``` ## Точки входа и пути + ```lua -- Создаёт файловую систему в памяти. app.create_memory_device( diff --git a/doc/ru/scripting/builtins/libtime.md b/doc/ru/scripting/builtins/libtime.md index 62e95892a..86d02488f 100644 --- a/doc/ru/scripting/builtins/libtime.md +++ b/doc/ru/scripting/builtins/libtime.md @@ -1,15 +1,18 @@ # Библиотека *time* ```lua --- Возвращает время с момента запуска движка в секундах и милисекундах. +-- Возвращает время с момента запуска движка в секундах и миллисекундах. time.uptime() -> number --- Возвращает дельту времени в секундах и милисекундах (время прошедшее с предыдущего кадра) +-- Возвращает дельту времени в секундах и миллисекундах (время прошедшее с предыдущего кадра) time.delta() -> number --- Возвращает время UTC в секундах +-- Возвращает время UTC (секунды с 1970-01-01 UTC) time.utc_time() -> int +-- Возвращает время UTC (секунды с 1970-01-01 UTC) с точностью до миллисекунд +time.precise_utc_time() -> number + -- Возвращает локальное (системное) время в секундах time.local_time() -> int diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index cb4a79bec..be99774d5 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -134,6 +134,11 @@ body:set_elasticity(elasticity: number) -- Возвращает скорость поверхности, на которой находится тело, либо {0,0,0} body:get_ground_vel() -> vec3 + +-- Проверяет свойство 'selectable' у сущности (непрозрачность для лучей) +body:is_selectable() -> bool +-- Устанавливает значение свойство 'selectable' у сущности +body:set_selectable(flag: bool) ``` ### Skeleton diff --git a/res/layouts/pages/settings_audio.xml.lua b/res/layouts/pages/settings_audio.xml.lua index 475ccc51b..97628d1e8 100644 --- a/res/layouts/pages/settings_audio.xml.lua +++ b/res/layouts/pages/settings_audio.xml.lua @@ -31,6 +31,14 @@ function update_checkbox_setting(id, value) document.input_device_select.enabled = recording_enabled document.input_volume_inner.enabled = recording_enabled document.input_volume_outer.enabled = recording_enabled + + local selectbox = document.input_device_select + local info = audio.input.get_input_info() + if info then + selectbox.value = info.device_specifier + else + selectbox.value = app.get_setting("audio.input-device") + end end function create_checkbox(id, name, tooltip) @@ -84,6 +92,7 @@ function on_open() table.insert(devices, {value=name, text=name}) end selectbox.options = devices + local info = audio.input.get_input_info() if info then selectbox.value = info.device_specifier diff --git a/res/modules/internal/stdcomp.lua b/res/modules/internal/stdcomp.lua index dbf7f9776..ac11692ca 100644 --- a/res/modules/internal/stdcomp.lua +++ b/res/modules/internal/stdcomp.lua @@ -39,6 +39,8 @@ local Rigidbody = {__index={ set_mass=function(self, v) return __rigidbody.set_mass(self.eid, v) end, set_elasticity=function(self, v) return __rigidbody.set_elasticity(self.eid, v) end, get_ground_vel=function(self) return __rigidbody.get_ground_vel(self.eid) end, + is_selectable=function (self) return __rigidbody.is_selectable(self.eid) end, + set_selectable=function (self, b) return __rigidbody.set_selectable(self.eid, b) end, }} local function new_Rigidbody(eid) diff --git a/res/scripts/post_content.lua b/res/scripts/post_content.lua index d6d48fbd1..97b2e93b6 100644 --- a/res/scripts/post_content.lua +++ b/res/scripts/post_content.lua @@ -1,3 +1,6 @@ +local app = __vc_app +local internals = __vc_internals + local user_props = file.read_combined_object("config/user-props.toml") local names = { "parent", "caption", "texture", "texture-faces", "model", "model-name", @@ -42,7 +45,7 @@ local function make_read_only(t) __newindex = function() error("table is read-only") end - }) + }) end make_read_only(block.properties) @@ -85,3 +88,33 @@ cache_names(block) cache_names(item) __vc_scripts_registry.build_registry() + +local packs = app.get_content() +table.insert(packs, 1, "core") + +for i, packid in ipairs(packs) do + local loaders_dir = packid..":scripts/loaders" + if not file.isdir(loaders_dir) then + goto continue + end + local files = file.list(loaders_dir) + for _, path in ipairs(files) do + if file.ext(path) == "json" then + debug.log("registering loader "..path) + local dto = json.parse(file.read(path)) + local module = setmetatable({}, {__index=internals.get_pack_env(packid)}) + local module_path = file.join(file.parent(path), file.stem(path)..".lua") + if not file.exists(module_path) then + error("loader "..path.." missing module "..module_path) + end + load(file.read(module_path), module_path, "t", module)() + if rawget(module, "execute") == nil then + error("loader "..path.." missing function 'execute'") + end + if dto.type == "compiler" then + internals.register_compiler(packid, dto.extensions, module) + end + end + end + ::continue:: +end diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 2869bba1c..56d86bf8f 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -392,6 +392,7 @@ end ffi = nil __vc_app = nil +__vc_internals = nil __vc_lock_internal_modules() __vc_lock_internal_modules = nil __vc_update_coroutines = nil diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 03de8cbaa..e1b735478 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -154,6 +154,20 @@ package = { } local __cached_scripts = {} local __warnings_hidden = {} +local __compilers = {} + +function __vc_internals.register_compiler(sourcepack, extensions, module) + local compiler = { + packid = sourcepack, + extensions = extensions, + module = module, + } + for i, ext in ipairs(extensions) do + if not __compilers[ext] then + __compilers[ext] = compiler + end + end +end function on_deprecated_call(name, alternatives) if __warnings_hidden[name] then @@ -200,6 +214,21 @@ end local __internal_locked = false +local default_compiler = { + module = { + execute = function(code, path, env) + local script, err = load(code, path) + if script == nil then + error(err) + end + if env then + script = setfenv(script, env) + end + return script + end + } +} + -- Load script with caching -- -- path - script path `contentpack:filename`. @@ -211,6 +240,9 @@ function __load_script(path, nocache, env) local is_internal = (packname == "res" or packname == "core") and filename:find("modules/internal") == 1 + local ext = path:match("%.([^:/\\]+)$") + local compiler = __compilers[ext] or default_compiler + nocache = nocache or is_internal if is_internal and __internal_locked then @@ -225,13 +257,7 @@ function __load_script(path, nocache, env) error("script '"..filename.."' not found in '"..packname.."'") end - local script, err = load(file.read(path), path) - if script == nil then - error(err) - end - if env then - script = setfenv(script, env) - end + local script = compiler.module.execute(file.read(path), path, env) local result = script() if not nocache then __cached_scripts[path] = script @@ -245,6 +271,9 @@ function __vc_lock_internal_modules() end local __pack_envs = __vc__pack_envs +function __vc_internals.get_pack_env(packid) + return __pack_envs[packid] +end __vc__pack_envs = nil function require(path) @@ -277,6 +306,13 @@ function __scripts_cleanup(non_reset_packs) __pack_envs[packname] = nil ::continue:: end + for ext, compiler in pairs(__compilers) do + if table.has(non_reset_packs, compiler.packid) then + goto continue + end + __compilers[ext] = nil + ::continue:: + end end function __vc__error(msg, frame, n, lastn) diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 23b316e6b..581e6e109 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -47,9 +47,10 @@ void AssetsLoader::add( AssetType tag, const std::string& filename, const std::string& alias, - std::shared_ptr settings + std::shared_ptr settings, + bool overwrite ) { - if (enqueued.find({tag, alias}) != enqueued.end()){ + if (!overwrite && enqueued.find({tag, alias}) != enqueued.end()){ return; } entries.push(aloader_entry {tag, filename, alias, std::move(settings)}); @@ -154,11 +155,12 @@ void AssetsLoader::processPreload( std::string defFolder = assets_def_folder(tag); std::string path = defFolder + "/" + name; if (map == nullptr) { - add(tag, path, name); + add(tag, path, name, nullptr, true); return; } std::shared_ptr config = nullptr; map.at("path").get(path); + logger.debug() << "processing preload " << util::quote(name) << " path: " << util::quote(path); switch (tag) { case AssetType::SOUND: { bool keepPCM = false; @@ -196,7 +198,7 @@ void AssetsLoader::processPreload( default: break; } - add(tag, path, name, std::move(config)); + add(tag, path, name, std::move(config), true); } void AssetsLoader::processPreloadList(AssetType tag, const dv::value& list) { diff --git a/src/assets/AssetsLoader.hpp b/src/assets/AssetsLoader.hpp index e237e01e9..d919d1f08 100644 --- a/src/assets/AssetsLoader.hpp +++ b/src/assets/AssetsLoader.hpp @@ -110,7 +110,8 @@ public: AssetType tag, const std::string& filename, const std::string& alias, - std::shared_ptr settings = nullptr + std::shared_ptr settings = nullptr, + bool overwrite = false ); bool hasNext() const; diff --git a/src/audio/AL/ALAudio.cpp b/src/audio/AL/ALAudio.cpp index d681d15a2..c3ec5b24f 100644 --- a/src/audio/AL/ALAudio.cpp +++ b/src/audio/AL/ALAudio.cpp @@ -5,6 +5,7 @@ #include "debug/Logger.hpp" #include "alutil.hpp" +#include "util/stringutil.hpp" #include "../MemoryPCMStream.hpp" static debug::Logger logger("al-audio"); @@ -99,6 +100,8 @@ ALInputDevice::ALInputDevice( if (deviceName) { deviceSpecifier = std::string(deviceName); + logger.info() << "created input device with specifier " + << util::quote(deviceSpecifier); } else { logger.warning() << "could not retrieve input device specifier"; } diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index b037addcb..ee35abeeb 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -211,12 +211,15 @@ void audio::initialize( return; } if (enabled) { - logger.info() << "recording enabled; input device is " - << settings.inputDevice.get(); + const auto& targetDevice = settings.inputDevice.get(); + logger.info() << "recording enabled; target input device is " + << (targetDevice.empty() ? "not specified" + : targetDevice); ::input_device = backend->openInputDevice( settings.inputDevice.get(), 44100, 1, 16 ); if (::input_device != nullptr) { + logger.error() << "could not create input device"; ::input_device->startCapture(); } } else { diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp index a907b49db..f4f9799ed 100644 --- a/src/coders/xml.cpp +++ b/src/coders/xml.cpp @@ -208,6 +208,14 @@ namespace { class Parser : BasicParser { std::unique_ptr document; + void processEscapes(std::string& text) { + util::replaceAll(text, """, "\""); + util::replaceAll(text, "'", "'"); + util::replaceAll(text, "<", "<"); + util::replaceAll(text, ">", ">"); + util::replaceAll(text, "&", "&"); + } + std::unique_ptr parseOpenTag() { std::string tag = parseXMLName(); auto node = std::make_unique(tag); @@ -230,6 +238,7 @@ class Parser : BasicParser { } skip(1); attrtext = parseString(quote); + processEscapes(attrtext); } node->set(attrname, attrtext); } @@ -241,11 +250,7 @@ class Parser : BasicParser { if (peek() != '<') { auto element = std::make_unique("#"); auto text = parseText(); - util::replaceAll(text, """, "\""); - util::replaceAll(text, "'", "'"); - util::replaceAll(text, "<", "<"); - util::replaceAll(text, ">", ">"); - util::replaceAll(text, "&", "&"); + processEscapes(text); element->set("#", text); return element; } diff --git a/src/graphics/core/Texture.cpp b/src/graphics/core/Texture.cpp index 5f326d20c..65afd4342 100644 --- a/src/graphics/core/Texture.cpp +++ b/src/graphics/core/Texture.cpp @@ -46,7 +46,7 @@ void Texture::resize(uint width, uint height) { } void Texture::reload(const ImageData& image) { - reload(image.getData(), width, height); + reload(image.getData(), image.getWidth(), image.getHeight()); } void Texture::reload(const ubyte* data, uint width, uint height) { diff --git a/src/graphics/ui/elements/Panel.cpp b/src/graphics/ui/elements/Panel.cpp index 4d133c875..26401ee10 100644 --- a/src/graphics/ui/elements/Panel.cpp +++ b/src/graphics/ui/elements/Panel.cpp @@ -78,9 +78,10 @@ void Panel::refresh() { glm::vec2 size = getSize(); if (orientation == Orientation::VERTICAL) { float maxw = size.x; - for (auto& node : nodes) { + for (size_t i = 0; i < nodes.size(); i++) { + const auto& node = nodes[i]; const glm::vec4 margin = node->getMargin(); - y += margin.y; + y += margin.y + (i > 0 ? interval : 0); float ex = x + margin.x; node->setPos(glm::vec2(ex, y)); @@ -94,16 +95,17 @@ void Panel::refresh() { } node->refresh(); glm::vec2 nodeSize = node->getSize(); - y += nodeSize.y + margin.w + interval; + y += nodeSize.y + margin.w; maxw = fmax(maxw, ex + nodeSize.x + margin.z + padding.z); } actualLengthX = size.x; actualLengthY = y + padding.w; } else { float maxh = size.y; - for (auto& node : nodes) { + for (size_t i = 0; i < nodes.size(); i++) { + const auto& node = nodes[i]; const glm::vec4 margin = node->getMargin(); - x += margin.x; + x += margin.x + (i > 0 ? interval : 0); float ey = y + margin.y; node->setPos(glm::vec2(x, ey)); @@ -117,7 +119,7 @@ void Panel::refresh() { } node->refresh(); glm::vec2 nodesize = node->getSize(); - x += nodesize.x + margin.z + interval; + x += nodesize.x + margin.z; maxh = fmax(maxh, ey + nodesize.y + margin.w + padding.w); } actualLengthY = size.y; diff --git a/src/logic/scripting/lua/libs/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp index a36f2b080..5a65b1b53 100644 --- a/src/logic/scripting/lua/libs/lib__rigidbody.cpp +++ b/src/logic/scripting/lua/libs/lib__rigidbody.cpp @@ -199,6 +199,20 @@ static int l_get_ground_vel(lua::State* L) { return 0; } +static int l_is_selectable(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + return lua::pushboolean(L, entity->getRigidbody().hitbox.selectable); + } + return 0; +} + +static int l_set_selectable(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + entity->getRigidbody().selectable = lua::toboolean(L, 2); + } + return 0; +} + const luaL_Reg rigidbodylib[] = { {"is_enabled", lua::wrap}, @@ -226,5 +240,7 @@ const luaL_Reg rigidbodylib[] = { {"get_elasticity", lua::wrap}, {"set_elasticity", lua::wrap}, {"get_ground_vel", lua::wrap}, + {"is_selectable", lua::wrap}, + {"set_selectable", lua::wrap}, {nullptr, nullptr} }; diff --git a/src/logic/scripting/lua/libs/libapp.cpp b/src/logic/scripting/lua/libs/libapp.cpp index 84f319a9c..b5a385bb9 100644 --- a/src/logic/scripting/lua/libs/libapp.cpp +++ b/src/logic/scripting/lua/libs/libapp.cpp @@ -92,6 +92,17 @@ static int l_reconfig_packs(lua::State* L) { return 0; } +/// @brief Get current content packs configuration +static int l_get_content(lua::State* L) { + const auto& configuration = content_control->getContentPacks(); + lua::createtable(L, configuration.size(), 0); + for (int i = 0; i < configuration.size(); i++) { + lua::pushlstring(L, configuration[i].id); + lua::rawseti(L, i + 1); + } + return 1; +} + /// @brief Get content sources list static int l_get_content_sources(lua::State* L) { const auto& sources = engine->getContentControl().getContentSources(); @@ -453,6 +464,7 @@ const luaL_Reg applib[] = { {"load_content", lua::wrap}, {"reset_content", lua::wrap}, {"reconfig_packs", lua::wrap}, + {"get_content", lua::wrap}, {"get_content_sources", lua::wrap}, {"set_content_sources", lua::wrap}, {"reset_content_sources", lua::wrap}, diff --git a/src/logic/scripting/lua/libs/libtime.cpp b/src/logic/scripting/lua/libs/libtime.cpp index 342226035..88f49a6c0 100644 --- a/src/logic/scripting/lua/libs/libtime.cpp +++ b/src/logic/scripting/lua/libs/libtime.cpp @@ -1,5 +1,6 @@ -#include "engine/Engine.hpp" #include "api_lua.hpp" +#include "engine/Engine.hpp" + #include using namespace scripting; @@ -20,11 +21,18 @@ static int l_utc_time(lua::State* L) { return lua::pushnumber(L, std::time(nullptr)); } +static int l_precise_utc_time(lua::State* L) { + auto now = std::chrono::system_clock::now(); + return lua::pushnumber( + L, std::chrono::duration(now.time_since_epoch()).count() + ); +} + static int l_local_time(lua::State* L) { std::time_t t = std::time(nullptr); - std::tm gmt_tm{}; - std::tm local_tm{}; + std::tm gmt_tm {}; + std::tm local_tm {}; #if defined(USE_MSVC_TIME_SAFE) gmtime_s(&gmt_tm, &t); @@ -44,8 +52,8 @@ static int l_local_time(lua::State* L) { static int l_utc_offset(lua::State* L) { std::time_t t = std::time(nullptr); - std::tm gmt_tm{}; - std::tm local_tm{}; + std::tm gmt_tm {}; + std::tm local_tm {}; #if defined(USE_MSVC_TIME_SAFE) gmtime_s(&gmt_tm, &t); @@ -74,6 +82,7 @@ const luaL_Reg timelib[] = { {"uptime", lua::wrap}, {"delta", lua::wrap}, {"utc_time", lua::wrap}, + {"precise_utc_time", lua::wrap}, {"utc_offset", lua::wrap}, {"local_time", lua::wrap}, {"precise_time", lua::wrap}, diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 78708deca..850b34109 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -74,6 +74,11 @@ static void create_libs(State* L, StateType stateType) { getglobal(L, "__vc_app"); setregistry(L, "app"); + createtable(L, 0, 0); + pushvalue(L, -1); + setglobal(L, "__vc_internals"); + setregistry(L, "internals"); + if (stateType == StateType::SCRIPT) { getregistry(L, "app"); setglobal(L, "app"); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index d448a8421..15da3697e 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -288,7 +288,23 @@ void scripting::on_content_load(Content* content) { lua::setfield(L, "properties"); lua::pop(L); } - load_script("post_content.lua", true); + + lua::getregistry(L, "app"); + lua::setglobal(L, "__vc_app"); + lua::getregistry(L, "internals"); + lua::setglobal(L, "__vc_internals"); + try { + load_script("post_content.lua", true); + } catch (const std::exception&) { + lua::pushnil(L); + lua::setglobal(L, "__vc_app"); + throw; + } + lua::pushnil(L); + lua::setglobal(L, "__vc_app"); + lua::pushnil(L); + lua::setglobal(L, "__vc_internals"); + load_script("stdcmd.lua", true); } diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 211d22e61..039c358d8 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -175,9 +175,10 @@ std::optional Entities::rayCast( glm::ivec3 foundNormal; for (auto [entity, eid, transform, body] : view.each()) { + const auto& hitbox = body.hitbox; if (eid.uid == settings.ignoredUid || !body.enabled || (settings.solidEntitiesOnly && !eid.def.solid) || - (!eid.def.selectable && !settings.includeNonSelectable)) { + (!hitbox.selectable && !settings.includeNonSelectable)) { continue; } if (settings.entitiesFilter) { @@ -187,7 +188,6 @@ std::optional Entities::rayCast( continue; } } - const auto& hitbox = body.hitbox; glm::ivec3 normal; double distance; if (ray.intersectAABB( @@ -330,6 +330,7 @@ void Entities::preparePhysics(float delta) { ? rigidbody.mass : std::numeric_limits::infinity(); rigidbody.hitbox.elasticity = rigidbody.elasticity; + rigidbody.hitbox.selectable = rigidbody.selectable; hitboxes.emplace_back(&rigidbody.hitbox); if (!eid.def.solid) { continue; diff --git a/src/objects/Rigidbody.cpp b/src/objects/Rigidbody.cpp index 2426aabac..337bb9f67 100644 --- a/src/objects/Rigidbody.cpp +++ b/src/objects/Rigidbody.cpp @@ -24,6 +24,7 @@ dv::value Rigidbody::serialize(bool saveVelocity, bool saveBodySettings) const { } bodymap["mass"] = mass; bodymap["elasticity"] = elasticity; + bodymap["selectable"] = selectable; } return bodymap; } @@ -37,6 +38,7 @@ void Rigidbody::deserialize(const dv::value& root) { root["damping"].asNumber(hitbox.linearDamping); root["mass"].asNumber(mass); root["elasticity"].asNumber(elasticity); + root["selectable"].asBoolean(selectable); } template @@ -55,6 +57,7 @@ void Rigidbody::initialize( ) { mass = def.mass; elasticity = def.elasticity; + selectable = def.selectable; sensors.resize(def.radialSensors.size() + def.boxSensors.size()); for (auto& [i, box] : def.boxSensors) { SensorParams params {}; diff --git a/src/objects/Rigidbody.hpp b/src/objects/Rigidbody.hpp index bc2a85f90..fb96f138f 100644 --- a/src/objects/Rigidbody.hpp +++ b/src/objects/Rigidbody.hpp @@ -15,6 +15,7 @@ struct Rigidbody { std::vector sensors; float mass; float elasticity; + bool selectable; dv::value serialize(bool saveVelocity, bool saveBodySettings) const; void deserialize(const dv::value& root); diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 80c628491..91cabb2b3 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -65,6 +65,7 @@ struct Hitbox { float stepHeight = 0.5f; float mass = 1.0f; float elasticity = 0.0f; + bool selectable = true; std::string material; std::string groundMaterial; glm::vec3 groundVelocity {}; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 7ecc07f05..a3a190f82 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -122,7 +122,7 @@ static void calc_collision( auto aabb = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 8.0f; + scale[nz] = 1.0f - E * 2.0f; aabb.scale(scale); aabb = aabb + offset; aabb.b.y -= stepHeight + E * 2; diff --git a/src/settings.hpp b/src/settings.hpp index 5220ca328..bc83ba345 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -18,7 +18,7 @@ struct AudioSettings { NumberSetting volumeAmbient {1.0f, 0.0f, 1.0f, setting_format::percent}; NumberSetting volumeMusic {1.0f, 0.0f, 1.0f, setting_format::percent}; - StringSetting inputDevice {"auto"}; + StringSetting inputDevice {""}; FlagSetting acousticEffects {true}; }; diff --git a/src/world/files/WorldConverter.cpp b/src/world/files/WorldConverter.cpp index 2f7716d3f..73a525d87 100644 --- a/src/world/files/WorldConverter.cpp +++ b/src/world/files/WorldConverter.cpp @@ -109,7 +109,7 @@ void WorldConverter::createBlockFieldsConvertTasks() { REGION_LAYER_BLOCKS_DATA, ConvertTaskType::CONVERT_BLOCKS_DATA ); - break; + return; default: break; } @@ -189,7 +189,7 @@ void WorldConverter::upgradeRegion( } void WorldConverter::convertVoxels(const io::path& file, int x, int z) const { - logger.info() << "converting voxels region " << x << "_" << z; + logger.info() << "converting voxels in region " << x << "_" << z; wfile->getRegions().processRegion(x, z, REGION_LAYER_VOXELS, [=](std::unique_ptr data, uint32_t*) { Chunk::convert(data.get(), report.get()); @@ -198,7 +198,7 @@ void WorldConverter::convertVoxels(const io::path& file, int x, int z) const { } void WorldConverter::convertInventories(const io::path& file, int x, int z) const { - logger.info() << "converting inventories region " << x << "_" << z; + logger.info() << "converting inventories in region " << x << "_" << z; wfile->getRegions().processInventories(x, z, [=](Inventory* inventory) { inventory->convert(report.get()); }); @@ -212,7 +212,7 @@ void WorldConverter::convertPlayer(const io::path& file) const { } void WorldConverter::convertBlocksData(int x, int z, const ContentReport& report) const { - logger.info() << "converting blocks data"; + logger.info() << "converting blocks data in region " << x << "_" << z; wfile->getRegions().processBlocksData(x, z, [=](BlocksMetadata* heap, std::unique_ptr voxelsData) { Chunk chunk(0, 0);