add script api for mods and add temp fix for scripts.rson not loading right

This commit is contained in:
BobTheBob 2021-07-29 02:57:31 +01:00
parent a5a937d19f
commit 30e6754944
11 changed files with 267 additions and 4 deletions

View File

@ -307,6 +307,7 @@
<ClInclude Include="main.h" />
<ClInclude Include="modmanager.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="scriptmodmenu.h" />
<ClInclude Include="scriptsrson.h" />
<ClInclude Include="serverauthentication.h" />
<ClInclude Include="sigscanning.h" />
@ -332,6 +333,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="scriptmodmenu.cpp" />
<ClCompile Include="scriptsrson.cpp" />
<ClCompile Include="serverauthentication.cpp" />
<ClCompile Include="sigscanning.cpp" />

View File

@ -534,6 +534,9 @@
<ClInclude Include="serverauthentication.h">
<Filter>Header Files\Server\Authentication</Filter>
</ClInclude>
<ClInclude Include="scriptmodmenu.h">
<Filter>Header Files\Client</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@ -590,6 +593,9 @@
<ClCompile Include="serverauthentication.cpp">
<Filter>Source Files\Server\Authentication</Filter>
</ClCompile>
<ClCompile Include="scriptmodmenu.cpp">
<Filter>Source Files\Client</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="include\spdlog\fmt\bundled\LICENSE.rst">

View File

@ -9,7 +9,7 @@
#include "modmanager.h"
#include "filesystem.h"
#include "serverauthentication.h"
#include <iostream>
#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);

View File

@ -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<IFileSystem>* g_Filesystem;
@ -34,6 +38,7 @@ void InitialiseFilesystem(HMODULE baseAddress)
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x5CBA0, &ReadFileFromVPKHook, reinterpret_cast<LPVOID*>(&readFileFromVPK));
ENABLER_CREATEHOOK(hook, (*g_Filesystem)->m_vtable->ReadFromCache, &ReadFromCacheHook, reinterpret_cast<LPVOID*>(&readFromCache));
ENABLER_CREATEHOOK(hook, (*g_Filesystem)->m_vtable->AddSearchPath, &AddSearchPathHook, reinterpret_cast<LPVOID*>(&addSearchPathOriginal));
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x15F20, &ReadFileFromFilesystemHook, reinterpret_cast<LPVOID*>(&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);
}

View File

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

View File

@ -0,0 +1,124 @@
#include "pch.h"
#include "scriptmodmenu.h"
#include "modmanager.h"
#include "squirrel.h"
// array<string> 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<string> 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<string>", "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<string>", "NSGetModConvarsByModName", "string modName", "Returns the names of all a given mod's cvars", SQ_GetModConvars);
}

View File

@ -0,0 +1,3 @@
#pragma once
void InitialiseScriptModMenu(HMODULE baseAddress);

View File

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

View File

@ -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<LPVOID*>(&CBaseServer__ConnectClient));
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x101740, &CBaseClient__ConnectHook, reinterpret_cast<LPVOID*>(&CBaseClient__Connect));
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x100F80, &CBaseClient__ActivatePlayerHook, reinterpret_cast<LPVOID*>(&CBaseClient__ActivatePlayer));
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1012C0, &CBaseClient__DisconnectHook, reinterpret_cast<LPVOID*>(&CBaseClient__Disconnect));
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1022E0, &CGameClient__ExecuteStringCommandHook, reinterpret_cast<LPVOID*>(&CGameClient__ExecuteStringCommand));
// patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token
{

View File

@ -34,6 +34,7 @@ CallScriptInitCallbackType ClientCallScriptInitCallback;
CallScriptInitCallbackType ServerCallScriptInitCallback;
template<Context context> 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<Context context> 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<CLIENT>, reinterpret_cast<LPVOID*>(&ClientCreateNewVM)); // client createnewvm function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26E70, &DestroyVMHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientDestroyVM)); // client destroyvm function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x79A50, &ScriptCompileErrorHook<CLIENT>, reinterpret_cast<LPVOID*>(&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<SERVER>, reinterpret_cast<LPVOID*>(&ServerSQPrint)); // server print function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x260E0, &CreateNewVMHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerCreateNewVM)); // server createnewvm function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26E20, &DestroyVMHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerDestroyVM)); // server destroyvm function

View File

@ -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<Context context> void ExecuteSQCode(SquirrelManager<context> 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<Context context> class SquirrelManager
{