deps: update chai script

This commit is contained in:
Diavolo 2022-12-20 15:49:56 +01:00
parent b389bf4a62
commit 94c7364dd1
No known key found for this signature in database
GPG Key ID: FA77F074E98D98A5
40 changed files with 530 additions and 1571 deletions

4
.gitmodules vendored
View File

@ -26,10 +26,6 @@
[submodule "deps/udis86"] [submodule "deps/udis86"]
path = deps/udis86 path = deps/udis86
url = https://github.com/vmt/udis86.git 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"] [submodule "deps/zstd"]
path = deps/zstd path = deps/zstd
url = https://github.com/facebook/zstd.git url = https://github.com/facebook/zstd.git

1
deps/ChaiScript vendored

@ -1 +0,0 @@
Subproject commit 69123db3dcde16cb38adae753c40edde86254246

View File

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

View File

@ -39,10 +39,17 @@ namespace game
SL_GetStringOfSize_t SL_GetStringOfSize; SL_GetStringOfSize_t SL_GetStringOfSize;
Scr_AddEntityNum_t Scr_AddEntityNum; 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_Notify_t Scr_Notify;
Scr_NotifyLevel_t Scr_NotifyLevel; Scr_NotifyLevel_t Scr_NotifyLevel;
Scr_GetNumParam_t Scr_GetNumParam; Scr_GetNumParam_t Scr_GetNumParam;
Scr_GetString_t Scr_GetString; Scr_GetString_t Scr_GetString;
Scr_CastString_t Scr_CastString;
Scr_ErrorInternal_t Scr_ErrorInternal;
GetObjectType_t GetObjectType;
Sys_ShowConsole_t Sys_ShowConsole; Sys_ShowConsole_t Sys_ShowConsole;
Sys_Error_t Sys_Error; Sys_Error_t Sys_Error;
@ -66,6 +73,7 @@ namespace game
SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString; SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString;
SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString; SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString;
SV_SpawnServer_t SV_SpawnServer; SV_SpawnServer_t SV_SpawnServer;
SV_GetConfigstring_t SV_GetConfigstring;
XUIDToString_t XUIDToString; XUIDToString_t XUIDToString;
@ -90,6 +98,8 @@ namespace game
player_die_t player_die; player_die_t player_die;
LargeLocalResetToMark_t LargeLocalResetToMark;
decltype(longjmp)* _longjmp; decltype(longjmp)* _longjmp;
CmdArgs* sv_cmd_args; CmdArgs* sv_cmd_args;
@ -99,6 +109,7 @@ namespace game
char** scrMemTreePub; char** scrMemTreePub;
char* scrMemTreeGlob; char* scrMemTreeGlob;
function_stack_t* scr_function_stack;
scrVarPub_t* scr_VarPub; scrVarPub_t* scr_VarPub;
scrVmPub_t* scr_VmPub; scrVmPub_t* scr_VmPub;
@ -167,17 +178,17 @@ namespace game
void AddRefToValue(VariableValue* value) void AddRefToValue(VariableValue* value)
{ {
if (value->type == SCRIPT_OBJECT) if (value->type == VAR_POINTER)
{ {
++scrVarGlob[4 * value->u.entityId]; ++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; static const auto size = is_sp() ? 16 : 12;
const auto ref_count = reinterpret_cast<unsigned volatile *>(*scrMemTreePub + size * value->u.stringValue); const auto ref_count = reinterpret_cast<unsigned volatile *>(*scrMemTreePub + size * value->u.stringValue);
InterlockedIncrement(ref_count); InterlockedIncrement(ref_count);
} }
else if (value->type == SCRIPT_VECTOR) else if (value->type == VAR_VECTOR)
{ {
if (!*PBYTE(value->u.vectorValue - 1)) if (!*PBYTE(value->u.vectorValue - 1))
{ {
@ -269,8 +280,8 @@ namespace game
static auto ent_array = reinterpret_cast<WORD*>(SELECT_VALUE(0x19AFC82, 0x1E72182)); static auto ent_array = reinterpret_cast<WORD*>(SELECT_VALUE(0x19AFC82, 0x1E72182));
scr_entref_t result; scr_entref_t result;
result.raw.classnum = static_cast<unsigned short>(class_array[2 * id]) >> 8; result.classnum = static_cast<unsigned short>(class_array[2 * id]) >> 8;
result.raw.entnum = ent_array[4 * id]; result.entnum = ent_array[4 * id];
return result; return result;
} }
@ -323,19 +334,6 @@ namespace game
(SELECT_VALUE(0x42CAD0, 0x52BCC0))(classnum, entnum, offset); (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) const char* SL_ConvertToString(const unsigned int stringValue)
{ {
if (!stringValue) return nullptr; if (!stringValue) return nullptr;
@ -723,11 +721,18 @@ namespace game
native::SL_GetStringOfSize = native::SL_GetStringOfSize_t(SELECT_VALUE(0x4E13F0, 0x564650)); native::SL_GetStringOfSize = native::SL_GetStringOfSize_t(SELECT_VALUE(0x4E13F0, 0x564650));
native::Scr_AddEntityNum = native::Scr_AddEntityNum_t(SELECT_VALUE(0x0, 0x56ABC0)); 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_Notify = native::Scr_Notify_t(SELECT_VALUE(0x4895B0, 0x52B190));
native::Scr_NotifyLevel = native::Scr_NotifyLevel_t(SELECT_VALUE(0x445E10, 0x56B6B0)); native::Scr_NotifyLevel = native::Scr_NotifyLevel_t(SELECT_VALUE(0x445E10, 0x56B6B0));
native::Scr_GetNumParam = native::Scr_GetNumParam_t(SELECT_VALUE(0x4C6FE0, 0x56AA10)); native::Scr_GetNumParam = native::Scr_GetNumParam_t(SELECT_VALUE(0x4C6FE0, 0x56AA10));
native::Scr_GetString = native::Scr_GetString_t(SELECT_VALUE(0x497530, 0x56A3D0)); 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_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590));
native::Sys_Error = native::Sys_Error_t(SELECT_VALUE(0x490D90, 0x5CC3B0)); 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_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_Cmd_EndTokenizedString = native::SV_Cmd_EndTokenizedString_t(SELECT_VALUE(0x0, 0x545D70));
native::SV_SpawnServer = native::SV_SpawnServer_t(SELECT_VALUE(0x0, 0x575020)); 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_GameSendServerCommand = native::mp::SV_GameSendServerCommand_t(0x573220);
native::mp::SV_GetGuid = native::mp::SV_GetGuid_t(0x573990); 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::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::_longjmp = reinterpret_cast<decltype(longjmp)*>(SELECT_VALUE(0x73AC20, 0x7363BC));
native::sv_cmd_args = reinterpret_cast<native::CmdArgs*>(SELECT_VALUE(0x1757218, 0x1CAA998)); 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::scrMemTreePub = reinterpret_cast<char**>(SELECT_VALUE(0x196FB00, 0x1E32000));
native::scrMemTreeGlob = reinterpret_cast<char*>(SELECT_VALUE(0x186DA00, 0x1D6FF00)); 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_VarPub = reinterpret_cast<native::scrVarPub_t*>(SELECT_VALUE(0x0, 0x208E188));
native::scr_VmPub = reinterpret_cast<native::scrVmPub_t*>(SELECT_VALUE(0x1BF2580, 0x20B4A80)); native::scr_VmPub = reinterpret_cast<native::scrVmPub_t*>(SELECT_VALUE(0x1BF2580, 0x20B4A80));

View File

@ -70,7 +70,7 @@ namespace game
typedef void* (*MT_AllocIndex_t)(int numBytes, int type); typedef void* (*MT_AllocIndex_t)(int numBytes, int type);
extern MT_AllocIndex_t MT_AllocIndex; 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; extern RemoveRefToValue_t RemoveRefToValue;
typedef unsigned int (*SL_GetStringOfSize_t)(const char* str, unsigned int user, unsigned int len, int type); 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); typedef void (*Scr_AddEntityNum_t)(int entnum, unsigned int classnum);
extern Scr_AddEntityNum_t Scr_AddEntityNum; 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); typedef void (*Scr_Notify_t)(gentity_s* ent, scr_string_t, unsigned int paramcount);
extern Scr_Notify_t Scr_Notify; extern Scr_Notify_t Scr_Notify;
@ -91,6 +100,15 @@ namespace game
typedef const char* (*Scr_GetString_t)(unsigned int index); typedef const char* (*Scr_GetString_t)(unsigned int index);
extern Scr_GetString_t Scr_GetString; 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)(); typedef void (*Sys_ShowConsole_t)();
extern Sys_ShowConsole_t Sys_ShowConsole; extern Sys_ShowConsole_t Sys_ShowConsole;
@ -139,6 +157,9 @@ namespace game
typedef void (*SV_SpawnServer_t)(const char* server, int mapIsPreloaded, int savegame); typedef void (*SV_SpawnServer_t)(const char* server, int mapIsPreloaded, int savegame);
extern SV_SpawnServer_t SV_SpawnServer; 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); typedef void (*XUIDToString_t)(const unsigned __int64* xuid, char* str);
extern XUIDToString_t XUIDToString; 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); 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; extern player_die_t player_die;
typedef void (*LargeLocalResetToMark_t)(int markPos);
extern LargeLocalResetToMark_t LargeLocalResetToMark;
extern decltype(longjmp)* _longjmp; extern decltype(longjmp)* _longjmp;
constexpr auto CMD_MAX_NESTING = 8; constexpr auto CMD_MAX_NESTING = 8;
@ -192,6 +216,7 @@ namespace game
extern char** scrMemTreePub; extern char** scrMemTreePub;
extern char* scrMemTreeGlob; extern char* scrMemTreeGlob;
extern function_stack_t* scr_function_stack;
extern scrVarPub_t* scr_VarPub; extern scrVarPub_t* scr_VarPub;
extern scrVmPub_t* scr_VmPub; extern scrVmPub_t* scr_VmPub;
@ -293,8 +318,6 @@ namespace game
scr_entref_t Scr_GetEntityIdRef(unsigned int id); scr_entref_t Scr_GetEntityIdRef(unsigned int id);
void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount); void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount);
int Scr_SetObjectField(unsigned int classnum, int entnum, int offset); 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); const char* SL_ConvertToString(unsigned int stringValue);
unsigned int SL_GetString(const char* str, unsigned int user); unsigned int SL_GetString(const char* str, unsigned int user);

View File

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

View File

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

View File

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

View File

@ -1,7 +0,0 @@
#pragma once
#include "context.hpp"
namespace game::scripting::context_initializer
{
void initialize(context* context);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,6 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "game/game.hpp"
#include "functions.hpp" #include "functions.hpp"
#include <utils/string.hpp> #include <utils/string.hpp>
@ -70,20 +72,20 @@ namespace scripting
return game::native::SL_GetCanonicalString(name.data()); 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 function_table = SELECT_VALUE(0x186C68C, 0x1D6EB34);
static const auto method_table = SELECT_VALUE(0x184CDB0, 0x1D4F258); static const auto method_table = SELECT_VALUE(0x184CDB0, 0x1D4F258);
if (index < 0x1C7) 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); const auto index = find_function_index(name, prefer_global);
if (index < 0) return nullptr; if (index < 0) return nullptr;

View File

@ -1,5 +1,4 @@
#pragma once #pragma once
#include "game/game.hpp"
namespace scripting namespace scripting
{ {
@ -8,6 +7,4 @@ namespace scripting
unsigned int find_token_id(const std::string& name); unsigned int find_token_id(const std::string& name);
int find_function_index(const std::string& name, bool prefer_global); 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);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -523,34 +523,50 @@ namespace game
int freeFlags; int freeFlags;
}; };
struct scr_entref_raw struct scr_entref_t
{ {
unsigned __int16 entnum; unsigned __int16 entnum;
unsigned __int16 classnum; unsigned __int16 classnum;
}; };
union scr_entref_t typedef void(*BuiltinFunction)();
{ typedef void(*BuiltinMethod)(scr_entref_t);
unsigned int val;
scr_entref_raw raw;
};
typedef void (__cdecl * scr_call_t)(int entref);
typedef unsigned __int16 scr_string_t; typedef unsigned __int16 scr_string_t;
enum scriptType_e enum
{ {
SCRIPT_NONE = 0, VAR_UNDEFINED = 0x0,
SCRIPT_OBJECT = 1, VAR_BEGIN_REF = 0x1,
SCRIPT_STRING = 2, VAR_POINTER = 0x1,
SCRIPT_ISTRING = 2, VAR_STRING = 0x2,
SCRIPT_VECTOR = 4, VAR_ISTRING = 0x3,
SCRIPT_FLOAT = 5, VAR_VECTOR = 0x4,
SCRIPT_INTEGER = 6, VAR_END_REF = 0x5,
SCRIPT_CODEPOS = 7, VAR_FLOAT = 0x5,
SCRIPT_END = 8, VAR_INTEGER = 0x6,
// Custom 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 struct VariableStackBuffer
@ -578,7 +594,7 @@ namespace game
struct VariableValue struct VariableValue
{ {
VariableUnion u; VariableUnion u;
scriptType_e type; int type;
}; };
struct function_stack_t struct function_stack_t
@ -603,10 +619,6 @@ namespace game
int function_count; int function_count;
function_frame_t* function_frame; function_frame_t* function_frame;
VariableValue* top; VariableValue* top;
/*bool debugCode;
bool abort_on_error;
bool terminal_error;
bool block_execution;*/
unsigned int inparamcount; unsigned int inparamcount;
unsigned int outparamcount; unsigned int outparamcount;
unsigned int breakpointOutparamcount; unsigned int breakpointOutparamcount;
@ -910,7 +922,7 @@ namespace game
PLAYERVIEWLOCKCOUNT = 0x3, PLAYERVIEWLOCKCOUNT = 0x3,
}; };
enum playerStateFlag enum
{ {
PMF_PRONE = 0x1, PMF_PRONE = 0x1,
PMF_DUCKED = 0x2, PMF_DUCKED = 0x2,
@ -1382,6 +1394,11 @@ namespace game
namespace mp namespace mp
{ {
enum ConfigString
{
CS_EFFECT_NAMES = 0x9DC,
};
struct client_t struct client_t
{ {
clientHeader_t header; clientHeader_t header;
@ -1402,6 +1419,11 @@ namespace game
namespace sp namespace sp
{ {
enum ConfigString
{
CS_EFFECT_NAMES = 0xB8F,
};
struct usercmd_s struct usercmd_s
{ {
int serverTime; int serverTime;

View File

@ -412,7 +412,13 @@ namespace demonware
void dw::bd_logger_stub(int /*type*/, const char* const /*channelName*/, const char*, const char* const /*file*/, 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, ...) 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_list ap;
va_start(ap, msg); va_start(ap, msg);

View File

@ -1,13 +1,13 @@
#include <std_include.hpp> #include <std_include.hpp>
#include <loader/module_loader.hpp> #include <loader/module_loader.hpp>
#include "game/game.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include "game/game.hpp"
#include "game_log.hpp" #include "game_log.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "file_system.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_log;
const game::native::dvar_t* game_log::g_logSync; 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::set<void(*)()>(0x8AC858, gscr_log_print);
utils::hook(0x50D135, g_init_game_stub, HOOK_CALL).install()->quick(); utils::hook(0x50D135, g_init_game_stub, HOOK_CALL).install()->quick();
scripting::on_shutdown(g_shutdown_game_stub);
utils::hook(0x573C82, g_shutdown_game_stub, HOOK_CALL).install()->quick();
utils::hook(0x573D3A, g_shutdown_game_stub, HOOK_CALL).install()->quick();
utils::hook(0x50D5F4, exit_level_stub, HOOK_JUMP).install()->quick(); utils::hook(0x50D5F4, exit_level_stub, HOOK_JUMP).install()->quick();

View File

@ -3,24 +3,63 @@
#include "game/game.hpp" #include "game/game.hpp"
#include "script_error.hpp" #include "script_error.hpp"
#include "script_loading.hpp"
#include "module/log_file.hpp"
#include "module/scripting.hpp" #include "module/scripting.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
using namespace utils::string; using namespace utils::string;
namespace gsc namespace gsc
{ {
namespace 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; utils::hook::detour scr_emit_function_hook;
unsigned int current_filename = 0; unsigned int current_filename = 0;
std::string unknown_function_error; 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) void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos)
{ {
current_filename = filename; current_filename = filename;
@ -82,6 +121,142 @@ namespace gsc
return res; 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) std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
@ -101,6 +276,177 @@ namespace gsc
return {}; 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 class error final : public module
{ {
public: public:
@ -111,6 +457,29 @@ namespace gsc
utils::hook(SELECT_VALUE(0x60DABA, 0x5615FA), &compile_error_stub, HOOK_CALL).install()->quick(); 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(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(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 void pre_destroy() override

View File

@ -3,4 +3,18 @@
namespace gsc namespace gsc
{ {
std::optional<std::pair<std::string, std::string>> find_function(const char* pos); 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);
} }

View File

@ -19,6 +19,8 @@
namespace gsc namespace gsc
{ {
std::uint16_t scr_func_max_id = 0x1C7;
namespace namespace
{ {
auto compiler = ::gsc::compiler(); 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(0x44685E, 0x56B13E), find_script, HOOK_CALL).install()->quick();
utils::hook(SELECT_VALUE(0x446868, 0x56B148), db_is_x_asset_default, 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 scripting::on_shutdown([](int free_scripts) -> void
{ {
if (free_scripts) if (free_scripts)

View File

@ -2,5 +2,7 @@
namespace gsc 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); game::native::ScriptFile* find_script(game::native::XAssetType type, const char* name, int allow_create_default);
} }

View File

@ -79,6 +79,8 @@ void log_file::info(const char* fmt, ...)
{ {
com_log_print_message(msg); com_log_print_message(msg);
} }
printf("%s", msg);
} }
void log_file::post_load() void log_file::post_load()
@ -86,7 +88,7 @@ void log_file::post_load()
// The game closes the logfile handle in Com_Quit_f // The game closes the logfile handle in Com_Quit_f
com_logfile = game::native::Dvar_RegisterInt("logfile", 1, 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"); log_file_name = SELECT_VALUE("console_sp.log", "console_mp.log");
} }

View File

@ -73,7 +73,7 @@ private:
static __declspec(noreturn) void long_jump_stub(jmp_buf buf, const int value) noexcept(false) static __declspec(noreturn) void long_jump_stub(jmp_buf buf, const int value) noexcept(false)
{ {
#ifdef DEBUG #ifdef _DEBUG
{ {
printf("Unwinding the stack...\n"); printf("Unwinding the stack...\n");
} }

View File

@ -1,14 +1,11 @@
#include <std_include.hpp> #include <std_include.hpp>
#include <loader/module_loader.hpp> #include <loader/module_loader.hpp>
#include "game/game.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/io.hpp>
#include "game/scripting/context.hpp"
#include "game/scripting/functions.hpp" #include "game/scripting/functions.hpp"
#include "scheduler.hpp"
#include "scripting.hpp"
#include "gsc/script_loading.hpp" #include "gsc/script_loading.hpp"
namespace scripting namespace scripting
@ -129,171 +126,13 @@ namespace scripting
public: public:
void post_load() override 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(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(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(0x651E1B, 0x573C82), &g_shutdown_game_stub, HOOK_CALL).install()->quick();
utils::hook(SELECT_VALUE(0x651ECC, 0x573D3A), &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) REGISTER_MODULE(scripting::scripting_class)

View File

@ -90,7 +90,7 @@ game::native::gentity_s* test_clients::sv_add_test_client()
game::native::SV_ClientEnterWorld(client, &cmd); game::native::SV_ClientEnterWorld(client, &cmd);
assert(client->gentity != nullptr); assert(client->gentity);
return client->gentity; return client->gentity;
} }
@ -99,7 +99,7 @@ void test_clients::gscr_add_test_client()
{ {
const auto* ent = sv_add_test_client(); const auto* ent = sv_add_test_client();
if (ent != nullptr) if (ent)
{ {
game::native::Scr_AddEntityNum(ent->s.number, 0); game::native::Scr_AddEntityNum(ent->s.number, 0);
} }

View File

@ -6,7 +6,6 @@
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4244) #pragma warning(disable: 4244)
#pragma warning(disable: 4458) #pragma warning(disable: 4458)
#pragma warning(disable: 4702)
#pragma warning(disable: 6297) #pragma warning(disable: 6297)
#pragma warning(disable: 6385) #pragma warning(disable: 6385)
#pragma warning(disable: 6386) #pragma warning(disable: 6386)
@ -44,11 +43,13 @@
#include <filesystem> #include <filesystem>
#include <format> #include <format>
#include <fstream> #include <fstream>
#include <functional>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <regex> #include <regex>
#include <thread> #include <thread>
#include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -62,8 +63,6 @@
#include <udis86.h> #include <udis86.h>
#include <chaiscript/chaiscript.hpp>
#pragma comment(lib, "gdiplus.lib") #pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")