GSC: expand and enhance

This commit is contained in:
FutureRave 2022-10-25 22:13:49 +01:00
parent f02b6afb71
commit 4d51ee436f
No known key found for this signature in database
GPG Key ID: 22F9079C86CFAB31
30 changed files with 921 additions and 1851 deletions

2
deps/gsc-tool vendored

@ -1 +1 @@
Subproject commit 2ab5118d168a1889b3b59d3d8996509046e2642b
Subproject commit 842f168a67878f18ea6a42b9d3ce56ff9fafff0d

View File

@ -21,6 +21,8 @@ namespace command
{
namespace
{
constexpr auto CMD_MAX_NESTING = 8;
utils::hook::detour client_command_hook;
std::unordered_map<std::string, std::function<void(params&)>> handlers;
@ -31,7 +33,7 @@ namespace command
params params = {};
const auto command = utils::string::to_lower(params[0]);
if (handlers.find(command) != handlers.end())
if (handlers.contains(command))
{
handlers[command](params);
}
@ -131,6 +133,7 @@ namespace command
params::params()
: nesting_(game::cmd_args->nesting)
{
assert(this->nesting_ < CMD_MAX_NESTING);
}
int params::size() const
@ -163,6 +166,7 @@ namespace command
params_sv::params_sv()
: nesting_(game::sv_cmd_args->nesting)
{
assert(this->nesting_ < CMD_MAX_NESTING);
}
int params_sv::size() const
@ -201,7 +205,7 @@ namespace command
{
const auto command = utils::string::to_lower(name);
if (handlers.find(command) == handlers.end())
if (!handlers.contains(command))
add_raw(name, main_handler);
handlers[command] = callback;
@ -222,7 +226,7 @@ namespace command
const auto command = utils::string::to_lower(name);
if (handlers_sv.find(command) == handlers_sv.end())
if (!handlers_sv.contains(command))
handlers_sv[command] = std::move(callback);
}

View File

@ -78,12 +78,11 @@ namespace dedicated
return console_command_queue;
}
void execute_console_command(const int client, const char* command)
void execute_console_command([[maybe_unused]] const int local_client_num, const char* command)
{
if (game::Live_SyncOnlineDataFlags(0) == 0)
{
game::Cbuf_AddText(client, command);
game::Cbuf_AddText(client, "\n");
command::execute(command);
}
else
{
@ -145,7 +144,7 @@ namespace dedicated
va_end(ap);
scheduler::once([]()
scheduler::once([]
{
command::execute("map_rotate");
}, scheduler::main, 3s);
@ -285,7 +284,7 @@ namespace dedicated
{
if (game::Live_SyncOnlineDataFlags(0) == 32 && game::Sys_IsDatabaseReady2())
{
scheduler::once([]()
scheduler::once([]
{
command::execute("xstartprivateparty", true);
command::execute("disconnect", true); // 32 -> 0
@ -296,7 +295,7 @@ namespace dedicated
return scheduler::cond_continue;
}, scheduler::pipeline::main, 1s);
scheduler::on_game_initialized([]()
scheduler::on_game_initialized([]
{
initialize();

View File

@ -5,7 +5,6 @@
#include "game/dvars.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -177,17 +176,14 @@ namespace dvar_cheats
utils::hook::call(0x1401BB782, cg_set_client_dvar_from_server);
// check for dvars being sent as string before parsing ids
scheduler::once([]()
{
#ifdef _DEBUG
constexpr auto value = true;
constexpr auto value = true;
#else
constexpr auto value = false;
constexpr auto value = false;
#endif
dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DvarFlags::DVAR_FLAG_REPLICATED,
"Allow cheat commands and dvars on this server");
}, scheduler::pipeline::main);
dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED,
"Allow cheat commands and dvars on this server");
}
};
}

View File

@ -53,9 +53,15 @@ namespace filesystem
custom_path_registered = false;
register_path(get_binary_directory() + "\\data");
register_path(".");
register_path("s1x");
// game's search paths
register_path("devraw");
register_path("devraw_shared");
register_path("raw_shared");
register_path("raw");
register_path("main");
game::FS_Startup(gamename);
}

View File

@ -0,0 +1,116 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "scheduler.hpp"
#include "scripting.hpp"
#include "console.hpp"
#include "game_log.hpp"
#include "gsc/script_extension.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
namespace game_log
{
namespace
{
void gscr_log_print()
{
char buf[1024]{};
std::size_t out_chars = 0;
for (auto i = 0u; i < game::Scr_GetNumParam(); ++i)
{
const auto* value = game::Scr_GetString(i);
const auto len = std::strlen(value);
out_chars += len;
if (out_chars >= sizeof(buf))
{
break;
}
strncat_s(buf, value, _TRUNCATE);
}
g_log_printf("%s", buf);
}
}
void g_log_printf(const char* fmt, ...)
{
const auto* log = dvars::g_log->current.string;
if (*log == '\0')
{
return;
}
char buffer[0x400]{};
va_list ap;
va_start(ap, fmt);
vsprintf_s(buffer, fmt, ap);
va_end(ap);
const auto time = *game::level_time / 1000;
utils::io::write_file(log, utils::string::va("%3i:%i%i %s",
time / 60,
time % 60 / 10,
time % 60 % 10,
buffer
), true);
}
class component final : public component_interface
{
public:
void post_load() override
{
if (game::environment::is_sp())
{
return;
}
gsc::override_function("logprint", &gscr_log_print);
scheduler::once([]
{
dvars::g_log = game::Dvar_RegisterString("g_log", "logs/games_mp.log", game::DVAR_FLAG_NONE, "Log file name");
}, scheduler::pipeline::main);
scripting::on_init([]
{
console::info("------- Game Initialization -------\n");
console::info("gamename: S1\n");
console::info("gamedate: " __DATE__ "\n");
const auto* log = dvars::g_log->current.string;
if (*log == '\0')
{
console::info("Not logging to disk.\n");
return;
}
console::info("Logging to disk: '%s'.\n", log);
g_log_printf("------------------------------------------------------------\n");
g_log_printf("InitGame\n");
});
scripting::on_shutdown([](int free_scripts)
{
console::info("==== ShutdownGame (%d) ====\n", free_scripts);
g_log_printf("ShutdownGame:\n");
g_log_printf("------------------------------------------------------------\n");
});
}
};
}
REGISTER_COMPONENT(game_log::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace game_log
{
void g_log_printf(const char* fmt, ...);
}

View File

@ -0,0 +1,126 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "script_extension.hpp"
#include "script_error.hpp"
#include "component/scripting.hpp"
#include <utils/hook.hpp>
namespace gsc
{
namespace
{
utils::hook::detour scr_emit_function_hook;
unsigned int current_filename = 0;
std::string unknown_function_error;
void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos)
{
current_filename = filename;
scr_emit_function_hook.invoke<void>(filename, thread_name, code_pos);
}
std::string get_filename_name()
{
const auto filename_str = game::SL_ConvertToString(static_cast<game::scr_string_t>(current_filename));
const auto id = std::atoi(filename_str);
if (!id)
{
return filename_str;
}
return scripting::get_token(id);
}
void get_unknown_function_error(const char* code_pos)
{
const auto function = find_function(code_pos);
if (function.has_value())
{
const auto& pos = function.value();
unknown_function_error = std::format(
"while processing function '{}' in script '{}':\nunknown script '{}'", pos.first, pos.second, scripting::current_file
);
}
else
{
unknown_function_error = std::format("unknown script '{}'", scripting::current_file);
}
}
void get_unknown_function_error(unsigned int thread_name)
{
const auto filename = get_filename_name();
const auto name = scripting::get_token(thread_name);
unknown_function_error = std::format(
"while processing script '{}':\nunknown function '{}::{}'", scripting::current_file, filename, name
);
}
void compile_error_stub(const char* code_pos, [[maybe_unused]] const char* msg)
{
get_unknown_function_error(code_pos);
game::Com_Error(game::ERR_DROP, "script link error\n%s", unknown_function_error.data());
}
unsigned int find_variable_stub(unsigned int parent_id, unsigned int thread_name)
{
const auto res = game::FindVariable(parent_id, thread_name);
if (!res)
{
get_unknown_function_error(thread_name);
game::Com_Error(game::ERR_DROP, "script link error\n%s", unknown_function_error.data());
}
return res;
}
}
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
{
for (const auto& file : scripting::script_function_table_sort)
{
for (auto i = file.second.begin(); i != file.second.end() && std::next(i) != file.second.end(); ++i)
{
const auto next = std::next(i);
if (pos >= i->second && pos < next->second)
{
return {std::make_pair(i->first, file.first)};
}
}
}
return {};
}
class error final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
scr_emit_function_hook.create(0x1403ED900, &scr_emit_function_stub);
utils::hook::call(0x1403ED7E6, compile_error_stub); // LinkFile
utils::hook::call(0x1403ED8D0, compile_error_stub); // LinkFile
utils::hook::call(0x1403ED9DB, find_variable_stub); // Scr_EmitFunction
}
void pre_destroy() override
{
scr_emit_function_hook.clear();
}
};
}
REGISTER_COMPONENT(gsc::error)

View File

@ -0,0 +1,6 @@
#pragma once
namespace gsc
{
std::optional<std::pair<std::string, std::string>> find_function(const char* pos);
}

View File

@ -1,27 +1,172 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/scripting/functions.hpp"
#include <utils/hook.hpp>
#include "component/console.hpp"
#include "script_error.hpp"
#include "script_extension.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
namespace gsc
{
std::uint16_t function_id_start = 0x2DF;
void* func_table[0x1000];
const game::dvar_t* developer_script = nullptr;
namespace
{
typedef void(*builtin_function)();
std::unordered_map<std::uint32_t, builtin_function> builtin_funcs_overrides;
#define RVA(ptr) static_cast<std::uint32_t>(reinterpret_cast<std::size_t>(ptr) - 0x140000000)
struct gsc_error : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
std::unordered_map<std::uint16_t, game::BuiltinFunction> functions;
bool force_error_print = false;
std::optional<std::string> gsc_error_msg;
std::unordered_map<std::uint32_t, game::BuiltinFunction> builtin_funcs_overrides;
utils::hook::detour scr_register_function_hook;
void override_function(const std::string& name, builtin_function func)
unsigned int scr_get_function_stub(const char** p_name, int* type)
{
const auto id = xsk::gsc::s1::resolver::function_id(name);
builtin_funcs_overrides.emplace(id, func);
const auto result = utils::hook::invoke<unsigned int>(0x1403318B0, p_name, type);
for (const auto& [name, func] : functions)
{
game::Scr_RegisterFunction(func, 0, name);
}
return result;
}
void execute_custom_function(game::BuiltinFunction function)
{
auto error = false;
try
{
function();
}
catch (const std::exception& ex)
{
error = true;
force_error_print = true;
gsc_error_msg = ex.what();
}
if (error)
{
game::Scr_ErrorInternal();
}
}
void vm_call_builtin_function(const std::uint32_t index)
{
const auto func = reinterpret_cast<game::BuiltinFunction>(scripting::get_function_by_index(index));
const auto custom = functions.contains(static_cast<std::uint16_t>(index));
if (!custom)
{
func();
}
else
{
execute_custom_function(func);
}
}
void builtin_call_error(const std::string& error)
{
const auto pos = game::scr_function_stack->pos;
const auto function_id = *reinterpret_cast<std::uint16_t*>(reinterpret_cast<std::size_t>(pos - 2));
if (function_id > 0x1000)
{
console::warn("in call to builtin method \"%s\"%s", xsk::gsc::s1::resolver::method_name(function_id).data(), error.data());
}
else
{
console::warn("in call to builtin function \"%s\"%s", xsk::gsc::s1::resolver::function_name(function_id).data(), error.data());
}
}
std::optional<std::string> get_opcode_name(const std::uint8_t opcode)
{
try
{
return {xsk::gsc::s1::resolver::opcode_name(opcode)};
}
catch (...)
{
return {};
}
}
void print_callstack()
{
for (auto frame = game::scr_VmPub->function_frame; frame != game::scr_VmPub->function_frame_start; --frame)
{
const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos;
const auto function = find_function(frame->fs.pos);
if (function.has_value())
{
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
}
else
{
console::warn("\tat unknown location %p\n", pos);
}
}
}
void vm_error_stub(int mark_pos)
{
if (!developer_script->current.enabled && !force_error_print)
{
utils::hook::invoke<void>(0x1404B6790, mark_pos);
return;
}
console::warn("******* script runtime error ********\n");
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x148806768);
const std::string error = gsc_error_msg.has_value() ? std::format(": {}", gsc_error_msg.value()) : std::string();
if ((opcode_id >= 0x1A && opcode_id <= 0x20) || (opcode_id >= 0xA9 && opcode_id <= 0xAF))
{
builtin_call_error(error);
}
else
{
const auto opcode = get_opcode_name(opcode_id);
if (opcode.has_value())
{
console::warn("while processing instruction %s%s\n", opcode.value().data(), error.data());
}
else
{
console::warn("while processing instruction 0x%X%s\n", opcode_id, error.data());
}
}
force_error_print = false;
gsc_error_msg = {};
print_callstack();
console::warn("************************************\n");
utils::hook::invoke<void>(0x1404B6790, mark_pos);
}
void scr_register_function_stub(void* func, int type, unsigned int name)
@ -53,6 +198,27 @@ namespace gsc
}
}
void scr_error(const char* error)
{
force_error_print = true;
gsc_error_msg = error;
game::Scr_ErrorInternal();
}
void override_function(const std::string& name, game::BuiltinFunction func)
{
const auto id = xsk::gsc::s1::resolver::function_id(name);
builtin_funcs_overrides.emplace(id, func);
}
void add_function(const std::string& name, game::BuiltinFunction function)
{
++function_id_start;
functions[function_id_start] = function;
xsk::gsc::s1::resolver::add_function(name, function_id_start);
}
class extension final : public component_interface
{
public:
@ -62,6 +228,26 @@ namespace gsc
override_function("print", &scr_print);
override_function("println", &scr_print_ln);
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115BC, 0x1403EDAEC), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115C2 + 4, 0x1403EDAF2 + 4), RVA(&func_table)); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14031F097 + 3, 0x1403FB7F7 + 3), RVA(&func_table)); // VM_Execute_0
utils::hook::inject(SELECT_VALUE(0x140311964 + 3, 0x1403EDEE4 + 3), &func_table); // Scr_BeginLoadScripts
if (game::environment::is_sp())
{
return;
}
developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
utils::hook::nop(0x1403FB7F7 + 5, 2);
utils::hook::call(0x1403FB7F7, vm_call_builtin_function);
utils::hook::call(0x1403FCAB0, vm_error_stub); // LargeLocalResetToMark
utils::hook::call(0x1403EDF1F, scr_get_function_stub);
}
};
}

