From 93fe64e04ed766727634e5b5f6906f6461a70711 Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Wed, 21 Jul 2021 00:51:32 +0100 Subject: add eval commands and script compile error hook --- NorthstarDedicatedTest/squirrel.cpp | 130 +++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 24 deletions(-) (limited to 'NorthstarDedicatedTest/squirrel.cpp') diff --git a/NorthstarDedicatedTest/squirrel.cpp b/NorthstarDedicatedTest/squirrel.cpp index 90d16e89..27b997ab 100644 --- a/NorthstarDedicatedTest/squirrel.cpp +++ b/NorthstarDedicatedTest/squirrel.cpp @@ -3,6 +3,7 @@ #include "hooks.h" #include "hookutils.h" #include "sigscanning.h" +#include "concommand.h" #include // hook forward declarations @@ -12,10 +13,31 @@ SQPrintType UISQPrint; SQPrintType ServerSQPrint; template SQInteger SQPrintHook(void* sqvm, char* fmt, ...); -typedef void* (*CreateNewVMType)(void* a1, Context contextArg); +typedef void*(*CreateNewVMType)(void* a1, Context contextArg); CreateNewVMType ClientCreateNewVM; // only need a client one since ui doesn't have its own func for this CreateNewVMType ServerCreateNewVM; -void* CreateNewVMHook(void* a1, Context contextArg); +template void* CreateNewVMHook(void* a1, Context 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 void DestroyVMHook(void* a1, void* sqvm); + +typedef void (*SQCompileErrorType)(void* sqvm, const char* error, const char* file, int line, int column); +SQCompileErrorType ClientSQCompileError; // only need a client one since ui doesn't have its own func for this +SQCompileErrorType ServerSQCompileError; +template void SQCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column); + +sq_compilebufferType ClientSq_compilebuffer; +sq_compilebufferType ServerSq_compilebuffer; + +sq_pushroottableType ClientSq_pushroottable; +sq_pushroottableType ServerSq_pushroottable; + +sq_callType ClientSq_call; +sq_callType ServerSq_call; + +template void ExecuteCodeCommand(const CCommand& args); // inits SquirrelManager* g_ClientSquirrelManager; @@ -29,13 +51,21 @@ void InitialiseClientSquirrel(HMODULE baseAddress) // client inits g_ClientSquirrelManager = new SquirrelManager(); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x12B00, &SQPrintHook, reinterpret_cast(&ClientSQPrint)); // client print function + RegisterConCommand("script_client", ExecuteCodeCommand, "Executes script code in the client vm", FCVAR_CLIENTDLL); // ui inits g_UISquirrelManager = new SquirrelManager(); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x12BA0, &SQPrintHook, reinterpret_cast(&UISQPrint)); // ui print function + RegisterConCommand("script_ui", ExecuteCodeCommand, "Executes script code in the client 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); - // hooks for both client and ui, since they share some functions - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26130, &CreateNewVMHook, reinterpret_cast(&ClientCreateNewVM)); // client createnewvm function + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26130, &CreateNewVMHook, reinterpret_cast(&ClientCreateNewVM)); // client createnewvm function + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26E70, &DestroyVMHook, reinterpret_cast(&ClientDestroyVM)); // client destroyvm function + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x79A50, &SQCompileErrorHook, reinterpret_cast(&ClientSQCompileError)); // client compileerror function } void InitialiseServerSquirrel(HMODULE baseAddress) @@ -43,8 +73,17 @@ void InitialiseServerSquirrel(HMODULE baseAddress) g_ServerSquirrelManager = new SquirrelManager(); HookEnabler hook; + + ServerSq_compilebuffer = (sq_compilebufferType)((char*)baseAddress + 0x3110); + ServerSq_pushroottable = (sq_pushroottableType)((char*)baseAddress + 0x5840); + ServerSq_call = (sq_callType)((char*)baseAddress + 0x8620); + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1FE90, &SQPrintHook, reinterpret_cast(&ServerSQPrint)); // server print function - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x260E0, &CreateNewVMHook, reinterpret_cast(&ServerCreateNewVM)); // server createnewvm function + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x260E0, &CreateNewVMHook, reinterpret_cast(&ServerCreateNewVM)); // server createnewvm function + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26E20, &DestroyVMHook, reinterpret_cast(&ServerDestroyVM)); // server destroyvm function + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x799E0, &SQCompileErrorHook, reinterpret_cast(&ServerSQCompileError)); // server compileerror function + + RegisterConCommand("script", ExecuteCodeCommand, "Executes script code in the server vm", FCVAR_GAMEDLL); } // hooks @@ -59,7 +98,7 @@ template SQInteger SQPrintHook(void* sqvm, char* fmt, ...) if (charsWritten > 0) { if (buf[charsWritten - 1] == '\n') - buf[charsWritten - 1] == '\0'; + buf[charsWritten - 1] = '\0'; spdlog::info("[{} SCRIPT] {}", GetContextName(context), buf); } @@ -68,35 +107,78 @@ template SQInteger SQPrintHook(void* sqvm, char* fmt, ...) return 0; } -void* CreateNewVMHook(void* a1, Context context) -{ - spdlog::info("CreateNewVM {}", GetContextName(context)); - +template void* CreateNewVMHook(void* a1, Context realContext) +{ + void* sqvm; + if (context == CLIENT) { - void* sqvm = ClientCreateNewVM(a1, context); - g_ClientSquirrelManager->sqvm = sqvm; + sqvm = ClientCreateNewVM(a1, realContext); - return sqvm; + if (realContext == UI) + g_UISquirrelManager->sqvm = sqvm; + else + g_ClientSquirrelManager->sqvm = sqvm; } - else if (context == UI) + else if (context == SERVER) { - void* sqvm = ClientCreateNewVM(a1, context); - g_UISquirrelManager->sqvm = sqvm; + sqvm = ServerCreateNewVM(a1, context); + g_ServerSquirrelManager->sqvm = sqvm; + } + + spdlog::info("CreateNewVM {} {}", GetContextName(context), sqvm); + return sqvm; +} + +template void DestroyVMHook(void* a1, void* sqvm) +{ + Context realContext = context; // ui and client use the same function so we use this for prints - return sqvm; + if (context == CLIENT) + { + if (g_ClientSquirrelManager->sqvm == sqvm) + g_ClientSquirrelManager->sqvm = nullptr; + else if (g_UISquirrelManager->sqvm == sqvm) + { + g_UISquirrelManager->sqvm = nullptr; + realContext == UI; + } + + ClientDestroyVM(a1, sqvm); } else if (context == SERVER) { - void* sqvm = ServerCreateNewVM(a1, context); - g_ServerSquirrelManager->sqvm = sqvm; - - return sqvm; + g_ServerSquirrelManager->sqvm = nullptr; + ServerDestroyVM(a1, sqvm); } + + spdlog::info("DestroyVM {} {}", GetContextName(realContext), sqvm); } -// manager -template SquirrelManager::SquirrelManager() : sqvm(nullptr) +template void SQCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column) { - + // note: i think vanilla might actually show the script line that errored, might be nice to implement that if it's a thing + // look into client.dll+79540 for way better errors too + + Context realContext = context; + if (context == CLIENT && sqvm == g_UISquirrelManager->sqvm) + realContext = UI; + + 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 +} + +template void ExecuteCodeCommand(const CCommand& args) +{ + if (context == CLIENT) + g_ClientSquirrelManager->ExecuteCode(args.ArgS()); + else if (context == UI) + g_UISquirrelManager->ExecuteCode(args.ArgS()); + else if (context == SERVER) + g_ServerSquirrelManager->ExecuteCode(args.ArgS()); } \ No newline at end of file -- cgit v1.2.3