deps: update chai script
This commit is contained in:
parent
b389bf4a62
commit
94c7364dd1
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -26,10 +26,6 @@
|
||||
[submodule "deps/udis86"]
|
||||
path = deps/udis86
|
||||
url = https://github.com/vmt/udis86.git
|
||||
[submodule "deps/ChaiScript"]
|
||||
path = deps/ChaiScript
|
||||
url = https://github.com/ChaiScript/ChaiScript.git
|
||||
branch = master
|
||||
[submodule "deps/zstd"]
|
||||
path = deps/zstd
|
||||
url = https://github.com/facebook/zstd.git
|
||||
|
1
deps/ChaiScript
vendored
1
deps/ChaiScript
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 69123db3dcde16cb38adae753c40edde86254246
|
23
deps/premake/chaiscript.lua
vendored
23
deps/premake/chaiscript.lua
vendored
@ -1,23 +0,0 @@
|
||||
chaiscript = {
|
||||
source = path.join(dependencies.basePath, "ChaiScript"),
|
||||
}
|
||||
|
||||
function chaiscript.import()
|
||||
chaiscript.includes()
|
||||
|
||||
defines {
|
||||
--"CHAISCRIPT_NO_THREADS" -- :(
|
||||
}
|
||||
end
|
||||
|
||||
function chaiscript.includes()
|
||||
includedirs {
|
||||
path.join(chaiscript.source, "include"),
|
||||
}
|
||||
end
|
||||
|
||||
function chaiscript.project()
|
||||
|
||||
end
|
||||
|
||||
table.insert(dependencies, chaiscript)
|
@ -39,10 +39,17 @@ namespace game
|
||||
SL_GetStringOfSize_t SL_GetStringOfSize;
|
||||
|
||||
Scr_AddEntityNum_t Scr_AddEntityNum;
|
||||
Scr_AddString_t Scr_AddString;
|
||||
Scr_AddInt_t Scr_AddInt;
|
||||
Scr_AddFloat_t Scr_AddFloat;
|
||||
Scr_Notify_t Scr_Notify;
|
||||
Scr_NotifyLevel_t Scr_NotifyLevel;
|
||||
Scr_GetNumParam_t Scr_GetNumParam;
|
||||
Scr_GetString_t Scr_GetString;
|
||||
Scr_CastString_t Scr_CastString;
|
||||
Scr_ErrorInternal_t Scr_ErrorInternal;
|
||||
|
||||
GetObjectType_t GetObjectType;
|
||||
|
||||
Sys_ShowConsole_t Sys_ShowConsole;
|
||||
Sys_Error_t Sys_Error;
|
||||
@ -66,6 +73,7 @@ namespace game
|
||||
SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString;
|
||||
SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString;
|
||||
SV_SpawnServer_t SV_SpawnServer;
|
||||
SV_GetConfigstring_t SV_GetConfigstring;
|
||||
|
||||
XUIDToString_t XUIDToString;
|
||||
|
||||
@ -90,6 +98,8 @@ namespace game
|
||||
|
||||
player_die_t player_die;
|
||||
|
||||
LargeLocalResetToMark_t LargeLocalResetToMark;
|
||||
|
||||
decltype(longjmp)* _longjmp;
|
||||
|
||||
CmdArgs* sv_cmd_args;
|
||||
@ -99,6 +109,7 @@ namespace game
|
||||
char** scrMemTreePub;
|
||||
char* scrMemTreeGlob;
|
||||
|
||||
function_stack_t* scr_function_stack;
|
||||
scrVarPub_t* scr_VarPub;
|
||||
scrVmPub_t* scr_VmPub;
|
||||
|
||||
@ -167,17 +178,17 @@ namespace game
|
||||
|
||||
void AddRefToValue(VariableValue* value)
|
||||
{
|
||||
if (value->type == SCRIPT_OBJECT)
|
||||
if (value->type == VAR_POINTER)
|
||||
{
|
||||
++scrVarGlob[4 * value->u.entityId];
|
||||
}
|
||||
else if ((value->type & ~1) == SCRIPT_STRING)
|
||||
else if ((value->type & ~1) == VAR_STRING)
|
||||
{
|
||||
static const auto size = is_sp() ? 16 : 12;
|
||||
const auto ref_count = reinterpret_cast<unsigned volatile *>(*scrMemTreePub + size * value->u.stringValue);
|
||||
InterlockedIncrement(ref_count);
|
||||
}
|
||||
else if (value->type == SCRIPT_VECTOR)
|
||||
else if (value->type == VAR_VECTOR)
|
||||
{
|
||||
if (!*PBYTE(value->u.vectorValue - 1))
|
||||
{
|
||||
@ -269,8 +280,8 @@ namespace game
|
||||
static auto ent_array = reinterpret_cast<WORD*>(SELECT_VALUE(0x19AFC82, 0x1E72182));
|
||||
|
||||
scr_entref_t result;
|
||||
result.raw.classnum = static_cast<unsigned short>(class_array[2 * id]) >> 8;
|
||||
result.raw.entnum = ent_array[4 * id];
|
||||
result.classnum = static_cast<unsigned short>(class_array[2 * id]) >> 8;
|
||||
result.entnum = ent_array[4 * id];
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -323,19 +334,6 @@ namespace game
|
||||
(SELECT_VALUE(0x42CAD0, 0x52BCC0))(classnum, entnum, offset);
|
||||
}
|
||||
|
||||
void Scr_AddString(const char* value)
|
||||
{
|
||||
reinterpret_cast<void(*)(const char*)> (SELECT_VALUE(0x4A5600, 0x56AC00))(value);
|
||||
}
|
||||
|
||||
void Scr_AddInt(int value)
|
||||
{
|
||||
IncInParam();
|
||||
|
||||
scr_VmPub->top->type = SCRIPT_INTEGER;
|
||||
scr_VmPub->top->u.intValue = value;
|
||||
}
|
||||
|
||||
const char* SL_ConvertToString(const unsigned int stringValue)
|
||||
{
|
||||
if (!stringValue) return nullptr;
|
||||
@ -723,11 +721,18 @@ namespace game
|
||||
native::SL_GetStringOfSize = native::SL_GetStringOfSize_t(SELECT_VALUE(0x4E13F0, 0x564650));
|
||||
|
||||
native::Scr_AddEntityNum = native::Scr_AddEntityNum_t(SELECT_VALUE(0x0, 0x56ABC0));
|
||||
native::Scr_AddString = native::Scr_AddString_t(SELECT_VALUE(0x4A5600, 0x56AC00));
|
||||
native::Scr_AddInt = native::Scr_AddInt_t(SELECT_VALUE(0x42DE20, 0x56AA20));
|
||||
native::Scr_AddFloat = native::Scr_AddFloat_t(SELECT_VALUE(0x5349D0, 0x56AA70));
|
||||
|
||||
native::Scr_Notify = native::Scr_Notify_t(SELECT_VALUE(0x4895B0, 0x52B190));
|
||||
native::Scr_NotifyLevel = native::Scr_NotifyLevel_t(SELECT_VALUE(0x445E10, 0x56B6B0));
|
||||
native::Scr_GetNumParam = native::Scr_GetNumParam_t(SELECT_VALUE(0x4C6FE0, 0x56AA10));
|
||||
native::Scr_GetString = native::Scr_GetString_t(SELECT_VALUE(0x497530, 0x56A3D0));
|
||||
native::Scr_CastString = native::Scr_CastString_t(SELECT_VALUE(0x447CE0, 0x566EE0));
|
||||
native::Scr_ErrorInternal = native::Scr_ErrorInternal_t(SELECT_VALUE(0x42B910, 0x568FD0));
|
||||
|
||||
native::GetObjectType = native::GetObjectType_t(SELECT_VALUE(0x4D8FE0, 0x565C60));
|
||||
|
||||
native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590));
|
||||
native::Sys_Error = native::Sys_Error_t(SELECT_VALUE(0x490D90, 0x5CC3B0));
|
||||
@ -752,6 +757,7 @@ namespace game
|
||||
native::SV_Cmd_TokenizeString = native::SV_Cmd_TokenizeString_t(SELECT_VALUE(0x0, 0x545D40));
|
||||
native::SV_Cmd_EndTokenizedString = native::SV_Cmd_EndTokenizedString_t(SELECT_VALUE(0x0, 0x545D70));
|
||||
native::SV_SpawnServer = native::SV_SpawnServer_t(SELECT_VALUE(0x0, 0x575020));
|
||||
native::SV_GetConfigstring = native::SV_GetConfigstring_t(SELECT_VALUE(0x4C6E30, 0x573D50));
|
||||
native::mp::SV_GameSendServerCommand = native::mp::SV_GameSendServerCommand_t(0x573220);
|
||||
native::mp::SV_GetGuid = native::mp::SV_GetGuid_t(0x573990);
|
||||
|
||||
@ -783,6 +789,8 @@ namespace game
|
||||
|
||||
native::player_die = native::player_die_t(SELECT_VALUE(0x0, 0x503460));
|
||||
|
||||
native::LargeLocalResetToMark = native::LargeLocalResetToMark_t(SELECT_VALUE(0x524350, 0x5B7150));
|
||||
|
||||
native::_longjmp = reinterpret_cast<decltype(longjmp)*>(SELECT_VALUE(0x73AC20, 0x7363BC));
|
||||
|
||||
native::sv_cmd_args = reinterpret_cast<native::CmdArgs*>(SELECT_VALUE(0x1757218, 0x1CAA998));
|
||||
@ -792,6 +800,7 @@ namespace game
|
||||
native::scrMemTreePub = reinterpret_cast<char**>(SELECT_VALUE(0x196FB00, 0x1E32000));
|
||||
native::scrMemTreeGlob = reinterpret_cast<char*>(SELECT_VALUE(0x186DA00, 0x1D6FF00));
|
||||
|
||||
native::scr_function_stack = reinterpret_cast<native::function_stack_t*>(SELECT_VALUE(0x1BF255C, 0x20B4A5C));
|
||||
native::scr_VarPub = reinterpret_cast<native::scrVarPub_t*>(SELECT_VALUE(0x0, 0x208E188));
|
||||
native::scr_VmPub = reinterpret_cast<native::scrVmPub_t*>(SELECT_VALUE(0x1BF2580, 0x20B4A80));
|
||||
|
||||
|
@ -70,7 +70,7 @@ namespace game
|
||||
typedef void* (*MT_AllocIndex_t)(int numBytes, int type);
|
||||
extern MT_AllocIndex_t MT_AllocIndex;
|
||||
|
||||
typedef void (*RemoveRefToValue_t)(scriptType_e type, VariableUnion u);
|
||||
typedef void (*RemoveRefToValue_t)(int type, VariableUnion u);
|
||||
extern RemoveRefToValue_t RemoveRefToValue;
|
||||
|
||||
typedef unsigned int (*SL_GetStringOfSize_t)(const char* str, unsigned int user, unsigned int len, int type);
|
||||
@ -79,6 +79,15 @@ namespace game
|
||||
typedef void (*Scr_AddEntityNum_t)(int entnum, unsigned int classnum);
|
||||
extern Scr_AddEntityNum_t Scr_AddEntityNum;
|
||||
|
||||
typedef void (*Scr_AddString_t)(const char* value);
|
||||
extern Scr_AddString_t Scr_AddString;
|
||||
|
||||
typedef void (*Scr_AddInt_t)(int value);
|
||||
extern Scr_AddInt_t Scr_AddInt;
|
||||
|
||||
typedef void (*Scr_AddFloat_t)(float value);
|
||||
extern Scr_AddFloat_t Scr_AddFloat;
|
||||
|
||||
typedef void (*Scr_Notify_t)(gentity_s* ent, scr_string_t, unsigned int paramcount);
|
||||
extern Scr_Notify_t Scr_Notify;
|
||||
|
||||
@ -91,6 +100,15 @@ namespace game
|
||||
typedef const char* (*Scr_GetString_t)(unsigned int index);
|
||||
extern Scr_GetString_t Scr_GetString;
|
||||
|
||||
typedef bool (*Scr_CastString_t)(VariableValue* value);
|
||||
extern Scr_CastString_t Scr_CastString;
|
||||
|
||||
typedef void (*Scr_ErrorInternal_t)();
|
||||
extern Scr_ErrorInternal_t Scr_ErrorInternal;
|
||||
|
||||
typedef unsigned int(*GetObjectType_t)(unsigned int id);
|
||||
extern GetObjectType_t GetObjectType;
|
||||
|
||||
typedef void (*Sys_ShowConsole_t)();
|
||||
extern Sys_ShowConsole_t Sys_ShowConsole;
|
||||
|
||||
@ -139,6 +157,9 @@ namespace game
|
||||
typedef void (*SV_SpawnServer_t)(const char* server, int mapIsPreloaded, int savegame);
|
||||
extern SV_SpawnServer_t SV_SpawnServer;
|
||||
|
||||
typedef void (*SV_GetConfigstring_t)(int index, char* buffer, int bufferSize);
|
||||
extern SV_GetConfigstring_t SV_GetConfigstring;
|
||||
|
||||
typedef void (*XUIDToString_t)(const unsigned __int64* xuid, char* str);
|
||||
extern XUIDToString_t XUIDToString;
|
||||
|
||||
@ -182,6 +203,9 @@ namespace game
|
||||
typedef void (*player_die_t)(gentity_s* self, const gentity_s* inflictor, gentity_s* attacker, int damage, int meansOfDeath, const Weapon* iWeapon, bool isAlternate, const float* vDir, const hitLocation_t hitLoc, int psTimeOffset);
|
||||
extern player_die_t player_die;
|
||||
|
||||
typedef void (*LargeLocalResetToMark_t)(int markPos);
|
||||
extern LargeLocalResetToMark_t LargeLocalResetToMark;
|
||||
|
||||
extern decltype(longjmp)* _longjmp;
|
||||
|
||||
constexpr auto CMD_MAX_NESTING = 8;
|
||||
@ -192,6 +216,7 @@ namespace game
|
||||
extern char** scrMemTreePub;
|
||||
extern char* scrMemTreeGlob;
|
||||
|
||||
extern function_stack_t* scr_function_stack;
|
||||
extern scrVarPub_t* scr_VarPub;
|
||||
extern scrVmPub_t* scr_VmPub;
|
||||
|
||||
@ -293,8 +318,6 @@ namespace game
|
||||
scr_entref_t Scr_GetEntityIdRef(unsigned int id);
|
||||
void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount);
|
||||
int Scr_SetObjectField(unsigned int classnum, int entnum, int offset);
|
||||
void Scr_AddString(const char* value);
|
||||
void Scr_AddInt(int value);
|
||||
|
||||
const char* SL_ConvertToString(unsigned int stringValue);
|
||||
unsigned int SL_GetString(const char* str, unsigned int user);
|
||||
|
@ -1,35 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "context_initializer.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
context::context() : executer_(this), scheduler_(this), parameters_(this), event_handler_(this)
|
||||
{
|
||||
context_initializer::initialize(this);
|
||||
}
|
||||
|
||||
executer* context::get_executer()
|
||||
{
|
||||
return &this->executer_;
|
||||
}
|
||||
|
||||
scheduler* context::get_scheduler()
|
||||
{
|
||||
return &this->scheduler_;
|
||||
}
|
||||
|
||||
parameters* context::get_parameters()
|
||||
{
|
||||
return &this->parameters_;
|
||||
}
|
||||
|
||||
event_handler* context::get_event_handler()
|
||||
{
|
||||
return &this->event_handler_;
|
||||
}
|
||||
|
||||
chaiscript::ChaiScript* context::get_chai()
|
||||
{
|
||||
return &this->chai_;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
#include "executer.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "parameters.hpp"
|
||||
#include "event_handler.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class context final
|
||||
{
|
||||
public:
|
||||
context();
|
||||
|
||||
chaiscript::ChaiScript* get_chai();
|
||||
|
||||
executer* get_executer();
|
||||
scheduler* get_scheduler();
|
||||
parameters* get_parameters();
|
||||
event_handler* get_event_handler();
|
||||
|
||||
private:
|
||||
chaiscript::ChaiScript chai_;
|
||||
|
||||
executer executer_;
|
||||
scheduler scheduler_;
|
||||
parameters parameters_;
|
||||
event_handler event_handler_;
|
||||
};
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "context_initializer.hpp"
|
||||
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace game::scripting::context_initializer
|
||||
{
|
||||
void initialize_entity(context* context)
|
||||
{
|
||||
auto* const chai = context->get_chai();
|
||||
|
||||
chai->add(chaiscript::user_type<entity>(), "_entity");
|
||||
chai->add(chaiscript::constructor<entity()>(), "_entity");
|
||||
chai->add(chaiscript::constructor<entity(const entity&)>(), "_entity");
|
||||
|
||||
chai->add(chaiscript::fun([](entity& lhs, const entity& rhs) -> entity&
|
||||
{
|
||||
return lhs = rhs;
|
||||
}), "=");
|
||||
|
||||
chai->add(chaiscript::fun(&entity::get), "get");
|
||||
chai->add(chaiscript::fun(&entity::set), "set");
|
||||
|
||||
chai->add(chaiscript::fun(&entity::on_notify), "onNotify");
|
||||
chai->add(chaiscript::fun([](const entity& ent, const std::string& event,
|
||||
const std::function<void(
|
||||
std::vector<chaiscript::Boxed_Value>)>&
|
||||
callback)
|
||||
{
|
||||
return ent.on_notify(event, callback, false);
|
||||
}), "onNotify");
|
||||
|
||||
chai->add(chaiscript::fun([context](const std::string& event,
|
||||
const std::function<void(
|
||||
entity, std::vector<chaiscript::Boxed_Value>)>&
|
||||
callback)
|
||||
{
|
||||
generic_event_listener listener;
|
||||
listener.event = event;
|
||||
listener.is_volatile = false;
|
||||
listener.callback = callback;
|
||||
|
||||
return context->get_event_handler()->add_event_listener(listener);
|
||||
}), "onNotify");
|
||||
|
||||
chai->add(chaiscript::fun([context](const std::string& event,
|
||||
const std::function<void(
|
||||
entity, std::vector<chaiscript::Boxed_Value>)>&
|
||||
callback, const bool is_volatile)
|
||||
{
|
||||
generic_event_listener listener;
|
||||
listener.event = event;
|
||||
listener.is_volatile = is_volatile;
|
||||
listener.callback = callback;
|
||||
|
||||
return context->get_event_handler()->add_event_listener(listener);
|
||||
}), "onNotify");
|
||||
|
||||
chai->add_global(chaiscript::Boxed_Value(0), "gsc");
|
||||
|
||||
chai->add(chaiscript::fun([context](const entity& entity, const std::string& function,
|
||||
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||
{
|
||||
const auto function_lower = utils::string::to_lower(function);
|
||||
|
||||
if (function_lower == "notify" && !arguments.empty())
|
||||
{
|
||||
const auto real_arguments = std::vector<chaiscript::Boxed_Value>(
|
||||
arguments.begin() + 1, arguments.end());
|
||||
|
||||
entity.notify(chaiscript::boxed_cast<std::string>(arguments[0]), real_arguments);
|
||||
}
|
||||
else if (context->get_executer()->function_exists(function_lower, false))
|
||||
{
|
||||
return entity.call(function_lower, arguments);
|
||||
}
|
||||
|
||||
return chaiscript::Boxed_Value(0);
|
||||
}), "method_missing");
|
||||
|
||||
chai->add(chaiscript::fun([context](const chaiscript::Boxed_Value&/*object*/,
|
||||
const std::string& function,
|
||||
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||
{
|
||||
const auto function_lower = utils::string::to_lower(function);
|
||||
|
||||
if (context->get_executer()->function_exists(function_lower, true))
|
||||
{
|
||||
return context->get_executer()->call(function_lower, 0, arguments);
|
||||
}
|
||||
|
||||
return chaiscript::Boxed_Value(0);
|
||||
}), "method_missing");
|
||||
}
|
||||
|
||||
void initialize(context* context)
|
||||
{
|
||||
initialize_entity(context);
|
||||
|
||||
auto* const chai = context->get_chai();
|
||||
|
||||
chai->add(chaiscript::fun([](const std::string& string)
|
||||
{
|
||||
printf("%s\n", string.data());
|
||||
}), "print");
|
||||
|
||||
chai->add(chaiscript::fun([](const std::string& string)
|
||||
{
|
||||
MessageBoxA(nullptr, string.data(), nullptr, 0);
|
||||
}), "alert");
|
||||
|
||||
const auto level_id = *native::levelEntityId;
|
||||
chai->add_global(chaiscript::var(entity(context, level_id)), "level");
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
#include "context.hpp"
|
||||
|
||||
namespace game::scripting::context_initializer
|
||||
{
|
||||
void initialize(context* context);
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "context.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
entity::entity() : entity(nullptr, 0)
|
||||
{
|
||||
}
|
||||
|
||||
entity::entity(const entity& other) : entity()
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
entity::entity(entity&& other) noexcept : entity()
|
||||
{
|
||||
this->operator=(std::move(other));
|
||||
}
|
||||
|
||||
entity::entity(context* context, const unsigned int entity_id) : context_(context), entity_id_(entity_id)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
entity::~entity()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
entity& entity::operator=(const entity& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->~entity();
|
||||
|
||||
this->context_ = other.context_;
|
||||
this->entity_id_ = other.entity_id_;
|
||||
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
entity& entity::operator=(entity&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->~entity();
|
||||
|
||||
this->context_ = other.context_;
|
||||
this->entity_id_ = other.entity_id_;
|
||||
|
||||
other.context_ = nullptr;
|
||||
other.entity_id_ = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
event_listener_handle entity::on_notify(const std::string& event,
|
||||
const std::function<void(std::vector<chaiscript::Boxed_Value>)>&
|
||||
callback,
|
||||
const bool is_volatile)
|
||||
const
|
||||
{
|
||||
event_listener listener;
|
||||
listener.event = event;
|
||||
listener.callback = callback;
|
||||
listener.entity_id = this->entity_id_;
|
||||
listener.is_volatile = is_volatile;
|
||||
|
||||
return this->context_->get_event_handler()->add_event_listener(listener);
|
||||
}
|
||||
|
||||
unsigned int entity::get_entity_id() const
|
||||
{
|
||||
return this->entity_id_;
|
||||
}
|
||||
|
||||
native::scr_entref_t entity::get_entity_reference() const
|
||||
{
|
||||
return game::native::Scr_GetEntityIdRef(this->get_entity_id());
|
||||
}
|
||||
|
||||
chaiscript::Boxed_Value entity::call(const std::string& function,
|
||||
const std::vector<chaiscript::Boxed_Value>& arguments) const
|
||||
{
|
||||
return this->context_->get_executer()->call(function, this->get_entity_id(), arguments);
|
||||
}
|
||||
|
||||
void entity::notify(const std::string& event,
|
||||
const std::vector<chaiscript::Boxed_Value>& arguments) const
|
||||
{
|
||||
this->context_->get_executer()->notify(event, this->get_entity_id(), arguments);
|
||||
}
|
||||
|
||||
void entity::set(const std::string& field, const chaiscript::Boxed_Value& value) const
|
||||
{
|
||||
this->context_->get_executer()->set_entity_field(field, this->get_entity_id(), value);
|
||||
}
|
||||
|
||||
chaiscript::Boxed_Value entity::get(const std::string& field) const
|
||||
{
|
||||
return this->context_->get_executer()->get_entity_field(field, this->get_entity_id());
|
||||
}
|
||||
|
||||
void entity::add() const
|
||||
{
|
||||
if (this->entity_id_)
|
||||
{
|
||||
native::VariableValue value{};
|
||||
value.type = native::SCRIPT_OBJECT;
|
||||
value.u.entityId = this->entity_id_;
|
||||
native::AddRefToValue(&value);
|
||||
}
|
||||
}
|
||||
|
||||
void entity::release() const
|
||||
{
|
||||
if (this->entity_id_)
|
||||
{
|
||||
native::RemoveRefToValue(native::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class context;
|
||||
class event_listener_handle;
|
||||
|
||||
class entity final
|
||||
{
|
||||
public:
|
||||
entity();
|
||||
entity(const entity& other);
|
||||
entity(entity&& other) noexcept;
|
||||
entity(context* context, unsigned int entity_id);
|
||||
~entity();
|
||||
|
||||
entity& operator=(const entity& other);
|
||||
entity& operator=(entity&& other) noexcept;
|
||||
|
||||
event_listener_handle on_notify(const std::string& event,
|
||||
const std::function<void(std::vector<chaiscript::Boxed_Value>)>& callback,
|
||||
bool is_volatile) const;
|
||||
|
||||
unsigned int get_entity_id() const;
|
||||
game::native::scr_entref_t get_entity_reference() const;
|
||||
|
||||
chaiscript::Boxed_Value call(const std::string& function,
|
||||
const std::vector<chaiscript::Boxed_Value>& arguments) const;
|
||||
void notify(const std::string& event, const std::vector<chaiscript::Boxed_Value>& arguments) const;
|
||||
|
||||
void set(const std::string& field, const chaiscript::Boxed_Value& value) const;
|
||||
chaiscript::Boxed_Value get(const std::string& field) const;
|
||||
|
||||
private:
|
||||
context* context_;
|
||||
unsigned int entity_id_;
|
||||
|
||||
void add() const;
|
||||
void release() const;
|
||||
};
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class event final
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
unsigned int entity_id;
|
||||
std::vector<native::VariableValue> arguments;
|
||||
};
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
event_handler::event_handler(context* context) : context_(context)
|
||||
{
|
||||
const auto chai = this->context_->get_chai();
|
||||
|
||||
chai->add(chaiscript::user_type<event_listener_handle>(), "_event_listener_handle");
|
||||
chai->add(chaiscript::constructor<event_listener_handle()>(), "_event_listener_handle");
|
||||
chai->add(chaiscript::constructor<event_listener_handle(const event_listener_handle&)>(),
|
||||
"_event_listener_handle");
|
||||
|
||||
chai->add(chaiscript::fun(
|
||||
[](event_listener_handle& lhs, const event_listener_handle& rhs) -> event_listener_handle&
|
||||
{
|
||||
return lhs = rhs;
|
||||
}), "=");
|
||||
|
||||
chai->add(chaiscript::fun([this](const event_listener_handle& handle)
|
||||
{
|
||||
this->remove(handle);
|
||||
}), "clear");
|
||||
}
|
||||
|
||||
void event_handler::dispatch(event* event)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<chaiscript::Boxed_Value> arguments;
|
||||
|
||||
for (auto argument : event->arguments)
|
||||
{
|
||||
arguments.push_back(this->context_->get_parameters()->load(argument));
|
||||
}
|
||||
|
||||
this->dispatch_to_specific_listeners(event, arguments);
|
||||
this->dispatch_to_generic_listeners(event, arguments);
|
||||
}
|
||||
catch (chaiscript::exception::eval_error& e)
|
||||
{
|
||||
throw std::runtime_error(e.pretty_print());
|
||||
}
|
||||
}
|
||||
|
||||
void event_handler::dispatch_to_specific_listeners(event* event,
|
||||
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||
{
|
||||
this->event_listeners_.access([&](task_list& tasks)
|
||||
{
|
||||
for (auto listener = tasks.begin(); listener != tasks.end();)
|
||||
{
|
||||
if (listener->event == event->name && listener->entity_id == event->entity_id)
|
||||
{
|
||||
if (listener->is_volatile)
|
||||
{
|
||||
listener = tasks.erase(listener);
|
||||
continue;
|
||||
}
|
||||
|
||||
listener->callback(arguments);
|
||||
}
|
||||
|
||||
++listener;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void event_handler::dispatch_to_generic_listeners(event* event,
|
||||
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||
{
|
||||
this->generic_event_listeners_.access([&](generic_task_list& tasks)
|
||||
{
|
||||
for (auto listener = tasks.begin(); listener != tasks.end();)
|
||||
{
|
||||
if (listener->event == event->name)
|
||||
{
|
||||
if (listener->is_volatile)
|
||||
{
|
||||
listener = tasks.erase(listener);
|
||||
continue;
|
||||
}
|
||||
|
||||
listener->callback(entity(this->context_, event->entity_id), arguments);
|
||||
}
|
||||
|
||||
++listener;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
event_listener_handle event_handler::add_event_listener(event_listener listener)
|
||||
{
|
||||
listener.id = ++this->current_listener_id_;
|
||||
this->event_listeners_.access([listener](task_list& tasks)
|
||||
{
|
||||
tasks.emplace_back(std::move(listener));
|
||||
});
|
||||
return {listener.id};
|
||||
}
|
||||
|
||||
event_listener_handle event_handler::add_event_listener(generic_event_listener listener)
|
||||
{
|
||||
listener.id = ++this->current_listener_id_;
|
||||
this->generic_event_listeners_.access([listener](generic_task_list& tasks)
|
||||
{
|
||||
tasks.emplace_back(std::move(listener));
|
||||
});
|
||||
return {listener.id};
|
||||
}
|
||||
|
||||
void event_handler::clear()
|
||||
{
|
||||
this->event_listeners_.access([](task_list& tasks)
|
||||
{
|
||||
tasks.clear();
|
||||
});
|
||||
|
||||
this->generic_event_listeners_.access([](generic_task_list& tasks)
|
||||
{
|
||||
tasks.clear();
|
||||
});
|
||||
}
|
||||
|
||||
void event_handler::remove(const event_listener_handle& handle)
|
||||
{
|
||||
this->event_listeners_.access([handle](task_list& tasks)
|
||||
{
|
||||
for (auto i = tasks.begin(); i != tasks.end();)
|
||||
{
|
||||
if (i->id == handle.id)
|
||||
{
|
||||
i = tasks.erase(i);
|
||||
return;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
});
|
||||
|
||||
this->generic_event_listeners_.access([handle](generic_task_list& tasks)
|
||||
{
|
||||
for (auto i = tasks.begin(); i != tasks.end();)
|
||||
{
|
||||
if (i->id == handle.id)
|
||||
{
|
||||
i = tasks.erase(i);
|
||||
return;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
#include <utils/concurrency.hpp>
|
||||
#include "entity.hpp"
|
||||
#include "event.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class context;
|
||||
|
||||
class event_listener_handle
|
||||
{
|
||||
public:
|
||||
unsigned long long id = 0;
|
||||
};
|
||||
|
||||
class event_listener final : public event_listener_handle
|
||||
{
|
||||
public:
|
||||
std::string event = {};
|
||||
unsigned int entity_id = 0;
|
||||
std::function<void(std::vector<chaiscript::Boxed_Value>)> callback = {};
|
||||
bool is_volatile = false;
|
||||
};
|
||||
|
||||
class generic_event_listener final : public event_listener_handle
|
||||
{
|
||||
public:
|
||||
std::string event = {};
|
||||
std::function<void(entity, std::vector<chaiscript::Boxed_Value>)> callback = {};
|
||||
bool is_volatile = false;
|
||||
};
|
||||
|
||||
class event_handler final
|
||||
{
|
||||
public:
|
||||
explicit event_handler(context* context);
|
||||
|
||||
void dispatch(event* event);
|
||||
|
||||
event_listener_handle add_event_listener(event_listener listener);
|
||||
event_listener_handle add_event_listener(generic_event_listener listener);
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
context* context_;
|
||||
std::atomic_int64_t current_listener_id_ = 0;
|
||||
|
||||
using task_list = std::vector<event_listener>;
|
||||
utils::concurrency::container<task_list> event_listeners_;
|
||||
|
||||
using generic_task_list = std::vector<generic_event_listener>;
|
||||
utils::concurrency::container<generic_task_list> generic_event_listeners_;
|
||||
|
||||
void dispatch_to_specific_listeners(event* event, const std::vector<chaiscript::Boxed_Value>& arguments);
|
||||
void dispatch_to_generic_listeners(event* event, const std::vector<chaiscript::Boxed_Value>& arguments);
|
||||
|
||||
void remove(const event_listener_handle& handle);
|
||||
};
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "functions.hpp"
|
||||
#include "stack_isolation.hpp"
|
||||
#include "safe_executer.hpp"
|
||||
#include "context.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
executer::executer(context* context) : context_(context)
|
||||
{
|
||||
}
|
||||
|
||||
int executer::get_field_id(const int classnum, const std::string& field) const
|
||||
{
|
||||
const auto field_name = utils::string::to_lower(field);
|
||||
const auto class_id = native::g_classMap[classnum].id;
|
||||
const auto field_str = native::SL_GetString(field_name.data(), 1);
|
||||
const auto _ = gsl::finally([field_str]()
|
||||
{
|
||||
native::RemoveRefToValue(native::SCRIPT_STRING, {int(field_str)});
|
||||
});
|
||||
|
||||
const auto offset = native::FindVariable(class_id, field_str);
|
||||
if (offset)
|
||||
{
|
||||
const auto index = 4 * (offset + 0xC800 * (class_id & 1));
|
||||
return PINT(SELECT_VALUE(0x1A3BC80, 0x1EFE180))[index];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void executer::set_entity_field(const std::string& field, const unsigned int entity_id,
|
||||
const chaiscript::Boxed_Value& value)
|
||||
{
|
||||
const auto entref = native::Scr_GetEntityIdRef(entity_id);
|
||||
const int id = get_field_id(entref.raw.classnum, field);
|
||||
|
||||
if (id != -1)
|
||||
{
|
||||
stack_isolation _;
|
||||
this->context_->get_parameters()->push(value);
|
||||
|
||||
native::scr_VmPub->outparamcount = native::scr_VmPub->inparamcount;
|
||||
native::scr_VmPub->inparamcount = 0;
|
||||
|
||||
if (!safe_executer::set_entity_field(entref, id))
|
||||
{
|
||||
throw std::runtime_error("Failed to set value for field '" + field + "'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->entity_fields_[entity_id][field] = value;
|
||||
}
|
||||
}
|
||||
|
||||
chaiscript::Boxed_Value executer::get_entity_field(const std::string& field, const unsigned int entity_id)
|
||||
{
|
||||
const auto entref = native::Scr_GetEntityIdRef(entity_id);
|
||||
const auto id = this->get_field_id(entref.raw.classnum, field);
|
||||
|
||||
if (id != -1)
|
||||
{
|
||||
stack_isolation _;
|
||||
|
||||
native::VariableValue value{};
|
||||
if (!safe_executer::get_entity_field(entref, id, &value))
|
||||
{
|
||||
throw std::runtime_error("Failed to get value for field '" + field + "'");
|
||||
}
|
||||
|
||||
const auto $ = gsl::finally([value]()
|
||||
{
|
||||
native::RemoveRefToValue(value.type, value.u);
|
||||
});
|
||||
|
||||
return this->context_->get_parameters()->load(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& map = this->entity_fields_[entity_id];
|
||||
const auto value = map.find(field);
|
||||
if (value != map.end())
|
||||
{
|
||||
return value->second;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void executer::notify(const std::string& event, const unsigned int entity_id,
|
||||
std::vector<chaiscript::Boxed_Value> arguments) const
|
||||
{
|
||||
stack_isolation _;
|
||||
|
||||
std::reverse(arguments.begin(), arguments.end());
|
||||
for (auto argument : arguments)
|
||||
{
|
||||
this->context_->get_parameters()->push(argument);
|
||||
}
|
||||
|
||||
const auto event_id = native::SL_GetString(event.data(), 0);
|
||||
native::Scr_NotifyId(entity_id, event_id, native::scr_VmPub->inparamcount);
|
||||
}
|
||||
|
||||
chaiscript::Boxed_Value executer::call(const std::string& function, const unsigned int entity_id,
|
||||
std::vector<chaiscript::Boxed_Value> arguments) const
|
||||
{
|
||||
const auto function_index = ::scripting::find_function_index(function, entity_id == 0);
|
||||
if (function_index < 0)
|
||||
{
|
||||
throw std::runtime_error("No function found for name '" + function + "'");
|
||||
}
|
||||
|
||||
const auto entity = function_index > 0x1C7
|
||||
? native::Scr_GetEntityIdRef(entity_id)
|
||||
: native::scr_entref_t{~0u};
|
||||
|
||||
const auto function_ptr = ::scripting::get_function_by_index(function_index);
|
||||
|
||||
stack_isolation _;
|
||||
|
||||
std::reverse(arguments.begin(), arguments.end());
|
||||
for (const auto& argument : arguments)
|
||||
{
|
||||
this->context_->get_parameters()->push(argument);
|
||||
}
|
||||
|
||||
native::scr_VmPub->outparamcount = native::scr_VmPub->inparamcount;
|
||||
native::scr_VmPub->inparamcount = 0;
|
||||
|
||||
if (!safe_executer::call(function_ptr, entity))
|
||||
{
|
||||
throw std::runtime_error("Error executing function '" + function + "'");
|
||||
}
|
||||
|
||||
return this->context_->get_parameters()->get_return_value();
|
||||
}
|
||||
|
||||
bool executer::function_exists(const std::string& function, bool prefer_global)
|
||||
{
|
||||
return ::scripting::find_function_index(function, prefer_global) >= 0;
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class context;
|
||||
|
||||
class executer final
|
||||
{
|
||||
public:
|
||||
explicit executer(context* context);
|
||||
|
||||
void set_entity_field(const std::string& field, unsigned int entity_id,
|
||||
const chaiscript::Boxed_Value& value);
|
||||
chaiscript::Boxed_Value get_entity_field(const std::string& field, unsigned int entity_id);
|
||||
|
||||
void notify(const std::string& event, unsigned int entity_id,
|
||||
std::vector<chaiscript::Boxed_Value> arguments) const;
|
||||
|
||||
chaiscript::Boxed_Value call(const std::string& function, unsigned int entity_id,
|
||||
std::vector<chaiscript::Boxed_Value> arguments) const;
|
||||
|
||||
static bool function_exists(const std::string& function, bool prefer_global);
|
||||
|
||||
private:
|
||||
context* context_;
|
||||
|
||||
std::unordered_map<unsigned int, std::unordered_map<std::string, chaiscript::Boxed_Value>> entity_fields_;
|
||||
|
||||
int get_field_id(int classnum, const std::string& field) const;
|
||||
};
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
#include <std_include.hpp>
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "functions.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
@ -70,20 +72,20 @@ namespace scripting
|
||||
return game::native::SL_GetCanonicalString(name.data());
|
||||
}
|
||||
|
||||
game::native::scr_call_t get_function_by_index(const std::uint32_t index)
|
||||
game::native::BuiltinFunction get_function_by_index(const std::uint32_t index)
|
||||
{
|
||||
static const auto function_table = SELECT_VALUE(0x186C68C, 0x1D6EB34);
|
||||
static const auto method_table = SELECT_VALUE(0x184CDB0, 0x1D4F258);
|
||||
|
||||
if (index < 0x1C7)
|
||||
{
|
||||
return reinterpret_cast<game::native::scr_call_t*>(function_table)[index - 1];
|
||||
return reinterpret_cast<game::native::BuiltinFunction*>(function_table)[index - 1];
|
||||
}
|
||||
|
||||
return reinterpret_cast<game::native::scr_call_t*>(method_table)[index];
|
||||
return reinterpret_cast<game::native::BuiltinFunction*>(method_table)[index];
|
||||
}
|
||||
|
||||
game::native::scr_call_t find_function(const std::string& name, const bool prefer_global)
|
||||
game::native::BuiltinFunction find_function(const std::string& name, const bool prefer_global)
|
||||
{
|
||||
const auto index = find_function_index(name, prefer_global);
|
||||
if (index < 0) return nullptr;
|
||||
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
@ -8,6 +7,4 @@ namespace scripting
|
||||
unsigned int find_token_id(const std::string& name);
|
||||
|
||||
int find_function_index(const std::string& name, bool prefer_global);
|
||||
game::native::scr_call_t get_function_by_index(std::uint32_t index);
|
||||
game::native::scr_call_t find_function(const std::string& name, bool prefer_global);
|
||||
}
|
||||
|
@ -1,156 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "context.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
parameters::parameters(context* context) : context_(context)
|
||||
{
|
||||
}
|
||||
|
||||
chaiscript::Boxed_Value parameters::load(const native::VariableValue value) const
|
||||
{
|
||||
if (value.type == native::SCRIPT_STRING)
|
||||
{
|
||||
const std::string string = native::SL_ConvertToString(value.u.stringValue);
|
||||
return chaiscript::var(string);
|
||||
}
|
||||
if (value.type == native::SCRIPT_FLOAT)
|
||||
{
|
||||
return chaiscript::var(value.u.floatValue);
|
||||
}
|
||||
if (value.type == native::SCRIPT_INTEGER)
|
||||
{
|
||||
return chaiscript::var(value.u.intValue);
|
||||
}
|
||||
if (value.type == native::SCRIPT_OBJECT)
|
||||
{
|
||||
return chaiscript::var(entity(this->context_, value.u.entityId));
|
||||
}
|
||||
if (value.type == native::SCRIPT_VECTOR)
|
||||
{
|
||||
std::vector<chaiscript::Boxed_Value> values;
|
||||
values.push_back(chaiscript::var(value.u.vectorValue[0]));
|
||||
values.push_back(chaiscript::var(value.u.vectorValue[1]));
|
||||
values.push_back(chaiscript::var(value.u.vectorValue[2]));
|
||||
|
||||
return chaiscript::var(values);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void parameters::push(const chaiscript::Boxed_Value& value) const
|
||||
{
|
||||
if (native::scr_VmPub->outparamcount)
|
||||
{
|
||||
native::Scr_ClearOutParams();
|
||||
}
|
||||
|
||||
if (native::scr_VmPub->top == native::scr_VmPub->maxStack)
|
||||
{
|
||||
throw std::runtime_error("Internal script stack overflow");
|
||||
}
|
||||
|
||||
native::VariableValue* value_ptr = ++native::scr_VmPub->top;
|
||||
++native::scr_VmPub->inparamcount;
|
||||
|
||||
value_ptr->type = native::SCRIPT_NONE;
|
||||
value_ptr->u.intValue = 0;
|
||||
|
||||
if (value.get_type_info() == typeid(float))
|
||||
{
|
||||
const auto real_value = this->context_->get_chai()->boxed_cast<float>(value);
|
||||
value_ptr->type = native::SCRIPT_FLOAT;
|
||||
value_ptr->u.floatValue = real_value;
|
||||
}
|
||||
else if (value.get_type_info() == typeid(double))
|
||||
{
|
||||
const auto real_value = this->context_->get_chai()->boxed_cast<double>(value);
|
||||
value_ptr->type = native::SCRIPT_FLOAT;
|
||||
value_ptr->u.floatValue = static_cast<float>(real_value);
|
||||
}
|
||||
else if (value.get_type_info() == typeid(int))
|
||||
{
|
||||
const auto real_value = this->context_->get_chai()->boxed_cast<int>(value);
|
||||
value_ptr->type = native::SCRIPT_INTEGER;
|
||||
value_ptr->u.intValue = real_value;
|
||||
}
|
||||
else if (value.get_type_info() == typeid(unsigned int))
|
||||
{
|
||||
const auto real_value = this->context_->get_chai()->boxed_cast<unsigned int>(value);
|
||||
value_ptr->type = native::SCRIPT_INTEGER;
|
||||
value_ptr->u.intValue = static_cast<int>(real_value);
|
||||
}
|
||||
else if (value.get_type_info() == typeid(bool))
|
||||
{
|
||||
const auto real_value = this->context_->get_chai()->boxed_cast<bool>(value);
|
||||
value_ptr->type = native::SCRIPT_INTEGER;
|
||||
value_ptr->u.intValue = real_value;
|
||||
}
|
||||
else if (value.get_type_info() == typeid(entity))
|
||||
{
|
||||
const auto real_value = this->context_->get_chai()->boxed_cast<entity>(value);
|
||||
value_ptr->type = native::SCRIPT_OBJECT;
|
||||
value_ptr->u.entityId = real_value.get_entity_id();
|
||||
|
||||
game::native::AddRefToValue(value_ptr);
|
||||
}
|
||||
else if (value.get_type_info() == typeid(std::string))
|
||||
{
|
||||
const auto real_value = this->context_->get_chai()->boxed_cast<std::string>(value);
|
||||
value_ptr->type = native::SCRIPT_STRING;
|
||||
value_ptr->u.stringValue = game::native::SL_GetString(real_value.data(), 0);
|
||||
}
|
||||
else if (value.get_type_info() == typeid(std::vector<chaiscript::Boxed_Value>))
|
||||
{
|
||||
float values[3];
|
||||
const auto real_value = this->context_->get_chai()->boxed_cast<std::vector<chaiscript::Boxed_Value>
|
||||
>(value);
|
||||
if (real_value.size() != 3)
|
||||
{
|
||||
throw std::runtime_error("Invalid vector length. Size must be exactly 3");
|
||||
}
|
||||
|
||||
const auto unbox_float = [&real_value, this](const size_t index) -> float
|
||||
{
|
||||
const auto value = real_value[index];
|
||||
if (value.get_type_info() == typeid(float))
|
||||
{
|
||||
return this->context_->get_chai()->boxed_cast<float>(value);
|
||||
}
|
||||
if (value.get_type_info() == typeid(double))
|
||||
{
|
||||
return float(this->context_->get_chai()->boxed_cast<double>(value));
|
||||
}
|
||||
if (value.get_type_info() == typeid(int))
|
||||
{
|
||||
return float(this->context_->get_chai()->boxed_cast<int>(value));
|
||||
}
|
||||
|
||||
throw std::runtime_error("Vector element at index " + std::to_string(index) + " is not a number");
|
||||
};
|
||||
|
||||
values[0] = unbox_float(0);
|
||||
values[1] = unbox_float(1);
|
||||
values[2] = unbox_float(2);
|
||||
|
||||
value_ptr->type = native::SCRIPT_VECTOR;
|
||||
value_ptr->u.vectorValue = native::Scr_AllocVector(values);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unable to unbox value of type '"s + value.get_type_info().bare_name() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
chaiscript::Boxed_Value parameters::get_return_value() const
|
||||
{
|
||||
if (native::scr_VmPub->inparamcount == 0) return {};
|
||||
|
||||
native::Scr_ClearOutParams();
|
||||
native::scr_VmPub->outparamcount = native::scr_VmPub->inparamcount;
|
||||
native::scr_VmPub->inparamcount = 0;
|
||||
|
||||
return this->load(native::scr_VmPub->top[1 - native::scr_VmPub->outparamcount]);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class context;
|
||||
|
||||
class parameters final
|
||||
{
|
||||
public:
|
||||
explicit parameters(context* context);
|
||||
|
||||
void push(const chaiscript::Boxed_Value& value) const;
|
||||
chaiscript::Boxed_Value load(native::VariableValue value) const;
|
||||
|
||||
chaiscript::Boxed_Value get_return_value() const;
|
||||
private:
|
||||
context* context_;
|
||||
};
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "safe_executer.hpp"
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4611)
|
||||
|
||||
namespace game::scripting::safe_executer
|
||||
{
|
||||
static_assert(sizeof(jmp_buf) == 64);
|
||||
|
||||
bool call(const native::scr_call_t function, const native::scr_entref_t entref)
|
||||
{
|
||||
*native::g_script_error_level += 1;
|
||||
if (setjmp(native::g_script_error[*native::g_script_error_level]))
|
||||
{
|
||||
*native::g_script_error_level -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
function(entref.val);
|
||||
|
||||
*native::g_script_error_level -= 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_entity_field(const native::scr_entref_t entref, const int offset)
|
||||
{
|
||||
*native::g_script_error_level += 1;
|
||||
if (setjmp(native::g_script_error[*native::g_script_error_level]))
|
||||
{
|
||||
*native::g_script_error_level -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
native::Scr_SetObjectField(entref.raw.classnum, entref.raw.entnum, offset);
|
||||
|
||||
*native::g_script_error_level -= 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_entity_field(const native::scr_entref_t entref, const int offset, native::VariableValue* value)
|
||||
{
|
||||
*native::g_script_error_level += 1;
|
||||
if (setjmp(native::g_script_error[*native::g_script_error_level]))
|
||||
{
|
||||
value->type = native::SCRIPT_NONE;
|
||||
value->u.intValue = 0;
|
||||
*native::g_script_error_level -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = native::GetEntityFieldValue(entref.raw.classnum, entref.raw.entnum, offset);
|
||||
|
||||
*native::g_script_error_level -= 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace game::scripting::safe_executer
|
||||
{
|
||||
bool call(const native::scr_call_t function, const native::scr_entref_t entref);
|
||||
bool set_entity_field(const native::scr_entref_t entref, const int offset);
|
||||
bool get_entity_field(const native::scr_entref_t entref, const int offset, native::VariableValue* value);
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
scheduler::scheduler(context* context) : context_(context)
|
||||
{
|
||||
const auto chai = this->context_->get_chai();
|
||||
|
||||
chai->add(chaiscript::user_type<task_handle>(), "_task_handle");
|
||||
chai->add(chaiscript::constructor<task_handle()>(), "_task_handle");
|
||||
chai->add(chaiscript::constructor<task_handle(const task_handle&)>(), "_task_handle");
|
||||
|
||||
chai->add(chaiscript::fun([](task_handle& lhs, const task_handle& rhs) -> task_handle&
|
||||
{
|
||||
return lhs = rhs;
|
||||
}), "=");
|
||||
|
||||
chai->add(chaiscript::fun(
|
||||
[this](const std::function<void()>& callback, const long long milliseconds) -> task_handle
|
||||
{
|
||||
return this->add(callback, milliseconds, true);
|
||||
}), "setTimeout");
|
||||
|
||||
chai->add(chaiscript::fun(
|
||||
[this](const std::function<void()>& callback, const long long milliseconds) -> task_handle
|
||||
{
|
||||
return this->add(callback, milliseconds, false);
|
||||
}), "setInterval");
|
||||
|
||||
const auto clear = [this](const task_handle& handle)
|
||||
{
|
||||
this->remove(handle);
|
||||
};
|
||||
|
||||
chai->add(chaiscript::fun(clear), "clear");
|
||||
chai->add(chaiscript::fun(clear), "clearTimeout");
|
||||
chai->add(chaiscript::fun(clear), "clearInterval");
|
||||
}
|
||||
|
||||
void scheduler::run_frame()
|
||||
{
|
||||
this->tasks_.access([&](task_list& tasks)
|
||||
{
|
||||
for (auto i = tasks.begin(); i != tasks.end();)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto diff = now - i->last_execution;
|
||||
|
||||
if (diff < i->delay)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->last_execution = now;
|
||||
|
||||
if (i->is_volatile)
|
||||
{
|
||||
i = tasks.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
i->callback();
|
||||
++i;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void scheduler::clear()
|
||||
{
|
||||
this->tasks_.access([&](task_list& tasks)
|
||||
{
|
||||
tasks.clear();
|
||||
});
|
||||
}
|
||||
|
||||
task_handle scheduler::add(const std::function<void()>& callback, const long long milliseconds,
|
||||
const bool is_volatile)
|
||||
{
|
||||
return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile);
|
||||
}
|
||||
|
||||
task_handle scheduler::add(const std::function<void()>& callback, const std::chrono::milliseconds delay,
|
||||
const bool is_volatile)
|
||||
{
|
||||
task task;
|
||||
task.is_volatile = is_volatile;
|
||||
task.callback = callback;
|
||||
task.delay = delay;
|
||||
task.last_execution = std::chrono::steady_clock::now();
|
||||
task.id = ++this->current_task_id_;
|
||||
|
||||
this->tasks_.access([&task](task_list& tasks)
|
||||
{
|
||||
tasks.emplace_back(std::move(task));
|
||||
});
|
||||
|
||||
return {task.id};
|
||||
}
|
||||
|
||||
void scheduler::remove(const task_handle& handle)
|
||||
{
|
||||
this->tasks_.access([&](task_list& tasks)
|
||||
{
|
||||
for (auto i = tasks.begin(); i != tasks.end();)
|
||||
{
|
||||
if (i->id == handle.id)
|
||||
{
|
||||
i = tasks.erase(i);
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#include <utils/concurrency.hpp>
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class context;
|
||||
|
||||
class task_handle
|
||||
{
|
||||
public:
|
||||
std::uint64_t id = 0;
|
||||
};
|
||||
|
||||
class task final : public task_handle
|
||||
{
|
||||
public:
|
||||
std::chrono::steady_clock::time_point last_execution{};
|
||||
std::function<void()> callback{};
|
||||
std::chrono::milliseconds delay{};
|
||||
bool is_volatile = false;
|
||||
};
|
||||
|
||||
class scheduler final
|
||||
{
|
||||
public:
|
||||
explicit scheduler(context* context);
|
||||
|
||||
void run_frame();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
context* context_;
|
||||
|
||||
using task_list = std::vector<task>;
|
||||
utils::concurrency::container<task_list> tasks_;
|
||||
std::atomic_int64_t current_task_id_ = 0;
|
||||
|
||||
task_handle add(const std::function<void()>& callback, long long milliseconds, bool is_volatile);
|
||||
task_handle add(const std::function<void()>& callback, std::chrono::milliseconds delay, bool is_volatile);
|
||||
|
||||
void remove(const task_handle& handle);
|
||||
};
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "stack_isolation.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
stack_isolation::stack_isolation()
|
||||
{
|
||||
this->in_param_count_ = native::scr_VmPub->inparamcount;
|
||||
this->out_param_count_ = native::scr_VmPub->outparamcount;
|
||||
this->top_ = native::scr_VmPub->top;
|
||||
this->max_stack_ = native::scr_VmPub->maxStack;
|
||||
|
||||
native::scr_VmPub->top = this->stack_;
|
||||
native::scr_VmPub->maxStack = &this->stack_[ARRAYSIZE(this->stack_) - 1];
|
||||
native::scr_VmPub->inparamcount = 0;
|
||||
native::scr_VmPub->outparamcount = 0;
|
||||
}
|
||||
|
||||
stack_isolation::~stack_isolation()
|
||||
{
|
||||
native::Scr_ClearOutParams();
|
||||
native::scr_VmPub->inparamcount = this->in_param_count_;
|
||||
native::scr_VmPub->outparamcount = this->out_param_count_;
|
||||
native::scr_VmPub->top = this->top_;
|
||||
native::scr_VmPub->maxStack = this->max_stack_;
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class stack_isolation final
|
||||
{
|
||||
public:
|
||||
stack_isolation();
|
||||
~stack_isolation();
|
||||
|
||||
private:
|
||||
native::VariableValue stack_[512]{};
|
||||
|
||||
native::VariableValue* max_stack_;
|
||||
native::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 game::scripting
|
||||
{
|
||||
variable_value::variable_value(const native::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 = native::SCRIPT_NONE;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
variable_value::~variable_value()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
const native::VariableValue& variable_value::get() const
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
|
||||
void variable_value::assign(const native::VariableValue& value)
|
||||
{
|
||||
this->value_ = value;
|
||||
native::AddRefToValue(&this->value_);
|
||||
}
|
||||
|
||||
void variable_value::release()
|
||||
{
|
||||
if (this->value_.type != native::SCRIPT_NONE)
|
||||
{
|
||||
native::RemoveRefToValue(this->value_.type, this->value_.u);
|
||||
this->value_.type = native::SCRIPT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace game::scripting
|
||||
{
|
||||
class variable_value
|
||||
{
|
||||
public:
|
||||
variable_value() = default;
|
||||
variable_value(const native::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 native::VariableValue& get() const;
|
||||
|
||||
private:
|
||||
void assign(const native::VariableValue& value);
|
||||
void release();
|
||||
|
||||
native::VariableValue value_{{0}, native::SCRIPT_NONE};
|
||||
};
|
||||
}
|
@ -523,34 +523,50 @@ namespace game
|
||||
int freeFlags;
|
||||
};
|
||||
|
||||
struct scr_entref_raw
|
||||
struct scr_entref_t
|
||||
{
|
||||
unsigned __int16 entnum;
|
||||
unsigned __int16 classnum;
|
||||
};
|
||||
|
||||
union scr_entref_t
|
||||
{
|
||||
unsigned int val;
|
||||
scr_entref_raw raw;
|
||||
};
|
||||
|
||||
typedef void (__cdecl * scr_call_t)(int entref);
|
||||
typedef void(*BuiltinFunction)();
|
||||
typedef void(*BuiltinMethod)(scr_entref_t);
|
||||
|
||||
typedef unsigned __int16 scr_string_t;
|
||||
|
||||
enum scriptType_e
|
||||
enum
|
||||
{
|
||||
SCRIPT_NONE = 0,
|
||||
SCRIPT_OBJECT = 1,
|
||||
SCRIPT_STRING = 2,
|
||||
SCRIPT_ISTRING = 2,
|
||||
SCRIPT_VECTOR = 4,
|
||||
SCRIPT_FLOAT = 5,
|
||||
SCRIPT_INTEGER = 6,
|
||||
SCRIPT_CODEPOS = 7,
|
||||
SCRIPT_END = 8,
|
||||
// Custom
|
||||
VAR_UNDEFINED = 0x0,
|
||||
VAR_BEGIN_REF = 0x1,
|
||||
VAR_POINTER = 0x1,
|
||||
VAR_STRING = 0x2,
|
||||
VAR_ISTRING = 0x3,
|
||||
VAR_VECTOR = 0x4,
|
||||
VAR_END_REF = 0x5,
|
||||
VAR_FLOAT = 0x5,
|
||||
VAR_INTEGER = 0x6,
|
||||
VAR_CODEPOS = 0x7,
|
||||
VAR_PRECODEPOS = 0x8,
|
||||
VAR_FUNCTION = 0x9,
|
||||
VAR_BUILTIN_FUNCTION = 0xA,
|
||||
VAR_BUILTIN_METHOD = 0xB,
|
||||
VAR_STACK = 0xC,
|
||||
VAR_ANIMATION = 0xD,
|
||||
VAR_PRE_ANIMATION = 0xE,
|
||||
VAR_THREAD = 0xF,
|
||||
VAR_NOTIFY_THREAD = 0x10,
|
||||
VAR_TIME_THREAD = 0x11,
|
||||
VAR_CHILD_THREAD = 0x12,
|
||||
VAR_OBJECT = 0x13,
|
||||
VAR_DEAD_ENTITY = 0x14,
|
||||
VAR_ENTITY = 0x15,
|
||||
VAR_ARRAY = 0x16,
|
||||
VAR_DEAD_THREAD = 0x17,
|
||||
VAR_COUNT = 0x18,
|
||||
VAR_FREE = 0x18,
|
||||
VAR_THREAD_LIST = 0x19,
|
||||
VAR_ENDON_LIST = 0x1A,
|
||||
VAR_TOTAL_COUNT = 0x1B,
|
||||
};
|
||||
|
||||
struct VariableStackBuffer
|
||||
@ -578,7 +594,7 @@ namespace game
|
||||
struct VariableValue
|
||||
{
|
||||
VariableUnion u;
|
||||
scriptType_e type;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct function_stack_t
|
||||
@ -603,10 +619,6 @@ namespace game
|
||||
int function_count;
|
||||
function_frame_t* function_frame;
|
||||
VariableValue* top;
|
||||
/*bool debugCode;
|
||||
bool abort_on_error;
|
||||
bool terminal_error;
|
||||
bool block_execution;*/
|
||||
unsigned int inparamcount;
|
||||
unsigned int outparamcount;
|
||||
unsigned int breakpointOutparamcount;
|
||||
@ -910,7 +922,7 @@ namespace game
|
||||
PLAYERVIEWLOCKCOUNT = 0x3,
|
||||
};
|
||||
|
||||
enum playerStateFlag
|
||||
enum
|
||||
{
|
||||
PMF_PRONE = 0x1,
|
||||
PMF_DUCKED = 0x2,
|
||||
@ -1382,6 +1394,11 @@ namespace game
|
||||
|
||||
namespace mp
|
||||
{
|
||||
enum ConfigString
|
||||
{
|
||||
CS_EFFECT_NAMES = 0x9DC,
|
||||
};
|
||||
|
||||
struct client_t
|
||||
{
|
||||
clientHeader_t header;
|
||||
@ -1402,6 +1419,11 @@ namespace game
|
||||
|
||||
namespace sp
|
||||
{
|
||||
enum ConfigString
|
||||
{
|
||||
CS_EFFECT_NAMES = 0xB8F,
|
||||
};
|
||||
|
||||
struct usercmd_s
|
||||
{
|
||||
int serverTime;
|
||||
|
@ -412,7 +412,13 @@ namespace demonware
|
||||
void dw::bd_logger_stub(int /*type*/, const char* const /*channelName*/, const char*, const char* const /*file*/,
|
||||
const char* const function, const unsigned int /*line*/, const char* const msg, ...)
|
||||
{
|
||||
char buffer[2048];
|
||||
static const auto* bd_logger_enabled = game::native::Dvar_RegisterBool("bd_logger_enabled", false, game::native::DVAR_NONE, "Enable bdLogger");
|
||||
if (!bd_logger_enabled->current.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[2048]{};
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
|
@ -1,13 +1,13 @@
|
||||
#include <std_include.hpp>
|
||||
#include <loader/module_loader.hpp>
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "game_log.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "file_system.hpp"
|
||||
#include "scripting.hpp"
|
||||
|
||||
const game::native::dvar_t* game_log::g_log;
|
||||
const game::native::dvar_t* game_log::g_logSync;
|
||||
@ -120,9 +120,7 @@ void game_log::post_load()
|
||||
utils::hook::set<void(*)()>(0x8AC858, gscr_log_print);
|
||||
|
||||
utils::hook(0x50D135, g_init_game_stub, HOOK_CALL).install()->quick();
|
||||
|
||||
utils::hook(0x573C82, g_shutdown_game_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(0x573D3A, g_shutdown_game_stub, HOOK_CALL).install()->quick();
|
||||
scripting::on_shutdown(g_shutdown_game_stub);
|
||||
|
||||
utils::hook(0x50D5F4, exit_level_stub, HOOK_JUMP).install()->quick();
|
||||
|
||||
|
@ -3,24 +3,63 @@
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "script_error.hpp"
|
||||
#include "script_loading.hpp"
|
||||
|
||||
#include "module/log_file.hpp"
|
||||
#include "module/scripting.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include <xsk/gsc/types.hpp>
|
||||
#include <xsk/resolver.hpp>
|
||||
|
||||
using namespace utils::string;
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Array count confirmed at TODO
|
||||
std::array<const char*, game::native::VAR_TOTAL_COUNT> var_typename =
|
||||
{
|
||||
"undefined",
|
||||
"object",
|
||||
"string",
|
||||
"localized string",
|
||||
"vector",
|
||||
"float",
|
||||
"int",
|
||||
"codepos",
|
||||
"precodepos",
|
||||
"function",
|
||||
"builtin function",
|
||||
"builtin method",
|
||||
"stack",
|
||||
"animation",
|
||||
"pre animation",
|
||||
"thread",
|
||||
"thread",
|
||||
"thread",
|
||||
"thread",
|
||||
"struct",
|
||||
"removed entity",
|
||||
"entity",
|
||||
"array",
|
||||
"removed thread",
|
||||
"<free>",
|
||||
"thread list",
|
||||
"endon list",
|
||||
};
|
||||
|
||||
utils::hook::detour scr_emit_function_hook;
|
||||
|
||||
unsigned int current_filename = 0;
|
||||
|
||||
std::string unknown_function_error;
|
||||
|
||||
char gsc_error_msg[1024];
|
||||
|
||||
void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos)
|
||||
{
|
||||
current_filename = filename;
|
||||
@ -82,6 +121,142 @@ namespace gsc
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::optional<std::string> get_opcode_name(const std::uint8_t opcode)
|
||||
{
|
||||
try
|
||||
{
|
||||
return {xsk::gsc::iw5::resolver::opcode_name(opcode)};
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void builtin_call_error(const std::string& error)
|
||||
{
|
||||
const auto pos = game::native::scr_function_stack->pos;
|
||||
const auto function_id = *reinterpret_cast<std::uint16_t*>(reinterpret_cast<std::size_t>(pos - 2));
|
||||
|
||||
if (function_id > (scr_func_max_id - 1))
|
||||
{
|
||||
log_file::info("in call to builtin method \"%s\"%s\n", xsk::gsc::iw5::resolver::method_name(function_id).data(), error.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
log_file::info("in call to builtin function \"%s\"%s\n", xsk::gsc::iw5::resolver::function_name(function_id).data(), error.data());
|
||||
}
|
||||
}
|
||||
|
||||
void vm_error_stub(int mark_pos)
|
||||
{
|
||||
log_file::info("******* script runtime error ********\n");
|
||||
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(SELECT_VALUE(0x1BF6928, 0x20B8E28));
|
||||
const auto error = (*gsc_error_msg) ? std::format(": {}\n", gsc_error_msg) : std::string();
|
||||
|
||||
if ((opcode_id >= 0x84 && opcode_id <= 0x8A) || (opcode_id >= 0x8B && opcode_id <= 0x91))
|
||||
{
|
||||
builtin_call_error(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto opcode = get_opcode_name(opcode_id);
|
||||
if (opcode.has_value())
|
||||
{
|
||||
log_file::info("while processing instruction %s%s\n", opcode.value().data(), error.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
log_file::info("while processing instruction 0x%X%s\n", opcode_id, error.data());
|
||||
}
|
||||
}
|
||||
|
||||
ZeroMemory(gsc_error_msg, sizeof(gsc_error_msg));
|
||||
|
||||
log_file::info("************************************\n");
|
||||
|
||||
game::native::LargeLocalResetToMark(mark_pos);
|
||||
}
|
||||
|
||||
void scr_fx_param_error([[maybe_unused]] int param_index, const char* error_string, int fx_id)
|
||||
{
|
||||
assert(error_string);
|
||||
|
||||
char fx_name[0x400]{};
|
||||
|
||||
if (fx_id)
|
||||
{
|
||||
const auto index = SELECT_VALUE(game::native::sp::CS_EFFECT_NAMES, game::native::mp::CS_EFFECT_NAMES);
|
||||
game::native::SV_GetConfigstring(fx_id + index, fx_name, 1024);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy_s(fx_name, "not successfully loaded", _TRUNCATE);
|
||||
}
|
||||
|
||||
scr_error(va("%s (effect = %s)\n", error_string, fx_name));
|
||||
}
|
||||
|
||||
void gscr_cast_int()
|
||||
{
|
||||
switch (scr_get_type(0))
|
||||
{
|
||||
case game::native::VAR_STRING:
|
||||
game::native::Scr_AddInt(std::atoi(game::native::Scr_GetString(0)));
|
||||
break;
|
||||
case game::native::VAR_FLOAT:
|
||||
game::native::Scr_AddInt(static_cast<int>(scr_get_float(0)));
|
||||
break;
|
||||
case game::native::VAR_INTEGER:
|
||||
game::native::Scr_AddInt(scr_get_int(0));
|
||||
break;
|
||||
default:
|
||||
scr_error(va("cannot cast %s to int", scr_get_type_name(0)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gscr_cast_float()
|
||||
{
|
||||
switch (scr_get_type(0))
|
||||
{
|
||||
case game::native::VAR_STRING:
|
||||
game::native::Scr_AddFloat(static_cast<float>(std::atof(game::native::Scr_GetString(0))));
|
||||
break;
|
||||
case game::native::VAR_FLOAT:
|
||||
game::native::Scr_AddFloat(scr_get_float(0));
|
||||
break;
|
||||
case game::native::VAR_INTEGER:
|
||||
game::native::Scr_AddFloat(static_cast<float>(scr_get_int(0)));
|
||||
break;
|
||||
default:
|
||||
scr_error(va("cannot cast %s to float", scr_get_type_name(0)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_cmd()
|
||||
{
|
||||
if (!scr_get_int(0))
|
||||
{
|
||||
scr_error("Assert fail");
|
||||
}
|
||||
}
|
||||
|
||||
void assert_ex_cmd()
|
||||
{
|
||||
if (!scr_get_int(0))
|
||||
{
|
||||
scr_error(utils::string::va("Assert fail: %s", game::native::Scr_GetString(1)));
|
||||
}
|
||||
}
|
||||
|
||||
void assert_msg_cmd()
|
||||
{
|
||||
scr_error(utils::string::va("Assert fail: %s", game::native::Scr_GetString(0)));
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
|
||||
@ -101,6 +276,177 @@ namespace gsc
|
||||
return {};
|
||||
}
|
||||
|
||||
unsigned int scr_get_object(unsigned int index)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::native::scr_VmPub->top - index;
|
||||
if (value->type == game::native::VAR_POINTER)
|
||||
{
|
||||
return value->u.pointerValue;
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not an object", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int scr_get_const_string(unsigned int index)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::native::scr_VmPub->top - index;
|
||||
if (game::native::Scr_CastString(value))
|
||||
{
|
||||
assert(value->type == game::native::VAR_STRING);
|
||||
return value->u.stringValue;
|
||||
}
|
||||
|
||||
game::native::Scr_ErrorInternal();
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int scr_get_const_istring(unsigned int index)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::native::scr_VmPub->top - index;
|
||||
if (value->type == game::native::VAR_ISTRING)
|
||||
{
|
||||
return value->u.stringValue;
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not a localized string", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void scr_validate_localized_string_ref([[maybe_unused]] int parm_index, const char* token, int token_len)
|
||||
{
|
||||
assert(token);
|
||||
assert(token_len >= 0);
|
||||
|
||||
if (token_len < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto char_iter = 0; char_iter < token_len; ++char_iter)
|
||||
{
|
||||
if (!std::isalnum(static_cast<unsigned char>(token[char_iter])) && token[char_iter] != '_')
|
||||
{
|
||||
scr_error(va("Illegal localized string reference: %s must contain only alpha-numeric characters and underscores", token));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scr_get_vector(unsigned int index, float* vector_value)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::native::scr_VmPub->top - index;
|
||||
if (value->type == game::native::VAR_VECTOR)
|
||||
{
|
||||
std::memcpy(vector_value, value->u.vectorValue, sizeof(game::native::vec3_t));
|
||||
return;
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not a vector", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
}
|
||||
|
||||
int scr_get_int(unsigned int index)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::native::scr_VmPub->top - index;
|
||||
if (value->type == game::native::VAR_INTEGER)
|
||||
{
|
||||
return value->u.intValue;
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not an int", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
float scr_get_float(unsigned int index)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::native::scr_VmPub->top - index;
|
||||
if (value->type == game::native::VAR_FLOAT)
|
||||
{
|
||||
return value->u.floatValue;
|
||||
}
|
||||
|
||||
if (value->type == game::native::VAR_INTEGER)
|
||||
{
|
||||
return static_cast<float>(value->u.intValue);
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not a float", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int scr_get_pointer_type(unsigned int index)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
if ((game::native::scr_VmPub->top - index)->type == game::native::VAR_POINTER)
|
||||
{
|
||||
return static_cast<int>(game::native::GetObjectType((game::native::scr_VmPub->top - index)->u.pointerValue));
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not an object", var_typename[(game::native::scr_VmPub->top - index)->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scr_get_type(unsigned int index)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
return (game::native::scr_VmPub->top - index)->type;
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void scr_error(const char* error)
|
||||
{
|
||||
strncpy_s(gsc_error_msg, error, _TRUNCATE);
|
||||
game::native::Scr_ErrorInternal();
|
||||
}
|
||||
|
||||
const char* scr_get_type_name(unsigned int index)
|
||||
{
|
||||
if (index < game::native::scr_VmPub->outparamcount)
|
||||
{
|
||||
return var_typename[(game::native::scr_VmPub->top - index)->type];
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class error final : public module
|
||||
{
|
||||
public:
|
||||
@ -111,6 +457,29 @@ namespace gsc
|
||||
utils::hook(SELECT_VALUE(0x60DABA, 0x5615FA), &compile_error_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x60DAD1, 0x561611), &compile_error_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x40DCFA, 0x56144A), &find_variable_stub, HOOK_CALL).install()->quick();
|
||||
|
||||
utils::hook(SELECT_VALUE(0x612BFC, 0x56D87D), &vm_error_stub, HOOK_CALL).install()->quick(); // LargeLocalResetToMark
|
||||
|
||||
// Restore basic error messages for commonly used scr functions
|
||||
utils::hook(SELECT_VALUE(0x52F730, 0x56A630), &scr_get_object, HOOK_JUMP).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x40FDE0, 0x56A200), &scr_get_const_string, HOOK_JUMP).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x4FD700, 0x56A420), &scr_get_const_istring, HOOK_JUMP).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x536FAC, 0x523FAE), &scr_validate_localized_string_ref, HOOK_CALL).install()->quick(); // Scr_ConstructMessageString
|
||||
utils::hook(SELECT_VALUE(0x452E90, 0x56A4D0), &scr_get_vector, HOOK_JUMP).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x51B520, 0x56A010), &scr_get_int, HOOK_JUMP).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x4D8B50, 0x56A190), &scr_get_float, HOOK_JUMP).install()->quick();
|
||||
|
||||
utils::hook(SELECT_VALUE(0x4C02D0, 0x51F230), &scr_fx_param_error, HOOK_JUMP).install()->quick();
|
||||
|
||||
utils::hook(SELECT_VALUE(0x4D6510, 0x56A980), &scr_get_pointer_type, HOOK_JUMP).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x4958D0, 0x56A8C0), &scr_get_type, HOOK_JUMP).install()->quick();
|
||||
|
||||
utils::hook::set<game::native::BuiltinFunction>(SELECT_VALUE(0x92BB58, 0x8AC040), gscr_cast_int);
|
||||
utils::hook::set<game::native::BuiltinFunction>(SELECT_VALUE(0x92BB64, 0x8AC04C), gscr_cast_float);
|
||||
|
||||
utils::hook::set<game::native::BuiltinFunction>(SELECT_VALUE(0x92B93C, 0x8ABE24), assert_cmd);
|
||||
utils::hook::set<game::native::BuiltinFunction>(SELECT_VALUE(0x92B948, 0x8ABE30), assert_ex_cmd);
|
||||
utils::hook::set<game::native::BuiltinFunction>(SELECT_VALUE(0x92B954, 0x8ABE3C), assert_msg_cmd);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
|
@ -3,4 +3,18 @@
|
||||
namespace gsc
|
||||
{
|
||||
std::optional<std::pair<std::string, std::string>> find_function(const char* pos);
|
||||
|
||||
unsigned int scr_get_object(unsigned int index);
|
||||
unsigned int scr_get_const_string(unsigned int index);
|
||||
unsigned int scr_get_const_istring(unsigned int index);
|
||||
void scr_validate_localized_string_ref(int parm_index, const char* token, int token_len);
|
||||
void scr_get_vector(unsigned int index, float* vector_value);
|
||||
int scr_get_int(unsigned int index);
|
||||
float scr_get_float(unsigned int index);
|
||||
|
||||
int scr_get_pointer_type(unsigned int index);
|
||||
int scr_get_type(unsigned int index);
|
||||
const char* scr_get_type_name(unsigned int index);
|
||||
|
||||
void scr_error(const char* error);
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
std::uint16_t scr_func_max_id = 0x1C7;
|
||||
|
||||
namespace
|
||||
{
|
||||
auto compiler = ::gsc::compiler();
|
||||
@ -165,6 +167,23 @@ namespace gsc
|
||||
utils::hook(SELECT_VALUE(0x44685E, 0x56B13E), find_script, HOOK_CALL).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x446868, 0x56B148), db_is_x_asset_default, HOOK_CALL).install()->quick();
|
||||
|
||||
// Allow custom scripts to include other custom scripts
|
||||
xsk::gsc::iw5::resolver::init([](const auto& include_name) -> std::vector<std::uint8_t>
|
||||
{
|
||||
const auto real_name = include_name + ".gsc";
|
||||
|
||||
std::string file_buffer;
|
||||
if (!read_script_file(real_name, &file_buffer) || file_buffer.empty())
|
||||
{
|
||||
throw std::runtime_error(std::format("Could not load gsc file '{}'", real_name));
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> result;
|
||||
result.assign(file_buffer.begin(), file_buffer.end());
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
scripting::on_shutdown([](int free_scripts) -> void
|
||||
{
|
||||
if (free_scripts)
|
||||
|
@ -2,5 +2,7 @@
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
extern std::uint16_t scr_func_max_id;
|
||||
|
||||
game::native::ScriptFile* find_script(game::native::XAssetType type, const char* name, int allow_create_default);
|
||||
}
|
||||
|
@ -79,6 +79,8 @@ void log_file::info(const char* fmt, ...)
|
||||
{
|
||||
com_log_print_message(msg);
|
||||
}
|
||||
|
||||
printf("%s", msg);
|
||||
}
|
||||
|
||||
void log_file::post_load()
|
||||
@ -86,7 +88,7 @@ void log_file::post_load()
|
||||
// The game closes the logfile handle in Com_Quit_f
|
||||
|
||||
com_logfile = game::native::Dvar_RegisterInt("logfile", 1,
|
||||
0, 2, 0, "Write to log file - 0 = disabled, 1 = async file write, 2 = Sync every write");
|
||||
0, 2, game::native::DVAR_NONE, "Write to log file - 0 = disabled, 1 = async file write, 2 = Sync every write");
|
||||
|
||||
log_file_name = SELECT_VALUE("console_sp.log", "console_mp.log");
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ private:
|
||||
|
||||
static __declspec(noreturn) void long_jump_stub(jmp_buf buf, const int value) noexcept(false)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
printf("Unwinding the stack...\n");
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
#include <std_include.hpp>
|
||||
#include <loader/module_loader.hpp>
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
#include "game/scripting/context.hpp"
|
||||
#include "game/scripting/functions.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
#include "scripting.hpp"
|
||||
|
||||
#include "gsc/script_loading.hpp"
|
||||
|
||||
namespace scripting
|
||||
@ -129,171 +126,13 @@ namespace scripting
|
||||
public:
|
||||
void post_load() override
|
||||
{
|
||||
start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2), &start_execution_stub, HOOK_CALL) //
|
||||
->install() //
|
||||
->quick();
|
||||
|
||||
stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46), &stop_execution_stub, HOOK_CALL) //
|
||||
->install() //
|
||||
->quick();
|
||||
|
||||
utils::hook(SELECT_VALUE(0x4F9706, 0x5772A0), &frame_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x4FFA48, 0x5774AB), &frame_stub, HOOK_CALL).install()->quick();
|
||||
// Only relevant one?
|
||||
|
||||
utils::hook(SELECT_VALUE(0x6109F3, 0x56B637), &vm_notify_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x6128BE, 0x56D541), &vm_notify_stub, HOOK_CALL).install()->quick();
|
||||
|
||||
if (game::is_sp())
|
||||
{
|
||||
utils::hook(0x50C57E, &frame_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(0x6523A3, &frame_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(0x5145D2, &frame_stub, HOOK_CALL).install()->quick();
|
||||
|
||||
utils::hook(0x610970, &vm_notify_stub, HOOK_JUMP).install()->quick();
|
||||
}
|
||||
|
||||
utils::hook(SELECT_VALUE(0x44690A, 0x56B1EA), &scr_set_thread_position, HOOK_CALL).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x4232A8, 0x561748), &process_script, HOOK_CALL).install()->quick();
|
||||
|
||||
utils::hook(SELECT_VALUE(0x651E1B, 0x573C82), &g_shutdown_game_stub, HOOK_CALL).install()->quick();
|
||||
utils::hook(SELECT_VALUE(0x651ECC, 0x573D3A), &g_shutdown_game_stub, HOOK_CALL).install()->quick();
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
{
|
||||
this->scripts_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<game::scripting::context>> scripts_;
|
||||
|
||||
void load_scripts()
|
||||
{
|
||||
const auto script_dir = "userraw/scripts/"s;
|
||||
|
||||
if (!utils::io::directory_exists(script_dir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto scripts = utils::io::list_files(script_dir);
|
||||
|
||||
for (const auto& script : scripts)
|
||||
{
|
||||
if (script.substr(script.find_last_of('.') + 1) == "chai")
|
||||
{
|
||||
try
|
||||
{
|
||||
auto context = std::make_unique<game::scripting::context>();
|
||||
context->get_chai()->eval_file(script);
|
||||
this->scripts_.push_back(std::move(context));
|
||||
}
|
||||
catch (chaiscript::exception::eval_error& e)
|
||||
{
|
||||
throw std::runtime_error(e.pretty_print());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void start_execution()
|
||||
{
|
||||
try
|
||||
{
|
||||
this->load_scripts();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
propagate_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
void stop_execution()
|
||||
{
|
||||
this->scripts_.clear();
|
||||
}
|
||||
|
||||
void run_frame() const
|
||||
{
|
||||
for (const auto& script : this->scripts_)
|
||||
{
|
||||
script->get_scheduler()->run_frame();
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch(game::scripting::event* event) const
|
||||
{
|
||||
for (const auto& script : this->scripts_)
|
||||
{
|
||||
script->get_event_handler()->dispatch(event);
|
||||
}
|
||||
}
|
||||
|
||||
static utils::hook start_hook_;
|
||||
static utils::hook stop_hook_;
|
||||
|
||||
static void propagate_error(const std::exception& e)
|
||||
{
|
||||
printf("\n******* Script execution error *******\n");
|
||||
printf("%s\n", e.what());
|
||||
printf("**************************************\n\n");
|
||||
|
||||
scheduler::once([]
|
||||
{
|
||||
game::native::Com_Error(game::native::errorParm_t::ERR_SCRIPT, "Script execution error\n(see console for actual details)\n");
|
||||
}, scheduler::pipeline::main);
|
||||
}
|
||||
|
||||
static void start_execution_stub()
|
||||
{
|
||||
module_loader::get<scripting_class>()->start_execution();
|
||||
static_cast<void(*)()>(start_hook_.get_original())();
|
||||
}
|
||||
|
||||
static void stop_execution_stub()
|
||||
{
|
||||
module_loader::get<scripting_class>()->stop_execution();
|
||||
static_cast<void(*)()>(stop_hook_.get_original())();
|
||||
}
|
||||
|
||||
static void vm_notify_stub(const unsigned int notify_id, const unsigned short type,
|
||||
game::native::VariableValue* stack)
|
||||
{
|
||||
try
|
||||
{
|
||||
game::scripting::event e;
|
||||
e.name = game::native::SL_ConvertToString(type);
|
||||
e.entity_id = notify_id;
|
||||
|
||||
if (e.name == "touch") return; // Skip that for now
|
||||
|
||||
for (auto* value = stack; value->type != game::native::SCRIPT_END; --value)
|
||||
{
|
||||
e.arguments.emplace_back(*value);
|
||||
}
|
||||
|
||||
module_loader::get<scripting_class>()->dispatch(&e);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
propagate_error(e);
|
||||
}
|
||||
|
||||
game::native::VM_Notify(notify_id, type, stack);
|
||||
}
|
||||
|
||||
static int frame_stub(const int a1, const int a2)
|
||||
{
|
||||
module_loader::get<scripting_class>()->run_frame();
|
||||
return game::native::G_RunFrame(a1, a2);
|
||||
}
|
||||
};
|
||||
|
||||
utils::hook scripting_class::start_hook_;
|
||||
utils::hook scripting_class::stop_hook_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
REGISTER_MODULE(scripting::scripting_class)
|
||||
|
@ -90,7 +90,7 @@ game::native::gentity_s* test_clients::sv_add_test_client()
|
||||
|
||||
game::native::SV_ClientEnterWorld(client, &cmd);
|
||||
|
||||
assert(client->gentity != nullptr);
|
||||
assert(client->gentity);
|
||||
|
||||
return client->gentity;
|
||||
}
|
||||
@ -99,7 +99,7 @@ void test_clients::gscr_add_test_client()
|
||||
{
|
||||
const auto* ent = sv_add_test_client();
|
||||
|
||||
if (ent != nullptr)
|
||||
if (ent)
|
||||
{
|
||||
game::native::Scr_AddEntityNum(ent->s.number, 0);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4458)
|
||||
#pragma warning(disable: 4702)
|
||||
#pragma warning(disable: 6297)
|
||||
#pragma warning(disable: 6385)
|
||||
#pragma warning(disable: 6386)
|
||||
@ -44,11 +43,13 @@
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@ -62,8 +63,6 @@
|
||||
|
||||
#include <udis86.h>
|
||||
|
||||
#include <chaiscript/chaiscript.hpp>
|
||||
|
||||
#pragma comment(lib, "gdiplus.lib")
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user