aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDedicatedTest
diff options
context:
space:
mode:
authorKawe Mazidjatari <48657826+Mauler125@users.noreply.github.com>2022-04-13 00:12:27 +0200
committerGitHub <noreply@github.com>2022-04-13 00:12:27 +0200
commit0c7fcc06f0fb072c00729c646773174b273a18b3 (patch)
tree240b3177c5eb8a9d005809c1d545fa0f3abdf6b1 /NorthstarDedicatedTest
parent9916463631b3c31501617cdce02129178908d052 (diff)
parent8d242da7981204ea61b4678741a7cfe73d337442 (diff)
downloadNorthstarLauncher-0c7fcc06f0fb072c00729c646773174b273a18b3.tar.gz
NorthstarLauncher-0c7fcc06f0fb072c00729c646773174b273a18b3.zip
Merge branch 'main' into NetCon
Diffstat (limited to 'NorthstarDedicatedTest')
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj2
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters6
-rw-r--r--NorthstarDedicatedTest/audio.cpp54
-rw-r--r--NorthstarDedicatedTest/audio.h2
-rw-r--r--NorthstarDedicatedTest/clientruihooks.cpp24
-rw-r--r--NorthstarDedicatedTest/clientruihooks.h2
-rw-r--r--NorthstarDedicatedTest/convar.h2
-rw-r--r--NorthstarDedicatedTest/dedicated.cpp7
-rw-r--r--NorthstarDedicatedTest/dllmain.cpp2
-rw-r--r--NorthstarDedicatedTest/masterserver.cpp6
-rw-r--r--NorthstarDedicatedTest/modmanager.cpp2
-rw-r--r--NorthstarDedicatedTest/modmanager.h2
-rw-r--r--NorthstarDedicatedTest/rpakfilesystem.cpp163
-rw-r--r--NorthstarDedicatedTest/rpakfilesystem.h6
-rw-r--r--NorthstarDedicatedTest/sourceconsole.cpp2
15 files changed, 245 insertions, 37 deletions
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
index 8c417112..4f7c0a9a 100644
--- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
+++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
@@ -118,6 +118,7 @@
<ClInclude Include="cl_rcon.pb.h" />
<ClInclude Include="host_state.h" />
<ClInclude Include="igameserverdata.h" />
+ <ClInclude Include="clientruihooks.h" />
<ClInclude Include="clientvideooverrides.h" />
<ClInclude Include="localchatwriter.h" />
<ClInclude Include="plugins.h" />
@@ -588,6 +589,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="clientruihooks.cpp" />
<ClCompile Include="clientvideooverrides.cpp" />
<ClCompile Include="concommand.cpp" />
<ClCompile Include="configurables.cpp" />
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
index 67137947..bf8424e3 100644
--- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
+++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
@@ -1536,6 +1536,9 @@
<ClInclude Include="version.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="clientruihooks.h">
+ <Filter>Header Files\Client</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@@ -1727,6 +1730,9 @@
<ClCompile Include="version.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="clientruihooks.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="audio_asm.asm">
diff --git a/NorthstarDedicatedTest/audio.cpp b/NorthstarDedicatedTest/audio.cpp
index d3201b89..7a2ae711 100644
--- a/NorthstarDedicatedTest/audio.cpp
+++ b/NorthstarDedicatedTest/audio.cpp
@@ -25,6 +25,11 @@ EventOverrideData::EventOverrideData()
LoadedSuccessfully = false;
}
+// Empty stereo 48000 WAVE file
+unsigned char EMPTY_WAVE[45] = {0x52, 0x49, 0x46, 0x46, 0x25, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74,
+ 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x88, 0x58,
+ 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00};
+
EventOverrideData::EventOverrideData(const std::string& data, const fs::path& path)
{
if (data.length() <= 0)
@@ -184,8 +189,10 @@ EventOverrideData::EventOverrideData(const std::string& data, const fs::path& pa
{
if (file.is_regular_file() && file.path().extension().string() == ".wav")
{
+ std::string pathString = file.path().string();
+
// Open the file.
- std::basic_ifstream<uint8_t> wavStream(file.path().string(), std::ios::binary);
+ std::basic_ifstream<uint8_t> wavStream(pathString, std::ios::binary);
if (wavStream.fail())
{
@@ -196,18 +203,40 @@ EventOverrideData::EventOverrideData(const std::string& data, const fs::path& pa
// Get file size.
wavStream.seekg(0, std::ios::end);
size_t fileSize = wavStream.tellg();
- wavStream.seekg(0, std::ios::beg);
+ wavStream.close();
// Allocate enough memory for the file.
+ // blank out the memory for now, then read it later
uint8_t* data = new uint8_t[fileSize];
-
- // Read the file.
- wavStream.read(data, fileSize);
-
+ memcpy(data, EMPTY_WAVE, sizeof(EMPTY_WAVE));
Samples.push_back({fileSize, std::unique_ptr<uint8_t[]>(data)});
- // Close the file.
- wavStream.close();
+ // thread off the file read
+ // should we spawn one thread per read? or should there be a cap to the number of reads at once?
+ std::thread readThread(
+ [pathString, fileSize, data]
+ {
+ std::shared_lock lock(g_CustomAudioManager.m_loadingMutex);
+ std::basic_ifstream<uint8_t> wavStream(pathString, std::ios::binary);
+
+ // would be weird if this got hit, since it would've worked previously
+ if (wavStream.fail())
+ {
+ spdlog::error("Failed async read of audio sample {}", pathString);
+ return;
+ }
+
+ // read from after the header first to preserve the empty header, then read the header last
+ wavStream.seekg(sizeof(EMPTY_WAVE), std::ios::beg);
+ wavStream.read(&data[sizeof(EMPTY_WAVE)], fileSize - sizeof(EMPTY_WAVE));
+ wavStream.seekg(0, std::ios::beg);
+ wavStream.read(data, sizeof(EMPTY_WAVE));
+ wavStream.close();
+
+ spdlog::info("Finished async read of audio sample {}", pathString);
+ });
+
+ readThread.detach();
}
}
@@ -289,6 +318,10 @@ void CustomAudioManager::ClearAudioOverrides()
// this is cancer but it works
Sleep(50);
}
+
+ // slightly (very) bad
+ // wait for all audio reads to complete so we don't kill preexisting audio buffers as we're writing to them
+ std::unique_lock lock(g_CustomAudioManager.m_loadingMutex);
m_loadedAudioOverrides.clear();
m_loadedAudioOverridesRegex.clear();
@@ -297,11 +330,6 @@ void CustomAudioManager::ClearAudioOverrides()
typedef bool (*LoadSampleMetadata_Type)(void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType);
LoadSampleMetadata_Type LoadSampleMetadata_Original;
-// Empty stereo 48000 WAVE file
-unsigned char EMPTY_WAVE[45] = {0x52, 0x49, 0x46, 0x46, 0x25, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74,
- 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x88, 0x58,
- 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00};
-
template <typename Iter, typename RandomGenerator> Iter select_randomly(Iter start, Iter end, RandomGenerator& g)
{
std::uniform_int_distribution<> dis(0, std::distance(start, end) - 1);
diff --git a/NorthstarDedicatedTest/audio.h b/NorthstarDedicatedTest/audio.h
index 3220d804..6ed3ce57 100644
--- a/NorthstarDedicatedTest/audio.h
+++ b/NorthstarDedicatedTest/audio.h
@@ -3,6 +3,7 @@
#include <vector>
#include <filesystem>
#include <regex>
+#include <shared_mutex>
namespace fs = std::filesystem;
@@ -39,6 +40,7 @@ class CustomAudioManager
bool TryLoadAudioOverride(const fs::path&);
void ClearAudioOverrides();
+ std::shared_mutex m_loadingMutex;
std::unordered_map<std::string, std::shared_ptr<EventOverrideData>> m_loadedAudioOverrides = {};
std::unordered_map<std::string, std::shared_ptr<EventOverrideData>> m_loadedAudioOverridesRegex = {};
};
diff --git a/NorthstarDedicatedTest/clientruihooks.cpp b/NorthstarDedicatedTest/clientruihooks.cpp
new file mode 100644
index 00000000..bc6c7aa7
--- /dev/null
+++ b/NorthstarDedicatedTest/clientruihooks.cpp
@@ -0,0 +1,24 @@
+#include "pch.h"
+#include "clientruihooks.h"
+#include "convar.h"
+
+ConVar* Cvar_rui_drawEnable;
+
+typedef char (*DrawRUIFuncType)(void* a1, float* a2);
+DrawRUIFuncType DrawRUIFunc;
+
+char DrawRUIFuncHook(void* a1, float* a2)
+{
+ if (!Cvar_rui_drawEnable->GetBool())
+ return 0;
+
+ return DrawRUIFunc(a1, a2);
+}
+
+void InitialiseEngineClientRUIHooks(HMODULE baseAddress)
+{
+ Cvar_rui_drawEnable = new ConVar("rui_drawEnable", "1", FCVAR_CLIENTDLL, "Controls whether RUI should be drawn");
+
+ HookEnabler hook;
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xFC500, &DrawRUIFuncHook, reinterpret_cast<LPVOID*>(&DrawRUIFunc));
+} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/clientruihooks.h b/NorthstarDedicatedTest/clientruihooks.h
new file mode 100644
index 00000000..967ccefe
--- /dev/null
+++ b/NorthstarDedicatedTest/clientruihooks.h
@@ -0,0 +1,2 @@
+#pragma once
+void InitialiseEngineClientRUIHooks(HMODULE baseAddress); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/convar.h b/NorthstarDedicatedTest/convar.h
index 72dc1d3c..07e7704b 100644
--- a/NorthstarDedicatedTest/convar.h
+++ b/NorthstarDedicatedTest/convar.h
@@ -129,7 +129,7 @@ class ConVar
};
ConCommandBase m_ConCommandBase{}; // 0x0000
- const char* defaultVal{}; // 0x0040
+ const char* m_pszDefaultValue{}; // 0x0040
CVValue_t m_Value{}; // 0x0048
bool m_bHasMin{}; // 0x005C
float m_fMinVal{}; // 0x0060
diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp
index 8f4ec78c..2661e918 100644
--- a/NorthstarDedicatedTest/dedicated.cpp
+++ b/NorthstarDedicatedTest/dedicated.cpp
@@ -185,13 +185,6 @@ void InitialiseDedicated(HMODULE engineAddress)
// NSMem::NOP(ea + 0x1C4E07, 5);
//}
- // not sure if this should be done, not loading ui at least is good, but should everything be gone?
- {
- // Host_Init
- // change the number of rpaks to load from 6 to 1, so we only load common.rpak
- NSMem::BytePatch(ea + 0x15653B + 1, "01");
- }
-
{
// Host_Init
// remove call to ui loading stuff
diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp
index 06878b1e..04dfe85a 100644
--- a/NorthstarDedicatedTest/dllmain.cpp
+++ b/NorthstarDedicatedTest/dllmain.cpp
@@ -42,6 +42,7 @@
#include "host_state.h"
#include "rcon_shared.h"
#include "clientvideooverrides.h"
+#include "clientruihooks.h"
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
@@ -244,6 +245,7 @@ bool InitialiseNorthstar()
AddDllLoadCallbackForClient("client.dll", InitialiseLocalChatWriter);
AddDllLoadCallbackForClient("client.dll", InitialiseScriptServerToClientStringCommands);
AddDllLoadCallbackForClient("client.dll", InitialiseClientVideoOverrides);
+ AddDllLoadCallbackForClient("engine.dll", InitialiseEngineClientRUIHooks);
// audio hooks
AddDllLoadCallbackForClient("client.dll", InitialiseMilesAudioHooks);
diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp
index 09fbb0ff..99b74d70 100644
--- a/NorthstarDedicatedTest/masterserver.cpp
+++ b/NorthstarDedicatedTest/masterserver.cpp
@@ -23,6 +23,8 @@ ConVar* Cvar_ns_server_name;
ConVar* Cvar_ns_server_desc;
ConVar* Cvar_ns_server_password;
+ConVar* Cvar_ns_curl_log_enable;
+
// Source ConVar
ConVar* Cvar_hostname;
@@ -167,7 +169,7 @@ RemoteServerInfo::RemoteServerInfo(
void MasterServerManager::SetCommonHttpClientOptions(CURL* curl)
{
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, Cvar_ns_curl_log_enable->GetBool());
curl_easy_setopt(curl, CURLOPT_USERAGENT, &NSUserAgent);
// curl_easy_setopt(curl, CURLOPT_STDERR, stdout);
if (CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work
@@ -1274,6 +1276,8 @@ void InitialiseSharedMasterServer(HMODULE baseAddress)
Cvar_ns_report_server_to_masterserver = new ConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, "");
Cvar_ns_report_sp_server_to_masterserver = new ConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, "");
+ Cvar_ns_curl_log_enable = new ConVar("ns_curl_log_enable", "0", FCVAR_NONE, "");
+
Cvar_hostname = *(ConVar**)((char*)baseAddress + 0x1315bae8);
g_MasterServerManager = new MasterServerManager;
diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp
index 0ac7e4b6..f28f87c6 100644
--- a/NorthstarDedicatedTest/modmanager.cpp
+++ b/NorthstarDedicatedTest/modmanager.cpp
@@ -380,7 +380,7 @@ void ModManager::LoadMods()
modPak.m_bAutoLoad =
!bUseRpakJson || (dRpakJson.HasMember("Preload") && dRpakJson["Preload"].IsObject() &&
dRpakJson["Preload"].HasMember(pakName) && dRpakJson["Preload"][pakName].IsTrue());
- modPak.m_sPakPath = pakName;
+ modPak.m_sPakName = pakName;
// not using atm because we need to resolve path to rpak
// if (m_hasLoadedMods && modPak.m_bAutoLoad)
diff --git a/NorthstarDedicatedTest/modmanager.h b/NorthstarDedicatedTest/modmanager.h
index 7f12559f..798c2306 100644
--- a/NorthstarDedicatedTest/modmanager.h
+++ b/NorthstarDedicatedTest/modmanager.h
@@ -56,7 +56,7 @@ struct ModRpakEntry
{
public:
bool m_bAutoLoad;
- std::string m_sPakPath;
+ std::string m_sPakName;
};
class Mod
diff --git a/NorthstarDedicatedTest/rpakfilesystem.cpp b/NorthstarDedicatedTest/rpakfilesystem.cpp
index 6853029b..b4ddcd1a 100644
--- a/NorthstarDedicatedTest/rpakfilesystem.cpp
+++ b/NorthstarDedicatedTest/rpakfilesystem.cpp
@@ -2,19 +2,26 @@
#include "rpakfilesystem.h"
#include "hookutils.h"
#include "modmanager.h"
+#include "dedicated.h"
typedef void* (*LoadCommonPaksForMapType)(char* map);
LoadCommonPaksForMapType LoadCommonPaksForMap;
typedef void* (*LoadPakSyncType)(const char* path, void* unknownSingleton, int flags);
typedef int (*LoadPakAsyncType)(const char* path, void* unknownSingleton, int flags, void* callback0, void* callback1);
+typedef void* (*UnloadPakType)(int pakHandle, void* callback);
+typedef void* (*ReadFullFileFromDiskType)(const char* requestedPath, void* a2);
// there are more i'm just too lazy to add
struct PakLoadFuncs
{
- void* unknown[2];
+ void* unk0[2];
LoadPakSyncType LoadPakSync;
LoadPakAsyncType LoadPakAsync;
+ void* unk1[2];
+ UnloadPakType UnloadPak;
+ void* unk2[17];
+ ReadFullFileFromDiskType ReadFullFileFromDisk;
};
PakLoadFuncs* g_pakLoadApi;
@@ -22,7 +29,21 @@ void** pUnknownPakLoadSingleton;
PakLoadManager* g_PakLoadManager;
void PakLoadManager::LoadPakSync(const char* path) { g_pakLoadApi->LoadPakSync(path, *pUnknownPakLoadSingleton, 0); }
-void PakLoadManager::LoadPakAsync(const char* path) { g_pakLoadApi->LoadPakAsync(path, *pUnknownPakLoadSingleton, 2, nullptr, nullptr); }
+void PakLoadManager::LoadPakAsync(const char* path, bool bMarkForUnload)
+{
+ int handle = g_pakLoadApi->LoadPakAsync(path, *pUnknownPakLoadSingleton, 2, nullptr, nullptr);
+
+ if (bMarkForUnload)
+ m_pakHandlesToUnload.push_back(handle);
+}
+
+void PakLoadManager::UnloadPaks()
+{
+ for (int pakHandle : m_pakHandlesToUnload)
+ g_pakLoadApi->UnloadPak(pakHandle, nullptr);
+
+ m_pakHandlesToUnload.clear();
+}
void HandlePakAliases(char** map)
{
@@ -41,12 +62,8 @@ void HandlePakAliases(char** map)
}
}
-bool bShouldPreload = true;
void LoadPreloadPaks()
{
- // disable preloading while we're doing this
- bShouldPreload = false;
-
// note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
for (Mod& mod : g_ModManager->m_loadedMods)
{
@@ -58,22 +75,75 @@ void LoadPreloadPaks()
for (ModRpakEntry& pak : mod.Rpaks)
if (pak.m_bAutoLoad)
- g_PakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakPath).string().c_str());
+ g_PakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), false);
}
+}
+
+void LoadCustomMapPaks(char** pakName, bool* bNeedToFreePakName)
+{
+ // whether the vanilla game has this rpak
+ bool bHasOriginalPak = fs::exists(fs::path("./r2/paks/Win64/") / *pakName);
+
+ // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
+ for (Mod& mod : g_ModManager->m_loadedMods)
+ {
+ if (!mod.Enabled)
+ continue;
+
+ // need to get a relative path of mod to mod folder
+ fs::path modPakPath("./" / mod.ModDirectory / "paks");
- bShouldPreload = true;
+ for (ModRpakEntry& pak : mod.Rpaks)
+ {
+ if (!pak.m_bAutoLoad && !pak.m_sPakName.compare(*pakName))
+ {
+ // if the game doesn't have the original pak, let it handle loading this one as if it was the one it was loading originally
+ if (!bHasOriginalPak)
+ {
+ std::string path = (modPakPath / pak.m_sPakName).string();
+ *pakName = new char[path.size() + 1];
+ strcpy(*pakName, &path[0]);
+ (*pakName)[path.size()] = '\0';
+
+ bHasOriginalPak = true;
+ *bNeedToFreePakName =
+ true; // we can't free this memory until we're done with the pak, so let whatever's calling this deal with it
+ }
+ else
+ g_PakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), true);
+ }
+ }
+ }
}
LoadPakSyncType LoadPakSyncOriginal;
void* LoadPakSyncHook(char* path, void* unknownSingleton, int flags)
{
HandlePakAliases(&path);
+
+ bool bNeedToFreePakName = false;
+
// note: we don't handle loading any preloaded custom paks synchronously since LoadPakSync is never actually called in retail, just load
// them async instead
- LoadPreloadPaks();
+ static bool bShouldLoadPaks = true;
+ if (bShouldLoadPaks)
+ {
+ // disable preloading while we're doing this
+ bShouldLoadPaks = false;
+
+ LoadPreloadPaks();
+ LoadCustomMapPaks(&path, &bNeedToFreePakName);
+
+ bShouldLoadPaks = true;
+ }
spdlog::info("LoadPakSync {}", path);
- return LoadPakSyncOriginal(path, unknownSingleton, flags);
+ void* ret = LoadPakSyncOriginal(path, unknownSingleton, flags);
+
+ if (bNeedToFreePakName)
+ delete[] path;
+
+ return ret;
}
LoadPakAsyncType LoadPakAsyncOriginal;
@@ -81,11 +151,79 @@ int LoadPakAsyncHook(char* path, void* unknownSingleton, int flags, void* callba
{
HandlePakAliases(&path);
- if (bShouldPreload)
+ bool bNeedToFreePakName = false;
+
+ static bool bShouldLoadPaks = true;
+ if (bShouldLoadPaks)
+ {
+ // disable preloading while we're doing this
+ bShouldLoadPaks = false;
+
LoadPreloadPaks();
+ LoadCustomMapPaks(&path, &bNeedToFreePakName);
+
+ bShouldLoadPaks = true;
+
+ // do this after custom paks load and in bShouldLoadPaks so we only ever call this on the root pakload call
+ // todo: could probably add some way to flag custom paks to not be loaded on dedicated servers in rpak.json
+ if (IsDedicated() && strncmp(path, "common", 6)) // dedicated only needs common and common_mp
+ return -1;
+ }
int ret = LoadPakAsyncOriginal(path, unknownSingleton, flags, callback0, callback1);
spdlog::info("LoadPakAsync {} {}", path, ret);
+
+ if (bNeedToFreePakName)
+ delete[] path;
+
+ return ret;
+}
+
+UnloadPakType UnloadPakOriginal;
+void* UnloadPakHook(int pakHandle, void* callback)
+{
+ static bool bShouldUnloadPaks = true;
+ if (bShouldUnloadPaks)
+ {
+ bShouldUnloadPaks = false;
+ g_PakLoadManager->UnloadPaks();
+ bShouldUnloadPaks = true;
+ }
+
+ spdlog::info("UnloadPak {}", pakHandle);
+ return UnloadPakOriginal(pakHandle, callback);
+}
+
+// we hook this exclusively for resolving stbsp paths, but seemingly it's also used for other stuff like vpk and rpak loads
+// possibly just async loading all together?
+ReadFullFileFromDiskType ReadFullFileFromDiskOriginal;
+void* ReadFullFileFromDiskHook(const char* requestedPath, void* a2)
+{
+ fs::path path(requestedPath);
+ char* allocatedNewPath = nullptr;
+
+ if (path.extension() == ".stbsp")
+ {
+ fs::path filename = path.filename();
+ spdlog::info("LoadStreamBsp: {}", filename.string());
+
+ // resolve modded stbsp path so we can load mod stbsps
+ auto modFile = g_ModManager->m_modFiles.find(fs::path("maps" / filename).lexically_normal().string());
+ if (modFile != g_ModManager->m_modFiles.end())
+ {
+ // need to allocate a new string for this
+ std::string newPath = (modFile->second.owningMod->ModDirectory / "mod" / modFile->second.path).string();
+ allocatedNewPath = new char[newPath.size() + 1];
+ strncpy(allocatedNewPath, newPath.c_str(), newPath.size());
+ allocatedNewPath[newPath.size()] = '\0';
+ requestedPath = allocatedNewPath;
+ }
+ }
+
+ void* ret = ReadFullFileFromDiskOriginal(requestedPath, a2);
+ if (allocatedNewPath)
+ delete[] allocatedNewPath;
+
return ret;
}
@@ -99,4 +237,7 @@ void InitialiseEngineRpakFilesystem(HMODULE baseAddress)
HookEnabler hook;
ENABLER_CREATEHOOK(hook, g_pakLoadApi->LoadPakSync, &LoadPakSyncHook, reinterpret_cast<LPVOID*>(&LoadPakSyncOriginal));
ENABLER_CREATEHOOK(hook, g_pakLoadApi->LoadPakAsync, &LoadPakAsyncHook, reinterpret_cast<LPVOID*>(&LoadPakAsyncOriginal));
+ ENABLER_CREATEHOOK(hook, g_pakLoadApi->UnloadPak, &UnloadPakHook, reinterpret_cast<LPVOID*>(&UnloadPakOriginal));
+ ENABLER_CREATEHOOK(
+ hook, g_pakLoadApi->ReadFullFileFromDisk, &ReadFullFileFromDiskHook, reinterpret_cast<LPVOID*>(&ReadFullFileFromDiskOriginal));
} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/rpakfilesystem.h b/NorthstarDedicatedTest/rpakfilesystem.h
index 334104a9..3c104822 100644
--- a/NorthstarDedicatedTest/rpakfilesystem.h
+++ b/NorthstarDedicatedTest/rpakfilesystem.h
@@ -6,7 +6,11 @@ class PakLoadManager
{
public:
void LoadPakSync(const char* path);
- void LoadPakAsync(const char* path);
+ void LoadPakAsync(const char* path, bool bMarkForUnload);
+ void UnloadPaks();
+
+ private:
+ std::vector<int> m_pakHandlesToUnload;
};
extern PakLoadManager* g_PakLoadManager; \ No newline at end of file
diff --git a/NorthstarDedicatedTest/sourceconsole.cpp b/NorthstarDedicatedTest/sourceconsole.cpp
index 1e2e2f20..7bc68598 100644
--- a/NorthstarDedicatedTest/sourceconsole.cpp
+++ b/NorthstarDedicatedTest/sourceconsole.cpp
@@ -48,7 +48,7 @@ void InitialiseConsoleOnInterfaceCreation()
void InitialiseSourceConsole(HMODULE baseAddress)
{
g_SourceGameConsole = new SourceInterface<CGameConsole>("client.dll", "GameConsole004");
- RegisterConCommand("toggleconsole", ConCommand_toggleconsole, "toggles the console", FCVAR_NONE);
+ RegisterConCommand("toggleconsole", ConCommand_toggleconsole, "toggles the console", FCVAR_DONTRECORD);
}
// logging stuff