mirror of
https://github.com/R2Northstar/NorthstarLauncher
synced 2026-08-23 20:26:04 -04:00
add pdata support, improve filesystem hooks
This commit is contained in:
parent
80428e6f62
commit
64b8db08ed
9 changed files with 226 additions and 18 deletions
|
|
@ -308,6 +308,7 @@
|
|||
<ClInclude Include="modmanager.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="scriptsrson.h" />
|
||||
<ClInclude Include="serverauthentication.h" />
|
||||
<ClInclude Include="sigscanning.h" />
|
||||
<ClInclude Include="sourceconsole.h" />
|
||||
<ClInclude Include="sourceinterface.h" />
|
||||
|
|
@ -332,6 +333,7 @@
|
|||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="scriptsrson.cpp" />
|
||||
<ClCompile Include="serverauthentication.cpp" />
|
||||
<ClCompile Include="sigscanning.cpp" />
|
||||
<ClCompile Include="sourceconsole.cpp" />
|
||||
<ClCompile Include="sourceinterface.cpp" />
|
||||
|
|
|
|||
|
|
@ -88,6 +88,18 @@
|
|||
<Filter Include="Source Files\Shared\Mods\Compiled">
|
||||
<UniqueIdentifier>{14fc0931-acad-46ec-a55e-94f4469d4235}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Server">
|
||||
<UniqueIdentifier>{3d41d3fc-8a3b-4358-b3e8-4f06dc96abfe}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Server\Authentication">
|
||||
<UniqueIdentifier>{d69760a9-d5ec-4f3e-8f43-f74041654d44}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\Server">
|
||||
<UniqueIdentifier>{365e5c1f-4b2f-4d8b-a1d8-cdef401ca689}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\Server\Authentication">
|
||||
<UniqueIdentifier>{24fd0855-9288-4129-93ba-c6cafdc98d1b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
|
|
@ -519,6 +531,9 @@
|
|||
<ClInclude Include="scriptsrson.h">
|
||||
<Filter>Header Files\Shared\Mods\Compiled</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="serverauthentication.h">
|
||||
<Filter>Header Files\Server\Authentication</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
|
|
@ -572,6 +587,9 @@
|
|||
<ClCompile Include="scriptsrson.cpp">
|
||||
<Filter>Source Files\Shared\Mods\Compiled</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="serverauthentication.cpp">
|
||||
<Filter>Source Files\Server\Authentication</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="include\spdlog\fmt\bundled\LICENSE.rst">
|
||||
|
|
|
|||
|
|
@ -64,14 +64,14 @@ class ConCommand;
|
|||
// also i sure do hope this size is right because there's a fairly decent chance it isn't
|
||||
class ConVar
|
||||
{
|
||||
private:
|
||||
public:
|
||||
// if there are ever crashes caused by modifying custom cvars, check this
|
||||
unsigned char unknown[0x58];
|
||||
unsigned char unknown[0x40];
|
||||
char* m_pszString;
|
||||
int64_t m_StringLength;
|
||||
size_t m_StringLength;
|
||||
float m_fValue;
|
||||
int32_t m_nValue;
|
||||
unsigned char unknown2[0x10];
|
||||
unsigned char unknown2[0x28];
|
||||
|
||||
public:
|
||||
virtual void EngineDestructor(void) {}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "concommand.h"
|
||||
#include "modmanager.h"
|
||||
#include "filesystem.h"
|
||||
#include "serverauthentication.h"
|
||||
#include <iostream>
|
||||
|
||||
bool initialised = false;
|
||||
|
|
@ -56,6 +57,7 @@ void InitialiseNorthstar()
|
|||
}
|
||||
|
||||
AddDllLoadCallback("server.dll", InitialiseServerSquirrel);
|
||||
AddDllLoadCallback("engine.dll", InitialiseServerAuthentication);
|
||||
|
||||
AddDllLoadCallback("filesystem_stdio.dll", InitialiseFilesystem);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,12 @@ typedef bool(*ReadFromCacheType)(IFileSystem* filesystem, const char* path, void
|
|||
ReadFromCacheType readFromCache;
|
||||
bool ReadFromCacheHook(IFileSystem* filesystem, const char* path, void* result);
|
||||
|
||||
typedef void(*AddSearchPathType)(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType);
|
||||
AddSearchPathType addSearchPathOriginal;
|
||||
void AddSearchPathHook(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType);
|
||||
|
||||
bool readingOriginalFile;
|
||||
std::string currentModPath;
|
||||
SourceInterface<IFileSystem>* g_Filesystem;
|
||||
|
||||
void InitialiseFilesystem(HMODULE baseAddress)
|
||||
|
|
@ -28,6 +33,7 @@ void InitialiseFilesystem(HMODULE baseAddress)
|
|||
HookEnabler hook;
|
||||
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));
|
||||
}
|
||||
|
||||
std::string ReadVPKFile(const char* path)
|
||||
|
|
@ -60,13 +66,21 @@ std::string ReadVPKOriginalFile(const char* path)
|
|||
|
||||
void SetNewModSearchPaths(Mod* mod)
|
||||
{
|
||||
// put our new path to the head
|
||||
// in future we should look into manipulating paths at head manually, might be effort tho
|
||||
// potentially we could also determine whether the file we're setting paths for needs a mod dir, or compiled assets
|
||||
// put our new path to the head if we need to read from a different mod path
|
||||
// in the future we could also determine whether the file we're setting paths for needs a mod dir, or compiled assets
|
||||
if (mod != nullptr)
|
||||
(*g_Filesystem)->m_vtable->AddSearchPath(&*(*g_Filesystem), (fs::absolute(mod->ModDirectory) / MOD_OVERRIDE_DIR).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
|
||||
|
||||
(*g_Filesystem)->m_vtable->AddSearchPath(&*(*g_Filesystem), fs::absolute(COMPILED_ASSETS_PATH).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
|
||||
{
|
||||
if ((fs::absolute(mod->ModDirectory) / MOD_OVERRIDE_DIR).string().compare(currentModPath))
|
||||
{
|
||||
spdlog::info("changing mod search path from {} to {}", currentModPath, mod->ModDirectory.string());
|
||||
|
||||
addSearchPathOriginal(&*(*g_Filesystem), (fs::absolute(mod->ModDirectory) / MOD_OVERRIDE_DIR).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
|
||||
currentModPath = (fs::absolute(mod->ModDirectory) / MOD_OVERRIDE_DIR).string();
|
||||
}
|
||||
}
|
||||
else if (!currentModPath.size()) // if currentModPath isn't set yet, then push compiled to head
|
||||
addSearchPathOriginal(&*(*g_Filesystem), fs::absolute(COMPILED_ASSETS_PATH).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
|
||||
|
||||
}
|
||||
|
||||
bool TryReplaceFile(const char* path)
|
||||
|
|
@ -76,7 +90,7 @@ bool TryReplaceFile(const char* path)
|
|||
|
||||
(*g_ModManager).CompileAssetsForFile(path);
|
||||
|
||||
// is this efficient? no clue
|
||||
// is this efficient? could probably be improved
|
||||
for (ModOverrideFile* modFile : g_ModManager->m_modFiles)
|
||||
{
|
||||
if (!modFile->path.compare(fs::path(path).lexically_normal()))
|
||||
|
|
@ -111,4 +125,16 @@ bool ReadFromCacheHook(IFileSystem* filesystem, const char* path, void* result)
|
|||
return false;
|
||||
|
||||
return readFromCache(filesystem, path, result);
|
||||
}
|
||||
|
||||
void AddSearchPathHook(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType)
|
||||
{
|
||||
addSearchPathOriginal(fileSystem, pPath, pathID, addType);
|
||||
|
||||
// make sure current mod paths are at head
|
||||
if (!strcmp(pathID, "GAME") && currentModPath.compare(pPath) && addType == PATH_ADD_TO_HEAD)
|
||||
{
|
||||
addSearchPathOriginal(fileSystem, currentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD);
|
||||
addSearchPathOriginal(fileSystem, COMPILED_ASSETS_PATH.string().c_str(), "GAME", PATH_ADD_TO_HEAD);
|
||||
}
|
||||
}
|
||||
|
|
@ -162,12 +162,6 @@ Mod::Mod(fs::path modDir, char* jsonBuf)
|
|||
}
|
||||
}
|
||||
|
||||
// vpk stuff
|
||||
if (fs::exists(modDir / "vpk"))
|
||||
for (fs::directory_entry file : fs::directory_iterator(modDir / "vpk"))
|
||||
if (fs::is_regular_file(file) && file.path().extension() == "vpk")
|
||||
Vpks.push_back(file.path().string());
|
||||
|
||||
wasReadSuccessfully = true;
|
||||
}
|
||||
|
||||
|
|
@ -239,6 +233,12 @@ void ModManager::LoadMods()
|
|||
for (ModConVar* convar : mod->ConVars)
|
||||
if (g_CustomConvars.find(convar->Name) == g_CustomConvars.end()) // make sure convar isn't registered yet, unsure if necessary but idk what behaviour is for defining same convar multiple times
|
||||
RegisterConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str());
|
||||
|
||||
// read vpk paths
|
||||
if (fs::exists(mod->ModDirectory / "vpk"))
|
||||
for (fs::directory_entry file : fs::directory_iterator(mod->ModDirectory / "vpk"))
|
||||
if (fs::is_regular_file(file) && file.path().extension() == "vpk")
|
||||
mod->Vpks.push_back(file.path().string());
|
||||
}
|
||||
|
||||
// in a seperate loop because we register mod files in reverse order, since mods loaded later should have their files prioritised
|
||||
|
|
|
|||
130
NorthstarDedicatedTest/serverauthentication.cpp
Normal file
130
NorthstarDedicatedTest/serverauthentication.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#include "pch.h"
|
||||
#include "serverauthentication.h"
|
||||
#include "convar.h"
|
||||
#include "hookutils.h"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
// hooks
|
||||
typedef void(*RejectClientType)(void* a1, const char* a2, void* player, const char* fmt, ...);
|
||||
RejectClientType RejectClient;
|
||||
|
||||
typedef void*(*CBaseServer__ConnectClientType)(void* server, void* a2, void* a3, uint32_t a4, uint32_t a5, int32_t a6, void* a7, void* a8, char* serverFilter, void* a10, char a11, void* a12, char a13, char a14, void* a15, uint32_t a16, uint32_t a17);
|
||||
CBaseServer__ConnectClientType CBaseServer__ConnectClient;
|
||||
|
||||
typedef char(*CBaseClient__ConnectType)(void* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, int a7);
|
||||
CBaseClient__ConnectType CBaseClient__Connect;
|
||||
|
||||
// global vars
|
||||
ServerAuthenticationManager* g_ServerAuthenticationManager;
|
||||
|
||||
ConVar* CVar_ns_auth_allow_insecure;
|
||||
ConVar* CVar_ns_auth_allow_insecure_write;
|
||||
|
||||
ServerAuthenticationManager::ServerAuthenticationManager() : m_authData(std::unordered_map<std::string, AuthData*>())
|
||||
{}
|
||||
|
||||
void ServerAuthenticationManager::AddPlayerAuth(char* authToken, char* uid, char* pdata, size_t pdataSize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ServerAuthenticationManager::AuthenticatePlayer(__int64 player, char* authToken)
|
||||
{
|
||||
// straight up just given up
|
||||
if (!m_authData.empty() && m_authData.count(authToken))
|
||||
{
|
||||
// use stored auth data
|
||||
AuthData* authData = m_authData[authToken];
|
||||
|
||||
// uuid
|
||||
strcpy((char*)player + 0xF500, authData->uid);
|
||||
|
||||
// copy pdata into buffer
|
||||
memcpy((char*)player + 0x4FA, authData->pdata, authData->pdataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CVar_ns_auth_allow_insecure->m_nValue) // no auth data and insecure connections aren't allowed, so dc the client
|
||||
return false;
|
||||
|
||||
spdlog::info("wtf");
|
||||
spdlog::info(player);
|
||||
|
||||
// no auth data available and insecure connections are allowed, try reading from disk, using authtoken as uid
|
||||
|
||||
// uuid
|
||||
strcpy((char*)(player + 0xF500), authToken);
|
||||
|
||||
// try reading pdata file for player
|
||||
std::string pdataPath = "playerdata/playerdata_";
|
||||
pdataPath += authToken;
|
||||
pdataPath += ".pdata";
|
||||
|
||||
std::fstream pdataStream(pdataPath, std::ios_base::in);
|
||||
if (pdataStream.fail()) // file doesn't exist, use placeholder
|
||||
pdataStream = std::fstream("playerdata/placeholder_playerdata.pdata");
|
||||
|
||||
// get file length
|
||||
pdataStream.seekg(0, pdataStream.end);
|
||||
int length = pdataStream.tellg();
|
||||
pdataStream.seekg(0, pdataStream.beg);
|
||||
|
||||
// copy pdata into buffer
|
||||
|
||||
|
||||
pdataStream.read((char*)(player + 0x4FA), length);
|
||||
}
|
||||
|
||||
// set persistent data as ready
|
||||
*(char*)(player + 0x4a0) = (char)0x3;
|
||||
|
||||
return true; // auth successful, client stays on
|
||||
}
|
||||
|
||||
// store this in a var so we can use it in CBaseClient::Connect
|
||||
// this is fine because serverfilter ptr won't decay by the time we use this
|
||||
char* nextPlayerToken;
|
||||
|
||||
void* CBaseServer__ConnectClientHook(void* server, void* a2, void* a3, uint32_t a4, uint32_t a5, int32_t a6, void* a7, void* a8, char* serverFilter, void* a10, char a11, void* a12, char a13, char a14, void* a15, uint32_t a16, uint32_t a17)
|
||||
{
|
||||
// auth tokens are sent with serverfilter, can't be accessed from player struct to my knowledge, so have to do this here
|
||||
nextPlayerToken = serverFilter;
|
||||
|
||||
return CBaseServer__ConnectClient(server, a2, a3, a4, a5, a6, a7, a8, serverFilter, a10, a11, a12, a13, a14, a15, a16, a17);
|
||||
}
|
||||
|
||||
char CBaseClient__ConnectHook(void* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, int a7)
|
||||
{
|
||||
if (!g_ServerAuthenticationManager->AuthenticatePlayer((__int64)self, nextPlayerToken))
|
||||
{
|
||||
//RejectClient(nullptr, "Authentication failed", self, "Authentication failed");
|
||||
//return 0;
|
||||
}
|
||||
|
||||
return CBaseClient__Connect(self, name, netchan_ptr_arg, b_fake_player_arg, a5, Buffer, a7);
|
||||
}
|
||||
|
||||
void InitialiseServerAuthentication(HMODULE baseAddress)
|
||||
{
|
||||
g_ServerAuthenticationManager = new ServerAuthenticationManager;
|
||||
|
||||
RejectClient = (RejectClientType)((char*)baseAddress + 0x1182E0);
|
||||
|
||||
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");
|
||||
|
||||
spdlog::info((void*)CVar_ns_auth_allow_insecure);
|
||||
spdlog::info((void*)&CVar_ns_auth_allow_insecure->m_nValue);
|
||||
|
||||
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));
|
||||
|
||||
// patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token
|
||||
{
|
||||
void* ptr = (char*)baseAddress + 0x114655;
|
||||
TempReadWrite rw(ptr);
|
||||
*((char*)ptr) = (char)0xEB; // jz => jmp
|
||||
}
|
||||
}
|
||||
28
NorthstarDedicatedTest/serverauthentication.h
Normal file
28
NorthstarDedicatedTest/serverauthentication.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
struct AuthData
|
||||
{
|
||||
char* uid;
|
||||
|
||||
// pdata
|
||||
char* pdata;
|
||||
size_t pdataSize;
|
||||
};
|
||||
|
||||
class ServerAuthenticationManager
|
||||
{
|
||||
public:
|
||||
std::unordered_map<std::string, AuthData*> m_authData;
|
||||
|
||||
public:
|
||||
ServerAuthenticationManager();
|
||||
void AddPlayerAuth(char* authToken, char* uid, char* pdata, size_t pdataSize);
|
||||
bool AuthenticatePlayer(__int64 player, char* authToken);
|
||||
void WritePersistentData(void* player);
|
||||
};
|
||||
|
||||
void InitialiseServerAuthentication(HMODULE baseAddress);
|
||||
|
||||
extern ServerAuthenticationManager* g_ServerAuthenticationManager;
|
||||
|
|
@ -89,18 +89,20 @@ void InitialiseClientSquirrel(HMODULE baseAddress)
|
|||
void InitialiseServerSquirrel(HMODULE baseAddress)
|
||||
{
|
||||
g_ServerSquirrelManager = new SquirrelManager<SERVER>();
|
||||
g_ServerSquirrelManager->AddFuncRegistration("void", "SavePdataForEntityIndex", "int i", "idk", NSTestFunc);
|
||||
|
||||
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 + 0x1DDD10);
|
||||
ServerRegisterSquirrelFunc = (RegisterSquirrelFuncType)((char*)baseAddress + 0x1DD10);
|
||||
|
||||
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
|
||||
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x799E0, &ScriptCompileErrorHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerSQCompileError)); // server compileerror function
|
||||
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1D5C0, &CallScriptInitCallbackHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerCallScriptInitCallback)); // server callscriptinitcallback function
|
||||
|
||||
RegisterConCommand("script", ExecuteCodeCommand<SERVER>, "Executes script code on the server vm", FCVAR_GAMEDLL);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue