Merge pull request #504 from XLabsProject/develop

Release v2.0.8
This commit is contained in:
Maurice Heumann 2022-11-01 11:39:59 +01:00 committed by GitHub
commit 34c4f99c4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1069 additions and 1963 deletions

View File

@ -35,10 +35,9 @@ jobs:
lfs: false
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1
uses: microsoft/setup-msbuild@v1.1.3
- name: Generate project files
#run: tools/premake5 vs2022 --ci-build
run: tools/premake5 vs2022
- name: Set up problem matching
@ -48,7 +47,7 @@ jobs:
run: msbuild /m /v:minimal /p:Configuration=${{matrix.configuration}} /p:Platform=x64 build/s1x.sln
- name: Upload ${{matrix.configuration}} binaries
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3.1.0
with:
name: ${{matrix.configuration}} binaries
path: |
@ -56,7 +55,7 @@ jobs:
build/bin/x64/${{matrix.configuration}}/s1x.pdb
- name: Upload ${{matrix.configuration}} data artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3.1.0
with:
name: ${{matrix.configuration}} data artifacts
path: |
@ -77,12 +76,12 @@ jobs:
run: echo "XLABS_MASTER_PATH=${{ secrets.XLABS_MASTER_SSH_PATH_DEV }}" >> $GITHUB_ENV
- name: Download Release binaries
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: Release binaries
- name: Download Release data artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: Release data artifacts
path: data

1
.gitmodules vendored
View File

@ -47,3 +47,4 @@
[submodule "deps/gsc-tool"]
path = deps/gsc-tool
url = https://github.com/xensik/gsc-tool.git
branch = xlabs

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit 991fa6682e819590c695f00c6b880548e55fa914
Subproject commit 9c4212aca46fb4fe5bda2921c2269d35d6d1860f

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit 15a603661871b86c048e697f0e6cd17374dcecc0
Subproject commit 8f2c237b8315a7d662e0e67d06807296a7bbe5ab

2
deps/gsc-tool vendored

@ -1 +1 @@
Subproject commit e5d6196da194457ba01cc94674a83da802b769ca
Subproject commit 842f168a67878f18ea6a42b9d3ce56ff9fafff0d

2
deps/libtommath vendored

@ -1 +1 @@
Subproject commit 96f9edf9aaf145c6ecf5225eea3f5e86a0f26935
Subproject commit 03de03dee753442d4b23166982514639c4ccbc39

2
deps/lua vendored

@ -1 +1 @@
Subproject commit 26be27459b11feabed52cf40aaa76f86c7edc977
Subproject commit c954db39241a8b21d7b32b42b87a066b4708f969

View File

@ -6,6 +6,7 @@
#include "auth.hpp"
#include "command.hpp"
#include "network.hpp"
#include "console.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -219,9 +220,9 @@ namespace auth
utils::hook::call(0x140208C54, send_connect_data_stub);
}
command::add("guid", []()
command::add("guid", []
{
printf("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits);
console::info("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits);
});
}
};

View File

@ -4,6 +4,7 @@
#include "game/scripting/execution.hpp"
#include "command.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include "party.hpp"
#include "network.hpp"
@ -93,7 +94,7 @@ namespace bots
game::netadr_s master{};
if (server_list::get_master_server(master))
{
printf("Getting bots...\n");
console::info("Getting bots...\n");
network::send(master, "getbots");
}
}

View File