View File

@ -0,0 +1,12 @@
#pragma once
namespace gsc
{
extern void* func_table[0x1000];
extern const game::dvar_t* developer_script;
void scr_error(const char* error);
void override_function(const std::string& name, game::BuiltinFunction func);
void add_function(const std::string& name, game::BuiltinFunction function);
}

View File

@ -10,6 +10,8 @@
#include "component/scripting.hpp"
#include "component/fastfiles.hpp"
#include "script_loading.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/gsc/interfaces/compiler.hpp>
#include <xsk/gsc/interfaces/decompiler.hpp>
@ -226,24 +228,6 @@ namespace gsc
}
}
game::ScriptFile* find_script(game::XAssetType /*type*/, const char* name, int /*allow_create_default*/)
{
std::string real_name = name;
const auto id = static_cast<std::uint16_t>(std::atoi(name));
if (id)
{
real_name = xsk::gsc::s1::resolver::token_name(id);
}
auto* script = load_custom_script(name, real_name);
if (script)
{
return script;
}
return game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, name, 1).scriptfile;
}
int db_is_x_asset_default(game::XAssetType type, const char* name)
{
if (loaded_scripts.contains(name))
@ -254,9 +238,9 @@ namespace gsc
return game::DB_IsXAssetDefault(type, name);
}
void gscr_load_game_type_script_stub()
void gscr_post_load_scripts_stub()
{
utils::hook::invoke<void>(0x140330490);
utils::hook::invoke<void>(0x140323F20);
clear();
@ -315,6 +299,24 @@ namespace gsc
}
}
game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default)
{
std::string real_name = name;
const auto id = static_cast<std::uint16_t>(std::atoi(name));
if (id)
{
real_name = xsk::gsc::s1::resolver::token_name(id);
}
auto* script = load_custom_script(name, real_name);
if (script)
{
return script;
}
return game::DB_FindXAssetHeader(type, name, allow_create_default).scriptfile;
}
class loading final : public component_interface
{
public:
@ -353,7 +355,7 @@ namespace gsc
utils::hook::call(0x1403F7327, db_is_x_asset_default);
// GScr_LoadScripts
utils::hook::call(0x140330B19, gscr_load_game_type_script_stub);
utils::hook::call(0x140330B97, gscr_post_load_scripts_stub);
// Load our scripts with an uncompressed stack
utils::hook::call(0x1403F7380, db_get_raw_buffer_stub);

View File

@ -0,0 +1,6 @@
#pragma once
namespace gsc
{
game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default);
}

