Initial commit

This commit is contained in:
Federico Cecchetto
2021-05-29 22:07:08 +02:00
parent daf26438b0
commit dc150bb940
55 changed files with 11496 additions and 1 deletions

244
src/component/gsc.cpp Normal file
View File

@ -0,0 +1,244 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/scripting/event.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/functions.hpp"
namespace gsc
{
using function_args = std::vector<scripting::script_value>;
using builtin_function = void(*)();
using builtin_method = void(*)(game::scr_entref_t);
using script_function = std::function<scripting::script_value(function_args)>;
using script_method = std::function<scripting::script_value(game::scr_entref_t, function_args)>;
std::unordered_map<unsigned, script_function> functions;
std::unordered_map<unsigned, script_method> methods;
namespace
{
void gsc_function_stub(game::scr_entref_t ent)
{
/*const auto function = scripting::script_value(*game::scr_VmPub->top);
if (!function.is<std::string>())
{
return;
}
const auto function_str = function.as<std::string>();
if (gsc_functions.find(function_str) != gsc_functions.end())
{
std::vector<scripting::script_value> arguments;
for (auto i = 1; i < game::scr_VmPub->outparamcount; i++)
{
const auto value = game::scr_VmPub->top[-i];
arguments.emplace_back(value);
}
const auto value = gsc_functions[function_str](ent, arguments);
if (*reinterpret_cast<const int*>(&value))
{
game::Scr_ClearOutParams();
scripting::push_value(value);
}
}*/
}
std::string method_name(unsigned int id)
{
const auto map = *game::plutonium::method_map_rev;
for (const auto& function : map)
{
if (function.second == id)
{
return function.first;
}
}
return {};
}
std::string function_name(unsigned int id)
{
const auto map = *game::plutonium::function_map_rev;
for (const auto& function : map)
{
if (function.second == id)
{
return function.first;
}
}
return {};
}
function_args get_arguments()
{
function_args args;
const auto top = game::scr_VmPub->top;
for (auto i = 0; i < game::scr_VmPub->outparamcount; i++)
{
const auto value = game::scr_VmPub->top[i];
args.push_back(value);
}
return args;
}
auto function_map_start = 0x200;
auto method_map_start = 0x8400;
void call_function(unsigned int id)
{
if (id >= 0x200)
{
try
{
const auto result = functions[id](get_arguments());
scripting::push_value(result);
}
catch (std::exception e)
{
printf("************** Script execution error **************\n");
printf("Error executing function %s\n", function_name(id).data());
printf("%s\n", e.what());
printf("****************************************************\n");
}
}
else
{
reinterpret_cast<builtin_function*>(0x1D6EB34)[id]();
}
}
void call_method(game::scr_entref_t ent, unsigned int id)
{
if (id >= 0x8400)
{
try
{
const auto result = methods[id](ent, get_arguments());
scripting::push_value(result);
}
catch (std::exception e)
{
printf("************** Script execution error **************\n");
printf("Error executing method %s\n", method_name(id).data());
printf("%s\n", e.what());
printf("****************************************************\n");
}
}
else
{
reinterpret_cast<builtin_method*>(0x1D4F258)[id](ent);
}
}
__declspec(naked) void call_builtin_stub()
{
__asm
{
push eax
mov eax, 0x20B4A5C
mov [eax], esi
mov eax, 0x20B4A90
mov [eax], edx
pop eax
pushad
push eax
call call_function
pop eax
popad
push 0x56C900
retn
}
}
__declspec(naked) void call_builtin_method_stub()
{
__asm
{
pushad
push ecx
push ebx
call call_method
pop ebx
pop ecx
popad
push ebx
add esp, 0xC
push 0x56CBE9
retn
}
}
}
namespace function
{
void add(const std::string& name, const script_function& func)
{
const auto index = function_map_start++;
functions[index] = func;
(*game::plutonium::function_map_rev)[name] = index;
}
}
namespace method
{
void add(const std::string& name, const script_method& func)
{
const auto index = method_map_start++;
methods[index] = func;
(*game::plutonium::method_map_rev)[name] = index;
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
function::add("lol", [](function_args args) -> scripting::script_value
{
const auto str = args[0].as<std::string>();
return str;
});
method::add("test", [](game::scr_entref_t, function_args args) -> scripting::script_value
{
printf("here\n");
return {};
});
utils::hook::jump(0x56C8EB, call_builtin_stub);
utils::hook::jump(0x56CBDC, call_builtin_method_stub);
}
};
}
REGISTER_COMPONENT(gsc::component)

View File

@ -0,0 +1,58 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/scripting/entity.hpp"
#include "game/scripting/execution.hpp"
#include "notifies.hpp"
namespace notifies
{
namespace
{
utils::hook::detour client_command_hook;
utils::hook::detour scr_player_killed_hook;
utils::hook::detour scr_player_damage_hook;
void client_command_stub(int clientNum)
{
char cmd[1024] = { 0 };
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
if (cmd == "say"s)
{
std::string message = game::ConcatArgs(1);
message.erase(0, 1);
scheduler::once([message, clientNum]()
{
const scripting::entity level{*game::levelEntityId};
const auto _player = scripting::call("getEntByNum", {clientNum});
if (_player.get_raw().type == game::SCRIPT_OBJECT)
{
const auto player = _player.as<scripting::entity>();
scripting::notify(level, "say", {player, message});
scripting::notify(player, "say", {message});
}
});
}
return client_command_hook.invoke<void>(clientNum);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
client_command_hook.create(0x502CB0, client_command_stub);
}
};
}
REGISTER_COMPONENT(notifies::component)

