From 30e67549449a0ffbb58f7fc736bdd9e4ce7ec9d5 Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Thu, 29 Jul 2021 02:57:31 +0100 Subject: add script api for mods and add temp fix for scripts.rson not loading right --- .../NorthstarDedicatedTest.vcxproj | 2 + .../NorthstarDedicatedTest.vcxproj.filters | 6 + NorthstarDedicatedTest/dllmain.cpp | 4 +- NorthstarDedicatedTest/filesystem.cpp | 12 ++ NorthstarDedicatedTest/modmanager.cpp | 8 ++ NorthstarDedicatedTest/scriptmodmenu.cpp | 124 +++++++++++++++++++++ NorthstarDedicatedTest/scriptmodmenu.h | 3 + NorthstarDedicatedTest/scriptsrson.cpp | 2 +- NorthstarDedicatedTest/serverauthentication.cpp | 14 +++ NorthstarDedicatedTest/squirrel.cpp | 54 +++++++++ NorthstarDedicatedTest/squirrel.h | 42 ++++++- 11 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 NorthstarDedicatedTest/scriptmodmenu.cpp create mode 100644 NorthstarDedicatedTest/scriptmodmenu.h (limited to 'NorthstarDedicatedTest') diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index 87ea35a2..203edbb3 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -307,6 +307,7 @@ + @@ -332,6 +333,7 @@ Create Create + diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index f6ff5453..71bae5bc 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -534,6 +534,9 @@ Header Files\Server\Authentication + + Header Files\Client + @@ -590,6 +593,9 @@ Source Files\Server\Authentication + + Source Files\Client + diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index cc40a00c..b0040598 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -9,7 +9,7 @@ #include "modmanager.h" #include "filesystem.h" #include "serverauthentication.h" -#include +#include "scriptmodmenu.h" bool initialised = false; @@ -53,7 +53,9 @@ void InitialiseNorthstar() if (!IsDedicated()) { AddDllLoadCallback("client.dll", InitialiseClientSquirrel); + AddDllLoadCallback("client.dll", InitialiseSourceConsole); + AddDllLoadCallback("client.dll", InitialiseScriptModMenu); } AddDllLoadCallback("server.dll", InitialiseServerSquirrel); diff --git a/NorthstarDedicatedTest/filesystem.cpp b/NorthstarDedicatedTest/filesystem.cpp index 1a2fb993..323b8756 100644 --- a/NorthstarDedicatedTest/filesystem.cpp +++ b/NorthstarDedicatedTest/filesystem.cpp @@ -21,6 +21,10 @@ typedef void(*AddSearchPathType)(IFileSystem* fileSystem, const char* pPath, con AddSearchPathType addSearchPathOriginal; void AddSearchPathHook(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType); +typedef FileHandle_t(*ReadFileFromFilesystemType)(IFileSystem* filesystem, const char* pPath, const char* pOptions, int64_t a4, uint32_t a5); +ReadFileFromFilesystemType readFileFromFilesystem; +FileHandle_t ReadFileFromFilesystemHook(IFileSystem* filesystem, const char* pPath, const char* pOptions, int64_t a4, uint32_t a5); + bool readingOriginalFile; std::string currentModPath; SourceInterface* g_Filesystem; @@ -34,6 +38,7 @@ void InitialiseFilesystem(HMODULE baseAddress) ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x5CBA0, &ReadFileFromVPKHook, reinterpret_cast(&readFileFromVPK)); ENABLER_CREATEHOOK(hook, (*g_Filesystem)->m_vtable->ReadFromCache, &ReadFromCacheHook, reinterpret_cast(&readFromCache)); ENABLER_CREATEHOOK(hook, (*g_Filesystem)->m_vtable->AddSearchPath, &AddSearchPathHook, reinterpret_cast(&addSearchPathOriginal)); + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x15F20, &ReadFileFromFilesystemHook, reinterpret_cast(&readFileFromFilesystem)); } std::string ReadVPKFile(const char* path) @@ -136,4 +141,11 @@ void AddSearchPathHook(IFileSystem* fileSystem, const char* pPath, const char* p addSearchPathOriginal(fileSystem, currentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD); addSearchPathOriginal(fileSystem, COMPILED_ASSETS_PATH.string().c_str(), "GAME", PATH_ADD_TO_HEAD); } +} + +FileHandle_t ReadFileFromFilesystemHook(IFileSystem* filesystem, const char* pPath, const char* pOptions, int64_t a4, uint32_t a5) +{ + // this isn't super efficient, but it's necessary, since calling addsearchpath in readfilefromvpk doesn't work, possibly refactor later + TryReplaceFile((char*)pPath); + return readFileFromFilesystem(filesystem, pPath, pOptions, a4, a5); } \ No newline at end of file diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp index 964d37ae..ae1ad98a 100644 --- a/NorthstarDedicatedTest/modmanager.cpp +++ b/NorthstarDedicatedTest/modmanager.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "modmanager.h" #include "convar.h" +#include "concommand.h" #include "rapidjson/error/en.h" #include "rapidjson/document.h" @@ -272,7 +273,14 @@ void ModManager::CompileAssetsForFile(const char* filename) } +void ReloadModsCommand(const CCommand& args) +{ + g_ModManager->LoadMods(); +} + void InitialiseModManager(HMODULE baseAddress) { g_ModManager = new ModManager(); + + RegisterConCommand("reload_mods", ReloadModsCommand, "idk", FCVAR_NONE); } \ No newline at end of file diff --git a/NorthstarDedicatedTest/scriptmodmenu.cpp b/NorthstarDedicatedTest/scriptmodmenu.cpp new file mode 100644 index 00000000..f65b3a8c --- /dev/null +++ b/NorthstarDedicatedTest/scriptmodmenu.cpp @@ -0,0 +1,124 @@ +#include "pch.h" +#include "scriptmodmenu.h" +#include "modmanager.h" +#include "squirrel.h" + +// array NSGetModNames() +SQInteger SQ_GetModNames(void* sqvm) +{ + ClientSq_newarray(sqvm, 0); + + for (Mod* mod : g_ModManager->m_loadedMods) + { + ClientSq_pushstring(sqvm, mod->Name.c_str(), -1); + ClientSq_arrayappend(sqvm, -2); + } + + return 1; +} + +// string NSGetModDescriptionByModName(string modName) +SQInteger SQ_GetModDescription(void* sqvm) +{ + const SQChar* modName = ClientSq_getstring(sqvm, 1); + + // manual lookup, not super performant but eh not a big deal + for (Mod* mod : g_ModManager->m_loadedMods) + { + if (!mod->Name.compare(modName)) + { + ClientSq_pushstring(sqvm, mod->Description.c_str(), -1); + return 1; + } + } + + return 0; // return null +} + +// string NSGetModVersionByModName(string modName) +SQInteger SQ_GetModVersion(void* sqvm) +{ + const SQChar* modName = ClientSq_getstring(sqvm, 1); + + // manual lookup, not super performant but eh not a big deal + for (Mod* mod : g_ModManager->m_loadedMods) + { + if (!mod->Name.compare(modName)) + { + ClientSq_pushstring(sqvm, mod->Version.c_str(), -1); + return 1; + } + } + + return 0; // return null +} + +// string NSGetModDownloadLinkByModName(string modName) +SQInteger SQ_GetModDownloadLink(void* sqvm) +{ + const SQChar* modName = ClientSq_getstring(sqvm, 1); + + // manual lookup, not super performant but eh not a big deal + for (Mod* mod : g_ModManager->m_loadedMods) + { + if (!mod->Name.compare(modName)) + { + ClientSq_pushstring(sqvm, mod->DownloadLink.c_str(), -1); + return 1; + } + } + + return 0; // return null +} + +// int NSGetModLoadPriority(string modName) +SQInteger SQ_GetModLoadPriority(void* sqvm) +{ + const SQChar* modName = ClientSq_getstring(sqvm, 1); + + // manual lookup, not super performant but eh not a big deal + for (Mod* mod : g_ModManager->m_loadedMods) + { + if (!mod->Name.compare(modName)) + { + ClientSq_pushinteger(sqvm, mod->LoadPriority); + return 1; + } + } + + return 0; // return null +} + +// array NSGetModConvarsByModName(string modName) +SQInteger SQ_GetModConvars(void* sqvm) +{ + const SQChar* modName = ClientSq_getstring(sqvm, 1); + ClientSq_newarray(sqvm, 0); + + // manual lookup, not super performant but eh not a big deal + for (Mod* mod : g_ModManager->m_loadedMods) + { + if (!mod->Name.compare(modName)) + { + for (ModConVar* cvar : mod->ConVars) + { + ClientSq_pushstring(sqvm, cvar->Name.c_str(), -1); + ClientSq_arrayappend(sqvm, -2); + } + + return 1; + } + } + + return 1; // return empty array +} + +void InitialiseScriptModMenu(HMODULE baseAddress) +{ + g_UISquirrelManager->AddFuncRegistration("array", "NSGetModNames", "", "Returns the names of all loaded mods", SQ_GetModNames); + g_UISquirrelManager->AddFuncRegistration("string", "NSGetModDescriptionByModName", "asset modName", "Returns a given mod's description", SQ_GetModDescription); + g_UISquirrelManager->AddFuncRegistration("string", "NSGetModVersionByModName", "string modName", "Returns a given mod's version", SQ_GetModVersion); + g_UISquirrelManager->AddFuncRegistration("string", "NSGetModDownloadLinkByModName", "string modName", "Returns a given mod's download link", SQ_GetModVersion); + g_UISquirrelManager->AddFuncRegistration("int", "NSGetModLoadPriority", "string modName", "Returns a given mod's load priority", SQ_GetModLoadPriority); + g_UISquirrelManager->AddFuncRegistration("array", "NSGetModConvarsByModName", "string modName", "Returns the names of all a given mod's cvars", SQ_GetModConvars); +} \ No newline at end of file diff --git a/NorthstarDedicatedTest/scriptmodmenu.h b/NorthstarDedicatedTest/scriptmodmenu.h new file mode 100644 index 00000000..019346d3 --- /dev/null +++ b/NorthstarDedicatedTest/scriptmodmenu.h @@ -0,0 +1,3 @@ +#pragma once + +void InitialiseScriptModMenu(HMODULE baseAddress); \ No newline at end of file diff --git a/NorthstarDedicatedTest/scriptsrson.cpp b/NorthstarDedicatedTest/scriptsrson.cpp index b5aef421..9c590280 100644 --- a/NorthstarDedicatedTest/scriptsrson.cpp +++ b/NorthstarDedicatedTest/scriptsrson.cpp @@ -15,7 +15,7 @@ void ModManager::BuildScriptsRson() // not really important since it doesn't affect actual functionality at all, but the rson we output is really weird // has a shitload of newlines added, even in places where we don't modify it at all - std::string scriptsRson = ReadVPKOriginalFile("scripts/vscripts/scripts.rson"); + std::string scriptsRson = ReadVPKOriginalFile(VPK_SCRIPTS_RSON_PATH); scriptsRson += "\n\n// START MODDED SCRIPT CONTENT\n\n"; // newline before we start custom stuff for (Mod* mod : m_loadedMods) diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp index a856c00f..d6f3b2b2 100644 --- a/NorthstarDedicatedTest/serverauthentication.cpp +++ b/NorthstarDedicatedTest/serverauthentication.cpp @@ -19,11 +19,15 @@ CBaseClient__ActivatePlayerType CBaseClient__ActivatePlayer; typedef void(*CBaseClient__DisconnectType)(void* self, uint32_t unknownButAlways1, const char* reason, ...); CBaseClient__DisconnectType CBaseClient__Disconnect; +typedef char(*CGameClient__ExecuteStringCommandType)(void* self, uint32_t unknown, const char* pCommandString); +CGameClient__ExecuteStringCommandType CGameClient__ExecuteStringCommand; + // global vars ServerAuthenticationManager* g_ServerAuthenticationManager; ConVar* CVar_ns_auth_allow_insecure; ConVar* CVar_ns_auth_allow_insecure_write; +ConVar* CVar_sv_quota_stringcmdspersecond; void ServerAuthenticationManager::AddPlayerAuth(char* authToken, char* uid, char* pdata, size_t pdataSize) { @@ -167,18 +171,28 @@ void CBaseClient__DisconnectHook(void* self, uint32_t unknownButAlways1, const c CBaseClient__Disconnect(self, unknownButAlways1, buf); } +// maybe this should be done outside of auth code, but effort to refactor rn and it sorta fits +char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const char* pCommandString) +{ + // todo later, basically just limit to CVar_sv_quota_stringcmdspersecond->m_nValue stringcmds per client per second + return CGameClient__ExecuteStringCommand(self, unknown, pCommandString); +} + void InitialiseServerAuthentication(HMODULE baseAddress) { g_ServerAuthenticationManager = new ServerAuthenticationManager; CVar_ns_auth_allow_insecure = RegisterConVar("ns_auth_allow_insecure", "0", FCVAR_GAMEDLL, "Whether this server will allow unauthenicated players to connect"); CVar_ns_auth_allow_insecure_write = RegisterConVar("ns_auth_allow_insecure_write", "0", FCVAR_GAMEDLL, "Whether the pdata of unauthenticated clients will be written to disk when changed"); + // literally just stolen from a fix valve used in csgo + CVar_sv_quota_stringcmdspersecond = RegisterConVar("sv_quota_stringcmdspersecond", "40", FCVAR_NONE, "How many string commands per second clients are allowed to submit, 0 to disallow all string commands"); HookEnabler hook; ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x114430, &CBaseServer__ConnectClientHook, reinterpret_cast(&CBaseServer__ConnectClient)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x101740, &CBaseClient__ConnectHook, reinterpret_cast(&CBaseClient__Connect)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x100F80, &CBaseClient__ActivatePlayerHook, reinterpret_cast(&CBaseClient__ActivatePlayer)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1012C0, &CBaseClient__DisconnectHook, reinterpret_cast(&CBaseClient__Disconnect)); + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1022E0, &CGameClient__ExecuteStringCommandHook, reinterpret_cast(&CGameClient__ExecuteStringCommand)); // patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token { diff --git a/NorthstarDedicatedTest/squirrel.cpp b/NorthstarDedicatedTest/squirrel.cpp index b57e22ae..17758282 100644 --- a/NorthstarDedicatedTest/squirrel.cpp +++ b/NorthstarDedicatedTest/squirrel.cpp @@ -34,6 +34,7 @@ CallScriptInitCallbackType ClientCallScriptInitCallback; CallScriptInitCallbackType ServerCallScriptInitCallback; template char CallScriptInitCallbackHook(void* sqvm, const char* callback); +// core sqvm funcs sq_compilebufferType ClientSq_compilebuffer; sq_compilebufferType ServerSq_compilebuffer; @@ -46,6 +47,37 @@ sq_callType ServerSq_call; RegisterSquirrelFuncType ClientRegisterSquirrelFunc; RegisterSquirrelFuncType ServerRegisterSquirrelFunc; + +// sq stack array funcs +sq_newarrayType ClientSq_newarray; +sq_newarrayType ServerSq_newarray; + +sq_arrayappendType ClientSq_arrayappend; +sq_arrayappendType ServerSq_arrayappend; + + +// sq stack push funcs +sq_pushstringType ClientSq_pushstring; +sq_pushstringType ServerSq_pushstring; + +sq_pushintegerType ClientSq_pushinteger; +sq_pushintegerType ServerSq_pushinteger; + +sq_pushfloatType ClientSq_pushfloat; +sq_pushfloatType ServerSq_pushfloat; + + +// sq stack get funcs +sq_getstringType ClientSq_getstring; +sq_getstringType ServerSq_getstring; + +sq_getintegerType ClientSq_getinteger; +sq_getintegerType ServerSq_getinteger; + +sq_getfloatType ClientSq_getfloat; +sq_getfloatType ServerSq_getfloat; + + template void ExecuteCodeCommand(const CCommand& args); // inits @@ -80,6 +112,17 @@ void InitialiseClientSquirrel(HMODULE baseAddress) 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_getstring = (sq_getstringType)((char*)baseAddress + 0x60C0); + ClientSq_getinteger = (sq_getintegerType)((char*)baseAddress + 0x60E0); + ClientSq_getfloat = (sq_getfloatType)((char*)baseAddress + 0x6100); + 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, &ScriptCompileErrorHook, reinterpret_cast(&ClientSQCompileError)); // client compileerror function @@ -98,6 +141,17 @@ void InitialiseServerSquirrel(HMODULE baseAddress) 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_getstring = (sq_getstringType)((char*)baseAddress + 0x60A0); + ServerSq_getinteger = (sq_getintegerType)((char*)baseAddress + 0x60C0); + ServerSq_getfloat = (sq_getfloatType)((char*)baseAddress + 0x60E0); + 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 + 0x26E20, &DestroyVMHook, reinterpret_cast(&ServerDestroyVM)); // server destroyvm function diff --git a/NorthstarDedicatedTest/squirrel.h b/NorthstarDedicatedTest/squirrel.h index 2704dbc7..b094dbaf 100644 --- a/NorthstarDedicatedTest/squirrel.h +++ b/NorthstarDedicatedTest/squirrel.h @@ -12,6 +12,8 @@ typedef char SQChar; typedef SQUnsignedInteger SQBool; typedef SQInteger SQRESULT; +typedef SQInteger(*SQFunction)(void* sqvm); + struct CompileBufferState { const SQChar* buffer; @@ -53,6 +55,7 @@ struct SQFuncRegistration } }; +// core sqvm funcs typedef SQRESULT(*sq_compilebufferType)(void* sqvm, CompileBufferState* compileBuffer, const char* file, int a1, int a2); extern sq_compilebufferType ClientSq_compilebuffer; extern sq_compilebufferType ServerSq_compilebuffer; @@ -69,9 +72,44 @@ typedef int64_t(*RegisterSquirrelFuncType)(void* sqvm, SQFuncRegistration* funcR extern RegisterSquirrelFuncType ClientRegisterSquirrelFunc; extern RegisterSquirrelFuncType ServerRegisterSquirrelFunc; -//template void ExecuteSQCode(SquirrelManager sqManager, const char* code); // need this because we can't do template class functions in the .cpp file +// 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; + + +// 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 SQInteger(*SQFunction)(void* sqvm); template class SquirrelManager { -- cgit v1.2.3