#include <../modmanager.h> #pragma once void InitialiseClientSquirrel(HMODULE baseAddress); void InitialiseServerSquirrel(HMODULE baseAddress); // stolen from ttf2sdk: sqvm types typedef float SQFloat; typedef long SQInteger; typedef unsigned long SQUnsignedInteger; typedef char SQChar; typedef SQUnsignedInteger SQBool; typedef SQInteger SQRESULT; #define uint8 char const SQRESULT SQRESULT_ERROR = -1; const SQRESULT SQRESULT_NULL = 0; const SQRESULT SQRESULT_NOTNULL = 1; typedef SQInteger (*SQFunction)(void* sqvm); enum SQReturnTypeEnum { SqReturnFloat = 0x1, SqReturnVector = 0x3, SqReturnInteger = 0x5, SqReturnBoolean = 0x6, SqReturnEntity = 0xD, SqReturnString = 0x21, SqReturnDefault = 0x20, SqReturnArrays = 0x25, SqReturnAsset = 0x28, SqReturnTable = 0x26, }; const char* sq_getTypeName(int type); struct CompileBufferState { const SQChar* buffer; const SQChar* bufferPlusLength; const SQChar* bufferAgain; CompileBufferState(const std::string& code) { buffer = code.c_str(); bufferPlusLength = code.c_str() + code.size(); bufferAgain = code.c_str(); } }; struct SQFuncRegistration { const char* squirrelFuncName; const char* cppFuncName; const char* helpText; const char* returnTypeString; const char* argTypes; __int32 unknown1; __int32 devLevel; const char* shortNameMaybe; __int32 unknown2; SQReturnTypeEnum returnTypeEnum; __int32* externalBufferPointer; __int64 externalBufferSize; __int64 unknown3; __int64 unknown4; void* funcPtr; SQFuncRegistration() { memset(this, 0, sizeof(SQFuncRegistration)); this->returnTypeEnum = SqReturnDefault; } }; SQReturnTypeEnum GetReturnTypeEnumFromString(const char* returnTypeString); struct CallInfo; struct SQTable; struct SQString; struct SQFunctionProto; struct SQClosure; struct SQSharedState; struct StringTable; struct SQStructInstance; struct SQStructDef; struct SQNativeClosure; struct SQArray; struct SQInstruction; /* 127 */ enum SQObjectType : __int32 { _RT_NULL = 0x1, _RT_INTEGER = 0x2, _RT_FLOAT = 0x4, _RT_BOOL = 0x8, _RT_STRING = 0x10, _RT_TABLE = 0x20, _RT_ARRAY = 0x40, _RT_USERDATA = 0x80, _RT_CLOSURE = 0x100, _RT_NATIVECLOSURE = 0x200, _RT_GENERATOR = 0x400, OT_USERPOINTER = 0x800, _RT_USERPOINTER = 0x800, _RT_THREAD = 0x1000, _RT_FUNCPROTO = 0x2000, _RT_CLASS = 0x4000, _RT_INSTANCE = 0x8000, _RT_WEAKREF = 0x10000, OT_VECTOR = 0x40000, SQOBJECT_CANBEFALSE = 0x1000000, OT_NULL = 0x1000001, OT_BOOL = 0x1000008, SQOBJECT_DELEGABLE = 0x2000000, SQOBJECT_NUMERIC = 0x4000000, OT_INTEGER = 0x5000002, OT_FLOAT = 0x5000004, SQOBJECT_REF_COUNTED = 0x8000000, OT_STRING = 0x8000010, OT_ARRAY = 0x8000040, OT_CLOSURE = 0x8000100, OT_NATIVECLOSURE = 0x8000200, OT_ASSET = 0x8000400, OT_THREAD = 0x8001000, OT_FUNCPROTO = 0x8002000, OT_CLAAS = 0x8004000, OT_STRUCT = 0x8200000, OT_WEAKREF = 0x8010000, OT_TABLE = 0xA000020, OT_USERDATA = 0xA000080, OT_INSTANCE = 0xA008000, OT_ENTITY = 0xA400000, }; /* 156 */ union alignas(8) SQObjectValue { SQString* asString; SQTable* asTable; SQClosure* asClosure; SQFunctionProto* asFuncProto; SQStructDef* asStructDef; __int64 asInteger; SQStructInstance* asStructInstance; float asFloat; SQNativeClosure* asNativeClosure; SQArray* asArray; }; /* 128 */ struct alignas(8) SQObject { SQObjectType _Type; __int32 _structOffset; SQObjectValue _VAL; }; struct tableNode { SQObject val; SQObject key; tableNode* next; }; /* 138 */ struct alignas(8) SQString { __int64* vftable; __int32 uiRef; __int32 uiRef1; SQString* _next_maybe; SQSharedState* sharedState; __int32 length; uint8 gap_24[4]; char _hash[8]; char _val[1]; }; /* 137 */ struct alignas(8) SQTable { __int64* vftable; uint8 gap_08[4]; __int32 uiRef; uint8 gap_10[8]; void* pointer_18; void* pointer_20; void* _sharedState; __int64 field_30; tableNode* _nodes; __int32 _numOfNodes; __int32 size; __int32 field_48; __int32 _usedNodes; uint8 _gap_50[20]; __int32 field_64; uint8 _gap_68[80]; }; /* 140 */ struct alignas(8) SQClosure { void* vftable; uint8 gap_08[4]; __int32 uiRef; void* pointer_10; void* pointer_18; void* pointer_20; void* sharedState; SQObject obj_30; SQObject _function; SQObject* _outervalues; uint8 gap_58[8]; uint8 gap_60[96]; SQObject* objectPointer_C0; }; /* 139 */ struct alignas(8) SQFunctionProto { void* vftable; uint8 gap_08[4]; __int32 uiRef; uint8 gap_10[8]; void* pointer_18; void* pointer_20; void* sharedState; void* pointer_30; SQObject fileName; SQObject funcName; SQObject obj_58; uint8 gap_68[64]; __int32 nParameters; uint8 gap_AC[60]; __int32 nDefaultParams; uint8 gap_EC[200]; }; /* 152 */ struct SQStructDef { uint8 gap_0[56]; SQString* name; uint8 gap_[300]; }; /* 150 */ struct SQStructInstance { void* vftable; uint8 gap_8[16]; void* pointer_18; uint8 gap_20[8]; SQSharedState* _sharedState; uint8 gap_30[8]; SQObject data[1]; }; /* 157 */ struct alignas(8) SQNativeClosure { void* vftable; uint8 gap_08[4]; __int32 uiRef; uint8 gap_10[88]; SQString* _name; uint8 gap_0[300]; }; /* 148 */ struct SQSharedState { uint8 gap_0[72]; StringTable* _stringtable; uint8 gap_50[30000]; }; /* 149 */ struct StringTable { uint8 gap_0[12]; int _numofslots; uint8 gap_10[200]; }; /* 129 */ struct alignas(8) HSquirrelVM { void* vftable; __int32 uiRef; uint8 gap_8[12]; void* _toString; void* _roottable_pointer; void* pointer_28; CallInfo* ci; CallInfo* _callsstack; __int32 _callsstacksize; __int32 _stackbase; SQObject* _stackOfCurrentFunction; SQSharedState* sharedState; void* pointer_58; void* pointer_60; __int32 _top; SQObject* _stack; uint8 gap_78[8]; SQObject* _vargsstack; uint8 gap_88[8]; SQObject temp_reg; uint8 gapA0[8]; void* pointer_A8; uint8 gap_B0[8]; SQObject _roottable_object; SQObject _lasterror; SQObject _errorHandler; __int64 field_E8; __int32 traps; uint8 gap_F4[12]; __int32 _nnativecalls; __int32 _suspended; __int32 _suspended_root; __int32 _callstacksize; __int32 _suspended_target; __int32 field_114; __int32 _suspend_varargs; SQObject* _object_pointer_120; }; /* 136 */ struct alignas(8) CallInfo { SQInstruction* ip; SQObject* _literals; SQObject obj10; SQObject closure; __int32 _etraps[4]; __int32 _root; short _vargs_size; short _vargs_base; }; /* 135 */ enum SQOpcode : int { _OP_LOAD = 0x0, _OP_LOADCOPY = 0x1, _OP_LOADINT = 0x2, _OP_LOADFLOAT = 0x3, _OP_DLOAD = 0x4, _OP_TAILCALL = 0x5, _OP_CALL = 0x6, _OP_PREPCALL = 0x7, _OP_PREPCALLK = 0x8, _OP_GETK = 0x9, _OP_MOVE = 0xA, _OP_NEWSLOT = 0xB, _OP_DELETE = 0xC, _OP_SET = 0xD, _OP_GET = 0xE, _OP_EQ = 0xF, _OP_NE = 0x10, _OP_ARITH = 0x11, _OP_BITW = 0x12, _OP_RETURN = 0x13, _OP_LOADNULLS = 0x14, _OP_LOADROOTTABLE = 0x15, _OP_LOADBOOL = 0x16, _OP_DMOVE = 0x17, _OP_JMP = 0x18, _OP_JNZ = 0x19, _OP_JZ = 0x1A, _OP_LOADFREEVAR = 0x1B, _OP_VARGC = 0x1C, _OP_GETVARGV = 0x1D, _OP_NEWTABLE = 0x1E, _OP_NEWARRAY = 0x1F, _OP_APPENDARRAY = 0x20, _OP_GETPARENT = 0x21, _OP_COMPOUND_ARITH = 0x22, _OP_COMPOUND_ARITH_LOCAL = 0x23, _OP_INCREMENT_PREFIX = 0x24, _OP_INCREMENT_PREFIX_LOCAL = 0x25, _OP_INCREMENT_PREFIX_STRUCTFIELD = 0x26, _OP_INCREMENT_POSTFIX = 0x27, _OP_INCREMENT_POSTFIX_LOCAL = 0x28, _OP_INCREMENT_POSTFIX_STRUCTFIELD = 0x29, _OP_CMP = 0x2A, _OP_EXISTS = 0x2B, _OP_INSTANCEOF = 0x2C, _OP_NEG = 0x2D, _OP_NOT = 0x2E, _OP_BWNOT = 0x2F, _OP_CLOSURE = 0x30, _OP_FOREACH = 0x31, _OP_FOREACH_STATICARRAY_START = 0x32, _OP_FOREACH_STATICARRAY_NEXT = 0x33, _OP_FOREACH_STATICARRAY_NESTEDSTRUCT_START = 0x34, _OP_FOREACH_STATICARRAY_NESTEDSTRUCT_NEXT = 0x35, _OP_DELEGATE = 0x36, _OP_CLONE = 0x37, _OP_TYPEOF = 0x38, _OP_PUSHTRAP = 0x39, _OP_POPTRAP = 0x3A, _OP_THROW = 0x3B, _OP_CLASS = 0x3C, _OP_NEWSLOTA = 0x3D, _OP_EQ_LITERAL = 0x3E, _OP_NE_LITERAL = 0x3F, _OP_FOREACH_SETUP = 0x40, _OP_ASSERT_FAILED = 0x41, _OP_ADD = 0x42, _OP_SUB = 0x43, _OP_MUL = 0x44, _OP_DIV = 0x45, _OP_MOD = 0x46, _OP_PREPCALLK_CALL = 0x47, _OP_PREPCALLK_MOVE_CALL = 0x48, _OP_PREPCALLK_LOADINT_CALL = 0x49, _OP_CMP_JZ = 0x4A, _OP_INCREMENT_LOCAL_DISCARD_JMP = 0x4B, _OP_JZ_RETURN = 0x4C, _OP_JZ_LOADBOOL_RETURN = 0x4D, _OP_NEWVECTOR = 0x4E, _OP_ZEROVECTOR = 0x4F, _OP_GET_VECTOR_COMPONENT = 0x50, _OP_SET_VECTOR_COMPONENT = 0x51, _OP_VECTOR_COMPONENT_MINUSEQ = 0x52, _OP_VECTOR_COMPONENT_PLUSEQ = 0x53, _OP_VECTOR_COMPONENT_MULEQ = 0x54, _OP_VECTOR_COMPONENT_DIVEQ = 0x55, _OP_VECTOR_NORMALIZE = 0x56, _OP_VECTOR_NORMALIZE_IN_PLACE = 0x57, _OP_VECTOR_DOT_PRODUCT = 0x58, _OP_VECTOR_DOT_PRODUCT2D = 0x59, _OP_VECTOR_CROSS_PRODUCT = 0x5A, _OP_VECTOR_CROSS_PRODUCT2D = 0x5B, _OP_VECTOR_LENGTH = 0x5C, _OP_VECTOR_LENGTHSQR = 0x5D, _OP_VECTOR_LENGTH2D = 0x5E, _OP_VECTOR_LENGTH2DSQR = 0x5F, _OP_VECTOR_DISTANCE = 0x60, _OP_VECTOR_DISTANCESQR = 0x61, _OP_VECTOR_DISTANCE2D = 0x62, _OP_VECTOR_DISTANCE2DSQR = 0x63, _OP_INCREMENT_LOCAL_DISCARD = 0x64, _OP_FASTCALL = 0x65, _OP_FASTCALL_NATIVE = 0x66, _OP_FASTCALL_NATIVE_ARGTYPECHECK = 0x67, _OP_FASTCALL_ENV = 0x68, _OP_FASTCALL_NATIVE_ENV = 0x69, _OP_FASTCALL_NATIVE_ENV_ARGTYPECHECK = 0x6A, _OP_LOADGLOBALARRAY = 0x6B, _OP_GETGLOBAL = 0x6C, _OP_SETGLOBAL = 0x6D, _OP_COMPOUND_ARITH_GLOBAL = 0x6E, _OP_GETSTRUCTFIELD = 0x6F, _OP_SETSTRUCTFIELD = 0x70, _OP_COMPOUND_ARITH_STRUCTFIELD = 0x71, _OP_NEWSTRUCT = 0x72, _OP_GETSUBSTRUCT = 0x73, _OP_GETSUBSTRUCT_DYNAMIC = 0x74, _OP_TYPECAST = 0x75, _OP_TYPECHECK = 0x76, _OP_TYPECHECK_ORNULL = 0x77, _OP_TYPECHECK_NOTNULL = 0x78, _OP_CHECK_ENTITY_CLASS = 0x79, _OP_UNREACHABLE = 0x7A, _OP_ARRAY_RESIZE = 0x7B, }; /* 141 */ struct alignas(8) SQStackInfos { char* _name; char* _sourceName; __int32 _line; }; /* 151 */ struct alignas(4) SQInstruction { int op; int arg1; int output; __int16 arg2; __int16 arg3; }; /* 154 */ struct SQLexer { uint8 gap_0[112]; }; /* 153 */ struct SQCompiler { uint8 gap_0[4]; __int32 _token; uint8 gap_8[8]; SQObject object_10; SQLexer lexer; uint8 gap_1[768]; }; /* 155 */ struct CSquirrelVM { uint8 gap_0[8]; HSquirrelVM* sqvm; }; struct SQVector { SQObjectType _Type; float x; float y; float z; }; struct SQArray { void* vftable; __int32 uiRef; uint8 gap_24[36]; SQObject* _values; __int32 _usedSlots; __int32 _allocated; }; #define INCREMENT_REFERENCECOUNT(val) \ if ((val->_Type & SQOBJECT_REF_COUNTED) != 0) \ ++val->_VAL.asString->uiRef; #define DECREMENT_REFERENCECOUNT(val) \ if ((val->_Type & SQOBJECT_REF_COUNTED) != 0) \ { \ if (val->_VAL.asString->uiRef-- == 1) \ { \ spdlog::info("Deleted SQObject of type {} with address {:X}", sq_getTypeName(val->_Type), val->_VAL.asInteger); \ (*(void(__fastcall**)(SQString*))(&val->_VAL.asString->vftable[1]))(val->_VAL.asString); \ } \ } // core sqvm funcs typedef SQRESULT (*sq_compilebufferType)(void* sqvm, CompileBufferState* compileBuffer, const char* file, int a1, ScriptContext a2); extern sq_compilebufferType ClientSq_compilebuffer; extern sq_compilebufferType ServerSq_compilebuffer; typedef void (*sq_pushroottableType)(void* sqvm); extern sq_pushroottableType ClientSq_pushroottable; extern sq_pushroottableType ServerSq_pushroottable; typedef SQRESULT (*sq_callType)(void* sqvm, SQInteger s1, SQBool a2, SQBool a3); extern sq_callType ClientSq_call; extern sq_callType ServerSq_call; typedef int64_t (*RegisterSquirrelFuncType)(void* sqvm, SQFuncRegistration* funcReg, char unknown); extern RegisterSquirrelFuncType ClientRegisterSquirrelFunc; extern RegisterSquirrelFuncType ServerRegisterSquirrelFunc; // sq stack array funcs typedef void (*sq_newarrayType)(void* sqvm, SQInteger stackpos); extern sq_newarrayType ClientSq_newarray; extern sq_newarrayType ServerSq_newarray; typedef SQRESULT (*sq_arrayappendType)(void* sqvm, SQInteger stackpos); extern sq_arrayappendType ClientSq_arrayappend; extern sq_arrayappendType ServerSq_arrayappend; // sq stack push funcs typedef void (*sq_pushstringType)(void* sqvm, const SQChar* str, SQInteger stackpos); extern sq_pushstringType ClientSq_pushstring; extern sq_pushstringType ServerSq_pushstring; // weird how these don't take a stackpos arg? typedef void (*sq_pushintegerType)(void* sqvm, SQInteger i); extern sq_pushintegerType ClientSq_pushinteger; extern sq_pushintegerType ServerSq_pushinteger; typedef void (*sq_pushfloatType)(void* sqvm, SQFloat f); extern sq_pushfloatType ClientSq_pushfloat; extern sq_pushfloatType ServerSq_pushfloat; typedef void (*sq_pushboolType)(void* sqvm, SQBool b); extern sq_pushboolType ClientSq_pushbool; extern sq_pushboolType ServerSq_pushbool; typedef SQInteger (*sq_pusherrorType)(void* sqvm, const SQChar* error); extern sq_pusherrorType ClientSq_pusherror; extern sq_pusherrorType ServerSq_pusherror; typedef void (*sq_defconst)(void* sqvm, const SQChar* name, int value); extern sq_defconst ClientSq_defconst; extern sq_defconst ServerSq_defconst; typedef SQRESULT (*sq_pushAssetType)(void* sqvm, const SQChar* assetName, SQInteger nameLength); extern sq_pushAssetType ServerSq_pushAsset; extern sq_pushAssetType ClientSq_pushAsset; // sq stack get funcs typedef const SQChar* (*sq_getstringType)(void* sqvm, SQInteger stackpos); extern sq_getstringType ClientSq_getstring; extern sq_getstringType ServerSq_getstring; typedef SQInteger (*sq_getintegerType)(void* sqvm, SQInteger stackpos); extern sq_getintegerType ClientSq_getinteger; extern sq_getintegerType ServerSq_getinteger; typedef SQFloat (*sq_getfloatType)(void*, SQInteger stackpos); extern sq_getfloatType ClientSq_getfloat; extern sq_getfloatType ServerSq_getfloat; typedef SQBool (*sq_getboolType)(void*, SQInteger stackpos); extern sq_getboolType ClientSq_getbool; extern sq_getboolType ServerSq_getbool; typedef SQRESULT (*sq_getType)(void* sqvm, SQInteger idx); extern sq_getType ServerSq_sq_get; extern sq_getType ClientSq_sq_get; // sq table functions typedef SQRESULT (*sq_newTableType)(void* sqvm); extern sq_newTableType ServerSq_newTable; extern sq_newTableType ClientSq_newTable; typedef SQRESULT (*sq_newSlotType)(void* sqvm, int idx, bool bStatic); extern sq_newSlotType ServerSq_newSlot; extern sq_newSlotType ClientSq_newSlot; template <ScriptContext context> class SquirrelManager { private: std::vector<SQFuncRegistration*> m_funcRegistrations; public: void* sqvm; void* sqvm2; public: SquirrelManager() : sqvm(nullptr) {} void VMCreated(void* newSqvm) { sqvm = newSqvm; sqvm2 = *((void**)((char*)sqvm + 8)); // honestly not 100% sure on what this is, but alot of functions take it for (SQFuncRegistration* funcReg : m_funcRegistrations) { spdlog::info("Registering {} function {}", GetContextName(context), funcReg->squirrelFuncName); if (context == ScriptContext::CLIENT || context == ScriptContext::UI) ClientRegisterSquirrelFunc(sqvm, funcReg, 1); else ServerRegisterSquirrelFunc(sqvm, funcReg, 1); } for (auto& pair : g_ModManager->DependencyConstants) { bool wasFound = false; for (Mod& dependency : g_ModManager->m_loadedMods) { if (dependency.Name == pair.second) { wasFound = dependency.Enabled; break; } } if (context == ScriptContext::SERVER) ServerSq_defconst(sqvm, pair.first.c_str(), wasFound); else ClientSq_defconst(sqvm, pair.first.c_str(), wasFound); } } void VMDestroyed() { sqvm = nullptr; } void ExecuteCode(const char* code) { // ttf2sdk checks ThreadIsInMainThread here, might be good to do that? doesn't seem like an issue rn tho if (!sqvm) { spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context)); return; } spdlog::info("Executing {} script code {} ", GetContextName(context), code); std::string strCode(code); CompileBufferState bufferState = CompileBufferState(strCode); SQRESULT compileResult; if (context == ScriptContext::CLIENT || context == ScriptContext::UI) compileResult = ClientSq_compilebuffer(sqvm2, &bufferState, "console", -1, context); else if (context == ScriptContext::SERVER) compileResult = ServerSq_compilebuffer(sqvm2, &bufferState, "console", -1, context); spdlog::info("sq_compilebuffer returned {}", compileResult); if (compileResult >= 0) { if (context == ScriptContext::CLIENT || context == ScriptContext::UI) { ClientSq_pushroottable(sqvm2); SQRESULT callResult = ClientSq_call(sqvm2, 1, false, false); spdlog::info("sq_call returned {}", callResult); } else if (context == ScriptContext::SERVER) { ServerSq_pushroottable(sqvm2); SQRESULT callResult = ServerSq_call(sqvm2, 1, false, false); spdlog::info("sq_call returned {}", callResult); } } } int setupfunc(const char* funcname) { int result = -2; if (context == ScriptContext::CLIENT || context == ScriptContext::UI) { ClientSq_pushroottable(sqvm2); ClientSq_pushstring(sqvm2, funcname, -1); result = ClientSq_sq_get(sqvm2, -2); if (result != SQRESULT_ERROR) { ClientSq_pushroottable(sqvm2); } } else if (context == ScriptContext::SERVER) { ServerSq_pushroottable(sqvm2); ServerSq_pushstring(sqvm2, funcname, -1); result = ServerSq_sq_get(sqvm2, -2); if (result != SQRESULT_ERROR) { ServerSq_pushroottable(sqvm2); } } return result; } void pusharg(int arg) { if (context == ScriptContext::CLIENT || context == ScriptContext::UI) ClientSq_pushinteger(sqvm2, arg); else if (context == ScriptContext::SERVER) ServerSq_pushinteger(sqvm2, arg); } void pusharg(const char* arg) { if (context == ScriptContext::CLIENT || context == ScriptContext::UI) ClientSq_pushstring(sqvm2, arg, -1); else if (context == ScriptContext::SERVER) ServerSq_pushstring(sqvm2, arg, -1); } void pusharg(float arg) { if (context == ScriptContext::CLIENT || context == ScriptContext::UI) ClientSq_pushfloat(sqvm2, arg); else if (context == ScriptContext::SERVER) ServerSq_pushfloat(sqvm2, arg); } void pusharg(bool arg) { if (context == ScriptContext::CLIENT || context == ScriptContext::UI) ClientSq_pushbool(sqvm2, arg); else if (context == ScriptContext::SERVER) ServerSq_pushbool(sqvm2, arg); } int call(int args) { int result = -2; if (context == ScriptContext::CLIENT || context == ScriptContext::UI) result = ClientSq_call(sqvm2, args + 1, false, false); else if (context == ScriptContext::SERVER) result = ServerSq_call(sqvm2, args + 1, false, false); return result; } void AddFuncRegistration(std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func) { SQFuncRegistration* reg = new SQFuncRegistration; reg->squirrelFuncName = new char[name.size() + 1]; strcpy((char*)reg->squirrelFuncName, name.c_str()); reg->cppFuncName = reg->squirrelFuncName; reg->helpText = new char[helpText.size() + 1]; strcpy((char*)reg->helpText, helpText.c_str()); reg->returnTypeString = new char[returnType.size() + 1]; strcpy((char*)reg->returnTypeString, returnType.c_str()); reg->returnTypeEnum = GetReturnTypeEnumFromString(returnType.c_str()); reg->argTypes = new char[argTypes.size() + 1]; strcpy((char*)reg->argTypes, argTypes.c_str()); reg->funcPtr = reinterpret_cast<void*>(func); m_funcRegistrations.push_back(reg); } }; extern SquirrelManager<ScriptContext::CLIENT>* g_ClientSquirrelManager; extern SquirrelManager<ScriptContext::SERVER>* g_ServerSquirrelManager; extern SquirrelManager<ScriptContext::UI>* g_UISquirrelManager;