small clean up [skip ci]
also no compiler err's
This commit is contained in:
parent
aabe0b427b
commit
369b0f3a1c
@ -1,103 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "command.hpp"
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
#include "network.hpp"
|
|
||||||
#include "party.hpp"
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "game/scripting/execution.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
#include <utils/cryptography.hpp>
|
|
||||||
|
|
||||||
namespace bots
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
bool can_add()
|
|
||||||
{
|
|
||||||
if (party::get_client_count() < *game::mp::svs_numclients)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: when scripting comes, fix this to use better notifies
|
|
||||||
void bot_team_join(const int entity_num)
|
|
||||||
{
|
|
||||||
const game::scr_entref_t entref{static_cast<uint16_t>(entity_num), 0};
|
|
||||||
scheduler::once([entref]()
|
|
||||||
{
|
|
||||||
scripting::notify(entref, "luinotifyserver", {"team_select", 2});
|
|
||||||
scheduler::once([entref]()
|
|
||||||
{
|
|
||||||
auto* _class = utils::string::va("class%d", utils::cryptography::random::get_integer() % 5);
|
|
||||||
scripting::notify(entref, "luinotifyserver", {"class_select", _class});
|
|
||||||
}, scheduler::pipeline::server, 2s);
|
|
||||||
}, scheduler::pipeline::server, 2s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void spawn_bot(const int entity_num)
|
|
||||||
{
|
|
||||||
game::SV_SpawnTestClient(&game::mp::g_entities[entity_num]);
|
|
||||||
if (game::Com_GetCurrentCoDPlayMode() == game::CODPLAYMODE_CORE)
|
|
||||||
{
|
|
||||||
bot_team_join(entity_num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_bot()
|
|
||||||
{
|
|
||||||
if (!can_add())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SV_BotGetRandomName
|
|
||||||
const auto* const bot_name = game::SV_BotGetRandomName();
|
|
||||||
auto* bot_ent = game::SV_AddBot(bot_name);
|
|
||||||
if (bot_ent)
|
|
||||||
{
|
|
||||||
spawn_bot(bot_ent->s.entityNum);
|
|
||||||
}
|
|
||||||
else if (can_add()) // workaround since first bot won't ever spawn
|
|
||||||
{
|
|
||||||
add_bot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
if (game::environment::is_sp())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
command::add("spawnBot", [](const command::params& params)
|
|
||||||
{
|
|
||||||
if (!game::SV_Loaded() || game::VirtualLobby_Loaded()) return;
|
|
||||||
|
|
||||||
auto num_bots = 1;
|
|
||||||
if (params.size() == 2)
|
|
||||||
{
|
|
||||||
num_bots = atoi(params.get(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = 0; i < (num_bots > *game::mp::svs_numclients ? *game::mp::svs_numclients : num_bots); i++)
|
|
||||||
{
|
|
||||||
scheduler::once(add_bot, scheduler::pipeline::server, 100ms * i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(bots::component)
|
|
@ -1,317 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
|
|
||||||
#include "game/scripting/entity.hpp"
|
|
||||||
#include "game/scripting/execution.hpp"
|
|
||||||
#include "game/scripting/lua/value_conversion.hpp"
|
|
||||||
#include "game/scripting/lua/error.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
|
|
||||||
#include "logfile.hpp"
|
|
||||||
|
|
||||||
namespace logfile
|
|
||||||
{
|
|
||||||
std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
sol::lua_value convert_entity(lua_State* state, const game::mp::gentity_s* ent)
|
|
||||||
{
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const scripting::entity player{game::Scr_GetEntityId(ent->s.entityNum, 0)};
|
|
||||||
return scripting::lua::convert(state, player);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_weapon_name(unsigned int weapon, bool isAlternate)
|
|
||||||
{
|
|
||||||
char output[1024] = {0};
|
|
||||||
game::BG_GetWeaponNameComplete(weapon, isAlternate, output, 1024);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
sol::lua_value convert_vector(lua_State* state, const float* vec)
|
|
||||||
{
|
|
||||||
if (!vec)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto vec_ = scripting::vector(vec);
|
|
||||||
return scripting::lua::convert(state, vec_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string convert_mod(const int meansOfDeath)
|
|
||||||
{
|
|
||||||
const auto value = reinterpret_cast<game::scr_string_t**>(0x140FEC3F0)[meansOfDeath];
|
|
||||||
const auto string = game::SL_ConvertToString(*value);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 std::string hitloc = reinterpret_cast<const char**>(0x140FEC4D0)[hitLoc];
|
|
||||||
const auto mod_ = convert_mod(meansOfDeath);
|
|
||||||
|
|
||||||
const auto weapon_ = get_weapon_name(weapon, isAlternate);
|
|
||||||
|
|
||||||
for (const auto& callback : player_killed_callbacks)
|
|
||||||
{
|
|
||||||
const auto state = callback.lua_state();
|
|
||||||
|
|
||||||
const auto self_ = convert_entity(state, self);
|
|
||||||
const auto inflictor_ = convert_entity(state, inflictor);
|
|
||||||
const auto attacker_ = convert_entity(state, attacker);
|
|
||||||
|
|
||||||
const auto dir = convert_vector(state, vDir);
|
|
||||||
|
|
||||||
const auto result = callback(self_, inflictor_, attacker_, damage,
|
|
||||||
mod_, weapon_, dir, hitloc, psTimeOffset, deathAnimDuration);
|
|
||||||
|
|
||||||
scripting::lua::handle_error(result);
|
|
||||||
|
|
||||||
if (result.valid() && result.get_type() == sol::type::number)
|
|
||||||
{
|
|
||||||
damage = result.get<int>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (damage == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scr_player_killed_hook.invoke<void>(self, inflictor, attacker, damage, meansOfDeath,
|
|
||||||
weapon, isAlternate, vDir, hitLoc, psTimeOffset, deathAnimDuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 std::string hitloc = reinterpret_cast<const char**>(0x140FEC4D0)[hitLoc];
|
|
||||||
const auto mod_ = convert_mod(meansOfDeath);
|
|
||||||
|
|
||||||
const auto weapon_ = get_weapon_name(weapon, isAlternate);
|
|
||||||
|
|
||||||
for (const auto& callback : player_damage_callbacks)
|
|
||||||
{
|
|
||||||
const auto state = callback.lua_state();
|
|
||||||
|
|
||||||
const auto self_ = convert_entity(state, self);
|
|
||||||
const auto inflictor_ = convert_entity(state, inflictor);
|
|
||||||
const auto attacker_ = convert_entity(state, attacker);
|
|
||||||
|
|
||||||
const auto point = convert_vector(state, vPoint);
|
|
||||||
const auto dir = convert_vector(state, vDir);
|
|
||||||
|
|
||||||
const auto result = callback(self_, inflictor_, attacker_,
|
|
||||||
damage, dflags, mod_, weapon_, point, dir, hitloc);
|
|
||||||
|
|
||||||
scripting::lua::handle_error(result);
|
|
||||||
|
|
||||||
if (result.valid() && result.get_type() == sol::type::number)
|
|
||||||
{
|
|
||||||
damage = result.get<int>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (damage == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scr_player_damage_hook.invoke<void>(self, inflictor, attacker, damage, dflags,
|
|
||||||
meansOfDeath, weapon, isAlternate, vPoint, vDir, hitLoc, timeOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void client_command_stub(const int clientNum)
|
|
||||||
{
|
|
||||||
auto self = &game::mp::g_entities[clientNum];
|
|
||||||
char cmd[1024] = {0};
|
|
||||||
|
|
||||||
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
|
|
||||||
|
|
||||||
if (cmd == "say"s || cmd == "say_team"s)
|
|
||||||
{
|
|
||||||
auto hidden = false;
|
|
||||||
std::string message(game::ConcatArgs(1));
|
|
||||||
|
|
||||||
hidden = message[1] == '/';
|
|
||||||
message.erase(0, hidden ? 2 : 1);
|
|
||||||
|
|
||||||
scheduler::once([cmd, message, self]()
|
|
||||||
{
|
|
||||||
const scripting::entity level{*game::levelEntityId};
|
|
||||||
const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)};
|
|
||||||
|
|
||||||
scripting::notify(level, cmd, {player, message});
|
|
||||||
scripting::notify(player, cmd, {message});
|
|
||||||
}, scheduler::pipeline::server);
|
|
||||||
|
|
||||||
if (hidden)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientCommand
|
|
||||||
return utils::hook::invoke<void>(0x140336000, clientNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void g_shutdown_game_stub(const int freeScripts)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
const scripting::entity level{*game::levelEntityId};
|
|
||||||
scripting::notify(level, "shutdownGame_called", {1});
|
|
||||||
}
|
|
||||||
|
|
||||||
// G_ShutdownGame
|
|
||||||
return utils::hook::invoke<void>(0x140345A60, freeScripts);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int local_id_to_entity(unsigned int local_id)
|
|
||||||
{
|
|
||||||
const auto variable = game::scr_VarGlob->objectVariableValue[local_id];
|
|
||||||
return variable.u.f.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool execute_vm_hook(const char* pos)
|
|
||||||
{
|
|
||||||
if (vm_execute_hooks.find(pos) == vm_execute_hooks.end())
|
|
||||||
{
|
|
||||||
hook_enabled = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hook_enabled && pos > reinterpret_cast<char*>(vm_execute_hooks.size()))
|
|
||||||
{
|
|
||||||
hook_enabled = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
args.push_back(scripting::lua::convert(state, *value));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto result = hook(self, sol::as_args(args));
|
|
||||||
scripting::lua::handle_error(result);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vm_execute_stub(utils::hook::assembler& a)
|
|
||||||
{
|
|
||||||
const auto replace = a.newLabel();
|
|
||||||
const auto end = a.newLabel();
|
|
||||||
|
|
||||||
a.pushad64();
|
|
||||||
|
|
||||||
a.mov(rcx, r14);
|
|
||||||
a.call_aligned(execute_vm_hook);
|
|
||||||
|
|
||||||
a.cmp(al, 0);
|
|
||||||
a.jne(replace);
|
|
||||||
|
|
||||||
a.popad64();
|
|
||||||
a.jmp(end);
|
|
||||||
|
|
||||||
a.bind(end);
|
|
||||||
|
|
||||||
a.movzx(r15d, byte_ptr(r14));
|
|
||||||
a.inc(r14);
|
|
||||||
a.mov(dword_ptr(rbp, 0xA4), r15d);
|
|
||||||
|
|
||||||
a.jmp(SELECT_VALUE(0x140376663, 0x140444653));
|
|
||||||
|
|
||||||
a.bind(replace);
|
|
||||||
|
|
||||||
a.popad64();
|
|
||||||
a.mov(r14, reinterpret_cast<char*>(empty_function));
|
|
||||||
a.jmp(end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_player_damage_callback(const sol::protected_function& callback)
|
|
||||||
{
|
|
||||||
player_damage_callbacks.push_back(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_player_killed_callback(const sol::protected_function& callback)
|
|
||||||
{
|
|
||||||
player_killed_callbacks.push_back(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_callbacks()
|
|
||||||
{
|
|
||||||
player_damage_callbacks.clear();
|
|
||||||
player_killed_callbacks.clear();
|
|
||||||
vm_execute_hooks.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_vm_execute_hook()
|
|
||||||
{
|
|
||||||
hook_enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_vm_execute_hook()
|
|
||||||
{
|
|
||||||
hook_enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
if (game::environment::is_sp())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
utils::hook::call(0x14048191D, client_command_stub);
|
|
||||||
|
|
||||||
scr_player_damage_hook.create(0x14037DC50, scr_player_damage_stub);
|
|
||||||
scr_player_killed_hook.create(0x14037DF30, scr_player_killed_stub);
|
|
||||||
|
|
||||||
utils::hook::call(0x140484EC0, g_shutdown_game_stub);
|
|
||||||
utils::hook::call(0x1404853C1, g_shutdown_game_stub);
|
|
||||||
|
|
||||||
utils::hook::jump(SELECT_VALUE(0x140376655, 0x140444645), utils::hook::assemble(vm_execute_stub), true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(logfile::component)
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace logfile
|
|
||||||
{
|
|
||||||
extern std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
|
|
||||||
|
|
||||||
void add_player_damage_callback(const sol::protected_function& callback);
|
|
||||||
void add_player_killed_callback(const sol::protected_function& callback);
|
|
||||||
void clear_callbacks();
|
|
||||||
|
|
||||||
void enable_vm_execute_hook();
|
|
||||||
void disable_vm_execute_hook();
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
#include "command.hpp"
|
|
||||||
#include "console.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
|
|
||||||
namespace lui
|
|
||||||
{
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
// Don't show create cod account popup
|
|
||||||
//utils::hook::set<uint32_t>(0x14017C957, 0); // H1(1.4)
|
|
||||||
|
|
||||||
//#ifdef _DEBUG
|
|
||||||
// Enable development menus (causes issues in sp)
|
|
||||||
//utils::hook::set<uint32_t>(SELECT_VALUE(0x1400B4ABC, 0x1401AB779), 1);
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
command::add("lui_open", [](const command::params& params)
|
|
||||||
{
|
|
||||||
if (params.size() <= 1)
|
|
||||||
{
|
|
||||||
console::info("usage: lui_open <name>\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::LUI_OpenMenu(0, params[1], 0, 0, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
command::add("lui_open_popup", [](const command::params& params)
|
|
||||||
{
|
|
||||||
if (params.size() <= 1)
|
|
||||||
{
|
|
||||||
console::info("usage: lui_open_popup <name>\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::LUI_OpenMenu(0, params[1], 1, 0, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
command::add("runMenuScript", [](const command::params& params)
|
|
||||||
{
|
|
||||||
const auto args_str = params.join(1);
|
|
||||||
const auto* args = args_str.data();
|
|
||||||
game::UI_RunMenuScript(0, &args);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(lui::component)
|
|
@ -1,141 +0,0 @@
|
|||||||
#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"
|
|
||||||
#include "game/scripting/event.hpp"
|
|
||||||
#include "game/scripting/lua/engine.hpp"
|
|
||||||
#include "game/scripting/execution.hpp"
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
#include "scripting.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, const char*>> script_function_table;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
utils::hook::detour vm_notify_hook;
|
|
||||||
utils::hook::detour scr_load_level_hook;
|
|
||||||
utils::hook::detour g_shutdown_game_hook;
|
|
||||||
|
|
||||||
utils::hook::detour scr_add_class_field_hook;
|
|
||||||
|
|
||||||
utils::hook::detour scr_set_thread_position_hook;
|
|
||||||
utils::hook::detour process_script_hook;
|
|
||||||
|
|
||||||
std::string current_file;
|
|
||||||
|
|
||||||
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())
|
|
||||||
{
|
|
||||||
const auto* string = game::SL_ConvertToString(string_value);
|
|
||||||
if (string)
|
|
||||||
{
|
|
||||||
event e;
|
|
||||||
e.name = string;
|
|
||||||
e.entity = notify_list_owner_id;
|
|
||||||
|
|
||||||
for (auto* value = top; value->type != game::SCRIPT_END; --value)
|
|
||||||
{
|
|
||||||
e.arguments.emplace_back(*value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.name == "entitydeleted")
|
|
||||||
{
|
|
||||||
scripting::clear_entity_fields(e.entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua::engine::notify(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vm_notify_hook.invoke<void>(notify_list_owner_id, string_value, top);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scr_load_level_stub()
|
|
||||||
{
|
|
||||||
scr_load_level_hook.invoke<void>();
|
|
||||||
if (!game::VirtualLobby_Loaded())
|
|
||||||
{
|
|
||||||
lua::engine::start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void g_shutdown_game_stub(const int free_scripts)
|
|
||||||
{
|
|
||||||
lua::engine::stop();
|
|
||||||
return g_shutdown_game_hook.invoke<void>(free_scripts);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t _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 process_script_stub(const char* filename)
|
|
||||||
{
|
|
||||||
const auto file_id = atoi(filename);
|
|
||||||
if (file_id)
|
|
||||||
{
|
|
||||||
current_file = scripting::find_token(file_id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
current_file = filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
process_script_hook.invoke<void>(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scr_set_thread_position_stub(unsigned int threadName, const char* codePos)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
if (game::environment::is_sp())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
vm_notify_hook.create(SELECT_VALUE(0x140379A00, 0x1404479F0), vm_notify_stub);
|
|
||||||
|
|
||||||
scr_add_class_field_hook.create(SELECT_VALUE(0x140370370, 0x14043E2C0), scr_add_class_field_stub);
|
|
||||||
|
|
||||||
scr_set_thread_position_hook.create(SELECT_VALUE(0x14036A180, 0x140437D10), scr_set_thread_position_stub);
|
|
||||||
process_script_hook.create(SELECT_VALUE(0x1403737E0, 0x1404417E0), process_script_stub);
|
|
||||||
|
|
||||||
scr_load_level_hook.create(SELECT_VALUE(0x1402A5BE0, 0x1403727C0), scr_load_level_stub);
|
|
||||||
g_shutdown_game_hook.create(SELECT_VALUE(0x140277D40, 0x140345A60), g_shutdown_game_stub);
|
|
||||||
|
|
||||||
scheduler::loop([]()
|
|
||||||
{
|
|
||||||
lua::engine::run_frame();
|
|
||||||
}, scheduler::pipeline::server);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(scripting::component)
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <utils/concurrency.hpp>
|
|
||||||
|
|
||||||
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, const char*>> script_function_table;
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
#include "dvars.hpp"
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
|
||||||
|
|
||||||
#include <utils/nt.hpp>
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
#include <utils/flags.hpp>
|
|
||||||
|
|
||||||
namespace shaders
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
game::dvar_t* disable_shader_caching = nullptr;
|
|
||||||
|
|
||||||
bool shader_should_show_dialog_stub()
|
|
||||||
{
|
|
||||||
return !disable_shader_caching->current.enabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
if (!game::environment::is_mp())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto has_flag = utils::flags::has_flag("noshadercaching");
|
|
||||||
|
|
||||||
disable_shader_caching = dvars::register_bool("disable_shader_caching", has_flag, game::DVAR_FLAG_SAVED, true);
|
|
||||||
if (has_flag)
|
|
||||||
{
|
|
||||||
dvars::override::set_bool("disable_shader_caching", 1);
|
|
||||||
dvars::override::set_from_string("disable_shader_caching", "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
utils::hook::jump(0x14007E710, shader_should_show_dialog_stub);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(shaders::component)
|
|
@ -1,53 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
|
|
||||||
namespace slowmotion
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
void scr_cmd_set_slow_motion()
|
|
||||||
{
|
|
||||||
if (game::Scr_GetNumParam() < 1)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int duration = 1000;
|
|
||||||
float end = 1.0f;
|
|
||||||
const float start = game::Scr_GetFloat(0);
|
|
||||||
|
|
||||||
if (game::Scr_GetNumParam() >= 2)
|
|
||||||
{
|
|
||||||
end = game::Scr_GetFloat(1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game::Scr_GetNumParam() >= 3)
|
|
||||||
{
|
|
||||||
duration = static_cast<int>(game::Scr_GetFloat(2u) * 1000.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
game::SV_SetConfigstring(10, utils::string::va("%i %i %g %g", *game::mp::gameTime, duration, start, end));
|
|
||||||
game::Com_SetSlowMotion(start, end, duration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
if (!game::environment::is_dedi())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
utils::hook::jump(0x140365480, scr_cmd_set_slow_motion); // H1(1.4)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(slowmotion::component)
|
|
@ -1,60 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
#include <utils/thread.hpp>
|
|
||||||
|
|
||||||
namespace thread_names
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
void set_thread_names()
|
|
||||||
{
|
|
||||||
static std::unordered_map<int, std::string> thread_names =
|
|
||||||
{
|
|
||||||
{game::THREAD_CONTEXT_MAIN, "Main"},
|
|
||||||
{game::THREAD_CONTEXT_BACKEND, "Backend"}, // Renderer
|
|
||||||
{game::THREAD_CONTEXT_WORKER0, "Worker0"},
|
|
||||||
{game::THREAD_CONTEXT_WORKER1, "Worker1"},
|
|
||||||
{game::THREAD_CONTEXT_WORKER2, "Worker2"},
|
|
||||||
{game::THREAD_CONTEXT_WORKER3, "Worker3"},
|
|
||||||
{game::THREAD_CONTEXT_WORKER4, "Worker4"},
|
|
||||||
{game::THREAD_CONTEXT_WORKER5, "Worker5"},
|
|
||||||
{game::THREAD_CONTEXT_WORKER6, "Worker6"},
|
|
||||||
{game::THREAD_CONTEXT_WORKER7, "Worker7"},
|
|
||||||
{game::THREAD_CONTEXT_SERVER, "Server"},
|
|
||||||
{game::THREAD_CONTEXT_CINEMATIC, "Cinematic"},
|
|
||||||
{game::THREAD_CONTEXT_DATABASE, "Database"},
|
|
||||||
{game::THREAD_CONTEXT_STREAM, "Stream"},
|
|
||||||
{game::THREAD_CONTEXT_SNDSTREAMPACKETCALLBACK, "Snd stream packet callback"},
|
|
||||||
{game::THREAD_CONTEXT_STATS_WRITE, "Stats write"},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& thread_name : thread_names)
|
|
||||||
{
|
|
||||||
const auto id = game::threadIds[thread_name.first];
|
|
||||||
if (id)
|
|
||||||
{
|
|
||||||
utils::thread::set_name(id, thread_name.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
set_thread_names();
|
|
||||||
scheduler::once(set_thread_names, scheduler::pipeline::main);
|
|
||||||
scheduler::once(set_thread_names, scheduler::pipeline::renderer);
|
|
||||||
scheduler::once(set_thread_names, scheduler::pipeline::server);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(thread_names::component)
|
|
@ -1,180 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
#include "command.hpp"
|
|
||||||
|
|
||||||
#include "ui_scripting.hpp"
|
|
||||||
|
|
||||||
#include "game/ui_scripting/lua/engine.hpp"
|
|
||||||
#include "game/ui_scripting/execution.hpp"
|
|
||||||
#include "game/ui_scripting/lua/error.hpp"
|
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
|
|
||||||
namespace ui_scripting
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
std::unordered_map<game::hks::cclosure*, sol::protected_function> converted_functions;
|
|
||||||
|
|
||||||
utils::hook::detour hksi_lual_error_hook;
|
|
||||||
utils::hook::detour hksi_lual_error_hook2;
|
|
||||||
utils::hook::detour hks_start_hook;
|
|
||||||
utils::hook::detour hks_shutdown_hook;
|
|
||||||
utils::hook::detour hks_allocator_hook;
|
|
||||||
utils::hook::detour hks_frame_hook;
|
|
||||||
|
|
||||||
bool error_hook_enabled = false;
|
|
||||||
|
|
||||||
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
|
|
||||||
{
|
|
||||||
char va_buffer[2048] = {0};
|
|
||||||
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsprintf_s(va_buffer, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
const auto formatted = std::string(va_buffer);
|
|
||||||
|
|
||||||
if (!error_hook_enabled)
|
|
||||||
{
|
|
||||||
return hksi_lual_error_hook.invoke<void>(s, formatted.data());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::runtime_error(formatted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void* hks_start_stub(char a1)
|
|
||||||
{
|
|
||||||
const auto _1 = gsl::finally([]()
|
|
||||||
{
|
|
||||||
ui_scripting::lua::engine::start();
|
|
||||||
});
|
|
||||||
|
|
||||||
return hks_start_hook.invoke<void*>(a1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hks_shutdown_stub()
|
|
||||||
{
|
|
||||||
ui_scripting::lua::engine::stop();
|
|
||||||
hks_shutdown_hook.invoke<void*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* hks_allocator_stub(void* userData, void* oldMemory, unsigned __int64 oldSize, unsigned __int64 newSize)
|
|
||||||
{
|
|
||||||
const auto closure = reinterpret_cast<game::hks::cclosure*>(oldMemory);
|
|
||||||
if (converted_functions.find(closure) != converted_functions.end())
|
|
||||||
{
|
|
||||||
converted_functions.erase(closure);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hks_allocator_hook.invoke<void*>(userData, oldMemory, oldSize, newSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hks_frame_stub()
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
if (state)
|
|
||||||
{
|
|
||||||
ui_scripting::lua::engine::run_frame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main_function_handler(game::hks::lua_State* state)
|
|
||||||
{
|
|
||||||
const auto value = state->m_apistack.base[-1];
|
|
||||||
if (value.t != game::hks::TCFUNCTION)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto closure = reinterpret_cast<game::hks::cclosure*>(value.v.cClosure);
|
|
||||||
if (converted_functions.find(closure) == converted_functions.end())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto function = converted_functions[closure];
|
|
||||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
|
||||||
const auto arguments = get_return_values(count);
|
|
||||||
const auto s = function.lua_state();
|
|
||||||
|
|
||||||
std::vector<sol::lua_value> converted_args;
|
|
||||||
|
|
||||||
for (const auto& argument : arguments)
|
|
||||||
{
|
|
||||||
converted_args.push_back(lua::convert(s, argument));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto results = function(sol::as_args(converted_args));
|
|
||||||
lua::handle_error(results);
|
|
||||||
|
|
||||||
for (const auto& result : results)
|
|
||||||
{
|
|
||||||
push_value(lua::convert({s, result}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return results.return_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function)
|
|
||||||
{
|
|
||||||
converted_functions[closure] = function;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_converted_functions()
|
|
||||||
{
|
|
||||||
converted_functions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_error_hook()
|
|
||||||
{
|
|
||||||
error_hook_enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_error_hook()
|
|
||||||
{
|
|
||||||
error_hook_enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
if (game::environment::is_dedi())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hks_start_hook.create(SELECT_VALUE(0x1400E4B40, 0x140176A40), hks_start_stub);
|
|
||||||
hks_shutdown_hook.create(SELECT_VALUE(0x1400DD3D0, 0x14016CA80), hks_shutdown_stub);
|
|
||||||
hksi_lual_error_hook.create(SELECT_VALUE(0x1400A5EA0, 0x14012F300), hksi_lual_error_stub);
|
|
||||||
hks_allocator_hook.create(SELECT_VALUE(0x14009B570, 0x14012BAC0), hks_allocator_stub);
|
|
||||||
hks_frame_hook.create(SELECT_VALUE(0x1400E37F0, 0x1401755B0), hks_frame_stub);
|
|
||||||
|
|
||||||
if (game::environment::is_mp())
|
|
||||||
{
|
|
||||||
hksi_lual_error_hook2.create(0x1401366B0, hksi_lual_error_stub);
|
|
||||||
}
|
|
||||||
|
|
||||||
command::add("lui_restart", []()
|
|
||||||
{
|
|
||||||
utils::hook::invoke<void>(SELECT_VALUE(0x1400DD3D0, 0x14016CA80));
|
|
||||||
utils::hook::invoke<void>(SELECT_VALUE(0x1400E6170, 0x1401780D0));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(ui_scripting::component)
|
|
@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/ui_scripting/lua/value_conversion.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting
|
|
||||||
{
|
|
||||||
int main_function_handler(game::hks::lua_State* state);
|
|
||||||
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function);
|
|
||||||
void clear_converted_functions();
|
|
||||||
|
|
||||||
void enable_error_hook();
|
|
||||||
void disable_error_hook();
|
|
||||||
}
|
|
@ -1,474 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
#include "dvars.hpp"
|
|
||||||
#include "updater.hpp"
|
|
||||||
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
|
||||||
|
|
||||||
#include <utils/nt.hpp>
|
|
||||||
#include <utils/concurrency.hpp>
|
|
||||||
#include <utils/http.hpp>
|
|
||||||
#include <utils/cryptography.hpp>
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
|
|
||||||
#define MASTER "https://master.fed0001.xyz/h1-mod/"
|
|
||||||
|
|
||||||
#define FILES_PATH "files.json"
|
|
||||||
#define FILES_PATH_DEV "files-dev.json"
|
|
||||||
|
|
||||||
#define DATA_PATH "data/"
|
|
||||||
#define DATA_PATH_DEV "data-dev/"
|
|
||||||
|
|
||||||
#define ERR_UPDATE_CHECK_FAIL "Failed to check for updates"
|
|
||||||
#define ERR_DOWNLOAD_FAIL "Failed to download file "
|
|
||||||
#define ERR_WRITE_FAIL "Failed to write file "
|
|
||||||
|
|
||||||
#define BINARY_NAME "h1-mod.exe"
|
|
||||||
|
|
||||||
namespace updater
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
game::dvar_t* cl_auto_update;
|
|
||||||
bool has_tried_update = false;
|
|
||||||
|
|
||||||
struct status
|
|
||||||
{
|
|
||||||
bool done;
|
|
||||||
bool success;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct file_data
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::string data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct update_data_t
|
|
||||||
{
|
|
||||||
bool restart_required{};
|
|
||||||
bool cancelled{};
|
|
||||||
status check{};
|
|
||||||
status download{};
|
|
||||||
std::string error{};
|
|
||||||
std::string current_file{};
|
|
||||||
std::vector<std::string> required_files{};
|
|
||||||
};
|
|
||||||
|
|
||||||
utils::concurrency::container<update_data_t> update_data;
|
|
||||||
|
|
||||||
std::string get_branch()
|
|
||||||
{
|
|
||||||
return GIT_BRANCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string select(const std::string& main, const std::string& develop)
|
|
||||||
{
|
|
||||||
if (get_branch() == "develop")
|
|
||||||
{
|
|
||||||
return develop;
|
|
||||||
}
|
|
||||||
|
|
||||||
return main;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_data_path()
|
|
||||||
{
|
|
||||||
if (get_branch() == "develop")
|
|
||||||
{
|
|
||||||
return DATA_PATH_DEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DATA_PATH;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_update_check_status(bool done, bool success, const std::string& error = {})
|
|
||||||
{
|
|
||||||
update_data.access([done, success, error](update_data_t& data_)
|
|
||||||
{
|
|
||||||
data_.check.done = done;
|
|
||||||
data_.check.success = success;
|
|
||||||
data_.error = error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_update_download_status(bool done, bool success, const std::string& error = {})
|
|
||||||
{
|
|
||||||
update_data.access([done, success, error](update_data_t& data_)
|
|
||||||
{
|
|
||||||
data_.download.done = done;
|
|
||||||
data_.download.success = success;
|
|
||||||
data_.error = error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_file(const std::string& name, const std::string& sha)
|
|
||||||
{
|
|
||||||
std::string data;
|
|
||||||
if (!utils::io::read_file(name, &data))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (utils::cryptography::sha1::compute(data, true) != sha)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string load_binary_name()
|
|
||||||
{
|
|
||||||
// utils::nt::library self;
|
|
||||||
// return self.get_name();
|
|
||||||
// returns the game's name and not the client's
|
|
||||||
|
|
||||||
return BINARY_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_binary_name()
|
|
||||||
{
|
|
||||||
static const auto name = load_binary_name();
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_time_str()
|
|
||||||
{
|
|
||||||
return utils::string::va("%i", uint32_t(time(nullptr)));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> download_file(const std::string& name)
|
|
||||||
{
|
|
||||||
return utils::http::get_data(MASTER + select(DATA_PATH, DATA_PATH_DEV) + name + "?" + get_time_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_update_cancelled()
|
|
||||||
{
|
|
||||||
return update_data.access<bool>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.cancelled;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write_file(const std::string& name, const std::string& data)
|
|
||||||
{
|
|
||||||
if (get_binary_name() == name &&
|
|
||||||
utils::io::file_exists(name) &&
|
|
||||||
!utils::io::move_file(name, name + ".old"))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
return utils::io::write_file("update_test/" + name, data);
|
|
||||||
#else
|
|
||||||
return utils::io::write_file(name, data);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_old_file()
|
|
||||||
{
|
|
||||||
utils::io::remove_file(get_binary_name() + ".old");
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset_data()
|
|
||||||
{
|
|
||||||
update_data.access([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
data_ = {};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_mode_flag()
|
|
||||||
{
|
|
||||||
if (game::environment::is_mp())
|
|
||||||
{
|
|
||||||
return "-multiplayer";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game::environment::is_sp())
|
|
||||||
{
|
|
||||||
return "-singleplayer";
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// workaround
|
|
||||||
void relaunch()
|
|
||||||
{
|
|
||||||
if (!utils::io::file_exists(BINARY_NAME))
|
|
||||||
{
|
|
||||||
utils::nt::terminate(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
STARTUPINFOA startup_info;
|
|
||||||
PROCESS_INFORMATION process_info;
|
|
||||||
|
|
||||||
ZeroMemory(&startup_info, sizeof(startup_info));
|
|
||||||
ZeroMemory(&process_info, sizeof(process_info));
|
|
||||||
startup_info.cb = sizeof(startup_info);
|
|
||||||
|
|
||||||
char current_dir[MAX_PATH];
|
|
||||||
GetCurrentDirectoryA(sizeof(current_dir), current_dir);
|
|
||||||
|
|
||||||
char buf[1024] = {0};
|
|
||||||
const auto command_line = utils::string::va("%s %s", GetCommandLineA(), get_mode_flag().data());
|
|
||||||
strcpy_s(buf, 1024, command_line);
|
|
||||||
|
|
||||||
CreateProcess(BINARY_NAME, buf, nullptr, nullptr, false, NULL, nullptr, current_dir,
|
|
||||||
&startup_info, &process_info);
|
|
||||||
|
|
||||||
if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread);
|
|
||||||
if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE) CloseHandle(process_info.hProcess);
|
|
||||||
|
|
||||||
utils::nt::terminate(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_has_tried_update(bool tried)
|
|
||||||
{
|
|
||||||
has_tried_update = tried;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_has_tried_update()
|
|
||||||
{
|
|
||||||
return has_tried_update;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool auto_updates_enabled()
|
|
||||||
{
|
|
||||||
return cl_auto_update->current.enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_update_check_done()
|
|
||||||
{
|
|
||||||
return update_data.access<bool>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.check.done;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_update_download_done()
|
|
||||||
{
|
|
||||||
return update_data.access<bool>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.download.done;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_update_check_status()
|
|
||||||
{
|
|
||||||
return update_data.access<bool>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.check.success;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_update_download_status()
|
|
||||||
{
|
|
||||||
return update_data.access<bool>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.download.success;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_update_available()
|
|
||||||
{
|
|
||||||
return update_data.access<bool>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.required_files.size() > 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_restart_required()
|
|
||||||
{
|
|
||||||
return update_data.access<bool>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.restart_required;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_last_error()
|
|
||||||
{
|
|
||||||
return update_data.access<std::string>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_current_file()
|
|
||||||
{
|
|
||||||
return update_data.access<std::string>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.current_file;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void cancel_update()
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf("[Updater] Cancelling update\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return update_data.access([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
data_.cancelled = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void start_update_check()
|
|
||||||
{
|
|
||||||
cancel_update();
|
|
||||||
reset_data();
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf("[Updater] starting update check\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
scheduler::once([]()
|
|
||||||
{
|
|
||||||
const auto files_data = utils::http::get_data(MASTER + select(FILES_PATH, FILES_PATH_DEV) + "?" + get_time_str());
|
|
||||||
|
|
||||||
if (is_update_cancelled())
|
|
||||||
{
|
|
||||||
reset_data();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!files_data.has_value())
|
|
||||||
{
|
|
||||||
set_update_check_status(true, false, ERR_UPDATE_CHECK_FAIL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rapidjson::Document j;
|
|
||||||
j.Parse(files_data.value().data());
|
|
||||||
|
|
||||||
if (!j.IsArray())
|
|
||||||
{
|
|
||||||
set_update_check_status(true, false, ERR_UPDATE_CHECK_FAIL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> required_files;
|
|
||||||
|
|
||||||
const auto files = j.GetArray();
|
|
||||||
for (const auto& file : files)
|
|
||||||
{
|
|
||||||
if (!file.IsArray() || file.Size() != 3 || !file[0].IsString() || !file[2].IsString())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto name = file[0].GetString();
|
|
||||||
const auto sha = file[2].GetString();
|
|
||||||
|
|
||||||
if (!check_file(name, sha))
|
|
||||||
{
|
|
||||||
if (get_binary_name() == name)
|
|
||||||
{
|
|
||||||
update_data.access([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
data_.restart_required = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf("[Updater] need file %s\n", name);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
required_files.push_back(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update_data.access([&required_files](update_data_t& data_)
|
|
||||||
{
|
|
||||||
data_.check.done = true;
|
|
||||||
data_.check.success = true;
|
|
||||||
data_.required_files = required_files;
|
|
||||||
});
|
|
||||||
}, scheduler::pipeline::async);
|
|
||||||
}
|
|
||||||
|
|
||||||
void start_update_download()
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf("[Updater] starting update download\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!is_update_check_done() || !get_update_check_status() || is_update_cancelled())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduler::once([]()
|
|
||||||
{
|
|
||||||
const auto required_files = update_data.access<std::vector<std::string>>([](update_data_t& data_)
|
|
||||||
{
|
|
||||||
return data_.required_files;
|
|
||||||
});
|
|
||||||
|
|
||||||
std::vector<file_data> downloads;
|
|
||||||
|
|
||||||
for (const auto& file : required_files)
|
|
||||||
{
|
|
||||||
update_data.access([file](update_data_t& data_)
|
|
||||||
{
|
|
||||||
data_.current_file = file;
|
|
||||||
});
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf("[Updater] downloading file %s\n", file.data());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const auto data = download_file(file);
|
|
||||||
|
|
||||||
if (is_update_cancelled())
|
|
||||||
{
|
|
||||||
reset_data();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.has_value())
|
|
||||||
{
|
|
||||||
set_update_download_status(true, false, ERR_DOWNLOAD_FAIL + file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
downloads.push_back({file, data.value()});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& download : downloads)
|
|
||||||
{
|
|
||||||
if (!write_file(download.name, download.data))
|
|
||||||
{
|
|
||||||
set_update_download_status(true, false, ERR_WRITE_FAIL + download.name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_update_download_status(true, true);
|
|
||||||
}, scheduler::pipeline::async);
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
delete_old_file();
|
|
||||||
cl_auto_update = dvars::register_bool("cg_auto_update", true, game::DVAR_FLAG_SAVED, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(updater::component)
|
|
@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace updater
|
|
||||||
{
|
|
||||||
void relaunch();
|
|
||||||
|
|
||||||
void set_has_tried_update(bool tried);
|
|
||||||
bool get_has_tried_update();
|
|
||||||
bool auto_updates_enabled();
|
|
||||||
|
|
||||||
bool is_update_available();
|
|
||||||
bool is_update_check_done();
|
|
||||||
bool get_update_check_status();
|
|
||||||
|
|
||||||
bool is_update_download_done();
|
|
||||||
bool get_update_download_status();
|
|
||||||
|
|
||||||
bool is_restart_required();
|
|
||||||
|
|
||||||
std::string get_last_error();
|
|
||||||
std::string get_current_file();
|
|
||||||
|
|
||||||
void start_update_check();
|
|
||||||
void start_update_download();
|
|
||||||
void cancel_update();
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
|
|
||||||
namespace videos
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
utils::hook::detour playvid_hook;
|
|
||||||
std::unordered_map<std::string, std::string> video_replaces;
|
|
||||||
|
|
||||||
void playvid(const char* name, const int a2, const int a3)
|
|
||||||
{
|
|
||||||
const auto vid = video_replaces.find(name);
|
|
||||||
if (vid != video_replaces.end())
|
|
||||||
{
|
|
||||||
char path[256];
|
|
||||||
game::Sys_BuildAbsPath(path, sizeof(path), game::SF_VIDEO, vid->second.data(), ".bik");
|
|
||||||
|
|
||||||
if (game::Sys_FileExists(path))
|
|
||||||
{
|
|
||||||
name = vid->second.data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return playvid_hook.invoke<void>(name, a2, a3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
playvid_hook.create(SELECT_VALUE(0x1404A9D00, 0x1405B0AF0), &playvid); // H1(1.4)
|
|
||||||
|
|
||||||
if (game::environment::is_mp())
|
|
||||||
{
|
|
||||||
video_replaces["menus_bg_comp2"] = "menus_bg_h1mod";
|
|
||||||
video_replaces["mp_menus_bg_options"] = "menus_bg_h1mod_blur";
|
|
||||||
}
|
|
||||||
else if (game::environment::is_sp())
|
|
||||||
{
|
|
||||||
video_replaces["sp_menus_bg_main_menu"] = "menus_bg_h1mod_sp";
|
|
||||||
video_replaces["sp_menus_bg_campaign"] = "menus_bg_h1mod_sp";
|
|
||||||
video_replaces["sp_menus_bg_options"] = "menus_bg_h1mod_sp";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(videos::component)
|
|
@ -1,63 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
|
|
||||||
namespace virtuallobby
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
game::dvar_t* virtualLobby_fovscale;
|
|
||||||
|
|
||||||
const auto get_fovscale_stub = utils::hook::assemble([](utils::hook::assembler& a)
|
|
||||||
{
|
|
||||||
const auto ret = a.newLabel();
|
|
||||||
const auto original = a.newLabel();
|
|
||||||
|
|
||||||
a.pushad64();
|
|
||||||
a.mov(rax, qword_ptr(0x1425F7210)); // virtualLobbyInFiringRange
|
|
||||||
a.cmp(byte_ptr(rax, 0x10), 1);
|
|
||||||
a.je(original);
|
|
||||||
a.call_aligned(game::VirtualLobby_Loaded);
|
|
||||||
a.cmp(al, 0);
|
|
||||||
a.je(original);
|
|
||||||
|
|
||||||
// virtuallobby
|
|
||||||
a.popad64();
|
|
||||||
a.mov(rax, ptr(reinterpret_cast<int64_t>(&virtualLobby_fovscale)));
|
|
||||||
a.jmp(ret);
|
|
||||||
|
|
||||||
// original
|
|
||||||
a.bind(original);
|
|
||||||
a.popad64();
|
|
||||||
a.mov(rax, qword_ptr(0x1413A8580));
|
|
||||||
a.jmp(ret);
|
|
||||||
|
|
||||||
a.bind(ret);
|
|
||||||
a.mov(rcx, 0x142935000);
|
|
||||||
a.jmp(0x1400B556A);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
if (!game::environment::is_mp())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtualLobby_fovscale = dvars::register_float("virtualLobby_fovScale", 0.7f, 0.0f, 2.0f, game::DVAR_FLAG_SAVED);
|
|
||||||
|
|
||||||
utils::hook::nop(0x1400B555C, 14);
|
|
||||||
utils::hook::jump(0x1400B555C, get_fovscale_stub, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_COMPONENT(virtuallobby::component)
|
|
@ -1,120 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "entity.hpp"
|
|
||||||
#include "script_value.hpp"
|
|
||||||
#include "execution.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
entity::entity()
|
|
||||||
: entity(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
entity::entity(const entity& other) : entity(other.entity_id_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
entity::entity(entity&& other) noexcept
|
|
||||||
{
|
|
||||||
this->entity_id_ = other.entity_id_;
|
|
||||||
other.entity_id_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity::entity(const unsigned int entity_id)
|
|
||||||
: entity_id_(entity_id)
|
|
||||||
{
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
entity::entity(game::scr_entref_t entref)
|
|
||||||
: entity(game::FindEntityId(entref.entnum, entref.classnum))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
entity::~entity()
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
}
|
|
||||||
|
|
||||||
entity& entity::operator=(const entity& other)
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->entity_id_ = other.entity_id_;
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity& entity::operator=(entity&& other) noexcept
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->entity_id_ = other.entity_id_;
|
|
||||||
other.entity_id_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int entity::get_entity_id() const
|
|
||||||
{
|
|
||||||
return this->entity_id_;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::scr_entref_t entity::get_entity_reference() const
|
|
||||||
{
|
|
||||||
if (!this->entity_id_)
|
|
||||||
{
|
|
||||||
const auto not_null = static_cast<uint16_t>(~0ui16);
|
|
||||||
return game::scr_entref_t{not_null, not_null};
|
|
||||||
}
|
|
||||||
|
|
||||||
return game::Scr_GetEntityIdRef(this->get_entity_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool entity::operator==(const entity& other) const noexcept
|
|
||||||
{
|
|
||||||
return this->get_entity_id() == other.get_entity_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool entity::operator!=(const entity& other) const noexcept
|
|
||||||
{
|
|
||||||
return !this->operator==(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
void entity::add() const
|
|
||||||
{
|
|
||||||
if (this->entity_id_)
|
|
||||||
{
|
|
||||||
game::AddRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void entity::release() const
|
|
||||||
{
|
|
||||||
if (this->entity_id_)
|
|
||||||
{
|
|
||||||
game::RemoveRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void entity::set(const std::string& field, const script_value& value) const
|
|
||||||
{
|
|
||||||
set_entity_field(*this, field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
script_value entity::get<script_value>(const std::string& field) const
|
|
||||||
{
|
|
||||||
return get_entity_field(*this, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value entity::call(const std::string& name, const std::vector<script_value>& arguments) const
|
|
||||||
{
|
|
||||||
return call_function(name, *this, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "script_value.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
class entity final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
entity();
|
|
||||||
entity(unsigned int entity_id);
|
|
||||||
entity(game::scr_entref_t entref);
|
|
||||||
|
|
||||||
entity(const entity& other);
|
|
||||||
entity(entity&& other) noexcept;
|
|
||||||
|
|
||||||
~entity();
|
|
||||||
|
|
||||||
entity& operator=(const entity& other);
|
|
||||||
entity& operator=(entity&& other) noexcept;
|
|
||||||
|
|
||||||
void set(const std::string& field, const script_value& value) const;
|
|
||||||
|
|
||||||
template <typename T = script_value>
|
|
||||||
T get(const std::string& field) const;
|
|
||||||
|
|
||||||
script_value call(const std::string& name, const std::vector<script_value>& arguments = {}) const;
|
|
||||||
|
|
||||||
unsigned int get_entity_id() const;
|
|
||||||
game::scr_entref_t get_entity_reference() const;
|
|
||||||
|
|
||||||
bool operator ==(const entity& other) const noexcept;
|
|
||||||
bool operator !=(const entity& other) const noexcept;
|
|
||||||
|
|
||||||
private:
|
|
||||||
unsigned int entity_id_;
|
|
||||||
|
|
||||||
void add() const;
|
|
||||||
void release() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
script_value entity::get(const std::string& field) const;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T entity::get(const std::string& field) const
|
|
||||||
{
|
|
||||||
return this->get<script_value>(field).as<T>();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "script_value.hpp"
|
|
||||||
#include "entity.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
struct event
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
entity entity{};
|
|
||||||
std::vector<script_value> arguments;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,240 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "execution.hpp"
|
|
||||||
#include "safe_execution.hpp"
|
|
||||||
#include "stack_isolation.hpp"
|
|
||||||
|
|
||||||
#include "component/scripting.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
game::VariableValue* allocate_argument()
|
|
||||||
{
|
|
||||||
game::VariableValue* value_ptr = ++game::scr_VmPub->top;
|
|
||||||
++game::scr_VmPub->inparamcount;
|
|
||||||
return value_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_value(const script_value& value)
|
|
||||||
{
|
|
||||||
auto* value_ptr = allocate_argument();
|
|
||||||
*value_ptr = value.get_raw();
|
|
||||||
|
|
||||||
game::AddRefToValue(value_ptr->type, value_ptr->u);
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_field_id(const int classnum, const std::string& field)
|
|
||||||
{
|
|
||||||
if (scripting::fields_table[classnum].find(field) != scripting::fields_table[classnum].end())
|
|
||||||
{
|
|
||||||
return scripting::fields_table[classnum][field];
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value get_return_value()
|
|
||||||
{
|
|
||||||
if (game::scr_VmPub->inparamcount == 0)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
game::Scr_ClearOutParams();
|
|
||||||
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
|
|
||||||
game::scr_VmPub->inparamcount = 0;
|
|
||||||
|
|
||||||
return script_value(game::scr_VmPub->top[1 - game::scr_VmPub->outparamcount]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments)
|
|
||||||
{
|
|
||||||
stack_isolation _;
|
|
||||||
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
|
|
||||||
{
|
|
||||||
push_value(*i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto event_id = game::SL_GetString(event.data(), 0);
|
|
||||||
game::Scr_NotifyId(entity.get_entity_id(), event_id, game::scr_VmPub->inparamcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value call_function(const std::string& name, const entity& entity,
|
|
||||||
const std::vector<script_value>& arguments)
|
|
||||||
{
|
|
||||||
const auto entref = entity.get_entity_reference();
|
|
||||||
|
|
||||||
const auto is_method_call = *reinterpret_cast<const int*>(&entref) != -1;
|
|
||||||
const auto function = find_function(name, !is_method_call);
|
|
||||||
if (function == nullptr)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Unknown "s + (is_method_call ? "method" : "function") + " '" + name + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
stack_isolation _;
|
|
||||||
|
|
||||||
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
|
|
||||||
{
|
|
||||||
push_value(*i);
|
|
||||||
}
|
|
||||||
|
|
||||||
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
|
|
||||||
game::scr_VmPub->inparamcount = 0;
|
|
||||||
|
|
||||||
if (!safe_execution::call(function, entref))
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Error executing "s + (is_method_call ? "method" : "function") + " '" + name + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return get_return_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value call_function(const std::string& name, const std::vector<script_value>& arguments)
|
|
||||||
{
|
|
||||||
return call_function(name, entity(), arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
script_value call(const std::string& name, const std::vector<script_value>& arguments)
|
|
||||||
{
|
|
||||||
return call_function(name, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments)
|
|
||||||
{
|
|
||||||
const auto id = entity.get_entity_id();
|
|
||||||
|
|
||||||
stack_isolation _;
|
|
||||||
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
|
|
||||||
{
|
|
||||||
scripting::push_value(*i);
|
|
||||||
}
|
|
||||||
|
|
||||||
game::AddRefToObject(id);
|
|
||||||
|
|
||||||
const auto local_id = game::AllocThread(id);
|
|
||||||
const auto result = game::VM_Execute(local_id, pos, (unsigned int)arguments.size());
|
|
||||||
game::RemoveRefToObject(result);
|
|
||||||
|
|
||||||
return get_return_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* get_function_pos(const std::string& filename, const std::string& function)
|
|
||||||
{
|
|
||||||
if (scripting::script_function_table.find(filename) == scripting::script_function_table.end())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("File '" + filename + "' not found");
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto functions = scripting::script_function_table[filename];
|
|
||||||
if (functions.find(function) == functions.end())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Function '" + function + "' in file '" + filename + "' not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
return functions.at(function);
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value call_script_function(const entity& entity, const std::string& filename,
|
|
||||||
const std::string& function, const std::vector<script_value>& arguments)
|
|
||||||
{
|
|
||||||
const auto pos = get_function_pos(filename, function);
|
|
||||||
return exec_ent_thread(entity, pos, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::unordered_map<unsigned int, std::unordered_map<std::string, script_value>> custom_fields;
|
|
||||||
|
|
||||||
script_value get_custom_field(const entity& entity, const std::string& field)
|
|
||||||
{
|
|
||||||
auto& fields = custom_fields[entity.get_entity_id()];
|
|
||||||
const auto _field = fields.find(field);
|
|
||||||
if (_field != fields.end())
|
|
||||||
{
|
|
||||||
return _field->second;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_custom_field(const entity& entity, const std::string& field, const script_value& value)
|
|
||||||
{
|
|
||||||
const auto id = entity.get_entity_id();
|
|
||||||
|
|
||||||
if (custom_fields[id].find(field) != custom_fields[id].end())
|
|
||||||
{
|
|
||||||
custom_fields[id][field] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
custom_fields[id].insert(std::make_pair(field, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_entity_fields(const entity& entity)
|
|
||||||
{
|
|
||||||
const auto id = entity.get_entity_id();
|
|
||||||
|
|
||||||
if (custom_fields.find(id) != custom_fields.end())
|
|
||||||
{
|
|
||||||
custom_fields[id].clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_custom_fields()
|
|
||||||
{
|
|
||||||
custom_fields.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_entity_field(const entity& entity, const std::string& field, const script_value& value)
|
|
||||||
{
|
|
||||||
const auto entref = entity.get_entity_reference();
|
|
||||||
const int id = get_field_id(entref.classnum, field);
|
|
||||||
|
|
||||||
if (id != -1)
|
|
||||||
{
|
|
||||||
stack_isolation _;
|
|
||||||
push_value(value);
|
|
||||||
|
|
||||||
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
|
|
||||||
game::scr_VmPub->inparamcount = 0;
|
|
||||||
|
|
||||||
if (!safe_execution::set_entity_field(entref, id))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to set value for field '" + field + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Read custom fields
|
|
||||||
set_custom_field(entity, field, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value get_entity_field(const entity& entity, const std::string& field)
|
|
||||||
{
|
|
||||||
const auto entref = entity.get_entity_reference();
|
|
||||||
const auto id = get_field_id(entref.classnum, field);
|
|
||||||
|
|
||||||
if (id != -1)
|
|
||||||
{
|
|
||||||
stack_isolation _;
|
|
||||||
|
|
||||||
game::VariableValue value{};
|
|
||||||
if (!safe_execution::get_entity_field(entref, id, &value))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to get value for field '" + field + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto __ = gsl::finally([value]()
|
|
||||||
{
|
|
||||||
game::RemoveRefToValue(value.type, value.u);
|
|
||||||
});
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom fields
|
|
||||||
return get_custom_field(entity, field);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "entity.hpp"
|
|
||||||
#include "script_value.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
script_value call_function(const std::string& name, const std::vector<script_value>& arguments);
|
|
||||||
script_value call_function(const std::string& name, const entity& entity,
|
|
||||||
const std::vector<script_value>& arguments);
|
|
||||||
|
|
||||||
template <typename T = script_value>
|
|
||||||
T call(const std::string& name, const std::vector<script_value>& arguments = {});
|
|
||||||
|
|
||||||
template <>
|
|
||||||
script_value call(const std::string& name, const std::vector<script_value>& arguments);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T call(const std::string& name, const std::vector<script_value>& arguments)
|
|
||||||
{
|
|
||||||
return call<script_value>(name, arguments).as<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments);
|
|
||||||
const char* get_function_pos(const std::string& filename, const std::string& function);
|
|
||||||
script_value call_script_function(const entity& entity, const std::string& filename,
|
|
||||||
const std::string& function, const std::vector<script_value>& arguments);
|
|
||||||
|
|
||||||
void clear_entity_fields(const entity& entity);
|
|
||||||
void clear_custom_fields();
|
|
||||||
|
|
||||||
void set_entity_field(const entity& entity, const std::string& field, const script_value& value);
|
|
||||||
script_value get_entity_field(const entity& entity, const std::string& field);
|
|
||||||
|
|
||||||
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,106 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "functions.hpp"
|
|
||||||
|
|
||||||
#include <utils/string.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())
|
|
||||||
{
|
|
||||||
return function_entry->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
function_entry = secondary_map.find(target);
|
|
||||||
if (function_entry != secondary_map.end())
|
|
||||||
{
|
|
||||||
return function_entry->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
for (const auto& token : token_map)
|
|
||||||
{
|
|
||||||
if (token.second == id)
|
|
||||||
{
|
|
||||||
return token.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils::string::va("_ID%i", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int find_token_id(const std::string& name)
|
|
||||||
{
|
|
||||||
const auto result = token_map.find(name);
|
|
||||||
|
|
||||||
if (result != token_map.end())
|
|
||||||
{
|
|
||||||
return result->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_function find_function(const std::string& name, const bool prefer_global)
|
|
||||||
{
|
|
||||||
const auto index = find_function_index(name, prefer_global);
|
|
||||||
if (index < 0) return nullptr;
|
|
||||||
|
|
||||||
return get_function_by_index(index);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
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);
|
|
||||||
unsigned int find_token_id(const std::string& name);
|
|
||||||
|
|
||||||
script_function find_function(const std::string& name, const bool prefer_global);
|
|
||||||
}
|
|
@ -1,542 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "value_conversion.hpp"
|
|
||||||
|
|
||||||
#include "../execution.hpp"
|
|
||||||
#include "../functions.hpp"
|
|
||||||
|
|
||||||
#include "../../../component/command.hpp"
|
|
||||||
#include "../../../component/logfile.hpp"
|
|
||||||
#include "../../../component/scripting.hpp"
|
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
vector normalize_vector(const vector& vec)
|
|
||||||
{
|
|
||||||
const auto length = sqrt(
|
|
||||||
(vec.get_x() * vec.get_x()) +
|
|
||||||
(vec.get_y() * vec.get_y()) +
|
|
||||||
(vec.get_z() * vec.get_z())
|
|
||||||
);
|
|
||||||
|
|
||||||
return vector(
|
|
||||||
vec.get_x() / length,
|
|
||||||
vec.get_y() / length,
|
|
||||||
vec.get_z() / length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_io(sol::state& state)
|
|
||||||
{
|
|
||||||
state["io"]["fileexists"] = utils::io::file_exists;
|
|
||||||
state["io"]["writefile"] = utils::io::write_file;
|
|
||||||
state["io"]["remove_file"] = utils::io::remove_file;
|
|
||||||
state["io"]["filesize"] = utils::io::file_size;
|
|
||||||
state["io"]["createdirectory"] = utils::io::create_directory;
|
|
||||||
state["io"]["directoryexists"] = utils::io::directory_exists;
|
|
||||||
state["io"]["directoryisempty"] = utils::io::directory_is_empty;
|
|
||||||
state["io"]["listfiles"] = utils::io::list_files;
|
|
||||||
state["io"]["copyfolder"] = utils::io::copy_folder;
|
|
||||||
state["io"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_vector_type(sol::state& state)
|
|
||||||
{
|
|
||||||
auto vector_type = state.new_usertype<vector>("vector", sol::constructors<vector(float, float, float)>());
|
|
||||||
vector_type["x"] = sol::property(&vector::get_x, &vector::set_x);
|
|
||||||
vector_type["y"] = sol::property(&vector::get_y, &vector::set_y);
|
|
||||||
vector_type["z"] = sol::property(&vector::get_z, &vector::set_z);
|
|
||||||
|
|
||||||
vector_type["r"] = sol::property(&vector::get_x, &vector::set_x);
|
|
||||||
vector_type["g"] = sol::property(&vector::get_y, &vector::set_y);
|
|
||||||
vector_type["b"] = sol::property(&vector::get_z, &vector::set_z);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::addition] = sol::overload(
|
|
||||||
[](const vector& a, const vector& b)
|
|
||||||
{
|
|
||||||
return vector(
|
|
||||||
a.get_x() + b.get_x(),
|
|
||||||
a.get_y() + b.get_y(),
|
|
||||||
a.get_z() + b.get_z()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[](const vector& a, const int value)
|
|
||||||
{
|
|
||||||
return vector(
|
|
||||||
a.get_x() + value,
|
|
||||||
a.get_y() + value,
|
|
||||||
a.get_z() + value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::subtraction] = sol::overload(
|
|
||||||
[](const vector& a, const vector& b)
|
|
||||||
{
|
|
||||||
return vector(
|
|
||||||
a.get_x() - b.get_x(),
|
|
||||||
a.get_y() - b.get_y(),
|
|
||||||
a.get_z() - b.get_z()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[](const vector& a, const int value)
|
|
||||||
{
|
|
||||||
return vector(
|
|
||||||
a.get_x() - value,
|
|
||||||
a.get_y() - value,
|
|
||||||
a.get_z() - value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::multiplication] = sol::overload(
|
|
||||||
[](const vector& a, const vector& b)
|
|
||||||
{
|
|
||||||
return vector(
|
|
||||||
a.get_x() * b.get_x(),
|
|
||||||
a.get_y() * b.get_y(),
|
|
||||||
a.get_z() * b.get_z()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[](const vector& a, const int value)
|
|
||||||
{
|
|
||||||
return vector(
|
|
||||||
a.get_x() * value,
|
|
||||||
a.get_y() * value,
|
|
||||||
a.get_z() * value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::division] = sol::overload(
|
|
||||||
[](const vector& a, const vector& b)
|
|
||||||
{
|
|
||||||
return vector(
|
|
||||||
a.get_x() / b.get_x(),
|
|
||||||
a.get_y() / b.get_y(),
|
|
||||||
a.get_z() / b.get_z()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[](const vector& a, const int value)
|
|
||||||
{
|
|
||||||
return vector(
|
|
||||||
a.get_x() / value,
|
|
||||||
a.get_y() / value,
|
|
||||||
a.get_z() / value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::equal_to] = [](const vector& a, const vector& b)
|
|
||||||
{
|
|
||||||
return a.get_x() == b.get_x() &&
|
|
||||||
a.get_y() == b.get_y() &&
|
|
||||||
a.get_z() == b.get_z();
|
|
||||||
};
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::length] = [](const vector& a)
|
|
||||||
{
|
|
||||||
return sqrt((a.get_x() * a.get_x()) + (a.get_y() * a.get_y()) + (a.get_z() * a.get_z()));
|
|
||||||
};
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::to_string] = [](const vector& a)
|
|
||||||
{
|
|
||||||
return utils::string::va("{x: %f, y: %f, z: %f}", a.get_x(), a.get_y(), a.get_z());
|
|
||||||
};
|
|
||||||
|
|
||||||
vector_type["normalize"] = [](const vector& a)
|
|
||||||
{
|
|
||||||
return normalize_vector(a);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_entity_type(sol::state& state, event_handler& handler, scheduler& scheduler)
|
|
||||||
{
|
|
||||||
state["level"] = entity{*game::levelEntityId};
|
|
||||||
|
|
||||||
auto entity_type = state.new_usertype<entity>("entity");
|
|
||||||
|
|
||||||
for (const auto& func : method_map)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
std::vector<script_value> arguments{};
|
|
||||||
|
|
||||||
for (auto arg : va)
|
|
||||||
{
|
|
||||||
arguments.push_back(convert({s, arg}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return convert(s, entity.call(name, arguments));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
entity_type["set"] = [](const entity& entity, const std::string& field,
|
|
||||||
const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
entity.set(field, convert(value));
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type["get"] = [](const entity& entity, const sol::this_state s, const std::string& field)
|
|
||||||
{
|
|
||||||
return convert(s, entity.get(field));
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type["notify"] = [](const entity& entity, const sol::this_state s, const std::string& event,
|
|
||||||
sol::variadic_args va)
|
|
||||||
{
|
|
||||||
std::vector<script_value> arguments{};
|
|
||||||
|
|
||||||
for (auto arg : va)
|
|
||||||
{
|
|
||||||
arguments.push_back(convert({s, arg}));
|
|
||||||
}
|
|
||||||
|
|
||||||
notify(entity, event, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type["onnotify"] = [&handler](const entity& entity, const std::string& event,
|
|
||||||
const event_callback& callback)
|
|
||||||
{
|
|
||||||
event_listener listener{};
|
|
||||||
listener.callback = callback;
|
|
||||||
listener.entity = entity;
|
|
||||||
listener.event = event;
|
|
||||||
listener.is_volatile = false;
|
|
||||||
|
|
||||||
return handler.add_event_listener(std::move(listener));
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type["onnotifyonce"] = [&handler](const entity& entity, const std::string& event,
|
|
||||||
const event_callback& callback)
|
|
||||||
{
|
|
||||||
event_listener listener{};
|
|
||||||
listener.callback = callback;
|
|
||||||
listener.entity = entity;
|
|
||||||
listener.event = event;
|
|
||||||
listener.is_volatile = true;
|
|
||||||
|
|
||||||
return handler.add_event_listener(std::move(listener));
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type["call"] = [](const entity& entity, const sol::this_state s, const std::string& function,
|
|
||||||
sol::variadic_args va)
|
|
||||||
{
|
|
||||||
std::vector<script_value> arguments{};
|
|
||||||
|
|
||||||
for (auto arg : va)
|
|
||||||
{
|
|
||||||
arguments.push_back(convert({s, arg}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return convert(s, entity.call(function, arguments));
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type[sol::meta_function::new_index] = [](const entity& entity, const std::string& field,
|
|
||||||
const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
entity.set(field, convert(value));
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type[sol::meta_function::index] = [](const entity& entity, const sol::this_state s, const std::string& field)
|
|
||||||
{
|
|
||||||
return convert(s, entity.get(field));
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type["getstruct"] = [](const entity& entity, const sol::this_state s)
|
|
||||||
{
|
|
||||||
const auto id = entity.get_entity_id();
|
|
||||||
return scripting::lua::entity_to_struct(s, id);
|
|
||||||
};
|
|
||||||
|
|
||||||
entity_type["struct"] = sol::property([](const entity& entity, const sol::this_state s)
|
|
||||||
{
|
|
||||||
const auto id = entity.get_entity_id();
|
|
||||||
return scripting::lua::entity_to_struct(s, id);
|
|
||||||
});
|
|
||||||
|
|
||||||
entity_type["scriptcall"] = [](const entity& entity, const sol::this_state s, const std::string& filename,
|
|
||||||
const std::string function, sol::variadic_args va)
|
|
||||||
{
|
|
||||||
std::vector<script_value> arguments{};
|
|
||||||
|
|
||||||
for (auto arg : va)
|
|
||||||
{
|
|
||||||
arguments.push_back(convert({s, arg}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return convert(s, call_script_function(entity, filename, function, arguments));
|
|
||||||
};
|
|
||||||
|
|
||||||
struct game
|
|
||||||
{
|
|
||||||
};
|
|
||||||
auto game_type = state.new_usertype<game>("game_");
|
|
||||||
state["game"] = game();
|
|
||||||
|
|
||||||
for (const auto& func : function_map)
|
|
||||||
{
|
|
||||||
const auto name = utils::string::to_lower(func.first);
|
|
||||||
game_type[name] = [name](const game&, 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, call(name, arguments));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
game_type["call"] = [](const game&, const sol::this_state s, const std::string& function,
|
|
||||||
sol::variadic_args va)
|
|
||||||
{
|
|
||||||
std::vector<script_value> arguments{};
|
|
||||||
|
|
||||||
for (auto arg : va)
|
|
||||||
{
|
|
||||||
arguments.push_back(convert({s, arg}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return convert(s, call(function, arguments));
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback,
|
|
||||||
const long long milliseconds)
|
|
||||||
{
|
|
||||||
return scheduler.add(callback, milliseconds, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback,
|
|
||||||
const long long milliseconds)
|
|
||||||
{
|
|
||||||
return scheduler.add(callback, milliseconds, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["executecommand"] = [](const game&, const std::string& command)
|
|
||||||
{
|
|
||||||
command::execute(command, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["onplayerdamage"] = [](const game&, const sol::protected_function& callback)
|
|
||||||
{
|
|
||||||
logfile::add_player_damage_callback(callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["onplayerkilled"] = [](const game&, const sol::protected_function& callback)
|
|
||||||
{
|
|
||||||
logfile::add_player_killed_callback(callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["getgamevar"] = [](const sol::this_state s)
|
|
||||||
{
|
|
||||||
const auto value = ::game::scr_VarGlob->childVariableValue[*::game::gameEntityId];
|
|
||||||
|
|
||||||
::game::VariableValue variable{};
|
|
||||||
variable.type = value.type;
|
|
||||||
variable.u.uintValue = value.u.u.uintValue;
|
|
||||||
|
|
||||||
return convert(s, variable);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["getfunctions"] = [entity_type](const game&, const sol::this_state s, const std::string& filename)
|
|
||||||
{
|
|
||||||
if (scripting::script_function_table.find(filename) == scripting::script_function_table.end())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("File '" + filename + "' not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto functions = sol::table::create(s.lua_state());
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return functions;
|
|
||||||
};
|
|
||||||
|
|
||||||
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{};
|
|
||||||
|
|
||||||
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, arguments));
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["detour"] = [](const game&, const sol::this_state s, const std::string& filename,
|
|
||||||
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;
|
|
||||||
|
|
||||||
auto detour = sol::table::create(function.lua_state());
|
|
||||||
|
|
||||||
detour["disable"] = [pos]()
|
|
||||||
{
|
|
||||||
logfile::vm_execute_hooks.erase(pos);
|
|
||||||
};
|
|
||||||
|
|
||||||
detour["enable"] = [pos, function]()
|
|
||||||
{
|
|
||||||
logfile::vm_execute_hooks[pos] = function;
|
|
||||||
};
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return detour;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context::context(std::string folder)
|
|
||||||
: folder_(std::move(folder))
|
|
||||||
, scheduler_(state_)
|
|
||||||
, event_handler_(state_)
|
|
||||||
|
|
||||||
{
|
|
||||||
this->state_.open_libraries(sol::lib::base,
|
|
||||||
sol::lib::package,
|
|
||||||
sol::lib::io,
|
|
||||||
sol::lib::string,
|
|
||||||
sol::lib::os,
|
|
||||||
sol::lib::math,
|
|
||||||
sol::lib::table);
|
|
||||||
|
|
||||||
this->state_["include"] = [this](const std::string& file)
|
|
||||||
{
|
|
||||||
this->load_script(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
sol::function old_require = this->state_["require"];
|
|
||||||
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
|
|
||||||
this->state_["require"] = [base_path, old_require](const std::string& path)
|
|
||||||
{
|
|
||||||
return old_require(base_path + path);
|
|
||||||
};
|
|
||||||
|
|
||||||
this->state_["scriptdir"] = [this]()
|
|
||||||
{
|
|
||||||
return this->folder_;
|
|
||||||
};
|
|
||||||
|
|
||||||
setup_io(this->state_);
|
|
||||||
setup_vector_type(this->state_);
|
|
||||||
setup_entity_type(this->state_, this->event_handler_, this->scheduler_);
|
|
||||||
|
|
||||||
printf("Loading script '%s'\n", this->folder_.data());
|
|
||||||
this->load_script("__init__");
|
|
||||||
}
|
|
||||||
|
|
||||||
context::~context()
|
|
||||||
{
|
|
||||||
this->collect_garbage();
|
|
||||||
this->scheduler_.clear();
|
|
||||||
this->event_handler_.clear();
|
|
||||||
this->state_ = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void context::run_frame()
|
|
||||||
{
|
|
||||||
this->scheduler_.run_frame();
|
|
||||||
this->collect_garbage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void context::notify(const event& e)
|
|
||||||
{
|
|
||||||
this->scheduler_.dispatch(e);
|
|
||||||
this->event_handler_.dispatch(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void context::collect_garbage()
|
|
||||||
{
|
|
||||||
this->state_.collect_garbage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void context::load_script(const std::string& script)
|
|
||||||
{
|
|
||||||
if (!this->loaded_scripts_.emplace(script).second)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto file = (std::filesystem::path{this->folder_} / (script + ".lua")).generic_string();
|
|
||||||
handle_error(this->state_.safe_script_file(file, &sol::script_pass_on_error));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../event.hpp"
|
|
||||||
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable: 4702)
|
|
||||||
|
|
||||||
#define SOL_ALL_SAFETIES_ON 1
|
|
||||||
#define SOL_PRINT_ERRORS 0
|
|
||||||
#include <sol/sol.hpp>
|
|
||||||
|
|
||||||
#pragma warning(pop)
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
#include "event_handler.hpp"
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
class context
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
context(std::string folder);
|
|
||||||
~context();
|
|
||||||
|
|
||||||
context(context&&) noexcept = delete;
|
|
||||||
context& operator=(context&&) noexcept = delete;
|
|
||||||
|
|
||||||
context(const context&) = delete;
|
|
||||||
context& operator=(const context&) = delete;
|
|
||||||
|
|
||||||
void run_frame();
|
|
||||||
void notify(const event& e);
|
|
||||||
void collect_garbage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
sol::state state_{};
|
|
||||||
std::string folder_;
|
|
||||||
std::unordered_set<std::string> loaded_scripts_;
|
|
||||||
|
|
||||||
scheduler scheduler_;
|
|
||||||
event_handler event_handler_;
|
|
||||||
|
|
||||||
void load_script(const std::string& script);
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "engine.hpp"
|
|
||||||
#include "context.hpp"
|
|
||||||
|
|
||||||
#include "../execution.hpp"
|
|
||||||
#include "../../../component/logfile.hpp"
|
|
||||||
#include "../../../component/game_module.hpp"
|
|
||||||
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
|
|
||||||
namespace scripting::lua::engine
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
auto& get_scripts()
|
|
||||||
{
|
|
||||||
static std::vector<std::unique_ptr<context>> scripts{};
|
|
||||||
return scripts;
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_scripts(const std::string& script_dir)
|
|
||||||
{
|
|
||||||
if (!utils::io::directory_exists(script_dir))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto scripts = utils::io::list_files(script_dir);
|
|
||||||
|
|
||||||
for (const auto& script : scripts)
|
|
||||||
{
|
|
||||||
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
|
||||||
{
|
|
||||||
get_scripts().push_back(std::make_unique<context>(script));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
logfile::clear_callbacks();
|
|
||||||
get_scripts().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void start()
|
|
||||||
{
|
|
||||||
// No SP until there is a concept
|
|
||||||
if (game::environment::is_sp())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stop();
|
|
||||||
load_scripts(game_module::get_host_module().get_folder() + "/data/scripts/");
|
|
||||||
load_scripts("h1-mod/scripts/");
|
|
||||||
load_scripts("data/scripts/");
|
|
||||||
}
|
|
||||||
|
|
||||||
void notify(const event& e)
|
|
||||||
{
|
|
||||||
for (auto& script : get_scripts())
|
|
||||||
{
|
|
||||||
script->notify(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_frame()
|
|
||||||
{
|
|
||||||
for (auto& script : get_scripts())
|
|
||||||
{
|
|
||||||
script->run_frame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../event.hpp"
|
|
||||||
|
|
||||||
namespace scripting::lua::engine
|
|
||||||
{
|
|
||||||
void start();
|
|
||||||
void stop();
|
|
||||||
void notify(const event& e);
|
|
||||||
void run_frame();
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "../execution.hpp"
|
|
||||||
|
|
||||||
#include "component/console.hpp"
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
void notify_error()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
call("iprintln", {"^1Script execution error!"});
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_error(const sol::protected_function_result& result)
|
|
||||||
{
|
|
||||||
if (!result.valid())
|
|
||||||
{
|
|
||||||
console::error("************** Script execution error **************\n");
|
|
||||||
|
|
||||||
const sol::error err = result;
|
|
||||||
console::error("%s\n", err.what());
|
|
||||||
|
|
||||||
console::error("****************************************************\n");
|
|
||||||
|
|
||||||
notify_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "context.hpp"
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
void handle_error(const sol::protected_function_result& result);
|
|
||||||
}
|
|
@ -1,174 +0,0 @@
|
|||||||
#include "std_include.hpp"
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "value_conversion.hpp"
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
event_handler::event_handler(sol::state& state)
|
|
||||||
: state_(state)
|
|
||||||
{
|
|
||||||
auto event_listener_handle_type = state.new_usertype<event_listener_handle>("event_listener_handle");
|
|
||||||
|
|
||||||
event_listener_handle_type["clear"] = [this](const event_listener_handle& handle)
|
|
||||||
{
|
|
||||||
this->remove(handle);
|
|
||||||
};
|
|
||||||
|
|
||||||
event_listener_handle_type["endon"] = [this](const event_listener_handle& handle, const entity& entity, const std::string& event)
|
|
||||||
{
|
|
||||||
this->add_endon_condition(handle, entity, event);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void event_handler::dispatch(const event& event)
|
|
||||||
{
|
|
||||||
bool has_built_arguments = false;
|
|
||||||
event_arguments arguments{};
|
|
||||||
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
this->merge_callbacks();
|
|
||||||
this->handle_endon_conditions(event);
|
|
||||||
|
|
||||||
for (auto i = tasks.begin(); i != tasks.end();)
|
|
||||||
{
|
|
||||||
if (i->event != event.name || i->entity != event.entity)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!i->is_deleted)
|
|
||||||
{
|
|
||||||
if(!has_built_arguments)
|
|
||||||
{
|
|
||||||
has_built_arguments = true;
|
|
||||||
arguments = this->build_arguments(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_error(i->callback(sol::as_args(arguments)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i->is_volatile || i->is_deleted)
|
|
||||||
{
|
|
||||||
i = tasks.erase(i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
event_listener_handle event_handler::add_event_listener(event_listener&& listener)
|
|
||||||
{
|
|
||||||
const uint64_t id = ++this->current_listener_id_;
|
|
||||||
listener.id = id;
|
|
||||||
listener.is_deleted = false;
|
|
||||||
|
|
||||||
new_callbacks_.access([&listener](task_list& tasks)
|
|
||||||
{
|
|
||||||
tasks.emplace_back(std::move(listener));
|
|
||||||
});
|
|
||||||
|
|
||||||
return {id};
|
|
||||||
}
|
|
||||||
|
|
||||||
void event_handler::add_endon_condition(const event_listener_handle& handle, const entity& entity,
|
|
||||||
const std::string& event)
|
|
||||||
{
|
|
||||||
auto merger = [&](task_list& tasks)
|
|
||||||
{
|
|
||||||
for(auto& task : tasks)
|
|
||||||
{
|
|
||||||
if(task.id == handle.id)
|
|
||||||
{
|
|
||||||
task.endon_conditions.emplace_back(entity, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
merger(tasks);
|
|
||||||
new_callbacks_.access(merger);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void event_handler::clear()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
new_tasks.clear();
|
|
||||||
tasks.clear();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void event_handler::remove(const event_listener_handle& handle)
|
|
||||||
{
|
|
||||||
auto mask_as_deleted = [&](task_list& tasks)
|
|
||||||
{
|
|
||||||
for (auto& task : tasks)
|
|
||||||
{
|
|
||||||
if (task.id == handle.id)
|
|
||||||
{
|
|
||||||
task.is_deleted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callbacks_.access(mask_as_deleted);
|
|
||||||
new_callbacks_.access(mask_as_deleted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void event_handler::merge_callbacks()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()),
|
|
||||||
std::move_iterator(new_tasks.end()));
|
|
||||||
new_tasks = {};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void event_handler::handle_endon_conditions(const event& event)
|
|
||||||
{
|
|
||||||
auto deleter = [&](task_list& tasks)
|
|
||||||
{
|
|
||||||
for(auto& task : tasks)
|
|
||||||
{
|
|
||||||
for(auto& condition : task.endon_conditions)
|
|
||||||
{
|
|
||||||
if(condition.first == event.entity && condition.second == event.name)
|
|
||||||
{
|
|
||||||
task.is_deleted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callbacks_.access(deleter);
|
|
||||||
}
|
|
||||||
|
|
||||||
event_arguments event_handler::build_arguments(const event& event) const
|
|
||||||
{
|
|
||||||
event_arguments arguments;
|
|
||||||
|
|
||||||
for (const auto& argument : event.arguments)
|
|
||||||
{
|
|
||||||
arguments.emplace_back(convert(this->state_, argument));
|
|
||||||
}
|
|
||||||
|
|
||||||
return arguments;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
using event_arguments = std::vector<sol::lua_value>;
|
|
||||||
using event_callback = sol::protected_function;
|
|
||||||
|
|
||||||
class event_listener_handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint64_t id = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class event_listener final : public event_listener_handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::string event = {};
|
|
||||||
entity entity{};
|
|
||||||
event_callback callback = {};
|
|
||||||
bool is_volatile = false;
|
|
||||||
bool is_deleted = false;
|
|
||||||
std::vector<std::pair<scripting::entity, std::string>> endon_conditions{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class event_handler final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
event_handler(sol::state& state);
|
|
||||||
|
|
||||||
event_handler(event_handler&&) noexcept = delete;
|
|
||||||
event_handler& operator=(event_handler&&) noexcept = delete;
|
|
||||||
|
|
||||||
event_handler(const scheduler&) = delete;
|
|
||||||
event_handler& operator=(const event_handler&) = delete;
|
|
||||||
|
|
||||||
void dispatch(const event& event);
|
|
||||||
|
|
||||||
event_listener_handle add_event_listener(event_listener&& listener);
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
private:
|
|
||||||
sol::state& state_;
|
|
||||||
std::atomic_int64_t current_listener_id_ = 0;
|
|
||||||
|
|
||||||
using task_list = std::vector<event_listener>;
|
|
||||||
utils::concurrency::container<task_list> new_callbacks_;
|
|
||||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
|
||||||
|
|
||||||
void remove(const event_listener_handle& handle);
|
|
||||||
void merge_callbacks();
|
|
||||||
void handle_endon_conditions(const event& event);
|
|
||||||
|
|
||||||
void add_endon_condition(const event_listener_handle& handle, const entity& entity, const std::string& event);
|
|
||||||
|
|
||||||
event_arguments build_arguments(const event& event) const;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,171 +0,0 @@
|
|||||||
#include "std_include.hpp"
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
scheduler::scheduler(sol::state& state)
|
|
||||||
{
|
|
||||||
auto task_handle_type = state.new_usertype<task_handle>("task_handle");
|
|
||||||
|
|
||||||
task_handle_type["clear"] = [this](const task_handle& handle)
|
|
||||||
{
|
|
||||||
this->remove(handle);
|
|
||||||
};
|
|
||||||
|
|
||||||
task_handle_type["endon"] = [this](const task_handle& handle, const entity& entity, const std::string& event)
|
|
||||||
{
|
|
||||||
this->add_endon_condition(handle, entity, event);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::dispatch(const event& event)
|
|
||||||
{
|
|
||||||
auto deleter = [&](task_list& tasks)
|
|
||||||
{
|
|
||||||
for(auto& task : tasks)
|
|
||||||
{
|
|
||||||
for(auto& condition : task.endon_conditions)
|
|
||||||
{
|
|
||||||
if(condition.first == event.entity && condition.second == event.name)
|
|
||||||
{
|
|
||||||
task.is_deleted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
deleter(tasks);
|
|
||||||
new_callbacks_.access(deleter);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::run_frame()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
this->merge_callbacks();
|
|
||||||
|
|
||||||
for (auto i = tasks.begin(); i != tasks.end();)
|
|
||||||
{
|
|
||||||
const auto now = std::chrono::high_resolution_clock::now();
|
|
||||||
const auto diff = now - i->last_call;
|
|
||||||
|
|
||||||
if (diff < i->delay)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
i->last_call = now;
|
|
||||||
|
|
||||||
if (!i->is_deleted)
|
|
||||||
{
|
|
||||||
handle_error(i->callback());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i->is_volatile || i->is_deleted)
|
|
||||||
{
|
|
||||||
i = tasks.erase(i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::clear()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
new_tasks.clear();
|
|
||||||
tasks.clear();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
task_handle scheduler::add(const sol::protected_function& callback, const long long milliseconds,
|
|
||||||
const bool is_volatile)
|
|
||||||
{
|
|
||||||
return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile);
|
|
||||||
}
|
|
||||||
|
|
||||||
task_handle scheduler::add(const sol::protected_function& callback, const std::chrono::milliseconds delay,
|
|
||||||
const bool is_volatile)
|
|
||||||
{
|
|
||||||
const uint64_t id = ++this->current_task_id_;
|
|
||||||
|
|
||||||
task task;
|
|
||||||
task.is_volatile = is_volatile;
|
|
||||||
task.callback = callback;
|
|
||||||
task.delay = delay;
|
|
||||||
task.last_call = std::chrono::steady_clock::now();
|
|
||||||
task.id = id;
|
|
||||||
task.is_deleted = false;
|
|
||||||
|
|
||||||
new_callbacks_.access([&task](task_list& tasks)
|
|
||||||
{
|
|
||||||
tasks.emplace_back(std::move(task));
|
|
||||||
});
|
|
||||||
|
|
||||||
return {id};
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::add_endon_condition(const task_handle& handle, const entity& entity, const std::string& event)
|
|
||||||
{
|
|
||||||
auto merger = [&](task_list& tasks)
|
|
||||||
{
|
|
||||||
for(auto& task : tasks)
|
|
||||||
{
|
|
||||||
if(task.id == handle.id)
|
|
||||||
{
|
|
||||||
task.endon_conditions.emplace_back(entity, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
merger(tasks);
|
|
||||||
new_callbacks_.access(merger);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::remove(const task_handle& handle)
|
|
||||||
{
|
|
||||||
auto mask_as_deleted = [&](task_list& tasks)
|
|
||||||
{
|
|
||||||
for (auto& task : tasks)
|
|
||||||
{
|
|
||||||
if (task.id == handle.id)
|
|
||||||
{
|
|
||||||
task.is_deleted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callbacks_.access(mask_as_deleted);
|
|
||||||
new_callbacks_.access(mask_as_deleted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::merge_callbacks()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
|
||||||
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
|
||||||
new_tasks = {};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <utils/concurrency.hpp>
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
class context;
|
|
||||||
|
|
||||||
class task_handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint64_t id = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class task final : public task_handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::chrono::steady_clock::time_point last_call{};
|
|
||||||
sol::protected_function callback{};
|
|
||||||
std::chrono::milliseconds delay{};
|
|
||||||
bool is_volatile = false;
|
|
||||||
bool is_deleted = false;
|
|
||||||
std::vector<std::pair<entity, std::string>> endon_conditions{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class scheduler final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
scheduler(sol::state& state);
|
|
||||||
|
|
||||||
scheduler(scheduler&&) noexcept = delete;
|
|
||||||
scheduler& operator=(scheduler&&) noexcept = delete;
|
|
||||||
|
|
||||||
scheduler(const scheduler&) = delete;
|
|
||||||
scheduler& operator=(const scheduler&) = delete;
|
|
||||||
|
|
||||||
void dispatch(const event& event);
|
|
||||||
void run_frame();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
task_handle add(const sol::protected_function& callback, long long milliseconds, bool is_volatile);
|
|
||||||
task_handle add(const sol::protected_function& callback, std::chrono::milliseconds delay, bool is_volatile);
|
|
||||||
|
|
||||||
private:
|
|
||||||
using task_list = std::vector<task>;
|
|
||||||
utils::concurrency::container<task_list> new_callbacks_;
|
|
||||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
|
||||||
std::atomic_int64_t current_task_id_ = 0;
|
|
||||||
|
|
||||||
void add_endon_condition(const task_handle& handle, const entity& entity, const std::string& event);
|
|
||||||
|
|
||||||
void remove(const task_handle& handle);
|
|
||||||
void merge_callbacks();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,319 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "value_conversion.hpp"
|
|
||||||
#include "../functions.hpp"
|
|
||||||
#include "../execution.hpp"
|
|
||||||
#include ".../../component/logfile.hpp"
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
struct array_value
|
|
||||||
{
|
|
||||||
int index;
|
|
||||||
script_value value;
|
|
||||||
};
|
|
||||||
|
|
||||||
sol::lua_value entity_to_array(lua_State* state, unsigned int id)
|
|
||||||
{
|
|
||||||
auto table = sol::table::create(state);
|
|
||||||
auto metatable = sol::table::create(state);
|
|
||||||
|
|
||||||
std::unordered_map<std::string, array_value> values;
|
|
||||||
|
|
||||||
const auto offset = 0xA000 * (id & 3);
|
|
||||||
|
|
||||||
auto current = game::scr_VarGlob->objectVariableChildren[id].firstChild;
|
|
||||||
auto idx = 1;
|
|
||||||
|
|
||||||
for (auto i = offset + current; current; i = offset + current)
|
|
||||||
{
|
|
||||||
const auto var = game::scr_VarGlob->childVariableValue[i];
|
|
||||||
|
|
||||||
if (var.type == game::SCRIPT_NONE)
|
|
||||||
{
|
|
||||||
current = var.nextSibling;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto string_value = (game::scr_string_t)((unsigned __int8)var.name_lo + (var.k.keys.name_hi << 8));
|
|
||||||
const auto* str = game::SL_ConvertToString(string_value);
|
|
||||||
|
|
||||||
std::string key = string_value < 0x40000 && str
|
|
||||||
? str
|
|
||||||
: std::to_string(idx++);
|
|
||||||
|
|
||||||
game::VariableValue variable{};
|
|
||||||
variable.type = var.type;
|
|
||||||
variable.u = var.u.u;
|
|
||||||
|
|
||||||
array_value value;
|
|
||||||
value.index = i;
|
|
||||||
value.value = variable;
|
|
||||||
|
|
||||||
values[key] = value;
|
|
||||||
|
|
||||||
current = var.nextSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
table["getkeys"] = [values]()
|
|
||||||
{
|
|
||||||
std::vector<std::string> _keys;
|
|
||||||
|
|
||||||
for (const auto& entry : values)
|
|
||||||
{
|
|
||||||
_keys.push_back(entry.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _keys;
|
|
||||||
};
|
|
||||||
|
|
||||||
metatable[sol::meta_function::new_index] = [values](const sol::table t, const sol::this_state s,
|
|
||||||
const sol::lua_value& key_value, const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
const auto key = key_value.is<int>()
|
|
||||||
? std::to_string(key_value.as<int>())
|
|
||||||
: key_value.as<std::string>();
|
|
||||||
|
|
||||||
if (values.find(key) == values.end())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto i = values.at(key).index;
|
|
||||||
const auto variable = &game::scr_VarGlob->childVariableValue[i];
|
|
||||||
|
|
||||||
const auto new_variable = convert({s, value}).get_raw();
|
|
||||||
|
|
||||||
game::AddRefToValue(new_variable.type, new_variable.u);
|
|
||||||
game::RemoveRefToValue(variable->type, variable->u.u);
|
|
||||||
|
|
||||||
variable->type = (char)new_variable.type;
|
|
||||||
variable->u.u = new_variable.u;
|
|
||||||
};
|
|
||||||
|
|
||||||
metatable[sol::meta_function::index] = [values](const sol::table t, const sol::this_state s,
|
|
||||||
const sol::lua_value& key_value)
|
|
||||||
{
|
|
||||||
const auto key = key_value.is<int>()
|
|
||||||
? std::to_string(key_value.as<int>())
|
|
||||||
: key_value.as<std::string>();
|
|
||||||
|
|
||||||
if (values.find(key) == values.end())
|
|
||||||
{
|
|
||||||
return sol::lua_value{s, sol::lua_nil};
|
|
||||||
}
|
|
||||||
|
|
||||||
return convert(s, values.at(key).value);
|
|
||||||
};
|
|
||||||
|
|
||||||
metatable[sol::meta_function::length] = [values]()
|
|
||||||
{
|
|
||||||
return values.size();
|
|
||||||
};
|
|
||||||
|
|
||||||
table[sol::metatable_key] = metatable;
|
|
||||||
|
|
||||||
return {state, table};
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
game::VariableValue func;
|
|
||||||
func.type = game::SCRIPT_FUNCTION;
|
|
||||||
func.u.codePosValue = index;
|
|
||||||
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sol::lua_value entity_to_struct(lua_State* state, unsigned int parent_id)
|
|
||||||
{
|
|
||||||
auto table = sol::table::create(state);
|
|
||||||
auto metatable = sol::table::create(state);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
const auto id = field.is<std::string>()
|
|
||||||
? scripting::find_token_id(field.as<std::string>())
|
|
||||||
: field.as<int>();
|
|
||||||
|
|
||||||
if (!id)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto variable_id = game::GetVariable(parent_id, id);
|
|
||||||
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + offset];
|
|
||||||
const auto new_variable = convert({s, value}).get_raw();
|
|
||||||
|
|
||||||
game::AddRefToValue(new_variable.type, new_variable.u);
|
|
||||||
game::RemoveRefToValue(variable->type, variable->u.u);
|
|
||||||
|
|
||||||
variable->type = (char)new_variable.type;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
const auto id = field.is<std::string>()
|
|
||||||
? scripting::find_token_id(field.as<std::string>())
|
|
||||||
: field.as<int>();
|
|
||||||
|
|
||||||
if (!id)
|
|
||||||
{
|
|
||||||
return sol::lua_value{s, sol::lua_nil};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto variable_id = game::FindVariable(parent_id, id);
|
|
||||||
if (!variable_id)
|
|
||||||
{
|
|
||||||
return sol::lua_value{s, sol::lua_nil};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto variable = game::scr_VarGlob->childVariableValue[variable_id + offset];
|
|
||||||
|
|
||||||
game::VariableValue result{};
|
|
||||||
result.u = variable.u.u;
|
|
||||||
result.type = (game::scriptType_e)variable.type;
|
|
||||||
|
|
||||||
return convert(s, result);
|
|
||||||
};
|
|
||||||
|
|
||||||
table[sol::metatable_key] = metatable;
|
|
||||||
|
|
||||||
return {state, table};
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value convert(const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
if (value.is<int>())
|
|
||||||
{
|
|
||||||
return {value.as<int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<unsigned int>())
|
|
||||||
{
|
|
||||||
return {value.as<unsigned int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<bool>())
|
|
||||||
{
|
|
||||||
return {value.as<bool>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<double>())
|
|
||||||
{
|
|
||||||
return {value.as<double>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<float>())
|
|
||||||
{
|
|
||||||
return {value.as<float>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::string>())
|
|
||||||
{
|
|
||||||
return {value.as<std::string>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<entity>())
|
|
||||||
{
|
|
||||||
return {value.as<entity>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<vector>())
|
|
||||||
{
|
|
||||||
return {value.as<vector>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<sol::protected_function>())
|
|
||||||
{
|
|
||||||
return convert_function(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
sol::lua_value convert(lua_State* state, const script_value& value)
|
|
||||||
{
|
|
||||||
if (value.is<int>())
|
|
||||||
{
|
|
||||||
return {state, value.as<int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<float>())
|
|
||||||
{
|
|
||||||
return {state, value.as<float>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::string>())
|
|
||||||
{
|
|
||||||
return {state, value.as<std::string>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::map<std::string, script_value>>())
|
|
||||||
{
|
|
||||||
return entity_to_struct(state, value.get_raw().u.uintValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::vector<script_value>>())
|
|
||||||
{
|
|
||||||
return entity_to_array(state, value.get_raw().u.uintValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::function<void()>>())
|
|
||||||
{
|
|
||||||
return convert_function(state, value.get_raw().u.codePosValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<entity>())
|
|
||||||
{
|
|
||||||
return {state, value.as<entity>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<vector>())
|
|
||||||
{
|
|
||||||
return {state, value.as<vector>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {state, sol::lua_nil};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "context.hpp"
|
|
||||||
|
|
||||||
namespace scripting::lua
|
|
||||||
{
|
|
||||||
sol::lua_value entity_to_struct(lua_State* state, unsigned int parent_id);
|
|
||||||
|
|
||||||
script_value convert(const sol::lua_value& value);
|
|
||||||
sol::lua_value convert(lua_State* state, const script_value& value);
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "safe_execution.hpp"
|
|
||||||
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable: 4611)
|
|
||||||
|
|
||||||
namespace scripting::safe_execution
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
bool execute_with_seh(const script_function function, const game::scr_entref_t& entref)
|
|
||||||
{
|
|
||||||
__try
|
|
||||||
{
|
|
||||||
function(entref);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool call(const script_function function, const game::scr_entref_t& entref)
|
|
||||||
{
|
|
||||||
*game::g_script_error_level += 1;
|
|
||||||
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level]))
|
|
||||||
{
|
|
||||||
*game::g_script_error_level -= 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto result = execute_with_seh(function, entref);
|
|
||||||
*game::g_script_error_level -= 1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set_entity_field(const game::scr_entref_t& entref, const int offset)
|
|
||||||
{
|
|
||||||
*game::g_script_error_level += 1;
|
|
||||||
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level]))
|
|
||||||
{
|
|
||||||
*game::g_script_error_level -= 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::Scr_SetObjectField(entref.classnum, entref.entnum, offset);
|
|
||||||
|
|
||||||
*game::g_script_error_level -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_entity_field(const game::scr_entref_t& entref, const int offset, game::VariableValue* value)
|
|
||||||
{
|
|
||||||
*game::g_script_error_level += 1;
|
|
||||||
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level]))
|
|
||||||
{
|
|
||||||
value->type = game::SCRIPT_NONE;
|
|
||||||
value->u.intValue = 0;
|
|
||||||
*game::g_script_error_level -= 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::GetEntityFieldValue(value, entref.classnum, entref.entnum, offset);
|
|
||||||
|
|
||||||
*game::g_script_error_level -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning(pop)
|
|
@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "functions.hpp"
|
|
||||||
|
|
||||||
namespace scripting::safe_execution
|
|
||||||
{
|
|
||||||
bool call(script_function function, const game::scr_entref_t& entref);
|
|
||||||
|
|
||||||
bool set_entity_field(const game::scr_entref_t& entref, int offset);
|
|
||||||
bool get_entity_field(const game::scr_entref_t& entref, int offset, game::VariableValue* value);
|
|
||||||
}
|
|
@ -1,278 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "script_value.hpp"
|
|
||||||
#include "entity.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
/***************************************************************
|
|
||||||
* Constructors
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
script_value::script_value(const game::VariableValue& value)
|
|
||||||
: value_(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const int value)
|
|
||||||
{
|
|
||||||
game::VariableValue variable{};
|
|
||||||
variable.type = game::SCRIPT_INTEGER;
|
|
||||||
variable.u.intValue = value;
|
|
||||||
|
|
||||||
this->value_ = variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const unsigned int value)
|
|
||||||
{
|
|
||||||
game::VariableValue variable{};
|
|
||||||
variable.type = game::SCRIPT_INTEGER;
|
|
||||||
variable.u.uintValue = value;
|
|
||||||
|
|
||||||
this->value_ = variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const bool value)
|
|
||||||
: script_value(static_cast<unsigned>(value))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const float value)
|
|
||||||
{
|
|
||||||
game::VariableValue variable{};
|
|
||||||
variable.type = game::SCRIPT_FLOAT;
|
|
||||||
variable.u.floatValue = value;
|
|
||||||
|
|
||||||
this->value_ = variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const double value)
|
|
||||||
: script_value(static_cast<float>(value))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const char* value)
|
|
||||||
{
|
|
||||||
game::VariableValue variable{};
|
|
||||||
variable.type = game::SCRIPT_STRING;
|
|
||||||
variable.u.stringValue = game::SL_GetString(value, 0);
|
|
||||||
|
|
||||||
const auto _ = gsl::finally([&variable]()
|
|
||||||
{
|
|
||||||
game::RemoveRefToValue(variable.type, variable.u);
|
|
||||||
});
|
|
||||||
|
|
||||||
this->value_ = variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const std::string& value)
|
|
||||||
: script_value(value.data())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const entity& value)
|
|
||||||
{
|
|
||||||
game::VariableValue variable{};
|
|
||||||
variable.type = game::SCRIPT_OBJECT;
|
|
||||||
variable.u.pointerValue = value.get_entity_id();
|
|
||||||
|
|
||||||
this->value_ = variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const vector& value)
|
|
||||||
{
|
|
||||||
game::VariableValue variable{};
|
|
||||||
variable.type = game::SCRIPT_VECTOR;
|
|
||||||
variable.u.vectorValue = game::Scr_AllocVector(value);
|
|
||||||
|
|
||||||
const auto _ = gsl::finally([&variable]()
|
|
||||||
{
|
|
||||||
game::RemoveRefToValue(variable.type, variable.u);
|
|
||||||
});
|
|
||||||
|
|
||||||
this->value_ = variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Integer
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<int>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().type == game::SCRIPT_INTEGER;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<unsigned int>() const
|
|
||||||
{
|
|
||||||
return this->is<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<bool>() const
|
|
||||||
{
|
|
||||||
return this->is<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
int script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().u.intValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
unsigned int script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().u.uintValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().u.uintValue != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Float
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<float>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().type == game::SCRIPT_FLOAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<double>() const
|
|
||||||
{
|
|
||||||
return this->is<float>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
float script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().u.floatValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
double script_value::get() const
|
|
||||||
{
|
|
||||||
return static_cast<double>(this->get_raw().u.floatValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* String
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<const char*>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().type == game::SCRIPT_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<std::string>() const
|
|
||||||
{
|
|
||||||
return this->is<const char*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
const char* script_value::get() const
|
|
||||||
{
|
|
||||||
return game::SL_ConvertToString(static_cast<game::scr_string_t>(this->get_raw().u.stringValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get<const char*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Array
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<std::vector<script_value>>() const
|
|
||||||
{
|
|
||||||
if (this->get_raw().type != game::SCRIPT_OBJECT)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto id = this->get_raw().u.uintValue;
|
|
||||||
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
|
|
||||||
|
|
||||||
return type == game::SCRIPT_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Struct
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<std::map<std::string, script_value>>() const
|
|
||||||
{
|
|
||||||
if (this->get_raw().type != game::SCRIPT_OBJECT)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto id = this->get_raw().u.uintValue;
|
|
||||||
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
|
|
||||||
|
|
||||||
return type == game::SCRIPT_STRUCT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Function
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<std::function<void()>>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().type == game::SCRIPT_FUNCTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Entity
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<entity>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().type == game::SCRIPT_OBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
entity script_value::get() const
|
|
||||||
{
|
|
||||||
return entity(this->get_raw().u.pointerValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Vector
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<vector>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().type == game::SCRIPT_VECTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
vector script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().u.vectorValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
*
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
const game::VariableValue& script_value::get_raw() const
|
|
||||||
{
|
|
||||||
return this->value_.get();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "variable_value.hpp"
|
|
||||||
#include "vector.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
class entity;
|
|
||||||
|
|
||||||
class script_value
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
script_value() = default;
|
|
||||||
script_value(const game::VariableValue& value);
|
|
||||||
|
|
||||||
script_value(int value);
|
|
||||||
script_value(unsigned int value);
|
|
||||||
script_value(bool value);
|
|
||||||
|
|
||||||
script_value(float value);
|
|
||||||
script_value(double value);
|
|
||||||
|
|
||||||
script_value(const char* value);
|
|
||||||
script_value(const std::string& value);
|
|
||||||
|
|
||||||
script_value(const entity& value);
|
|
||||||
|
|
||||||
script_value(const vector& value);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool is() const;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T as() const
|
|
||||||
{
|
|
||||||
if (!this->is<T>())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid type");
|
|
||||||
}
|
|
||||||
|
|
||||||
return get<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
const game::VariableValue& get_raw() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename T>
|
|
||||||
T get() const;
|
|
||||||
|
|
||||||
variable_value value_{};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "stack_isolation.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
stack_isolation::stack_isolation()
|
|
||||||
{
|
|
||||||
this->in_param_count_ = game::scr_VmPub->inparamcount;
|
|
||||||
this->out_param_count_ = game::scr_VmPub->outparamcount;
|
|
||||||
this->top_ = game::scr_VmPub->top;
|
|
||||||
this->max_stack_ = game::scr_VmPub->maxstack;
|
|
||||||
|
|
||||||
game::scr_VmPub->top = this->stack_;
|
|
||||||
game::scr_VmPub->maxstack = &this->stack_[ARRAYSIZE(this->stack_) - 1];
|
|
||||||
game::scr_VmPub->inparamcount = 0;
|
|
||||||
game::scr_VmPub->outparamcount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack_isolation::~stack_isolation()
|
|
||||||
{
|
|
||||||
game::Scr_ClearOutParams();
|
|
||||||
game::scr_VmPub->inparamcount = this->in_param_count_;
|
|
||||||
game::scr_VmPub->outparamcount = this->out_param_count_;
|
|
||||||
game::scr_VmPub->top = this->top_;
|
|
||||||
game::scr_VmPub->maxstack = this->max_stack_;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
class stack_isolation final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
stack_isolation();
|
|
||||||
~stack_isolation();
|
|
||||||
|
|
||||||
stack_isolation(stack_isolation&&) = delete;
|
|
||||||
stack_isolation(const stack_isolation&) = delete;
|
|
||||||
stack_isolation& operator=(stack_isolation&&) = delete;
|
|
||||||
stack_isolation& operator=(const stack_isolation&) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
game::VariableValue stack_[512]{};
|
|
||||||
|
|
||||||
game::VariableValue* max_stack_;
|
|
||||||
game::VariableValue* top_;
|
|
||||||
unsigned int in_param_count_;
|
|
||||||
unsigned int out_param_count_;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "variable_value.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
variable_value::variable_value(const game::VariableValue& value)
|
|
||||||
{
|
|
||||||
this->assign(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
variable_value::variable_value(const variable_value& other) noexcept
|
|
||||||
{
|
|
||||||
this->operator=(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
variable_value::variable_value(variable_value&& other) noexcept
|
|
||||||
{
|
|
||||||
this->operator=(std::move(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
variable_value& variable_value::operator=(const variable_value& other) noexcept
|
|
||||||
{
|
|
||||||
if (this != &other)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->assign(other.value_);
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable_value& variable_value::operator=(variable_value&& other) noexcept
|
|
||||||
{
|
|
||||||
if (this != &other)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->value_ = other.value_;
|
|
||||||
other.value_.type = game::SCRIPT_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable_value::~variable_value()
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
}
|
|
||||||
|
|
||||||
const game::VariableValue& variable_value::get() const
|
|
||||||
{
|
|
||||||
return this->value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void variable_value::assign(const game::VariableValue& value)
|
|
||||||
{
|
|
||||||
this->value_ = value;
|
|
||||||
game::AddRefToValue(this->value_.type, this->value_.u);
|
|
||||||
}
|
|
||||||
|
|
||||||
void variable_value::release()
|
|
||||||
{
|
|
||||||
if (this->value_.type != game::SCRIPT_NONE)
|
|
||||||
{
|
|
||||||
game::RemoveRefToValue(this->value_.type, this->value_.u);
|
|
||||||
this->value_.type = game::SCRIPT_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
class variable_value
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
variable_value() = default;
|
|
||||||
variable_value(const game::VariableValue& value);
|
|
||||||
variable_value(const variable_value& other) noexcept;
|
|
||||||
variable_value(variable_value&& other) noexcept;
|
|
||||||
|
|
||||||
variable_value& operator=(const variable_value& other) noexcept;
|
|
||||||
variable_value& operator=(variable_value&& other) noexcept;
|
|
||||||
|
|
||||||
~variable_value();
|
|
||||||
|
|
||||||
const game::VariableValue& get() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void assign(const game::VariableValue& value);
|
|
||||||
void release();
|
|
||||||
|
|
||||||
game::VariableValue value_{{0}, game::SCRIPT_NONE};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "vector.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
vector::vector(const float* value)
|
|
||||||
{
|
|
||||||
for (auto i = 0; i < 3; ++i)
|
|
||||||
{
|
|
||||||
this->value_[i] = value[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vector::vector(const game::vec3_t& value)
|
|
||||||
: vector(&value[0])
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
vector::vector(const float x, const float y, const float z)
|
|
||||||
{
|
|
||||||
this->value_[0] = x;
|
|
||||||
this->value_[1] = y;
|
|
||||||
this->value_[2] = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector::operator game::vec3_t&()
|
|
||||||
{
|
|
||||||
return this->value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector::operator const game::vec3_t&() const
|
|
||||||
{
|
|
||||||
return this->value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::vec_t& vector::operator[](const size_t i)
|
|
||||||
{
|
|
||||||
if (i >= 3)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Out of bounds.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->value_[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
const game::vec_t& vector::operator[](const size_t i) const
|
|
||||||
{
|
|
||||||
if (i >= 3)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Out of bounds.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->value_[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
float vector::get_x() const
|
|
||||||
{
|
|
||||||
return this->operator[](0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float vector::get_y() const
|
|
||||||
{
|
|
||||||
return this->operator[](1);
|
|
||||||
}
|
|
||||||
|
|
||||||
float vector::get_z() const
|
|
||||||
{
|
|
||||||
return this->operator[](2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void vector::set_x(const float value)
|
|
||||||
{
|
|
||||||
this->operator[](0) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vector::set_y(const float value)
|
|
||||||
{
|
|
||||||
this->operator[](1) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vector::set_z(const float value)
|
|
||||||
{
|
|
||||||
this->operator[](2) = value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
namespace scripting
|
|
||||||
{
|
|
||||||
class vector final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
vector() = default;
|
|
||||||
vector(const float* value);
|
|
||||||
vector(const game::vec3_t& value);
|
|
||||||
vector(float x, float y, float z);
|
|
||||||
|
|
||||||
operator game::vec3_t&();
|
|
||||||
operator const game::vec3_t&() const;
|
|
||||||
|
|
||||||
game::vec_t& operator[](size_t i);
|
|
||||||
const game::vec_t& operator[](size_t i) const;
|
|
||||||
|
|
||||||
float get_x() const;
|
|
||||||
float get_y() const;
|
|
||||||
float get_z() const;
|
|
||||||
|
|
||||||
void set_x(float value);
|
|
||||||
void set_y(float value);
|
|
||||||
void set_z(float value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
game::vec3_t value_{0};
|
|
||||||
};
|
|
||||||
}
|
|
@ -27,6 +27,8 @@ namespace game
|
|||||||
|
|
||||||
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x9D0F0};
|
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x9D0F0};
|
||||||
|
|
||||||
|
WEAK symbol<DWOnlineStatus()> dwGetLogOnStatus{ 0, 0x14053CCB0 }; // S1
|
||||||
|
|
||||||
WEAK symbol<void(unsigned int weapon, bool isAlternate,
|
WEAK symbol<void(unsigned int weapon, bool isAlternate,
|
||||||
char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x430550, 0x1F9670};
|
char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x430550, 0x1F9670};
|
||||||
|
|
||||||
|
@ -1,161 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "execution.hpp"
|
|
||||||
#include "component/ui_scripting.hpp"
|
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
|
|
||||||
namespace ui_scripting
|
|
||||||
{
|
|
||||||
void push_value(const script_value& value)
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
const auto value_ = value.get_raw();
|
|
||||||
*state->m_apistack.top = value_;
|
|
||||||
state->m_apistack.top++;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value get_return_value(int offset)
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
return state->m_apistack.top[-1 - offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments get_return_values(int count)
|
|
||||||
{
|
|
||||||
arguments values;
|
|
||||||
|
|
||||||
for (auto i = count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
values.push_back(get_return_value(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.size() == 0)
|
|
||||||
{
|
|
||||||
values.push_back({});
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments call_script_function(const function& function, const arguments& arguments)
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
push_value(function);
|
|
||||||
for (auto i = arguments.begin(); i != arguments.end(); ++i)
|
|
||||||
{
|
|
||||||
push_value(*i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0);
|
|
||||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
|
||||||
return get_return_values(count);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("Error executing script function: ") + e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value get_field(const userdata& self, const script_value& key)
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
push_value(key);
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
game::hks::HksObject value{};
|
|
||||||
game::hks::HksObject userdata{};
|
|
||||||
userdata.t = game::hks::TUSERDATA;
|
|
||||||
userdata.v.ptr = self.ptr;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("Error getting userdata field: ") + e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value get_field(const table& self, const script_value& key)
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
push_value(key);
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
game::hks::HksObject value{};
|
|
||||||
game::hks::HksObject userdata{};
|
|
||||||
userdata.t = game::hks::TTABLE;
|
|
||||||
userdata.v.ptr = self.ptr;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("Error getting table field: ") + e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_field(const userdata& self, const script_value& key, const script_value& value)
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
game::hks::HksObject userdata{};
|
|
||||||
userdata.t = game::hks::TUSERDATA;
|
|
||||||
userdata.v.ptr = self.ptr;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("Error setting userdata field: ") + e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_field(const table& self, const script_value& key, const script_value& value)
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
game::hks::HksObject userdata{};
|
|
||||||
userdata.t = game::hks::TTABLE;
|
|
||||||
userdata.v.ptr = self.ptr;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("Error setting table field: ") + e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "types.hpp"
|
|
||||||
#include "script_value.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting
|
|
||||||
{
|
|
||||||
void push_value(const script_value& value);
|
|
||||||
script_value get_return_value(int offset);
|
|
||||||
arguments get_return_values(int count);
|
|
||||||
|
|
||||||
arguments call_script_function(const function& function, const arguments& arguments);
|
|
||||||
|
|
||||||
script_value get_field(const userdata& self, const script_value& key);
|
|
||||||
script_value get_field(const table& self, const script_value& key);
|
|
||||||
void set_field(const userdata& self, const script_value& key, const script_value& value);
|
|
||||||
void set_field(const table& self, const script_value& key, const script_value& value);
|
|
||||||
}
|
|
@ -1,239 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "value_conversion.hpp"
|
|
||||||
#include "../script_value.hpp"
|
|
||||||
#include "../execution.hpp"
|
|
||||||
|
|
||||||
#include "../../../component/ui_scripting.hpp"
|
|
||||||
#include "../../../component/command.hpp"
|
|
||||||
#include "../../../component/updater.hpp"
|
|
||||||
|
|
||||||
#include "component/game_console.hpp"
|
|
||||||
#include "component/scheduler.hpp"
|
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
#include <utils/nt.hpp>
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
void setup_types(sol::state& state, scheduler& scheduler)
|
|
||||||
{
|
|
||||||
struct game
|
|
||||||
{
|
|
||||||
};
|
|
||||||
auto game_type = state.new_usertype<game>("game_");
|
|
||||||
state["game"] = game();
|
|
||||||
|
|
||||||
game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback,
|
|
||||||
const long long milliseconds)
|
|
||||||
{
|
|
||||||
return scheduler.add(callback, milliseconds, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback,
|
|
||||||
const long long milliseconds)
|
|
||||||
{
|
|
||||||
return scheduler.add(callback, milliseconds, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto userdata_type = state.new_usertype<userdata>("userdata_");
|
|
||||||
|
|
||||||
userdata_type["new"] = sol::property(
|
|
||||||
[](const userdata& userdata, const sol::this_state s)
|
|
||||||
{
|
|
||||||
return convert(s, userdata.get("new"));
|
|
||||||
},
|
|
||||||
[](const userdata& userdata, const sol::this_state s, const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
userdata.set("new", convert({s, value}));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
userdata_type["get"] = [](const userdata& userdata, const sol::this_state s,
|
|
||||||
const sol::lua_value& key)
|
|
||||||
{
|
|
||||||
return convert(s, userdata.get(convert({s, key})));
|
|
||||||
};
|
|
||||||
|
|
||||||
userdata_type["set"] = [](const userdata& userdata, const sol::this_state s,
|
|
||||||
const sol::lua_value& key, const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
userdata.set(convert({s, key}), convert({s, value}));
|
|
||||||
};
|
|
||||||
|
|
||||||
userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s,
|
|
||||||
const sol::lua_value& key)
|
|
||||||
{
|
|
||||||
return convert(s, userdata.get(convert({s, key})));
|
|
||||||
};
|
|
||||||
|
|
||||||
userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s,
|
|
||||||
const sol::lua_value& key, const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
userdata.set(convert({s, key}), convert({s, value}));
|
|
||||||
};
|
|
||||||
|
|
||||||
auto table_type = state.new_usertype<table>("table_");
|
|
||||||
|
|
||||||
table_type["new"] = sol::property(
|
|
||||||
[](const table& table, const sol::this_state s)
|
|
||||||
{
|
|
||||||
return convert(s, table.get("new"));
|
|
||||||
},
|
|
||||||
[](const table& table, const sol::this_state s, const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
table.set("new", convert({s, value}));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
table_type["get"] = [](const table& table, const sol::this_state s,
|
|
||||||
const sol::lua_value& key)
|
|
||||||
{
|
|
||||||
return convert(s, table.get(convert({s, key})));
|
|
||||||
};
|
|
||||||
|
|
||||||
table_type["set"] = [](const table& table, const sol::this_state s,
|
|
||||||
const sol::lua_value& key, const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
table.set(convert({s, key}), convert({s, value}));
|
|
||||||
};
|
|
||||||
|
|
||||||
table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s,
|
|
||||||
const sol::lua_value& key)
|
|
||||||
{
|
|
||||||
return convert(s, table.get(convert({s, key})));
|
|
||||||
};
|
|
||||||
|
|
||||||
table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s,
|
|
||||||
const sol::lua_value& key, const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
table.set(convert({s, key}), convert({s, value}));
|
|
||||||
};
|
|
||||||
|
|
||||||
auto function_type = state.new_usertype<function>("function_");
|
|
||||||
|
|
||||||
function_type[sol::meta_function::call] = [](const function& function, const sol::this_state s, sol::variadic_args va)
|
|
||||||
{
|
|
||||||
arguments arguments{};
|
|
||||||
|
|
||||||
for (auto arg : va)
|
|
||||||
{
|
|
||||||
arguments.push_back(convert({s, arg}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto values = function.call(arguments);
|
|
||||||
std::vector<sol::lua_value> returns;
|
|
||||||
|
|
||||||
for (const auto& value : values)
|
|
||||||
{
|
|
||||||
returns.push_back(convert(s, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sol::as_returns(returns);
|
|
||||||
};
|
|
||||||
|
|
||||||
state["luiglobals"] = table((*::game::hks::lua_state)->globals.v.table);
|
|
||||||
state["CoD"] = state["luiglobals"]["CoD"];
|
|
||||||
state["LUI"] = state["luiglobals"]["LUI"];
|
|
||||||
state["Engine"] = state["luiglobals"]["Engine"];
|
|
||||||
state["Game"] = state["luiglobals"]["Game"];
|
|
||||||
|
|
||||||
auto updater_table = sol::table::create(state.lua_state());
|
|
||||||
|
|
||||||
updater_table["relaunch"] = updater::relaunch;
|
|
||||||
|
|
||||||
updater_table["sethastriedupdate"] = updater::set_has_tried_update;
|
|
||||||
updater_table["gethastriedupdate"] = updater::get_has_tried_update;
|
|
||||||
updater_table["autoupdatesenabled"] = updater::auto_updates_enabled;
|
|
||||||
|
|
||||||
updater_table["startupdatecheck"] = updater::start_update_check;
|
|
||||||
updater_table["isupdatecheckdone"] = updater::is_update_check_done;
|
|
||||||
updater_table["getupdatecheckstatus"] = updater::get_update_check_status;
|
|
||||||
updater_table["isupdateavailable"] = updater::is_update_available;
|
|
||||||
|
|
||||||
updater_table["startupdatedownload"] = updater::start_update_download;
|
|
||||||
updater_table["isupdatedownloaddone"] = updater::is_update_download_done;
|
|
||||||
updater_table["getupdatedownloadstatus"] = updater::get_update_download_status;
|
|
||||||
updater_table["cancelupdate"] = updater::cancel_update;
|
|
||||||
updater_table["isrestartrequired"] = updater::is_restart_required;
|
|
||||||
|
|
||||||
updater_table["getlasterror"] = updater::get_last_error;
|
|
||||||
updater_table["getcurrentfile"] = updater::get_current_file;
|
|
||||||
|
|
||||||
state["updater"] = updater_table;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context::context(std::string data, script_type type)
|
|
||||||
: scheduler_(state_)
|
|
||||||
{
|
|
||||||
this->state_.open_libraries(sol::lib::base,
|
|
||||||
sol::lib::package,
|
|
||||||
sol::lib::io,
|
|
||||||
sol::lib::string,
|
|
||||||
sol::lib::os,
|
|
||||||
sol::lib::math,
|
|
||||||
sol::lib::table);
|
|
||||||
|
|
||||||
setup_types(this->state_, this->scheduler_);
|
|
||||||
|
|
||||||
if (type == script_type::file)
|
|
||||||
{
|
|
||||||
this->folder_ = data;
|
|
||||||
|
|
||||||
this->state_["include"] = [this](const std::string& file)
|
|
||||||
{
|
|
||||||
this->load_script(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
sol::function old_require = this->state_["require"];
|
|
||||||
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
|
|
||||||
this->state_["require"] = [base_path, old_require](const std::string& path)
|
|
||||||
{
|
|
||||||
return old_require(base_path + path);
|
|
||||||
};
|
|
||||||
|
|
||||||
this->state_["scriptdir"] = [this]()
|
|
||||||
{
|
|
||||||
return this->folder_;
|
|
||||||
};
|
|
||||||
|
|
||||||
printf("Loading ui script '%s'\n", this->folder_.data());
|
|
||||||
this->load_script("__init__");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == script_type::code)
|
|
||||||
{
|
|
||||||
handle_error(this->state_.safe_script(data, &sol::script_pass_on_error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context::~context()
|
|
||||||
{
|
|
||||||
this->state_.collect_garbage();
|
|
||||||
this->scheduler_.clear();
|
|
||||||
this->state_ = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void context::run_frame()
|
|
||||||
{
|
|
||||||
this->scheduler_.run_frame();
|
|
||||||
this->state_.collect_garbage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void context::load_script(const std::string& script)
|
|
||||||
{
|
|
||||||
if (!this->loaded_scripts_.emplace(script).second)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto file = (std::filesystem::path{this->folder_} / (script + ".lua")).generic_string();
|
|
||||||
handle_error(this->state_.safe_script_file(file, &sol::script_pass_on_error));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable: 4702)
|
|
||||||
|
|
||||||
#define SOL_ALL_SAFETIES_ON 1
|
|
||||||
#define SOL_PRINT_ERRORS 0
|
|
||||||
#include <sol/sol.hpp>
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
enum script_type
|
|
||||||
{
|
|
||||||
file,
|
|
||||||
code
|
|
||||||
};
|
|
||||||
|
|
||||||
class context
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
context(std::string folder, script_type type);
|
|
||||||
~context();
|
|
||||||
|
|
||||||
context(context&&) noexcept = delete;
|
|
||||||
context& operator=(context&&) noexcept = delete;
|
|
||||||
|
|
||||||
context(const context&) = delete;
|
|
||||||
context& operator=(const context&) = delete;
|
|
||||||
|
|
||||||
void run_frame();
|
|
||||||
|
|
||||||
private:
|
|
||||||
sol::state state_{};
|
|
||||||
std::string folder_;
|
|
||||||
std::unordered_set<std::string> loaded_scripts_;
|
|
||||||
|
|
||||||
scheduler scheduler_;
|
|
||||||
|
|
||||||
void load_script(const std::string& script);
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "engine.hpp"
|
|
||||||
#include "context.hpp"
|
|
||||||
|
|
||||||
#include "../../../component/ui_scripting.hpp"
|
|
||||||
#include "../../../component/game_module.hpp"
|
|
||||||
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
|
|
||||||
namespace ui_scripting::lua::engine
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const auto lui_common = utils::nt::load_resource(LUI_COMMON);
|
|
||||||
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
|
|
||||||
|
|
||||||
auto& get_scripts()
|
|
||||||
{
|
|
||||||
static std::vector<std::unique_ptr<context>> scripts{};
|
|
||||||
return scripts;
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_scripts(const std::string& script_dir)
|
|
||||||
{
|
|
||||||
if (!utils::io::directory_exists(script_dir))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto scripts = utils::io::list_files(script_dir);
|
|
||||||
|
|
||||||
for (const auto& script : scripts)
|
|
||||||
{
|
|
||||||
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
|
||||||
{
|
|
||||||
get_scripts().push_back(std::make_unique<context>(script, script_type::file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_code(const std::string& code)
|
|
||||||
{
|
|
||||||
get_scripts().push_back(std::make_unique<context>(code, script_type::code));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void start()
|
|
||||||
{
|
|
||||||
clear_converted_functions();
|
|
||||||
get_scripts().clear();
|
|
||||||
|
|
||||||
load_code(lui_common);
|
|
||||||
load_code(lui_updater);
|
|
||||||
|
|
||||||
load_scripts(game_module::get_host_module().get_folder() + "/data/ui_scripts/");
|
|
||||||
load_scripts("h1-mod/ui_scripts/");
|
|
||||||
load_scripts("data/ui_scripts/");
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
clear_converted_functions();
|
|
||||||
get_scripts().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_frame()
|
|
||||||
{
|
|
||||||
for (auto& script : get_scripts())
|
|
||||||
{
|
|
||||||
script->run_frame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace ui_scripting::lua::engine
|
|
||||||
{
|
|
||||||
void start();
|
|
||||||
void stop();
|
|
||||||
void run_frame();
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "error.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
void handle_error(const sol::protected_function_result& result)
|
|
||||||
{
|
|
||||||
if (!result.valid())
|
|
||||||
{
|
|
||||||
printf("************** UI Script execution error **************\n");
|
|
||||||
|
|
||||||
const sol::error err = result;
|
|
||||||
printf("%s\n", err.what());
|
|
||||||
|
|
||||||
printf("****************************************************\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "context.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
void handle_error(const sol::protected_function_result& result);
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
#include "std_include.hpp"
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
scheduler::scheduler(sol::state& state)
|
|
||||||
{
|
|
||||||
auto task_handle_type = state.new_usertype<task_handle>("task_handle");
|
|
||||||
|
|
||||||
task_handle_type["clear"] = [this](const task_handle& handle)
|
|
||||||
{
|
|
||||||
this->remove(handle);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::run_frame()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
this->merge_callbacks();
|
|
||||||
|
|
||||||
for (auto i = tasks.begin(); i != tasks.end();)
|
|
||||||
{
|
|
||||||
const auto now = std::chrono::high_resolution_clock::now();
|
|
||||||
const auto diff = now - i->last_call;
|
|
||||||
|
|
||||||
if (diff < i->delay)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
i->last_call = now;
|
|
||||||
|
|
||||||
if (!i->is_deleted)
|
|
||||||
{
|
|
||||||
handle_error(i->callback());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i->is_volatile || i->is_deleted)
|
|
||||||
{
|
|
||||||
i = tasks.erase(i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::clear()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
new_tasks.clear();
|
|
||||||
tasks.clear();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
task_handle scheduler::add(const sol::protected_function& callback, const long long milliseconds,
|
|
||||||
const bool is_volatile)
|
|
||||||
{
|
|
||||||
return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile);
|
|
||||||
}
|
|
||||||
|
|
||||||
task_handle scheduler::add(const sol::protected_function& callback, const std::chrono::milliseconds delay,
|
|
||||||
const bool is_volatile)
|
|
||||||
{
|
|
||||||
const uint64_t id = ++this->current_task_id_;
|
|
||||||
|
|
||||||
task task;
|
|
||||||
task.is_volatile = is_volatile;
|
|
||||||
task.callback = callback;
|
|
||||||
task.delay = delay;
|
|
||||||
task.last_call = std::chrono::steady_clock::now();
|
|
||||||
task.id = id;
|
|
||||||
task.is_deleted = false;
|
|
||||||
|
|
||||||
new_callbacks_.access([&task](task_list& tasks)
|
|
||||||
{
|
|
||||||
tasks.emplace_back(std::move(task));
|
|
||||||
});
|
|
||||||
|
|
||||||
return {id};
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::remove(const task_handle& handle)
|
|
||||||
{
|
|
||||||
auto mask_as_deleted = [&](task_list& tasks)
|
|
||||||
{
|
|
||||||
for (auto& task : tasks)
|
|
||||||
{
|
|
||||||
if (task.id == handle.id)
|
|
||||||
{
|
|
||||||
task.is_deleted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callbacks_.access(mask_as_deleted);
|
|
||||||
new_callbacks_.access(mask_as_deleted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::merge_callbacks()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
|
||||||
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
|
||||||
new_tasks = {};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <utils/concurrency.hpp>
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
class context;
|
|
||||||
|
|
||||||
class task_handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint64_t id = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class task final : public task_handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::chrono::steady_clock::time_point last_call{};
|
|
||||||
sol::protected_function callback{};
|
|
||||||
std::chrono::milliseconds delay{};
|
|
||||||
bool is_volatile = false;
|
|
||||||
bool is_deleted = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class scheduler final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
scheduler(sol::state& state);
|
|
||||||
|
|
||||||
scheduler(scheduler&&) noexcept = delete;
|
|
||||||
scheduler& operator=(scheduler&&) noexcept = delete;
|
|
||||||
|
|
||||||
scheduler(const scheduler&) = delete;
|
|
||||||
scheduler& operator=(const scheduler&) = delete;
|
|
||||||
|
|
||||||
void run_frame();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
task_handle add(const sol::protected_function& callback, long long milliseconds, bool is_volatile);
|
|
||||||
task_handle add(const sol::protected_function& callback, std::chrono::milliseconds delay, bool is_volatile);
|
|
||||||
|
|
||||||
private:
|
|
||||||
using task_list = std::vector<task>;
|
|
||||||
utils::concurrency::container<task_list> new_callbacks_;
|
|
||||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
|
||||||
std::atomic_int64_t current_task_id_ = 0;
|
|
||||||
|
|
||||||
void remove(const task_handle& handle);
|
|
||||||
void merge_callbacks();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "value_conversion.hpp"
|
|
||||||
#include "../execution.hpp"
|
|
||||||
#include "../../../component/ui_scripting.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
table convert_table(const sol::table& t)
|
|
||||||
{
|
|
||||||
table res{};
|
|
||||||
|
|
||||||
t.for_each([res](const sol::object& key, const sol::object& value)
|
|
||||||
{
|
|
||||||
res.set(convert(key), convert(value));
|
|
||||||
});
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value convert_function(const sol::protected_function& function)
|
|
||||||
{
|
|
||||||
const auto closure = game::hks::cclosure_Create(*game::hks::lua_state, main_function_handler, 0, 0, 0);
|
|
||||||
add_converted_function(closure, function);
|
|
||||||
|
|
||||||
game::hks::HksObject value{};
|
|
||||||
value.t = game::hks::TCFUNCTION;
|
|
||||||
value.v.cClosure = closure;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value convert(const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
if (value.is<bool>())
|
|
||||||
{
|
|
||||||
return {value.as<bool>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<int>())
|
|
||||||
{
|
|
||||||
return {value.as<int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<unsigned int>())
|
|
||||||
{
|
|
||||||
return {value.as<unsigned int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<double>())
|
|
||||||
{
|
|
||||||
return {value.as<double>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<float>())
|
|
||||||
{
|
|
||||||
return {value.as<float>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::string>())
|
|
||||||
{
|
|
||||||
return {value.as<std::string>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<lightuserdata>())
|
|
||||||
{
|
|
||||||
return {value.as<lightuserdata>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<userdata>())
|
|
||||||
{
|
|
||||||
return {value.as<userdata>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<table>())
|
|
||||||
{
|
|
||||||
return {value.as<table>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<function>())
|
|
||||||
{
|
|
||||||
return {value.as<function>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<sol::table>())
|
|
||||||
{
|
|
||||||
return {convert_table(value.as<sol::table>())};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<sol::protected_function>())
|
|
||||||
{
|
|
||||||
return {convert_function(value.as<sol::protected_function>())};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
sol::lua_value convert(lua_State* state, const script_value& value)
|
|
||||||
{
|
|
||||||
if (value.is<int>())
|
|
||||||
{
|
|
||||||
return {state, value.as<int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<float>())
|
|
||||||
{
|
|
||||||
return {state, value.as<float>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<bool>())
|
|
||||||
{
|
|
||||||
return {state, value.as<bool>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::string>())
|
|
||||||
{
|
|
||||||
return {state, value.as<std::string>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<lightuserdata>())
|
|
||||||
{
|
|
||||||
return {state, value.as<lightuserdata>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<userdata>())
|
|
||||||
{
|
|
||||||
return {state, value.as<userdata>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<table>())
|
|
||||||
{
|
|
||||||
return {state, value.as<table>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<function>())
|
|
||||||
{
|
|
||||||
return {state, value.as<function>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {state, sol::lua_nil};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "../script_value.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
script_value convert(const sol::lua_value& value);
|
|
||||||
sol::lua_value convert(lua_State* state, const script_value& value);
|
|
||||||
}
|
|
@ -1,274 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "execution.hpp"
|
|
||||||
#include "types.hpp"
|
|
||||||
#include "script_value.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting
|
|
||||||
{
|
|
||||||
/***************************************************************
|
|
||||||
* Constructors
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
script_value::script_value(const game::hks::HksObject& value)
|
|
||||||
: value_(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const int value)
|
|
||||||
{
|
|
||||||
game::hks::HksObject obj{};
|
|
||||||
obj.t = game::hks::TNUMBER;
|
|
||||||
obj.v.number = static_cast<float>(value);
|
|
||||||
|
|
||||||
this->value_ = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const unsigned int value)
|
|
||||||
{
|
|
||||||
game::hks::HksObject obj{};
|
|
||||||
obj.t = game::hks::TNUMBER;
|
|
||||||
obj.v.number = static_cast<float>(value);
|
|
||||||
|
|
||||||
this->value_ = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const bool value)
|
|
||||||
{
|
|
||||||
game::hks::HksObject obj{};
|
|
||||||
obj.t = game::hks::TBOOLEAN;
|
|
||||||
obj.v.boolean = value;
|
|
||||||
|
|
||||||
this->value_ = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const float value)
|
|
||||||
{
|
|
||||||
game::hks::HksObject obj{};
|
|
||||||
obj.t = game::hks::TNUMBER;
|
|
||||||
obj.v.number = static_cast<float>(value);
|
|
||||||
|
|
||||||
this->value_ = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const double value)
|
|
||||||
: script_value(static_cast<float>(value))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const char* value)
|
|
||||||
{
|
|
||||||
game::hks::HksObject obj{};
|
|
||||||
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
game::hks::hksi_lua_pushlstring(state, value, (unsigned int)strlen(value));
|
|
||||||
obj = state->m_apistack.top[-1];
|
|
||||||
|
|
||||||
this->value_ = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const std::string& value)
|
|
||||||
: script_value(value.data())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const lightuserdata& value)
|
|
||||||
{
|
|
||||||
this->value_.t = game::hks::TLIGHTUSERDATA;
|
|
||||||
this->value_.v.ptr = value.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const userdata& value)
|
|
||||||
{
|
|
||||||
this->value_.t = game::hks::TUSERDATA;
|
|
||||||
this->value_.v.ptr = value.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const table& value)
|
|
||||||
{
|
|
||||||
this->value_.t = game::hks::TTABLE;
|
|
||||||
this->value_.v.ptr = value.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const function& value)
|
|
||||||
{
|
|
||||||
this->value_.t = value.type;
|
|
||||||
this->value_.v.ptr = value.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Integer
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<int>() const
|
|
||||||
{
|
|
||||||
const auto number = this->get_raw().v.number;
|
|
||||||
return this->get_raw().t == game::hks::TNUMBER && static_cast<int>(number) == number;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<unsigned int>() const
|
|
||||||
{
|
|
||||||
return this->is<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
int script_value::get() const
|
|
||||||
{
|
|
||||||
return static_cast<int>(this->get_raw().v.number);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
unsigned int script_value::get() const
|
|
||||||
{
|
|
||||||
return static_cast<unsigned int>(this->get_raw().v.number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Boolean
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<bool>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().t == game::hks::TBOOLEAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().v.boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Float
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<float>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().t == game::hks::TNUMBER;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<double>() const
|
|
||||||
{
|
|
||||||
return this->is<float>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
float script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().v.number;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
double script_value::get() const
|
|
||||||
{
|
|
||||||
return static_cast<double>(this->get_raw().v.number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* String
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<const char*>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().t == game::hks::TSTRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<std::string>() const
|
|
||||||
{
|
|
||||||
return this->is<const char*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
const char* script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().v.str->m_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get<const char*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Lightuserdata
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<lightuserdata>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().t == game::hks::TLIGHTUSERDATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
lightuserdata script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().v.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Userdata
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<userdata>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().t == game::hks::TUSERDATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
userdata script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().v.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Table
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<table>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().t == game::hks::TTABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
table script_value::get() const
|
|
||||||
{
|
|
||||||
return this->get_raw().v.table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Function
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
bool script_value::is<function>() const
|
|
||||||
{
|
|
||||||
return this->get_raw().t == game::hks::TIFUNCTION
|
|
||||||
|| this->get_raw().t == game::hks::TCFUNCTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
function script_value::get() const
|
|
||||||
{
|
|
||||||
return { this->get_raw().v.cClosure, this->get_raw().t };
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
*
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
const game::hks::HksObject& script_value::get_raw() const
|
|
||||||
{
|
|
||||||
return this->value_;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting
|
|
||||||
{
|
|
||||||
class lightuserdata;
|
|
||||||
class userdata;
|
|
||||||
class table;
|
|
||||||
class function;
|
|
||||||
|
|
||||||
class script_value
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
script_value() = default;
|
|
||||||
script_value(const game::hks::HksObject& value);
|
|
||||||
|
|
||||||
script_value(int value);
|
|
||||||
script_value(unsigned int value);
|
|
||||||
script_value(bool value);
|
|
||||||
|
|
||||||
script_value(float value);
|
|
||||||
script_value(double value);
|
|
||||||
|
|
||||||
script_value(const char* value);
|
|
||||||
script_value(const std::string& value);
|
|
||||||
|
|
||||||
script_value(const lightuserdata& value);
|
|
||||||
script_value(const userdata& value);
|
|
||||||
script_value(const table& value);
|
|
||||||
script_value(const function& value);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool is() const;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T as() const
|
|
||||||
{
|
|
||||||
if (!this->is<T>())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid type");
|
|
||||||
}
|
|
||||||
|
|
||||||
return get<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
const game::hks::HksObject& get_raw() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename T>
|
|
||||||
T get() const;
|
|
||||||
|
|
||||||
game::hks::HksObject value_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
using arguments = std::vector<script_value>;
|
|
||||||
}
|
|
@ -1,276 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "types.hpp"
|
|
||||||
#include "execution.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting
|
|
||||||
{
|
|
||||||
/***************************************************************
|
|
||||||
* Lightuserdata
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
lightuserdata::lightuserdata(void* ptr_)
|
|
||||||
: ptr(ptr_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Userdata
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
userdata::userdata(void* ptr_)
|
|
||||||
: ptr(ptr_)
|
|
||||||
{
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
userdata::userdata(const userdata& other)
|
|
||||||
{
|
|
||||||
this->operator=(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
userdata::userdata(userdata&& other) noexcept
|
|
||||||
{
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->ref = other.ref;
|
|
||||||
other.ref = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
userdata::~userdata()
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
}
|
|
||||||
|
|
||||||
userdata& userdata::operator=(const userdata& other)
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->ref = other.ref;
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
userdata& userdata::operator=(userdata&& other) noexcept
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->ref = other.ref;
|
|
||||||
other.ref = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void userdata::add()
|
|
||||||
{
|
|
||||||
game::hks::HksObject value{};
|
|
||||||
value.v.ptr = this->ptr;
|
|
||||||
value.t = game::hks::TUSERDATA;
|
|
||||||
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
push_value(value);
|
|
||||||
|
|
||||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void userdata::release()
|
|
||||||
{
|
|
||||||
if (this->ref)
|
|
||||||
{
|
|
||||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void userdata::set(const script_value& key, const script_value& value) const
|
|
||||||
{
|
|
||||||
set_field(*this, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value userdata::get(const script_value& key) const
|
|
||||||
{
|
|
||||||
return get_field(*this, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Table
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
table::table()
|
|
||||||
{
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
table::table(game::hks::HashTable* ptr_)
|
|
||||||
: ptr(ptr_)
|
|
||||||
{
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
table::table(const table& other)
|
|
||||||
{
|
|
||||||
this->operator=(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
table::table(table&& other) noexcept
|
|
||||||
{
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->ref = other.ref;
|
|
||||||
other.ref = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table::~table()
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
}
|
|
||||||
|
|
||||||
table& table::operator=(const table& other)
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->ref = other.ref;
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
table& table::operator=(table&& other) noexcept
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->ref = other.ref;
|
|
||||||
other.ref = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void table::add()
|
|
||||||
{
|
|
||||||
game::hks::HksObject value{};
|
|
||||||
value.v.table = this->ptr;
|
|
||||||
value.t = game::hks::TTABLE;
|
|
||||||
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
push_value(value);
|
|
||||||
|
|
||||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void table::release()
|
|
||||||
{
|
|
||||||
if (this->ref)
|
|
||||||
{
|
|
||||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void table::set(const script_value& key, const script_value& value) const
|
|
||||||
{
|
|
||||||
set_field(*this, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value table::get(const script_value& key) const
|
|
||||||
{
|
|
||||||
return get_field(*this, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
|
||||||
* Function
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
|
|
||||||
: ptr(ptr_)
|
|
||||||
, type(type_)
|
|
||||||
{
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
function::function(const function& other)
|
|
||||||
{
|
|
||||||
this->operator=(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
function::function(function&& other) noexcept
|
|
||||||
{
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->type = other.type;
|
|
||||||
this->ref = other.ref;
|
|
||||||
other.ref = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function::~function()
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
}
|
|
||||||
|
|
||||||
function& function::operator=(const function& other)
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->type = other.type;
|
|
||||||
this->ref = other.ref;
|
|
||||||
this->add();
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
function& function::operator=(function&& other) noexcept
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
this->ptr = other.ptr;
|
|
||||||
this->type = other.type;
|
|
||||||
this->ref = other.ref;
|
|
||||||
other.ref = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void function::add()
|
|
||||||
{
|
|
||||||
game::hks::HksObject value{};
|
|
||||||
value.v.cClosure = this->ptr;
|
|
||||||
value.t = this->type;
|
|
||||||
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
|
|
||||||
push_value(value);
|
|
||||||
|
|
||||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void function::release()
|
|
||||||
{
|
|
||||||
if (this->ref)
|
|
||||||
{
|
|
||||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments function::call(const arguments& arguments) const
|
|
||||||
{
|
|
||||||
return call_script_function(*this, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "script_value.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting
|
|
||||||
{
|
|
||||||
class lightuserdata
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
lightuserdata(void*);
|
|
||||||
void* ptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class userdata
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
userdata(void*);
|
|
||||||
|
|
||||||
userdata(const userdata& other);
|
|
||||||
userdata(userdata&& other) noexcept;
|
|
||||||
|
|
||||||
~userdata();
|
|
||||||
|
|
||||||
userdata& operator=(const userdata& other);
|
|
||||||
userdata& operator=(userdata&& other) noexcept;
|
|
||||||
|
|
||||||
script_value get(const script_value& key) const;
|
|
||||||
void set(const script_value& key, const script_value& value) const;
|
|
||||||
|
|
||||||
void* ptr;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void add();
|
|
||||||
void release();
|
|
||||||
|
|
||||||
int ref{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class table
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
table();
|
|
||||||
table(game::hks::HashTable* ptr_);
|
|
||||||
|
|
||||||
table(const table& other);
|
|
||||||
table(table&& other) noexcept;
|
|
||||||
|
|
||||||
~table();
|
|
||||||
|
|
||||||
table& operator=(const table& other);
|
|
||||||
table& operator=(table&& other) noexcept;
|
|
||||||
|
|
||||||
script_value get(const script_value& key) const;
|
|
||||||
void set(const script_value& key, const script_value& value) const;
|
|
||||||
|
|
||||||
game::hks::HashTable* ptr;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void add();
|
|
||||||
void release();
|
|
||||||
|
|
||||||
int ref{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class function
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
function(game::hks::cclosure*, game::hks::HksObjectType);
|
|
||||||
|
|
||||||
function(const function& other);
|
|
||||||
function(function&& other) noexcept;
|
|
||||||
|
|
||||||
~function();
|
|
||||||
|
|
||||||
function& operator=(const function& other);
|
|
||||||
function& operator=(function&& other) noexcept;
|
|
||||||
|
|
||||||
arguments call(const arguments& arguments) const;
|
|
||||||
|
|
||||||
game::hks::cclosure* ptr;
|
|
||||||
game::hks::HksObjectType type;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void add();
|
|
||||||
void release();
|
|
||||||
|
|
||||||
int ref{};
|
|
||||||
};
|
|
||||||
}
|
|
@ -97,7 +97,7 @@ FARPROC load_binary(const launcher::mode mode, uint64_t* base_address)
|
|||||||
if (!utils::io::read_file(binary, &data))
|
if (!utils::io::read_file(binary, &data))
|
||||||
{
|
{
|
||||||
throw std::runtime_error(utils::string::va(
|
throw std::runtime_error(utils::string::va(
|
||||||
"Failed to read game binary (%s)!\nPlease copy the h1x.exe into your Call of Duty: Modern Warfare Remastered installation folder and run it from there.",
|
"Failed to read game binary (%s)!\nPlease copy the h1-mod.exe into your Call of Duty: Modern Warfare Remastered installation folder and run it from there.",
|
||||||
binary.data()));
|
binary.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user