mirror of
https://github.com/XLabsProject/s1x-client.git
synced 2023-08-02 15:02:12 +02:00
Merge pull request #263 from fedddddd/lui-scripting
Bring over LUI stuff from iw6x
This commit is contained in:
commit
f4ffbe1f17
@ -181,6 +181,22 @@ namespace patches
|
|||||||
// CG_SetClientDvarFromServer
|
// CG_SetClientDvarFromServer
|
||||||
reinterpret_cast<void(*)(void*, void*, const char*, const char*)>(0x1401BF0A0)(a1, a2, dvar, value);
|
reinterpret_cast<void(*)(void*, void*, const char*, const char*)>(0x1401BF0A0)(a1, a2, dvar, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::hook::detour cmd_lui_notify_server_hook;
|
||||||
|
void cmd_lui_notify_server_stub(game::mp::gentity_s* ent)
|
||||||
|
{
|
||||||
|
command::params_sv params{};
|
||||||
|
const auto menu_id = atoi(params.get(1));
|
||||||
|
const auto client = &game::mp::svs_clients[ent->s.entityNum];
|
||||||
|
|
||||||
|
// 22 => "end_game"
|
||||||
|
if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_lui_notify_server_hook.invoke<void>(ent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -310,9 +326,11 @@ namespace patches
|
|||||||
dvars::override::Dvar_RegisterInt("sv_timeout", 90, 90, 1800, game::DVAR_FLAG_NONE); // 30 - 0 - 1800
|
dvars::override::Dvar_RegisterInt("sv_timeout", 90, 90, 1800, game::DVAR_FLAG_NONE); // 30 - 0 - 1800
|
||||||
dvars::override::Dvar_RegisterInt("cl_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // Seems unused
|
dvars::override::Dvar_RegisterInt("cl_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // Seems unused
|
||||||
dvars::override::Dvar_RegisterInt("sv_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // 60 - 0 - 1800
|
dvars::override::Dvar_RegisterInt("sv_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // 60 - 0 - 1800
|
||||||
|
|
||||||
// Spectate dvar
|
|
||||||
game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED, "");
|
game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED, "");
|
||||||
|
|
||||||
|
// Prevent clients from ending the game as non host by sending 'end_game' lui notification
|
||||||
|
cmd_lui_notify_server_hook.create(0x1402E9390, cmd_lui_notify_server_stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void patch_sp()
|
static void patch_sp()
|
||||||
|
159
src/client/component/ui_scripting.cpp
Normal file
159
src/client/component/ui_scripting.cpp
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "command.hpp"
|
||||||
|
|
||||||
|
#include "ui_scripting.hpp"
|
||||||
|
|
||||||
|
#include "game/ui_scripting/lua/engine.hpp"
|
||||||
|
#include "game/ui_scripting/execution.hpp"
|
||||||
|
#include "game/ui_scripting/lua/error.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::unordered_map<game::hks::cclosure*, sol::protected_function> converted_functions;
|
||||||
|
|
||||||
|
utils::hook::detour hksi_lual_error_hook;
|
||||||
|
utils::hook::detour hksi_lual_error_hook2;
|
||||||
|
utils::hook::detour hks_start_hook;
|
||||||
|
utils::hook::detour hks_shutdown_hook;
|
||||||
|
|
||||||
|
bool error_hook_enabled = false;
|
||||||
|
|
||||||
|
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
char va_buffer[0x200] = { 0 };
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsprintf_s(va_buffer, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
const auto formatted = std::string(va_buffer);
|
||||||
|
|
||||||
|
if (!error_hook_enabled)
|
||||||
|
{
|
||||||
|
return hksi_lual_error_hook.invoke<void>(s, formatted.data());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error(formatted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* hks_start_stub(char a1)
|
||||||
|
{
|
||||||
|
const auto _ = gsl::finally([]()
|
||||||
|
{
|
||||||
|
ui_scripting::lua::engine::start();
|
||||||
|
});
|
||||||
|
|
||||||
|
return hks_start_hook.invoke<void*>(a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hks_shutdown_stub()
|
||||||
|
{
|
||||||
|
ui_scripting::lua::engine::stop();
|
||||||
|
hks_shutdown_hook.invoke<void*>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main_function_handler(game::hks::lua_State* state)
|
||||||
|
{
|
||||||
|
const auto value = state->m_apistack.base[-1];
|
||||||
|
if (value.t != game::hks::TCFUNCTION)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto closure = value.v.cClosure;
|
||||||
|
if (converted_functions.find(closure) == converted_functions.end())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto function = converted_functions[closure];
|
||||||
|
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||||
|
const auto arguments = get_return_values(count);
|
||||||
|
const auto s = function.lua_state();
|
||||||
|
|
||||||
|
std::vector<sol::lua_value> converted_args;
|
||||||
|
|
||||||
|
for (const auto& argument : arguments)
|
||||||
|
{
|
||||||
|
converted_args.push_back(lua::convert(s, argument));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto results = function(sol::as_args(converted_args));
|
||||||
|
lua::handle_error(results);
|
||||||
|
|
||||||
|
for (const auto& result : results)
|
||||||
|
{
|
||||||
|
push_value(lua::convert({s, result}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.return_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function)
|
||||||
|
{
|
||||||
|
converted_functions[closure] = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_converted_functions()
|
||||||
|
{
|
||||||
|
converted_functions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_error_hook()
|
||||||
|
{
|
||||||
|
error_hook_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable_error_hook()
|
||||||
|
{
|
||||||
|
error_hook_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
if (!game::environment::is_mp())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler::loop(ui_scripting::lua::engine::run_frame, scheduler::pipeline::renderer);
|
||||||
|
|
||||||
|
hks_start_hook.create(0x1400E4E40, hks_start_stub);
|
||||||
|
hks_shutdown_hook.create(0x1400DB9A0, hks_shutdown_stub);
|
||||||
|
hksi_lual_error_hook.create(0x1400A03D0, hksi_lual_error_stub);
|
||||||
|
hksi_lual_error_hook2.create(0x1400A77D0, hksi_lual_error_stub);
|
||||||
|
|
||||||
|
command::add("lui_restart", []()
|
||||||
|
{
|
||||||
|
utils::hook::invoke<void>(0x1400DB9A0);
|
||||||
|
utils::hook::invoke<void>(0x1400E6730);
|
||||||
|
});
|
||||||
|
|
||||||
|
command::add("reloaduiscripts", []()
|
||||||
|
{
|
||||||
|
scheduler::once(ui_scripting::lua::engine::start, scheduler::pipeline::renderer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(ui_scripting::component)
|
12
src/client/component/ui_scripting.hpp
Normal file
12
src/client/component/ui_scripting.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/ui_scripting/lua/value_conversion.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
int main_function_handler(game::hks::lua_State* state);
|
||||||
|
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function);
|
||||||
|
void clear_converted_functions();
|
||||||
|
|
||||||
|
void enable_error_hook();
|
||||||
|
void disable_error_hook();
|
||||||
|
}
|
@ -1424,4 +1424,208 @@ namespace game
|
|||||||
sp::playerState_s* sp;
|
sp::playerState_s* sp;
|
||||||
mp::playerState_s* mp;
|
mp::playerState_s* mp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace hks
|
||||||
|
{
|
||||||
|
struct GenericChunkHeader
|
||||||
|
{
|
||||||
|
unsigned __int64 m_flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChunkHeader : GenericChunkHeader
|
||||||
|
{
|
||||||
|
ChunkHeader* m_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UserData : ChunkHeader
|
||||||
|
{
|
||||||
|
unsigned __int64 m_envAndSizeOffsetHighBits;
|
||||||
|
unsigned __int64 m_metaAndSizeOffsetLowBits;
|
||||||
|
char m_data[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InternString
|
||||||
|
{
|
||||||
|
unsigned __int64 m_flags;
|
||||||
|
unsigned __int64 m_lengthbits;
|
||||||
|
unsigned int m_hash;
|
||||||
|
char m_data[30];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashTable;
|
||||||
|
struct cclosure;
|
||||||
|
|
||||||
|
union HksValue
|
||||||
|
{
|
||||||
|
cclosure* cClosure;
|
||||||
|
void* closure;
|
||||||
|
UserData* userData;
|
||||||
|
HashTable* table;
|
||||||
|
void* tstruct;
|
||||||
|
InternString* str;
|
||||||
|
void* thread;
|
||||||
|
void* ptr;
|
||||||
|
float number;
|
||||||
|
unsigned int native;
|
||||||
|
bool boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HksObjectType
|
||||||
|
{
|
||||||
|
TANY = 0xFFFFFFFE,
|
||||||
|
TNONE = 0xFFFFFFFF,
|
||||||
|
TNIL = 0x0,
|
||||||
|
TBOOLEAN = 0x1,
|
||||||
|
TLIGHTUSERDATA = 0x2,
|
||||||
|
TNUMBER = 0x3,
|
||||||
|
TSTRING = 0x4,
|
||||||
|
TTABLE = 0x5,
|
||||||
|
TFUNCTION = 0x6, // idk
|
||||||
|
TUSERDATA = 0x7,
|
||||||
|
TTHREAD = 0x8,
|
||||||
|
TIFUNCTION = 0x9, // Lua function
|
||||||
|
TCFUNCTION = 0xA, // C function
|
||||||
|
TUI64 = 0xB,
|
||||||
|
TSTRUCT = 0xC,
|
||||||
|
NUM_TYPE_OBJECTS = 0xE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HksObject
|
||||||
|
{
|
||||||
|
HksObjectType t;
|
||||||
|
HksValue v;
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct hksInstruction
|
||||||
|
{
|
||||||
|
unsigned int code;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActivationRecord
|
||||||
|
{
|
||||||
|
HksObject* m_base;
|
||||||
|
const hksInstruction* m_returnAddress;
|
||||||
|
__int16 m_tailCallDepth;
|
||||||
|
__int16 m_numVarargs;
|
||||||
|
int m_numExpectedReturns;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CallStack
|
||||||
|
{
|
||||||
|
ActivationRecord* m_records;
|
||||||
|
ActivationRecord* m_lastrecord;
|
||||||
|
ActivationRecord* m_current;
|
||||||
|
const hksInstruction* m_current_lua_pc;
|
||||||
|
const hksInstruction* m_hook_return_addr;
|
||||||
|
int m_hook_level;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ApiStack
|
||||||
|
{
|
||||||
|
HksObject* top;
|
||||||
|
HksObject* base;
|
||||||
|
HksObject* alloc_top;
|
||||||
|
HksObject* bottom;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UpValue : ChunkHeader
|
||||||
|
{
|
||||||
|
HksObject m_storage;
|
||||||
|
HksObject* loc;
|
||||||
|
UpValue* m_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CallSite
|
||||||
|
{
|
||||||
|
_SETJMP_FLOAT128 m_jumpBuffer[16];
|
||||||
|
CallSite* m_prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
NEW = 0x1,
|
||||||
|
RUNNING = 0x2,
|
||||||
|
YIELDED = 0x3,
|
||||||
|
DEAD_ERROR = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HksError
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lua_Debug
|
||||||
|
{
|
||||||
|
int event;
|
||||||
|
const char* name;
|
||||||
|
const char* namewhat;
|
||||||
|
const char* what;
|
||||||
|
const char* source;
|
||||||
|
int currentline;
|
||||||
|
int nups;
|
||||||
|
int nparams;
|
||||||
|
int ishksfunc;
|
||||||
|
int linedefined;
|
||||||
|
int lastlinedefined;
|
||||||
|
char short_src[512];
|
||||||
|
int callstack_level;
|
||||||
|
int is_tail_call;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lua_State : ChunkHeader
|
||||||
|
{
|
||||||
|
void* m_global;
|
||||||
|
CallStack m_callStack;
|
||||||
|
ApiStack m_apistack;
|
||||||
|
UpValue* pending;
|
||||||
|
HksObject globals;
|
||||||
|
HksObject m_cEnv;
|
||||||
|
CallSite* m_callsites;
|
||||||
|
int m_numberOfCCalls;
|
||||||
|
void* m_context;
|
||||||
|
InternString* m_name;
|
||||||
|
lua_State* m_nextState;
|
||||||
|
lua_State* m_nextStateStack;
|
||||||
|
Status m_status;
|
||||||
|
HksError m_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
using lua_function = int(__fastcall*)(lua_State*);
|
||||||
|
|
||||||
|
struct luaL_Reg
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
lua_function function;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
HksObject m_key;
|
||||||
|
HksObject m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Metatable
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashTable : ChunkHeader
|
||||||
|
{
|
||||||
|
Metatable* m_meta;
|
||||||
|
unsigned int m_version;
|
||||||
|
unsigned int m_mask;
|
||||||
|
Node* m_hashPart;
|
||||||
|
HksObject* m_arrayPart;
|
||||||
|
unsigned int m_arraySize;
|
||||||
|
Node* m_freeNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cclosure : ChunkHeader
|
||||||
|
{
|
||||||
|
lua_function m_function;
|
||||||
|
HashTable* m_env;
|
||||||
|
__int16 m_numUpvalues;
|
||||||
|
__int16 m_flags;
|
||||||
|
InternString* m_name;
|
||||||
|
HksObject m_upvalues[1];
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,4 +280,19 @@ namespace game
|
|||||||
{
|
{
|
||||||
WEAK symbol<gentity_s> g_entities{0x143C26DC0, 0};
|
WEAK symbol<gentity_s> g_entities{0x143C26DC0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace hks
|
||||||
|
{
|
||||||
|
WEAK symbol<lua_State*> lua_state{0, 0x1412E2B50};
|
||||||
|
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0, 0x1400290B0};
|
||||||
|
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0, 0x14009D3C0};
|
||||||
|
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0, 0x14009E480};
|
||||||
|
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0, 0x14009D800};
|
||||||
|
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0, 0x1400C9EC0};
|
||||||
|
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0, 0x14008AAE0};
|
||||||
|
WEAK symbol<cclosure*(lua_State* s, lua_function function, int num_upvalues,
|
||||||
|
int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0, 0x14008AD00};
|
||||||
|
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0, 0x1400A7D60};
|
||||||
|
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0, 0x1400A0660};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
164
src/client/game/ui_scripting/execution.cpp
Normal file
164
src/client/game/ui_scripting/execution.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "execution.hpp"
|
||||||
|
#include "stack_isolation.hpp"
|
||||||
|
#include "component/ui_scripting.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
void push_value(const script_value& value)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
const auto value_ = value.get_raw();
|
||||||
|
*state->m_apistack.top = value_;
|
||||||
|
state->m_apistack.top++;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value get_return_value(int offset)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
return state->m_apistack.top[-1 - offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments get_return_values(int count)
|
||||||
|
{
|
||||||
|
arguments values;
|
||||||
|
|
||||||
|
for (auto i = count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
values.push_back(get_return_value(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.size() == 0)
|
||||||
|
{
|
||||||
|
values.push_back({});
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments call_script_function(const function& function, const arguments& arguments)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
|
stack_isolation _;
|
||||||
|
push_value(function);
|
||||||
|
for (auto i = arguments.begin(); i != arguments.end(); ++i)
|
||||||
|
{
|
||||||
|
push_value(*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _1 = gsl::finally(&disable_error_hook);
|
||||||
|
enable_error_hook();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0);
|
||||||
|
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||||
|
return get_return_values(count);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Error executing script function: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value get_field(const userdata& self, const script_value& key)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
|
stack_isolation _;
|
||||||
|
push_value(key);
|
||||||
|
|
||||||
|
const auto _1 = gsl::finally(&disable_error_hook);
|
||||||
|
enable_error_hook();
|
||||||
|
|
||||||
|
game::hks::HksObject value{};
|
||||||
|
game::hks::HksObject userdata{};
|
||||||
|
userdata.t = game::hks::TUSERDATA;
|
||||||
|
userdata.v.ptr = self.ptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Error getting userdata field: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value get_field(const table& self, const script_value& key)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
|
stack_isolation _;
|
||||||
|
push_value(key);
|
||||||
|
|
||||||
|
const auto _1 = gsl::finally(&disable_error_hook);
|
||||||
|
enable_error_hook();
|
||||||
|
|
||||||
|
game::hks::HksObject value{};
|
||||||
|
game::hks::HksObject userdata{};
|
||||||
|
userdata.t = game::hks::TTABLE;
|
||||||
|
userdata.v.ptr = self.ptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Error getting table field: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_field(const userdata& self, const script_value& key, const script_value& value)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
|
stack_isolation _;
|
||||||
|
|
||||||
|
const auto _1 = gsl::finally(&disable_error_hook);
|
||||||
|
enable_error_hook();
|
||||||
|
|
||||||
|
game::hks::HksObject userdata{};
|
||||||
|
userdata.t = game::hks::TUSERDATA;
|
||||||
|
userdata.v.ptr = self.ptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Error setting userdata field: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_field(const table& self, const script_value& key, const script_value& value)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
|
stack_isolation _;
|
||||||
|
|
||||||
|
const auto _1 = gsl::finally(&disable_error_hook);
|
||||||
|
enable_error_hook();
|
||||||
|
|
||||||
|
game::hks::HksObject userdata{};
|
||||||
|
userdata.t = game::hks::TTABLE;
|
||||||
|
userdata.v.ptr = self.ptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Error setting table field: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/client/game/ui_scripting/execution.hpp
Normal file
18
src/client/game/ui_scripting/execution.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
#include "script_value.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
void push_value(const script_value& value);
|
||||||
|
script_value get_return_value(int offset);
|
||||||
|
arguments get_return_values(int count);
|
||||||
|
|
||||||
|
arguments call_script_function(const function& function, const arguments& arguments);
|
||||||
|
|
||||||
|
script_value get_field(const userdata& self, const script_value& key);
|
||||||
|
script_value get_field(const table& self, const script_value& key);
|
||||||
|
void set_field(const userdata& self, const script_value& key, const script_value& value);
|
||||||
|
void set_field(const table& self, const script_value& key, const script_value& value);
|
||||||
|
}
|
205
src/client/game/ui_scripting/lua/context.cpp
Normal file
205
src/client/game/ui_scripting/lua/context.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "context.hpp"
|
||||||
|
#include "error.hpp"
|
||||||
|
#include "value_conversion.hpp"
|
||||||
|
#include "../script_value.hpp"
|
||||||
|
#include "../execution.hpp"
|
||||||
|
|
||||||
|
#include "../../../component/ui_scripting.hpp"
|
||||||
|
#include "../../../component/command.hpp"
|
||||||
|
|
||||||
|
#include "component/game_console.hpp"
|
||||||
|
#include "component/scheduler.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/nt.hpp>
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
|
||||||
|
namespace ui_scripting::lua
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void setup_types(sol::state& state, scheduler& scheduler)
|
||||||
|
{
|
||||||
|
struct game
|
||||||
|
{
|
||||||
|
};
|
||||||
|
auto game_type = state.new_usertype<game>("game_");
|
||||||
|
state["game"] = game();
|
||||||
|
|
||||||
|
game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback,
|
||||||
|
const long long milliseconds)
|
||||||
|
{
|
||||||
|
return scheduler.add(callback, milliseconds, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback,
|
||||||
|
const long long milliseconds)
|
||||||
|
{
|
||||||
|
return scheduler.add(callback, milliseconds, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto userdata_type = state.new_usertype<userdata>("userdata_");
|
||||||
|
|
||||||
|
userdata_type["new"] = sol::property(
|
||||||
|
[](const userdata& userdata, const sol::this_state s)
|
||||||
|
{
|
||||||
|
return convert(s, userdata.get("new"));
|
||||||
|
},
|
||||||
|
[](const userdata& userdata, const sol::this_state s, const sol::lua_value& value)
|
||||||
|
{
|
||||||
|
userdata.set("new", convert({s, value}));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
userdata_type["get"] = [](const userdata& userdata, const sol::this_state s,
|
||||||
|
const sol::lua_value& key)
|
||||||
|
{
|
||||||
|
return convert(s, userdata.get(convert({s, key})));
|
||||||
|
};
|
||||||
|
|
||||||
|
userdata_type["set"] = [](const userdata& userdata, const sol::this_state s,
|
||||||
|
const sol::lua_value& key, const sol::lua_value& value)
|
||||||
|
{
|
||||||
|
userdata.set(convert({s, key}), convert({s, value}));
|
||||||
|
};
|
||||||
|
|
||||||
|
userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s,
|
||||||
|
const sol::lua_value& key)
|
||||||
|
{
|
||||||
|
return convert(s, userdata.get(convert({s, key})));
|
||||||
|
};
|
||||||
|
|
||||||
|
userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s,
|
||||||
|
const sol::lua_value& key, const sol::lua_value& value)
|
||||||
|
{
|
||||||
|
userdata.set(convert({s, key }), convert({s, value}));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto table_type = state.new_usertype<table>("table_");
|
||||||
|
|
||||||
|
table_type["new"] = sol::property(
|
||||||
|
[](const table& table, const sol::this_state s)
|
||||||
|
{
|
||||||
|
return convert(s, table.get("new"));
|
||||||
|
},
|
||||||
|
[](const table& table, const sol::this_state s, const sol::lua_value& value)
|
||||||
|
{
|
||||||
|
table.set("new", convert({s, value}));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
table_type["get"] = [](const table& table, const sol::this_state s,
|
||||||
|
const sol::lua_value& key)
|
||||||
|
{
|
||||||
|
return convert(s, table.get(convert({s, key})));
|
||||||
|
};
|
||||||
|
|
||||||
|
table_type["set"] = [](const table& table, const sol::this_state s,
|
||||||
|
const sol::lua_value& key, const sol::lua_value& value)
|
||||||
|
{
|
||||||
|
table.set(convert({s, key}), convert({s, value}));
|
||||||
|
};
|
||||||
|
|
||||||
|
table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s,
|
||||||
|
const sol::lua_value& key)
|
||||||
|
{
|
||||||
|
return convert(s, table.get(convert({s, key})));
|
||||||
|
};
|
||||||
|
|
||||||
|
table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s,
|
||||||
|
const sol::lua_value& key, const sol::lua_value& value)
|
||||||
|
{
|
||||||
|
table.set(convert({s, key}), convert({s, value}));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto function_type = state.new_usertype<function>("function_");
|
||||||
|
|
||||||
|
function_type[sol::meta_function::call] = [](const function& function, const sol::this_state s, sol::variadic_args va)
|
||||||
|
{
|
||||||
|
arguments arguments{};
|
||||||
|
|
||||||
|
for (auto arg : va)
|
||||||
|
{
|
||||||
|
arguments.push_back(convert({s, arg}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto values = function.call(arguments);
|
||||||
|
std::vector<sol::lua_value> returns;
|
||||||
|
|
||||||
|
for (const auto& value : values)
|
||||||
|
{
|
||||||
|
returns.push_back(convert(s, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sol::as_returns(returns);
|
||||||
|
};
|
||||||
|
|
||||||
|
state["luiglobals"] = table((*::game::hks::lua_state)->globals.v.table);
|
||||||
|
state["CoD"] = state["luiglobals"]["CoD"];
|
||||||
|
state["LUI"] = state["luiglobals"]["LUI"];
|
||||||
|
state["Engine"] = state["luiglobals"]["Engine"];
|
||||||
|
state["Game"] = state["luiglobals"]["Game"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context::context(std::string folder)
|
||||||
|
: folder_(std::move(folder))
|
||||||
|
, scheduler_(state_)
|
||||||
|
{
|
||||||
|
this->state_.open_libraries(sol::lib::base,
|
||||||
|
sol::lib::package,
|
||||||
|
sol::lib::io,
|
||||||
|
sol::lib::string,
|
||||||
|
sol::lib::os,
|
||||||
|
sol::lib::math,
|
||||||
|
sol::lib::table);
|
||||||
|
|
||||||
|
this->state_["include"] = [this](const std::string& file)
|
||||||
|
{
|
||||||
|
this->load_script(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
sol::function old_require = this->state_["require"];
|
||||||
|
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
|
||||||
|
this->state_["require"] = [base_path, old_require](const std::string& path)
|
||||||
|
{
|
||||||
|
return old_require(base_path + path);
|
||||||
|
};
|
||||||
|
|
||||||
|
this->state_["scriptdir"] = [this]()
|
||||||
|
{
|
||||||
|
return this->folder_;
|
||||||
|
};
|
||||||
|
|
||||||
|
setup_types(this->state_, this->scheduler_);
|
||||||
|
|
||||||
|
printf("Loading ui script '%s'\n", this->folder_.data());
|
||||||
|
this->load_script("__init__");
|
||||||
|
}
|
||||||
|
|
||||||
|
context::~context()
|
||||||
|
{
|
||||||
|
this->state_.collect_garbage();
|
||||||
|
this->scheduler_.clear();
|
||||||
|
this->state_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void context::run_frame()
|
||||||
|
{
|
||||||
|
this->scheduler_.run_frame();
|
||||||
|
this->state_.collect_garbage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void context::load_script(const std::string& script)
|
||||||
|
{
|
||||||
|
if (!this->loaded_scripts_.emplace(script).second)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto file = (std::filesystem::path{this->folder_} / (script + ".lua")).generic_string();
|
||||||
|
handle_error(this->state_.safe_script_file(file, &sol::script_pass_on_error));
|
||||||
|
}
|
||||||
|
}
|
36
src/client/game/ui_scripting/lua/context.hpp
Normal file
36
src/client/game/ui_scripting/lua/context.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4702)
|
||||||
|
|
||||||
|
#define SOL_ALL_SAFETIES_ON 1
|
||||||
|
#define SOL_PRINT_ERRORS 0
|
||||||
|
#include <sol/sol.hpp>
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting::lua
|
||||||
|
{
|
||||||
|
class context
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
context(std::string folder);
|
||||||
|
~context();
|
||||||
|
|
||||||
|
context(context&&) noexcept = delete;
|
||||||
|
context& operator=(context&&) noexcept = delete;
|
||||||
|
|
||||||
|
context(const context&) = delete;
|
||||||
|
context& operator=(const context&) = delete;
|
||||||
|
|
||||||
|
void run_frame();
|
||||||
|
|
||||||
|
private:
|
||||||
|
sol::state state_{};
|
||||||
|
std::string folder_;
|
||||||
|
std::unordered_set<std::string> loaded_scripts_;
|
||||||
|
|
||||||
|
scheduler scheduler_;
|
||||||
|
|
||||||
|
void load_script(const std::string& script);
|
||||||
|
};
|
||||||
|
}
|
61
src/client/game/ui_scripting/lua/engine.cpp
Normal file
61
src/client/game/ui_scripting/lua/engine.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "engine.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
#include "../../../component/scheduler.hpp"
|
||||||
|
#include "../../../component/ui_scripting.hpp"
|
||||||
|
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
namespace ui_scripting::lua::engine
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
auto& get_scripts()
|
||||||
|
{
|
||||||
|
static std::vector<std::unique_ptr<context>> scripts{};
|
||||||
|
return scripts;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_scripts(const std::string& script_dir)
|
||||||
|
{
|
||||||
|
if (!utils::io::directory_exists(script_dir))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scripts = utils::io::list_files(script_dir);
|
||||||
|
|
||||||
|
for (const auto& script : scripts)
|
||||||
|
{
|
||||||
|
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
||||||
|
{
|
||||||
|
get_scripts().push_back(std::make_unique<context>(script));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
clear_converted_functions();
|
||||||
|
get_scripts().clear();
|
||||||
|
load_scripts("s1x/ui_scripts/");
|
||||||
|
load_scripts("data/ui_scripts/");
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
clear_converted_functions();
|
||||||
|
get_scripts().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_frame()
|
||||||
|
{
|
||||||
|
for (auto& script : get_scripts())
|
||||||
|
{
|
||||||
|
script->run_frame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/client/game/ui_scripting/lua/engine.hpp
Normal file
8
src/client/game/ui_scripting/lua/engine.hpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ui_scripting::lua::engine
|
||||||
|
{
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
void run_frame();
|
||||||
|
}
|
18
src/client/game/ui_scripting/lua/error.cpp
Normal file
18
src/client/game/ui_scripting/lua/error.cpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "error.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting::lua
|
||||||
|
{
|
||||||
|
void handle_error(const sol::protected_function_result& result)
|
||||||
|
{
|
||||||
|
if (!result.valid())
|
||||||
|
{
|
||||||
|
printf("************** UI Script execution error **************\n");
|
||||||
|
|
||||||
|
const sol::error err = result;
|
||||||
|
printf("%s\n", err.what());
|
||||||
|
|
||||||
|
printf("****************************************************\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/client/game/ui_scripting/lua/error.hpp
Normal file
8
src/client/game/ui_scripting/lua/error.hpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting::lua
|
||||||
|
{
|
||||||
|
void handle_error(const sol::protected_function_result& result);
|
||||||
|
}
|
122
src/client/game/ui_scripting/lua/scheduler.cpp
Normal file
122
src/client/game/ui_scripting/lua/scheduler.cpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
#include "error.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting::lua
|
||||||
|
{
|
||||||
|
scheduler::scheduler(sol::state& state)
|
||||||
|
{
|
||||||
|
auto task_handle_type = state.new_usertype<task_handle>("task_handle");
|
||||||
|
|
||||||
|
task_handle_type["clear"] = [this](const task_handle& handle)
|
||||||
|
{
|
||||||
|
this->remove(handle);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduler::run_frame()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
this->merge_callbacks();
|
||||||
|
|
||||||
|
for (auto i = tasks.begin(); i != tasks.end();)
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto diff = now - i->last_call;
|
||||||
|
|
||||||
|
if (diff < i->delay)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i->last_call = now;
|
||||||
|
|
||||||
|
if (!i->is_deleted)
|
||||||
|
{
|
||||||
|
handle_error(i->callback());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i->is_volatile || i->is_deleted)
|
||||||
|
{
|
||||||
|
i = tasks.erase(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduler::clear()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&](task_list& new_tasks)
|
||||||
|
{
|
||||||
|
new_tasks.clear();
|
||||||
|
tasks.clear();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
task_handle scheduler::add(const sol::protected_function& callback, const long long milliseconds,
|
||||||
|
const bool is_volatile)
|
||||||
|
{
|
||||||
|
return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile);
|
||||||
|
}
|
||||||
|
|
||||||
|
task_handle scheduler::add(const sol::protected_function& callback, const std::chrono::milliseconds delay,
|
||||||
|
const bool is_volatile)
|
||||||
|
{
|
||||||
|
const uint64_t id = ++this->current_task_id_;
|
||||||
|
|
||||||
|
task task;
|
||||||
|
task.is_volatile = is_volatile;
|
||||||
|
task.callback = callback;
|
||||||
|
task.delay = delay;
|
||||||
|
task.last_call = std::chrono::steady_clock::now();
|
||||||
|
task.id = id;
|
||||||
|
task.is_deleted = false;
|
||||||
|
|
||||||
|
new_callbacks_.access([&task](task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.emplace_back(std::move(task));
|
||||||
|
});
|
||||||
|
|
||||||
|
return {id};
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduler::remove(const task_handle& handle)
|
||||||
|
{
|
||||||
|
auto mask_as_deleted = [&](task_list& tasks)
|
||||||
|
{
|
||||||
|
for (auto& task : tasks)
|
||||||
|
{
|
||||||
|
if (task.id == handle.id)
|
||||||
|
{
|
||||||
|
task.is_deleted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
callbacks_.access(mask_as_deleted);
|
||||||
|
new_callbacks_.access(mask_as_deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduler::merge_callbacks()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&](task_list& new_tasks)
|
||||||
|
{
|
||||||
|
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
||||||
|
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||||
|
new_tasks = {};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
50
src/client/game/ui_scripting/lua/scheduler.hpp
Normal file
50
src/client/game/ui_scripting/lua/scheduler.hpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
|
namespace ui_scripting::lua
|
||||||
|
{
|
||||||
|
class context;
|
||||||
|
|
||||||
|
class task_handle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint64_t id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class task final : public task_handle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::chrono::steady_clock::time_point last_call{};
|
||||||
|
sol::protected_function callback{};
|
||||||
|
std::chrono::milliseconds delay{};
|
||||||
|
bool is_volatile = false;
|
||||||
|
bool is_deleted = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class scheduler final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
scheduler(sol::state& state);
|
||||||
|
|
||||||
|
scheduler(scheduler&&) noexcept = delete;
|
||||||
|
scheduler& operator=(scheduler&&) noexcept = delete;
|
||||||
|
|
||||||
|
scheduler(const scheduler&) = delete;
|
||||||
|
scheduler& operator=(const scheduler&) = delete;
|
||||||
|
|
||||||
|
void run_frame();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
task_handle add(const sol::protected_function& callback, long long milliseconds, bool is_volatile);
|
||||||
|
task_handle add(const sol::protected_function& callback, std::chrono::milliseconds delay, bool is_volatile);
|
||||||
|
|
||||||
|
private:
|
||||||
|
using task_list = std::vector<task>;
|
||||||
|
utils::concurrency::container<task_list> new_callbacks_;
|
||||||
|
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||||
|
std::atomic_int64_t current_task_id_ = 0;
|
||||||
|
|
||||||
|
void remove(const task_handle& handle);
|
||||||
|
void merge_callbacks();
|
||||||
|
};
|
||||||
|
}
|
145
src/client/game/ui_scripting/lua/value_conversion.cpp
Normal file
145
src/client/game/ui_scripting/lua/value_conversion.cpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "value_conversion.hpp"
|
||||||
|
#include "../execution.hpp"
|
||||||
|
#include "../stack_isolation.hpp"
|
||||||
|
#include "../../../component/ui_scripting.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting::lua
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
table convert_table(const sol::table& t)
|
||||||
|
{
|
||||||
|
table res{};
|
||||||
|
|
||||||
|
t.for_each([res](const sol::object& key, const sol::object& value)
|
||||||
|
{
|
||||||
|
res.set(convert(key), convert(value));
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value convert_function(const sol::protected_function& function)
|
||||||
|
{
|
||||||
|
const auto closure = game::hks::cclosure_Create(*game::hks::lua_state, main_function_handler, 0, 0, 0);
|
||||||
|
add_converted_function(closure, function);
|
||||||
|
|
||||||
|
game::hks::HksObject value{};
|
||||||
|
value.t = game::hks::TCFUNCTION;
|
||||||
|
value.v.cClosure = closure;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value convert(const sol::lua_value& value)
|
||||||
|
{
|
||||||
|
if (value.is<bool>())
|
||||||
|
{
|
||||||
|
return {value.as<bool>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<int>())
|
||||||
|
{
|
||||||
|
return {value.as<int>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<unsigned int>())
|
||||||
|
{
|
||||||
|
return {value.as<unsigned int>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<double>())
|
||||||
|
{
|
||||||
|
return {value.as<double>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<float>())
|
||||||
|
{
|
||||||
|
return {value.as<float>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<std::string>())
|
||||||
|
{
|
||||||
|
return {value.as<std::string>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<lightuserdata>())
|
||||||
|
{
|
||||||
|
return {value.as<lightuserdata>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<userdata>())
|
||||||
|
{
|
||||||
|
return {value.as<userdata>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<table>())
|
||||||
|
{
|
||||||
|
return {value.as<table>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<function>())
|
||||||
|
{
|
||||||
|
return {value.as<function>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<sol::table>())
|
||||||
|
{
|
||||||
|
return {convert_table(value.as<sol::table>())};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<sol::protected_function>())
|
||||||
|
{
|
||||||
|
return {convert_function(value.as<sol::protected_function>())};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::lua_value convert(lua_State* state, const script_value& value)
|
||||||
|
{
|
||||||
|
if (value.is<int>())
|
||||||
|
{
|
||||||
|
return {state, value.as<int>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<float>())
|
||||||
|
{
|
||||||
|
return {state, value.as<float>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<bool>())
|
||||||
|
{
|
||||||
|
return {state, value.as<bool>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<std::string>())
|
||||||
|
{
|
||||||
|
return {state, value.as<std::string>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<lightuserdata>())
|
||||||
|
{
|
||||||
|
return {state, value.as<lightuserdata>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<userdata>())
|
||||||
|
{
|
||||||
|
return {state, value.as<userdata>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<table>())
|
||||||
|
{
|
||||||
|
return {state, value.as<table>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.is<function>())
|
||||||
|
{
|
||||||
|
return {state, value.as<function>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {state, sol::lua_nil};
|
||||||
|
}
|
||||||
|
}
|
9
src/client/game/ui_scripting/lua/value_conversion.hpp
Normal file
9
src/client/game/ui_scripting/lua/value_conversion.hpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "context.hpp"
|
||||||
|
#include "../script_value.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting::lua
|
||||||
|
{
|
||||||
|
script_value convert(const sol::lua_value& value);
|
||||||
|
sol::lua_value convert(lua_State* state, const script_value& value);
|
||||||
|
}
|
274
src/client/game/ui_scripting/script_value.cpp
Normal file
274
src/client/game/ui_scripting/script_value.cpp
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "execution.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
#include "stack_isolation.hpp"
|
||||||
|
#include "script_value.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
/***************************************************************
|
||||||
|
* Constructors
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
script_value::script_value(const game::hks::HksObject& value)
|
||||||
|
: value_(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const int value)
|
||||||
|
{
|
||||||
|
game::hks::HksObject obj{};
|
||||||
|
obj.t = game::hks::TNUMBER;
|
||||||
|
obj.v.number = static_cast<float>(value);
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const unsigned int value)
|
||||||
|
{
|
||||||
|
game::hks::HksObject obj{};
|
||||||
|
obj.t = game::hks::TNUMBER;
|
||||||
|
obj.v.number = static_cast<float>(value);
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const bool value)
|
||||||
|
{
|
||||||
|
game::hks::HksObject obj{};
|
||||||
|
obj.t = game::hks::TBOOLEAN;
|
||||||
|
obj.v.boolean = value;
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const float value)
|
||||||
|
{
|
||||||
|
game::hks::HksObject obj{};
|
||||||
|
obj.t = game::hks::TNUMBER;
|
||||||
|
obj.v.number = static_cast<float>(value);
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const double value)
|
||||||
|
: script_value(static_cast<float>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const char* value)
|
||||||
|
{
|
||||||
|
game::hks::HksObject obj{};
|
||||||
|
stack_isolation _;
|
||||||
|
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
game::hks::hksi_lua_pushlstring(state, value, (unsigned int)strlen(value));
|
||||||
|
obj = state->m_apistack.top[-1];
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const std::string& value)
|
||||||
|
: script_value(value.data())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const lightuserdata& value)
|
||||||
|
{
|
||||||
|
this->value_.t = game::hks::TLIGHTUSERDATA;
|
||||||
|
this->value_.v.ptr = value.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const userdata& value)
|
||||||
|
{
|
||||||
|
this->value_.t = game::hks::TUSERDATA;
|
||||||
|
this->value_.v.ptr = value.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const table& value)
|
||||||
|
{
|
||||||
|
this->value_.t = game::hks::TTABLE;
|
||||||
|
this->value_.v.ptr = value.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const function& value)
|
||||||
|
{
|
||||||
|
this->value_.t = value.type;
|
||||||
|
this->value_.v.ptr = value.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Integer
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<int>() const
|
||||||
|
{
|
||||||
|
const auto number = this->get_raw().v.number;
|
||||||
|
return this->get_raw().t == game::hks::TNUMBER && static_cast<int>(number) == number;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<unsigned int>() const
|
||||||
|
{
|
||||||
|
return this->is<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
int script_value::get() const
|
||||||
|
{
|
||||||
|
return static_cast<int>(this->get_raw().v.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
unsigned int script_value::get() const
|
||||||
|
{
|
||||||
|
return static_cast<unsigned int>(this->get_raw().v.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Boolean
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<bool>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().t == game::hks::TBOOLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::get() const
|
||||||
|
{
|
||||||
|
return this->get_raw().v.boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Float
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<float>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().t == game::hks::TNUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<double>() const
|
||||||
|
{
|
||||||
|
return this->is<float>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
float script_value::get() const
|
||||||
|
{
|
||||||
|
return this->get_raw().v.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
double script_value::get() const
|
||||||
|
{
|
||||||
|
return static_cast<double>(this->get_raw().v.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* String
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<const char*>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().t == game::hks::TSTRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<std::string>() const
|
||||||
|
{
|
||||||
|
return this->is<const char*>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const char* script_value::get() const
|
||||||
|
{
|
||||||
|
return this->get_raw().v.str->m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string script_value::get() const
|
||||||
|
{
|
||||||
|
return this->get<const char*>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Lightuserdata
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<lightuserdata>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().t == game::hks::TLIGHTUSERDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
lightuserdata script_value::get() const
|
||||||
|
{
|
||||||
|
return this->get_raw().v.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Userdata
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<userdata>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().t == game::hks::TUSERDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
userdata script_value::get() const
|
||||||
|
{
|
||||||
|
return this->get_raw().v.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Table
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<table>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().t == game::hks::TTABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
table script_value::get() const
|
||||||
|
{
|
||||||
|
return this->get_raw().v.table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Function
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<function>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().t == game::hks::TIFUNCTION
|
||||||
|
|| this->get_raw().t == game::hks::TCFUNCTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
function script_value::get() const
|
||||||
|
{
|
||||||
|
return {this->get_raw().v.cClosure, this->get_raw().t};
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
*
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
const game::hks::HksObject& script_value::get_raw() const
|
||||||
|
{
|
||||||
|
return this->value_;
|
||||||
|
}
|
||||||
|
}
|
56
src/client/game/ui_scripting/script_value.hpp
Normal file
56
src/client/game/ui_scripting/script_value.hpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
class lightuserdata;
|
||||||
|
class userdata;
|
||||||
|
class table;
|
||||||
|
class function;
|
||||||
|
|
||||||
|
class script_value
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
script_value() = default;
|
||||||
|
script_value(const game::hks::HksObject& value);
|
||||||
|
|
||||||
|
script_value(int value);
|
||||||
|
script_value(unsigned int value);
|
||||||
|
script_value(bool value);
|
||||||
|
|
||||||
|
script_value(float value);
|
||||||
|
script_value(double value);
|
||||||
|
|
||||||
|
script_value(const char* value);
|
||||||
|
script_value(const std::string& value);
|
||||||
|
|
||||||
|
script_value(const lightuserdata& value);
|
||||||
|
script_value(const userdata& value);
|
||||||
|
script_value(const table& value);
|
||||||
|
script_value(const function& value);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool is() const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T as() const
|
||||||
|
{
|
||||||
|
if (!this->is<T>())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const game::hks::HksObject& get_raw() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
T get() const;
|
||||||
|
|
||||||
|
game::hks::HksObject value_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
using arguments = std::vector<script_value>;
|
||||||
|
}
|
30
src/client/game/ui_scripting/stack_isolation.cpp
Normal file
30
src/client/game/ui_scripting/stack_isolation.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "stack_isolation.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
stack_isolation::stack_isolation()
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
|
this->top_ = state->m_apistack.top;
|
||||||
|
this->base_ = state->m_apistack.base;
|
||||||
|
this->alloc_top_ = state->m_apistack.alloc_top;
|
||||||
|
this->bottom_ = state->m_apistack.bottom;
|
||||||
|
|
||||||
|
state->m_apistack.top = this->stack_;
|
||||||
|
state->m_apistack.base = state->m_apistack.top;
|
||||||
|
state->m_apistack.alloc_top = &this->stack_[ARRAYSIZE(this->stack_) - 1];
|
||||||
|
state->m_apistack.bottom = state->m_apistack.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_isolation::~stack_isolation()
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
|
state->m_apistack.top = this->top_;
|
||||||
|
state->m_apistack.base = this->base_;
|
||||||
|
state->m_apistack.alloc_top = this->alloc_top_;
|
||||||
|
state->m_apistack.bottom = this->bottom_;
|
||||||
|
}
|
||||||
|
}
|
25
src/client/game/ui_scripting/stack_isolation.hpp
Normal file
25
src/client/game/ui_scripting/stack_isolation.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
class stack_isolation final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
stack_isolation();
|
||||||
|
~stack_isolation();
|
||||||
|
|
||||||
|
stack_isolation(stack_isolation&&) = delete;
|
||||||
|
stack_isolation(const stack_isolation&) = delete;
|
||||||
|
stack_isolation& operator=(stack_isolation&&) = delete;
|
||||||
|
stack_isolation& operator=(const stack_isolation&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
game::hks::HksObject stack_[512]{};
|
||||||
|
|
||||||
|
game::hks::HksObject* top_;
|
||||||
|
game::hks::HksObject* base_;
|
||||||
|
game::hks::HksObject* alloc_top_;
|
||||||
|
game::hks::HksObject* bottom_;
|
||||||
|
};
|
||||||
|
}
|
142
src/client/game/ui_scripting/types.cpp
Normal file
142
src/client/game/ui_scripting/types.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "types.hpp"
|
||||||
|
#include "execution.hpp"
|
||||||
|
#include "stack_isolation.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
/***************************************************************
|
||||||
|
* Lightuserdata
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
lightuserdata::lightuserdata(void* ptr_)
|
||||||
|
: ptr(ptr_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Userdata
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
userdata::userdata(void* ptr_)
|
||||||
|
: ptr(ptr_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void userdata::set(const script_value& key, const script_value& value) const
|
||||||
|
{
|
||||||
|
set_field(*this, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value userdata::get(const script_value& key) const
|
||||||
|
{
|
||||||
|
return get_field(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Table
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
table::table()
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
table::table(game::hks::HashTable* ptr_)
|
||||||
|
: ptr(ptr_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void table::set(const script_value& key, const script_value& value) const
|
||||||
|
{
|
||||||
|
set_field(*this, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value table::get(const script_value& key) const
|
||||||
|
{
|
||||||
|
return get_field(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Function
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
|
||||||
|
: ptr(ptr_)
|
||||||
|
, type(type_)
|
||||||
|
{
|
||||||
|
this->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
function::function(const function& other) : function(other.ptr, other.type)
|
||||||
|
{
|
||||||
|
this->ref = other.ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
function::function(function&& other) noexcept
|
||||||
|
{
|
||||||
|
this->ptr = other.ptr;
|
||||||
|
this->type = other.type;
|
||||||
|
this->ref = other.ref;
|
||||||
|
other.ref = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function::~function()
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
function& function::operator=(const function& other)
|
||||||
|
{
|
||||||
|
if (&other != this)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
this->ptr = other.ptr;
|
||||||
|
this->type = other.type;
|
||||||
|
this->ref = other.ref;
|
||||||
|
this->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function& function::operator=(function&& other) noexcept
|
||||||
|
{
|
||||||
|
if (&other != this)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
this->ptr = other.ptr;
|
||||||
|
this->type = other.type;
|
||||||
|
this->ref = other.ref;
|
||||||
|
other.ref = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void function::add()
|
||||||
|
{
|
||||||
|
game::hks::HksObject value{};
|
||||||
|
value.v.cClosure = this->ptr;
|
||||||
|
value.t = this->type;
|
||||||
|
|
||||||
|
stack_isolation _;
|
||||||
|
push_value(value);
|
||||||
|
|
||||||
|
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void function::release()
|
||||||
|
{
|
||||||
|
if (this->ref)
|
||||||
|
{
|
||||||
|
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments function::call(const arguments& arguments) const
|
||||||
|
{
|
||||||
|
return call_script_function(*this, arguments);
|
||||||
|
}
|
||||||
|
}
|
61
src/client/game/ui_scripting/types.hpp
Normal file
61
src/client/game/ui_scripting/types.hpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "script_value.hpp"
|
||||||
|
|
||||||
|
namespace ui_scripting
|
||||||
|
{
|
||||||
|
class lightuserdata
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
lightuserdata(void*);
|
||||||
|
void* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class userdata
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
userdata(void*);
|
||||||
|
|
||||||
|
script_value get(const script_value& key) const;
|
||||||
|
void set(const script_value& key, const script_value& value) const;
|
||||||
|
|
||||||
|
void* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class table
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
table();
|
||||||
|
table(game::hks::HashTable* ptr_);
|
||||||
|
|
||||||
|
script_value get(const script_value& key) const;
|
||||||
|
void set(const script_value& key, const script_value& value) const;
|
||||||
|
|
||||||
|
game::hks::HashTable* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class function
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
function(game::hks::cclosure*, game::hks::HksObjectType);
|
||||||
|
|
||||||
|
function(const function& other);
|
||||||
|
function(function&& other) noexcept;
|
||||||
|
|
||||||
|
~function();
|
||||||
|
|
||||||
|
function& operator=(const function& other);
|
||||||
|
function& operator=(function&& other) noexcept;
|
||||||
|
|
||||||
|
arguments call(const arguments& arguments) const;
|
||||||
|
|
||||||
|
game::hks::cclosure* ptr;
|
||||||
|
game::hks::HksObjectType type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void add();
|
||||||
|
void release();
|
||||||
|
|
||||||
|
int ref;
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user