View File

@ -1,6 +1,5 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/scripting/entity.hpp"
#include "game/scripting/execution.hpp"
@ -9,23 +8,32 @@
#include <utils/hook.hpp>
#include "logfile.hpp"
#include "scheduler.hpp"
#include "notifies.hpp"
#include "scripting.hpp"
namespace logfile
namespace notifies
{
std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
bool hook_enabled = true;
namespace
{
struct gsc_hook
{
bool is_lua_hook{};
const char* target_pos{};
sol::protected_function lua_function;
};
std::unordered_map<const char*, gsc_hook> vm_execute_hooks;
utils::hook::detour scr_player_killed_hook;
utils::hook::detour scr_player_damage_hook;
std::vector<sol::protected_function> player_killed_callbacks;
std::vector<sol::protected_function> player_damage_callbacks;
utils::hook::detour vm_execute_hook;
char empty_function[2] = {0x32, 0x34}; // CHECK_CLEAR_PARAMS, END
bool hook_enabled = true;
const char* target_function = nullptr;
sol::lua_value convert_entity(lua_State* state, const game::mp::gentity_s* ent)
{
@ -41,7 +49,7 @@ namespace logfile
std::string get_weapon_name(unsigned int weapon, bool isAlternate)
{
char output[1024] = { 0 };
char output[1024]{};
game::BG_GetWeaponNameComplete(weapon, isAlternate, output, 1024);
return output;
@ -68,13 +76,13 @@ namespace logfile
}
void scr_player_killed_stub(game::mp::gentity_s* self, const game::mp::gentity_s* inflictor, game::mp::gentity_s* attacker, int damage,
const int meansOfDeath, const unsigned int weapon, const bool isAlternate, const float* vDir, const unsigned int hitLoc, int psTimeOffset, int deathAnimDuration)
const int means_of_death, const unsigned int weapon, const bool is_alternate, const float* v_dir, const unsigned int hit_loc, int ps_time_offset, int death_anim_duration)
{
{
const std::string _hitLoc = reinterpret_cast<const char**>(0x1409B5400)[hitLoc];
const auto _mod = convert_mod(meansOfDeath);
const std::string _hit_loc = reinterpret_cast<const char**>(0x1409E62B0)[hit_loc];
const auto _mod = convert_mod(means_of_death);
const auto _weapon = get_weapon_name(weapon, isAlternate);
const auto _weapon = get_weapon_name(weapon, is_alternate);
for (const auto& callback : player_killed_callbacks)
{
@ -84,9 +92,9 @@ namespace logfile
const auto _inflictor = convert_entity(state, inflictor);
const auto _attacker = convert_entity(state, attacker);
const auto _vDir = convert_vector(state, vDir);
const auto _v_dir = convert_vector(state, v_dir);
const auto result = callback(_self, _inflictor, _attacker, damage, _mod, _weapon, _vDir, _hitLoc, psTimeOffset, deathAnimDuration);
const auto result = callback(_self, _inflictor, _attacker, damage, _mod, _weapon, _v_dir, _hit_loc, ps_time_offset, death_anim_duration);
scripting::lua::handle_error(result);
@ -102,17 +110,17 @@ namespace logfile
}
}
scr_player_killed_hook.invoke<void>(self, inflictor, attacker, damage, meansOfDeath, weapon, isAlternate, vDir, hitLoc, psTimeOffset, deathAnimDuration);
scr_player_killed_hook.invoke<void>(self, inflictor, attacker, damage, means_of_death, weapon, is_alternate, v_dir, hit_loc, ps_time_offset, death_anim_duration);
}
void scr_player_damage_stub(game::mp::gentity_s* self, const game::mp::gentity_s* inflictor, game::mp::gentity_s* attacker, int damage, int dflags,
const int meansOfDeath, const unsigned int weapon, const bool isAlternate, const float* vPoint, const float* vDir, const unsigned int hitLoc, const int timeOffset)
const int means_of_death, const unsigned int weapon, const bool is_alternate, const float* v_point, const float* v_dir, const unsigned int hit_loc, const int time_offset)
{
{
const std::string _hitLoc = reinterpret_cast<const char**>(0x1409B5400)[hitLoc];
const auto _mod = convert_mod(meansOfDeath);
const std::string _hit_loc = reinterpret_cast<const char**>(0x1409E62B0)[hit_loc];
const auto _mod = convert_mod(means_of_death);
const auto _weapon = get_weapon_name(weapon, isAlternate);
const auto _weapon = get_weapon_name(weapon, is_alternate);
for (const auto& callback : player_damage_callbacks)
{
@ -122,10 +130,10 @@ namespace logfile
const auto _inflictor = convert_entity(state, inflictor);
const auto _attacker = convert_entity(state, attacker);
const auto _vPoint = convert_vector(state, vPoint);
const auto _vDir = convert_vector(state, vDir);
const auto _v_point = convert_vector(state, v_point);
const auto _v_dir = convert_vector(state, v_dir);
const auto result = callback(_self, _inflictor, _attacker, damage, dflags, _mod, _weapon, _vPoint, _vDir, _hitLoc);
const auto result = callback(_self, _inflictor, _attacker, damage, dflags, _mod, _weapon, _v_point, _v_dir, _hit_loc);
scripting::lua::handle_error(result);
@ -141,15 +149,20 @@ namespace logfile
}
}
scr_player_damage_hook.invoke<void>(self, inflictor, attacker, damage, dflags, meansOfDeath, weapon, isAlternate, vPoint, vDir, hitLoc, timeOffset);
scr_player_damage_hook.invoke<void>(self, inflictor, attacker, damage, dflags, means_of_death, weapon, is_alternate, v_point, v_dir, hit_loc, time_offset);
}
void client_command_stub(const int clientNum)
void client_command_stub(const int client_num)
{
auto self = &game::mp::g_entities[clientNum];
char cmd[1024]{};
auto self = &game::mp::g_entities[client_num];
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
if (!self->client)
{
return;
}
char cmd[1024]{};
game::SV_Cmd_ArgvBuffer(0, cmd, sizeof(cmd));
if (cmd == "say"s || cmd == "say_team"s)
{
@ -175,18 +188,7 @@ namespace logfile
}
// ClientCommand
return reinterpret_cast<void(*)(int)>(0x1402E98F0)(clientNum);
}
void g_shutdown_game_stub(const int freeScripts)
{
{
const scripting::entity level{*game::levelEntityId};
scripting::notify(level, "shutdownGame_called", {1});
}
// G_ShutdownGame
return reinterpret_cast<void(*)(int)>(0x1402F8C10)(freeScripts);
utils::hook::invoke<void>(0x1402E98F0, client_num);
}
unsigned int local_id_to_entity(unsigned int local_id)
@ -197,7 +199,7 @@ namespace logfile
bool execute_vm_hook(const char* pos)
{
if (vm_execute_hooks.find(pos) == vm_execute_hooks.end())
if (!vm_execute_hooks.contains(pos))
{
hook_enabled = true;
return false;
@ -210,21 +212,28 @@ namespace logfile
}
const auto hook = vm_execute_hooks[pos];
const auto state = hook.lua_state();
const scripting::entity self = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
std::vector<sol::lua_value> args;
const auto top = game::scr_function_stack->top;
for (auto* value = top; value->type != game::SCRIPT_END; --value)
if (hook.is_lua_hook)
{
args.push_back(scripting::lua::convert(state, *value));
}
const auto& function = hook.lua_function;
const auto state = function.lua_state();
const auto result = hook(self, sol::as_args(args));
scripting::lua::handle_error(result);
const scripting::entity self = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
std::vector<sol::lua_value> args;
const auto top = game::scr_function_stack->top;
for (auto* value = top; value->type != game::SCRIPT_END; --value)
{
args.push_back(scripting::lua::convert(state, *value));
}
const auto result = function(self, sol::as_args(args));
scripting::lua::handle_error(result);
target_function = empty_function;
}
else
{
target_function = hook.target_pos;
}
return true;
}
@ -257,7 +266,8 @@ namespace logfile
a.bind(replace);
a.popad64();
a.mov(r14, reinterpret_cast<char*>(empty_function));
a.mov(rax, qword_ptr(reinterpret_cast<std::int64_t>(&target_function)));
a.mov(r14, rax);
a.jmp(end);
}
}
@ -289,6 +299,32 @@ namespace logfile
hook_enabled = false;
}
void set_lua_hook(const char* pos, const sol::protected_function& callback)
{
gsc_hook hook;
hook.is_lua_hook = true;
hook.lua_function = callback;
vm_execute_hooks[pos] = hook;
}
void set_gsc_hook(const char* source, const char* target)
{
gsc_hook hook;
hook.is_lua_hook = false;
hook.target_pos = target;
vm_execute_hooks[source] = hook;
}
void clear_hook(const char* pos)
{
vm_execute_hooks.erase(pos);
}
std::size_t get_hook_count()
{
return vm_execute_hooks.size();
}
class component final : public component_interface
{
public:
@ -304,12 +340,20 @@ namespace logfile
scr_player_damage_hook.create(0x140332150, scr_player_damage_stub);
scr_player_killed_hook.create(0x1403323D0, scr_player_killed_stub);
utils::hook::call(0x14043E550, g_shutdown_game_stub);
utils::hook::call(0x14043EA11, g_shutdown_game_stub);
utils::hook::jump(0x1403FA134, utils::hook::assemble(vm_execute_stub), true);
scripting::on_shutdown([](bool free_scripts)
{
if (free_scripts)
{
vm_execute_hooks.clear();
}
const scripting::entity level{*game::levelEntityId};
scripting::notify(level, "shutdownGame_called", {free_scripts});
});
}
};
}
REGISTER_COMPONENT(logfile::component)
REGISTER_COMPONENT(notifies::component)