@ -10,6 +10,7 @@
#include "console.hpp"
#include "game_console.hpp"
#include "scheduler.hpp"
#include "fastfiles.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -20,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;
@ -30,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);
}
}
@ -47,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);
@ -130,6 +133,7 @@ namespace command
params::params()
: nesting_(game::cmd_args->nesting)
{
assert(this->nesting_ < CMD_MAX_NESTING);
}
int params::size() const
@ -162,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
@ -200,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;
}
@ -214,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)
@ -258,15 +267,6 @@ namespace command
}
}
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool includeOverride)
{
game::DB_EnumXAssets_Internal(type, static_cast<void(*)(game::XAssetHeader, void*)>([](game::XAssetHeader header, void* data)
{
const auto& cb = *static_cast<const std::function<void(game::XAssetHeader)>*>(data);
cb(header);
}), &callback, includeOverride);
}
class component final : public component_interface
{
public:
@ -407,7 +407,7 @@ namespace command
console::info("Listing assets in pool %s\n", game::g_assetNames[type]);
const std::string filter = params.get(2);
enum_assets(type, [type, filter](const game::XAssetHeader header)
fastfiles::enum_assets(type, [type, filter](const game::XAssetHeader header)
{
const auto asset = game::XAsset{ type, header };
const auto* const asset_name = game::DB_GetXAssetName(&asset);

View File

@ -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);
}

View File

@ -1,9 +1,10 @@
#include <std_include.hpp>
#include "console.hpp"
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "command.hpp"
#include "command.hpp"
#include "console.hpp"
#include "game_console.hpp"
#include "rcon.hpp"
#include <utils/thread.hpp>
@ -11,11 +12,6 @@
#include <utils/concurrency.hpp>
#include <utils/hook.hpp>
namespace game_console
{
void print(int type, const std::string& data);
}
namespace console
{
namespace
@ -40,7 +36,7 @@ namespace console
{
static thread_local char buffer[0x1000];
const auto count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
const auto count = vsnprintf_s(buffer, _TRUNCATE, message, *ap);
if (count < 0) return {};
return {buffer, static_cast<size_t>(count)};
@ -113,7 +109,7 @@ namespace console
messages.access([&](message_queue& msgs)
{
msgs = {};
msgs = std::queue<std::string>();
});
}
@ -190,7 +186,7 @@ namespace console
messages.access([&](message_queue& msgs)
{
message_queue_copy = std::move(msgs);
msgs = {};
msgs = std::queue<std::string>();
});
}

View File

@ -1,10 +1,12 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include "server_list.hpp"
#include "network.hpp"
#include "command.hpp"
#include "game/game.hpp"
#include "dvars.hpp"
#include <utils/hook.hpp>
@ -78,12 +80,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 +146,7 @@ namespace dedicated
va_end(ap);
scheduler::once([]()
scheduler::once([]
{
command::execute("map_rotate");
}, scheduler::main, 3s);
@ -285,7 +286,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,13 +297,13 @@ namespace dedicated
return scheduler::cond_continue;
}, scheduler::pipeline::main, 1s);
scheduler::on_game_initialized([]()
scheduler::on_game_initialized([]
{
initialize();
printf("==================================\n");
printf("Server started!\n");
printf("==================================\n");
console::info("==================================\n");
console::info("Server started!\n");
console::info("==================================\n");
// remove disconnect command
game::Cmd_RemoveCommand(reinterpret_cast<const char*>(751));

View File

@ -353,7 +353,7 @@ namespace demonware
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
printf("%s: %s\n", function, buffer);
va_end(ap);

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

@ -365,7 +365,7 @@ namespace game_console
void print_internal(const char* fmt, ...)
{
char va_buffer[0x200] = {0};
char va_buffer[0x200]{};
va_list ap;
va_start(ap, fmt);

View File

@ -2,6 +2,8 @@
namespace game_console
{
void print(int type, const std::string& data);
bool console_char_event(int local_client_num, int key);
bool console_key_event(int local_client_num, int key, int down);

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,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(0x1403ED894, compile_error_stub); // LinkFile
utils::hook::call(0x1403ED8E8, 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)

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,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);
});
}
};
}

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>
@ -47,7 +49,8 @@ namespace gsc
return true;
}
// This will prevent 'fake' GSC raw files from being compiled. They are parsed by the game's own parser later.
// This will prevent 'fake' GSC raw files from being compiled.
// They are parsed by the game's own parser later as they are special files.
if (name.starts_with("maps/createfx") || name.starts_with("maps/createart") ||
(name.starts_with("maps/mp") && name.ends_with("_fx.gsc")))
{
@ -78,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{};
@ -225,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))
@ -253,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();
@ -314,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:
@ -352,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

@ -3,6 +3,7 @@
#include "game/game.hpp"
#include "command.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include <utils/hook.hpp>
@ -126,7 +127,7 @@ namespace map_rotation
change_process_priority();
if (!game::SV_MapExists(value.data()))
{
printf("map_rotation: '%s' map doesn't exist!\n", value.data());
console::info("map_rotation: '%s' map doesn't exist!\n", value.data());
launch_default_map();
return;
}
@ -135,7 +136,7 @@ namespace map_rotation
}
else
{
printf("Invalid map rotation key: %s\n", key.data());
console::info("Invalid map rotation key: %s\n", key.data());
}
}