View File

@ -0,0 +1,5 @@
#pragma once
namespace notifies
{
}

View File

@ -0,0 +1,83 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
namespace scheduler
{
namespace
{
std::queue<std::function<void()>> tasks;
struct task
{
std::function<bool()> handler;
std::chrono::milliseconds interval{};
std::chrono::high_resolution_clock::time_point last_call{};
};
utils::concurrent_list<task> callbacks;
void execute()
{
for (auto callback : callbacks)
{
const auto now = std::chrono::high_resolution_clock::now();
const auto diff = now - callback->last_call;
if (diff < callback->interval) continue;
callback->last_call = now;
const auto res = callback->handler();
if (res)
{
callbacks.remove(callback);
}
}
}
void server_frame()
{
execute();
reinterpret_cast<void (*)()>(0x50C1E0)();
}
}
void schedule(const std::function<bool()>& callback, const std::chrono::milliseconds delay)
{
task task;
task.handler = callback;
task.interval = delay;
task.last_call = std::chrono::high_resolution_clock::now();
callbacks.add(task);
}
void loop(const std::function<void()>& callback, const std::chrono::milliseconds delay)
{
schedule([callback]()
{
callback();
return false;
}, delay);
}
void once(const std::function<void()>& callback, const std::chrono::milliseconds delay)
{
schedule([callback]()
{
callback();
return true;
}, delay);
}
class component final : public component_interface
{
public:
void post_unpack() override
{
utils::hook::call(0x50CEDC, server_frame);
}
};
}
REGISTER_COMPONENT(scheduler::component)

View File

@ -0,0 +1,10 @@
#pragma once
namespace scheduler
{
void schedule(const std::function<bool()>& callback, std::chrono::milliseconds delay = 0ms);
void loop(const std::function<void()>& callback, std::chrono::milliseconds delay = 0ms);
void once(const std::function<void()>& callback, std::chrono::milliseconds delay = 0ms);
void init();
}

139
src/component/scripting.cpp Normal file
View File

@ -0,0 +1,139 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/scripting/event.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/functions.hpp"
namespace scripting
{
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
std::unordered_map<std::string, std::unordered_map<std::string, char*>> script_function_table;
namespace
{
utils::hook::detour vm_notify_hook;
utils::hook::detour scr_add_class_field_hook;
utils::hook::detour scr_load_level_hook;
utils::hook::detour g_shutdown_game_hook;
utils::hook::detour scr_emit_function_hook;
utils::hook::detour scr_end_load_scripts_hook;
void vm_notify_stub(const unsigned int notify_list_owner_id, const unsigned int string_value,
game::VariableValue* top)
{
const auto* name = game::SL_ConvertToString(string_value);
if (name)
{
event e;
e.name = name;
e.entity = notify_list_owner_id;
for (auto* value = top; value->type != game::SCRIPT_END; --value)
{
e.arguments.emplace_back(*value);
}
if (e.name == "connected")
{
scripting::clear_entity_fields(e.entity);
}
}
vm_notify_hook.invoke<void>(notify_list_owner_id, string_value, top);
}
void scr_add_class_field_stub(unsigned int classnum, unsigned int _name, unsigned int canonicalString, unsigned int offset)
{
const auto name = game::SL_ConvertToString(_name);
if (fields_table[classnum].find(name) == fields_table[classnum].end())
{
fields_table[classnum][name] = offset;
}
scr_add_class_field_hook.invoke<void>(classnum, _name, canonicalString, offset);
}
void scr_load_level_stub()
{
scr_load_level_hook.invoke<void>();
}
void g_shutdown_game_stub(const int free_scripts)
{
g_shutdown_game_hook.invoke<void>(free_scripts);
}
char* function_pos(unsigned int filename, unsigned int name)
{
const auto scripts_pos = *reinterpret_cast<int*>(0x1D6EB14);
const auto v2 = game::FindVariable(scripts_pos, filename);
const auto v3 = game::FindObject(scripts_pos, v2);
const auto v4 = game::FindVariable(v3, name);
if (!v2 || !v3 || !v4)
{
return 0;
}
return utils::hook::invoke<char*>(0x5659C0, v3, v4);
}
void scr_emit_function_stub(unsigned int filename, unsigned int threadName, char* codePos)
{
const auto* name = game::SL_ConvertToString(filename);
const auto filename_id = atoi(name);
for (const auto& entry : scripting::file_list)
{
if (entry.first == filename_id)
{
if (script_function_table.find(entry.second) == script_function_table.end())
{
script_function_table[entry.second] = {};
}
for (const auto& token : scripting::token_map)
{
if (token.second == threadName)
{
const auto pos = function_pos(filename, threadName);
if (pos)
{
script_function_table[entry.second][token.first] = pos;
}
}
}
}
}
scr_emit_function_hook.invoke<void>(filename, threadName, codePos);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
scr_load_level_hook.create(0x527AF0, scr_load_level_stub);
g_shutdown_game_hook.create(0x50C100, g_shutdown_game_stub);
scr_add_class_field_hook.create(0x567CD0, scr_add_class_field_stub);
//vm_notify_hook.create(0x569720, vm_notify_stub);
//scr_emit_function_hook.create(0x561400, scr_emit_function_stub);
}
};
}
REGISTER_COMPONENT(scripting::component)

View File

@ -0,0 +1,7 @@
#pragma once
namespace scripting
{
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
extern std::unordered_map<std::string, std::unordered_map<std::string, char*>> script_function_table;
}