mirror of
https://github.com/XLabsProject/s1x-client.git
synced 2023-08-02 15:02:12 +02:00
commit
319b250d79
2
deps/gsc-tool
vendored
2
deps/gsc-tool
vendored
@ -1 +1 @@
|
||||
Subproject commit 2ab5118d168a1889b3b59d3d8996509046e2642b
|
||||
Subproject commit 842f168a67878f18ea6a42b9d3ce56ff9fafff0d
|
@ -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,9 +33,9 @@ namespace command
|
||||
params params = {};
|
||||
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
if (handlers.find(command) != handlers.end())
|
||||
if (const auto itr = handlers.find(command); itr != handlers.end())
|
||||
{
|
||||
handlers[command](params);
|
||||
itr->second(params);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,9 +50,9 @@ namespace command
|
||||
params_sv params = {};
|
||||
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
if (const auto got = handlers_sv.find(command); got != handlers_sv.end())
|
||||
if (const auto itr = handlers_sv.find(command); itr != handlers_sv.end())
|
||||
{
|
||||
got->second(&game::mp::g_entities[client_num], params);
|
||||
itr->second(&game::mp::g_entities[client_num], params);
|
||||
}
|
||||
|
||||
client_command_hook.invoke<void>(client_num);
|
||||
@ -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,8 +205,10 @@ 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;
|
||||
}
|
||||
@ -215,15 +221,17 @@ namespace command
|
||||
});
|
||||
}
|
||||
|
||||
void add_sv(const char* name, std::function<void(game::mp::gentity_s*, const params_sv&)> callback)
|
||||
void add_sv(const char* name, const std::function<void(game::mp::gentity_s*, const params_sv&)>& callback)
|
||||
{
|
||||
// doing this so the sv command would show up in the console
|
||||
add_raw(name, nullptr);
|
||||
|
||||
const auto command = utils::string::to_lower(name);
|
||||
|
||||
if (handlers_sv.find(command) == handlers_sv.end())
|
||||
handlers_sv[command] = std::move(callback);
|
||||
if (!handlers_sv.contains(command))
|
||||
{
|
||||
handlers_sv[command] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
bool cheats_ok(const game::mp::gentity_s* ent)
|
||||
|
@ -44,7 +44,7 @@ namespace command
|
||||
void add(const char* name, const std::function<void(const params&)>& callback);
|
||||
void add(const char* name, const std::function<void()>& callback);
|
||||
|
||||
void add_sv(const char* name, std::function<void(game::mp::gentity_s*, const params_sv&)> callback);
|
||||
void add_sv(const char* name, const std::function<void(game::mp::gentity_s*, const params_sv&)>& callback);
|
||||
|
||||
void execute(std::string command, bool sync = false);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
116
src/client/component/game_log.cpp
Normal file
116
src/client/component/game_log.cpp
Normal 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)
|
6
src/client/component/game_log.hpp
Normal file
6
src/client/component/game_log.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace game_log
|
||||
{
|
||||
void g_log_printf(const char* fmt, ...);
|
||||
}
|
148
src/client/component/gsc/script_error.cpp
Normal file
148
src/client/component/gsc/script_error.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
#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>
|
||||
#include <utils/string.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;
|
||||
}
|
||||
|
||||
unsigned int scr_get_const_string(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::scr_VmPub->top - index;
|
||||
if (game::Scr_CastString(value))
|
||||
{
|
||||
assert(value->type == game::SCRIPT_STRING);
|
||||
return value->u.stringValue;
|
||||
}
|
||||
|
||||
game::Scr_ErrorInternal();
|
||||
}
|
||||
|
||||
scr_error(utils::string::va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// Restore basic error messages to scr functions
|
||||
utils::hook::jump(0x1403F8510, scr_get_const_string);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
{
|
||||
scr_emit_function_hook.clear();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(gsc::error)
|
6
src/client/component/gsc/script_error.hpp
Normal file
6
src/client/component/gsc/script_error.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
std::optional<std::pair<std::string, std::string>> find_function(const char* pos);
|
||||
}
|
@ -1,34 +1,182 @@
|
||||
#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 "component/command.hpp"
|
||||
|
||||
#include "script_error.hpp"
|
||||
#include "script_extension.hpp"
|
||||
|
||||
#include <utils/string.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)
|
||||
{
|
||||
if (const auto got = builtin_funcs_overrides.find(name); got != builtin_funcs_overrides.end())
|
||||
if (const auto itr = builtin_funcs_overrides.find(name); itr != builtin_funcs_overrides.end())
|
||||
{
|
||||
func = got->second;
|
||||
func = itr->second;
|
||||
}
|
||||
|
||||
scr_register_function_hook.invoke<void>(func, type, name);
|
||||
@ -51,6 +199,48 @@ namespace gsc
|
||||
|
||||
console::info("\n");
|
||||
}
|
||||
|
||||
void assert_cmd()
|
||||
{
|
||||
if (!game::Scr_GetInt(0))
|
||||
{
|
||||
scr_error("Assert fail");
|
||||
}
|
||||
}
|
||||
|
||||
void assert_ex_cmd()
|
||||
{
|
||||
if (!game::Scr_GetInt(0))
|
||||
{
|
||||
scr_error(utils::string::va("Assert fail: %s", game::Scr_GetString(1)));
|
||||
}
|
||||
}
|
||||
|
||||
void assert_msg_cmd()
|
||||
{
|
||||
scr_error(utils::string::va("Assert fail: %s", game::Scr_GetString(0)));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -62,6 +252,36 @@ 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);
|
||||
|
||||
override_function("assert", &assert_cmd);
|
||||
override_function("assertex", &assert_ex_cmd);
|
||||
override_function("assertmsg", &assert_ex_cmd);
|
||||
|
||||
add_function("executecommand", []
|
||||
{
|
||||
const auto* cmd = game::Scr_GetString(0);
|
||||
command::execute(cmd);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
12
src/client/component/gsc/script_extension.hpp
Normal file
12
src/client/component/gsc/script_extension.hpp
Normal 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);
|
||||
}
|
@ -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>
|
||||
@ -79,9 +81,9 @@ namespace gsc
|
||||
|
||||
game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name)
|
||||
{
|
||||
if (const auto got = loaded_scripts.find(real_name); got != loaded_scripts.end())
|
||||
if (const auto itr = loaded_scripts.find(real_name); itr != loaded_scripts.end())
|
||||
{
|
||||
return got->second;
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
std::string source_buffer{};
|
||||
@ -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);
|
||||
|
6
src/client/component/gsc/script_loading.hpp
Normal file
6
src/client/component/gsc/script_loading.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default);
|
||||
}
|
@ -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)
|
@ -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);
|
@ -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,13 +31,17 @@ 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)
|
||||
void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value, game::VariableValue* top)
|
||||
{
|
||||
if (!game::VirtualLobby_Loaded())
|
||||
{
|
||||
@ -49,7 +59,7 @@ namespace scripting
|
||||
|
||||
if (e.name == "connected")
|
||||
{
|
||||
scripting::clear_entity_fields(e.entity);
|
||||
clear_entity_fields(e.entity);
|
||||
}
|
||||
|
||||
lua::engine::notify(e);
|
||||
@ -65,6 +75,11 @@ namespace scripting
|
||||
if (!game::VirtualLobby_Loaded())
|
||||
{
|
||||
lua::engine::start();
|
||||
|
||||
for (const auto& callback : init_callbacks)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +87,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 +104,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 +190,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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(¬ifies::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(¬ifies::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(¬ifies::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(¬ifies::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(¬ifies::enable_vm_execute_hook);
|
||||
notifies::disable_vm_execute_hook();
|
||||
|
||||
return convert(s, call_script_function(*::game::levelEntityId, filename, function_name, arguments));
|
||||
});
|
||||
|
||||
return detour;
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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>())
|
||||
|
@ -22,6 +22,9 @@ namespace game
|
||||
unsigned short classnum;
|
||||
};
|
||||
|
||||
typedef void(*BuiltinMethod)(scr_entref_t);
|
||||
typedef void(*BuiltinFunction)();
|
||||
|
||||
enum scriptType_e
|
||||
{
|
||||
SCRIPT_NONE = 0,
|
||||
|
@ -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};
|
||||
@ -160,6 +153,7 @@ namespace game
|
||||
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x14031A0D0, 0x1403F68A0};
|
||||
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x14026B620, 0x140339450};
|
||||
WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x14031CB80, 0x1403F92D0};
|
||||
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x0, 0x1403F4500};
|
||||
|
||||
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x0, 0x1403F8120};
|
||||
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x0, 0x1403EE250};
|
||||
@ -167,10 +161,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 +249,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};
|
||||
|
@ -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
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "execution.hpp"
|
||||
#include "types.hpp"
|
||||
#include "script_value.hpp"
|
||||
#include "../../component/ui_scripting.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user