View File

@ -1,8 +1,13 @@
#pragma once
namespace logfile
namespace notifies
{
extern std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
extern bool hook_enabled;
void set_lua_hook(const char* pos, const sol::protected_function&);
void set_gsc_hook(const char* source, const char* target);
void clear_hook(const char* pos);
std::size_t get_hook_count();
void add_player_damage_callback(const sol::protected_function& callback);
void add_player_killed_callback(const sol::protected_function& callback);

View File

@ -1,8 +1,6 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
#include "game/scripting/entity.hpp"
#include "game/scripting/functions.hpp"
@ -13,9 +11,17 @@
#include "scheduler.hpp"
#include "scripting.hpp"
#include "gsc/script_loading.hpp"
#include <utils/hook.hpp>
namespace scripting
{
std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
std::string current_file;
namespace
{
@ -25,10 +31,15 @@ namespace scripting
utils::hook::detour scr_set_thread_position_hook;
utils::hook::detour process_script_hook;
utils::hook::detour sl_get_canonical_string_hook;
std::string current_file;
std::string current_script_file;
std::uint32_t current_file_id = 0;
std::unordered_map<unsigned int, std::string> canonical_string_table;
std::vector<std::function<void(int)>> shutdown_callbacks;
std::vector<std::function<void()>> init_callbacks;
void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value,
game::VariableValue* top)
@ -49,7 +60,7 @@ namespace scripting
if (e.name == "connected")
{
scripting::clear_entity_fields(e.entity);
clear_entity_fields(e.entity);
}
lua::engine::notify(e);
@ -65,6 +76,11 @@ namespace scripting
if (!game::VirtualLobby_Loaded())
{
lua::engine::start();
for (const auto& callback : init_callbacks)
{
callback();
}
}
}
@ -72,6 +88,13 @@ namespace scripting
{
lua::engine::stop();
if (free_scripts)
{
script_function_table_sort.clear();
script_function_table.clear();
canonical_string_table.clear();
}
for (const auto& callback : shutdown_callbacks)
{
callback(free_scripts);
@ -82,25 +105,85 @@ namespace scripting
void process_script_stub(const char* filename)
{
current_script_file = filename;
const auto file_id = atoi(filename);
if (file_id)
{
current_file = scripting::find_token(file_id);
current_file_id = static_cast<std::uint16_t>(file_id);
}
else
{
current_file_id = 0;
current_file = filename;
}
process_script_hook.invoke<void>(filename);
}
void scr_set_thread_position_stub(unsigned int threadName, const char* codePos)
void add_function_sort(unsigned int id, const char* pos)
{
const auto function_name = scripting::find_token(threadName);
script_function_table[current_file][function_name] = codePos;
scr_set_thread_position_hook.invoke<void>(threadName, codePos);
std::string filename = current_file;
if (current_file_id)
{
filename = get_token(current_file_id);
}
if (!script_function_table_sort.contains(filename))
{
const auto script = gsc::find_script(game::ASSET_TYPE_SCRIPTFILE, current_script_file.data(), false);
if (script)
{
const auto* end = &script->bytecode[script->bytecodeLen];
script_function_table_sort[filename].emplace_back("__end__", reinterpret_cast<const char*>(end));
}
}
const auto name = scripting::get_token(id);
auto& itr = script_function_table_sort[filename];
itr.insert(itr.end() - 1, {name, pos});
}
void add_function(const std::string& file, unsigned int id, const char* pos)
{
const auto name = get_token(id);
script_function_table[file][name] = pos;
script_function_table_rev[pos] = {file, name};
}
void scr_set_thread_position_stub(unsigned int thread_name, const char* code_pos)
{
add_function_sort(thread_name, code_pos);
if (current_file_id)
{
const auto name = get_token(current_file_id);
add_function(name, thread_name, code_pos);
}
else
{
add_function(current_file, thread_name, code_pos);
}
scr_set_thread_position_hook.invoke<void>(thread_name, code_pos);
}
unsigned int sl_get_canonical_string_stub(const char* str)
{
const auto result = sl_get_canonical_string_hook.invoke<unsigned int>(str);
canonical_string_table[result] = str;
return result;
}
}
std::string get_token(unsigned int id)
{
if (const auto itr = canonical_string_table.find(id); itr != canonical_string_table.end())
{
return itr->second;
}
return find_token(id);
}
void on_shutdown(const std::function<void(int)>& callback)
@ -108,20 +191,36 @@ namespace scripting
shutdown_callbacks.push_back(callback);
}
void on_init(const std::function<void()>& callback)
{
init_callbacks.push_back(callback);
}
std::optional<std::string> get_canonical_string(const unsigned int id)
{
if (const auto itr = canonical_string_table.find(id); itr != canonical_string_table.end())
{
return {itr->second};
}
return {};
}
class component final : public component_interface
{
public:
void post_unpack() override
{
vm_notify_hook.create(SELECT_VALUE(0x140320E50, 0x1403FD5B0), vm_notify_stub);
vm_notify_hook.create(SELECT_VALUE(0x140320E50, 0x1403FD5B0), &vm_notify_stub);
// SP address is wrong, but should be ok
scr_load_level_hook.create(SELECT_VALUE(0x140005260, 0x140325B90), scr_load_level_stub);
g_shutdown_game_hook.create(SELECT_VALUE(0x140228BA0, 0x1402F8C10), g_shutdown_game_stub);
scr_load_level_hook.create(SELECT_VALUE(0x140005260, 0x140325B90), &scr_load_level_stub);
g_shutdown_game_hook.create(SELECT_VALUE(0x140228BA0, 0x1402F8C10), &g_shutdown_game_stub);
scr_set_thread_position_hook.create(SELECT_VALUE(0x1403115E0, 0x1403EDB10), scr_set_thread_position_stub);
process_script_hook.create(SELECT_VALUE(0x14031AB30, 0x1403F7300), process_script_stub);
scr_set_thread_position_hook.create(SELECT_VALUE(0x1403115E0, 0x1403EDB10), &scr_set_thread_position_stub);
process_script_hook.create(SELECT_VALUE(0x14031AB30, 0x1403F7300), &process_script_stub);
sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, &sl_get_canonical_string_stub);
scheduler::loop([]()
scheduler::loop([]
{
lua::engine::run_frame();
}, scheduler::pipeline::server);

View File

@ -3,6 +3,14 @@
namespace scripting
{
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
extern std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
extern std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
extern std::string current_file;
void on_shutdown(const std::function<void(int)>& callback);
void on_init(const std::function<void()>& callback);
std::optional<std::string> get_canonical_string(unsigned int id);
std::string get_token(unsigned int id);
}

View File

@ -25,6 +25,7 @@ namespace dvars
game::dvar_t* g_gravity = nullptr;
game::dvar_t* g_speed = nullptr;
game::dvar_t* g_elevators = nullptr;
game::dvar_t* g_log = nullptr;
game::dvar_t* jump_height = nullptr;
game::dvar_t* jump_ladderPushVel = nullptr;

View File

@ -24,6 +24,7 @@ namespace dvars
extern game::dvar_t* g_gravity;
extern game::dvar_t* g_speed;
extern game::dvar_t* g_elevators;
extern game::dvar_t* g_log;
extern game::dvar_t* jump_height;
extern game::dvar_t* jump_ladderPushVel;

File diff suppressed because it is too large Load Diff

View File

@ -3,97 +3,89 @@
#include <utils/string.hpp>
#include "component/gsc/script_extension.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
namespace scripting
{
namespace
{
std::unordered_map<std::string, unsigned> lowercase_map(
const std::unordered_map<std::string, unsigned>& old_map)
{
std::unordered_map<std::string, unsigned> new_map{};
for (auto& entry : old_map)
{
new_map[utils::string::to_lower(entry.first)] = entry.second;
}
return new_map;
}
const std::unordered_map<std::string, unsigned>& get_methods()
{
static auto methods = lowercase_map(method_map);
return methods;
}
const std::unordered_map<std::string, unsigned>& get_functions()
{
static auto function = lowercase_map(function_map);
return function;
}
int find_function_index(const std::string& name, const bool prefer_global)
{
const auto target = utils::string::to_lower(name);
const auto& primary_map = prefer_global
? get_functions()
: get_methods();
const auto& secondary_map = !prefer_global
? get_functions()
: get_methods();
auto function_entry = primary_map.find(target);
if (function_entry != primary_map.end())
auto first = xsk::gsc::s1::resolver::function_id;
auto second = xsk::gsc::s1::resolver::method_id;
if (!prefer_global)
{
return function_entry->second;
std::swap(first, second);
}
function_entry = secondary_map.find(target);
if (function_entry != secondary_map.end())
const auto first_res = first(target);
if (first_res)
{
return function_entry->second;
return first_res;
}
const auto second_res = second(target);
if (second_res)
{
return second_res;
}
return -1;
}
script_function get_function_by_index(const unsigned index)
{
static const auto function_table = SELECT_VALUE(0x149668F50, 0x147DD1850);
static const auto method_table = SELECT_VALUE(0x14966A670, 0x147DD2F50);
if (index < 0x2DF)
{
return reinterpret_cast<script_function*>(function_table)[index];
}
return reinterpret_cast<script_function*>(method_table)[index - 0x8000];
}
}
std::string find_token(unsigned int id)
std::uint32_t parse_token_id(const std::string& name)
{
for (const auto& token : token_map)
if (name.starts_with("_ID"))
{
if (token.second == id)
{
return token.first;
}
return static_cast<std::uint32_t>(std::strtol(name.substr(3).data(), nullptr, 10));
}
return utils::string::va("_ID%i", id);
return 0;
}
std::string find_token(std::uint32_t id)
{
return xsk::gsc::s1::resolver::token_name(static_cast<std::uint16_t>(id));
}
std::string find_token_single(std::uint32_t id)
{
return xsk::gsc::s1::resolver::token_name(static_cast<std::uint16_t>(id));
}
unsigned int find_token_id(const std::string& name)
{
const auto result = token_map.find(name);
if (result != token_map.end())
const auto id = xsk::gsc::s1::resolver::token_id(name);
if (id)
{
return result->second;
return id;
}
return 0;
const auto parsed_id = parse_token_id(name);
if (parsed_id)
{
return parsed_id;
}
return game::SL_GetCanonicalString(name.data());
}
script_function get_function_by_index(const std::uint32_t index)
{
static const auto function_table = &gsc::func_table;
static const auto method_table = SELECT_VALUE(0x14966A670, 0x147DD2F50);
if (index < 0x1000)
{
return reinterpret_cast<script_function*>(function_table)[index - 1];
}
return reinterpret_cast<script_function*>(method_table)[index - 0x8000];
}
script_function find_function(const std::string& name, const bool prefer_global)

View File

@ -3,14 +3,12 @@
namespace scripting
{
extern std::unordered_map<std::string, unsigned> method_map;
extern std::unordered_map<std::string, unsigned> function_map;
extern std::unordered_map<std::string, unsigned> token_map;
using script_function = void(*)(game::scr_entref_t);
std::string find_token(unsigned int id);
std::string find_token(std::uint32_t id);
std::string find_token_single(std::uint32_t id);
unsigned int find_token_id(const std::string& name);
script_function find_function(const std::string& name, const bool prefer_global);
script_function get_function_by_index(std::uint32_t index);
script_function find_function(const std::string& name, bool prefer_global);
}

View File

@ -3,15 +3,17 @@
#include "error.hpp"
#include "value_conversion.hpp"
#include "../execution.hpp"
#include "../functions.hpp"
#include "game/scripting/execution.hpp"
#include "../../../component/command.hpp"
#include "../../../component/logfile.hpp"
#include "../../../component/scripting.hpp"
#include "component/command.hpp"
#include "component/notifies.hpp"
#include "component/scripting.hpp"
#include <utils/string.hpp>
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
namespace scripting::lua
{
namespace
@ -244,10 +246,10 @@ namespace scripting::lua
auto entity_type = state.new_usertype<entity>("entity");
for (const auto& func : method_map)
for (const auto& func : xsk::gsc::s1::resolver::get_methods())
{
const auto name = utils::string::to_lower(func.first);
entity_type[name.data()] = [name](const entity& entity, const sol::this_state s, sol::variadic_args va)
const auto name = std::string(func.first);
entity_type[name] = [name](const entity& entity, const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
@ -376,9 +378,9 @@ namespace scripting::lua
auto game_type = state.new_usertype<game>("game_");
state["game"] = game();
for (const auto& func : function_map)
for (const auto& func : xsk::gsc::s1::resolver::get_functions())
{
const auto name = utils::string::to_lower(func.first);
const auto name = std::string(func.first);
game_type[name] = [name](const game&, const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
@ -424,12 +426,12 @@ namespace scripting::lua
game_type["onplayerdamage"] = [](const game&, const sol::protected_function& callback)
{
logfile::add_player_damage_callback(callback);
notifies::add_player_damage_callback(callback);
};
game_type["onplayerkilled"] = [](const game&, const sol::protected_function& callback)
{
logfile::add_player_killed_callback(callback);
notifies::add_player_killed_callback(callback);
};
game_type["getgamevar"] = [](const sol::this_state s)
@ -455,43 +457,40 @@ namespace scripting::lua
for (const auto& function : scripting::script_function_table[filename])
{
functions[function.first] = sol::overload(
[filename, function](const entity& entity, const sol::this_state s, sol::variadic_args va)
functions[function.first] = sol::overload([filename, function](const entity& entity, const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
gsl::finally(&logfile::enable_vm_execute_hook);
logfile::disable_vm_execute_hook();
return convert(s, call_script_function(entity, filename, function.first, arguments));
},
[filename, function](const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
gsl::finally(&logfile::enable_vm_execute_hook);
logfile::disable_vm_execute_hook();
return convert(s, call_script_function(*::game::levelEntityId, filename, function.first, arguments));
arguments.push_back(convert({s, arg}));
}
);
const auto _0 = gsl::finally(&notifies::enable_vm_execute_hook);
notifies::disable_vm_execute_hook();
return convert(s, call_script_function(entity, filename, function.first, arguments));
},
[filename, function](const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
const auto _0 = gsl::finally(&notifies::enable_vm_execute_hook);
notifies::disable_vm_execute_hook();
return convert(s, call_script_function(*::game::levelEntityId, filename, function.first, arguments));
});
}
return functions;
};
game_type["scriptcall"] = [](const game&, const sol::this_state s, const std::string& filename,
const std::string function, sol::variadic_args va)
game_type["scriptcall"] = [](const game&, const sol::this_state s, const std::string& filename, const std::string function, sol::variadic_args va)
{
std::vector<script_value> arguments{};
@ -500,8 +499,8 @@ namespace scripting::lua
arguments.push_back(convert({s, arg}));
}
gsl::finally(&logfile::enable_vm_execute_hook);
logfile::disable_vm_execute_hook();
const auto _0 = gsl::finally(&notifies::enable_vm_execute_hook);
notifies::disable_vm_execute_hook();
return convert(s, call_script_function(*::game::levelEntityId, filename, function, arguments));
};
@ -510,50 +509,48 @@ namespace scripting::lua
const std::string function_name, const sol::protected_function& function)
{
const auto pos = get_function_pos(filename, function_name);
logfile::vm_execute_hooks[pos] = function;
notifies::set_lua_hook(pos, function);
auto detour = sol::table::create(function.lua_state());
detour["disable"] = [pos]()
detour["disable"] = [pos]
{
logfile::vm_execute_hooks.erase(pos);
notifies::clear_hook(pos);
};
detour["enable"] = [pos, function]()
detour["enable"] = [pos, function]
{
logfile::vm_execute_hooks[pos] = function;
notifies::set_lua_hook(pos, function);
};
detour["invoke"] = sol::overload(
[filename, function_name](const entity& entity, const sol::this_state s, sol::variadic_args va)
detour["invoke"] = sol::overload([filename, function_name](const entity& entity, const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
gsl::finally(&logfile::enable_vm_execute_hook);
logfile::disable_vm_execute_hook();
return convert(s, call_script_function(entity, filename, function_name, arguments));
},
[filename, function_name](const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
gsl::finally(&logfile::enable_vm_execute_hook);
logfile::disable_vm_execute_hook();
return convert(s, call_script_function(*::game::levelEntityId, filename, function_name, arguments));
arguments.push_back(convert({s, arg}));
}
);
const auto _0 = gsl::finally(&notifies::enable_vm_execute_hook);
notifies::disable_vm_execute_hook();
return convert(s, call_script_function(entity, filename, function_name, arguments));
},
[filename, function_name](const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
const auto _0 = gsl::finally(&notifies::enable_vm_execute_hook);
notifies::disable_vm_execute_hook();
return convert(s, call_script_function(*::game::levelEntityId, filename, function_name, arguments));
});
return detour;
};

View File

@ -1,10 +1,11 @@
#include <std_include.hpp>
#include "engine.hpp"
#include "context.hpp"
#include "game/scripting/execution.hpp"
#include "../execution.hpp"
#include "../../../component/logfile.hpp"
#include "../../../component/game_module.hpp"
#include "component/notifies.hpp"
#include "component/game_module.hpp"
#include <utils/io.hpp>
@ -39,7 +40,7 @@ namespace scripting::lua::engine
void stop()
{
logfile::clear_callbacks();
notifies::clear_callbacks();
get_scripts().clear();
}

View File

@ -1,8 +1,9 @@
#include <std_include.hpp>
#include "value_conversion.hpp"
#include "../functions.hpp"
#include "../execution.hpp"
#include ".../../component/logfile.hpp"
#include "game/scripting/functions.hpp"
#include "game/scripting/execution.hpp"
#include "component/notifies.hpp"
namespace scripting::lua
{
@ -120,9 +121,8 @@ namespace scripting::lua
game::VariableValue convert_function(sol::lua_value value)
{
const auto function = value.as<sol::protected_function>();
const auto index = reinterpret_cast<char*>(logfile::vm_execute_hooks.size());
logfile::vm_execute_hooks[index] = function;
const auto index = reinterpret_cast<char*>(notifies::get_hook_count() + 1);
notifies::set_lua_hook(index, function);
game::VariableValue func;
func.type = game::SCRIPT_FUNCTION;
@ -133,30 +133,28 @@ namespace scripting::lua
sol::lua_value convert_function(lua_State* state, const char* pos)
{
return sol::overload(
[pos](const entity& entity, const sol::this_state s, sol::variadic_args va)
return sol::overload([pos](const entity& entity, const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
return convert(s, exec_ent_thread(entity, pos, arguments));
},
[pos](const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
return convert(s, exec_ent_thread(*game::levelEntityId, pos, arguments));
arguments.push_back(convert({s, arg}));
}
);
return convert(s, exec_ent_thread(entity, pos, arguments));
},
[pos](const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
return convert(s, exec_ent_thread(*game::levelEntityId, pos, arguments));
});
}
}
@ -167,8 +165,7 @@ namespace scripting::lua
const auto offset = 64000 * (parent_id & 3);
metatable[sol::meta_function::new_index] = [offset, parent_id](const sol::table t, const sol::this_state s,
const sol::lua_value& field, const sol::lua_value& value)
metatable[sol::meta_function::new_index] = [offset, parent_id](const sol::table t, const sol::this_state s, const sol::lua_value& field, const sol::lua_value& value)
{
const auto id = field.is<std::string>()
? scripting::find_token_id(field.as<std::string>())
@ -190,8 +187,7 @@ namespace scripting::lua
variable->u.u = new_variable.u;
};
metatable[sol::meta_function::index] = [offset, parent_id](const sol::table t, const sol::this_state s,
const sol::lua_value& field)
metatable[sol::meta_function::index] = [offset, parent_id](const sol::table t, const sol::this_state s, const sol::lua_value& field)
{
const auto id = field.is<std::string>()
? scripting::find_token_id(field.as<std::string>())

View File

@ -22,6 +22,9 @@ namespace game
unsigned short classnum;
};
typedef void(*BuiltinMethod)(scr_entref_t);
typedef void(*BuiltinFunction)();
enum scriptType_e
{
SCRIPT_NONE = 0,

View File

@ -36,8 +36,7 @@ namespace game
WEAK symbol<void(int localClientNum, void (*)(int localClientNum))> Cbuf_AddCall{0x1402ED820, 0x1403AECF0};
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x1402ED890, 0x1403AED70};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer,
void (int, int, const char*))> Cbuf_ExecuteBufferInternal{0x1402ED9A0, 0x1403AEE80};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer, void (int, int, const char*))> Cbuf_ExecuteBufferInternal{0x1402ED9A0, 0x1403AEE80};
WEAK symbol<bool()> CL_IsCgameInitialized{0x140136560, 0x1401FD510};
WEAK symbol<void(int localClientNum, const char* string)> CL_ForwardCommandToServer{0x0, 0x14020B310};
@ -66,9 +65,7 @@ namespace game
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0x140372950, 0x1404C1DB0};
WEAK symbol<void(const char* dvar, const char* buffer)> Dvar_SetCommand{0x1403730D0, 0x1404C2520};
WEAK symbol<void(dvar_t* dvar, const char* string)> Dvar_SetString{0x140373DE0, 0x1404C3610};
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{
0x1403737D0, 0x1404C2E40
};
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x1403737D0, 0x1404C2E40};
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x140374E10, 0x1404C47B0};
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned int flags, const char* description)>
@ -100,9 +97,7 @@ namespace game
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403165D0, 0x1403F2DC0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x1403166D0, 0x1403F2CC0};
WEAK symbol<scr_string_t(unsigned int parentId, unsigned int id)> GetVariableName{0x1403170E0, 0x1403F37F0};
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{
0x14031AAD0, 0x1403F72A0
};
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x14031AAD0, 0x1403F72A0};
WEAK symbol<unsigned int(unsigned int)> GetObjectType{0x140316F70, 0x1403F3670};
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x0, 0x1403F3730};
@ -110,9 +105,7 @@ namespace game
WEAK symbol<int(int clientNum)> G_GetClientScore{0, 0x1402F6AB0};
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x140274590, 0x14033FF60};
WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield, int startInAltMode, int, int, int, char,
...)>
G_GivePlayerWeapon{0x1402749B0, 0x140340470};
WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield, int startInAltMode, int, int, int, char)> G_GivePlayerWeapon{0x1402749B0, 0x140340470};
WEAK symbol<void(playerState_s* ps, unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x1402217F0, 0x1402F22B0};
WEAK symbol<void(int clientNum, unsigned int weapon)> G_SelectWeapon{0x140275380, 0x140340D50};
WEAK symbol<int(playerState_s* ps, unsigned int weapon)> G_TakePlayerWeapon{0x1402754E0, 0x1403411D0};
@ -167,10 +160,12 @@ namespace game
WEAK symbol<unsigned int(void* func, int type, unsigned int name)> Scr_RegisterFunction{0x1403115B0, 0x1403EDAE0};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x0, 0x1403F9E40};
WEAK symbol<void()> Scr_ErrorInternal{0x0, 0x1403F80A0};
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x140314850, 0x1403F0F10};
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x140314AF0, 0x1403F11C0};
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x140314D90, 0x1403F1440};
WEAK symbol<unsigned int(char const* str)> SL_GetCanonicalString{0x140311770, 0x1403EDCA0};
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x1402EEFD0, 0x1403B05C0};
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0, 0x1403B0640};
@ -253,6 +248,8 @@ namespace game
WEAK symbol<SOCKET> query_socket{0, 0x14B5B9180};
WEAK symbol<int> level_time{0x0, 0x144959C2C};
WEAK symbol<void*> DB_XAssetPool{0x140804690, 0x1409B40D0};
WEAK symbol<unsigned int> db_hashTable{0x142C3E050, 0x143716B10};
WEAK symbol<XAssetEntry> g_assetEntryPool{0x142CC2400, 0x14379F100};

View File

@ -1,10 +1,7 @@
#include <std_include.hpp>
#include "execution.hpp"
#include "component/ui_scripting.hpp"
#include "component/console.hpp"
#include <utils/string.hpp>
namespace ui_scripting
{
namespace

View File

@ -2,7 +2,6 @@
#include "execution.hpp"
#include "types.hpp"
#include "script_value.hpp"
#include "../../component/ui_scripting.hpp"
namespace ui_scripting
{

View File

@ -55,6 +55,7 @@
#undef min
#endif
#include <ranges>
#include <map>
#include <atomic>
#include <vector>
@ -72,6 +73,7 @@
#include <optional>
#include <unordered_set>
#include <variant>
#include <format>
#include <gsl/gsl>
#include <udis86.h>