mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-09-11 03:13:20 -04:00
luau_runtime reorganization + audio_res service via Lua
Primary focus was to implement audio_res in Lua. As part of this I ended up re-organizing much of the luau_runtime to be less thrown together. The audio_res service relies on the (newly fetched) LuaBridge3 library to reduce all the boiler plate. All in all, I'm quite happy with how it turned out. I did have to fork LuaBridge3 to fix some bugs though, so that tag is fetched right now: https://github.com/TwilitRealm/LuaBridge3
This commit is contained in:
@@ -26,15 +26,37 @@ FetchContent_Declare(luau
|
||||
)
|
||||
FetchContent_MakeAvailable(luau)
|
||||
|
||||
FetchContent_Declare(luabridge3
|
||||
URL https://github.com/TwilitRealm/LuaBridge3/archive/refs/tags/twilit-v1.zip
|
||||
URL_HASH SHA256=aa65e6e3446363d03842134182db28e6206e08d366b8efb8f66d26f96a3314be
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(luabridge3)
|
||||
|
||||
add_mod(luau_runtime
|
||||
SOURCES
|
||||
src/bindings.cpp
|
||||
src/config.cpp
|
||||
src/services/bindings.cpp
|
||||
src/services/config.cpp
|
||||
src/services/config.hpp
|
||||
src/services/services.cpp
|
||||
src/services/services.hpp
|
||||
src/services/ui.cpp
|
||||
src/services/ui.hpp
|
||||
src/lua_bridge_helpers.cpp
|
||||
src/lua_bridge_helpers.hpp
|
||||
src/lua_helpers.cpp
|
||||
src/lua_helpers.hpp
|
||||
src/runtime.hpp
|
||||
src/runtime.cpp
|
||||
src/ui.cpp
|
||||
MOD_JSON mod.json
|
||||
RES_DIR res
|
||||
BUNDLE
|
||||
)
|
||||
|
||||
target_link_libraries(luau_runtime PRIVATE Luau.VM Luau.Compiler)
|
||||
target_precompile_headers(luau_runtime PRIVATE src/lua_bridge_helpers.hpp src/lua_helpers.hpp)
|
||||
|
||||
# As far as I can tell, Luau already does stack checks.
|
||||
target_compile_definitions(luau_runtime PRIVATE LUABRIDGE_SAFE_STACK_CHECKS=0)
|
||||
add_subdirectory(src/services/audio_res)
|
||||
|
||||
target_link_libraries(luau_runtime PRIVATE Luau.VM Luau.Compiler LuaBridge)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "lua_bridge_helpers.hpp"
|
||||
|
||||
luabridge::TypeResult<std::reference_wrapper<luau_runtime::Vm>>
|
||||
luabridge::Stack<luau_runtime::Vm&>::get(lua_State* L, int) {
|
||||
auto& ref = luau_runtime::vm_from_registry(L);
|
||||
return std::reference_wrapper(ref);
|
||||
}
|
||||
|
||||
luabridge::TypeResult<std::reference_wrapper<const luau_runtime::Vm>>
|
||||
luabridge::Stack<const luau_runtime::Vm&>::get(lua_State* L, int) {
|
||||
auto const& ref = luau_runtime::vm_from_registry(L);
|
||||
return std::reference_wrapper(ref);
|
||||
}
|
||||
|
||||
namespace luau_runtime {
|
||||
|
||||
BridgeScriptHandle::BridgeScriptHandle(uint64_t const handle) noexcept : handle(handle) {
|
||||
|
||||
}
|
||||
|
||||
BridgeScriptHandle::~BridgeScriptHandle() = default;
|
||||
|
||||
void BridgeScriptHandle::unregister(lua_State* state, Vm& vm) {
|
||||
if (handle == 0) {
|
||||
luaL_error(state, "Stale handle");
|
||||
}
|
||||
|
||||
try {
|
||||
unregister_impl(state, vm);
|
||||
} catch (std::exception const&) {
|
||||
// If an exception occurs, disallow the Lua code from attempting to re-run unregister.
|
||||
// Since we assume the object may be in an undefined state (but hopefully not?)
|
||||
handle = 0;
|
||||
throw;
|
||||
}
|
||||
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "LuaBridge/LuaBridge.h"
|
||||
#include "runtime.hpp"
|
||||
|
||||
/**
|
||||
* Defines (i.e. implements) a LuaBridge3 Stack<T> converter for an enum that is passed by string name.
|
||||
*
|
||||
* @param type The type name of the enum to convert.
|
||||
* @param names The constant field containing the string name <-> enum value mapping.
|
||||
* @see DECLARE_STRING_ENUM
|
||||
*/
|
||||
#define DEFINE_STRING_ENUM(type, names) \
|
||||
Result Stack<type>::push(lua_State* L, type value) { \
|
||||
return Stack<std::string_view>::push(L, ::luau_runtime::lua_helpers::enum_value_to_str(L, value, names)); \
|
||||
} \
|
||||
\
|
||||
TypeResult<type> Stack<type>::get(lua_State* L, int index) { \
|
||||
const char* str = lua_tolstring(L, index, nullptr); \
|
||||
return ::luau_runtime::lua_helpers::enum_str_to_value(L, str, names); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a LuaBridge3 Stack<T> converter for an enum that is passed by string name.
|
||||
*
|
||||
* @param type The type name of the enum to convert.
|
||||
* @see DEFINE_STRING_ENUM
|
||||
*/
|
||||
#define DECLARE_STRING_ENUM(type) \
|
||||
template<> \
|
||||
struct Stack<type> { \
|
||||
[[nodiscard]] static Result push(lua_State* L, type value); \
|
||||
[[nodiscard]] static TypeResult<type> get(lua_State* L, int index); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Template specialization to allow luau_runtime::Vm& to be directly acquired
|
||||
* from a LuaBridge3 function.
|
||||
*/
|
||||
template <>
|
||||
struct luabridge::Stack<luau_runtime::Vm&> {
|
||||
[[nodiscard]] static TypeResult<std::reference_wrapper<luau_runtime::Vm>> get(lua_State* L, int);
|
||||
};
|
||||
|
||||
/**
|
||||
* Template specialization to allow luau_runtime::Vm const& to be directly acquired
|
||||
* from a LuaBridge3 function.
|
||||
*/
|
||||
template<>
|
||||
struct luabridge::Stack<luau_runtime::Vm const&> {
|
||||
[[nodiscard]] static TypeResult<std::reference_wrapper<luau_runtime::Vm const>> get(lua_State* L, int);
|
||||
};
|
||||
|
||||
namespace luau_runtime {
|
||||
|
||||
/**
|
||||
* Base type for "handle" types using LuaBridge3.
|
||||
*
|
||||
* Inherit from it, then register your derived class with LuaBridge3.
|
||||
* Provide the base class @ref unregister member function.
|
||||
*/
|
||||
class BridgeScriptHandle {
|
||||
protected:
|
||||
uint64_t handle;
|
||||
explicit BridgeScriptHandle(uint64_t handle) noexcept;
|
||||
virtual void unregister_impl(lua_State* state, Vm& vm) = 0;
|
||||
|
||||
public:
|
||||
virtual ~BridgeScriptHandle();
|
||||
void unregister(lua_State* state, Vm& vm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a @ref luaBridge::LuaRef to an *optional* value.
|
||||
*
|
||||
* This uses the default LuaBridge3 handling for std::optional,
|
||||
* but just makes the type specification and casting a bit less verbose.
|
||||
*
|
||||
* @tparam T The type contained in the resulting optional.
|
||||
* @tparam TLuaRef The type of the lua ref. Let this be inferred, as table items are unnameable.
|
||||
* @param value Lua ref to convert to the desired type.
|
||||
*/
|
||||
template <typename T, typename TLuaRef>
|
||||
std::optional<T> opt(TLuaRef const& value) {
|
||||
return value.template cast<std::optional<T>>().value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a @ref luaBridge::LuaRef to a value, with a default fallback if nil.
|
||||
*
|
||||
* This uses the default LuaBridge3 handling for std::optional,
|
||||
* but just makes the type specification and casting a bit less verbose.
|
||||
*
|
||||
* @tparam T The resulting type.
|
||||
* @tparam TLuaRef The type of the lua ref. Let this be inferred, as table items are unnameable.
|
||||
* @param value Lua ref to convert to the desired type.
|
||||
* @param default_value The default value if the lua item is nil.
|
||||
*/
|
||||
template<typename T, typename TLuaRef>
|
||||
T opt_or(TLuaRef const& value, T const& default_value) {
|
||||
return opt<T>(value).value_or(default_value);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#include "lua_helpers.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace luau_runtime::lua_helpers {
|
||||
|
||||
bool get_optional_bool(lua_State* state, int table, const char* field, bool fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
const bool value = lua_isnil(state, -1) ? fallback : luaL_checkboolean(state, -1) != 0;
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool to_int64(lua_State* state, int index, int64_t& outValue) {
|
||||
if (lua_isinteger64(state, index)) {
|
||||
outValue = lua_tointeger64(state, index, nullptr);
|
||||
return true;
|
||||
}
|
||||
if (!lua_isnumber(state, index)) {
|
||||
return false;
|
||||
}
|
||||
const double value = lua_tonumber(state, index);
|
||||
constexpr double kMaxSafeInteger = 9007199254740991.0;
|
||||
if (!std::isfinite(value) || value < -kMaxSafeInteger || value > kMaxSafeInteger ||
|
||||
std::trunc(value) != value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outValue = static_cast<int64_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t check_int64(lua_State* state, int index) {
|
||||
int64_t value = 0;
|
||||
if (!to_int64(state, index, value)) {
|
||||
luaL_argerror(state, index, "integer value expected");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
int64_t get_optional_int(lua_State* state, int table, const char* field, int64_t fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
const int64_t value = lua_isnil(state, -1) ? fallback : check_int64(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
double get_optional_number(lua_State* state, int table, const char* field, double fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
const double value = lua_isnil(state, -1) ? fallback : luaL_checknumber(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
double get_number(lua_State* state, int table, const char* field) {
|
||||
lua_getfield(state, table, field);
|
||||
const double value = luaL_checknumber(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t get_optional_uint32(lua_State* state, int table, const char* field, uint32_t fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
const uint32_t value = lua_isnil(state, -1) ? fallback : luaL_checkunsigned(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t get_uint32(lua_State* state, int table, const char* field) {
|
||||
lua_getfield(state, table, field);
|
||||
const uint32_t value = luaL_checkunsigned(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
int32_t get_optional_int32(lua_State* state, int table, const char* field, int32_t fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
const int32_t value = lua_isnil(state, -1) ? fallback : luaL_checkinteger(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string get_optional_string(
|
||||
lua_State* state, int table, const char* field, std::string_view fallback) {
|
||||
std::string result;
|
||||
lua_getfield(state, table, field);
|
||||
if (!lua_isnil(state, -1)) {
|
||||
size_t length = 0;
|
||||
const char* value = luaL_checklstring(state, -1, &length);
|
||||
result.assign(value, length);
|
||||
} else {
|
||||
result = fallback;
|
||||
}
|
||||
lua_pop(state, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ref_optional_function(lua_State* state, int table, const char* field) {
|
||||
lua_getfield(state, table, field);
|
||||
if (lua_isnil(state, -1)) {
|
||||
lua_pop(state, 1);
|
||||
return LUA_NOREF;
|
||||
}
|
||||
luaL_argexpected(state, lua_isfunction(state, -1), table, "function field");
|
||||
const int ref = lua_ref(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return ref;
|
||||
}
|
||||
|
||||
int ref_required_function(lua_State* state, int table, const char* field) {
|
||||
const int ref = ref_optional_function(state, table, field);
|
||||
if (ref == LUA_NOREF) {
|
||||
luaL_error(state, "field '%s' is required", field);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
|
||||
/*
|
||||
* Helper functions for API bound between Lua <-> C++
|
||||
*/
|
||||
|
||||
namespace luau_runtime::lua_helpers {
|
||||
|
||||
bool get_optional_bool(lua_State* state, int table, const char* field, bool fallback);
|
||||
double get_optional_number(lua_State* state, int table, const char* field, double fallback);
|
||||
int32_t get_optional_int32(lua_State* state, int table, const char* field, int32_t fallback);
|
||||
uint32_t get_optional_uint32(lua_State* state, int table, const char* field, uint32_t fallback);
|
||||
std::string get_optional_string(
|
||||
lua_State* state, int table, const char* field, std::string_view fallback = {});
|
||||
|
||||
bool to_int64(lua_State* state, int index, int64_t& outValue);
|
||||
int64_t check_int64(lua_State* state, int index);
|
||||
int64_t get_optional_int(lua_State* state, int table, const char* field, int64_t fallback);
|
||||
int ref_optional_function(lua_State* state, int table, const char* field);
|
||||
int ref_required_function(lua_State* state, int table, const char* field);
|
||||
|
||||
double get_number(lua_State* state, int table, const char* field);
|
||||
uint32_t get_uint32(lua_State* state, int table, const char* field);
|
||||
|
||||
/**
|
||||
* Concept that demands things be an enum. C++ is such a cool language.
|
||||
*/
|
||||
template<typename T>
|
||||
concept Enum = std::is_enum_v<T>;
|
||||
|
||||
/**
|
||||
* Specifies a name/value pair for an enum. This is used by helpers to do automatic conversions
|
||||
* between the native and (string) Lua form.
|
||||
* @tparam T The enum type
|
||||
*/
|
||||
template <Enum T>
|
||||
struct EnumName {
|
||||
T value;
|
||||
std::string_view name;
|
||||
|
||||
constexpr EnumName(T value, std::string_view name) : value(value), name(name) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert an enum's string name to the actual C++ value.
|
||||
*
|
||||
* Raises a Lua error if the string is not mapped.
|
||||
*
|
||||
* @tparam T Type of the enum
|
||||
* @param state The Lua state.
|
||||
* @param str The string value, from Lua.
|
||||
* @param options The predefined set of possible enum names.
|
||||
*/
|
||||
template <Enum T, size_t N>
|
||||
T enum_str_to_value(lua_State* state, char const* str, EnumName<T> const(& options)[N]) {
|
||||
std::string_view strv(str);
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (options[i].name == strv) {
|
||||
return static_cast<T>(i);
|
||||
}
|
||||
}
|
||||
|
||||
luaL_errorL(state, "Invalid enum value for %s: '%s'", typeid(T).name(), str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an enum's C++ value to the Lua-exposed string.
|
||||
*
|
||||
* Raises a Lua error if the string is not mapped.
|
||||
*
|
||||
* @tparam T Type of the enum
|
||||
* @param state The Lua state.
|
||||
* @param value The C++ enum value.
|
||||
* @param options The predefined set of possible enum names.
|
||||
*/
|
||||
template <Enum T, size_t N>
|
||||
std::string_view enum_value_to_str(lua_State* state, T value, EnumName<T> const(& options)[N]) {
|
||||
for (auto [nameValue, name] : options) {
|
||||
if (nameValue == value) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
luaL_errorL(state, "Attempted to return invalid enum to Lua!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "runtime.hpp"
|
||||
#include "services/services.hpp"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
#include "luacode.h"
|
||||
#include "mods/runtime.h"
|
||||
#include "mods/service.hpp"
|
||||
#include "mods/svc/audio_res.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -26,10 +28,15 @@ IMPORT_OPTIONAL_SERVICE(ResourceService, svc_resource);
|
||||
IMPORT_OPTIONAL_SERVICE(OverlayService, svc_overlay);
|
||||
IMPORT_OPTIONAL_SERVICE(TextureService, svc_texture);
|
||||
IMPORT_OPTIONAL_SERVICE(UiService, svc_ui);
|
||||
IMPORT_OPTIONAL_SERVICE(AudioResService, svc_audio_res);
|
||||
|
||||
namespace luau_runtime {
|
||||
namespace {
|
||||
|
||||
// Just here to provide a globally-unique address to use as a registry index.
|
||||
// Index into the Lua registry to store the Vm*.
|
||||
int vm_registry_index;
|
||||
|
||||
std::unordered_map<ModContext*, std::unique_ptr<Vm>> s_vms;
|
||||
|
||||
void* limited_realloc(void* userData, void* pointer, size_t oldSize, size_t newSize) {
|
||||
@@ -153,31 +160,6 @@ std::optional<std::string> normalize_module_path(
|
||||
return normalized;
|
||||
}
|
||||
|
||||
ModuleOpenFn module_factory(std::string_view name) {
|
||||
if (name == "dusklight.log") {
|
||||
return open_log;
|
||||
}
|
||||
if (name == "dusklight.host") {
|
||||
return open_host;
|
||||
}
|
||||
if (name == "dusklight.resource") {
|
||||
return open_resource;
|
||||
}
|
||||
if (name == "dusklight.overlay") {
|
||||
return open_overlay;
|
||||
}
|
||||
if (name == "dusklight.texture") {
|
||||
return open_texture;
|
||||
}
|
||||
if (name == "dusklight.config") {
|
||||
return open_config;
|
||||
}
|
||||
if (name == "dusklight.ui") {
|
||||
return open_ui;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int module_require(lua_State* state);
|
||||
|
||||
void install_module_require(lua_State* state, Vm& vm, std::string_view currentPath) {
|
||||
@@ -262,7 +244,7 @@ int module_require(lua_State* state) {
|
||||
ModuleOpenFn factory = nullptr;
|
||||
if (requested.starts_with("dusklight.")) {
|
||||
moduleName = requested;
|
||||
factory = module_factory(moduleName);
|
||||
factory = services::module_factory(moduleName);
|
||||
if (factory == nullptr) {
|
||||
luaL_error(state, "unknown module '%s'", moduleName.c_str());
|
||||
}
|
||||
@@ -313,6 +295,8 @@ ModResult runtime_activate(ModContext*, ModContext* subject, ModError* outError)
|
||||
if (vm->state == nullptr) {
|
||||
return set_error(outError, MOD_ERROR, "Failed to create Luau VM");
|
||||
}
|
||||
lua_pushlightuserdata(vm->state, vm.get());
|
||||
lua_rawsetp(vm->state, LUA_REGISTRYINDEX, &vm_registry_index);
|
||||
lua_callbacks(vm->state)->userdata = vm.get();
|
||||
lua_callbacks(vm->state)->interrupt = [](lua_State* state, int gc) {
|
||||
auto* current = static_cast<Vm*>(lua_callbacks(state)->userdata);
|
||||
@@ -405,6 +389,16 @@ Vm& vm_from_upvalue(lua_State* state) {
|
||||
return *vm;
|
||||
}
|
||||
|
||||
Vm& vm_from_registry(lua_State* state) {
|
||||
lua_rawgetp(state, LUA_REGISTRYINDEX, &vm_registry_index);
|
||||
auto* vm = static_cast<Vm*>(lua_tolightuserdata(state, -1));
|
||||
if (vm == nullptr) {
|
||||
luaL_error(state, "missing Luau runtime context");
|
||||
}
|
||||
lua_pop(state, 1);
|
||||
return *vm;
|
||||
}
|
||||
|
||||
void push_vm_closure(lua_State* state, Vm& vm, lua_CFunction function, const char* name) {
|
||||
lua_pushlightuserdata(state, &vm);
|
||||
lua_pushcclosure(state, function, name, 1);
|
||||
@@ -443,86 +437,6 @@ void check_result(lua_State* state, ModResult result, const char* operation) {
|
||||
luaL_error(state, "%s failed: %s", operation, resultName);
|
||||
}
|
||||
|
||||
bool get_optional_bool(lua_State* state, int table, const char* field, bool fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
const bool value = lua_isnil(state, -1) ? fallback : luaL_checkboolean(state, -1) != 0;
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool to_int64(lua_State* state, int index, int64_t& outValue) {
|
||||
if (lua_isinteger64(state, index)) {
|
||||
outValue = lua_tointeger64(state, index, nullptr);
|
||||
return true;
|
||||
}
|
||||
if (!lua_isnumber(state, index)) {
|
||||
return false;
|
||||
}
|
||||
const double value = lua_tonumber(state, index);
|
||||
constexpr double kMaxSafeInteger = 9007199254740991.0;
|
||||
if (!std::isfinite(value) || value < -kMaxSafeInteger || value > kMaxSafeInteger ||
|
||||
std::trunc(value) != value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outValue = static_cast<int64_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t check_int64(lua_State* state, int index) {
|
||||
int64_t value = 0;
|
||||
if (!to_int64(state, index, value)) {
|
||||
luaL_argerror(state, index, "integer value expected");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
int64_t get_optional_int(lua_State* state, int table, const char* field, int64_t fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
const int64_t value = lua_isnil(state, -1) ? fallback : check_int64(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
double get_optional_number(lua_State* state, int table, const char* field, double fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
const double value = lua_isnil(state, -1) ? fallback : luaL_checknumber(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string get_optional_string(
|
||||
lua_State* state, int table, const char* field, std::string fallback) {
|
||||
lua_getfield(state, table, field);
|
||||
if (!lua_isnil(state, -1)) {
|
||||
size_t length = 0;
|
||||
const char* value = luaL_checklstring(state, -1, &length);
|
||||
fallback.assign(value, length);
|
||||
}
|
||||
lua_pop(state, 1);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
int ref_optional_function(lua_State* state, int table, const char* field) {
|
||||
lua_getfield(state, table, field);
|
||||
if (lua_isnil(state, -1)) {
|
||||
lua_pop(state, 1);
|
||||
return LUA_NOREF;
|
||||
}
|
||||
luaL_argexpected(state, lua_isfunction(state, -1), table, "function field");
|
||||
const int ref = lua_ref(state, -1);
|
||||
lua_pop(state, 1);
|
||||
return ref;
|
||||
}
|
||||
|
||||
int ref_required_function(lua_State* state, int table, const char* field) {
|
||||
const int ref = ref_optional_function(state, table, field);
|
||||
if (ref == LUA_NOREF) {
|
||||
luaL_error(state, "field '%s' is required", field);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
Callback& retain_callback(Vm& vm) {
|
||||
auto callback = std::make_unique<Callback>();
|
||||
callback->vm = &vm;
|
||||
|
||||
@@ -56,6 +56,9 @@ struct Vm {
|
||||
unsigned callDepth = 0;
|
||||
bool deadlineActive = false;
|
||||
|
||||
Vm() = default;
|
||||
Vm(Vm const&) = delete;
|
||||
Vm(Vm&&) = delete;
|
||||
~Vm();
|
||||
};
|
||||
|
||||
@@ -82,22 +85,13 @@ struct ScriptHandle {
|
||||
using ModuleOpenFn = int (*)(lua_State* state);
|
||||
|
||||
Vm& vm_from_upvalue(lua_State* state);
|
||||
Vm& vm_from_registry(lua_State* state);
|
||||
void push_vm_closure(lua_State* state, Vm& vm, lua_CFunction function, const char* name);
|
||||
void set_function(lua_State* state, Vm& vm, const char* name, lua_CFunction function);
|
||||
|
||||
[[noreturn]] void service_unavailable(lua_State* state, const char* name);
|
||||
void check_result(lua_State* state, ModResult result, const char* operation);
|
||||
|
||||
bool get_optional_bool(lua_State* state, int table, const char* field, bool fallback);
|
||||
bool to_int64(lua_State* state, int index, int64_t& outValue);
|
||||
int64_t check_int64(lua_State* state, int index);
|
||||
int64_t get_optional_int(lua_State* state, int table, const char* field, int64_t fallback);
|
||||
double get_optional_number(lua_State* state, int table, const char* field, double fallback);
|
||||
std::string get_optional_string(
|
||||
lua_State* state, int table, const char* field, std::string fallback = {});
|
||||
int ref_optional_function(lua_State* state, int table, const char* field);
|
||||
int ref_required_function(lua_State* state, int table, const char* field);
|
||||
|
||||
Callback& retain_callback(Vm& vm);
|
||||
bool call_ref(Vm& vm, int ref, int argumentCount, int resultCount,
|
||||
std::chrono::steady_clock::duration budget, std::string& outError);
|
||||
@@ -110,15 +104,4 @@ ScriptHandle& check_handle(lua_State* state, int index, const char* metatable, H
|
||||
void push_handle(lua_State* state, Vm& vm, uint64_t value, HandleKind kind, const char* metatable,
|
||||
ConfigVarType configType = CONFIG_VAR_BOOL);
|
||||
|
||||
void push_config_value(lua_State* state, const ConfigVarValue& value);
|
||||
void push_ui_handle(lua_State* state, Vm& vm, uint64_t value, HandleKind kind);
|
||||
|
||||
int open_log(lua_State* state);
|
||||
int open_host(lua_State* state);
|
||||
int open_resource(lua_State* state);
|
||||
int open_overlay(lua_State* state);
|
||||
int open_texture(lua_State* state);
|
||||
int open_config(lua_State* state);
|
||||
int open_ui(lua_State* state);
|
||||
|
||||
} // namespace luau_runtime
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
target_sources(luau_runtime PRIVATE
|
||||
audio_res.cpp
|
||||
bst.cpp
|
||||
bst.hpp
|
||||
wsys.cpp
|
||||
wsys.hpp
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "../../lua_bridge_helpers.hpp"
|
||||
#include "../../runtime.hpp"
|
||||
#include "../services.hpp"
|
||||
#include "LuaBridge/LuaBridge.h"
|
||||
|
||||
#include "mods/svc/audio_res.h"
|
||||
|
||||
#include "wsys.hpp"
|
||||
#include "bst.hpp"
|
||||
|
||||
namespace luau_runtime::services {
|
||||
namespace {
|
||||
|
||||
constexpr auto kServiceName = "AudioResService";
|
||||
|
||||
}
|
||||
|
||||
int open_audio_res(lua_State* state) {
|
||||
using namespace audio_res;
|
||||
|
||||
if (svc_audio_res == nullptr) {
|
||||
service_unavailable(state, kServiceName);
|
||||
}
|
||||
|
||||
lua_newtable(state);
|
||||
|
||||
luabridge::getNamespaceFromStack(state)
|
||||
// WSYS
|
||||
.addVariable("DEFAULT_KEY", AUDIO_RES_DEFAULT_KEY)
|
||||
.addFunction("default_wave_info", default_wave_info)
|
||||
.addFunction("replace_wave", replace_wave)
|
||||
.addFunction("add_wave", add_wave)
|
||||
.beginClass<LuaAudioWaveHandle>("AudioWaveHandle")
|
||||
.addFunction("unregister", &LuaAudioWaveHandle::unregister)
|
||||
.endClass()
|
||||
// BST
|
||||
.addFunction("default_effect_info", default_effect_info)
|
||||
.addFunction("replace_sound_table_effect", replace_sound_table_effect)
|
||||
.addFunction("add_sound_table_effect", add_sound_table_effect)
|
||||
.addFunction("default_stream_info", default_stream_info)
|
||||
.addFunction("replace_sound_table_stream", replace_sound_table_stream)
|
||||
.addFunction("add_sound_table_stream", add_sound_table_stream)
|
||||
.beginClass<LuaSoundTableHandle>("AudioSoundTableHandle")
|
||||
.addFunction("unregister", &LuaSoundTableHandle::unregister)
|
||||
.endClass();
|
||||
|
||||
|
||||
lua_setreadonly(state, -1, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "bst.hpp"
|
||||
|
||||
namespace luau_runtime::services::audio_res {
|
||||
|
||||
LuaSoundTableHandle::LuaSoundTableHandle(AudioSoundTableHandle handle) : BridgeScriptHandle(handle) { }
|
||||
|
||||
void LuaSoundTableHandle::unregister_impl(lua_State*, Vm& vm) {
|
||||
svc_audio_res->remove_sound_table(vm.subject, handle);
|
||||
}
|
||||
|
||||
AudioSoundTableEffectInfo const& default_effect_info() {
|
||||
return *svc_audio_res->default_effect_info;
|
||||
}
|
||||
|
||||
LuaSoundTableHandle replace_sound_table_effect(SoundEffectCategory category_id, uint16_t effect_id,
|
||||
std::optional<AudioSoundTableEffectInfo> info, lua_State* state, Vm& vm) {
|
||||
AudioSoundTableEffectInfo const* effect_info = nullptr;
|
||||
if (info.has_value()) {
|
||||
effect_info = &*info;
|
||||
}
|
||||
|
||||
AudioSoundTableHandle handle;
|
||||
|
||||
check_result(
|
||||
state,
|
||||
svc_audio_res->replace_sound_table_effect(vm.subject, category_id, effect_id, effect_info, &handle),
|
||||
"audio_res.replace_sound_table_effect");
|
||||
|
||||
return LuaSoundTableHandle(handle);
|
||||
}
|
||||
|
||||
std::tuple<uint16_t, LuaSoundTableHandle> add_sound_table_effect(
|
||||
SoundEffectCategory category_id,
|
||||
std::optional<AudioSoundTableEffectInfo> info,
|
||||
lua_State* state,
|
||||
Vm& vm) {
|
||||
AudioSoundTableEffectInfo const* effect_info = nullptr;
|
||||
if (info.has_value()) {
|
||||
effect_info = &*info;
|
||||
}
|
||||
|
||||
uint16_t out_effect_id;
|
||||
AudioSoundTableHandle handle;
|
||||
|
||||
check_result(
|
||||
state,
|
||||
svc_audio_res->add_sound_table_effect(vm.subject, category_id, effect_info, &handle, &out_effect_id),
|
||||
"audio_res.add_sound_table_effect");
|
||||
|
||||
return { out_effect_id, LuaSoundTableHandle(handle) };
|
||||
}
|
||||
|
||||
AudioSoundTableStreamInfo const& default_stream_info() {
|
||||
return *svc_audio_res->default_stream_info;
|
||||
}
|
||||
|
||||
LuaSoundTableHandle replace_sound_table_stream(uint16_t stream_id, char const* file_path,
|
||||
std::optional<AudioSoundTableStreamInfo> info, lua_State* state, Vm& vm) {
|
||||
AudioSoundTableStreamInfo const* stream_info = nullptr;
|
||||
if (info.has_value()) {
|
||||
stream_info = &*info;
|
||||
}
|
||||
|
||||
AudioSoundTableHandle handle;
|
||||
|
||||
check_result(
|
||||
state,
|
||||
svc_audio_res->replace_sound_table_stream(vm.subject, stream_id, file_path, stream_info, &handle),
|
||||
"audio_res.replace_sound_table_stream");
|
||||
|
||||
return LuaSoundTableHandle(handle);
|
||||
}
|
||||
|
||||
std::tuple<uint16_t, LuaSoundTableHandle> add_sound_table_stream(
|
||||
char const* file_path,
|
||||
std::optional<AudioSoundTableStreamInfo> info,
|
||||
lua_State* state,
|
||||
Vm& vm) {
|
||||
AudioSoundTableStreamInfo const* stream_info = nullptr;
|
||||
if (info.has_value()) {
|
||||
stream_info = &*info;
|
||||
}
|
||||
|
||||
uint16_t out_stream_id;
|
||||
AudioSoundTableHandle handle;
|
||||
|
||||
check_result(
|
||||
state,
|
||||
svc_audio_res->add_sound_table_stream(vm.subject, file_path, stream_info, &handle, &out_stream_id),
|
||||
"audio_res.add_sound_table_stream");
|
||||
|
||||
return { out_stream_id, LuaSoundTableHandle(handle) };
|
||||
}
|
||||
|
||||
|
||||
} // namespace luau_runtime::services::audio_res
|
||||
|
||||
namespace luabridge {
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
constexpr luau_runtime::lua_helpers::EnumName<SoundEffectCategory> kSoundEffectCategoryNames[] = {
|
||||
// clang-format off
|
||||
{ SE_CATEGORY_SYSTEM_SE, "system_se"sv },
|
||||
{ SE_CATEGORY_PLAYER_VOICE, "player_voice"sv },
|
||||
{ SE_CATEGORY_PLAYER_SE, "player_se"sv },
|
||||
{ SE_CATEGORY_FOOTNOTE_SE, "footnote_se"sv },
|
||||
{ SE_CATEGORY_COLLISION_SE, "collision_se"sv },
|
||||
{ SE_CATEGORY_CHARA_VOICE, "chara_voice"sv },
|
||||
{ SE_CATEGORY_CHARA_SE, "chara_se"sv },
|
||||
{ SE_CATEGORY_ENEMY_SE, "enemy_se"sv },
|
||||
{ SE_CATEGORY_OBJECT_SE, "object_se"sv },
|
||||
{ SE_CATEGORY_ENV_SE, "env_se"sv },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
constexpr luau_runtime::lua_helpers::EnumName<StreamPan> kStreamPanNames[] = {
|
||||
// clang-format off
|
||||
{ STREAM_PAN_CENTER, "center"sv },
|
||||
{ STREAM_PAN_LEFT, "left"sv },
|
||||
{ STREAM_PAN_RIGHT, "right"sv },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_STRING_ENUM(SoundEffectCategory, kSoundEffectCategoryNames);
|
||||
DEFINE_STRING_ENUM(StreamPan, kStreamPanNames);
|
||||
|
||||
TypeResult<AudioSoundTableEffectInfo> Stack<AudioSoundTableEffectInfo>::get(
|
||||
lua_State* L, int index) {
|
||||
|
||||
auto const ref = LuaRef::fromStack(L, index);
|
||||
|
||||
return AudioSoundTableEffectInfo {
|
||||
.priority = ref["priority"],
|
||||
.volume = ref["volume"],
|
||||
.pitch = ref["pitch"],
|
||||
.always_max_priority = ref["always_max_priority"],
|
||||
.ignore_distance_volume = ref["ignore_distance_volume"],
|
||||
.ignore_distance_fx_mix = ref["ignore_distance_fx_mix"],
|
||||
.ignore_pan = ref["ignore_pan"],
|
||||
.ignore_dolby = ref["ignore_dolby"],
|
||||
.random_volume = ref["random_volume"],
|
||||
.random_pitch = ref["random_pitch"],
|
||||
.doppler_power = ref["doppler_power"],
|
||||
.volume_dist_class = ref["volume_dist_class"],
|
||||
.clamp_min_volume = ref["clamp_min_volume"],
|
||||
.cull_at_max_distance = ref["cull_at_max_distance"],
|
||||
};
|
||||
}
|
||||
|
||||
Result Stack<AudioSoundTableEffectInfo>::push(
|
||||
lua_State* L, const AudioSoundTableEffectInfo& value) {
|
||||
|
||||
LuaRef const ref = newTable(L);
|
||||
|
||||
ref["priority"] = value.priority;
|
||||
ref["volume"] = value.volume;
|
||||
ref["pitch"] = value.pitch;
|
||||
ref["always_max_priority"] = value.always_max_priority;
|
||||
ref["ignore_distance_volume"] = value.ignore_distance_volume;
|
||||
ref["ignore_distance_fx_mix"] = value.ignore_distance_fx_mix;
|
||||
ref["ignore_pan"] = value.ignore_pan;
|
||||
ref["ignore_dolby"] = value.ignore_dolby;
|
||||
ref["random_volume"] = value.random_volume;
|
||||
ref["random_pitch"] = value.random_pitch;
|
||||
ref["doppler_power"] = value.doppler_power;
|
||||
ref["volume_dist_class"] = value.volume_dist_class;
|
||||
ref["clamp_min_volume"] = value.clamp_min_volume;
|
||||
ref["cull_at_max_distance"] = value.cull_at_max_distance;
|
||||
|
||||
ref.push();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
TypeResult<AudioSoundTableStreamInfo> Stack<AudioSoundTableStreamInfo>::get(
|
||||
lua_State* L, int index) {
|
||||
|
||||
auto const ref = LuaRef::fromStack(L, index);
|
||||
|
||||
std::array<std::optional<StreamPan>, STREAM_MAX_CHILDREN> const pan_parameters = ref["pan_parameters"];
|
||||
|
||||
auto info = AudioSoundTableStreamInfo {
|
||||
.priority = ref["priority"],
|
||||
.volume = ref["volume"],
|
||||
.stop_on_scene_change = ref["stop_on_scene_change"],
|
||||
};
|
||||
|
||||
for (int i = 0; i < STREAM_MAX_CHILDREN; i++) {
|
||||
info.pan_parameters[i] = pan_parameters[i].value_or(STREAM_PAN_CENTER);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
Result Stack<AudioSoundTableStreamInfo>::push(
|
||||
lua_State* L, const AudioSoundTableStreamInfo& value) {
|
||||
|
||||
LuaRef const ref = newTable(L);
|
||||
|
||||
ref["priority"] = value.priority;
|
||||
ref["volume"] = value.volume;
|
||||
ref["pan_parameters"] = value.pan_parameters;
|
||||
ref["stop_on_scene_change"] = value.stop_on_scene_change;
|
||||
|
||||
ref.push();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace luabridge
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "mods/svc/audio_res.h"
|
||||
#include "../../lua_helpers.hpp"
|
||||
#include "../../lua_bridge_helpers.hpp"
|
||||
|
||||
namespace luau_runtime::services::audio_res {
|
||||
|
||||
class LuaSoundTableHandle final : public BridgeScriptHandle {
|
||||
protected:
|
||||
void unregister_impl(lua_State*, Vm& vm) override;
|
||||
public:
|
||||
explicit LuaSoundTableHandle(AudioSoundTableHandle handle);
|
||||
};
|
||||
|
||||
AudioSoundTableEffectInfo const& default_effect_info();
|
||||
|
||||
LuaSoundTableHandle replace_sound_table_effect(
|
||||
SoundEffectCategory category_id,
|
||||
uint16_t effect_id,
|
||||
std::optional<AudioSoundTableEffectInfo> info,
|
||||
lua_State* state,
|
||||
Vm& vm);
|
||||
|
||||
std::tuple<uint16_t, LuaSoundTableHandle> add_sound_table_effect(
|
||||
SoundEffectCategory category_id,
|
||||
std::optional<AudioSoundTableEffectInfo> info,
|
||||
lua_State* state,
|
||||
Vm& vm);
|
||||
|
||||
AudioSoundTableStreamInfo const& default_stream_info();
|
||||
|
||||
LuaSoundTableHandle replace_sound_table_stream(
|
||||
uint16_t stream_id,
|
||||
char const* file_path,
|
||||
std::optional<AudioSoundTableStreamInfo> info,
|
||||
lua_State* state,
|
||||
Vm& vm);
|
||||
|
||||
std::tuple<uint16_t, LuaSoundTableHandle> add_sound_table_stream(
|
||||
char const* file_path,
|
||||
std::optional<AudioSoundTableStreamInfo> info,
|
||||
lua_State* state,
|
||||
Vm& vm);
|
||||
|
||||
}
|
||||
|
||||
namespace luabridge {
|
||||
|
||||
template<>
|
||||
struct Stack<AudioSoundTableEffectInfo> {
|
||||
[[nodiscard]] static Result push(lua_State* L, const AudioSoundTableEffectInfo& value);
|
||||
[[nodiscard]] static TypeResult<AudioSoundTableEffectInfo> get(lua_State* L, int index);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Stack<AudioSoundTableStreamInfo> {
|
||||
[[nodiscard]] static Result push(lua_State* L, const AudioSoundTableStreamInfo& value);
|
||||
[[nodiscard]] static TypeResult<AudioSoundTableStreamInfo> get(lua_State* L, int index);
|
||||
};
|
||||
|
||||
DECLARE_STRING_ENUM(SoundEffectCategory);
|
||||
DECLARE_STRING_ENUM(StreamPan);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#include "wsys.hpp"
|
||||
|
||||
namespace luau_runtime::services::audio_res {
|
||||
|
||||
LuaAudioWaveHandle::LuaAudioWaveHandle(AudioWaveHandle handle) : BridgeScriptHandle(handle) { }
|
||||
|
||||
void LuaAudioWaveHandle::unregister_impl(lua_State*, Vm& vm) {
|
||||
svc_audio_res->remove_wave(vm.subject, handle);
|
||||
}
|
||||
|
||||
AudioWaveInfo const& default_wave_info() {
|
||||
return *svc_audio_res->default_wave_info;
|
||||
}
|
||||
|
||||
LuaAudioWaveHandle replace_wave(AudioWaveBank bank, uint16_t wave_id, char const* file, std::optional<OwnedWaveInfo> info, lua_State* state, Vm& vm) {
|
||||
AudioWaveInfo const* wave_info = nullptr;
|
||||
if (info.has_value()) {
|
||||
wave_info = &info->info;
|
||||
}
|
||||
|
||||
AudioWaveHandle handle;
|
||||
|
||||
check_result(
|
||||
state,
|
||||
svc_audio_res->replace_wave(vm.subject, bank, wave_id, file, wave_info, &handle),
|
||||
"audio_res.replace_wave");
|
||||
|
||||
return LuaAudioWaveHandle(handle);
|
||||
}
|
||||
|
||||
std::tuple<uint16_t, LuaAudioWaveHandle> add_wave(AudioWaveBank bank, char const* file, std::optional<OwnedWaveInfo> info, lua_State* state, Vm& vm) {
|
||||
AudioWaveInfo const* wave_info = nullptr;
|
||||
if (info.has_value()) {
|
||||
wave_info = &info->info;
|
||||
}
|
||||
|
||||
AudioWaveHandle handle;
|
||||
uint16_t out_wave_id;
|
||||
|
||||
check_result(
|
||||
state,
|
||||
svc_audio_res->add_wave(vm.subject, bank, file, wave_info, &handle, &out_wave_id),
|
||||
"audio_res.replace_wave");
|
||||
|
||||
return {out_wave_id, LuaAudioWaveHandle(handle)};
|
||||
}
|
||||
|
||||
} // namespace luau_runtime::services::audio_res
|
||||
|
||||
namespace luabridge {
|
||||
|
||||
Result Stack<AudioWaveInfo>::push(lua_State* L, const AudioWaveInfo& value) {
|
||||
LuaRef const ref = newTable(L);
|
||||
|
||||
ref["base_key"] = value.base_key;
|
||||
ref["loop"] = value.loop;
|
||||
ref["loop_start_sample"] = value.loop_start_sample;
|
||||
ref["loop_end_sample"] = value.loop_end_sample;
|
||||
if (value.raw_wave) {
|
||||
ref["raw_wave"] = *value.raw_wave;
|
||||
}
|
||||
ref.push();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result Stack<AudioRawWave>::push(lua_State* L, const AudioRawWave& value) {
|
||||
LuaRef const ref = newTable(L);
|
||||
|
||||
ref["format"] = value.format;
|
||||
ref["sample_rate"] = value.sample_rate;
|
||||
ref["sample_value_last"] = value.sample_value_last;
|
||||
ref["sample_value_penult"] = value.sample_value_penult;
|
||||
|
||||
ref.push();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
TypeResult<AudioRawWave> Stack<AudioRawWave>::get(lua_State* L, int index) {
|
||||
auto const ref = LuaRef::fromStack(L, index);
|
||||
|
||||
return AudioRawWave {
|
||||
.format = ref["format"],
|
||||
.sample_rate = ref["sample_rate"],
|
||||
.sample_value_last = opt_or<int16_t>(ref["sample_value_last"], 0),
|
||||
.sample_value_penult = opt_or<int16_t>(ref["sample_value_penult"], 0),
|
||||
};
|
||||
}
|
||||
|
||||
using namespace luau_runtime::services::audio_res;
|
||||
|
||||
Result Stack<OwnedWaveInfo>::push(lua_State* L, const OwnedWaveInfo& value) {
|
||||
return Stack<AudioWaveInfo>::push(L, value.info);
|
||||
}
|
||||
|
||||
TypeResult<OwnedWaveInfo> Stack<OwnedWaveInfo>::get(lua_State* L, int index) {
|
||||
auto const ref = LuaRef::fromStack(L, index);
|
||||
|
||||
AudioWaveInfo info {
|
||||
.base_key = opt_or<uint8_t>(ref["base_key"], AUDIO_RES_DEFAULT_KEY),
|
||||
.loop = opt_or<bool>(ref["loop"], false),
|
||||
.loop_start_sample = opt_or<uint32_t>(ref["loop_start_sample"], 0),
|
||||
.loop_end_sample = opt_or<uint32_t>(ref["loop_end_sample"], std::numeric_limits<uint32_t>::max()),
|
||||
};
|
||||
|
||||
std::unique_ptr<AudioRawWave> raw;
|
||||
if (!ref["raw_wave"].isNil()) {
|
||||
raw = std::make_unique<AudioRawWave>(ref["raw_wave"]);
|
||||
info.raw_wave = raw.get();
|
||||
}
|
||||
|
||||
return OwnedWaveInfo { info, std::move(raw) };
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
constexpr luau_runtime::lua_helpers::EnumName<AudioWaveFormat> kWaveFormatNames[] = {
|
||||
{ AUDIO_WAVE_FORMAT_ADPCM4, "adpcm4"sv },
|
||||
{ AUDIO_WAVE_FORMAT_ADPCM2, "adpcm2"sv },
|
||||
{ AUDIO_WAVE_FORMAT_PCM8, "pcm8"sv },
|
||||
{ AUDIO_WAVE_FORMAT_PCM16, "pcm16"sv },
|
||||
};
|
||||
|
||||
constexpr luau_runtime::lua_helpers::EnumName<AudioWaveBank> kWaveBankNames[] = {
|
||||
{ AUDIO_WAVE_BANK_SOUND_EFFECTS, "sound_effects"sv } ,
|
||||
{ AUDIO_WAVE_BANK_MUSIC_SAMPLES, "music_samples"sv } ,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
DEFINE_STRING_ENUM(AudioWaveFormat, kWaveFormatNames);
|
||||
DEFINE_STRING_ENUM(AudioWaveBank, kWaveBankNames);
|
||||
|
||||
|
||||
} // namespace luabridge
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "LuaBridge/LuaBridge.h"
|
||||
|
||||
#include "../../runtime.hpp"
|
||||
#include "../../lua_helpers.hpp"
|
||||
#include "mods/svc/audio_res.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace luau_runtime::services::audio_res {
|
||||
|
||||
struct OwnedWaveInfo {
|
||||
AudioWaveInfo info;
|
||||
std::unique_ptr<AudioRawWave> raw_wave;
|
||||
};
|
||||
|
||||
class LuaAudioWaveHandle final : public BridgeScriptHandle {
|
||||
protected:
|
||||
void unregister_impl(lua_State*, Vm& vm) override;
|
||||
public:
|
||||
explicit LuaAudioWaveHandle(AudioWaveHandle handle);
|
||||
};
|
||||
|
||||
AudioWaveInfo const& default_wave_info();
|
||||
LuaAudioWaveHandle replace_wave(AudioWaveBank bank, uint16_t wave_id, char const* file, std::optional<OwnedWaveInfo> info, lua_State* state, Vm& vm);
|
||||
std::tuple<uint16_t, LuaAudioWaveHandle> add_wave(AudioWaveBank bank, char const* file, std::optional<OwnedWaveInfo> info, lua_State* state, Vm& vm);
|
||||
|
||||
}
|
||||
|
||||
namespace luabridge {
|
||||
|
||||
template<>
|
||||
struct Stack<AudioWaveInfo> {
|
||||
[[nodiscard]] static Result push(lua_State* L, const AudioWaveInfo& value);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Stack<AudioRawWave> {
|
||||
[[nodiscard]] static Result push(lua_State* L, const AudioRawWave& value);
|
||||
[[nodiscard]] static TypeResult<AudioRawWave> get(lua_State* L, int index);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Stack<luau_runtime::services::audio_res::OwnedWaveInfo> {
|
||||
[[nodiscard]] static Result push(lua_State* L, const luau_runtime::services::audio_res::OwnedWaveInfo& value);
|
||||
[[nodiscard]] static TypeResult<luau_runtime::services::audio_res::OwnedWaveInfo> get(lua_State* L, int index);
|
||||
};
|
||||
|
||||
DECLARE_STRING_ENUM(AudioWaveFormat);
|
||||
DECLARE_STRING_ENUM(AudioWaveBank);
|
||||
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
#include "runtime.hpp"
|
||||
#include "../lua_helpers.hpp"
|
||||
#include "../runtime.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace luau_runtime {
|
||||
namespace luau_runtime::services {
|
||||
namespace {
|
||||
|
||||
using namespace luau_runtime::lua_helpers;
|
||||
|
||||
constexpr char kOverlayMetatable[] = "dusklight.overlay_handle";
|
||||
constexpr char kTextureMetatable[] = "dusklight.texture_handle";
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
#include "runtime.hpp"
|
||||
#include "config.hpp"
|
||||
#include "../runtime.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace luau_runtime {
|
||||
#include "../lua_helpers.hpp"
|
||||
|
||||
namespace luau_runtime::services {
|
||||
namespace {
|
||||
|
||||
using namespace luau_runtime::lua_helpers;
|
||||
|
||||
constexpr char kConfigVarMetatable[] = "dusklight.config_var";
|
||||
constexpr char kConfigSubscriptionMetatable[] = "dusklight.config_subscription";
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "lua.h"
|
||||
#include "mods/svc/config.h"
|
||||
|
||||
namespace luau_runtime::services {
|
||||
|
||||
void push_config_value(lua_State* state, const ConfigVarValue& value);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "services.hpp"
|
||||
|
||||
namespace luau_runtime::services {
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
constexpr std::pair<std::string_view, lua_CFunction> kModules[] = {
|
||||
{ "dusklight.log"sv, open_log },
|
||||
{ "dusklight.host"sv, open_host },
|
||||
{ "dusklight.resource"sv, open_resource },
|
||||
{ "dusklight.overlay"sv, open_overlay },
|
||||
{ "dusklight.texture"sv, open_texture },
|
||||
{ "dusklight.config"sv, open_config },
|
||||
{ "dusklight.ui"sv, open_ui },
|
||||
{ "dusklight.audio_res"sv, open_audio_res },
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
ModuleOpenFn module_factory(std::string_view name) {
|
||||
for (auto [ modName, ptr ] : kModules) {
|
||||
if (name == modName) {
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include "../runtime.hpp"
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
namespace luau_runtime::services {
|
||||
|
||||
int open_audio_res(lua_State* state);
|
||||
int open_log(lua_State* state);
|
||||
int open_host(lua_State* state);
|
||||
int open_resource(lua_State* state);
|
||||
int open_overlay(lua_State* state);
|
||||
int open_texture(lua_State* state);
|
||||
int open_config(lua_State* state);
|
||||
int open_ui(lua_State* state);
|
||||
|
||||
ModuleOpenFn module_factory(std::string_view name);
|
||||
|
||||
}
|
||||
@@ -1,13 +1,20 @@
|
||||
#include "runtime.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
#include "../runtime.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace luau_runtime {
|
||||
#include "../lua_helpers.hpp"
|
||||
|
||||
namespace luau_runtime::services {
|
||||
namespace {
|
||||
|
||||
using namespace luau_runtime::lua_helpers;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
constexpr char kUiWindowMetatable[] = "dusklight.ui_window";
|
||||
constexpr char kUiDialogMetatable[] = "dusklight.ui_dialog";
|
||||
constexpr char kUiElementMetatable[] = "dusklight.ui_element";
|
||||
@@ -285,40 +292,32 @@ std::vector<UiListItem> list_items(lua_State* state, int table, std::vector<std:
|
||||
return items;
|
||||
}
|
||||
|
||||
constexpr EnumName<UiControlKind> kControlKindNames[] = {
|
||||
{ UI_CONTROL_BUTTON, "button"sv },
|
||||
{ UI_CONTROL_TOGGLE, "toggle"sv },
|
||||
{ UI_CONTROL_NUMBER, "number"sv },
|
||||
{ UI_CONTROL_STRING, "string"sv },
|
||||
{ UI_CONTROL_SELECT, "select"sv },
|
||||
{ UI_CONTROL_COLOR, "color"sv },
|
||||
{ UI_CONTROL_GROUP, "group"sv },
|
||||
{ UI_CONTROL_FILE_PICKER, "file_picker"sv },
|
||||
};
|
||||
|
||||
UiControlKind control_kind(lua_State* state, const std::string& kind) {
|
||||
if (kind == "button")
|
||||
return UI_CONTROL_BUTTON;
|
||||
if (kind == "toggle")
|
||||
return UI_CONTROL_TOGGLE;
|
||||
if (kind == "number")
|
||||
return UI_CONTROL_NUMBER;
|
||||
if (kind == "string")
|
||||
return UI_CONTROL_STRING;
|
||||
if (kind == "select")
|
||||
return UI_CONTROL_SELECT;
|
||||
if (kind == "color")
|
||||
return UI_CONTROL_COLOR;
|
||||
if (kind == "group")
|
||||
return UI_CONTROL_GROUP;
|
||||
if (kind == "file_picker")
|
||||
return UI_CONTROL_FILE_PICKER;
|
||||
luaL_error(state, "unknown UI control kind '%s'", kind.c_str());
|
||||
return enum_str_to_value(state, kind.data(), kControlKindNames);
|
||||
}
|
||||
|
||||
constexpr EnumName<UiStyleScope> kStyleScopeNames[] = {
|
||||
{ UI_SCOPE_PRELAUNCH, "prelaunch"sv },
|
||||
{ UI_SCOPE_WINDOW, "window"sv },
|
||||
{ UI_SCOPE_MENU_BAR, "menu_bar"sv },
|
||||
{ UI_SCOPE_OVERLAY, "overlay"sv },
|
||||
{ UI_SCOPE_TOUCH_CONTROLS, "touch_controls"sv },
|
||||
{ UI_SCOPE_GRAPHICS_TUNER, "graphics_tuner"sv },
|
||||
};
|
||||
|
||||
UiStyleScope style_scope(lua_State* state, const std::string& scope) {
|
||||
if (scope == "prelaunch")
|
||||
return UI_SCOPE_PRELAUNCH;
|
||||
if (scope == "window")
|
||||
return UI_SCOPE_WINDOW;
|
||||
if (scope == "menu_bar")
|
||||
return UI_SCOPE_MENU_BAR;
|
||||
if (scope == "overlay")
|
||||
return UI_SCOPE_OVERLAY;
|
||||
if (scope == "touch_controls")
|
||||
return UI_SCOPE_TOUCH_CONTROLS;
|
||||
if (scope == "graphics_tuner")
|
||||
return UI_SCOPE_GRAPHICS_TUNER;
|
||||
luaL_error(state, "unknown UI style scope '%s'", scope.c_str());
|
||||
return enum_str_to_value<UiStyleScope>(state, scope.data(), kStyleScopeNames);
|
||||
}
|
||||
|
||||
int pane_add_section(lua_State* state) {
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "../runtime.hpp"
|
||||
#include "lua.h"
|
||||
|
||||
namespace luau_runtime::services {
|
||||
|
||||
void push_ui_handle(lua_State* state, Vm& vm, uint64_t value, HandleKind kind);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user