diff options
author | BobTheBob <32057864+BobTheBob9@users.noreply.github.com> | 2022-10-17 23:26:07 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-17 23:26:07 +0100 |
commit | 841881af9ea6ec73b1d505d5a8f7c1f766273724 (patch) | |
tree | 91feb40fe810984b59d2d2da440e289370b0a137 /NorthstarDLL/squirrel.cpp | |
parent | dc0934d29caacc8da1e7df8b775d24b4e99c381c (diff) | |
download | NorthstarLauncher-841881af9ea6ec73b1d505d5a8f7c1f766273724.tar.gz NorthstarLauncher-841881af9ea6ec73b1d505d5a8f7c1f766273724.zip |
big refactor (#171)v1.10.0-rc1
* use in-file macros rather than global funcs for registering dll load callbacks
* move more things to macros
* fix debug crashes
* move sqvm funcs to sq managers
* get rid of context file
* refactor some squirrel stuff and ingame compilation error message
* move tier0 and playlist funcs to namespaces
* uiscript_reset concommand: don't loop forever if compilation fails
* improve showing console for ui script compile errors
* standardise concommand func naming in c++
* use lambdas for dll load callbacks so intellisense shits itself less
* use cvar change callbacks for unescaping ns_server_name and ns_server_desc
* add proper helpstrings to masterserver cvars
* add cvar help and find
* allow parsing of convar flags from string
* normalise mod fs paths to be lowercase
* move hoststate to its own file and add host_init hooks
* better IsFlagSet def
* replace files in ReadFromCache
* rename g_ModManager to g_pModManager
* formatting changes
* make cvar print work on dedi, move demo fix stuff, add findflags
* add proper map autocompletes and maps command
* formatting changes
* separate gameutils into multiple r2 headers
* Update keyvalues.cpp
* move sqvm funcs into wrappers in the manager class
* remove unnecessary header files
* lots of cleanup and starting moving to new hooking macros
* update more stuff to new hook macros
* rename project folder (:tf: commit log)
* fix up postbuild commands to use relative dir
* almost fully replaced hooking lib
* completely remove old hooking
* add nsprefix because i forgot to include it
* move exploit prevention and limits code out of serverauthentication, and have actual defs for CBasePlayer
* use modular ServerPresence system for registering servers
* add new memory lib
* accidentally pushed broke code oops
* lots of stuff idk
* implement some more prs
* improve rpakfilesystem
* fix line endings on vcxproj
* Revert "fix line endings on vcxproj"
This reverts commit 4ff7d022d2602c2dba37beba8b8df735cf5cd7d9.
* add more prs
* i swear i committed these how are they not there
* Add ability to load Datatables from files (#238)
* first version of kinda working custom datatables
* Fix copy error
* Finish custom datatables
* Fix Merge
* Fix line endings
* Add fallback to rpak when ns_prefere_datatable_from_disk is true
* fix typo
* Bug fixess
* Fix Function Registration hook
* Set convar value
* Fix Client and Ui VM
* enable server auth with ms agian
* Add Filters
* FIx unused import
* Merge remote-tracking branch 'upsteam/bobs-big-refactor-pr' into datatables
Co-authored-by: RoyalBlue1 <realEmail@veryRealURL.com>
* Add some changes from main to refactor (#243)
* Add PR template
* Update CI folder location
* Delete startup args txt files
* Fix line endings (hopefully) (#244)
* Fix line endings (hopefully)
* Fix more line endings
* Update refactor (#250)
* Add PR template
* Update CI folder location
* Delete startup args txt files
* Add editorconfig file (#246)
* Add editorconfig file
It's a cross-editor compatible config file that defines certain editor
behaviour (e.g. adding/removing newline at end of file)
It is supported by major editors like Visual Studio (Code) and by
version control providers like GitHub.
Should end the constant adding/removing of final newline in PRs
* More settings
- unicode by default
- trim newlines
- use tabs for indentation (ugh)
* Ignore folder rename (#245)
* Hot reload banlist on player join (#233)
* added banlist hotreload
* fix formatting
* didnt append, cleared whole file oopsie
* unfuckedunban not rewriting file
* fixed not checking for new line
Co-authored-by: ScureX <47725553+ScureX@users.noreply.github.com>
* Refactor cleanup (#256)
* Fix indentation
* Fix path in clang-format command in readme
* Refactor cleanup (some formatting fixes) (#257)
* Fix some formatting
* More formatting fixes
* add scriptdatatable.cpp rewrite
* Some formatting fixes (#260)
* More formatting stuff (#261)
* various formatting changes and fixes
* Fix changed icon (#264)
* clang format, fix issues with server registration and rpak loading
* fix more formatting
* update postbuild step
* set launcher directory and error on fail creating log files
* change some stuff in exploitfixes
* only unrestrict dev commands when commandline flag is present
* fix issues with cvar flag commit
* fixup command flags better and reformat
* bring up to date with main
* fixup formatting
* improve cvar flag fixup and remove temp thing from findflags
* set serverfilter better
* avoid ptr decay when setting auth token
* add more entity functions
* Fix the MS server registration issues. (#285)
* Port ms presence reporter to std::async
* Fix crash due to std::optional being assigned nullptr.
* Fix formatting.
* Wait 20 seconds if MS returns DUPLICATE_SERVER.
* Change PERSISTENCE_MAX_SIZE to fix player authentication (#287)
The size check added in the refactor was incorrect:
- 56306: expected pdata size based on the pdef
- 512: allowance for trailing junk (r2 adds 137 bytes of trailing junk)
- 100: for some wiggle room
Co-Authored-By: pg9182 <96569817+pg9182@users.noreply.github.com>
* change miscserverscript to use actual entity arguments rather than
player index jank
* Fix token clearing hook (#290)
A certain someone forgot to put an `0x` in front of their hex number, meaning the offset is wrong.
This would cause token to be leaked again
Co-authored-by: Maya <malte.hoermeyer@web.de>
Co-authored-by: RoyalBlue1 <realEmail@veryRealURL.com>
Co-authored-by: GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com>
Co-authored-by: ScureX <47725553+ScureX@users.noreply.github.com>
Co-authored-by: Erlite <ys.aameziane@gmail.com>
Co-authored-by: Emma Miler <emma.pi@protonmail.com>
Co-authored-by: pg9182 <96569817+pg9182@users.noreply.github.com>
Diffstat (limited to 'NorthstarDLL/squirrel.cpp')
-rw-r--r-- | NorthstarDLL/squirrel.cpp | 925 |
1 files changed, 467 insertions, 458 deletions
diff --git a/NorthstarDLL/squirrel.cpp b/NorthstarDLL/squirrel.cpp index 2fa957fb..23935827 100644 --- a/NorthstarDLL/squirrel.cpp +++ b/NorthstarDLL/squirrel.cpp @@ -1,271 +1,224 @@ #include "pch.h" #include "squirrel.h" -#include "hooks.h" -#include "hookutils.h" -#include "sigscanning.h" #include "concommand.h" #include "modmanager.h" -#include <iostream> -#include "gameutils.h" +#include "dedicated.h" +#include "r2engine.h" +#include "tier0.h" -// hook forward declarations -typedef SQInteger (*SQPrintType)(void* sqvm, char* fmt, ...); -SQPrintType ClientSQPrint; -SQPrintType UISQPrint; -SQPrintType ServerSQPrint; -template <ScriptContext context> SQInteger SQPrintHook(void* sqvm, char* fmt, ...); +AUTOHOOK_INIT() -typedef void* (*CreateNewVMType)(void* a1, ScriptContext contextArg); -CreateNewVMType ClientCreateNewVM; // only need a client one since ui doesn't have its own func for this -CreateNewVMType ServerCreateNewVM; -template <ScriptContext context> void* CreateNewVMHook(void* a1, ScriptContext contextArg); - -typedef void (*DestroyVMType)(void* a1, void* sqvm); -DestroyVMType ClientDestroyVM; // only need a client one since ui doesn't have its own func for this -DestroyVMType ServerDestroyVM; -template <ScriptContext context> void DestroyVMHook(void* a1, void* sqvm); +const char* GetContextName(ScriptContext context) +{ + switch (context) + { + case ScriptContext::CLIENT: + return "CLIENT"; + case ScriptContext::SERVER: + return "SERVER"; + case ScriptContext::UI: + return "UI"; + default: + return "UNKNOWN"; + } +} -typedef void (*ScriptCompileError)(void* sqvm, const char* error, const char* file, int line, int column); -ScriptCompileError ClientSQCompileError; // only need a client one since ui doesn't have its own func for this -ScriptCompileError ServerSQCompileError; -template <ScriptContext context> void ScriptCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column); +eSQReturnType SQReturnTypeFromString(const char* pReturnType) +{ + static const std::map<std::string, eSQReturnType> sqReturnTypeNameToString = { + {"bool", eSQReturnType::Boolean}, + {"float", eSQReturnType::Float}, + {"vector", eSQReturnType::Vector}, + {"int", eSQReturnType::Integer}, + {"entity", eSQReturnType::Entity}, + {"string", eSQReturnType::String}, + {"array", eSQReturnType::Arrays}, + {"asset", eSQReturnType::Asset}, + {"table", eSQReturnType::Table}}; + + if (sqReturnTypeNameToString.find(pReturnType) != sqReturnTypeNameToString.end()) + return sqReturnTypeNameToString.at(pReturnType); + else + return eSQReturnType::Default; // previous default value +} -typedef char (*CallScriptInitCallbackType)(void* sqvm, const char* callback); -CallScriptInitCallbackType ClientCallScriptInitCallback; -CallScriptInitCallbackType ServerCallScriptInitCallback; -template <ScriptContext context> char CallScriptInitCallbackHook(void* sqvm, const char* callback); +const char* SQTypeNameFromID(int type) +{ + switch (type) + { + case OT_ASSET: + return "asset"; + case OT_INTEGER: + return "int"; + case OT_BOOL: + return "bool"; + case SQOBJECT_NUMERIC: + return "float or int"; + case OT_NULL: + return "null"; + case OT_VECTOR: + return "vector"; + case 0: + return "var"; + case OT_USERDATA: + return "userdata"; + case OT_FLOAT: + return "float"; + case OT_STRING: + return "string"; + case OT_ARRAY: + return "array"; + case 0x8000200: + return "function"; + case 0x8100000: + return "structdef"; + case OT_THREAD: + return "thread"; + case OT_FUNCPROTO: + return "function"; + case OT_CLAAS: + return "class"; + case OT_WEAKREF: + return "weakref"; + case 0x8080000: + return "unimplemented function"; + case 0x8200000: + return "struct instance"; + case OT_TABLE: + return "table"; + case 0xA008000: + return "instance"; + case OT_ENTITY: + return "entity"; + } + return ""; +} -RegisterSquirrelFuncType ClientRegisterSquirrelFunc; -RegisterSquirrelFuncType ServerRegisterSquirrelFunc; -template <ScriptContext context> int64_t RegisterSquirrelFuncHook(void* sqvm, SQFuncRegistration* funcReg, char unknown); +// needed to define implementations for squirrelmanager outside of squirrel.h without compiler errors +template class SquirrelManager<ScriptContext::SERVER>; +template class SquirrelManager<ScriptContext::CLIENT>; +template class SquirrelManager<ScriptContext::UI>; -// core sqvm funcs -sq_compilebufferType ClientSq_compilebuffer; -sq_compilebufferType ServerSq_compilebuffer; +template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquirrelVM* newSqvm) +{ + m_pSQVM = newSqvm; -sq_pushroottableType ClientSq_pushroottable; -sq_pushroottableType ServerSq_pushroottable; + for (SQFuncRegistration* funcReg : m_funcRegistrations) + { + spdlog::info("Registering {} function {}", GetContextName(context), funcReg->squirrelFuncName); + RegisterSquirrelFunc(m_pSQVM, funcReg, 1); + } -sq_callType ClientSq_call; -sq_callType ServerSq_call; + for (auto& pair : g_pModManager->m_DependencyConstants) + { + bool bWasFound = false; + for (Mod& dependency : g_pModManager->m_LoadedMods) + { + if (!dependency.m_bEnabled) + continue; -// sq stack array funcs -sq_newarrayType ClientSq_newarray; -sq_newarrayType ServerSq_newarray; + if (dependency.Name == pair.second) + { + bWasFound = true; + break; + } + } -sq_arrayappendType ClientSq_arrayappend; -sq_arrayappendType ServerSq_arrayappend; + defconst(m_pSQVM, pair.first.c_str(), bWasFound); + } +} -// sq stack push funcs -sq_pushstringType ClientSq_pushstring; -sq_pushstringType ServerSq_pushstring; +template <ScriptContext context> void SquirrelManager<context>::VMDestroyed() +{ + m_pSQVM = nullptr; +} -sq_pushintegerType ClientSq_pushinteger; -sq_pushintegerType ServerSq_pushinteger; +template <ScriptContext context> void SquirrelManager<context>::ExecuteCode(const char* pCode) +{ + if (!m_pSQVM || !m_pSQVM->sqvm) + { + spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context)); + return; + } -sq_pushfloatType ClientSq_pushfloat; -sq_pushfloatType ServerSq_pushfloat; + spdlog::info("Executing {} script code {} ", GetContextName(context), pCode); -sq_pushboolType ClientSq_pushbool; -sq_pushboolType ServerSq_pushbool; + std::string strCode(pCode); + CompileBufferState bufferState = CompileBufferState(strCode); -sq_pusherrorType ClientSq_pusherror; -sq_pusherrorType ServerSq_pusherror; + SQRESULT compileResult = compilebuffer(&bufferState, "console"); + spdlog::info("sq_compilebuffer returned {}", PrintSQRESULT.at(compileResult)); -sq_defconst ClientSq_defconst; -sq_defconst ServerSq_defconst; + if (compileResult != SQRESULT_ERROR) + { + pushroottable(m_pSQVM->sqvm); + SQRESULT callResult = call(m_pSQVM->sqvm, 0); + spdlog::info("sq_call returned {}", PrintSQRESULT.at(callResult)); + } +} -sq_pushAssetType ClientSq_pushAsset; -sq_pushAssetType ServerSq_pushAsset; +template <ScriptContext context> void SquirrelManager<context>::AddFuncRegistration( + std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func) +{ + SQFuncRegistration* reg = new SQFuncRegistration; -// sq stack get funcs -sq_getstringType ClientSq_getstring; -sq_getstringType ServerSq_getstring; + reg->squirrelFuncName = new char[name.size() + 1]; + strcpy((char*)reg->squirrelFuncName, name.c_str()); + reg->cppFuncName = reg->squirrelFuncName; -sq_getintegerType ClientSq_getinteger; -sq_getintegerType ServerSq_getinteger; + reg->helpText = new char[helpText.size() + 1]; + strcpy((char*)reg->helpText, helpText.c_str()); -sq_getfloatType ClientSq_getfloat; -sq_getfloatType ServerSq_getfloat; + reg->returnTypeString = new char[returnType.size() + 1]; + strcpy((char*)reg->returnTypeString, returnType.c_str()); + reg->returnType = SQReturnTypeFromString(returnType.c_str()); -sq_getboolType ClientSq_getbool; -sq_getboolType ServerSq_getbool; + reg->argTypes = new char[argTypes.size() + 1]; + strcpy((char*)reg->argTypes, argTypes.c_str()); -sq_getType ClientSq_sq_get; -sq_getType ServerSq_sq_get; + reg->funcPtr = func; -sq_newSlotType ServerSq_newSlot; -sq_newSlotType ClientSq_newSlot; + m_funcRegistrations.push_back(reg); +} -sq_newTableType ServerSq_newTable; -sq_newTableType ClientSq_newTable; +template <ScriptContext context> SQRESULT SquirrelManager<context>::setupfunc(const SQChar* funcname) +{ + pushroottable(m_pSQVM->sqvm); + pushstring(m_pSQVM->sqvm, funcname, -1); -template <ScriptContext context> void ExecuteCodeCommand(const CCommand& args); + SQRESULT result = get(m_pSQVM->sqvm, -2); + if (result != SQRESULT_ERROR) + pushroottable(m_pSQVM->sqvm); -// inits -SquirrelManager<ScriptContext::CLIENT>* g_ClientSquirrelManager; -SquirrelManager<ScriptContext::SERVER>* g_ServerSquirrelManager; -SquirrelManager<ScriptContext::UI>* g_UISquirrelManager; + return result; +} -SQInteger NSTestFunc(void* sqvm) +template <ScriptContext context> void SquirrelManager<context>::AddFuncOverride(std::string name, SQFunction func) { - return 1; + m_funcOverrides[name] = func; } -void InitialiseClientSquirrel(HMODULE baseAddress) +// hooks +bool IsUIVM(ScriptContext context, HSquirrelVM* pSqvm) { - HookEnabler hook; - - // client inits - g_ClientSquirrelManager = new SquirrelManager<ScriptContext::CLIENT>(); - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x12B00, - &SQPrintHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientSQPrint)); // client print function - RegisterConCommand( - "script_client", ExecuteCodeCommand<ScriptContext::CLIENT>, "Executes script code on the client vm", FCVAR_CLIENTDLL); - - // ui inits - g_UISquirrelManager = new SquirrelManager<ScriptContext::UI>(); - - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x12BA0, &SQPrintHook<ScriptContext::UI>, reinterpret_cast<LPVOID*>(&UISQPrint)); // ui print function - RegisterConCommand("script_ui", ExecuteCodeCommand<ScriptContext::UI>, "Executes script code on the ui vm", FCVAR_CLIENTDLL); - - // inits for both client and ui, since they share some functions - ClientSq_compilebuffer = (sq_compilebufferType)((char*)baseAddress + 0x3110); - ClientSq_pushroottable = (sq_pushroottableType)((char*)baseAddress + 0x5860); - ClientSq_call = (sq_callType)((char*)baseAddress + 0x8650); - ClientRegisterSquirrelFunc = (RegisterSquirrelFuncType)((char*)baseAddress + 0x108E0); - - ClientSq_newarray = (sq_newarrayType)((char*)baseAddress + 0x39F0); - ClientSq_arrayappend = (sq_arrayappendType)((char*)baseAddress + 0x3C70); - - ClientSq_pushstring = (sq_pushstringType)((char*)baseAddress + 0x3440); - ClientSq_pushinteger = (sq_pushintegerType)((char*)baseAddress + 0x36A0); - ClientSq_pushfloat = (sq_pushfloatType)((char*)baseAddress + 0x3800); - ClientSq_pushbool = (sq_pushboolType)((char*)baseAddress + 0x3710); - ClientSq_pusherror = (sq_pusherrorType)((char*)baseAddress + 0x8470); - ClientSq_pushAsset = (sq_pushAssetType)((char*)baseAddress + 0x3560); - - ClientSq_getstring = (sq_getstringType)((char*)baseAddress + 0x60C0); - ClientSq_getinteger = (sq_getintegerType)((char*)baseAddress + 0x60E0); - ClientSq_getfloat = (sq_getfloatType)((char*)baseAddress + 0x6100); - ClientSq_getbool = (sq_getboolType)((char*)baseAddress + 0x6130); - - ClientSq_sq_get = (sq_getType)((char*)baseAddress + 0x7C30); - - ClientSq_defconst = (sq_defconst)((char*)baseAddress + 0x12120); - - // Table functions - ClientSq_newTable = (sq_newTableType)((char*)baseAddress + 0x3960); - ClientSq_newSlot = (sq_newSlotType)((char*)baseAddress + 0x70B0); - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x26130, - &CreateNewVMHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientCreateNewVM)); // client createnewvm function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x26E70, - &DestroyVMHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientDestroyVM)); // client destroyvm function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x79A50, - &ScriptCompileErrorHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientSQCompileError)); // client compileerror function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x10190, - &CallScriptInitCallbackHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientCallScriptInitCallback)); // client callscriptinitcallback function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x108E0, - &RegisterSquirrelFuncHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientRegisterSquirrelFunc)); // client registersquirrelfunc function + return context != ScriptContext::SERVER && g_pSquirrel<ScriptContext::UI>->m_pSQVM && + g_pSquirrel<ScriptContext::UI>->m_pSQVM->sqvm == pSqvm; } -void InitialiseServerSquirrel(HMODULE baseAddress) +template <ScriptContext context> void* (*__fastcall sq_compiler_create)(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError); +template <ScriptContext context> void* __fastcall sq_compiler_createHook(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError) { - g_ServerSquirrelManager = new SquirrelManager<ScriptContext::SERVER>(); - - HookEnabler hook; - - ServerSq_compilebuffer = (sq_compilebufferType)((char*)baseAddress + 0x3110); - ServerSq_pushroottable = (sq_pushroottableType)((char*)baseAddress + 0x5840); - ServerSq_call = (sq_callType)((char*)baseAddress + 0x8620); - ServerRegisterSquirrelFunc = (RegisterSquirrelFuncType)((char*)baseAddress + 0x1DD10); - - ServerSq_newarray = (sq_newarrayType)((char*)baseAddress + 0x39F0); - ServerSq_arrayappend = (sq_arrayappendType)((char*)baseAddress + 0x3C70); - - ServerSq_pushstring = (sq_pushstringType)((char*)baseAddress + 0x3440); - ServerSq_pushinteger = (sq_pushintegerType)((char*)baseAddress + 0x36A0); - ServerSq_pushfloat = (sq_pushfloatType)((char*)baseAddress + 0x3800); - ServerSq_pushbool = (sq_pushboolType)((char*)baseAddress + 0x3710); - ServerSq_pusherror = (sq_pusherrorType)((char*)baseAddress + 0x8440); - ServerSq_pushAsset = (sq_pushAssetType)((char*)baseAddress + 0x3560); - - ServerSq_getstring = (sq_getstringType)((char*)baseAddress + 0x60A0); - ServerSq_getinteger = (sq_getintegerType)((char*)baseAddress + 0x60C0); - ServerSq_getfloat = (sq_getfloatType)((char*)baseAddress + 0x60E0); - ServerSq_getbool = (sq_getboolType)((char*)baseAddress + 0x6110); - - ServerSq_sq_get = (sq_getType)((char*)baseAddress + 0x7C00); - - ServerSq_defconst = (sq_defconst)((char*)baseAddress + 0x1F550); - - ServerSq_newSlot = (sq_newSlotType)((char*)baseAddress + 0x7080); - ServerSq_newTable = (sq_newTableType)((char*)baseAddress + 0x3960); - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1FE90, - &SQPrintHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerSQPrint)); // server print function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x260E0, - &CreateNewVMHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerCreateNewVM)); // server createnewvm function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x26E20, - &DestroyVMHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerDestroyVM)); // server destroyvm function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x799E0, - &ScriptCompileErrorHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerSQCompileError)); // server compileerror function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1D5C0, - &CallScriptInitCallbackHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerCallScriptInitCallback)); // server callscriptinitcallback function - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1DD10, - &RegisterSquirrelFuncHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerRegisterSquirrelFunc)); // server registersquirrelfunc function - - // cheat and clientcmd_can_execute allows clients to execute this, but since it's unsafe we only allow it when cheats are enabled - // for script_client and script_ui, we don't use cheats, so clients can execute them on themselves all they want - RegisterConCommand( - "script", - ExecuteCodeCommand<ScriptContext::SERVER>, - "Executes script code on the server vm", - FCVAR_GAMEDLL | FCVAR_CLIENTCMD_CAN_EXECUTE | FCVAR_CHEAT); + // store whether errors generated from this compile should be fatal + if (IsUIVM(context, sqvm)) + g_pSquirrel<ScriptContext::UI>->m_bFatalCompilationErrors = bShouldThrowError; + else + g_pSquirrel<context>->m_bFatalCompilationErrors = bShouldThrowError; + + return sq_compiler_create<context>(sqvm, a2, a3, bShouldThrowError); } -// hooks -template <ScriptContext context> SQInteger SQPrintHook(void* sqvm, char* fmt, ...) +template <ScriptContext context> SQInteger (*SQPrint)(HSquirrelVM* sqvm, const char* fmt); +template <ScriptContext context> SQInteger SQPrintHook(HSquirrelVM* sqvm, const char* fmt, ...) { va_list va; va_start(va, fmt); @@ -285,189 +238,161 @@ template <ScriptContext context> SQInteger SQPrintHook(void* sqvm, char* fmt, .. return 0; } -template <ScriptContext context> void* CreateNewVMHook(void* a1, ScriptContext realContext) +template <ScriptContext context> CSquirrelVM* (*__fastcall CreateNewVM)(void* a1, ScriptContext realContext); +template <ScriptContext context> CSquirrelVM* __fastcall CreateNewVMHook(void* a1, ScriptContext realContext) { - void* sqvm; - - if (context == ScriptContext::CLIENT) - { - sqvm = ClientCreateNewVM(a1, realContext); - - if (realContext == ScriptContext::UI) - g_UISquirrelManager->VMCreated(sqvm); - else - g_ClientSquirrelManager->VMCreated(sqvm); - } - else if (context == ScriptContext::SERVER) - { - sqvm = ServerCreateNewVM(a1, context); - g_ServerSquirrelManager->VMCreated(sqvm); - } + CSquirrelVM* sqvm = CreateNewVM<context>(a1, realContext); + if (realContext == ScriptContext::UI) + g_pSquirrel<ScriptContext::UI>->VMCreated(sqvm); + else + g_pSquirrel<context>->VMCreated(sqvm); - spdlog::info("CreateNewVM {} {}", GetContextName(realContext), sqvm); + spdlog::info("CreateNewVM {} {}", GetContextName(realContext), (void*)sqvm); return sqvm; } -template <ScriptContext context> void DestroyVMHook(void* a1, void* sqvm) +template <ScriptContext context> void (*__fastcall DestroyVM)(void* a1, HSquirrelVM* sqvm); +template <ScriptContext context> void __fastcall DestroyVMHook(void* a1, HSquirrelVM* sqvm) { ScriptContext realContext = context; // ui and client use the same function so we use this for prints - - if (context == ScriptContext::CLIENT) + if (IsUIVM(context, sqvm)) { - if (g_ClientSquirrelManager->sqvm == sqvm) - g_ClientSquirrelManager->VMDestroyed(); - else if (g_UISquirrelManager->sqvm == sqvm) - { - g_UISquirrelManager->VMDestroyed(); - realContext = ScriptContext::UI; - } - - ClientDestroyVM(a1, sqvm); - } - else if (context == ScriptContext::SERVER) - { - g_ServerSquirrelManager->VMDestroyed(); - ServerDestroyVM(a1, sqvm); + realContext = ScriptContext::UI; + g_pSquirrel<ScriptContext::UI>->VMDestroyed(); } + else + DestroyVM<context>(a1, sqvm); - spdlog::info("DestroyVM {} {}", GetContextName(realContext), sqvm); + spdlog::info("DestroyVM {} {}", GetContextName(realContext), (void*)sqvm); } -template <ScriptContext context> void ScriptCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column) +template <ScriptContext context> +void (*__fastcall SQCompileError)(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column); +template <ScriptContext context> +void __fastcall ScriptCompileErrorHook(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column) { + bool bIsFatalError = g_pSquirrel<context>->m_bFatalCompilationErrors; ScriptContext realContext = context; // ui and client use the same function so we use this for prints - if (context == ScriptContext::CLIENT && sqvm == g_UISquirrelManager->sqvm) + if (IsUIVM(context, sqvm)) + { realContext = ScriptContext::UI; + bIsFatalError = g_pSquirrel<ScriptContext::UI>->m_bFatalCompilationErrors; + } spdlog::error("{} SCRIPT COMPILE ERROR {}", GetContextName(realContext), error); spdlog::error("{} line [{}] column [{}]", file, line, column); - // dont call the original since it kills game - // in the future it'd be nice to do an actual error with UICodeCallback_ErrorDialog here, but only if we're compiling level scripts - // compilestring and stuff shouldn't tho - // though, that also has potential to be REALLY bad if we're compiling ui scripts lol + // use disconnect to display an error message for the compile error, but only if the compilation error was fatal + // todo, we could get this from sqvm itself probably, rather than hooking sq_compiler_create + if (bIsFatalError) + { + // kill dedicated server if we hit this + if (IsDedicatedServer()) + abort(); + else + { + R2::Cbuf_AddText( + R2::Cbuf_GetCurrentPlayer(), + fmt::format("disconnect \"Encountered {} script compilation error, see console for details.\"", GetContextName(realContext)) + .c_str(), + R2::cmd_source_t::kCommandSrcCode); + + // likely temp: show console so user can see any errors, as error message wont display if ui is dead + // maybe we could disable all mods other than the coremods and try a reload before doing this? + // could also maybe do some vgui bullshit to show something visually rather than console + if (realContext == ScriptContext::UI) + R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "showconsole", R2::cmd_source_t::kCommandSrcCode); + } + } + + // dont call the original function since it kills game lol } -template <ScriptContext context> char CallScriptInitCallbackHook(void* sqvm, const char* callback) +template <ScriptContext context> +int64_t (*__fastcall RegisterSquirrelFunction)(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown); +template <ScriptContext context> +int64_t __fastcall RegisterSquirrelFunctionHook(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown) { - char ret; - - if (context == ScriptContext::CLIENT) + if (IsUIVM(context, sqvm->sqvm)) { - ScriptContext realContext = context; // ui and client use the same function so we use this for prints - bool shouldCallCustomCallbacks = false; - - // since we don't hook arbitrary callbacks yet, make sure we're only doing callbacks on inits - if (!strcmp(callback, "UICodeCallback_UIInit")) + if (g_pSquirrel<ScriptContext::UI>->m_funcOverrides.count(funcReg->squirrelFuncName)) { - realContext = ScriptContext::UI; - shouldCallCustomCallbacks = true; + g_pSquirrel<ScriptContext::UI>->m_funcOriginals[funcReg->squirrelFuncName] = funcReg->funcPtr; + funcReg->funcPtr = g_pSquirrel<ScriptContext::UI>->m_funcOverrides[funcReg->squirrelFuncName]; + spdlog::info("Replacing {} in UI", std::string(funcReg->squirrelFuncName)); } - else if (!strcmp(callback, "ClientCodeCallback_MapSpawn")) - shouldCallCustomCallbacks = true; - // run before callbacks - // todo: we need to verify if RunOn is valid for current state before calling callbacks - if (shouldCallCustomCallbacks) - { - for (Mod mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled) - continue; + return g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc(sqvm, funcReg, unknown); + } - for (ModScript script : mod.Scripts) - { - for (ModScriptCallback modCallback : script.Callbacks) - { - if (modCallback.Context == realContext && modCallback.BeforeCallback.length()) - { - spdlog::info( - "Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.BeforeCallback); - ClientCallScriptInitCallback(sqvm, modCallback.BeforeCallback.c_str()); - } - } - } - } - } + if (g_pSquirrel<context>->m_funcOverrides.find(funcReg->squirrelFuncName) != g_pSquirrel<context>->m_funcOverrides.end()) + { + g_pSquirrel<context>->m_funcOriginals[funcReg->squirrelFuncName] = funcReg->funcPtr; + funcReg->funcPtr = g_pSquirrel<context>->m_funcOverrides[funcReg->squirrelFuncName]; + spdlog::info("Replacing {} in Client", std::string(funcReg->squirrelFuncName)); + } - spdlog::info("{} CodeCallback {} called", GetContextName(realContext), callback); - if (!shouldCallCustomCallbacks) - spdlog::info("Not executing custom callbacks for CodeCallback {}", callback); - ret = ClientCallScriptInitCallback(sqvm, callback); + return g_pSquirrel<context>->RegisterSquirrelFunc(sqvm, funcReg, unknown); +} - // run after callbacks - if (shouldCallCustomCallbacks) - { - for (Mod mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled) - continue; +template <ScriptContext context> bool (*__fastcall CallScriptInitCallback)(void* sqvm, const char* callback); +template <ScriptContext context> bool __fastcall CallScriptInitCallbackHook(void* sqvm, const char* callback) +{ + ScriptContext realContext = context; + bool bShouldCallCustomCallbacks = true; - for (ModScript script : mod.Scripts) - { - for (ModScriptCallback modCallback : script.Callbacks) - { - if (modCallback.Context == realContext && modCallback.AfterCallback.length()) - { - spdlog::info( - "Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.AfterCallback); - ClientCallScriptInitCallback(sqvm, modCallback.AfterCallback.c_str()); - } - } - } - } - } + if (context == ScriptContext::CLIENT) + { + if (!strcmp(callback, "UICodeCallback_UIInit")) + realContext = ScriptContext::UI; + else if (strcmp(callback, "ClientCodeCallback_MapSpawn")) + bShouldCallCustomCallbacks = false; } else if (context == ScriptContext::SERVER) - { - // since we don't hook arbitrary callbacks yet, make sure we're only doing callbacks on inits - bool shouldCallCustomCallbacks = !strcmp(callback, "CodeCallback_MapSpawn"); + bShouldCallCustomCallbacks = !strcmp(callback, "CodeCallback_MapSpawn"); - // run before callbacks - // todo: we need to verify if RunOn is valid for current state before calling callbacks - if (shouldCallCustomCallbacks) + if (bShouldCallCustomCallbacks) + { + for (Mod mod : g_pModManager->m_LoadedMods) { - for (Mod mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled) - continue; + if (!mod.m_bEnabled) + continue; - for (ModScript script : mod.Scripts) + for (ModScript script : mod.Scripts) + { + for (ModScriptCallback modCallback : script.Callbacks) { - for (ModScriptCallback modCallback : script.Callbacks) + if (modCallback.Context == realContext && modCallback.BeforeCallback.length()) { - if (modCallback.Context == ScriptContext::SERVER && modCallback.BeforeCallback.length()) - { - spdlog::info("Running custom {} script callback \"{}\"", GetContextName(context), modCallback.BeforeCallback); - ServerCallScriptInitCallback(sqvm, modCallback.BeforeCallback.c_str()); - } + spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.BeforeCallback); + CallScriptInitCallback<context>(sqvm, modCallback.BeforeCallback.c_str()); } } } } + } - spdlog::info("{} CodeCallback {} called", GetContextName(context), callback); - if (!shouldCallCustomCallbacks) - spdlog::info("Not executing custom callbacks for CodeCallback {}", callback); - ret = ServerCallScriptInitCallback(sqvm, callback); + spdlog::info("{} CodeCallback {} called", GetContextName(realContext), callback); + if (!bShouldCallCustomCallbacks) + spdlog::info("Not executing custom callbacks for CodeCallback {}", callback); + bool ret = CallScriptInitCallback<context>(sqvm, callback); - // run after callbacks - if (shouldCallCustomCallbacks) + // run after callbacks + if (bShouldCallCustomCallbacks) + { + for (Mod mod : g_pModManager->m_LoadedMods) { - for (Mod mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled) - continue; + if (!mod.m_bEnabled) + continue; - for (ModScript script : mod.Scripts) + for (ModScript script : mod.Scripts) + { + for (ModScriptCallback modCallback : script.Callbacks) { - for (ModScriptCallback modCallback : script.Callbacks) + if (modCallback.Context == realContext && modCallback.AfterCallback.length()) { - if (modCallback.Context == ScriptContext::SERVER && modCallback.AfterCallback.length()) - { - spdlog::info("Running custom {} script callback \"{}\"", GetContextName(context), modCallback.AfterCallback); - ServerCallScriptInitCallback(sqvm, modCallback.AfterCallback.c_str()); - } + spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.AfterCallback); + CallScriptInitCallback<context>(sqvm, modCallback.AfterCallback.c_str()); } } } @@ -477,113 +402,197 @@ template <ScriptContext context> char CallScriptInitCallbackHook(void* sqvm, con return ret; } -template <ScriptContext context> void ExecuteCodeCommand(const CCommand& args) +template <ScriptContext context> void ConCommand_script(const CCommand& args) { - if (context == ScriptContext::CLIENT) - g_ClientSquirrelManager->ExecuteCode(args.ArgS()); - else if (context == ScriptContext::UI) - g_UISquirrelManager->ExecuteCode(args.ArgS()); - else if (context == ScriptContext::SERVER) - g_ServerSquirrelManager->ExecuteCode(args.ArgS()); + g_pSquirrel<context>->ExecuteCode(args.ArgS()); } -SQRESULT SQ_DevFuncStub(void* sqvm) +// literal class type that wraps a constant expression string +template <size_t N> struct TemplateStringLiteral { - spdlog::warn("Blocked execution of squirrel developer function for security reasons. To re-enable them use start parameter " - "-allowSquirrelDevFunctions."); - return SQRESULT_NULL; -} + constexpr TemplateStringLiteral(const char (&str)[N]) + { + std::copy_n(str, N, value); + } -template <ScriptContext context> int64_t RegisterSquirrelFuncHook(void* sqvm, SQFuncRegistration* funcReg, char unknown) + char value[N]; +}; + +template <ScriptContext context, TemplateStringLiteral funcName> SQRESULT SQ_StubbedFunc(HSquirrelVM* sqvm) { - static std::set<std::string> allowedDevFunctions = { - "Dev_CommandLineHasParm", - "Dev_CommandLineParmValue", - "Dev_CommandLineRemoveParm", - }; - - if ((funcReg->devLevel == 1) && (!CommandLine()->CheckParm("-allowSquirrelDevFunctions")) && - (!allowedDevFunctions.count(funcReg->squirrelFuncName))) - funcReg->funcPtr = reinterpret_cast<void*>(SQ_DevFuncStub); - - if (context == ScriptContext::SERVER) - return ServerRegisterSquirrelFunc(sqvm, funcReg, unknown); - else - return ClientRegisterSquirrelFunc(sqvm, funcReg, unknown); + spdlog::info("Blocking call to stubbed function {} in {}", funcName.value, GetContextName(context)); + return SQRESULT_NULL; } -const char* sq_getTypeName(int type) +template <ScriptContext context> void StubUnsafeSQFuncs() { - switch (type) + if (!Tier0::CommandLine()->CheckParm("-allowunsafesqfuncs")) { - case OT_ASSET: - return "asset"; - case OT_INTEGER: - return "int"; - case OT_BOOL: - return "bool"; - case SQOBJECT_NUMERIC: - return "float or int"; - case OT_NULL: - return "null"; - case OT_VECTOR: - return "vector"; - case 0: - return "var"; - case OT_USERDATA: - return "userdata"; - case OT_FLOAT: - return "float"; - case OT_STRING: - return "string"; - case 0x8000040: - return "array"; - case 0x8000200: - return "function"; - case 0x8100000: - return "structdef"; - case OT_THREAD: - return "thread"; - case OT_FUNCPROTO: - return "function"; - case OT_CLAAS: - return "class"; - case OT_WEAKREF: - return "weakref"; - case 0x8080000: - return "unimplemented function"; - case 0x8200000: - return "struct instance"; - case 0xA000020: - return "table"; - case 0xA008000: - return "instance"; - case 0xA400000: - return "entity"; + g_pSquirrel<context>->AddFuncOverride("DevTextBufferWrite", SQ_StubbedFunc<context, "DevTextBufferWrite">); + g_pSquirrel<context>->AddFuncOverride("DevTextBufferClear", SQ_StubbedFunc<context, "DevTextBufferClear">); + g_pSquirrel<context>->AddFuncOverride("DevTextBufferDumpToFile", SQ_StubbedFunc<context, "DevTextBufferDumpToFile">); + g_pSquirrel<context>->AddFuncOverride("Dev_CommandLineAddParam", SQ_StubbedFunc<context, "Dev_CommandLineAddParam">); + g_pSquirrel<context>->AddFuncOverride("DevP4Checkout", SQ_StubbedFunc<context, "DevP4Checkout">); + g_pSquirrel<context>->AddFuncOverride("DevP4Add", SQ_StubbedFunc<context, "DevP4Add">); } - return ""; } -SQReturnTypeEnum GetReturnTypeEnumFromString(const char* returnTypeString) +ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) { + AUTOHOOK_DISPATCH_MODULE(client.dll) + + g_pSquirrel<ScriptContext::CLIENT> = new SquirrelManager<ScriptContext::CLIENT>; + g_pSquirrel<ScriptContext::UI> = new SquirrelManager<ScriptContext::UI>; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst = module.Offset(0x12120).As<sq_defconstType>(); + g_pSquirrel<ScriptContext::UI>->__sq_defconst = g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_compilebuffer = module.Offset(0x3110).As<sq_compilebufferType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushroottable = module.Offset(0x5860).As<sq_pushroottableType>(); + g_pSquirrel<ScriptContext::UI>->__sq_compilebuffer = g_pSquirrel<ScriptContext::CLIENT>->__sq_compilebuffer; + g_pSquirrel<ScriptContext::UI>->__sq_pushroottable = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushroottable; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_call = module.Offset(0x8650).As<sq_callType>(); + g_pSquirrel<ScriptContext::UI>->__sq_call = g_pSquirrel<ScriptContext::CLIENT>->__sq_call; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_newarray = module.Offset(0x39F0).As<sq_newarrayType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_arrayappend = module.Offset(0x3C70).As<sq_arrayappendType>(); + g_pSquirrel<ScriptContext::UI>->__sq_newarray = g_pSquirrel<ScriptContext::CLIENT>->__sq_newarray; + g_pSquirrel<ScriptContext::UI>->__sq_arrayappend = g_pSquirrel<ScriptContext::CLIENT>->__sq_arrayappend; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_newtable = module.Offset(0x3960).As<sq_newtableType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot = module.Offset(0x70B0).As<sq_newslotType>(); + g_pSquirrel<ScriptContext::UI>->__sq_newtable = g_pSquirrel<ScriptContext::CLIENT>->__sq_newtable; + g_pSquirrel<ScriptContext::UI>->__sq_newslot = g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushstring = module.Offset(0x3440).As<sq_pushstringType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger = module.Offset(0x36A0).As<sq_pushintegerType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat = module.Offset(0x3800).As<sq_pushfloatType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool = module.Offset(0x3710).As<sq_pushboolType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset = module.Offset(0x3560).As<sq_pushassetType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushvector = module.Offset(0x3780).As<sq_pushvectorType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror = module.Offset(0x8470).As<sq_raiseerrorType>(); + g_pSquirrel<ScriptContext::UI>->__sq_pushstring = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushstring; + g_pSquirrel<ScriptContext::UI>->__sq_pushinteger = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger; + g_pSquirrel<ScriptContext::UI>->__sq_pushfloat = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat; + g_pSquirrel<ScriptContext::UI>->__sq_pushbool = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool; + g_pSquirrel<ScriptContext::UI>->__sq_pushvector = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushvector; + g_pSquirrel<ScriptContext::UI>->__sq_pushasset = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset; + g_pSquirrel<ScriptContext::UI>->__sq_raiseerror = g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_getstring = module.Offset(0x60C0).As<sq_getstringType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getinteger = module.Offset(0x60E0).As<sq_getintegerType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getfloat = module.Offset(0x6100).As<sq_getfloatType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getbool = module.Offset(0x6130).As<sq_getboolType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_get = module.Offset(0x7C30).As<sq_getType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getasset = module.Offset(0x6010).As<sq_getassetType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getuserdata = module.Offset(0x63D0).As<sq_getuserdataType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getvector = module.Offset(0x6140).As<sq_getvectorType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getthisentity = module.Offset(0x12F80).As<sq_getthisentityType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getentity = module.Offset(0x6140).As<sq_getentityType>(); + g_pSquirrel<ScriptContext::UI>->__sq_getstring = g_pSquirrel<ScriptContext::CLIENT>->__sq_getstring; + g_pSquirrel<ScriptContext::UI>->__sq_getinteger = g_pSquirrel<ScriptContext::CLIENT>->__sq_getinteger; + g_pSquirrel<ScriptContext::UI>->__sq_getfloat = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfloat; + g_pSquirrel<ScriptContext::UI>->__sq_getbool = g_pSquirrel<ScriptContext::CLIENT>->__sq_getbool; + g_pSquirrel<ScriptContext::UI>->__sq_get = g_pSquirrel<ScriptContext::CLIENT>->__sq_get; + g_pSquirrel<ScriptContext::UI>->__sq_getasset = g_pSquirrel<ScriptContext::CLIENT>->__sq_getasset; + g_pSquirrel<ScriptContext::UI>->__sq_getuserdata = g_pSquirrel<ScriptContext::CLIENT>->__sq_getuserdata; + g_pSquirrel<ScriptContext::UI>->__sq_getvector = g_pSquirrel<ScriptContext::CLIENT>->__sq_getvector; + g_pSquirrel<ScriptContext::UI>->__sq_getthisentity = g_pSquirrel<ScriptContext::CLIENT>->__sq_getthisentity; + g_pSquirrel<ScriptContext::CLIENT>->__sq_getentity = g_pSquirrel<ScriptContext::CLIENT>->__sq_getentity; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_createuserdata = module.Offset(0x38D0).As<sq_createuserdataType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_setuserdatatypeid = module.Offset(0x6490).As<sq_setuserdatatypeidType>(); + g_pSquirrel<ScriptContext::UI>->__sq_createuserdata = g_pSquirrel<ScriptContext::CLIENT>->__sq_createuserdata; + g_pSquirrel<ScriptContext::UI>->__sq_setuserdatatypeid = g_pSquirrel<ScriptContext::CLIENT>->__sq_setuserdatatypeid; + + MAKEHOOK( + module.Offset(0x108E0), + &RegisterSquirrelFunctionHook<ScriptContext::CLIENT>, + &g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc); + g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc = g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc; + + // uiscript_reset concommand: don't loop forever if compilation fails + module.Offset(0x3C6E4C).NOP(6); + + MAKEHOOK(module.Offset(0x8AD0), &sq_compiler_createHook<ScriptContext::CLIENT>, &sq_compiler_create<ScriptContext::CLIENT>); + + MAKEHOOK(module.Offset(0x12B00), &SQPrintHook<ScriptContext::CLIENT>, &SQPrint<ScriptContext::CLIENT>); + MAKEHOOK(module.Offset(0x12BA0), &SQPrintHook<ScriptContext::UI>, &SQPrint<ScriptContext::UI>); + + MAKEHOOK(module.Offset(0x26130), &CreateNewVMHook<ScriptContext::CLIENT>, &CreateNewVM<ScriptContext::CLIENT>); + MAKEHOOK(module.Offset(0x26E70), &DestroyVMHook<ScriptContext::CLIENT>, &DestroyVM<ScriptContext::CLIENT>); + MAKEHOOK(module.Offset(0x79A50), &ScriptCompileErrorHook<ScriptContext::CLIENT>, &SQCompileError<ScriptContext::CLIENT>); + + MAKEHOOK(module.Offset(0x10190), &CallScriptInitCallbackHook<ScriptContext::CLIENT>, &CallScriptInitCallback<ScriptContext::CLIENT>); + + RegisterConCommand("script_client", ConCommand_script<ScriptContext::CLIENT>, "Executes script code on the client vm", FCVAR_CLIENTDLL); + RegisterConCommand("script_ui", ConCommand_script<ScriptContext::UI>, "Executes script code on the ui vm", FCVAR_CLIENTDLL); + + StubUnsafeSQFuncs<ScriptContext::CLIENT>(); + StubUnsafeSQFuncs<ScriptContext::UI>(); +} - static std::map<std::string, SQReturnTypeEnum> sqEnumStrMap = { - {"bool", SqReturnBoolean}, - {"float", SqReturnFloat}, - {"vector", SqReturnVector}, - {"int", SqReturnInteger}, - {"entity", SqReturnEntity}, - {"string", SqReturnString}, - {"array", SqReturnArrays}, - {"asset", SqReturnAsset}, - {"table", SqReturnTable}}; - - if (sqEnumStrMap.count(returnTypeString)) - { - return sqEnumStrMap[returnTypeString]; - } - else - { - return SqReturnDefault; // previous default value - } +ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) +{ + AUTOHOOK_DISPATCH_MODULE(server.dll) + + g_pSquirrel<ScriptContext::SERVER> = new SquirrelManager<ScriptContext::SERVER>; + + g_pSquirrel<ScriptContext::SERVER>->__sq_defconst = module.Offset(0x1F550).As<sq_defconstType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_compilebuffer = module.Offset(0x3110).As<sq_compilebufferType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushroottable = module.Offset(0x5840).As<sq_pushroottableType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_call = module.Offset(0x8620).As<sq_callType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_newarray = module.Offset(0x39F0).As<sq_newarrayType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_arrayappend = module.Offset(0x3C70).As<sq_arrayappendType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_newtable = module.Offset(0x3960).As<sq_newtableType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_newslot = module.Offset(0x7080).As<sq_newslotType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_pushstring = module.Offset(0x3440).As<sq_pushstringType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushinteger = module.Offset(0x36A0).As<sq_pushintegerType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushfloat = module.Offset(0x3800).As<sq_pushfloatType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushbool = module.Offset(0x3710).As<sq_pushboolType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushasset = module.Offset(0x3560).As<sq_pushassetType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushvector = module.Offset(0x3780).As<sq_pushvectorType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_raiseerror = module.Offset(0x8440).As<sq_raiseerrorType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_getstring = module.Offset(0x60A0).As<sq_getstringType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getinteger = module.Offset(0x60C0).As<sq_getintegerType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getfloat = module.Offset(0x60E0).As<sq_getfloatType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getbool = module.Offset(0x6110).As<sq_getboolType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getasset = module.Offset(0x5FF0).As<sq_getassetType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getuserdata = module.Offset(0x63B0).As<sq_getuserdataType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getvector = module.Offset(0x6120).As<sq_getvectorType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_get = module.Offset(0x7C00).As<sq_getType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getthisentity = module.Offset(0x203B0).As<sq_getthisentityType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getentity = module.Offset(0x6120).As<sq_getentityType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_createuserdata = module.Offset(0x38D0).As<sq_createuserdataType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_setuserdatatypeid = module.Offset(0x6470).As<sq_setuserdatatypeidType>(); + + MAKEHOOK( + module.Offset(0x1DD10), + &RegisterSquirrelFunctionHook<ScriptContext::SERVER>, + &g_pSquirrel<ScriptContext::SERVER>->RegisterSquirrelFunc); + + MAKEHOOK(module.Offset(0x8AA0), &sq_compiler_createHook<ScriptContext::SERVER>, &sq_compiler_create<ScriptContext::SERVER>); + + MAKEHOOK(module.Offset(0x1FE90), &SQPrintHook<ScriptContext::SERVER>, &SQPrint<ScriptContext::SERVER>); + MAKEHOOK(module.Offset(0x260E0), &CreateNewVMHook<ScriptContext::SERVER>, &CreateNewVM<ScriptContext::SERVER>); + MAKEHOOK(module.Offset(0x26E20), &DestroyVMHook<ScriptContext::SERVER>, &DestroyVM<ScriptContext::SERVER>); + MAKEHOOK(module.Offset(0x799E0), &ScriptCompileErrorHook<ScriptContext::SERVER>, &SQCompileError<ScriptContext::SERVER>); + MAKEHOOK(module.Offset(0x1D5C0), &CallScriptInitCallbackHook<ScriptContext::SERVER>, &CallScriptInitCallback<ScriptContext::SERVER>); + + // FCVAR_CHEAT and FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS allows clients to execute this, but since it's unsafe we only allow it when cheats + // are enabled for script_client and script_ui, we don't use cheats, so clients can execute them on themselves all they want + RegisterConCommand( + "script", + ConCommand_script<ScriptContext::SERVER>, + "Executes script code on the server vm", + FCVAR_GAMEDLL | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS | FCVAR_CHEAT); + + StubUnsafeSQFuncs<ScriptContext::SERVER>(); } |