View File

@ -31,7 +31,7 @@ namespace network
return false;
}
const std::string_view data(message->data + offset, message->cursize - offset);
const std::string data(message->data + offset, message->cursize - offset);
handler->second(*address, data);
return true;
@ -274,11 +274,10 @@ namespace network
utils::hook::set<int>(0x1403DAD35, max_packet_size);
// ignore built in "print" oob command and add in our own
utils::hook::set<uint8_t>(0x14020A723, 0xEB);
on("print", [](const game::netadr_s&, const std::string_view& data)
utils::hook::set<std::uint8_t>(0x14020A723, 0xEB);
on("print", [](const game::netadr_s&, const std::string& data)
{
const std::string message{data};
console::info(message.data());
console::info("%s", data.data());
});
}
}

View File

@ -1,9 +1,8 @@
#pragma once
#include "game/game.hpp"
namespace network
{
using callback = std::function<void(const game::netadr_s&, const std::string_view&)>;
using callback = std::function<void(const game::netadr_s&, const std::string&)>;
void on(const std::string& command, const callback& callback);
void send(const game::netadr_s& address, const std::string& command, const std::string& data = {}, char separator = ' ');

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

@ -14,6 +14,8 @@
#include <utils/cryptography.hpp>
#include <utils/hook.hpp>
#include <version.hpp>
namespace party
{
namespace
@ -80,29 +82,18 @@ namespace party
std::string get_dvar_string(const std::string& dvar)
{
auto* dvar_value = game::Dvar_FindVar(dvar.data());
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
if (dvar_value && dvar_value->current.string)
{
return dvar_value->current.string;
return {dvar_value->current.string};
}
return {};
}
int get_dvar_int(const std::string& dvar)
{
auto* dvar_value = game::Dvar_FindVar(dvar.data());
if (dvar_value && dvar_value->current.integer)
{
return dvar_value->current.integer;
}
return -1;
}
bool get_dvar_bool(const std::string& dvar)
{
auto* dvar_value = game::Dvar_FindVar(dvar.data());
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
if (dvar_value && dvar_value->current.enabled)
{
return dvar_value->current.enabled;
@ -128,22 +119,20 @@ namespace party
{
if (game::CL_IsCgameInitialized())
{
// CL_ForwardCommandToServer
reinterpret_cast<void (*)(int, const char*)>(0x14020B310)(0, "disconnect");
// CL_WritePacket
reinterpret_cast<void (*)(int)>(0x1402058F0)(0);
game::CL_ForwardCommandToServer(0, "disconnect");
game::CL_WritePacket(0);
}
// CL_Disconnect
reinterpret_cast<void (*)(int)>(0x140209EC0)(0);
game::CL_Disconnect(0);
}
}
utils::hook::detour cldisconnect_hook;
utils::hook::detour cl_disconnect_hook;
void cldisconnect_stub(int a1)
void cl_disconnect_stub(int a1)
{
clear_sv_motd();
cldisconnect_hook.invoke<void>(a1);
cl_disconnect_hook.invoke<void>(a1);
}
const auto drop_reason_stub = utils::hook::assemble([](utils::hook::assembler& a)
@ -156,7 +145,7 @@ namespace party
void clear_sv_motd()
{
party::sv_motd.clear();
sv_motd.clear();
}
int get_client_num_by_name(const std::string& name)
@ -305,7 +294,7 @@ namespace party
utils::hook::jump(0x14020A010, disconnect_stub);
// detour CL_Disconnect to clear motd
cldisconnect_hook.create(0x140209EC0, cldisconnect_stub);
cl_disconnect_hook.create(0x140209EC0, cl_disconnect_stub);
if (game::environment::is_mp())
{
@ -548,6 +537,7 @@ namespace party
info.set("playmode", utils::string::va("%i", game::Com_GetCurrentCoDPlayMode()));
info.set("sv_running", utils::string::va("%i", get_dvar_bool("sv_running")));
info.set("dedicated", utils::string::va("%i", get_dvar_bool("dedicated")));
info.set("shortversion", SHORTVERSION);
network::send(target, "infoResponse", info.build(), '\n');
});

View File

@ -14,7 +14,7 @@
#include <utils/string.hpp>
#include <utils/hook.hpp>
#include "version.hpp"
#include <version.hpp>
namespace patches
{
@ -118,7 +118,7 @@ namespace patches
return 0;
}
const char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
{
std::string file_name = filename;
if (file_name.find(".cfg") == std::string::npos)
@ -133,9 +133,7 @@ namespace patches
return buf;
}
// DB_ReadRawFile
return reinterpret_cast<const char*(*)(const char*, char*, int)>(SELECT_VALUE(0x140180E30, 0x140273080))(
filename, buf, size);
return game::DB_ReadRawFile(filename, buf, size);
}
void aim_assist_add_to_target_list(void* a1, void* a2)

View File

@ -1,7 +1,5 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include "game/game.hpp"
#include "command.hpp"
@ -9,6 +7,9 @@
#include "network.hpp"
#include "scheduler.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace rcon
{
namespace
@ -112,6 +113,7 @@ namespace rcon
network::send(redirect_target_, "print", message);
return true;
}
return false;
}

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,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);

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

@ -1,8 +1,11 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include <utils/hook.hpp>
#include "game/game.hpp"
#include "console.hpp"
#include <utils/hook.hpp>
namespace security
{
namespace
@ -21,7 +24,7 @@ namespace security
if(snapshot.num_clients > 1200 && !printed)
{
printed = true;
printf("Too many entities (%d)... remapping!\n", snapshot.num_clients);
console::info("Too many entities (%d)... remapping!\n", snapshot.num_clients);
}
snapshot.num_clients = std::min(snapshot.num_clients, 1200);

View File

@ -138,17 +138,6 @@ namespace ui_scripting
{
const auto lua = get_globals();
lua["io"]["fileexists"] = utils::io::file_exists;
lua["io"]["writefile"] = utils::io::write_file;
lua["io"]["movefile"] = utils::io::move_file;
lua["io"]["filesize"] = utils::io::file_size;
lua["io"]["createdirectory"] = utils::io::create_directory;
lua["io"]["directoryexists"] = utils::io::directory_exists;
lua["io"]["directoryisempty"] = utils::io::directory_is_empty;
lua["io"]["listfiles"] = utils::io::list_files;
lua["io"]["removefile"] = utils::io::remove_file;
lua["io"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
using game = table;
auto game_type = game();
lua["game"] = game_type;

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

@ -24,12 +24,8 @@ namespace game
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1403D19B0};
WEAK symbol<void()> Com_Quit_f{0x1402F9390, 0x1403D08C0};
WEAK symbol<void(const char* cmdName, void (), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{
0x1402EDDB0, 0x1403AF2C0
};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{
0x1402EE350, 0x1403AF900
};
WEAK symbol<void(const char* cmdName, void (), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x1402EDDB0, 0x1403AF2C0};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x1402EE350, 0x1403AF900};
WEAK symbol<void(const char*)> Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0};
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x1402EEA30, 0x1403B0020};
WEAK symbol<void()> Cmd_EndTokenizeString{0x1402EE000, 0x1403AF5B0};
@ -40,10 +36,12 @@ 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};
WEAK symbol<void(int localClientNum)> CL_WritePacket{0x0, 0x1402058F0};
WEAK symbol<void(int localClientNum)> CL_Disconnect{0x0, 0x140209EC0};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x1400EE500, 0x1401A3050};
WEAK symbol<void(int localClientNum, /*mp::cg_s**/void* cg, const char* dvar, const char* value)> CG_SetClientDvarFromServer{0, 0x1401BF0A0};
@ -59,6 +57,7 @@ namespace game
WEAK symbol<int(XAssetType type, const char* name)> DB_IsXAssetDefault{0x0, 0x140270320};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x0, 0x14026FCC0};
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x0, 0x14026FB90};
WEAK symbol<char*(const char* filename, char* buf, int size)> DB_ReadRawFile{0x140180E30, 0x140273080};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140370860, 0x1404BF8B0};
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x140370700, 0x1404BF690};
@ -67,9 +66,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)>
@ -101,9 +98,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};
@ -111,9 +106,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};
@ -161,6 +154,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};
@ -168,10 +162,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};
@ -254,6 +250,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>