aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL
diff options
context:
space:
mode:
authorJack <66967891+ASpoonPlaysGames@users.noreply.github.com>2023-12-27 00:32:01 +0000
committerGitHub <noreply@github.com>2023-12-27 01:32:01 +0100
commitf5ab6fb5e8be7b73e6003d4145081d5e0c0ce287 (patch)
tree90f2c6a4885dbd181799e2325cf33588697674e1 /NorthstarDLL
parentbb8ed59f6891b1196c5f5bbe7346cd171c8215fa (diff)
downloadNorthstarLauncher-f5ab6fb5e8be7b73e6003d4145081d5e0c0ce287.tar.gz
NorthstarLauncher-f5ab6fb5e8be7b73e6003d4145081d5e0c0ce287.zip
Folder restructuring from primedev (#624)v1.21.2-rc3v1.21.2
Copies of over the primedev folder structure for easier cherry-picking of further changes Co-authored-by: F1F7Y <filip.bartos07@proton.me>
Diffstat (limited to 'NorthstarDLL')
-rw-r--r--NorthstarDLL/CMakeLists.txt187
-rw-r--r--NorthstarDLL/client/audio.cpp504
-rw-r--r--NorthstarDLL/client/audio.h46
-rw-r--r--NorthstarDLL/client/chatcommand.cpp36
-rw-r--r--NorthstarDLL/client/clientauthhooks.cpp72
-rw-r--r--NorthstarDLL/client/clientruihooks.cpp23
-rw-r--r--NorthstarDLL/client/clientvideooverrides.cpp41
-rw-r--r--NorthstarDLL/client/debugoverlay.cpp348
-rw-r--r--NorthstarDLL/client/demofixes.cpp25
-rw-r--r--NorthstarDLL/client/diskvmtfixes.cpp15
-rw-r--r--NorthstarDLL/client/languagehooks.cpp115
-rw-r--r--NorthstarDLL/client/latencyflex.cpp43
-rw-r--r--NorthstarDLL/client/localchatwriter.cpp449
-rw-r--r--NorthstarDLL/client/localchatwriter.h64
-rw-r--r--NorthstarDLL/client/modlocalisation.cpp55
-rw-r--r--NorthstarDLL/client/r2client.cpp13
-rw-r--r--NorthstarDLL/client/r2client.h7
-rw-r--r--NorthstarDLL/client/rejectconnectionfixes.cpp34
-rw-r--r--NorthstarDLL/config/profile.cpp40
-rw-r--r--NorthstarDLL/config/profile.h7
-rw-r--r--NorthstarDLL/core/convar/concommand.cpp157
-rw-r--r--NorthstarDLL/core/convar/concommand.h139
-rw-r--r--NorthstarDLL/core/convar/convar.cpp534
-rw-r--r--NorthstarDLL/core/convar/convar.h194
-rw-r--r--NorthstarDLL/core/convar/cvar.cpp26
-rw-r--r--NorthstarDLL/core/convar/cvar.h38
-rw-r--r--NorthstarDLL/core/filesystem/filesystem.cpp177
-rw-r--r--NorthstarDLL/core/filesystem/filesystem.h69
-rw-r--r--NorthstarDLL/core/filesystem/rpakfilesystem.cpp347
-rw-r--r--NorthstarDLL/core/filesystem/rpakfilesystem.h39
-rw-r--r--NorthstarDLL/core/hooks.cpp474
-rw-r--r--NorthstarDLL/core/hooks.h331
-rw-r--r--NorthstarDLL/core/macros.h19
-rw-r--r--NorthstarDLL/core/math/bitbuf.h1148
-rw-r--r--NorthstarDLL/core/math/bits.cpp45
-rw-r--r--NorthstarDLL/core/math/bits.h10
-rw-r--r--NorthstarDLL/core/math/color.cpp27
-rw-r--r--NorthstarDLL/core/math/color.h199
-rw-r--r--NorthstarDLL/core/math/vector.h47
-rw-r--r--NorthstarDLL/core/memalloc.cpp71
-rw-r--r--NorthstarDLL/core/memalloc.h49
-rw-r--r--NorthstarDLL/core/memory.cpp347
-rw-r--r--NorthstarDLL/core/memory.h90
-rw-r--r--NorthstarDLL/core/sourceinterface.cpp48
-rw-r--r--NorthstarDLL/core/sourceinterface.h31
-rw-r--r--NorthstarDLL/core/structs.h77
-rw-r--r--NorthstarDLL/core/tier0.cpp30
-rw-r--r--NorthstarDLL/core/tier0.h63
-rw-r--r--NorthstarDLL/core/vanilla.h29
-rw-r--r--NorthstarDLL/dedicated/dedicated.cpp296
-rw-r--r--NorthstarDLL/dedicated/dedicated.h3
-rw-r--r--NorthstarDLL/dedicated/dedicatedlogtoclient.cpp48
-rw-r--r--NorthstarDLL/dedicated/dedicatedlogtoclient.h11
-rw-r--r--NorthstarDLL/dedicated/dedicatedmaterialsystem.cpp40
-rw-r--r--NorthstarDLL/dllmain.cpp83
-rw-r--r--NorthstarDLL/dllmain.h3
-rw-r--r--NorthstarDLL/engine/host.cpp33
-rw-r--r--NorthstarDLL/engine/hoststate.cpp191
-rw-r--r--NorthstarDLL/engine/hoststate.h41
-rw-r--r--NorthstarDLL/engine/r2engine.cpp37
-rw-r--r--NorthstarDLL/engine/r2engine.h260
-rw-r--r--NorthstarDLL/engine/runframe.cpp19
-rw-r--r--NorthstarDLL/logging/crashhandler.cpp590
-rw-r--r--NorthstarDLL/logging/crashhandler.h97
-rw-r--r--NorthstarDLL/logging/logging.cpp302
-rw-r--r--NorthstarDLL/logging/logging.h136
-rw-r--r--NorthstarDLL/logging/loghooks.cpp261
-rw-r--r--NorthstarDLL/logging/loghooks.h1
-rw-r--r--NorthstarDLL/logging/sourceconsole.cpp91
-rw-r--r--NorthstarDLL/logging/sourceconsole.h85
-rw-r--r--NorthstarDLL/masterserver/masterserver.cpp1459
-rw-r--r--NorthstarDLL/masterserver/masterserver.h199
-rw-r--r--NorthstarDLL/mods/autodownload/moddownloader.cpp638
-rw-r--r--NorthstarDLL/mods/autodownload/moddownloader.h151
-rw-r--r--NorthstarDLL/mods/compiled/kb_act.cpp44
-rw-r--r--NorthstarDLL/mods/compiled/modkeyvalues.cpp106
-rw-r--r--NorthstarDLL/mods/compiled/modpdef.cpp118
-rw-r--r--NorthstarDLL/mods/compiled/modscriptsrson.cpp65
-rw-r--r--NorthstarDLL/mods/modmanager.cpp1150
-rw-r--r--NorthstarDLL/mods/modmanager.h187
-rw-r--r--NorthstarDLL/mods/modsavefiles.cpp572
-rw-r--r--NorthstarDLL/mods/modsavefiles.h16
-rw-r--r--NorthstarDLL/ns_version.h7
-rw-r--r--NorthstarDLL/pch.h44
-rw-r--r--NorthstarDLL/plugins/plugin_abi.h151
-rw-r--r--NorthstarDLL/plugins/pluginbackend.cpp50
-rw-r--r--NorthstarDLL/plugins/pluginbackend.h40
-rw-r--r--NorthstarDLL/plugins/plugins.cpp340
-rw-r--r--NorthstarDLL/plugins/plugins.h59
-rw-r--r--NorthstarDLL/resource1.h16
-rw-r--r--NorthstarDLL/resources.rc79
-rw-r--r--NorthstarDLL/scripts/client/clientchathooks.cpp72
-rw-r--r--NorthstarDLL/scripts/client/cursorposition.cpp22
-rw-r--r--NorthstarDLL/scripts/client/scriptbrowserhooks.cpp24
-rw-r--r--NorthstarDLL/scripts/client/scriptmainmenupromos.cpp123
-rw-r--r--NorthstarDLL/scripts/client/scriptmodmenu.cpp165
-rw-r--r--NorthstarDLL/scripts/client/scriptoriginauth.cpp35
-rw-r--r--NorthstarDLL/scripts/client/scriptserverbrowser.cpp209
-rw-r--r--NorthstarDLL/scripts/client/scriptservertoclientstringcommand.cpp18
-rw-r--r--NorthstarDLL/scripts/scriptdatatables.cpp909
-rw-r--r--NorthstarDLL/scripts/scripthttprequesthandler.cpp585
-rw-r--r--NorthstarDLL/scripts/scripthttprequesthandler.h130
-rw-r--r--NorthstarDLL/scripts/scriptjson.cpp250
-rw-r--r--NorthstarDLL/scripts/scriptjson.h13
-rw-r--r--NorthstarDLL/scripts/scriptutility.cpp28
-rw-r--r--NorthstarDLL/scripts/server/miscserverfixes.cpp6
-rw-r--r--NorthstarDLL/scripts/server/miscserverscript.cpp100
-rw-r--r--NorthstarDLL/scripts/server/scriptuserinfo.cpp104
-rw-r--r--NorthstarDLL/server/alltalk.cpp28
-rw-r--r--NorthstarDLL/server/auth/bansystem.cpp224
-rw-r--r--NorthstarDLL/server/auth/bansystem.h19
-rw-r--r--NorthstarDLL/server/auth/serverauthentication.cpp380
-rw-r--r--NorthstarDLL/server/auth/serverauthentication.h58
-rw-r--r--NorthstarDLL/server/buildainfile.cpp395
-rw-r--r--NorthstarDLL/server/r2server.cpp10
-rw-r--r--NorthstarDLL/server/r2server.h106
-rw-r--r--NorthstarDLL/server/serverchathooks.cpp174
-rw-r--r--NorthstarDLL/server/serverchathooks.h24
-rw-r--r--NorthstarDLL/server/servernethooks.cpp218
-rw-r--r--NorthstarDLL/server/serverpresence.cpp228
-rw-r--r--NorthstarDLL/server/serverpresence.h94
-rw-r--r--NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp461
-rw-r--r--NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp78
-rw-r--r--NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp199
-rw-r--r--NorthstarDLL/shared/exploit_fixes/ns_limits.cpp298
-rw-r--r--NorthstarDLL/shared/exploit_fixes/ns_limits.h52
-rw-r--r--NorthstarDLL/shared/keyvalues.cpp1322
-rw-r--r--NorthstarDLL/shared/keyvalues.h134
-rw-r--r--NorthstarDLL/shared/maxplayers.cpp640
-rw-r--r--NorthstarDLL/shared/maxplayers.h3
-rw-r--r--NorthstarDLL/shared/misccommands.cpp391
-rw-r--r--NorthstarDLL/shared/misccommands.h3
-rw-r--r--NorthstarDLL/shared/playlist.cpp125
-rw-r--r--NorthstarDLL/shared/playlist.h10
-rw-r--r--NorthstarDLL/squirrel/squirrel.cpp943
-rw-r--r--NorthstarDLL/squirrel/squirrel.h526
-rw-r--r--NorthstarDLL/squirrel/squirrelautobind.cpp20
-rw-r--r--NorthstarDLL/squirrel/squirrelautobind.h76
-rw-r--r--NorthstarDLL/squirrel/squirrelclasstypes.h248
-rw-r--r--NorthstarDLL/squirrel/squirreldatatypes.h501
-rw-r--r--NorthstarDLL/util/printcommands.cpp285
-rw-r--r--NorthstarDLL/util/printcommands.h6
-rw-r--r--NorthstarDLL/util/printmaps.cpp233
-rw-r--r--NorthstarDLL/util/printmaps.h2
-rw-r--r--NorthstarDLL/util/utils.cpp82
-rw-r--r--NorthstarDLL/util/utils.h3
-rw-r--r--NorthstarDLL/util/version.cpp95
-rw-r--r--NorthstarDLL/util/version.h6
-rw-r--r--NorthstarDLL/util/wininfo.cpp9
-rw-r--r--NorthstarDLL/util/wininfo.h4
150 files changed, 0 insertions, 26604 deletions
diff --git a/NorthstarDLL/CMakeLists.txt b/NorthstarDLL/CMakeLists.txt
deleted file mode 100644
index d238f61f..00000000
--- a/NorthstarDLL/CMakeLists.txt
+++ /dev/null
@@ -1,187 +0,0 @@
-# NorthstarDLL
-
-find_package(minhook REQUIRED)
-find_package(libcurl REQUIRED)
-find_package(minizip REQUIRED)
-
-add_library(NorthstarDLL SHARED
- "resources.rc"
- "client/audio.cpp"
- "client/audio.h"
- "client/chatcommand.cpp"
- "client/clientauthhooks.cpp"
- "client/clientruihooks.cpp"
- "client/clientvideooverrides.cpp"
- "client/debugoverlay.cpp"
- "client/demofixes.cpp"
- "client/diskvmtfixes.cpp"
- "client/languagehooks.cpp"
- "client/latencyflex.cpp"
- "client/localchatwriter.cpp"
- "client/localchatwriter.h"
- "client/modlocalisation.cpp"
- "client/r2client.cpp"
- "client/r2client.h"
- "client/rejectconnectionfixes.cpp"
- "config/profile.cpp"
- "config/profile.h"
- "core/convar/concommand.cpp"
- "core/convar/concommand.h"
- "core/convar/convar.cpp"
- "core/convar/convar.h"
- "core/convar/cvar.cpp"
- "core/convar/cvar.h"
- "core/filesystem/filesystem.cpp"
- "core/filesystem/filesystem.h"
- "core/filesystem/rpakfilesystem.cpp"
- "core/filesystem/rpakfilesystem.h"
- "core/math/bitbuf.h"
- "core/math/bits.cpp"
- "core/math/bits.h"
- "core/math/color.cpp"
- "core/math/color.h"
- "core/math/vector.h"
- "core/hooks.cpp"
- "core/hooks.h"
- "core/macros.h"
- "core/memalloc.cpp"
- "core/memalloc.h"
- "core/memory.cpp"
- "core/memory.h"
- "core/sourceinterface.cpp"
- "core/sourceinterface.h"
- "core/structs.h"
- "core/tier0.cpp"
- "core/tier0.h"
- "dedicated/dedicated.cpp"
- "dedicated/dedicated.h"
- "dedicated/dedicatedlogtoclient.cpp"
- "dedicated/dedicatedlogtoclient.h"
- "dedicated/dedicatedmaterialsystem.cpp"
- "engine/host.cpp"
- "engine/hoststate.cpp"
- "engine/hoststate.h"
- "engine/r2engine.cpp"
- "engine/r2engine.h"
- "engine/runframe.cpp"
- "logging/crashhandler.cpp"
- "logging/crashhandler.h"
- "logging/logging.cpp"
- "logging/logging.h"
- "logging/loghooks.cpp"
- "logging/loghooks.h"
- "logging/sourceconsole.cpp"
- "logging/sourceconsole.h"
- "masterserver/masterserver.cpp"
- "masterserver/masterserver.h"
- "mods/autodownload/moddownloader.h"
- "mods/autodownload/moddownloader.cpp"
- "mods/compiled/kb_act.cpp"
- "mods/compiled/modkeyvalues.cpp"
- "mods/compiled/modpdef.cpp"
- "mods/compiled/modscriptsrson.cpp"
- "mods/modmanager.cpp"
- "mods/modmanager.h"
- "mods/modsavefiles.cpp"
- "mods/modsavefiles.h"
- "plugins/plugin_abi.h"
- "plugins/pluginbackend.cpp"
- "plugins/pluginbackend.h"
- "plugins/plugins.cpp"
- "plugins/plugins.h"
- "scripts/client/clientchathooks.cpp"
- "scripts/client/cursorposition.cpp"
- "scripts/client/scriptbrowserhooks.cpp"
- "scripts/client/scriptmainmenupromos.cpp"
- "scripts/client/scriptmodmenu.cpp"
- "scripts/client/scriptoriginauth.cpp"
- "scripts/client/scriptserverbrowser.cpp"
- "scripts/client/scriptservertoclientstringcommand.cpp"
- "scripts/server/miscserverfixes.cpp"
- "scripts/server/miscserverscript.cpp"
- "scripts/server/scriptuserinfo.cpp"
- "scripts/scriptdatatables.cpp"
- "scripts/scripthttprequesthandler.cpp"
- "scripts/scripthttprequesthandler.h"
- "scripts/scriptjson.cpp"
- "scripts/scriptjson.h"
- "scripts/scriptutility.cpp"
- "server/auth/bansystem.cpp"
- "server/auth/bansystem.h"
- "server/auth/serverauthentication.cpp"
- "server/auth/serverauthentication.h"
- "server/alltalk.cpp"
- "server/buildainfile.cpp"
- "server/r2server.cpp"
- "server/r2server.h"
- "server/serverchathooks.cpp"
- "server/serverchathooks.h"
- "server/servernethooks.cpp"
- "server/serverpresence.cpp"
- "server/serverpresence.h"
- "shared/exploit_fixes/exploitfixes.cpp"
- "shared/exploit_fixes/exploitfixes_lzss.cpp"
- "shared/exploit_fixes/exploitfixes_utf8parser.cpp"
- "shared/exploit_fixes/ns_limits.cpp"
- "shared/exploit_fixes/ns_limits.h"
- "shared/keyvalues.cpp"
- "shared/keyvalues.h"
- "shared/maxplayers.cpp"
- "shared/maxplayers.h"
- "shared/misccommands.cpp"
- "shared/misccommands.h"
- "shared/playlist.cpp"
- "shared/playlist.h"
- "squirrel/squirrel.cpp"
- "squirrel/squirrel.h"
- "squirrel/squirrelautobind.cpp"
- "squirrel/squirrelautobind.h"
- "squirrel/squirrelclasstypes.h"
- "squirrel/squirreldatatypes.h"
- "util/printcommands.cpp"
- "util/printcommands.h"
- "util/printmaps.cpp"
- "util/printmaps.h"
- "util/utils.cpp"
- "util/utils.h"
- "util/version.cpp"
- "util/version.h"
- "util/wininfo.cpp"
- "util/wininfo.h"
- "dllmain.cpp"
- "dllmain.h"
- "ns_version.h"
-)
-
-target_link_libraries(NorthstarDLL PRIVATE
- minhook
- libcurl
- minizip
- WS2_32.lib
- Crypt32.lib
- Cryptui.lib
- dbghelp.lib
- Wldap32.lib
- Normaliz.lib
- Bcrypt.lib
- version.lib
-)
-
-target_include_directories(NorthstarDLL PRIVATE
- ${CMAKE_SOURCE_DIR}/NorthstarDLL
- ${CMAKE_SOURCE_DIR}/thirdparty
-)
-
-target_precompile_headers(NorthstarDLL PRIVATE pch.h)
-
-target_compile_definitions(NorthstarDLL PRIVATE
- UNICODE
- _UNICODE
- CURL_STATICLIB
-)
-
-set_target_properties(NorthstarDLL PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR}
- OUTPUT_NAME Northstar
- LINK_FLAGS "/MANIFEST:NO /DEBUG"
-)
diff --git a/NorthstarDLL/client/audio.cpp b/NorthstarDLL/client/audio.cpp
deleted file mode 100644
index aa32e390..00000000
--- a/NorthstarDLL/client/audio.cpp
+++ /dev/null
@@ -1,504 +0,0 @@
-#include "audio.h"
-#include "dedicated/dedicated.h"
-#include "core/convar/convar.h"
-
-#include "rapidjson/error/en.h"
-#include <fstream>
-#include <iostream>
-#include <sstream>
-#include <random>
-
-AUTOHOOK_INIT()
-
-static const char* pszAudioEventName;
-
-ConVar* Cvar_mileslog_enable;
-ConVar* Cvar_ns_print_played_sounds;
-
-CustomAudioManager g_CustomAudioManager;
-
-EventOverrideData::EventOverrideData()
-{
- spdlog::warn("Initialised struct EventOverrideData without any data!");
- 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)
- {
- spdlog::error("Failed reading audio override file {}: file is empty", path.string());
- return;
- }
-
- fs::path samplesFolder = path;
- samplesFolder = samplesFolder.replace_extension();
-
- if (!fs::exists(samplesFolder))
- {
- spdlog::error(
- "Failed reading audio override file {}: samples folder doesn't exist; should be named the same as the definition file without "
- "JSON extension.",
- path.string());
- return;
- }
-
- rapidjson_document dataJson;
- dataJson.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(data);
-
- // fail if parse error
- if (dataJson.HasParseError())
- {
- spdlog::error(
- "Failed reading audio override file {}: encountered parse error \"{}\" at offset {}",
- path.string(),
- GetParseError_En(dataJson.GetParseError()),
- dataJson.GetErrorOffset());
- return;
- }
-
- // fail if it's not a json obj (could be an array, string, etc)
- if (!dataJson.IsObject())
- {
- spdlog::error("Failed reading audio override file {}: file is not a JSON object", path.string());
- return;
- }
-
- // fail if no event ids given
- if (!dataJson.HasMember("EventId"))
- {
- spdlog::error("Failed reading audio override file {}: JSON object does not have the EventId property", path.string());
- return;
- }
-
- // array of event ids
- if (dataJson["EventId"].IsArray())
- {
- for (auto& eventId : dataJson["EventId"].GetArray())
- {
- if (!eventId.IsString())
- {
- spdlog::error(
- "Failed reading audio override file {}: EventId array has a value of invalid type, all must be strings", path.string());
- return;
- }
-
- EventIds.push_back(eventId.GetString());
- }
- }
- // singular event id
- else if (dataJson["EventId"].IsString())
- {
- EventIds.push_back(dataJson["EventId"].GetString());
- }
- // incorrect type
- else
- {
- spdlog::error(
- "Failed reading audio override file {}: EventId property is of invalid type (must be a string or an array of strings)",
- path.string());
- return;
- }
-
- if (dataJson.HasMember("EventIdRegex"))
- {
- // array of event id regex
- if (dataJson["EventIdRegex"].IsArray())
- {
- for (auto& eventId : dataJson["EventIdRegex"].GetArray())
- {
- if (!eventId.IsString())
- {
- spdlog::error(
- "Failed reading audio override file {}: EventIdRegex array has a value of invalid type, all must be strings",
- path.string());
- return;
- }
-
- const std::string& regex = eventId.GetString();
-
- try
- {
- EventIdsRegex.push_back({regex, std::regex(regex)});
- }
- catch (...)
- {
- spdlog::error("Malformed regex \"{}\" in audio override file {}", regex, path.string());
- return;
- }
- }
- }
- // singular event id regex
- else if (dataJson["EventIdRegex"].IsString())
- {
- const std::string& regex = dataJson["EventIdRegex"].GetString();
- try
- {
- EventIdsRegex.push_back({regex, std::regex(regex)});
- }
- catch (...)
- {
- spdlog::error("Malformed regex \"{}\" in audio override file {}", regex, path.string());
- return;
- }
- }
- // incorrect type
- else
- {
- spdlog::error(
- "Failed reading audio override file {}: EventIdRegex property is of invalid type (must be a string or an array of strings)",
- path.string());
- return;
- }
- }
-
- if (dataJson.HasMember("AudioSelectionStrategy"))
- {
- if (!dataJson["AudioSelectionStrategy"].IsString())
- {
- spdlog::error("Failed reading audio override file {}: AudioSelectionStrategy property must be a string", path.string());
- return;
- }
-
- std::string strategy = dataJson["AudioSelectionStrategy"].GetString();
-
- if (strategy == "sequential")
- {
- Strategy = AudioSelectionStrategy::SEQUENTIAL;
- }
- else if (strategy == "random")
- {
- Strategy = AudioSelectionStrategy::RANDOM;
- }
- else
- {
- spdlog::error(
- "Failed reading audio override file {}: AudioSelectionStrategy string must be either \"sequential\" or \"random\"",
- path.string());
- return;
- }
- }
-
- // load samples
- for (fs::directory_entry file : fs::recursive_directory_iterator(samplesFolder))
- {
- if (file.is_regular_file() && file.path().extension().string() == ".wav")
- {
- std::string pathString = file.path().string();
-
- // Open the file.
- std::ifstream wavStream(pathString, std::ios::binary);
-
- if (wavStream.fail())
- {
- spdlog::error("Failed reading audio sample {}", file.path().string());
- continue;
- }
-
- // Get file size.
- wavStream.seekg(0, std::ios::end);
- size_t fileSize = wavStream.tellg();
- 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];
- memcpy(data, EMPTY_WAVE, sizeof(EMPTY_WAVE));
- Samples.push_back({fileSize, std::unique_ptr<uint8_t[]>(data)});
-
- // 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::ifstream 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(0, std::ios::beg);
- wavStream.read(reinterpret_cast<char*>(data), fileSize);
- wavStream.close();
-
- spdlog::info("Finished async read of audio sample {}", pathString);
- });
-
- readThread.detach();
- }
- }
-
- /*
- if (dataJson.HasMember("EnableOnLoopedSounds"))
- {
- if (!dataJson["EnableOnLoopedSounds"].IsBool())
- {
- spdlog::error("Failed reading audio override file {}: EnableOnLoopedSounds property is of invalid type (must be a bool)",
- path.string()); return;
- }
-
- EnableOnLoopedSounds = dataJson["EnableOnLoopedSounds"].GetBool();
- }
- */
-
- if (Samples.size() == 0)
- spdlog::warn("Audio override {} has no valid samples! Sounds will not play for this event.", path.string());
-
- spdlog::info("Loaded audio override file {}", path.string());
-
- LoadedSuccessfully = true;
-}
-
-bool CustomAudioManager::TryLoadAudioOverride(const fs::path& defPath)
-{
- if (IsDedicatedServer())
- return true; // silently fail
-
- std::ifstream jsonStream(defPath);
- std::stringstream jsonStringStream;
-
- // fail if no audio json
- if (jsonStream.fail())
- {
- spdlog::warn("Unable to read audio override from file {}", defPath.string());
- return false;
- }
-
- while (jsonStream.peek() != EOF)
- jsonStringStream << (char)jsonStream.get();
-
- jsonStream.close();
-
- std::shared_ptr<EventOverrideData> data = std::make_shared<EventOverrideData>(jsonStringStream.str(), defPath);
-
- if (!data->LoadedSuccessfully)
- return false; // no logging, the constructor has probably already logged
-
- for (const std::string& eventId : data->EventIds)
- {
- spdlog::info("Registering sound event {}", eventId);
- m_loadedAudioOverrides.insert({eventId, data});
- }
-
- for (const auto& eventIdRegexData : data->EventIdsRegex)
- {
- spdlog::info("Registering sound event regex {}", eventIdRegexData.first);
- m_loadedAudioOverridesRegex.insert({eventIdRegexData.first, data});
- }
-
- return true;
-}
-
-typedef void (*MilesStopAll_Type)();
-MilesStopAll_Type MilesStopAll;
-
-void CustomAudioManager::ClearAudioOverrides()
-{
- if (IsDedicatedServer())
- return;
-
- if (m_loadedAudioOverrides.size() > 0 || m_loadedAudioOverridesRegex.size() > 0)
- {
- // stop all miles sounds beforehand
- // miles_stop_all
-
- MilesStopAll();
-
- // 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(m_loadingMutex);
-
- m_loadedAudioOverrides.clear();
- m_loadedAudioOverridesRegex.clear();
-}
-
-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);
- std::advance(start, dis(g));
- return start;
-}
-
-template <typename Iter> Iter select_randomly(Iter start, Iter end)
-{
- static std::random_device rd;
- static std::mt19937 gen(rd());
- return select_randomly(start, end, gen);
-}
-
-bool ShouldPlayAudioEvent(const char* eventName, const std::shared_ptr<EventOverrideData>& data)
-{
- std::string eventNameString = eventName;
- std::string eventNameStringBlacklistEntry = ("!" + eventNameString);
-
- for (const std::string& name : data->EventIds)
- {
- if (name == eventNameStringBlacklistEntry)
- return false; // event blacklisted
-
- if (name == "*")
- {
- // check for bad sounds I guess?
- // really feel like this should be an option but whatever
- if (!!strstr(eventName, "_amb_") || !!strstr(eventName, "_emit_") || !!strstr(eventName, "amb_"))
- return false; // would play static noise, I hate this
- }
- }
-
- return true; // good to go
-}
-
-// clang-format off
-AUTOHOOK(LoadSampleMetadata, mileswin64.dll + 0xF110,
-bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType))
-// clang-format on
-{
- // Raw source, used for voice data only
- if (audioType == 0)
- return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
-
- const char* eventName = pszAudioEventName;
-
- if (Cvar_ns_print_played_sounds->GetInt() > 0)
- spdlog::info("[AUDIO] Playing event {}", eventName);
-
- auto iter = g_CustomAudioManager.m_loadedAudioOverrides.find(eventName);
- std::shared_ptr<EventOverrideData> overrideData;
-
- if (iter == g_CustomAudioManager.m_loadedAudioOverrides.end())
- {
- // override for that specific event not found, try wildcard
- iter = g_CustomAudioManager.m_loadedAudioOverrides.find("*");
-
- if (iter == g_CustomAudioManager.m_loadedAudioOverrides.end())
- {
- // not found
-
- // try regex
- for (const auto& item : g_CustomAudioManager.m_loadedAudioOverridesRegex)
- for (const auto& regexData : item.second->EventIdsRegex)
- if (std::regex_search(eventName, regexData.second))
- overrideData = item.second;
-
- if (!overrideData)
- // not found either
- return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
- else
- {
- // cache found pattern to improve performance
- g_CustomAudioManager.m_loadedAudioOverrides[eventName] = overrideData;
- }
- }
- else
- overrideData = iter->second;
- }
- else
- overrideData = iter->second;
-
- if (!ShouldPlayAudioEvent(eventName, overrideData))
- return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
-
- void* data = 0;
- unsigned int dataLength = 0;
-
- if (overrideData->Samples.size() == 0)
- {
- // 0 samples, turn off this particular event.
-
- // using a dummy empty wave file
- data = EMPTY_WAVE;
- dataLength = sizeof(EMPTY_WAVE);
- }
- else
- {
- std::pair<size_t, std::unique_ptr<uint8_t[]>>* dat = NULL;
-
- switch (overrideData->Strategy)
- {
- case AudioSelectionStrategy::RANDOM:
- dat = &*select_randomly(overrideData->Samples.begin(), overrideData->Samples.end());
- break;
- case AudioSelectionStrategy::SEQUENTIAL:
- default:
- dat = &overrideData->Samples[overrideData->CurrentIndex++];
- if (overrideData->CurrentIndex >= overrideData->Samples.size())
- overrideData->CurrentIndex = 0; // reset back to the first sample entry
- break;
- }
-
- if (!dat)
- spdlog::warn("Could not get sample data from override struct for event {}! Shouldn't happen", eventName);
- else
- {
- data = dat->second.get();
- dataLength = dat->first;
- }
- }
-
- if (!data)
- {
- spdlog::warn("Could not fetch override sample data for event {}! Using original data instead.", eventName);
- return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
- }
-
- audioBuffer = data;
- audioBufferLength = dataLength;
-
- // most important change: set the sample class buffer so that the correct audio plays
- *(void**)((uintptr_t)sample + 0xE8) = audioBuffer;
- *(unsigned int*)((uintptr_t)sample + 0xF0) = audioBufferLength;
-
- // 64 - Auto-detect sample type
- bool res = LoadSampleMetadata(sample, audioBuffer, audioBufferLength, 64);
- if (!res)
- spdlog::error("LoadSampleMetadata failed! The game will crash :(");
-
- return res;
-}
-
-// clang-format off
-AUTOHOOK(sub_1800294C0, mileswin64.dll + 0x294C0,
-void*, __fastcall, (void* a1, void* a2))
-// clang-format on
-{
- pszAudioEventName = reinterpret_cast<const char*>((*((__int64*)a2 + 6)));
- return sub_1800294C0(a1, a2);
-}
-
-// clang-format off
-AUTOHOOK(MilesLog, client.dll + 0x57DAD0,
-void, __fastcall, (int level, const char* string))
-// clang-format on
-{
- if (!Cvar_mileslog_enable->GetBool())
- return;
-
- spdlog::info("[MSS] {} - {}", level, string);
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", MilesLogFuncHooks, ConVar, (CModule module))
-{
- Cvar_mileslog_enable = new ConVar("mileslog_enable", "0", FCVAR_NONE, "Enables/disables whether the mileslog func should be logged");
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("client.dll", AudioHooks, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- Cvar_ns_print_played_sounds = new ConVar("ns_print_played_sounds", "0", FCVAR_NONE, "");
- MilesStopAll = module.Offset(0x580850).RCast<MilesStopAll_Type>();
-}
diff --git a/NorthstarDLL/client/audio.h b/NorthstarDLL/client/audio.h
deleted file mode 100644
index 15fd1a35..00000000
--- a/NorthstarDLL/client/audio.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#pragma once
-
-#include <vector>
-#include <filesystem>
-#include <regex>
-#include <shared_mutex>
-
-enum class AudioSelectionStrategy
-{
- INVALID = -1,
- SEQUENTIAL,
- RANDOM
-};
-
-class EventOverrideData
-{
-public:
- EventOverrideData(const std::string&, const fs::path&);
- EventOverrideData();
-
-public:
- bool LoadedSuccessfully = false;
-
- std::vector<std::string> EventIds = {};
- std::vector<std::pair<std::string, std::regex>> EventIdsRegex = {};
-
- std::vector<std::pair<size_t, std::unique_ptr<uint8_t[]>>> Samples = {};
-
- AudioSelectionStrategy Strategy = AudioSelectionStrategy::SEQUENTIAL;
- size_t CurrentIndex = 0;
-
- bool EnableOnLoopedSounds = false;
-};
-
-class CustomAudioManager
-{
-public:
- 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 = {};
-};
-
-extern CustomAudioManager g_CustomAudioManager;
diff --git a/NorthstarDLL/client/chatcommand.cpp b/NorthstarDLL/client/chatcommand.cpp
deleted file mode 100644
index 9cf34e43..00000000
--- a/NorthstarDLL/client/chatcommand.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "core/convar/convar.h"
-#include "core/convar/concommand.h"
-#include "localchatwriter.h"
-
-// note: isIngameChat is an int64 because the whole register the arg is stored in needs to be 0'd out to work
-// if isIngameChat is false, we use network chat instead
-void(__fastcall* ClientSayText)(void* a1, const char* message, uint64_t isIngameChat, bool isTeamChat);
-
-void ConCommand_say(const CCommand& args)
-{
- if (args.ArgC() >= 2)
- ClientSayText(nullptr, args.ArgS(), true, false);
-}
-
-void ConCommand_say_team(const CCommand& args)
-{
- if (args.ArgC() >= 2)
- ClientSayText(nullptr, args.ArgS(), true, true);
-}
-
-void ConCommand_log(const CCommand& args)
-{
- if (args.ArgC() >= 2)
- {
- LocalChatWriter(LocalChatWriter::GameContext).WriteLine(args.ArgS());
- }
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", ClientChatCommand, ConCommand, (CModule module))
-{
- ClientSayText =
- module.Offset(0x54780).RCast<void(__fastcall*)(void* a1, const char* message, uint64_t isIngameChat, bool isTeamChat)>();
- RegisterConCommand("say", ConCommand_say, "Enters a message in public chat", FCVAR_CLIENTDLL);
- RegisterConCommand("say_team", ConCommand_say_team, "Enters a message in team chat", FCVAR_CLIENTDLL);
- RegisterConCommand("log", ConCommand_log, "Log a message to the local chat window", FCVAR_CLIENTDLL);
-}
diff --git a/NorthstarDLL/client/clientauthhooks.cpp b/NorthstarDLL/client/clientauthhooks.cpp
deleted file mode 100644
index 35ae3aa7..00000000
--- a/NorthstarDLL/client/clientauthhooks.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-#include "masterserver/masterserver.h"
-#include "core/convar/convar.h"
-#include "client/r2client.h"
-#include "core/vanilla.h"
-
-AUTOHOOK_INIT()
-
-ConVar* Cvar_ns_has_agreed_to_send_token;
-
-// mirrored in script
-const int NOT_DECIDED_TO_SEND_TOKEN = 0;
-const int AGREED_TO_SEND_TOKEN = 1;
-const int DISAGREED_TO_SEND_TOKEN = 2;
-
-// clang-format off
-AUTOHOOK(AuthWithStryder, engine.dll + 0x1843A0,
-void, __fastcall, (void* a1))
-// clang-format on
-{
- // don't attempt to do Atlas auth if we are in vanilla compatibility mode
- // this prevents users from joining untrustworthy servers (unless they use a concommand or something)
- if (g_pVanillaCompatibility->GetVanillaCompatibility())
- {
- AuthWithStryder(a1);
- return;
- }
-
- // game will call this forever, until it gets a valid auth key
- // so, we need to manually invalidate our key until we're authed with northstar, then we'll allow game to auth with stryder
- if (!g_pMasterServerManager->m_bOriginAuthWithMasterServerDone && Cvar_ns_has_agreed_to_send_token->GetInt() != DISAGREED_TO_SEND_TOKEN)
- {
- // if player has agreed to send token and we aren't already authing, try to auth
- if (Cvar_ns_has_agreed_to_send_token->GetInt() == AGREED_TO_SEND_TOKEN &&
- !g_pMasterServerManager->m_bOriginAuthWithMasterServerInProgress)
- g_pMasterServerManager->AuthenticateOriginWithMasterServer(g_pLocalPlayerUserID, g_pLocalPlayerOriginToken);
-
- // invalidate key so auth will fail
- *g_pLocalPlayerOriginToken = 0;
- }
-
- AuthWithStryder(a1);
-}
-
-char* p3PToken;
-
-// clang-format off
-AUTOHOOK(Auth3PToken, engine.dll + 0x183760,
-char*, __fastcall, ())
-// clang-format on
-{
- if (!g_pVanillaCompatibility->GetVanillaCompatibility() && g_pMasterServerManager->m_sOwnClientAuthToken[0])
- {
- memset(p3PToken, 0x0, 1024);
- strcpy(p3PToken, "Protocol 3: Protect the Pilot");
- }
-
- return Auth3PToken();
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", ClientAuthHooks, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- p3PToken = module.Offset(0x13979D80).RCast<char*>();
-
- // this cvar will save to cfg once initially agreed with
- Cvar_ns_has_agreed_to_send_token = new ConVar(
- "ns_has_agreed_to_send_token",
- "0",
- FCVAR_ARCHIVE_PLAYERPROFILE,
- "whether the user has agreed to send their origin token to the northstar masterserver");
-}
diff --git a/NorthstarDLL/client/clientruihooks.cpp b/NorthstarDLL/client/clientruihooks.cpp
deleted file mode 100644
index ad50d11a..00000000
--- a/NorthstarDLL/client/clientruihooks.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "core/convar/convar.h"
-
-AUTOHOOK_INIT()
-
-ConVar* Cvar_rui_drawEnable;
-
-// clang-format off
-AUTOHOOK(DrawRUIFunc, engine.dll + 0xFC500,
-bool, __fastcall, (void* a1, float* a2))
-// clang-format on
-{
- if (!Cvar_rui_drawEnable->GetBool())
- return 0;
-
- return DrawRUIFunc(a1, a2);
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", RUI, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- Cvar_rui_drawEnable = new ConVar("rui_drawEnable", "1", FCVAR_CLIENTDLL, "Controls whether RUI should be drawn");
-}
diff --git a/NorthstarDLL/client/clientvideooverrides.cpp b/NorthstarDLL/client/clientvideooverrides.cpp
deleted file mode 100644
index d8aa2754..00000000
--- a/NorthstarDLL/client/clientvideooverrides.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "mods/modmanager.h"
-
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK_PROCADDRESS(BinkOpen, bink2w64.dll, BinkOpen,
-void*, __fastcall, (const char* path, uint32_t flags))
-// clang-format on
-{
- std::string filename(fs::path(path).filename().string());
- spdlog::info("BinkOpen {}", filename);
-
- // figure out which mod is handling the bink
- Mod* fileOwner = nullptr;
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- if (std::find(mod.BinkVideos.begin(), mod.BinkVideos.end(), filename) != mod.BinkVideos.end())
- fileOwner = &mod;
- }
-
- if (fileOwner)
- {
- // create new path
- fs::path binkPath(fileOwner->m_ModDirectory / "media" / filename);
- return BinkOpen(binkPath.string().c_str(), flags);
- }
- else
- return BinkOpen(path, flags);
-}
-
-ON_DLL_LOAD_CLIENT("engine.dll", BinkVideo, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- // remove engine check for whether the bik we're trying to load exists in r2/media, as this will fail for biks in mods
- // note: the check in engine is actually unnecessary, so it's just useless in practice and we lose nothing by removing it
- module.Offset(0x459AD).NOP(6);
-}
diff --git a/NorthstarDLL/client/debugoverlay.cpp b/NorthstarDLL/client/debugoverlay.cpp
deleted file mode 100644
index e231054d..00000000
--- a/NorthstarDLL/client/debugoverlay.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-#include "dedicated/dedicated.h"
-#include "core/convar/cvar.h"
-#include "core/math/vector.h"
-
-AUTOHOOK_INIT()
-
-enum OverlayType_t
-{
- OVERLAY_BOX = 0,
- OVERLAY_SPHERE,
- OVERLAY_LINE,
- OVERLAY_SMARTAMMO,
- OVERLAY_TRIANGLE,
- OVERLAY_SWEPT_BOX,
- // [Fifty]: the 2 bellow i did not confirm, rest are good
- OVERLAY_BOX2,
- OVERLAY_CAPSULE
-};
-
-struct OverlayBase_t
-{
- OverlayBase_t()
- {
- m_Type = OVERLAY_BOX;
- m_nServerCount = -1;
- m_nCreationTick = -1;
- m_flEndTime = 0.0f;
- m_pNextOverlay = NULL;
- }
-
- OverlayType_t m_Type; // What type of overlay is it?
- int m_nCreationTick; // Duration -1 means go away after this frame #
- int m_nServerCount; // Latch server count, too
- float m_flEndTime; // When does this box go away
- OverlayBase_t* m_pNextOverlay;
- __int64 m_pUnk;
-};
-
-struct OverlayLine_t : public OverlayBase_t
-{
- OverlayLine_t()
- {
- m_Type = OVERLAY_LINE;
- }
-
- Vector3 origin;
- Vector3 dest;
- int r;
- int g;
- int b;
- int a;
- bool noDepthTest;
-};
-
-struct OverlayBox_t : public OverlayBase_t
-{
- OverlayBox_t()
- {
- m_Type = OVERLAY_BOX;
- }
-
- Vector3 origin;
- Vector3 mins;
- Vector3 maxs;
- QAngle angles;
- int r;
- int g;
- int b;
- int a;
-};
-
-struct OverlayTriangle_t : public OverlayBase_t
-{
- OverlayTriangle_t()
- {
- m_Type = OVERLAY_TRIANGLE;
- }
-
- Vector3 p1;
- Vector3 p2;
- Vector3 p3;
- int r;
- int g;
- int b;
- int a;
- bool noDepthTest;
-};
-
-struct OverlaySweptBox_t : public OverlayBase_t
-{
- OverlaySweptBox_t()
- {
- m_Type = OVERLAY_SWEPT_BOX;
- }
-
- Vector3 start;
- Vector3 end;
- Vector3 mins;
- Vector3 maxs;
- QAngle angles;
- int r;
- int g;
- int b;
- int a;
-};
-
-struct OverlaySphere_t : public OverlayBase_t
-{
- OverlaySphere_t()
- {
- m_Type = OVERLAY_SPHERE;
- }
-
- Vector3 vOrigin;
- float flRadius;
- int nTheta;
- int nPhi;
- int r;
- int g;
- int b;
- int a;
- bool m_bWireframe;
-};
-
-typedef bool (*OverlayBase_t__IsDeadType)(OverlayBase_t* a1);
-static OverlayBase_t__IsDeadType OverlayBase_t__IsDead;
-typedef void (*OverlayBase_t__DestroyOverlayType)(OverlayBase_t* a1);
-static OverlayBase_t__DestroyOverlayType OverlayBase_t__DestroyOverlay;
-
-static ConVar* Cvar_enable_debug_overlays;
-
-LPCRITICAL_SECTION s_OverlayMutex;
-
-// Render Line
-typedef void (*RenderLineType)(const Vector3& v1, const Vector3& v2, Color c, bool bZBuffer);
-static RenderLineType RenderLine;
-
-// Render box
-typedef void (*RenderBoxType)(
- const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut);
-static RenderBoxType RenderBox;
-
-// Render wireframe box
-static RenderBoxType RenderWireframeBox;
-
-// Render swept box
-typedef void (*RenderWireframeSweptBoxType)(
- const Vector3& vStart, const Vector3& vEnd, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer);
-RenderWireframeSweptBoxType RenderWireframeSweptBox;
-
-// Render Triangle
-typedef void (*RenderTriangleType)(const Vector3& p1, const Vector3& p2, const Vector3& p3, Color c, bool bZBuffer);
-static RenderTriangleType RenderTriangle;
-
-// Render Axis
-typedef void (*RenderAxisType)(const Vector3& vOrigin, float flScale, bool bZBuffer);
-static RenderAxisType RenderAxis;
-
-// I dont know
-typedef void (*RenderUnknownType)(const Vector3& vUnk, float flUnk, bool bUnk);
-static RenderUnknownType RenderUnknown;
-
-// Render Sphere
-typedef void (*RenderSphereType)(const Vector3& vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer);
-static RenderSphereType RenderSphere;
-
-OverlayBase_t** s_pOverlays;
-
-int* g_nRenderTickCount;
-int* g_nOverlayTickCount;
-
-// clang-format off
-AUTOHOOK(DrawOverlay, engine.dll + 0xABCB0,
-void, __fastcall, (OverlayBase_t * pOverlay))
-// clang-format on
-{
- EnterCriticalSection(s_OverlayMutex);
-
- switch (pOverlay->m_Type)
- {
- case OVERLAY_SMARTAMMO:
- case OVERLAY_LINE:
- {
- OverlayLine_t* pLine = static_cast<OverlayLine_t*>(pOverlay);
- RenderLine(pLine->origin, pLine->dest, Color(pLine->r, pLine->g, pLine->b, pLine->a), pLine->noDepthTest);
- }
- break;
- case OVERLAY_BOX:
- {
- OverlayBox_t* pCurrBox = static_cast<OverlayBox_t*>(pOverlay);
- if (pCurrBox->a > 0)
- {
- RenderBox(
- pCurrBox->origin,
- pCurrBox->angles,
- pCurrBox->mins,
- pCurrBox->maxs,
- Color(pCurrBox->r, pCurrBox->g, pCurrBox->b, pCurrBox->a),
- false,
- false);
- }
- if (pCurrBox->a < 255)
- {
- RenderWireframeBox(
- pCurrBox->origin,
- pCurrBox->angles,
- pCurrBox->mins,
- pCurrBox->maxs,
- Color(pCurrBox->r, pCurrBox->g, pCurrBox->b, 255),
- false,
- false);
- }
- }
- break;
- case OVERLAY_TRIANGLE:
- {
- OverlayTriangle_t* pTriangle = static_cast<OverlayTriangle_t*>(pOverlay);
- RenderTriangle(
- pTriangle->p1,
- pTriangle->p2,
- pTriangle->p3,
- Color(pTriangle->r, pTriangle->g, pTriangle->b, pTriangle->a),
- pTriangle->noDepthTest);
- }
- break;
- case OVERLAY_SWEPT_BOX:
- {
- OverlaySweptBox_t* pBox = static_cast<OverlaySweptBox_t*>(pOverlay);
- RenderWireframeSweptBox(
- pBox->start, pBox->end, pBox->angles, pBox->mins, pBox->maxs, Color(pBox->r, pBox->g, pBox->b, pBox->a), false);
- }
- break;
- case OVERLAY_SPHERE:
- {
- OverlaySphere_t* pSphere = static_cast<OverlaySphere_t*>(pOverlay);
- RenderSphere(
- pSphere->vOrigin,
- pSphere->flRadius,
- pSphere->nTheta,
- pSphere->nPhi,
- Color(pSphere->r, pSphere->g, pSphere->b, pSphere->a),
- false);
- }
- break;
- default:
- {
- spdlog::warn("Unimplemented overlay type {}", pOverlay->m_Type);
- }
- break;
- }
-
- LeaveCriticalSection(s_OverlayMutex);
-}
-
-// clang-format off
-AUTOHOOK(DrawAllOverlays, engine.dll + 0xAB780,
-void, __fastcall, (bool bRender))
-// clang-format on
-{
- EnterCriticalSection(s_OverlayMutex);
-
- OverlayBase_t* pCurrOverlay = *s_pOverlays; // rbx
- OverlayBase_t* pPrevOverlay = nullptr; // rsi
- OverlayBase_t* pNextOverlay = nullptr; // rdi
-
- int m_nCreationTick; // eax
- bool bShouldDraw; // zf
- int m_pUnk; // eax
-
- while (pCurrOverlay)
- {
- if (OverlayBase_t__IsDead(pCurrOverlay))
- {
- if (pPrevOverlay)
- {
- pPrevOverlay->m_pNextOverlay = pCurrOverlay->m_pNextOverlay;
- }
- else
- {
- *s_pOverlays = pCurrOverlay->m_pNextOverlay;
- }
-
- pNextOverlay = pCurrOverlay->m_pNextOverlay;
- OverlayBase_t__DestroyOverlay(pCurrOverlay);
- pCurrOverlay = pNextOverlay;
- }
- else
- {
- if (pCurrOverlay->m_nCreationTick == -1)
- {
- m_pUnk = pCurrOverlay->m_pUnk;
-
- if (m_pUnk == -1)
- {
- bShouldDraw = true;
- }
- else
- {
- bShouldDraw = m_pUnk == *g_nOverlayTickCount;
- }
- }
- else
- {
- bShouldDraw = pCurrOverlay->m_nCreationTick == *g_nRenderTickCount;
- }
-
- if (bShouldDraw && bRender && (Cvar_enable_debug_overlays->GetBool() || pCurrOverlay->m_Type == OVERLAY_SMARTAMMO))
- {
- DrawOverlay(pCurrOverlay);
- }
-
- pPrevOverlay = pCurrOverlay;
- pCurrOverlay = pCurrOverlay->m_pNextOverlay;
- }
- }
-
- LeaveCriticalSection(s_OverlayMutex);
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", DebugOverlay, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast<OverlayBase_t__IsDeadType>();
- OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast<OverlayBase_t__DestroyOverlayType>();
-
- RenderLine = module.Offset(0x192A70).RCast<RenderLineType>();
- RenderBox = module.Offset(0x192520).RCast<RenderBoxType>();
- RenderWireframeBox = module.Offset(0x193DA0).RCast<RenderBoxType>();
- RenderWireframeSweptBox = module.Offset(0x1945A0).RCast<RenderWireframeSweptBoxType>();
- RenderTriangle = module.Offset(0x193940).RCast<RenderTriangleType>();
- RenderAxis = module.Offset(0x1924D0).RCast<RenderAxisType>();
- RenderSphere = module.Offset(0x194170).RCast<RenderSphereType>();
- RenderUnknown = module.Offset(0x1924E0).RCast<RenderUnknownType>();
-
- s_OverlayMutex = module.Offset(0x10DB0A38).RCast<LPCRITICAL_SECTION>();
-
- s_pOverlays = module.Offset(0x10DB0968).RCast<OverlayBase_t**>();
-
- g_nRenderTickCount = module.Offset(0x10DB0984).RCast<int*>();
- g_nOverlayTickCount = module.Offset(0x10DB0980).RCast<int*>();
-
- // not in g_pCVar->FindVar by this point for whatever reason, so have to get from memory
- Cvar_enable_debug_overlays = module.Offset(0x10DB0990).RCast<ConVar*>();
- Cvar_enable_debug_overlays->SetValue(false);
- Cvar_enable_debug_overlays->m_pszDefaultValue = (char*)"0";
- Cvar_enable_debug_overlays->AddFlags(FCVAR_CHEAT);
-}
diff --git a/NorthstarDLL/client/demofixes.cpp b/NorthstarDLL/client/demofixes.cpp
deleted file mode 100644
index 344764ba..00000000
--- a/NorthstarDLL/client/demofixes.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "core/convar/convar.h"
-
-ON_DLL_LOAD_CLIENT("engine.dll", EngineDemoFixes, (CModule module))
-{
- // allow demo recording on loopback
- module.Offset(0x8E1B1).NOP(2);
- module.Offset(0x56CC3).NOP(2);
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientDemoFixes, ConVar, (CModule module))
-{
- // change default values of demo cvars to enable them by default, but not autorecord
- // this is before Host_Init, the setvalue calls here will get overwritten by custom cfgs/launch options
- ConVar* Cvar_demo_enableDemos = g_pCVar->FindVar("demo_enabledemos");
- Cvar_demo_enableDemos->m_pszDefaultValue = "1";
- Cvar_demo_enableDemos->SetValue(true);
-
- ConVar* Cvar_demo_writeLocalFile = g_pCVar->FindVar("demo_writeLocalFile");
- Cvar_demo_writeLocalFile->m_pszDefaultValue = "1";
- Cvar_demo_writeLocalFile->SetValue(true);
-
- ConVar* Cvar_demo_autoRecord = g_pCVar->FindVar("demo_autoRecord");
- Cvar_demo_autoRecord->m_pszDefaultValue = "0";
- Cvar_demo_autoRecord->SetValue(false);
-}
diff --git a/NorthstarDLL/client/diskvmtfixes.cpp b/NorthstarDLL/client/diskvmtfixes.cpp
deleted file mode 100644
index 4ab951c0..00000000
--- a/NorthstarDLL/client/diskvmtfixes.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-
-ON_DLL_LOAD_CLIENT("materialsystem_dx11.dll", DiskVMTFixes, (CModule module))
-{
- // in retail VMTs will never load if cache read is invalid due to a special case for them in KeyValues::LoadFromFile
- // this effectively makes it impossible to load them from mods because we invalidate cache for doing this
- // so uhh, stop that from happening
-
- // tbh idk why they even changed any of this what's the point it looks like it works fine who cares my god
-
- // matsystem KeyValues::LoadFromFile: patch special case on cache read failure for vmts
- module.Offset(0x1281B9).Patch("EB");
-
- // CMaterialSystem::FindMaterial: don't call function that crashes if previous patch is applied
- module.Offset(0x5F55A).NOP(5);
-}
diff --git a/NorthstarDLL/client/languagehooks.cpp b/NorthstarDLL/client/languagehooks.cpp
deleted file mode 100644
index 35ca5659..00000000
--- a/NorthstarDLL/client/languagehooks.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-#include "core/tier0.h"
-
-#include <filesystem>
-#include <regex>
-
-AUTOHOOK_INIT()
-
-typedef LANGID (*Tier0_DetectDefaultLanguageType)();
-
-bool CheckLangAudioExists(char* lang)
-{
- std::string path {"r2\\sound\\general_"};
- path += lang;
- path += ".mstr";
- return fs::exists(path);
-}
-
-std::vector<std::string> file_list(fs::path dir, std::regex ext_pattern)
-{
- std::vector<std::string> result;
-
- if (!fs::exists(dir) || !fs::is_directory(dir))
- return result;
-
- using iterator = fs::directory_iterator;
-
- const iterator end;
- for (iterator iter {dir}; iter != end; ++iter)
- {
- const std::string filename = iter->path().filename().string();
- std::smatch matches;
- if (fs::is_regular_file(*iter) && std::regex_match(filename, matches, ext_pattern))
- {
- result.push_back(std::move(matches.str(1)));
- }
- }
-
- return result;
-}
-
-std::string GetAnyInstalledAudioLanguage()
-{
- for (const auto& lang : file_list("r2\\sound\\", std::regex(".*?general_([a-z]+)_patch_1\\.mstr")))
- if (lang != "general" || lang != "")
- return lang;
- return "NO LANGUAGE DETECTED";
-}
-
-// clang-format off
-AUTOHOOK(GetGameLanguage, tier0.dll + 0xF560,
-char*, __fastcall, ())
-// clang-format on
-{
- auto tier0Handle = GetModuleHandleA("tier0.dll");
- auto Tier0_DetectDefaultLanguageType = GetProcAddress(tier0Handle, "Tier0_DetectDefaultLanguage");
- char* ingameLang1 = (char*)tier0Handle + 0xA9B60; // one of the globals we need to override if overriding lang (size: 256)
- bool& canOriginDictateLang = *(bool*)((char*)tier0Handle + 0xA9A90);
-
- const char* forcedLanguage;
- if (CommandLine()->CheckParm("-language", &forcedLanguage))
- {
- if (!CheckLangAudioExists((char*)forcedLanguage))
- {
- spdlog::info(
- "User tried to force the language (-language) to \"{}\", but audio for this language doesn't exist and the game is bound "
- "to error, falling back to next option...",
- forcedLanguage);
- }
- else
- {
- spdlog::info("User forcing the language (-language) to: {}", forcedLanguage);
- strncpy(ingameLang1, forcedLanguage, 256);
- return ingameLang1;
- }
- }
-
- canOriginDictateLang = true; // let it try
- {
- auto lang = GetGameLanguage();
- if (!CheckLangAudioExists(lang))
- {
- if (strcmp(lang, "russian") !=
- 0) // don't log for "russian" since it's the default and that means Origin detection just didn't change it most likely
- spdlog::info(
- "Origin detected language \"{}\", but we do not have audio for it installed, falling back to the next option", lang);
- }
- else
- {
- spdlog::info("Origin detected language: {}", lang);
- return lang;
- }
- }
-
- Tier0_DetectDefaultLanguageType(); // force the global in tier0 to be populated with language inferred from user's system rather than
- // defaulting to Russian
- canOriginDictateLang = false; // Origin has no say anymore, we will fallback to user's system setup language
- auto lang = GetGameLanguage();
- spdlog::info("Detected system language: {}", lang);
- if (!CheckLangAudioExists(lang))
- {
- spdlog::warn("Caution, audio for this language does NOT exist. You might want to override your game language with -language "
- "command line option.");
- auto lang = GetAnyInstalledAudioLanguage();
- spdlog::warn("Falling back to the first installed audio language: {}", lang.c_str());
- strncpy(ingameLang1, lang.c_str(), 256);
- return ingameLang1;
- }
-
- return lang;
-}
-
-ON_DLL_LOAD_CLIENT("tier0.dll", LanguageHooks, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/client/latencyflex.cpp b/NorthstarDLL/client/latencyflex.cpp
deleted file mode 100644
index 25e38c7a..00000000
--- a/NorthstarDLL/client/latencyflex.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include "core/convar/convar.h"
-
-AUTOHOOK_INIT()
-
-ConVar* Cvar_r_latencyflex;
-
-void (*m_winelfx_WaitAndBeginFrame)();
-
-// clang-format off
-AUTOHOOK(OnRenderStart, client.dll + 0x1952C0,
-void, __fastcall, ())
-// clang-format on
-{
- if (Cvar_r_latencyflex->GetBool() && m_winelfx_WaitAndBeginFrame)
- m_winelfx_WaitAndBeginFrame();
-
- OnRenderStart();
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("client.dll", LatencyFlex, ConVar, (CModule module))
-{
- // Connect to the LatencyFleX service
- // LatencyFleX is an open source vendor agnostic replacement for Nvidia Reflex input latency reduction technology.
- // https://ishitatsuyuki.github.io/post/latencyflex/
- HMODULE pLfxModule;
-
- if (pLfxModule = LoadLibraryA("latencyflex_layer.dll"))
- m_winelfx_WaitAndBeginFrame =
- reinterpret_cast<void (*)()>(reinterpret_cast<void*>(GetProcAddress(pLfxModule, "lfx_WaitAndBeginFrame")));
- else if (pLfxModule = LoadLibraryA("latencyflex_wine.dll"))
- m_winelfx_WaitAndBeginFrame =
- reinterpret_cast<void (*)()>(reinterpret_cast<void*>(GetProcAddress(pLfxModule, "winelfx_WaitAndBeginFrame")));
- else
- {
- spdlog::info("Unable to load LatencyFleX library, LatencyFleX disabled.");
- return;
- }
-
- AUTOHOOK_DISPATCH()
-
- spdlog::info("LatencyFleX initialized.");
- Cvar_r_latencyflex = new ConVar("r_latencyflex", "1", FCVAR_ARCHIVE, "Whether or not to use LatencyFleX input latency reduction.");
-}
diff --git a/NorthstarDLL/client/localchatwriter.cpp b/NorthstarDLL/client/localchatwriter.cpp
deleted file mode 100644
index 35cc065f..00000000
--- a/NorthstarDLL/client/localchatwriter.cpp
+++ /dev/null
@@ -1,449 +0,0 @@
-#include "localchatwriter.h"
-
-class vgui_BaseRichText_vtable;
-
-class vgui_BaseRichText
-{
-public:
- vgui_BaseRichText_vtable* vtable;
-};
-
-class vgui_BaseRichText_vtable
-{
-public:
- char unknown1[1880];
-
- void(__fastcall* InsertChar)(vgui_BaseRichText* self, wchar_t ch);
-
- // yes these are swapped from the Source 2013 code, who knows why
- void(__fastcall* InsertStringWide)(vgui_BaseRichText* self, const wchar_t* wszText);
- void(__fastcall* InsertStringAnsi)(vgui_BaseRichText* self, const char* text);
-
- void(__fastcall* SelectNone)(vgui_BaseRichText* self);
- void(__fastcall* SelectAllText)(vgui_BaseRichText* self);
- void(__fastcall* SelectNoText)(vgui_BaseRichText* self);
- void(__fastcall* CutSelected)(vgui_BaseRichText* self);
- void(__fastcall* CopySelected)(vgui_BaseRichText* self);
- void(__fastcall* SetPanelInteractive)(vgui_BaseRichText* self, bool bInteractive);
- void(__fastcall* SetUnusedScrollbarInvisible)(vgui_BaseRichText* self, bool bInvis);
-
- void* unknown2;
-
- void(__fastcall* GotoTextStart)(vgui_BaseRichText* self);
- void(__fastcall* GotoTextEnd)(vgui_BaseRichText* self);
-
- void* unknown3[3];
-
- void(__fastcall* SetVerticalScrollbar)(vgui_BaseRichText* self, bool state);
- void(__fastcall* SetMaximumCharCount)(vgui_BaseRichText* self, int maxChars);
- void(__fastcall* InsertColorChange)(vgui_BaseRichText* self, Color col);
- void(__fastcall* InsertIndentChange)(vgui_BaseRichText* self, int pixelsIndent);
- void(__fastcall* InsertClickableTextStart)(vgui_BaseRichText* self, const char* pchClickAction);
- void(__fastcall* InsertClickableTextEnd)(vgui_BaseRichText* self);
- void(__fastcall* InsertPossibleURLString)(vgui_BaseRichText* self, const char* text, Color URLTextColor, Color normalTextColor);
- void(__fastcall* InsertFade)(vgui_BaseRichText* self, float flSustain, float flLength);
- void(__fastcall* ResetAllFades)(vgui_BaseRichText* self, bool bHold, bool bOnlyExpired, float flNewSustain);
- void(__fastcall* SetToFullHeight)(vgui_BaseRichText* self);
- int(__fastcall* GetNumLines)(vgui_BaseRichText* self);
-};
-
-class CGameSettings
-{
-public:
- char unknown1[92];
- int isChatEnabled;
-};
-
-// Not sure what this actually refers to but chatFadeLength and chatFadeSustain
-// have their value at the same offset
-class CGameFloatVar
-{
-public:
- char unknown1[88];
- float value;
-};
-
-CGameSettings** gGameSettings;
-CGameFloatVar** gChatFadeLength;
-CGameFloatVar** gChatFadeSustain;
-
-CHudChat** CHudChat::allHuds;
-
-typedef void(__fastcall* ConvertANSIToUnicodeType)(LPCSTR ansi, int ansiCharLength, LPWSTR unicode, int unicodeCharLength);
-ConvertANSIToUnicodeType ConvertANSIToUnicode;
-
-LocalChatWriter::SwatchColor swatchColors[4] = {
- LocalChatWriter::MainTextColor,
- LocalChatWriter::SameTeamNameColor,
- LocalChatWriter::EnemyTeamNameColor,
- LocalChatWriter::NetworkNameColor,
-};
-
-Color darkColors[8] = {
- Color {0, 0, 0, 255},
- Color {205, 49, 49, 255},
- Color {13, 188, 121, 255},
- Color {229, 229, 16, 255},
- Color {36, 114, 200, 255},
- Color {188, 63, 188, 255},
- Color {17, 168, 205, 255},
- Color {229, 229, 229, 255}};
-
-Color lightColors[8] = {
- Color {102, 102, 102, 255},
- Color {241, 76, 76, 255},
- Color {35, 209, 139, 255},
- Color {245, 245, 67, 255},
- Color {59, 142, 234, 255},
- Color {214, 112, 214, 255},
- Color {41, 184, 219, 255},
- Color {255, 255, 255, 255}};
-
-class AnsiEscapeParser
-{
-public:
- explicit AnsiEscapeParser(LocalChatWriter* writer) : m_writer(writer) {}
-
- void HandleVal(unsigned long val)
- {
- switch (m_next)
- {
- case Next::ControlType:
- m_next = HandleControlType(val);
- break;
- case Next::ForegroundType:
- m_next = HandleForegroundType(val);
- break;
- case Next::Foreground8Bit:
- m_next = HandleForeground8Bit(val);
- break;
- case Next::ForegroundR:
- m_next = HandleForegroundR(val);
- break;
- case Next::ForegroundG:
- m_next = HandleForegroundG(val);
- break;
- case Next::ForegroundB:
- m_next = HandleForegroundB(val);
- break;
- }
- }
-
-private:
- enum class Next
- {
- ControlType,
- ForegroundType,
- Foreground8Bit,
- ForegroundR,
- ForegroundG,
- ForegroundB
- };
-
- LocalChatWriter* m_writer;
- Next m_next = Next::ControlType;
- Color m_expandedColor {0, 0, 0, 0};
-
- Next HandleControlType(unsigned long val)
- {
- // Reset
- if (val == 0 || val == 39)
- {
- m_writer->InsertSwatchColorChange(LocalChatWriter::MainTextColor);
- return Next::ControlType;
- }
-
- // Dark foreground color
- if (val >= 30 && val < 38)
- {
- m_writer->InsertColorChange(darkColors[val - 30]);
- return Next::ControlType;
- }
-
- // Light foreground color
- if (val >= 90 && val < 98)
- {
- m_writer->InsertColorChange(lightColors[val - 90]);
- return Next::ControlType;
- }
-
- // Game swatch color
- if (val >= 110 && val < 114)
- {
- m_writer->InsertSwatchColorChange(swatchColors[val - 110]);
- return Next::ControlType;
- }
-
- // Expanded foreground color
- if (val == 38)
- {
- return Next::ForegroundType;
- }
-
- return Next::ControlType;
- }
-
- Next HandleForegroundType(unsigned long val)
- {
- // Next values are r,g,b
- if (val == 2)
- {
- m_expandedColor.SetColor(0, 0, 0, 255);
- return Next::ForegroundR;
- }
- // Next value is 8-bit swatch color
- if (val == 5)
- {
- return Next::Foreground8Bit;
- }
-
- // Invalid
- return Next::ControlType;
- }
-
- Next HandleForeground8Bit(unsigned long val)
- {
- if (val < 8)
- {
- m_writer->InsertColorChange(darkColors[val]);
- }
- else if (val < 16)
- {
- m_writer->InsertColorChange(lightColors[val - 8]);
- }
- else if (val < 232)
- {
- unsigned char code = val - 16;
- unsigned char blue = code % 6;
- unsigned char green = ((code - blue) / 6) % 6;
- unsigned char red = (code - blue - (green * 6)) / 36;
- m_writer->InsertColorChange(Color {(unsigned char)(red * 51), (unsigned char)(green * 51), (unsigned char)(blue * 51), 255});
- }
- else if (val < UCHAR_MAX)
- {
- unsigned char brightness = (val - 232) * 10 + 8;
- m_writer->InsertColorChange(Color {brightness, brightness, brightness, 255});
- }
-
- return Next::ControlType;
- }
-
- Next HandleForegroundR(unsigned long val)
- {
- if (val >= UCHAR_MAX)
- return Next::ControlType;
-
- m_expandedColor[0] = (unsigned char)val;
- return Next::ForegroundG;
- }
-
- Next HandleForegroundG(unsigned long val)
- {
- if (val >= UCHAR_MAX)
- return Next::ControlType;
-
- m_expandedColor[1] = (unsigned char)val;
- return Next::ForegroundB;
- }
-
- Next HandleForegroundB(unsigned long val)
- {
- if (val >= UCHAR_MAX)
- return Next::ControlType;
-
- m_expandedColor[2] = (unsigned char)val;
- m_writer->InsertColorChange(m_expandedColor);
- return Next::ControlType;
- }
-};
-
-LocalChatWriter::LocalChatWriter(Context context) : m_context(context) {}
-
-void LocalChatWriter::Write(const char* str)
-{
- char writeBuffer[256];
-
- while (true)
- {
- const char* startOfEscape = strstr(str, "\033[");
-
- if (startOfEscape == NULL)
- {
- // No more escape sequences, write the remaining text and exit
- InsertText(str);
- break;
- }
-
- if (startOfEscape != str)
- {
- // There is some text before the escape sequence, just print that
- size_t copyChars = startOfEscape - str;
- if (copyChars > 255)
- copyChars = 255;
-
- strncpy_s(writeBuffer, copyChars + 1, str, copyChars);
-
- InsertText(writeBuffer);
- }
-
- const char* escape = startOfEscape + 2;
- str = ApplyAnsiEscape(escape);
- }
-}
-
-void LocalChatWriter::WriteLine(const char* str)
-{
- InsertChar(L'\n');
- InsertSwatchColorChange(MainTextColor);
- Write(str);
-}
-
-void LocalChatWriter::InsertChar(wchar_t ch)
-{
- for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
- {
- if (hud->m_unknownContext != (int)m_context)
- continue;
-
- hud->m_richText->vtable->InsertChar(hud->m_richText, ch);
- }
-
- if (ch != L'\n')
- {
- InsertDefaultFade();
- }
-}
-
-void LocalChatWriter::InsertText(const char* str)
-{
- spdlog::info(str);
-
- WCHAR messageUnicode[288];
- ConvertANSIToUnicode(str, -1, messageUnicode, 274);
-
- for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
- {
- if (hud->m_unknownContext != (int)m_context)
- continue;
-
- hud->m_richText->vtable->InsertStringWide(hud->m_richText, messageUnicode);
- }
-
- InsertDefaultFade();
-}
-
-void LocalChatWriter::InsertText(const wchar_t* str)
-{
- for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
- {
- if (hud->m_unknownContext != (int)m_context)
- continue;
-
- hud->m_richText->vtable->InsertStringWide(hud->m_richText, str);
- }
-
- InsertDefaultFade();
-}
-
-void LocalChatWriter::InsertColorChange(Color color)
-{
- for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
- {
- if (hud->m_unknownContext != (int)m_context)
- continue;
-
- hud->m_richText->vtable->InsertColorChange(hud->m_richText, color);
- }
-}
-
-static Color GetHudSwatchColor(CHudChat* hud, LocalChatWriter::SwatchColor swatchColor)
-{
- switch (swatchColor)
- {
- case LocalChatWriter::MainTextColor:
- return hud->m_mainTextColor;
-
- case LocalChatWriter::SameTeamNameColor:
- return hud->m_sameTeamColor;
-
- case LocalChatWriter::EnemyTeamNameColor:
- return hud->m_enemyTeamColor;
-
- case LocalChatWriter::NetworkNameColor:
- return hud->m_networkNameColor;
- }
-
- return Color(0, 0, 0, 0);
-}
-
-void LocalChatWriter::InsertSwatchColorChange(SwatchColor swatchColor)
-{
- for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
- {
- if (hud->m_unknownContext != (int)m_context)
- continue;
- hud->m_richText->vtable->InsertColorChange(hud->m_richText, GetHudSwatchColor(hud, swatchColor));
- }
-}
-
-const char* LocalChatWriter::ApplyAnsiEscape(const char* escape)
-{
- AnsiEscapeParser decoder(this);
- while (true)
- {
- char* afterControlType = NULL;
- unsigned long controlType = strtoul(escape, &afterControlType, 10);
-
- // Malformed cases:
- // afterControlType = NULL: strtoul errored
- // controlType = 0 and escape doesn't actually start with 0: wasn't a number
- if (afterControlType == NULL || (controlType == 0 && escape[0] != '0'))
- {
- return escape;
- }
-
- decoder.HandleVal(controlType);
-
- // m indicates the end of the sequence
- if (afterControlType[0] == 'm')
- {
- return afterControlType + 1;
- }
-
- // : or ; indicates more values remain, anything else is malformed
- if (afterControlType[0] != ':' && afterControlType[0] != ';')
- {
- return afterControlType;
- }
-
- escape = afterControlType + 1;
- }
-}
-
-void LocalChatWriter::InsertDefaultFade()
-{
- float fadeLength = 0.f;
- float fadeSustain = 0.f;
- if ((*gGameSettings)->isChatEnabled)
- {
- fadeLength = (*gChatFadeLength)->value;
- fadeSustain = (*gChatFadeSustain)->value;
- }
-
- for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
- {
- if (hud->m_unknownContext != (int)m_context)
- continue;
- hud->m_richText->vtable->InsertFade(hud->m_richText, fadeSustain, fadeLength);
- }
-}
-
-ON_DLL_LOAD_CLIENT("client.dll", LocalChatWriter, (CModule module))
-{
- gGameSettings = module.Offset(0x11BAA48).RCast<CGameSettings**>();
- gChatFadeLength = module.Offset(0x11BAB78).RCast<CGameFloatVar**>();
- gChatFadeSustain = module.Offset(0x11BAC08).RCast<CGameFloatVar**>();
- CHudChat::allHuds = module.Offset(0x11BA9E8).RCast<CHudChat**>();
-
- ConvertANSIToUnicode = module.Offset(0x7339A0).RCast<ConvertANSIToUnicodeType>();
-}
diff --git a/NorthstarDLL/client/localchatwriter.h b/NorthstarDLL/client/localchatwriter.h
deleted file mode 100644
index acf6f87e..00000000
--- a/NorthstarDLL/client/localchatwriter.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#pragma once
-#include "core/math/color.h"
-
-class vgui_BaseRichText;
-
-class CHudChat
-{
-public:
- static CHudChat** allHuds;
-
- char unknown1[720];
-
- Color m_sameTeamColor;
- Color m_enemyTeamColor;
- Color m_mainTextColor;
- Color m_networkNameColor;
-
- char unknown2[12];
-
- int m_unknownContext;
-
- char unknown3[8];
-
- vgui_BaseRichText* m_richText;
-
- CHudChat* next;
- CHudChat* previous;
-};
-
-class LocalChatWriter
-{
-public:
- enum Context
- {
- NetworkContext = 0,
- GameContext = 1
- };
- enum SwatchColor
- {
- MainTextColor,
- SameTeamNameColor,
- EnemyTeamNameColor,
- NetworkNameColor
- };
-
- explicit LocalChatWriter(Context context);
-
- // Custom chat writing with ANSI escape codes
- void Write(const char* str);
- void WriteLine(const char* str);
-
- // Low-level RichText access
- void InsertChar(wchar_t ch);
- void InsertText(const char* str);
- void InsertText(const wchar_t* str);
- void InsertColorChange(Color color);
- void InsertSwatchColorChange(SwatchColor color);
-
-private:
- Context m_context;
-
- const char* ApplyAnsiEscape(const char* escape);
- void InsertDefaultFade();
-};
diff --git a/NorthstarDLL/client/modlocalisation.cpp b/NorthstarDLL/client/modlocalisation.cpp
deleted file mode 100644
index 2b73876b..00000000
--- a/NorthstarDLL/client/modlocalisation.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "mods/modmanager.h"
-
-AUTOHOOK_INIT()
-
-void* g_pVguiLocalize;
-
-// clang-format off
-AUTOHOOK(CLocalize__AddFile, localize.dll + 0x6D80,
-bool, __fastcall, (void* pVguiLocalize, const char* path, const char* pathId, bool bIncludeFallbackSearchPaths))
-// clang-format on
-{
- // save this for later
- g_pVguiLocalize = pVguiLocalize;
-
- bool ret = CLocalize__AddFile(pVguiLocalize, path, pathId, bIncludeFallbackSearchPaths);
- if (ret)
- spdlog::info("Loaded localisation file {} successfully", path);
-
- return true;
-}
-
-// clang-format off
-AUTOHOOK(CLocalize__ReloadLocalizationFiles, localize.dll + 0xB830,
-void, __fastcall, (void* pVguiLocalize))
-// clang-format on
-{
- // load all mod localization manually, so we keep track of all files, not just previously loaded ones
- for (Mod mod : g_pModManager->m_LoadedMods)
- if (mod.m_bEnabled)
- for (std::string& localisationFile : mod.LocalisationFiles)
- CLocalize__AddFile(g_pVguiLocalize, localisationFile.c_str(), nullptr, false);
-
- spdlog::info("reloading localization...");
- CLocalize__ReloadLocalizationFiles(pVguiLocalize);
-}
-
-// clang-format off
-AUTOHOOK(CEngineVGui__Init, engine.dll + 0x247E10,
-void, __fastcall, (void* self))
-// clang-format on
-{
- CEngineVGui__Init(self); // this loads r1_english, valve_english, dev_english
-
- // previously we did this in CLocalize::AddFile, but for some reason it won't properly overwrite localization from
- // files loaded previously if done there, very weird but this works so whatever
- for (Mod mod : g_pModManager->m_LoadedMods)
- if (mod.m_bEnabled)
- for (std::string& localisationFile : mod.LocalisationFiles)
- CLocalize__AddFile(g_pVguiLocalize, localisationFile.c_str(), nullptr, false);
-}
-
-ON_DLL_LOAD_CLIENT("localize.dll", Localize, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/client/r2client.cpp b/NorthstarDLL/client/r2client.cpp
deleted file mode 100644
index c8e59d74..00000000
--- a/NorthstarDLL/client/r2client.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "r2client.h"
-
-char* g_pLocalPlayerUserID;
-char* g_pLocalPlayerOriginToken;
-GetBaseLocalClientType GetBaseLocalClient;
-
-ON_DLL_LOAD("engine.dll", R2EngineClient, (CModule module))
-{
- g_pLocalPlayerUserID = module.Offset(0x13F8E688).RCast<char*>();
- g_pLocalPlayerOriginToken = module.Offset(0x13979C80).RCast<char*>();
-
- GetBaseLocalClient = module.Offset(0x78200).RCast<GetBaseLocalClientType>();
-}
diff --git a/NorthstarDLL/client/r2client.h b/NorthstarDLL/client/r2client.h
deleted file mode 100644
index ea263dbc..00000000
--- a/NorthstarDLL/client/r2client.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-extern char* g_pLocalPlayerUserID;
-extern char* g_pLocalPlayerOriginToken;
-
-typedef void* (*GetBaseLocalClientType)();
-extern GetBaseLocalClientType GetBaseLocalClient;
diff --git a/NorthstarDLL/client/rejectconnectionfixes.cpp b/NorthstarDLL/client/rejectconnectionfixes.cpp
deleted file mode 100644
index 1b326a3c..00000000
--- a/NorthstarDLL/client/rejectconnectionfixes.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "engine/r2engine.h"
-
-AUTOHOOK_INIT()
-
-// this is called from when our connection is rejected, this is the only case we're hooking this for
-// clang-format off
-AUTOHOOK(COM_ExplainDisconnection, engine.dll + 0x1342F0,
-void,, (bool a1, const char* fmt, ...))
-// clang-format on
-{
- va_list va;
- va_start(va, fmt);
- char buf[4096];
- vsnprintf_s(buf, 4096, fmt, va);
- va_end(va);
-
- // slightly hacky comparison, but patching the function that calls this for reject would be worse
- if (!strncmp(fmt, "Connection rejected: ", 21))
- {
- // when COM_ExplainDisconnection is called from engine.dll + 19ff1c for connection rejected, it doesn't
- // call Host_Disconnect, which properly shuts down listen server
- // not doing this gets our client in a pretty weird state so we need to shut it down manually here
-
- // don't call Cbuf_Execute because we don't need this called immediately
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "disconnect", cmd_source_t::kCommandSrcCode);
- }
-
- return COM_ExplainDisconnection(a1, "%s", buf);
-}
-
-ON_DLL_LOAD_CLIENT("engine.dll", RejectConnectionFixes, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/config/profile.cpp b/NorthstarDLL/config/profile.cpp
deleted file mode 100644
index d5361efa..00000000
--- a/NorthstarDLL/config/profile.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "config/profile.h"
-#include "dedicated/dedicated.h"
-#include <string>
-
-std::string GetNorthstarPrefix()
-{
- return NORTHSTAR_FOLDER_PREFIX;
-}
-
-void InitialiseNorthstarPrefix()
-{
- char* clachar = strstr(GetCommandLineA(), "-profile=");
- if (clachar)
- {
- std::string cla = std::string(clachar);
- if (strncmp(cla.substr(9, 1).c_str(), "\"", 1))
- {
- int space = cla.find(" ");
- std::string dirname = cla.substr(9, space - 9);
- NORTHSTAR_FOLDER_PREFIX = dirname;
- }
- else
- {
- std::string quote = "\"";
- int quote1 = cla.find(quote);
- int quote2 = (cla.substr(quote1 + 1)).find(quote);
- std::string dirname = cla.substr(quote1 + 1, quote2);
- NORTHSTAR_FOLDER_PREFIX = dirname;
- }
- }
- else
- {
- NORTHSTAR_FOLDER_PREFIX = "R2Northstar";
- }
-
- // set the console title to show the current profile
- // dont do this on dedi as title contains useful information on dedi and setting title breaks it as well
- if (!IsDedicatedServer())
- SetConsoleTitleA((std::string("NorthstarLauncher | ") + NORTHSTAR_FOLDER_PREFIX).c_str());
-}
diff --git a/NorthstarDLL/config/profile.h b/NorthstarDLL/config/profile.h
deleted file mode 100644
index 9f3087fb..00000000
--- a/NorthstarDLL/config/profile.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-#include <string>
-
-static std::string NORTHSTAR_FOLDER_PREFIX;
-
-void InitialiseNorthstarPrefix();
-std::string GetNorthstarPrefix();
diff --git a/NorthstarDLL/core/convar/concommand.cpp b/NorthstarDLL/core/convar/concommand.cpp
deleted file mode 100644
index 41f54c76..00000000
--- a/NorthstarDLL/core/convar/concommand.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-#include "concommand.h"
-#include "shared/misccommands.h"
-#include "engine/r2engine.h"
-
-#include "plugins/pluginbackend.h"
-#include "plugins/plugin_abi.h"
-
-#include <iostream>
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns true if this is a command
-// Output : bool
-//-----------------------------------------------------------------------------
-bool ConCommand::IsCommand(void) const
-{
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns true if this is a command
-// Output : bool
-//-----------------------------------------------------------------------------
-bool ConCommandBase::IsCommand(void) const
-{
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Has this cvar been registered
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool ConCommandBase::IsRegistered(void) const
-{
- return m_bRegistered;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Test each ConCommand query before execution.
-// Input : *pCommandBase - nFlags
-// Output : False if execution is permitted, true if not.
-//-----------------------------------------------------------------------------
-bool ConCommandBase::IsFlagSet(int nFlags) const
-{
- return m_nFlags & nFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Checks if ConCommand has requested flags.
-// Input : nFlags -
-// Output : True if ConCommand has nFlags.
-//-----------------------------------------------------------------------------
-bool ConCommandBase::HasFlags(int nFlags)
-{
- return m_nFlags & nFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Add's flags to ConCommand.
-// Input : nFlags -
-//-----------------------------------------------------------------------------
-void ConCommandBase::AddFlags(int nFlags)
-{
- m_nFlags |= nFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Removes flags from ConCommand.
-// Input : nFlags -
-//-----------------------------------------------------------------------------
-void ConCommandBase::RemoveFlags(int nFlags)
-{
- m_nFlags &= ~nFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns current flags.
-// Output : int
-//-----------------------------------------------------------------------------
-int ConCommandBase::GetFlags(void) const
-{
- return m_nFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : const ConCommandBase
-//-----------------------------------------------------------------------------
-ConCommandBase* ConCommandBase::GetNext(void) const
-{
- return m_pNext;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the ConCommandBase help text.
-// Output : const char*
-//-----------------------------------------------------------------------------
-const char* ConCommandBase::GetHelpText(void) const
-{
- return m_pszHelpString;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Copies string using local new/delete operators
-// Input : *szFrom -
-// Output : char
-//-----------------------------------------------------------------------------
-char* ConCommandBase::CopyString(const char* szFrom) const
-{
- size_t nLen;
- char* szTo;
-
- nLen = strlen(szFrom);
- if (nLen <= 0)
- {
- szTo = new char[1];
- szTo[0] = 0;
- }
- else
- {
- szTo = new char[nLen + 1];
- memmove(szTo, szFrom, nLen + 1);
- }
- return szTo;
-}
-
-typedef void (*ConCommandConstructorType)(
- ConCommand* newCommand, const char* name, FnCommandCallback_t callback, const char* helpString, int flags, void* parent);
-ConCommandConstructorType ConCommandConstructor;
-
-void RegisterConCommand(const char* name, FnCommandCallback_t callback, const char* helpString, int flags)
-{
- spdlog::info("Registering ConCommand {}", name);
-
- // no need to free this ever really, it should exist as long as game does
- ConCommand* newCommand = new ConCommand;
- ConCommandConstructor(newCommand, name, callback, helpString, flags, nullptr);
-}
-
-void RegisterConCommand(
- const char* name, FnCommandCallback_t callback, const char* helpString, int flags, FnCommandCompletionCallback completionCallback)
-{
- spdlog::info("Registering ConCommand {}", name);
-
- // no need to free this ever really, it should exist as long as game does
- ConCommand* newCommand = new ConCommand;
- ConCommandConstructor(newCommand, name, callback, helpString, flags, nullptr);
- newCommand->m_pCompletionCallback = completionCallback;
-}
-
-ON_DLL_LOAD("engine.dll", ConCommand, (CModule module))
-{
- ConCommandConstructor = module.Offset(0x415F60).RCast<ConCommandConstructorType>();
- AddMiscConCommands();
-
- g_pPluginCommunicationhandler->m_sEngineData.ConCommandConstructor =
- reinterpret_cast<PluginConCommandConstructorType>(ConCommandConstructor);
-}
diff --git a/NorthstarDLL/core/convar/concommand.h b/NorthstarDLL/core/convar/concommand.h
deleted file mode 100644
index 71a82fec..00000000
--- a/NorthstarDLL/core/convar/concommand.h
+++ /dev/null
@@ -1,139 +0,0 @@
-#pragma once
-
-// From Source SDK
-class ConCommandBase;
-class IConCommandBaseAccessor
-{
-public:
- // Flags is a combination of FCVAR flags in cvar.h.
- // hOut is filled in with a handle to the variable.
- virtual bool RegisterConCommandBase(ConCommandBase* pVar) = 0;
-};
-
-class CCommand
-{
-public:
- CCommand() = delete;
-
- int64_t ArgC() const;
- const char** ArgV() const;
- const char* ArgS() const; // All args that occur after the 0th arg, in string form
- const char* GetCommandString() const; // The entire command in string form, including the 0th arg
- const char* operator[](int nIndex) const; // Gets at arguments
- const char* Arg(int nIndex) const; // Gets at arguments
-
- static int MaxCommandLength();
-
-private:
- enum
- {
- COMMAND_MAX_ARGC = 64,
- COMMAND_MAX_LENGTH = 512,
- };
-
- int64_t m_nArgc;
- int64_t m_nArgv0Size;
- char m_pArgSBuffer[COMMAND_MAX_LENGTH];
- char m_pArgvBuffer[COMMAND_MAX_LENGTH];
- const char* m_ppArgv[COMMAND_MAX_ARGC];
-};
-
-inline int CCommand::MaxCommandLength()
-{
- return COMMAND_MAX_LENGTH - 1;
-}
-inline int64_t CCommand::ArgC() const
-{
- return m_nArgc;
-}
-inline const char** CCommand::ArgV() const
-{
- return m_nArgc ? (const char**)m_ppArgv : NULL;
-}
-inline const char* CCommand::ArgS() const
-{
- return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : "";
-}
-inline const char* CCommand::GetCommandString() const
-{
- return m_nArgc ? m_pArgSBuffer : "";
-}
-inline const char* CCommand::Arg(int nIndex) const
-{
- // FIXME: Many command handlers appear to not be particularly careful
- // about checking for valid argc range. For now, we're going to
- // do the extra check and return an empty string if it's out of range
- if (nIndex < 0 || nIndex >= m_nArgc)
- return "";
- return m_ppArgv[nIndex];
-}
-inline const char* CCommand::operator[](int nIndex) const
-{
- return Arg(nIndex);
-}
-
-//-----------------------------------------------------------------------------
-// Called when a ConCommand needs to execute
-//-----------------------------------------------------------------------------
-typedef void (*FnCommandCallback_t)(const CCommand& command);
-
-#define COMMAND_COMPLETION_MAXITEMS 64
-#define COMMAND_COMPLETION_ITEM_LENGTH 128
-
-//-----------------------------------------------------------------------------
-// Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings
-//-----------------------------------------------------------------------------
-typedef int (*FnCommandCompletionCallback)(const char* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]);
-
-// From r5reloaded
-class ConCommandBase
-{
-public:
- bool HasFlags(int nFlags);
- void AddFlags(int nFlags);
- void RemoveFlags(int nFlags);
-
- bool IsCommand(void) const;
- bool IsRegistered(void) const;
- bool IsFlagSet(int nFlags) const;
- static bool IsFlagSet(ConCommandBase* pCommandBase, int nFlags); // For hooking to engine's implementation.
-
- int GetFlags(void) const;
- ConCommandBase* GetNext(void) const;
- const char* GetHelpText(void) const;
-
- char* CopyString(const char* szFrom) const;
-
- void* m_pConCommandBaseVTable; // 0x0000
- ConCommandBase* m_pNext; // 0x0008
- bool m_bRegistered; // 0x0010
- char pad_0011[7]; // 0x0011 <- 3 bytes padding + unk int32.
- const char* m_pszName; // 0x0018
- const char* m_pszHelpString; // 0x0020
- int m_nFlags; // 0x0028
- ConCommandBase* s_pConCommandBases; // 0x002C
- IConCommandBaseAccessor* s_pAccessor; // 0x0034
-}; // Size: 0x0040
-
-// taken from ttf2sdk
-class ConCommand : public ConCommandBase
-{
- friend class CCVar;
-
-public:
- ConCommand(void) {}; // !TODO: Rebuild engine constructor in SDK instead.
- ConCommand(const char* szName, const char* szHelpString, int nFlags, void* pCallback, void* pCommandCompletionCallback);
- void Init(void);
- bool IsCommand(void) const;
-
- FnCommandCallback_t m_pCommandCallback {}; // 0x0040 <- starts from 0x40 since we inherit ConCommandBase.
- FnCommandCompletionCallback m_pCompletionCallback {}; // 0x0048 <- defaults to sub_180417410 ('xor eax, eax').
- int m_nCallbackFlags {}; // 0x0050
- char pad_0054[4]; // 0x0054
- int unk0; // 0x0058
- int unk1; // 0x005C
-}; // Size: 0x0060
-
-void RegisterConCommand(const char* name, void (*callback)(const CCommand&), const char* helpString, int flags);
-void RegisterConCommand(
- const char* name, void (*callback)(const CCommand&), const char* helpString, int flags, FnCommandCompletionCallback completionCallback);
diff --git a/NorthstarDLL/core/convar/convar.cpp b/NorthstarDLL/core/convar/convar.cpp
deleted file mode 100644
index e77ae1fd..00000000
--- a/NorthstarDLL/core/convar/convar.cpp
+++ /dev/null
@@ -1,534 +0,0 @@
-#include "bits.h"
-#include "cvar.h"
-#include "convar.h"
-#include "core/sourceinterface.h"
-
-#include "plugins/pluginbackend.h"
-#include "plugins/plugin_abi.h"
-
-#include <float.h>
-
-typedef void (*ConVarRegisterType)(
- ConVar* pConVar,
- const char* pszName,
- const char* pszDefaultValue,
- int nFlags,
- const char* pszHelpString,
- bool bMin,
- float fMin,
- bool bMax,
- float fMax,
- void* pCallback);
-ConVarRegisterType conVarRegister;
-
-typedef void (*ConVarMallocType)(void* pConVarMaloc, int a2, int a3);
-ConVarMallocType conVarMalloc;
-
-void* g_pConVar_Vtable = nullptr;
-void* g_pIConVar_Vtable = nullptr;
-
-//-----------------------------------------------------------------------------
-// Purpose: ConVar interface initialization
-//-----------------------------------------------------------------------------
-ON_DLL_LOAD("engine.dll", ConVar, (CModule module))
-{
- conVarMalloc = module.Offset(0x415C20).RCast<ConVarMallocType>();
- conVarRegister = module.Offset(0x417230).RCast<ConVarRegisterType>();
-
- g_pConVar_Vtable = module.Offset(0x67FD28);
- g_pIConVar_Vtable = module.Offset(0x67FDC8);
-
- g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007");
- g_pCVar = *g_pCVarInterface;
-
- g_pPluginCommunicationhandler->m_sEngineData.conVarMalloc = reinterpret_cast<PluginConVarMallocType>(conVarMalloc);
- g_pPluginCommunicationhandler->m_sEngineData.conVarRegister = reinterpret_cast<PluginConVarRegisterType>(conVarRegister);
- g_pPluginCommunicationhandler->m_sEngineData.ConVar_Vtable = reinterpret_cast<void*>(g_pConVar_Vtable);
- g_pPluginCommunicationhandler->m_sEngineData.IConVar_Vtable = reinterpret_cast<void*>(g_pIConVar_Vtable);
- g_pPluginCommunicationhandler->m_sEngineData.g_pCVar = reinterpret_cast<void*>(g_pCVar);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: constructor
-//-----------------------------------------------------------------------------
-ConVar::ConVar(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString)
-{
- spdlog::info("Registering Convar {}", pszName);
-
- this->m_ConCommandBase.m_pConCommandBaseVTable = g_pConVar_Vtable;
- this->m_ConCommandBase.s_pConCommandBases = (ConCommandBase*)g_pIConVar_Vtable;
-
- conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar.
- conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, 0, 0, 0, 0, 0);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: constructor
-//-----------------------------------------------------------------------------
-ConVar::ConVar(
- const char* pszName,
- const char* pszDefaultValue,
- int nFlags,
- const char* pszHelpString,
- bool bMin,
- float fMin,
- bool bMax,
- float fMax,
- FnChangeCallback_t pCallback)
-{
- spdlog::info("Registering Convar {}", pszName);
-
- this->m_ConCommandBase.m_pConCommandBaseVTable = g_pConVar_Vtable;
- this->m_ConCommandBase.s_pConCommandBases = (ConCommandBase*)g_pIConVar_Vtable;
-
- conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar.
- conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, bMin, fMin, bMax, fMax, (void*)pCallback);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: destructor
-//-----------------------------------------------------------------------------
-ConVar::~ConVar(void)
-{
- if (m_Value.m_pszString)
- delete[] m_Value.m_pszString;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the base ConVar name.
-// Output : const char*
-//-----------------------------------------------------------------------------
-const char* ConVar::GetBaseName(void) const
-{
- return m_ConCommandBase.m_pszName;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the ConVar help text.
-// Output : const char*
-//-----------------------------------------------------------------------------
-const char* ConVar::GetHelpText(void) const
-{
- return m_ConCommandBase.m_pszHelpString;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Add's flags to ConVar.
-// Input : nFlags -
-//-----------------------------------------------------------------------------
-void ConVar::AddFlags(int nFlags)
-{
- m_ConCommandBase.m_nFlags |= nFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Removes flags from ConVar.
-// Input : nFlags -
-//-----------------------------------------------------------------------------
-void ConVar::RemoveFlags(int nFlags)
-{
- m_ConCommandBase.m_nFlags &= ~nFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return ConVar value as a boolean.
-// Output : bool
-//-----------------------------------------------------------------------------
-bool ConVar::GetBool(void) const
-{
- return !!GetInt();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return ConVar value as a float.
-// Output : float
-//-----------------------------------------------------------------------------
-float ConVar::GetFloat(void) const
-{
- return m_Value.m_fValue;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return ConVar value as an integer.
-// Output : int
-//-----------------------------------------------------------------------------
-int ConVar::GetInt(void) const
-{
- return m_Value.m_nValue;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return ConVar value as a color.
-// Output : Color
-//-----------------------------------------------------------------------------
-Color ConVar::GetColor(void) const
-{
- unsigned char* pColorElement = ((unsigned char*)&m_Value.m_nValue);
- return Color(pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3]);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return ConVar value as a string.
-// Output : const char *
-//-----------------------------------------------------------------------------
-const char* ConVar::GetString(void) const
-{
- if (m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING)
- {
- return "FCVAR_NEVER_AS_STRING";
- }
-
- char const* str = m_Value.m_pszString;
- return str ? str : "";
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : flMinVal -
-// Output : true if there is a min set.
-//-----------------------------------------------------------------------------
-bool ConVar::GetMin(float& flMinVal) const
-{
- flMinVal = m_fMinVal;
- return m_bHasMin;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : flMaxVal -
-// Output : true if there is a max set.
-//-----------------------------------------------------------------------------
-bool ConVar::GetMax(float& flMaxVal) const
-{
- flMaxVal = m_fMaxVal;
- return m_bHasMax;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: returns the min value.
-// Output : float
-//-----------------------------------------------------------------------------
-float ConVar::GetMinValue(void) const
-{
- return m_fMinVal;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: returns the max value.
-// Output : float
-//-----------------------------------------------------------------------------
-float ConVar::GetMaxValue(void) const
-{
- return m_fMaxVal;
- ;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: checks if ConVar has min value.
-// Output : bool
-//-----------------------------------------------------------------------------
-bool ConVar::HasMin(void) const
-{
- return m_bHasMin;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: checks if ConVar has max value.
-// Output : bool
-//-----------------------------------------------------------------------------
-bool ConVar::HasMax(void) const
-{
- return m_bHasMax;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets the ConVar int value.
-// Input : nValue -
-//-----------------------------------------------------------------------------
-void ConVar::SetValue(int nValue)
-{
- if (nValue == m_Value.m_nValue)
- {
- return;
- }
-
- float flValue = (float)nValue;
-
- // Check bounds.
- if (ClampValue(flValue))
- {
- nValue = (int)(flValue);
- }
-
- // Redetermine value.
- float flOldValue = m_Value.m_fValue;
- m_Value.m_fValue = flValue;
- m_Value.m_nValue = nValue;
-
- if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
- {
- char szTempValue[32];
- snprintf(szTempValue, sizeof(szTempValue), "%d", m_Value.m_nValue);
- ChangeStringValue(szTempValue, flOldValue);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets the ConVar float value.
-// Input : flValue -
-//-----------------------------------------------------------------------------
-void ConVar::SetValue(float flValue)
-{
- if (flValue == m_Value.m_fValue)
- {
- return;
- }
-
- // Check bounds.
- ClampValue(flValue);
-
- // Redetermine value.
- float flOldValue = m_Value.m_fValue;
- m_Value.m_fValue = flValue;
- m_Value.m_nValue = (int)m_Value.m_fValue;
-
- if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
- {
- char szTempValue[32];
- snprintf(szTempValue, sizeof(szTempValue), "%f", m_Value.m_fValue);
- ChangeStringValue(szTempValue, flOldValue);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets the ConVar string value.
-// Input : *szValue -
-//-----------------------------------------------------------------------------
-void ConVar::SetValue(const char* pszValue)
-{
- if (strcmp(this->m_Value.m_pszString, pszValue) == 0)
- return;
-
- char szTempValue[32] {};
- const char* pszNewValue {};
-
- float flOldValue = m_Value.m_fValue;
- pszNewValue = (char*)pszValue;
- if (!pszNewValue)
- {
- pszNewValue = "";
- }
-
- if (!SetColorFromString(pszValue))
- {
- // Not a color, do the standard thing
- float flNewValue = (float)atof(pszValue);
- if (!std::isfinite(flNewValue))
- {
- spdlog::warn("Warning: ConVar '{}' = '{}' is infinite, clamping value.\n", GetBaseName(), pszValue);
- flNewValue = FLT_MAX;
- }
-
- if (ClampValue(flNewValue))
- {
- snprintf(szTempValue, sizeof(szTempValue), "%f", flNewValue);
- pszNewValue = szTempValue;
- }
-
- // Redetermine value
- m_Value.m_fValue = flNewValue;
- m_Value.m_nValue = (int)(m_Value.m_fValue);
- }
-
- if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
- {
- ChangeStringValue(pszNewValue, flOldValue);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets the ConVar color value.
-// Input : clValue -
-//-----------------------------------------------------------------------------
-void ConVar::SetValue(Color clValue)
-{
- std::string svResult = "";
-
- for (int i = 0; i < 4; i++)
- {
- if (!(clValue.GetValue(i) == 0 && svResult.size() == 0))
- {
- svResult += std::to_string(clValue.GetValue(i));
- svResult.append(" ");
- }
- }
-
- this->m_Value.m_pszString = svResult.c_str();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: changes the ConVar string value.
-// Input : *pszTempVal - flOldValue
-//-----------------------------------------------------------------------------
-void ConVar::ChangeStringValue(const char* pszTempVal, float flOldValue)
-{
- assert(!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING));
-
- char* pszOldValue = (char*)_malloca(m_Value.m_iStringLength);
- if (pszOldValue != NULL)
- {
- memcpy(pszOldValue, m_Value.m_pszString, m_Value.m_iStringLength);
- }
-
- if (pszTempVal)
- {
- int len = strlen(pszTempVal) + 1;
-
- if (len > m_Value.m_iStringLength)
- {
- if (m_Value.m_pszString)
- delete[] m_Value.m_pszString;
-
- m_Value.m_pszString = new char[len];
- m_Value.m_iStringLength = len;
- }
-
- memcpy((char*)m_Value.m_pszString, pszTempVal, len);
- }
- else
- {
- m_Value.m_pszString = NULL;
- }
-
- pszOldValue = 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets the ConVar color value from string.
-// Input : *pszValue -
-//-----------------------------------------------------------------------------
-bool ConVar::SetColorFromString(const char* pszValue)
-{
- bool bColor = false;
-
- // Try pulling RGBA color values out of the string.
- int nRGBA[4] {};
- int nParamsRead = sscanf_s(pszValue, "%i %i %i %i", &(nRGBA[0]), &(nRGBA[1]), &(nRGBA[2]), &(nRGBA[3]));
-
- if (nParamsRead >= 3)
- {
- // This is probably a color!
- if (nParamsRead == 3)
- {
- // Assume they wanted full alpha.
- nRGBA[3] = 255;
- }
-
- if (nRGBA[0] >= 0 && nRGBA[0] <= 255 && nRGBA[1] >= 0 && nRGBA[1] <= 255 && nRGBA[2] >= 0 && nRGBA[2] <= 255 && nRGBA[3] >= 0 &&
- nRGBA[3] <= 255)
- {
- // printf("*** WOW! Found a color!! ***\n");
-
- // This is definitely a color!
- bColor = true;
-
- // Stuff all the values into each byte of our int.
- unsigned char* pColorElement = ((unsigned char*)&m_Value.m_nValue);
- pColorElement[0] = nRGBA[0];
- pColorElement[1] = nRGBA[1];
- pColorElement[2] = nRGBA[2];
- pColorElement[3] = nRGBA[3];
-
- // Copy that value into our float.
- m_Value.m_fValue = (float)(m_Value.m_nValue);
- }
- }
-
- return bColor;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Checks if ConVar is registered.
-// Output : bool
-//-----------------------------------------------------------------------------
-bool ConVar::IsRegistered(void) const
-{
- return m_ConCommandBase.m_bRegistered;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns true if this is a command
-// Output : bool
-//-----------------------------------------------------------------------------
-bool ConVar::IsCommand(void) const
-{
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Test each ConVar query before setting the value.
-// Input : nFlags
-// Output : False if change is permitted, true if not.
-//-----------------------------------------------------------------------------
-bool ConVar::IsFlagSet(int nFlags) const
-{
- return m_ConCommandBase.m_nFlags & nFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Check whether to clamp and then perform clamp.
-// Input : flValue -
-// Output : Returns true if value changed.
-//-----------------------------------------------------------------------------
-bool ConVar::ClampValue(float& flValue)
-{
- if (m_bHasMin && (flValue < m_fMinVal))
- {
- flValue = m_fMinVal;
- return true;
- }
-
- if (m_bHasMax && (flValue > m_fMaxVal))
- {
- flValue = m_fMaxVal;
- return true;
- }
-
- return false;
-}
-
-int ParseConVarFlagsString(std::string modName, std::string sFlags)
-{
- int iFlags = 0;
- std::stringstream stFlags(sFlags);
- std::string sFlag;
-
- while (std::getline(stFlags, sFlag, '|'))
- {
- // trim the flag
- sFlag.erase(sFlag.find_last_not_of(" \t\n\f\v\r") + 1);
- sFlag.erase(0, sFlag.find_first_not_of(" \t\n\f\v\r"));
-
- // skip if empty
- if (sFlag.empty())
- continue;
-
- // find the matching flag value
- bool ok = false;
- for (auto const& flagPair : g_PrintCommandFlags)
- {
- if (sFlag == flagPair.second)
- {
- iFlags |= flagPair.first;
- ok = true;
- break;
- }
- }
- if (!ok)
- {
- spdlog::warn("Mod ConCommand {} has unknown flag {}", modName, sFlag);
- }
- }
-
- return iFlags;
-}
diff --git a/NorthstarDLL/core/convar/convar.h b/NorthstarDLL/core/convar/convar.h
deleted file mode 100644
index f0366b46..00000000
--- a/NorthstarDLL/core/convar/convar.h
+++ /dev/null
@@ -1,194 +0,0 @@
-#pragma once
-#include "core/sourceinterface.h"
-#include "core/math/color.h"
-#include "cvar.h"
-#include "concommand.h"
-
-// taken directly from iconvar.h
-
-// The default, no flags at all
-#define FCVAR_NONE 0
-
-// Command to ConVars and ConCommands
-// ConVar Systems
-#define FCVAR_UNREGISTERED (1 << 0) // If this is set, don't add to linked list, etc.
-#define FCVAR_DEVELOPMENTONLY (1 << 1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined.
-#define FCVAR_GAMEDLL (1 << 2) // defined by the game DLL
-#define FCVAR_CLIENTDLL (1 << 3) // defined by the client DLL
-#define FCVAR_HIDDEN (1 << 4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out.
-
-// ConVar only
-#define FCVAR_PROTECTED \
- (1 << 5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as
- // value.
-#define FCVAR_SPONLY (1 << 6) // This cvar cannot be changed by clients connected to a multiplayer server.
-#define FCVAR_ARCHIVE (1 << 7) // set to cause it to be saved to vars.rc
-#define FCVAR_NOTIFY (1 << 8) // notifies players when changed
-#define FCVAR_USERINFO (1 << 9) // changes the client's info string
-
-#define FCVAR_PRINTABLEONLY (1 << 10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ).
-#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS \
- (1 << 10) // When on concommands this allows remote clients to execute this cmd on the server.
- // We are changing the default behavior of concommands to disallow execution by remote clients without
- // this flag due to the number existing concommands that can lag or crash the server when clients abuse them.
-
-#define FCVAR_UNLOGGED (1 << 11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log
-#define FCVAR_NEVER_AS_STRING (1 << 12) // never try to print that cvar
-
-// It's a ConVar that's shared between the client and the server.
-// At signon, the values of all such ConVars are sent from the server to the client (skipped for local client, of course )
-// If a change is requested it must come from the console (i.e., no remote client changes)
-// If a value is changed while a server is active, it's replicated to all connected clients
-#define FCVAR_REPLICATED (1 << 13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time
-#define FCVAR_CHEAT (1 << 14) // Only useable in singleplayer / debug / multiplayer & sv_cheats
-#define FCVAR_SS (1 << 15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated
-#define FCVAR_DEMO (1 << 16) // record this cvar when starting a demo file
-#define FCVAR_DONTRECORD (1 << 17) // don't record these command in demofiles
-#define FCVAR_SS_ADDED (1 << 18) // This is one of the "added" FCVAR_SS variables for the splitscreen players
-#define FCVAR_RELEASE (1 << 19) // Cvars tagged with this are the only cvars avaliable to customers
-#define FCVAR_RELOAD_MATERIALS (1 << 20) // If this cvar changes, it forces a material reload
-#define FCVAR_RELOAD_TEXTURES (1 << 21) // If this cvar changes, if forces a texture reload
-
-#define FCVAR_NOT_CONNECTED (1 << 22) // cvar cannot be changed by a client that is connected to a server
-#define FCVAR_MATERIAL_SYSTEM_THREAD (1 << 23) // Indicates this cvar is read from the material system thread
-#define FCVAR_ARCHIVE_PLAYERPROFILE (1 << 24) // respawn-defined flag, same as FCVAR_ARCHIVE but writes to profile.cfg
-
-#define FCVAR_SERVER_CAN_EXECUTE \
- (1 << 28) // the server is allowed to execute this command on clients via
- // ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd.
-#define FCVAR_SERVER_CANNOT_QUERY \
- (1 << 29) // If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue).
-
-// !!!NOTE!!! : this is likely incorrect, there are multiple concommands that the vanilla game registers with this flag that 100% should not
-// be remotely executable i.e. multiple commands that only exist on client (screenshot, joystick_initialize) we now use
-// FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS in all places this flag was previously used
-#define FCVAR_CLIENTCMD_CAN_EXECUTE \
- (1 << 30) // IVEngineClient::ClientCmd is allowed to execute this command.
- // Note: IVEngineClient::ClientCmd_Unrestricted can run any client command.
-
-#define FCVAR_ACCESSIBLE_FROM_THREADS (1 << 25) // used as a debugging tool necessary to check material system thread convars
-
-// TODO: could be cool to repurpose these for northstar use somehow?
-// #define FCVAR_AVAILABLE (1<<26)
-// #define FCVAR_AVAILABLE (1<<27)
-// #define FCVAR_AVAILABLE (1<<31)
-
-// flag => string stuff
-const std::multimap<int, const char*> g_PrintCommandFlags = {
- {FCVAR_UNREGISTERED, "UNREGISTERED"},
- {FCVAR_DEVELOPMENTONLY, "DEVELOPMENTONLY"},
- {FCVAR_GAMEDLL, "GAMEDLL"},
- {FCVAR_CLIENTDLL, "CLIENTDLL"},
- {FCVAR_HIDDEN, "HIDDEN"},
- {FCVAR_PROTECTED, "PROTECTED"},
- {FCVAR_SPONLY, "SPONLY"},
- {FCVAR_ARCHIVE, "ARCHIVE"},
- {FCVAR_NOTIFY, "NOTIFY"},
- {FCVAR_USERINFO, "USERINFO"},
-
- // TODO: PRINTABLEONLY and GAMEDLL_FOR_REMOTE_CLIENTS are both 1<<10, one is for vars and one is for commands
- // this fucking sucks i think
- {FCVAR_PRINTABLEONLY, "PRINTABLEONLY"},
- {FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS, "GAMEDLL_FOR_REMOTE_CLIENTS"},
-
- {FCVAR_UNLOGGED, "UNLOGGED"},
- {FCVAR_NEVER_AS_STRING, "NEVER_AS_STRING"},
- {FCVAR_REPLICATED, "REPLICATED"},
- {FCVAR_CHEAT, "CHEAT"},
- {FCVAR_SS, "SS"},
- {FCVAR_DEMO, "DEMO"},
- {FCVAR_DONTRECORD, "DONTRECORD"},
- {FCVAR_SS_ADDED, "SS_ADDED"},
- {FCVAR_RELEASE, "RELEASE"},
- {FCVAR_RELOAD_MATERIALS, "RELOAD_MATERIALS"},
- {FCVAR_RELOAD_TEXTURES, "RELOAD_TEXTURES"},
- {FCVAR_NOT_CONNECTED, "NOT_CONNECTED"},
- {FCVAR_MATERIAL_SYSTEM_THREAD, "MATERIAL_SYSTEM_THREAD"},
- {FCVAR_ARCHIVE_PLAYERPROFILE, "ARCHIVE_PLAYERPROFILE"},
- {FCVAR_SERVER_CAN_EXECUTE, "SERVER_CAN_EXECUTE"},
- {FCVAR_SERVER_CANNOT_QUERY, "SERVER_CANNOT_QUERY"},
- {FCVAR_CLIENTCMD_CAN_EXECUTE, "UNKNOWN"},
- {FCVAR_ACCESSIBLE_FROM_THREADS, "ACCESSIBLE_FROM_THREADS"}};
-
-//-----------------------------------------------------------------------------
-// Forward declarations
-//-----------------------------------------------------------------------------
-class ConCommandBase;
-class ConCommand;
-class ConVar;
-
-typedef void (*FnChangeCallback_t)(ConVar* var, const char* pOldValue, float flOldValue);
-
-//-----------------------------------------------------------------------------
-// Purpose: A console variable
-//-----------------------------------------------------------------------------
-class ConVar
-{
-public:
- ConVar(void) {};
- ConVar(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString);
- ConVar(
- const char* pszName,
- const char* pszDefaultValue,
- int nFlags,
- const char* pszHelpString,
- bool bMin,
- float fMin,
- bool bMax,
- float fMax,
- FnChangeCallback_t pCallback);
- ~ConVar(void);
-
- const char* GetBaseName(void) const;
- const char* GetHelpText(void) const;
-
- void AddFlags(int nFlags);
- void RemoveFlags(int nFlags);
-
- bool GetBool(void) const;
- float GetFloat(void) const;
- int GetInt(void) const;
- Color GetColor(void) const;
- const char* GetString(void) const;
-
- bool GetMin(float& flMinValue) const;
- bool GetMax(float& flMaxValue) const;
- float GetMinValue(void) const;
- float GetMaxValue(void) const;
-
- bool HasMin(void) const;
- bool HasMax(void) const;
-
- void SetValue(int nValue);
- void SetValue(float flValue);
- void SetValue(const char* pszValue);
- void SetValue(Color clValue);
-
- void ChangeStringValue(const char* pszTempValue, float flOldValue);
- bool SetColorFromString(const char* pszValue);
- bool ClampValue(float& value);
-
- bool IsRegistered(void) const;
- bool IsCommand(void) const;
- bool IsFlagSet(int nFlags) const;
-
- struct CVValue_t
- {
- const char* m_pszString;
- int64_t m_iStringLength;
- float m_fValue;
- int m_nValue;
- };
-
- ConCommandBase m_ConCommandBase {}; // 0x0000
- const char* m_pszDefaultValue {}; // 0x0040
- CVValue_t m_Value {}; // 0x0048
- bool m_bHasMin {}; // 0x005C
- float m_fMinVal {}; // 0x0060
- bool m_bHasMax {}; // 0x0064
- float m_fMaxVal {}; // 0x0068
- void* m_pMalloc {}; // 0x0070
- char m_pPad80[10] {}; // 0x0080
-}; // Size: 0x0080
-
-int ParseConVarFlagsString(std::string modName, std::string flags);
diff --git a/NorthstarDLL/core/convar/cvar.cpp b/NorthstarDLL/core/convar/cvar.cpp
deleted file mode 100644
index aa5f0365..00000000
--- a/NorthstarDLL/core/convar/cvar.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "cvar.h"
-#include "convar.h"
-#include "concommand.h"
-
-//-----------------------------------------------------------------------------
-// Purpose: returns all ConVars
-//-----------------------------------------------------------------------------
-std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap()
-{
- std::stringstream ss;
- CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocate new InternalIterator.
-
- std::unordered_map<std::string, ConCommandBase*> allConVars;
-
- for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances.
- {
- ConCommandBase* pCommand = itint->Get();
- const char* pszCommandName = pCommand->m_pszName;
- allConVars[pszCommandName] = pCommand;
- }
-
- return allConVars;
-}
-
-SourceInterface<CCvar>* g_pCVarInterface;
-CCvar* g_pCVar;
diff --git a/NorthstarDLL/core/convar/cvar.h b/NorthstarDLL/core/convar/cvar.h
deleted file mode 100644
index beaa84f4..00000000
--- a/NorthstarDLL/core/convar/cvar.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-#include "convar.h"
-
-//-----------------------------------------------------------------------------
-// Forward declarations
-//-----------------------------------------------------------------------------
-class ConCommandBase;
-class ConCommand;
-class ConVar;
-
-//-----------------------------------------------------------------------------
-// Internals for ICVarIterator
-//-----------------------------------------------------------------------------
-class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function.
-{
-public:
- virtual void SetFirst(void) = 0; // 0
- virtual void Next(void) = 0; // 1
- virtual bool IsValid(void) = 0; // 2
- virtual ConCommandBase* Get(void) = 0; // 3
-};
-
-//-----------------------------------------------------------------------------
-// Default implementation
-//-----------------------------------------------------------------------------
-class CCvar
-{
-public:
- M_VMETHOD(ConCommandBase*, FindCommandBase, 14, (const char* pszCommandName), (this, pszCommandName));
- M_VMETHOD(ConVar*, FindVar, 16, (const char* pszVarName), (this, pszVarName));
- M_VMETHOD(ConCommand*, FindCommand, 18, (const char* pszCommandName), (this, pszCommandName));
- M_VMETHOD(CCVarIteratorInternal*, FactoryInternalIterator, 41, (), (this));
-
- std::unordered_map<std::string, ConCommandBase*> DumpToMap();
-};
-
-extern SourceInterface<CCvar>* g_pCVarInterface;
-extern CCvar* g_pCVar;
diff --git a/NorthstarDLL/core/filesystem/filesystem.cpp b/NorthstarDLL/core/filesystem/filesystem.cpp
deleted file mode 100644
index b39939e4..00000000
--- a/NorthstarDLL/core/filesystem/filesystem.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-#include "filesystem.h"
-#include "core/sourceinterface.h"
-#include "mods/modmanager.h"
-
-#include <iostream>
-#include <sstream>
-
-AUTOHOOK_INIT()
-
-bool bReadingOriginalFile = false;
-std::string sCurrentModPath;
-
-ConVar* Cvar_ns_fs_log_reads;
-
-SourceInterface<IFileSystem>* g_pFilesystem;
-
-std::string ReadVPKFile(const char* path)
-{
- // read scripts.rson file, todo: check if this can be overwritten
- FileHandle_t fileHandle = (*g_pFilesystem)->m_vtable2->Open(&(*g_pFilesystem)->m_vtable2, path, "rb", "GAME", 0);
-
- std::stringstream fileStream;
- int bytesRead = 0;
- char data[4096];
- do
- {
- bytesRead = (*g_pFilesystem)->m_vtable2->Read(&(*g_pFilesystem)->m_vtable2, data, (int)std::size(data), fileHandle);
- fileStream.write(data, bytesRead);
- } while (bytesRead == std::size(data));
-
- (*g_pFilesystem)->m_vtable2->Close(*g_pFilesystem, fileHandle);
-
- return fileStream.str();
-}
-
-std::string ReadVPKOriginalFile(const char* path)
-{
- // todo: should probably set search path to be g_pModName here also
-
- bReadingOriginalFile = true;
- std::string ret = ReadVPKFile(path);
- bReadingOriginalFile = false;
-
- return ret;
-}
-
-// clang-format off
-HOOK(AddSearchPathHook, AddSearchPath,
-void, __fastcall, (IFileSystem * fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType))
-// clang-format on
-{
- AddSearchPath(fileSystem, pPath, pathID, addType);
-
- // make sure current mod paths are at head
- if (!strcmp(pathID, "GAME") && sCurrentModPath.compare(pPath) && addType == PATH_ADD_TO_HEAD)
- {
- AddSearchPath(fileSystem, sCurrentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD);
- AddSearchPath(fileSystem, GetCompiledAssetsPath().string().c_str(), "GAME", PATH_ADD_TO_HEAD);
- }
-}
-
-void SetNewModSearchPaths(Mod* mod)
-{
- // 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)
- {
- if ((fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().compare(sCurrentModPath))
- {
- AddSearchPath(
- &*(*g_pFilesystem), (fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
- sCurrentModPath = (fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string();
- }
- }
- else // push compiled to head
- AddSearchPath(&*(*g_pFilesystem), fs::absolute(GetCompiledAssetsPath()).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
-}
-
-bool TryReplaceFile(const char* pPath, bool shouldCompile)
-{
- if (bReadingOriginalFile)
- return false;
-
- if (shouldCompile)
- g_pModManager->CompileAssetsForFile(pPath);
-
- // idk how efficient the lexically normal check is
- // can't just set all /s in path to \, since some paths aren't in writeable memory
- auto file = g_pModManager->m_ModFiles.find(g_pModManager->NormaliseModFilePath(fs::path(pPath)));
- if (file != g_pModManager->m_ModFiles.end())
- {
- SetNewModSearchPaths(file->second.m_pOwningMod);
- return true;
- }
-
- return false;
-}
-
-// force modded files to be read from mods, not cache
-// clang-format off
-HOOK(ReadFromCacheHook, ReadFromCache,
-bool, __fastcall, (IFileSystem * filesystem, char* pPath, void* result))
-// clang-format off
-{
- if (TryReplaceFile(pPath, true))
- return false;
-
- return ReadFromCache(filesystem, pPath, result);
-}
-
-// force modded files to be read from mods, not vpk
-// clang-format off
-AUTOHOOK(ReadFileFromVPK, filesystem_stdio.dll + 0x5CBA0,
-FileHandle_t, __fastcall, (VPKData* vpkInfo, uint64_t* b, char* filename))
-// clang-format on
-{
- // don't compile here because this is only ever called from OpenEx, which already compiles
- if (TryReplaceFile(filename, false))
- {
- *b = -1;
- return b;
- }
-
- return ReadFileFromVPK(vpkInfo, b, filename);
-}
-
-// clang-format off
-AUTOHOOK(CBaseFileSystem__OpenEx, filesystem_stdio.dll + 0x15F50,
-FileHandle_t, __fastcall, (IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char **ppszResolvedFilename))
-// clang-format on
-{
- TryReplaceFile(pPath, true);
- return CBaseFileSystem__OpenEx(filesystem, pPath, pOptions, flags, pPathID, ppszResolvedFilename);
-}
-
-HOOK(MountVPKHook, MountVPK, VPKData*, , (IFileSystem * fileSystem, const char* pVpkPath))
-{
- NS::log::fs->info("MountVPK {}", pVpkPath);
- VPKData* ret = MountVPK(fileSystem, pVpkPath);
-
- for (Mod mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- for (ModVPKEntry& vpkEntry : mod.Vpks)
- {
- // if we're autoloading, just load no matter what
- if (!vpkEntry.m_bAutoLoad)
- {
- // resolve vpk name and try to load one with the same name
- // todo: we should be unloading these on map unload manually
- std::string mapName(fs::path(pVpkPath).filename().string());
- std::string modMapName(fs::path(vpkEntry.m_sVpkPath.c_str()).filename().string());
- if (mapName.compare(modMapName))
- continue;
- }
-
- VPKData* loaded = MountVPK(fileSystem, vpkEntry.m_sVpkPath.c_str());
- if (!ret) // this is primarily for map vpks and stuff, so the map's vpk is what gets returned from here
- ret = loaded;
- }
- }
-
- return ret;
-}
-
-ON_DLL_LOAD("filesystem_stdio.dll", Filesystem, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- g_pFilesystem = new SourceInterface<IFileSystem>("filesystem_stdio.dll", "VFileSystem017");
-
- AddSearchPathHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->AddSearchPath);
- ReadFromCacheHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->ReadFromCache);
- MountVPKHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->MountVPK);
-}
diff --git a/NorthstarDLL/core/filesystem/filesystem.h b/NorthstarDLL/core/filesystem/filesystem.h
deleted file mode 100644
index fcd1bb2f..00000000
--- a/NorthstarDLL/core/filesystem/filesystem.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#pragma once
-#include "core/sourceinterface.h"
-
-// taken from ttf2sdk
-typedef void* FileHandle_t;
-
-#pragma pack(push, 1)
-
-// clang-format off
-OFFSET_STRUCT(VPKFileEntry)
-{
- STRUCT_SIZE(0x44);
- FIELDS(0x0,
- char* directory;
- char* filename;
- char* extension;
- )
-};
-// clang-format on
-#pragma pack(pop)
-
-struct VPKData;
-
-enum SearchPathAdd_t
-{
- PATH_ADD_TO_HEAD, // First path searched
- PATH_ADD_TO_TAIL, // Last path searched
-};
-
-class CSearchPath
-{
-public:
- unsigned char unknown[0x18];
- const char* debugPath;
-};
-
-class IFileSystem
-{
-public:
- struct VTable
- {
- void* unknown[10];
- void (*AddSearchPath)(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType);
- void* unknown2[84];
- bool (*ReadFromCache)(IFileSystem* fileSystem, const char* path, void* result);
- void* unknown3[15];
- VPKData* (*MountVPK)(IFileSystem* fileSystem, const char* vpkPath);
- };
-
- struct VTable2
- {
- int (*Read)(IFileSystem::VTable2** fileSystem, void* pOutput, int size, FileHandle_t file);
- void* unknown[1];
- FileHandle_t (*Open)(
- IFileSystem::VTable2** fileSystem, const char* pFileName, const char* pOptions, const char* pathID, int64_t unknown);
- void (*Close)(IFileSystem* fileSystem, FileHandle_t file);
- long long (*Seek)(IFileSystem::VTable2** fileSystem, FileHandle_t file, long long offset, long long whence);
- void* unknown2[5];
- bool (*FileExists)(IFileSystem::VTable2** fileSystem, const char* pFileName, const char* pPathID);
- };
-
- VTable* m_vtable;
- VTable2* m_vtable2;
-};
-
-extern SourceInterface<IFileSystem>* g_pFilesystem;
-
-std::string ReadVPKFile(const char* path);
-std::string ReadVPKOriginalFile(const char* path);
diff --git a/NorthstarDLL/core/filesystem/rpakfilesystem.cpp b/NorthstarDLL/core/filesystem/rpakfilesystem.cpp
deleted file mode 100644
index da72646b..00000000
--- a/NorthstarDLL/core/filesystem/rpakfilesystem.cpp
+++ /dev/null
@@ -1,347 +0,0 @@
-#include "rpakfilesystem.h"
-#include "mods/modmanager.h"
-#include "dedicated/dedicated.h"
-#include "core/tier0.h"
-
-AUTOHOOK_INIT()
-
-// there are more i'm just too lazy to add
-struct PakLoadFuncs
-{
- void* unk0[3];
- int (*LoadPakAsync)(const char* pPath, void* unknownSingleton, int flags, void* callback0, void* callback1);
- void* unk1[2];
- void* (*UnloadPak)(int iPakHandle, void* callback);
- void* unk2[6];
- void* (*LoadFile)(const char* path); // unsure
- void* unk3[10];
- void* (*ReadFileAsync)(const char* pPath, void* a2);
-};
-
-PakLoadFuncs* g_pakLoadApi;
-
-PakLoadManager* g_pPakLoadManager;
-void** pUnknownPakLoadSingleton;
-
-int PakLoadManager::LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource)
-{
- int nHandle = g_pakLoadApi->LoadPakAsync(pPath, *pUnknownPakLoadSingleton, 2, nullptr, nullptr);
-
- // set the load source of the pak we just loaded
- if (nHandle != -1)
- GetPakInfo(nHandle)->m_nLoadSource = nLoadSource;
-
- return nHandle;
-}
-
-void PakLoadManager::UnloadPak(const int nPakHandle)
-{
- g_pakLoadApi->UnloadPak(nPakHandle, nullptr);
-}
-
-void PakLoadManager::UnloadMapPaks()
-{
- for (auto& pair : m_vLoadedPaks)
- if (pair.second.m_nLoadSource == ePakLoadSource::MAP)
- UnloadPak(pair.first);
-}
-
-LoadedPak* PakLoadManager::TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash)
-{
- LoadedPak pak;
- pak.m_nLoadSource = nLoadSource;
- pak.m_nPakHandle = nPakHandle;
- pak.m_nPakNameHash = nPakNameHash;
-
- m_vLoadedPaks.insert(std::make_pair(nPakHandle, pak));
- return &m_vLoadedPaks.at(nPakHandle);
-}
-
-void PakLoadManager::RemoveLoadedPak(int nPakHandle)
-{
- m_vLoadedPaks.erase(nPakHandle);
-}
-
-LoadedPak* PakLoadManager::GetPakInfo(const int nPakHandle)
-{
- return &m_vLoadedPaks.at(nPakHandle);
-}
-
-int PakLoadManager::GetPakHandle(const size_t nPakNameHash)
-{
- for (auto& pair : m_vLoadedPaks)
- if (pair.second.m_nPakNameHash == nPakNameHash)
- return pair.first;
-
- return -1;
-}
-
-int PakLoadManager::GetPakHandle(const char* pPath)
-{
- return GetPakHandle(STR_HASH(pPath));
-}
-
-void* PakLoadManager::LoadFile(const char* path)
-{
- return g_pakLoadApi->LoadFile(path);
-}
-
-void HandlePakAliases(char** map)
-{
- // convert the pak being loaded to it's aliased one, e.g. aliasing mp_hub_timeshift => sp_hub_timeshift
- for (int64_t i = g_pModManager->m_LoadedMods.size() - 1; i > -1; i--)
- {
- Mod* mod = &g_pModManager->m_LoadedMods[i];
- if (!mod->m_bEnabled)
- continue;
-
- if (mod->RpakAliases.find(*map) != mod->RpakAliases.end())
- {
- *map = &mod->RpakAliases[*map][0];
- return;
- }
- }
-}
-
-void LoadPreloadPaks()
-{
- // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
-
- for (ModRpakEntry& pak : mod.Rpaks)
- if (pak.m_bAutoLoad)
- g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::CONSTANT);
- }
-}
-
-void LoadPostloadPaks(const char* pPath)
-{
- // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
-
- for (ModRpakEntry& pak : mod.Rpaks)
- if (pak.m_sLoadAfterPak == pPath)
- g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::CONSTANT);
- }
-}
-
-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_pModManager->m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
-
- 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_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::MAP);
- }
- }
- }
-}
-
-// clang-format off
-HOOK(LoadPakAsyncHook, LoadPakAsync,
-int, __fastcall, (char* pPath, void* unknownSingleton, int flags, void* pCallback0, void* pCallback1))
-// clang-format on
-{
- HandlePakAliases(&pPath);
-
- // dont load the pak if it's currently loaded already
- size_t nPathHash = STR_HASH(pPath);
- if (g_pPakLoadManager->GetPakHandle(nPathHash) != -1)
- return -1;
-
- bool bNeedToFreePakName = false;
-
- static bool bShouldLoadPaks = true;
- if (bShouldLoadPaks)
- {
- // make a copy of the path for comparing to determine whether we should load this pak on dedi, before it could get overwritten by
- // LoadCustomMapPaks
- std::string originalPath(pPath);
-
- // disable preloading while we're doing this
- bShouldLoadPaks = false;
-
- LoadPreloadPaks();
- LoadCustomMapPaks(&pPath, &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
-
- // dedicated only needs common, common_mp, common_sp, and sp_<map> rpaks
- // sp_<map> rpaks contain tutorial ghost data
- // sucks to have to load the entire rpak for that but sp was never meant to be done on dedi
- if (IsDedicatedServer() &&
- (CommandLine()->CheckParm("-nopakdedi") || strncmp(&originalPath[0], "common", 6) && strncmp(&originalPath[0], "sp_", 3)))
- {
- if (bNeedToFreePakName)
- delete[] pPath;
-
- NS::log::rpak->info("Not loading pak {} for dedicated server", originalPath);
- return -1;
- }
- }
-
- int iPakHandle = LoadPakAsync(pPath, unknownSingleton, flags, pCallback0, pCallback1);
- NS::log::rpak->info("LoadPakAsync {} {}", pPath, iPakHandle);
-
- // trak the pak
- g_pPakLoadManager->TrackLoadedPak(ePakLoadSource::UNTRACKED, iPakHandle, nPathHash);
- LoadPostloadPaks(pPath);
-
- if (bNeedToFreePakName)
- delete[] pPath;
-
- return iPakHandle;
-}
-
-// clang-format off
-HOOK(UnloadPakHook, UnloadPak,
-void*, __fastcall, (int nPakHandle, void* pCallback))
-// clang-format on
-{
- // stop tracking the pak
- g_pPakLoadManager->RemoveLoadedPak(nPakHandle);
-
- static bool bShouldUnloadPaks = true;
- if (bShouldUnloadPaks)
- {
- bShouldUnloadPaks = false;
- g_pPakLoadManager->UnloadMapPaks();
- bShouldUnloadPaks = true;
- }
-
- NS::log::rpak->info("UnloadPak {}", nPakHandle);
- return UnloadPak(nPakHandle, pCallback);
-}
-
-// we hook this exclusively for resolving stbsp paths, but seemingly it's also used for other stuff like vpk, rpak, mprj and starpak loads
-// tbh this actually might be for memory mapped files or something, would make sense i think
-// clang-format off
-HOOK(ReadFileAsyncHook, ReadFileAsync,
-void*, __fastcall, (const char* pPath, void* pCallback))
-// clang-format on
-{
- fs::path path(pPath);
- std::string newPath = "";
- fs::path filename = path.filename();
-
- if (path.extension() == ".stbsp")
- {
- if (IsDedicatedServer())
- return nullptr;
-
- NS::log::rpak->info("LoadStreamBsp: {}", filename.string());
-
- // resolve modded stbsp path so we can load mod stbsps
- auto modFile = g_pModManager->m_ModFiles.find(g_pModManager->NormaliseModFilePath(fs::path("maps" / filename)));
- if (modFile != g_pModManager->m_ModFiles.end())
- {
- newPath = (modFile->second.m_pOwningMod->m_ModDirectory / "mod" / modFile->second.m_Path).string();
- pPath = newPath.c_str();
- }
- }
- else if (path.extension() == ".starpak")
- {
- if (IsDedicatedServer())
- return nullptr;
-
- // code for this is mostly stolen from above
-
- // unfortunately I can't find a way to get the rpak that is causing this function call, so I have to
- // store them on mod init and then compare the current path with the stored paths
-
- // game adds r2\ to every path, so assume that a starpak path that begins with r2\paks\ is a vanilla one
- // modded starpaks will be in the mod's paks folder (but can be in folders within the paks folder)
-
- // this might look a bit confusing, but its just an iterator over the various directories in a path.
- // path.begin() being the first directory, r2 in this case, which is guaranteed anyway,
- // so increment the iterator with ++ to get the first actual directory, * just gets the actual value
- // then we compare to "paks" to determine if it's a vanilla rpak or not
- if (*++path.begin() != "paks")
- {
- // remove the r2\ from the start used for path lookups
- std::string starpakPath = path.string().substr(3);
- // hash the starpakPath to compare with stored entries
- size_t hashed = STR_HASH(starpakPath);
-
- // loop through all loaded mods
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- // ignore non-loaded mods
- if (!mod.m_bEnabled)
- continue;
-
- // loop through the stored starpak paths
- for (size_t hash : mod.StarpakPaths)
- {
- if (hash == hashed)
- {
- // construct new path
- newPath = (mod.m_ModDirectory / "paks" / starpakPath).string();
- // set path to the new path
- pPath = newPath.c_str();
- goto LOG_STARPAK;
- }
- }
- }
- }
-
- LOG_STARPAK:
- NS::log::rpak->info("LoadStreamPak: {}", filename.string());
- }
-
- return ReadFileAsync(pPath, pCallback);
-}
-
-ON_DLL_LOAD("engine.dll", RpakFilesystem, (CModule module))
-{
- AUTOHOOK_DISPATCH();
-
- g_pPakLoadManager = new PakLoadManager;
-
- g_pakLoadApi = module.Offset(0x5BED78).Deref().RCast<PakLoadFuncs*>();
- pUnknownPakLoadSingleton = module.Offset(0x7C5E20).RCast<void**>();
-
- LoadPakAsyncHook.Dispatch((LPVOID*)g_pakLoadApi->LoadPakAsync);
- UnloadPakHook.Dispatch((LPVOID*)g_pakLoadApi->UnloadPak);
- ReadFileAsyncHook.Dispatch((LPVOID*)g_pakLoadApi->ReadFileAsync);
-}
diff --git a/NorthstarDLL/core/filesystem/rpakfilesystem.h b/NorthstarDLL/core/filesystem/rpakfilesystem.h
deleted file mode 100644
index bcd57a73..00000000
--- a/NorthstarDLL/core/filesystem/rpakfilesystem.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-enum class ePakLoadSource
-{
- UNTRACKED = -1, // not a pak we loaded, we shouldn't touch this one
-
- CONSTANT, // should be loaded at all times
- MAP // loaded from a map, should be unloaded when the map is unloaded
-};
-
-struct LoadedPak
-{
- ePakLoadSource m_nLoadSource;
- int m_nPakHandle;
- size_t m_nPakNameHash;
-};
-
-class PakLoadManager
-{
-private:
- std::map<int, LoadedPak> m_vLoadedPaks {};
- std::unordered_map<size_t, int> m_HashToPakHandle {};
-
-public:
- int LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource);
- void UnloadPak(const int nPakHandle);
- void UnloadMapPaks();
- void* LoadFile(const char* path); // this is a guess
-
- LoadedPak* TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash);
- void RemoveLoadedPak(int nPakHandle);
-
- LoadedPak* GetPakInfo(const int nPakHandle);
-
- int GetPakHandle(const size_t nPakNameHash);
- int GetPakHandle(const char* pPath);
-};
-
-extern PakLoadManager* g_pPakLoadManager;
diff --git a/NorthstarDLL/core/hooks.cpp b/NorthstarDLL/core/hooks.cpp
deleted file mode 100644
index 26b3fe57..00000000
--- a/NorthstarDLL/core/hooks.cpp
+++ /dev/null
@@ -1,474 +0,0 @@
-#include "dedicated/dedicated.h"
-#include "plugins/pluginbackend.h"
-
-#include <iostream>
-#include <wchar.h>
-#include <iostream>
-#include <vector>
-#include <fstream>
-#include <sstream>
-#include <filesystem>
-#include <Psapi.h>
-
-#define XINPUT1_3_DLL "XInput1_3.dll"
-
-AUTOHOOK_INIT()
-
-// called from the ON_DLL_LOAD macros
-__dllLoadCallback::__dllLoadCallback(
- eDllLoadCallbackSide side, const std::string dllName, DllLoadCallbackFuncType callback, std::string uniqueStr, std::string reliesOn)
-{
- // parse reliesOn array from string
- std::vector<std::string> reliesOnArray;
-
- if (reliesOn.length() && reliesOn[0] != '(')
- {
- reliesOnArray.push_back(reliesOn);
- }
- else
- {
- // follows the format (tag, tag, tag)
- std::string sCurrentTag;
- for (int i = 1; i < reliesOn.length(); i++)
- {
- if (!isspace(reliesOn[i]))
- {
- if (reliesOn[i] == ',' || reliesOn[i] == ')')
- {
- reliesOnArray.push_back(sCurrentTag);
- sCurrentTag = "";
- }
- else
- sCurrentTag += reliesOn[i];
- }
- }
- }
-
- switch (side)
- {
- case eDllLoadCallbackSide::UNSIDED:
- {
- AddDllLoadCallback(dllName, callback, uniqueStr, reliesOnArray);
- break;
- }
-
- case eDllLoadCallbackSide::CLIENT:
- {
- AddDllLoadCallbackForClient(dllName, callback, uniqueStr, reliesOnArray);
- break;
- }
-
- case eDllLoadCallbackSide::DEDICATED_SERVER:
- {
- AddDllLoadCallbackForDedicatedServer(dllName, callback, uniqueStr, reliesOnArray);
- break;
- }
- }
-}
-
-void __fileAutohook::Dispatch()
-{
- for (__autovar* var : vars)
- var->Dispatch();
-
- for (__autohook* hook : hooks)
- hook->Dispatch();
-}
-
-void __fileAutohook::DispatchForModule(const char* pModuleName)
-{
- const int iModuleNameLen = strlen(pModuleName);
-
- for (__autohook* hook : hooks)
- if ((hook->iAddressResolutionMode == __autohook::OFFSET_STRING && !strncmp(pModuleName, hook->pAddrString, iModuleNameLen)) ||
- (hook->iAddressResolutionMode == __autohook::PROCADDRESS && !strcmp(pModuleName, hook->pModuleName)))
- hook->Dispatch();
-}
-
-ManualHook::ManualHook(const char* funcName, LPVOID func) : pHookFunc(func), ppOrigFunc(nullptr)
-{
- const int iFuncNameStrlen = strlen(funcName);
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
-}
-
-ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func) : pHookFunc(func), ppOrigFunc(orig)
-{
- const int iFuncNameStrlen = strlen(funcName);
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
-}
-
-bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig)
-{
- if (orig)
- ppOrigFunc = orig;
-
- if (MH_CreateHook(addr, pHookFunc, ppOrigFunc) == MH_OK)
- {
- if (MH_EnableHook(addr) == MH_OK)
- {
- spdlog::info("Enabling hook {}", pFuncName);
- return true;
- }
- else
- spdlog::error("MH_EnableHook failed for function {}", pFuncName);
- }
- else
- spdlog::error("MH_CreateHook failed for function {}", pFuncName);
-
- return false;
-}
-
-uintptr_t ParseDLLOffsetString(const char* pAddrString)
-{
- // in the format server.dll + 0xDEADBEEF
- int iDllNameEnd = 0;
- for (; !isspace(pAddrString[iDllNameEnd]) && pAddrString[iDllNameEnd] != '+'; iDllNameEnd++)
- ;
-
- char* pModuleName = new char[iDllNameEnd + 1];
- memcpy(pModuleName, pAddrString, iDllNameEnd);
- pModuleName[iDllNameEnd] = '\0';
-
- // get the module address
- const HMODULE pModuleAddr = GetModuleHandleA(pModuleName);
-
- if (!pModuleAddr)
- return 0;
-
- // get the offset string
- uintptr_t iOffset = 0;
-
- int iOffsetBegin = iDllNameEnd;
- int iOffsetEnd = strlen(pAddrString);
-
- // seek until we hit the start of the number offset
- for (; !(pAddrString[iOffsetBegin] >= '0' && pAddrString[iOffsetBegin] <= '9') && pAddrString[iOffsetBegin]; iOffsetBegin++)
- ;
-
- bool bIsHex = pAddrString[iOffsetBegin] == '0' && (pAddrString[iOffsetBegin + 1] == 'X' || pAddrString[iOffsetBegin + 1] == 'x');
- if (bIsHex)
- iOffset = std::stoi(pAddrString + iOffsetBegin + 2, 0, 16);
- else
- iOffset = std::stoi(pAddrString + iOffsetBegin);
-
- return ((uintptr_t)pModuleAddr + iOffset);
-}
-
-// dll load callback stuff
-// this allows for code to register callbacks to be run as soon as a dll is loaded, mainly to allow for patches to be made on dll load
-struct DllLoadCallback
-{
- std::string dll;
- DllLoadCallbackFuncType callback;
- std::string tag;
- std::vector<std::string> reliesOn;
- bool called;
-};
-
-// HACK: declaring and initialising this vector at file scope crashes on debug builds due to static initialisation order
-// using a static var like this ensures that the vector is initialised lazily when it's used
-std::vector<DllLoadCallback>& GetDllLoadCallbacks()
-{
- static std::vector<DllLoadCallback> vec = std::vector<DllLoadCallback>();
- return vec;
-}
-
-void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::vector<std::string> reliesOn)
-{
- DllLoadCallback& callbackStruct = GetDllLoadCallbacks().emplace_back();
-
- callbackStruct.dll = dll;
- callbackStruct.callback = callback;
- callbackStruct.tag = tag;
- callbackStruct.reliesOn = reliesOn;
- callbackStruct.called = false;
-}
-
-void AddDllLoadCallbackForDedicatedServer(
- std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::vector<std::string> reliesOn)
-{
- if (!IsDedicatedServer())
- return;
-
- AddDllLoadCallback(dll, callback, tag, reliesOn);
-}
-
-void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::vector<std::string> reliesOn)
-{
- if (IsDedicatedServer())
- return;
-
- AddDllLoadCallback(dll, callback, tag, reliesOn);
-}
-
-void MakeHook(LPVOID pTarget, LPVOID pDetour, void* ppOriginal, const char* pFuncName)
-{
- char* pStrippedFuncName = (char*)pFuncName;
- // strip & char from funcname
- if (*pStrippedFuncName == '&')
- pStrippedFuncName++;
-
- if (MH_CreateHook(pTarget, pDetour, (LPVOID*)ppOriginal) == MH_OK)
- {
- if (MH_EnableHook(pTarget) == MH_OK)
- spdlog::info("Enabling hook {}", pStrippedFuncName);
- else
- spdlog::error("MH_EnableHook failed for function {}", pStrippedFuncName);
- }
- else
- spdlog::error("MH_CreateHook failed for function {}", pStrippedFuncName);
-}
-
-AUTOHOOK_ABSOLUTEADDR(_GetCommandLineA, (LPVOID)GetCommandLineA, LPSTR, WINAPI, ())
-{
- static char* cmdlineModified;
- static char* cmdlineOrg;
-
- if (cmdlineOrg == nullptr || cmdlineModified == nullptr)
- {
- cmdlineOrg = _GetCommandLineA();
- bool isDedi = strstr(cmdlineOrg, "-dedicated"); // well, this one has to be a real argument
- bool ignoreStartupArgs = strstr(cmdlineOrg, "-nostartupargs");
-
- std::string args;
- std::ifstream cmdlineArgFile;
-
- // it looks like CommandLine() prioritizes parameters apprearing first, so we want the real commandline to take priority
- // not to mention that cmdlineOrg starts with the EXE path
- args.append(cmdlineOrg);
- args.append(" ");
-
- // append those from the file
-
- if (!ignoreStartupArgs)
- {
-
- cmdlineArgFile = std::ifstream(!isDedi ? "ns_startup_args.txt" : "ns_startup_args_dedi.txt");
-
- if (cmdlineArgFile)
- {
- std::stringstream argBuffer;
- argBuffer << cmdlineArgFile.rdbuf();
- cmdlineArgFile.close();
-
- // if some other command line option includes "-northstar" in the future then you have to refactor this check to check with
- // both either space after or ending with
- if (!isDedi && argBuffer.str().find("-northstar") != std::string::npos)
- MessageBoxA(
- NULL,
- "The \"-northstar\" command line option is NOT supposed to go into ns_startup_args.txt file!\n\nThis option is "
- "supposed to go into Origin/Steam game launch options, and then you are supposed to launch the original "
- "Titanfall2.exe "
- "rather than NorthstarLauncher.exe to make use of it.",
- "Northstar Warning",
- MB_ICONWARNING);
-
- args.append(argBuffer.str());
- }
- }
-
- auto len = args.length();
- cmdlineModified = new char[len + 1];
- if (!cmdlineModified)
- {
- spdlog::error("malloc failed for command line");
- return cmdlineOrg;
- }
- memcpy(cmdlineModified, args.c_str(), len + 1);
- }
-
- return cmdlineModified;
-}
-
-std::vector<std::string> calledTags;
-void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress)
-{
- CModule cModule(moduleAddress);
-
- while (true)
- {
- bool bDoneCalling = true;
-
- for (auto& callbackStruct : GetDllLoadCallbacks())
- {
- if (!callbackStruct.called && fs::path(lpLibFileName).filename() == fs::path(callbackStruct.dll).filename())
- {
- bool bShouldContinue = false;
-
- if (!callbackStruct.reliesOn.empty())
- {
- for (std::string tag : callbackStruct.reliesOn)
- {
- if (std::find(calledTags.begin(), calledTags.end(), tag) == calledTags.end())
- {
- bDoneCalling = false;
- bShouldContinue = true;
- break;
- }
- }
- }
-
- if (bShouldContinue)
- continue;
-
- callbackStruct.callback(moduleAddress);
- calledTags.push_back(callbackStruct.tag);
- callbackStruct.called = true;
- }
- }
-
- if (bDoneCalling)
- break;
- }
-}
-
-void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress)
-{
- CModule cModule(moduleAddress);
-
- while (true)
- {
- bool bDoneCalling = true;
-
- for (auto& callbackStruct : GetDllLoadCallbacks())
- {
- if (!callbackStruct.called && fs::path(lpLibFileName).filename() == fs::path(callbackStruct.dll).filename())
- {
- bool bShouldContinue = false;
-
- if (!callbackStruct.reliesOn.empty())
- {
- for (std::string tag : callbackStruct.reliesOn)
- {
- if (std::find(calledTags.begin(), calledTags.end(), tag) == calledTags.end())
- {
- bDoneCalling = false;
- bShouldContinue = true;
- break;
- }
- }
- }
-
- if (bShouldContinue)
- continue;
-
- callbackStruct.callback(moduleAddress);
- calledTags.push_back(callbackStruct.tag);
- callbackStruct.called = true;
- }
- }
-
- if (bDoneCalling)
- break;
- }
-}
-
-void CallAllPendingDLLLoadCallbacks()
-{
- HMODULE hMods[1024];
- HANDLE hProcess = GetCurrentProcess();
- DWORD cbNeeded;
- unsigned int i;
-
- // Get a list of all the modules in this process.
- if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
- {
- for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
- {
- wchar_t szModName[MAX_PATH];
-
- // Get the full path to the module's file.
- if (GetModuleFileNameExW(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR)))
- {
- CallLoadLibraryWCallbacks(szModName, hMods[i]);
- }
- }
- }
-}
-
-// clang-format off
-AUTOHOOK_ABSOLUTEADDR(_LoadLibraryExA, (LPVOID)LoadLibraryExA,
-HMODULE, WINAPI, (LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags))
-// clang-format on
-{
- HMODULE moduleAddress;
-
- LPCSTR lpLibFileNameEnd = lpLibFileName + strlen(lpLibFileName);
- LPCSTR lpLibName = lpLibFileNameEnd - strlen(XINPUT1_3_DLL);
-
- // replace xinput dll with one that has ASLR
- if (lpLibFileName <= lpLibName && !strncmp(lpLibName, XINPUT1_3_DLL, strlen(XINPUT1_3_DLL) + 1))
- {
- moduleAddress = _LoadLibraryExA("XInput9_1_0.dll", hFile, dwFlags);
-
- if (!moduleAddress)
- {
- MessageBoxA(0, "Could not find XInput9_1_0.dll", "Northstar", MB_ICONERROR);
- exit(EXIT_FAILURE);
-
- return nullptr;
- }
- }
- else
- moduleAddress = _LoadLibraryExA(lpLibFileName, hFile, dwFlags);
-
- if (moduleAddress)
- {
- CallLoadLibraryACallbacks(lpLibFileName, moduleAddress);
- InformPluginsDLLLoad(fs::path(lpLibFileName), moduleAddress);
- }
-
- return moduleAddress;
-}
-
-// clang-format off
-AUTOHOOK_ABSOLUTEADDR(_LoadLibraryA, (LPVOID)LoadLibraryA,
-HMODULE, WINAPI, (LPCSTR lpLibFileName))
-// clang-format on
-{
- HMODULE moduleAddress = _LoadLibraryA(lpLibFileName);
-
- if (moduleAddress)
- CallLoadLibraryACallbacks(lpLibFileName, moduleAddress);
-
- return moduleAddress;
-}
-
-// clang-format off
-AUTOHOOK_ABSOLUTEADDR(_LoadLibraryExW, (LPVOID)LoadLibraryExW,
-HMODULE, WINAPI, (LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags))
-// clang-format on
-{
- HMODULE moduleAddress = _LoadLibraryExW(lpLibFileName, hFile, dwFlags);
-
- if (moduleAddress)
- CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress);
-
- return moduleAddress;
-}
-
-// clang-format off
-AUTOHOOK_ABSOLUTEADDR(_LoadLibraryW, (LPVOID)LoadLibraryW,
-HMODULE, WINAPI, (LPCWSTR lpLibFileName))
-// clang-format on
-{
- HMODULE moduleAddress = _LoadLibraryW(lpLibFileName);
-
- if (moduleAddress)
- {
- CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress);
- InformPluginsDLLLoad(fs::path(lpLibFileName), moduleAddress);
- }
-
- return moduleAddress;
-}
-
-void InstallInitialHooks()
-{
- if (MH_Initialize() != MH_OK)
- spdlog::error("MH_Initialize (minhook initialization) failed");
-
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/core/hooks.h b/NorthstarDLL/core/hooks.h
deleted file mode 100644
index 15edbf0b..00000000
--- a/NorthstarDLL/core/hooks.h
+++ /dev/null
@@ -1,331 +0,0 @@
-#pragma once
-#include "memory.h"
-
-#include <string>
-#include <iostream>
-
-void InstallInitialHooks();
-
-typedef void (*DllLoadCallbackFuncType)(CModule moduleAddress);
-void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::vector<std::string> reliesOn = {});
-void AddDllLoadCallbackForDedicatedServer(
- std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::vector<std::string> reliesOn = {});
-void AddDllLoadCallbackForClient(
- std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::vector<std::string> reliesOn = {});
-
-void CallAllPendingDLLLoadCallbacks();
-
-// new dll load callback stuff
-enum class eDllLoadCallbackSide
-{
- UNSIDED,
- CLIENT,
- DEDICATED_SERVER
-};
-
-class __dllLoadCallback
-{
-public:
- __dllLoadCallback() = delete;
- __dllLoadCallback(
- eDllLoadCallbackSide side,
- const std::string dllName,
- DllLoadCallbackFuncType callback,
- std::string uniqueStr,
- std::string reliesOn);
-};
-
-#define __CONCAT3(x, y, z) x##y##z
-#define CONCAT3(x, y, z) __CONCAT3(x, y, z)
-#define __CONCAT2(x, y) x##y
-#define CONCAT2(x, y) __CONCAT2(x, y)
-#define __STR(s) #s
-
-// adds a callback to be called when a given dll is loaded, for creating hooks and such
-#define __ON_DLL_LOAD(dllName, side, uniquestr, reliesOn, args) \
- void CONCAT2(__dllLoadCallback, uniquestr) args; \
- namespace \
- { \
- __dllLoadCallback CONCAT2(__dllLoadCallbackInstance, __LINE__)( \
- side, dllName, CONCAT2(__dllLoadCallback, uniquestr), __STR(uniquestr), reliesOn); \
- } \
- void CONCAT2(__dllLoadCallback, uniquestr) args
-
-#define ON_DLL_LOAD(dllName, uniquestr, args) __ON_DLL_LOAD(dllName, eDllLoadCallbackSide::UNSIDED, uniquestr, "", args)
-#define ON_DLL_LOAD_RELIESON(dllName, uniquestr, reliesOn, args) \
- __ON_DLL_LOAD(dllName, eDllLoadCallbackSide::UNSIDED, uniquestr, __STR(reliesOn), args)
-#define ON_DLL_LOAD_CLIENT(dllName, uniquestr, args) __ON_DLL_LOAD(dllName, eDllLoadCallbackSide::CLIENT, uniquestr, "", args)
-#define ON_DLL_LOAD_CLIENT_RELIESON(dllName, uniquestr, reliesOn, args) \
- __ON_DLL_LOAD(dllName, eDllLoadCallbackSide::CLIENT, uniquestr, __STR(reliesOn), args)
-#define ON_DLL_LOAD_DEDI(dllName, uniquestr, args) __ON_DLL_LOAD(dllName, eDllLoadCallbackSide::DEDICATED_SERVER, uniquestr, "", args)
-#define ON_DLL_LOAD_DEDI_RELIESON(dllName, uniquestr, reliesOn, args) \
- __ON_DLL_LOAD(dllName, eDllLoadCallbackSide::DEDICATED_SERVER, uniquestr, __STR(reliesOn), args)
-
-// new macro hook stuff
-class __autohook;
-class __autovar;
-
-class __fileAutohook
-{
-public:
- std::vector<__autohook*> hooks;
- std::vector<__autovar*> vars;
-
- void Dispatch();
- void DispatchForModule(const char* pModuleName);
-};
-
-uintptr_t ParseDLLOffsetString(const char* pAddrString);
-
-// initialise autohooks for this file
-#define AUTOHOOK_INIT() \
- namespace \
- { \
- __fileAutohook __FILEAUTOHOOK; \
- }
-
-// dispatch all autohooks in this file
-#define AUTOHOOK_DISPATCH() __FILEAUTOHOOK.Dispatch();
-
-#define AUTOHOOK_DISPATCH_MODULE(moduleName) __FILEAUTOHOOK.DispatchForModule(__STR(moduleName));
-
-class __autohook
-{
-public:
- enum AddressResolutionMode
- {
- OFFSET_STRING, // we're using a string that of the format dllname.dll + offset
- ABSOLUTE_ADDR, // we're using an absolute address, we don't need to process it at all
- PROCADDRESS // resolve using GetModuleHandle and GetProcAddress
- };
-
- char* pFuncName;
-
- LPVOID pHookFunc;
- LPVOID* ppOrigFunc;
-
- // address resolution props
- AddressResolutionMode iAddressResolutionMode;
- char* pAddrString = nullptr; // for OFFSET_STRING
- LPVOID iAbsoluteAddress = nullptr; // for ABSOLUTE_ADDR
- char* pModuleName; // for PROCADDRESS
- char* pProcName; // for PROCADDRESS
-
-public:
- __autohook() = delete;
-
- __autohook(__fileAutohook* autohook, const char* funcName, LPVOID absoluteAddress, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig), iAbsoluteAddress(absoluteAddress)
- {
- iAddressResolutionMode = ABSOLUTE_ADDR;
-
- const int iFuncNameStrlen = strlen(funcName) + 1;
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
-
- autohook->hooks.push_back(this);
- }
-
- __autohook(__fileAutohook* autohook, const char* funcName, const char* addrString, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig)
- {
- iAddressResolutionMode = OFFSET_STRING;
-
- const int iFuncNameStrlen = strlen(funcName) + 1;
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
-
- const int iAddrStrlen = strlen(addrString) + 1;
- pAddrString = new char[iAddrStrlen];
- memcpy(pAddrString, addrString, iAddrStrlen);
-
- autohook->hooks.push_back(this);
- }
-
- __autohook(__fileAutohook* autohook, const char* funcName, const char* moduleName, const char* procName, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig)
- {
- iAddressResolutionMode = PROCADDRESS;
-
- const int iFuncNameStrlen = strlen(funcName) + 1;
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
-
- const int iModuleNameStrlen = strlen(moduleName) + 1;
- pModuleName = new char[iModuleNameStrlen];
- memcpy(pModuleName, moduleName, iModuleNameStrlen);
-
- const int iProcNameStrlen = strlen(procName) + 1;
- pProcName = new char[iProcNameStrlen];
- memcpy(pProcName, procName, iProcNameStrlen);
-
- autohook->hooks.push_back(this);
- }
-
- ~__autohook()
- {
- delete[] pFuncName;
-
- if (pAddrString)
- delete[] pAddrString;
-
- if (pModuleName)
- delete[] pModuleName;
-
- if (pProcName)
- delete[] pProcName;
- }
-
- void Dispatch()
- {
- LPVOID targetAddr = nullptr;
-
- // determine the address of the function we're hooking
- switch (iAddressResolutionMode)
- {
- case ABSOLUTE_ADDR:
- {
- targetAddr = iAbsoluteAddress;
- break;
- }
-
- case OFFSET_STRING:
- {
- targetAddr = (LPVOID)ParseDLLOffsetString(pAddrString);
- break;
- }
-
- case PROCADDRESS:
- {
- targetAddr = (LPVOID)GetProcAddress(GetModuleHandleA(pModuleName), pProcName);
- break;
- }
- }
-
- if (MH_CreateHook(targetAddr, pHookFunc, ppOrigFunc) == MH_OK)
- {
- if (MH_EnableHook(targetAddr) == MH_OK)
- spdlog::info("Enabling hook {}", pFuncName);
- else
- spdlog::error("MH_EnableHook failed for function {}", pFuncName);
- }
- else
- spdlog::error("MH_CreateHook failed for function {}", pFuncName);
- }
-};
-
-// hook a function at a given offset from a dll to be dispatched with AUTOHOOK_DISPATCH()
-#define AUTOHOOK(name, addrString, type, callingConvention, args) \
- type callingConvention CONCAT2(__autohookfunc, name) args; \
- namespace \
- { \
- type(*name) args; \
- __autohook CONCAT2(__autohook, __LINE__)( \
- &__FILEAUTOHOOK, __STR(name), __STR(addrString), (LPVOID*)&name, (LPVOID)CONCAT2(__autohookfunc, name)); \
- } \
- type callingConvention CONCAT2(__autohookfunc, name) args
-
-// hook a function at a given absolute constant address to be dispatched with AUTOHOOK_DISPATCH()
-#define AUTOHOOK_ABSOLUTEADDR(name, addr, type, callingConvention, args) \
- type callingConvention CONCAT2(__autohookfunc, name) args; \
- namespace \
- { \
- type(*name) args; \
- __autohook \
- CONCAT2(__autohook, __LINE__)(&__FILEAUTOHOOK, __STR(name), addr, (LPVOID*)&name, (LPVOID)CONCAT2(__autohookfunc, name)); \
- } \
- type callingConvention CONCAT2(__autohookfunc, name) args
-
-// hook a function at a given module and exported function to be dispatched with AUTOHOOK_DISPATCH()
-#define AUTOHOOK_PROCADDRESS(name, moduleName, procName, type, callingConvention, args) \
- type callingConvention CONCAT2(__autohookfunc, name) args; \
- namespace \
- { \
- type(*name) args; \
- __autohook CONCAT2(__autohook, __LINE__)( \
- &__FILEAUTOHOOK, __STR(name), __STR(moduleName), __STR(procName), (LPVOID*)&name, (LPVOID)CONCAT2(__autohookfunc, name)); \
- } \
- type callingConvention CONCAT2(__autohookfunc, name) \
- args
-
-class ManualHook
-{
-public:
- char* pFuncName;
-
- LPVOID pHookFunc;
- LPVOID* ppOrigFunc;
-
-public:
- ManualHook() = delete;
- ManualHook(const char* funcName, LPVOID func);
- ManualHook(const char* funcName, LPVOID* orig, LPVOID func);
- bool Dispatch(LPVOID addr, LPVOID* orig = nullptr);
-};
-
-// hook a function to be dispatched manually later
-#define HOOK(varName, originalFunc, type, callingConvention, args) \
- namespace \
- { \
- type(*originalFunc) args; \
- } \
- type callingConvention CONCAT2(__manualhookfunc, varName) args; \
- ManualHook varName = ManualHook(__STR(varName), (LPVOID*)&originalFunc, (LPVOID)CONCAT2(__manualhookfunc, varName)); \
- type callingConvention CONCAT2(__manualhookfunc, varName) args
-
-#define HOOK_NOORIG(varName, type, callingConvention, args) \
- type callingConvention CONCAT2(__manualhookfunc, varName) args; \
- ManualHook varName = ManualHook(__STR(varName), (LPVOID)CONCAT2(__manualhookfunc, varName)); \
- type callingConvention CONCAT2(__manualhookfunc, varName) \
- args
-
-void MakeHook(LPVOID pTarget, LPVOID pDetour, void* ppOriginal, const char* pFuncName = "");
-#define MAKEHOOK(pTarget, pDetour, ppOriginal) MakeHook((LPVOID)pTarget, (LPVOID)pDetour, (void*)ppOriginal, __STR(pDetour))
-
-class __autovar
-{
-public:
- char* m_pAddrString;
- void** m_pTarget;
-
-public:
- __autovar(__fileAutohook* pAutohook, const char* pAddrString, void** pTarget)
- {
- m_pTarget = pTarget;
-
- const int iAddrStrlen = strlen(pAddrString) + 1;
- m_pAddrString = new char[iAddrStrlen];
- memcpy(m_pAddrString, pAddrString, iAddrStrlen);
-
- pAutohook->vars.push_back(this);
- }
-
- void Dispatch()
- {
- *m_pTarget = (void*)ParseDLLOffsetString(m_pAddrString);
- }
-};
-
-// VAR_AT(engine.dll+0x404, ConVar*, Cvar_host_timescale)
-#define VAR_AT(addrString, type, name) \
- type name; \
- namespace \
- { \
- __autovar CONCAT2(__autovar, __LINE__)(&__FILEAUTOHOOK, __STR(addrString), (void**)&name); \
- }
-
-// FUNCTION_AT(engine.dll + 0xDEADBEEF, void, __fastcall, SomeFunc, (void* a1))
-#define FUNCTION_AT(addrString, type, callingConvention, name, args) \
- type(*name) args; \
- namespace \
- { \
- __autovar CONCAT2(__autovar, __LINE__)(&__FILEAUTOHOOK, __STR(addrString), (void**)&name); \
- }
-
-// int* g_pSomeInt;
-// DEFINED_VAR_AT(engine.dll + 0x5005, g_pSomeInt)
-#define DEFINED_VAR_AT(addrString, name) \
- namespace \
- { \
- __autovar CONCAT2(__autovar, __LINE__)(&__FILEAUTOHOOK, __STR(addrString), (void**)&name); \
- }
diff --git a/NorthstarDLL/core/macros.h b/NorthstarDLL/core/macros.h
deleted file mode 100644
index ae944cca..00000000
--- a/NorthstarDLL/core/macros.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-template <typename ReturnType, typename... Args> ReturnType CallVFunc(int index, void* thisPtr, Args... args)
-{
- return (*reinterpret_cast<ReturnType(__fastcall***)(void*, Args...)>(thisPtr))[index](thisPtr, args...);
-}
-
-template <typename T, size_t index, typename... Args> constexpr T CallVFunc_Alt(void* classBase, Args... args) noexcept
-{
- return ((*(T(__thiscall***)(void*, Args...))(classBase))[index])(classBase, args...);
-}
-
-#define STR_HASH(s) (std::hash<std::string>()(s))
-
-// Example usage: M_VMETHOD(int, GetEntityIndex, 8, (CBaseEntity* ent), (this, ent))
-#define M_VMETHOD(returnType, name, index, args, argsRaw) \
- FORCEINLINE returnType name args noexcept \
- { \
- return CallVFunc_Alt<returnType, index> argsRaw; \
- }
diff --git a/NorthstarDLL/core/math/bitbuf.h b/NorthstarDLL/core/math/bitbuf.h
deleted file mode 100644
index 5ca75455..00000000
--- a/NorthstarDLL/core/math/bitbuf.h
+++ /dev/null
@@ -1,1148 +0,0 @@
-#pragma once
-
-#define INLINE inline
-
-#define BITS_PER_INT 32
-
-INLINE int GetBitForBitnum(int bitNum)
-{
- static int bitsForBitnum[] = {
- (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9), (1 << 10),
- (1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15), (1 << 16), (1 << 17), (1 << 18), (1 << 19), (1 << 20), (1 << 21),
- (1 << 22), (1 << 23), (1 << 24), (1 << 25), (1 << 26), (1 << 27), (1 << 28), (1 << 29), (1 << 30), (1 << 31),
- };
-
- return bitsForBitnum[(bitNum) & (BITS_PER_INT - 1)];
-}
-
-#undef BITS_PER_INT
-
-using u8 = uint8_t;
-using u16 = uint16_t;
-using u32 = uint32_t;
-using u64 = uint64_t;
-using uptr = uintptr_t;
-
-using i8 = int8_t;
-using i16 = int16_t;
-using i32 = int32_t;
-using i64 = int64_t;
-using iptr = intptr_t;
-
-// Endianess, don't use on PPC64 nor ARM64BE
-#define LittleDWord(val) (val)
-
-static INLINE void StoreLittleDWord(u32* base, size_t dwordIndex, u32 dword)
-{
- base[dwordIndex] = LittleDWord(dword);
-}
-
-static INLINE u32 LoadLittleDWord(u32* base, size_t dwordIndex)
-{
- return LittleDWord(base[dwordIndex]);
-}
-
-#include <algorithm>
-
-static inline const u32 s_nMaskTable[33] = {
- 0,
- (1 << 1) - 1,
- (1 << 2) - 1,
- (1 << 3) - 1,
- (1 << 4) - 1,
- (1 << 5) - 1,
- (1 << 6) - 1,
- (1 << 7) - 1,
- (1 << 8) - 1,
- (1 << 9) - 1,
- (1 << 10) - 1,
- (1 << 11) - 1,
- (1 << 12) - 1,
- (1 << 13) - 1,
- (1 << 14) - 1,
- (1 << 15) - 1,
- (1 << 16) - 1,
- (1 << 17) - 1,
- (1 << 18) - 1,
- (1 << 19) - 1,
- (1 << 20) - 1,
- (1 << 21) - 1,
- (1 << 22) - 1,
- (1 << 23) - 1,
- (1 << 24) - 1,
- (1 << 25) - 1,
- (1 << 26) - 1,
- (1 << 27) - 1,
- (1 << 28) - 1,
- (1 << 29) - 1,
- (1 << 30) - 1,
- 0x7fffffff,
- 0xffffffff,
-};
-
-enum EBitCoordType
-{
- kCW_None,
- kCW_LowPrecision,
- kCW_Integral
-};
-
-class BitBufferBase
-{
-protected:
- INLINE void SetName(const char* name)
- {
- m_BufferName = name;
- }
-
-public:
- INLINE bool IsOverflowed()
- {
- return m_Overflow;
- }
- INLINE void SetOverflowed()
- {
- m_Overflow = true;
- }
-
- INLINE const char* GetName()
- {
- return m_BufferName;
- }
-
-private:
- const char* m_BufferName = "";
-
-protected:
- u8 m_Overflow = false;
-};
-
-class BFRead : public BitBufferBase
-{
-public:
- BFRead() = default;
-
- INLINE BFRead(uptr data, size_t byteLength, size_t startPos = 0, const char* bufferName = 0)
- {
- StartReading(data, byteLength, startPos);
-
- if (bufferName)
- SetName(bufferName);
- }
-
-public:
- INLINE void StartReading(uptr data, size_t byteLength, size_t startPos = 0)
- {
- m_Data = reinterpret_cast<u32 const*>(data);
- m_DataIn = m_Data;
-
- m_DataBytes = byteLength;
- m_DataBits = byteLength << 3;
-
- m_DataEnd = reinterpret_cast<u32 const*>(reinterpret_cast<u8 const*>(m_Data) + m_DataBytes);
-
- Seek(startPos);
- }
-
- INLINE void GrabNextDWord(bool overflow = false)
- {
- if (m_Data == m_DataEnd)
- {
- m_CachedBitsLeft = 1;
- m_CachedBufWord = 0;
-
- m_DataIn++;
-
- if (overflow)
- SetOverflowed();
- }
- else
- {
- if (m_DataIn > m_DataEnd)
- {
- SetOverflowed();
- m_CachedBufWord = 0;
- }
- else
- {
- m_CachedBufWord = LittleDWord(*(m_DataIn++));
- }
- }
- }
-
- INLINE void FetchNext()
- {
- m_CachedBitsLeft = 32;
- GrabNextDWord(false);
- }
-
- INLINE i32 ReadOneBit()
- {
- i32 ret = m_CachedBufWord & 1;
-
- if (--m_CachedBitsLeft == 0)
- FetchNext();
- else
- m_CachedBufWord >>= 1;
-
- return ret;
- }
-
- INLINE u32 ReadUBitLong(i32 numBits)
- {
- if (m_CachedBitsLeft >= numBits)
- {
- u32 ret = m_CachedBufWord & s_nMaskTable[numBits];
-
- m_CachedBitsLeft -= numBits;
-
- if (m_CachedBitsLeft)
- m_CachedBufWord >>= numBits;
- else
- FetchNext();
-
- return ret;
- }
- else
- {
- // need to merge words
- u32 ret = m_CachedBufWord;
- numBits -= m_CachedBitsLeft;
-
- GrabNextDWord(true);
-
- if (IsOverflowed())
- return 0;
-
- ret |= ((m_CachedBufWord & s_nMaskTable[numBits]) << m_CachedBitsLeft);
-
- m_CachedBitsLeft = 32 - numBits;
- m_CachedBufWord >>= numBits;
-
- return ret;
- }
- }
-
- INLINE i32 ReadSBitLong(int numBits)
- {
- i32 ret = ReadUBitLong(numBits);
- return (ret << (32 - numBits)) >> (32 - numBits);
- }
-
- INLINE u32 ReadUBitVar()
- {
- u32 ret = ReadUBitLong(6);
-
- switch (ret & (16 | 32))
- {
- case 16:
- ret = (ret & 15) | (ReadUBitLong(4) << 4);
- // Assert(ret >= 16);
- break;
- case 32:
- ret = (ret & 15) | (ReadUBitLong(8) << 4);
- // Assert(ret >= 256);
- break;
- case 48:
- ret = (ret & 15) | (ReadUBitLong(32 - 4) << 4);
- // Assert(ret >= 4096);
- break;
- }
-
- return ret;
- }
-
- INLINE u32 PeekUBitLong(i32 numBits)
- {
- i32 nSaveBA = m_CachedBitsLeft;
- i32 nSaveW = m_CachedBufWord;
- u32 const* pSaveP = m_DataIn;
- u32 nRet = ReadUBitLong(numBits);
-
- m_CachedBitsLeft = nSaveBA;
- m_CachedBufWord = nSaveW;
- m_DataIn = pSaveP;
-
- return nRet;
- }
-
- INLINE float ReadBitFloat()
- {
- u32 value = ReadUBitLong(32);
- return *reinterpret_cast<float*>(&value);
- }
-
- /*INLINE float ReadBitCoord() {
- i32 intval = 0, fractval = 0, signbit = 0;
- float value = 0.0;
-
- // Read the required integer and fraction flags
- intval = ReadOneBit();
- fractval = ReadOneBit();
-
- // If we got either parse them, otherwise it's a zero.
- if (intval || fractval) {
- // Read the sign bit
- signbit = ReadOneBit();
-
- // If there's an integer, read it in
- if (intval) {
- // Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE]
- intval = ReadUBitLong(COORD_INTEGER_BITS) + 1;
- }
-
- // If there's a fraction, read it in
- if (fractval) {
- fractval = ReadUBitLong(COORD_FRACTIONAL_BITS);
- }
-
- // Calculate the correct floating point value
- value = intval + ((float)fractval * COORD_RESOLUTION);
-
- // Fixup the sign if negative.
- if (signbit)
- value = -value;
- }
-
- return value;
- }
-
- INLINE float ReadBitCoordMP() {
- i32 intval = 0, fractval = 0, signbit = 0;
- float value = 0.0;
-
- bool inBounds = ReadOneBit() ? true : false;
-
- // Read the required integer and fraction flags
- intval = ReadOneBit();
-
- // If we got either parse them, otherwise it's a zero.
- if (intval) {
- // Read the sign bit
- signbit = ReadOneBit();
-
- // If there's an integer, read it in
- // Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE]
- if (inBounds)
- value = ReadUBitLong(COORD_INTEGER_BITS_MP) + 1;
- else
- value = ReadUBitLong(COORD_INTEGER_BITS) + 1;
- }
-
- // Fixup the sign if negative.
- if (signbit)
- value = -value;
-
- return value;
- }
-
- INLINE float ReadBitCellCoord(int bits, EBitCoordType coordType) {
- bool bIntegral = (coordType == kCW_Integral);
- bool bLowPrecision = (coordType == kCW_LowPrecision);
-
- int intval = 0, fractval = 0;
- float value = 0.0;
-
- if (bIntegral)
- value = ReadUBitLong(bits);
- else {
- intval = ReadUBitLong(bits);
-
- // If there's a fraction, read it in
- fractval = ReadUBitLong(bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS);
-
- // Calculate the correct floating point value
- value = intval + ((float)fractval * (bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION));
- }
-
- return value;
- }
-
- INLINE float ReadBitNormal() {
- // Read the sign bit
- i32 signbit = ReadOneBit();
-
- // Read the fractional part
- u32 fractval = ReadUBitLong(NORMAL_FRACTIONAL_BITS);
-
- // Calculate the correct floating point value
- float value = (float)fractval * NORMAL_RESOLUTION;
-
- // Fixup the sign if negative.
- if (signbit)
- value = -value;
-
- return value;
- }
-
- INLINE void ReadBitVec3Coord(Vector& fa) {
- i32 xflag, yflag, zflag;
-
- // This vector must be initialized! Otherwise, If any of the flags aren't set,
- // the corresponding component will not be read and will be stack garbage.
- fa.Init(0, 0, 0);
-
- xflag = ReadOneBit();
- yflag = ReadOneBit();
- zflag = ReadOneBit();
-
- if (xflag)
- fa[0] = ReadBitCoord();
- if (yflag)
- fa[1] = ReadBitCoord();
- if (zflag)
- fa[2] = ReadBitCoord();
- }
-
- INLINE void ReadBitVec3Normal(Vector& fa) {
- i32 xflag = ReadOneBit();
- i32 yflag = ReadOneBit();
-
- if (xflag)
- fa[0] = ReadBitNormal();
- else
- fa[0] = 0.0f;
-
- if (yflag)
- fa[1] = ReadBitNormal();
- else
- fa[1] = 0.0f;
-
- // The first two imply the third (but not its sign)
- i32 znegative = ReadOneBit();
-
- float fafafbfb = fa[0] * fa[0] + fa[1] * fa[1];
- if (fafafbfb < 1.0f)
- fa[2] = sqrt(1.0f - fafafbfb);
- else
- fa[2] = 0.0f;
-
- if (znegative)
- fa[2] = -fa[2];
- }
-
- INLINE void ReadBitAngles(QAngle& fa) {
- Vector tmp;
- ReadBitVec3Coord(tmp);
- fa.Init(tmp.x, tmp.y, tmp.z);
- }*/
-
- INLINE float ReadBitAngle(int numBits)
- {
- float shift = (float)(GetBitForBitnum(numBits));
-
- i32 i = ReadUBitLong(numBits);
- float fReturn = (float)i * (360.0 / shift);
-
- return fReturn;
- }
-
- INLINE i32 ReadChar()
- {
- return ReadSBitLong(sizeof(char) << 3);
- }
- INLINE u32 ReadByte()
- {
- return ReadUBitLong(sizeof(unsigned char) << 3);
- }
-
- INLINE i32 ReadShort()
- {
- return ReadSBitLong(sizeof(short) << 3);
- }
- INLINE u32 ReadWord()
- {
- return ReadUBitLong(sizeof(unsigned short) << 3);
- }
-
- INLINE i32 ReadLong()
- {
- return (i32)(ReadUBitLong(sizeof(i32) << 3));
- }
- INLINE float ReadFloat()
- {
- u32 temp = ReadUBitLong(sizeof(float) << 3);
- return *reinterpret_cast<float*>(&temp);
- }
-
- INLINE u32 ReadVarInt32()
- {
- constexpr int kMaxVarint32Bytes = 5;
-
- u32 result = 0;
- int count = 0;
- u32 b;
-
- do
- {
- if (count == kMaxVarint32Bytes)
- return result;
-
- b = ReadUBitLong(8);
- result |= (b & 0x7F) << (7 * count);
- ++count;
- } while (b & 0x80);
-
- return result;
- }
-
- INLINE u64 ReadVarInt64()
- {
- constexpr int kMaxVarintBytes = 10;
-
- u64 result = 0;
- int count = 0;
- u64 b;
-
- do
- {
- if (count == kMaxVarintBytes)
- return result;
-
- b = ReadUBitLong(8);
- result |= static_cast<u64>(b & 0x7F) << (7 * count);
- ++count;
- } while (b & 0x80);
-
- return result;
- }
-
- INLINE void ReadBits(uptr outData, u32 bitLength)
- {
- u8* out = reinterpret_cast<u8*>(outData);
- int bitsLeft = bitLength;
-
- // align output to dword boundary
- while (((uptr)out & 3) != 0 && bitsLeft >= 8)
- {
- *out = (unsigned char)ReadUBitLong(8);
- ++out;
- bitsLeft -= 8;
- }
-
- // read dwords
- while (bitsLeft >= 32)
- {
- *((u32*)out) = ReadUBitLong(32);
- out += sizeof(u32);
- bitsLeft -= 32;
- }
-
- // read remaining bytes
- while (bitsLeft >= 8)
- {
- *out = ReadUBitLong(8);
- ++out;
- bitsLeft -= 8;
- }
-
- // read remaining bits
- if (bitsLeft)
- *out = ReadUBitLong(bitsLeft);
- }
-
- INLINE bool ReadBytes(uptr outData, u32 byteLength)
- {
- ReadBits(outData, byteLength << 3);
- return !IsOverflowed();
- }
-
- INLINE bool ReadString(char* str, i32 maxLength, bool stopAtLineTermination = false, i32* outNumChars = 0)
- {
- bool tooSmall = false;
- int iChar = 0;
-
- while (1)
- {
- char val = ReadChar();
-
- if (val == 0)
- break;
- else if (stopAtLineTermination && val == '\n')
- break;
-
- if (iChar < (maxLength - 1))
- {
- str[iChar] = val;
- ++iChar;
- }
- else
- {
- tooSmall = true;
- }
- }
-
- // Make sure it's null-terminated.
- // Assert(iChar < maxLength);
- str[iChar] = 0;
-
- if (outNumChars)
- *outNumChars = iChar;
-
- return !IsOverflowed() && !tooSmall;
- }
-
- INLINE char* ReadAndAllocateString(bool* hasOverflowed = 0)
- {
- char str[2048];
-
- int chars = 0;
- bool overflowed = !ReadString(str, sizeof(str), false, &chars);
-
- if (hasOverflowed)
- *hasOverflowed = overflowed;
-
- // Now copy into the output and return it;
- char* ret = new char[chars + 1];
- for (u32 i = 0; i <= chars; i++)
- ret[i] = str[i];
-
- return ret;
- }
-
- INLINE i64 ReadLongLong()
- {
- i64 retval;
- u32* longs = (u32*)&retval;
-
- // Read the two DWORDs according to network endian
- const short endianIndex = 0x0100;
- u8* idx = (u8*)&endianIndex;
-
- longs[*idx++] = ReadUBitLong(sizeof(i32) << 3);
- longs[*idx] = ReadUBitLong(sizeof(i32) << 3);
-
- return retval;
- }
-
- INLINE bool Seek(size_t startPos)
- {
- bool bSucc = true;
-
- if (startPos < 0 || startPos > m_DataBits)
- {
- SetOverflowed();
- bSucc = false;
- startPos = m_DataBits;
- }
-
- // non-multiple-of-4 bytes at head of buffer. We put the "round off"
- // at the head to make reading and detecting the end efficient.
- int nHead = m_DataBytes & 3;
-
- int posBytes = startPos / 8;
- if ((m_DataBytes < 4) || (nHead && (posBytes < nHead)))
- {
- // partial first dword
- u8 const* partial = (u8 const*)m_Data;
-
- if (m_Data)
- {
- m_CachedBufWord = *(partial++);
- if (nHead > 1)
- m_CachedBufWord |= (*partial++) << 8;
- if (nHead > 2)
- m_CachedBufWord |= (*partial++) << 16;
- }
-
- m_DataIn = (u32 const*)partial;
-
- m_CachedBufWord >>= (startPos & 31);
- m_CachedBitsLeft = (nHead << 3) - (startPos & 31);
- }
- else
- {
- int adjustedPos = startPos - (nHead << 3);
-
- m_DataIn = reinterpret_cast<u32 const*>(reinterpret_cast<u8 const*>(m_Data) + ((adjustedPos / 32) << 2) + nHead);
-
- if (m_Data)
- {
- m_CachedBitsLeft = 32;
- GrabNextDWord();
- }
- else
- {
- m_CachedBufWord = 0;
- m_CachedBitsLeft = 1;
- }
-
- m_CachedBufWord >>= (adjustedPos & 31);
- m_CachedBitsLeft = std::min(m_CachedBitsLeft, u32(32 - (adjustedPos & 31))); // in case grabnextdword overflowed
- }
-
- return bSucc;
- }
-
- INLINE size_t GetNumBitsRead()
- {
- if (!m_Data)
- return 0;
-
- size_t nCurOfs = size_t(((iptr(m_DataIn) - iptr(m_Data)) / 4) - 1);
- nCurOfs *= 32;
- nCurOfs += (32 - m_CachedBitsLeft);
-
- size_t nAdjust = 8 * (m_DataBytes & 3);
- return std::min(nCurOfs + nAdjust, m_DataBits);
- }
-
- INLINE bool SeekRelative(size_t offset)
- {
- return Seek(GetNumBitsRead() + offset);
- }
-
- INLINE size_t TotalBytesAvailable()
- {
- return m_DataBytes;
- }
-
- INLINE size_t GetNumBitsLeft()
- {
- return m_DataBits - GetNumBitsRead();
- }
- INLINE size_t GetNumBytesLeft()
- {
- return GetNumBitsLeft() >> 3;
- }
-
-private:
- size_t m_DataBits; // 0x0010
- size_t m_DataBytes; // 0x0018
-
- u32 m_CachedBufWord; // 0x0020
- u32 m_CachedBitsLeft; // 0x0024
-
- const u32* m_DataIn; // 0x0028
- const u32* m_DataEnd; // 0x0030
- const u32* m_Data; // 0x0038
-};
-
-class BFWrite : public BitBufferBase
-{
-public:
- BFWrite() = default;
-
- INLINE BFWrite(uptr data, size_t byteLength, const char* bufferName = 0)
- {
- StartWriting(data, byteLength);
-
- if (bufferName)
- SetName(bufferName);
- }
-
-public:
- INLINE void StartWriting(uptr data, size_t byteLength)
- {
- m_Data = reinterpret_cast<u32*>(data);
- m_DataOut = m_Data;
-
- m_DataBytes = byteLength;
- m_DataBits = byteLength << 3;
-
- m_DataEnd = reinterpret_cast<u32*>(reinterpret_cast<u8*>(m_Data) + m_DataBytes);
- }
-
- INLINE int GetNumBitsLeft()
- {
- return m_OutBitsLeft + (32 * (m_DataEnd - m_DataOut - 1));
- }
-
- INLINE void Reset()
- {
- m_Overflow = false;
- m_OutBufWord = 0;
- m_OutBitsLeft = 32;
- m_DataOut = m_Data;
- }
-
- INLINE void TempFlush()
- {
- if (m_OutBitsLeft != 32)
- {
- if (m_DataOut == m_DataEnd)
- SetOverflowed();
- else
- StoreLittleDWord(m_DataOut, 0, LoadLittleDWord(m_DataOut, 0) & ~s_nMaskTable[32 - m_OutBitsLeft] | m_OutBufWord);
- }
-
- m_Flushed = true;
- }
-
- INLINE u8* GetBasePointer()
- {
- TempFlush();
- return reinterpret_cast<u8*>(m_Data);
- }
-
- INLINE u8* GetData()
- {
- return GetBasePointer();
- }
-
- INLINE void Finish()
- {
- if (m_OutBitsLeft != 32)
- {
- if (m_DataOut == m_DataEnd)
- SetOverflowed();
-
- StoreLittleDWord(m_DataOut, 0, m_OutBufWord);
- }
- }
-
- INLINE void FlushNoCheck()
- {
- StoreLittleDWord(m_DataOut++, 0, m_OutBufWord);
-
- m_OutBitsLeft = 32;
- m_OutBufWord = 0;
- }
-
- INLINE void Flush()
- {
- if (m_DataOut == m_DataEnd)
- SetOverflowed();
- else
- StoreLittleDWord(m_DataOut++, 0, m_OutBufWord);
-
- m_OutBitsLeft = 32;
- m_OutBufWord = 0;
- }
-
- INLINE void WriteOneBitNoCheck(i32 value)
- {
- m_OutBufWord |= (value & 1) << (32 - m_OutBitsLeft);
-
- if (--m_OutBitsLeft == 0)
- FlushNoCheck();
- }
-
- INLINE void WriteOneBit(i32 value)
- {
- m_OutBufWord |= (value & 1) << (32 - m_OutBitsLeft);
-
- if (--m_OutBitsLeft == 0)
- Flush();
- }
-
- INLINE void WriteUBitLong(u32 data, i32 numBits, bool checkRange = true)
- {
- if (numBits <= m_OutBitsLeft)
- {
- if (checkRange)
- m_OutBufWord |= (data) << (32 - m_OutBitsLeft);
- else
- m_OutBufWord |= (data & s_nMaskTable[numBits]) << (32 - m_OutBitsLeft);
-
- m_OutBitsLeft -= numBits;
-
- if (m_OutBitsLeft == 0)
- Flush();
- }
- else
- {
- // split dwords case
- i32 overflowBits = (numBits - m_OutBitsLeft);
- m_OutBufWord |= (data & s_nMaskTable[m_OutBitsLeft]) << (32 - m_OutBitsLeft);
- Flush();
- m_OutBufWord = (data >> (numBits - overflowBits));
- m_OutBitsLeft = 32 - overflowBits;
- }
- }
-
- INLINE void WriteSBitLong(i32 data, i32 numBits)
- {
- WriteUBitLong((u32)data, numBits, false);
- }
-
- INLINE void WriteUBitVar(u32 n)
- {
- if (n < 16)
- WriteUBitLong(n, 6);
- else if (n < 256)
- WriteUBitLong((n & 15) | 16 | ((n & (128 | 64 | 32 | 16)) << 2), 10);
- else if (n < 4096)
- WriteUBitLong((n & 15) | 32 | ((n & (2048 | 1024 | 512 | 256 | 128 | 64 | 32 | 16)) << 2), 14);
- else
- {
- WriteUBitLong((n & 15) | 48, 6);
- WriteUBitLong((n >> 4), 32 - 4);
- }
- }
-
- INLINE void WriteBitFloat(float value)
- {
- auto temp = &value;
- WriteUBitLong(*reinterpret_cast<u32*>(temp), 32);
- }
-
- INLINE void WriteFloat(float value)
- {
- auto temp = &value;
- WriteUBitLong(*reinterpret_cast<u32*>(temp), 32);
- }
-
- INLINE bool WriteBits(const uptr data, i32 numBits)
- {
- u8* out = (u8*)data;
- i32 numBitsLeft = numBits;
-
- // Bounds checking..
- if ((GetNumBitsWritten() + numBits) > m_DataBits)
- {
- SetOverflowed();
- return false;
- }
-
- // !! speed!! need fast paths
- // write remaining bytes
- while (numBitsLeft >= 8)
- {
- WriteUBitLong(*out, 8, false);
- ++out;
- numBitsLeft -= 8;
- }
-
- // write remaining bits
- if (numBitsLeft)
- WriteUBitLong(*out, numBitsLeft, false);
-
- return !IsOverflowed();
- }
-
- INLINE bool WriteBytes(const uptr data, i32 numBytes)
- {
- return WriteBits(data, numBytes << 3);
- }
-
- INLINE i32 GetNumBitsWritten()
- {
- return (32 - m_OutBitsLeft) + (32 * (m_DataOut - m_Data));
- }
-
- INLINE i32 GetNumBytesWritten()
- {
- return (GetNumBitsWritten() + 7) >> 3;
- }
-
- INLINE void WriteChar(i32 val)
- {
- WriteSBitLong(val, sizeof(char) << 3);
- }
-
- INLINE void WriteByte(i32 val)
- {
- WriteUBitLong(val, sizeof(unsigned char) << 3, false);
- }
-
- INLINE void WriteShort(i32 val)
- {
- WriteSBitLong(val, sizeof(short) << 3);
- }
-
- INLINE void WriteWord(i32 val)
- {
- WriteUBitLong(val, sizeof(unsigned short) << 3);
- }
-
- INLINE bool WriteString(const char* str)
- {
- if (str)
- while (*str)
- WriteChar(*(str++));
-
- WriteChar(0);
-
- return !IsOverflowed();
- }
-
- INLINE void WriteLongLong(i64 val)
- {
- u32* pLongs = (u32*)&val;
-
- // Insert the two DWORDS according to network endian
- const short endianIndex = 0x0100;
- u8* idx = (u8*)&endianIndex;
-
- WriteUBitLong(pLongs[*idx++], sizeof(i32) << 3);
- WriteUBitLong(pLongs[*idx], sizeof(i32) << 3);
- }
-
- /*INLINE void WriteBitCoord(const float f) {
- i32 signbit = (f <= -COORD_RESOLUTION);
- i32 intval = (i32)abs(f);
- i32 fractval = abs((i32)(f * COORD_DENOMINATOR)) & (COORD_DENOMINATOR - 1);
-
- // Send the bit flags that indicate whether we have an integer part and/or a fraction part.
- WriteOneBit(intval);
- WriteOneBit(fractval);
-
- if (intval || fractval) {
- // Send the sign bit
- WriteOneBit(signbit);
-
- // Send the integer if we have one.
- if (intval) {
- // Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
- intval--;
- WriteUBitLong((u32)intval, COORD_INTEGER_BITS);
- }
-
- // Send the fraction if we have one
- if (fractval) {
- WriteUBitLong((u32)fractval, COORD_FRACTIONAL_BITS);
- }
- }
- }
-
- INLINE void WriteBitCoordMP(const float f) {
- i32 signbit = (f <= -COORD_RESOLUTION);
- i32 intval = (i32)abs(f);
- i32 fractval = (abs((i32)(f * COORD_DENOMINATOR)) & (COORD_DENOMINATOR - 1));
-
- bool bInBounds = intval < (1 << COORD_INTEGER_BITS_MP);
-
- WriteOneBit(bInBounds);
-
- // Send the sign bit
- WriteOneBit(intval);
-
- if (intval) {
- WriteOneBit(signbit);
-
- // Send the integer if we have one.
- // Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
- intval--;
-
- if (bInBounds)
- WriteUBitLong((u32)intval, COORD_INTEGER_BITS_MP);
- else
- WriteUBitLong((u32)intval, COORD_INTEGER_BITS);
- }
- }
-
- INLINE void WriteBitCellCoord(const float f, int bits, EBitCoordType coordType) {
- bool bIntegral = (coordType == kCW_Integral);
- bool bLowPrecision = (coordType == kCW_LowPrecision);
-
- i32 intval = (i32)abs(f);
- i32 fractval = bLowPrecision ? (abs((i32)(f * COORD_DENOMINATOR_LOWPRECISION)) & (COORD_DENOMINATOR_LOWPRECISION - 1)) :
- (abs((i32)(f * COORD_DENOMINATOR)) & (COORD_DENOMINATOR - 1));
-
- if (bIntegral)
- WriteUBitLong((u32)intval, bits);
- else {
- WriteUBitLong((u32)intval, bits);
- WriteUBitLong((u32)fractval, bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS);
- }
- }*/
-
- INLINE void SeekToBit(int bit)
- {
- TempFlush();
-
- m_DataOut = m_Data + (bit / 32);
- m_OutBufWord = LoadLittleDWord(m_DataOut, 0);
- m_OutBitsLeft = 32 - (bit & 31);
- }
-
- /*INLINE void WriteBitVec3Coord(const Vector& fa) {
- i32 xflag, yflag, zflag;
-
- xflag = (fa[0] >= COORD_RESOLUTION) || (fa[0] <= -COORD_RESOLUTION);
- yflag = (fa[1] >= COORD_RESOLUTION) || (fa[1] <= -COORD_RESOLUTION);
- zflag = (fa[2] >= COORD_RESOLUTION) || (fa[2] <= -COORD_RESOLUTION);
-
- WriteOneBit(xflag);
- WriteOneBit(yflag);
- WriteOneBit(zflag);
-
- if (xflag)
- WriteBitCoord(fa[0]);
- if (yflag)
- WriteBitCoord(fa[1]);
- if (zflag)
- WriteBitCoord(fa[2]);
- }
-
- INLINE void WriteBitNormal(float f) {
- i32 signbit = (f <= -NORMAL_RESOLUTION);
-
- // NOTE: Since +/-1 are valid values for a normal, I'm going to encode that as all ones
- u32 fractval = abs((i32)(f * NORMAL_DENOMINATOR));
-
- // clamp..
- if (fractval > NORMAL_DENOMINATOR)
- fractval = NORMAL_DENOMINATOR;
-
- // Send the sign bit
- WriteOneBit(signbit);
-
- // Send the fractional component
- WriteUBitLong(fractval, NORMAL_FRACTIONAL_BITS);
- }
-
- INLINE void WriteBitVec3Normal(const Vector& fa) {
- i32 xflag, yflag;
-
- xflag = (fa[0] >= NORMAL_RESOLUTION) || (fa[0] <= -NORMAL_RESOLUTION);
- yflag = (fa[1] >= NORMAL_RESOLUTION) || (fa[1] <= -NORMAL_RESOLUTION);
-
- WriteOneBit(xflag);
- WriteOneBit(yflag);
-
- if (xflag)
- WriteBitNormal(fa[0]);
- if (yflag)
- WriteBitNormal(fa[1]);
-
- // Write z sign bit
- i32 signbit = (fa[2] <= -NORMAL_RESOLUTION);
- WriteOneBit(signbit);
- }*/
-
- INLINE void WriteBitAngle(float angle, int numBits)
- {
- u32 shift = GetBitForBitnum(numBits);
- u32 mask = shift - 1;
-
- i32 d = (i32)((angle / 360.0) * shift);
- d &= mask;
-
- WriteUBitLong((u32)d, numBits);
- }
-
- INLINE bool WriteBitsFromBuffer(BFRead* in, int numBits)
- {
- while (numBits > 32)
- {
- WriteUBitLong(in->ReadUBitLong(32), 32);
- numBits -= 32;
- }
-
- WriteUBitLong(in->ReadUBitLong(numBits), numBits);
- return !IsOverflowed() && !in->IsOverflowed();
- }
-
- /*INLINE void WriteBitAngles(const QAngle& fa) {
- // FIXME:
- Vector tmp(fa.x, fa.y, fa.z);
- WriteBitVec3Coord(tmp);
- }*/
-
-private:
- size_t m_DataBits = 0;
- size_t m_DataBytes = 0;
-
- u32 m_OutBufWord = 0;
- u32 m_OutBitsLeft = 32;
-
- u32* m_DataOut = nullptr;
- u32* m_DataEnd = nullptr;
- u32* m_Data = nullptr;
-
- bool m_Flushed = false; // :flushed:
-};
-
-#undef INLINE
diff --git a/NorthstarDLL/core/math/bits.cpp b/NorthstarDLL/core/math/bits.cpp
deleted file mode 100644
index c879a45c..00000000
--- a/NorthstarDLL/core/math/bits.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//=============================================================================//
-//
-// Purpose: look for NANs, infinities, and underflows.
-//
-//=============================================================================//
-
-#include "bits.h"
-
-//-----------------------------------------------------------------------------
-// This follows the ANSI/IEEE 754-1985 standard
-//-----------------------------------------------------------------------------
-unsigned long& FloatBits(float& f)
-{
- return *reinterpret_cast<unsigned long*>(&f);
-}
-
-unsigned long const& FloatBits(float const& f)
-{
- return *reinterpret_cast<unsigned long const*>(&f);
-}
-
-float BitsToFloat(unsigned long i)
-{
- return *reinterpret_cast<float*>(&i);
-}
-
-bool IsFinite(float f)
-{
- return ((FloatBits(f) & 0x7F800000) != 0x7F800000);
-}
-
-unsigned long FloatAbsBits(float f)
-{
- return FloatBits(f) & 0x7FFFFFFF;
-}
-
-float FloatMakePositive(float f)
-{
- return fabsf(f);
-}
-
-float FloatNegate(float f)
-{
- return -f; // BitsToFloat( FloatBits(f) ^ 0x80000000 );
-}
diff --git a/NorthstarDLL/core/math/bits.h b/NorthstarDLL/core/math/bits.h
deleted file mode 100644
index 0532a9bd..00000000
--- a/NorthstarDLL/core/math/bits.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-unsigned long& FloatBits(float& f);
-unsigned long const& FloatBits(float const& f);
-float BitsToFloat(unsigned long i);
-bool IsFinite(float f);
-unsigned long FloatAbsBits(float f);
-
-#define FLOAT32_NAN_BITS (std::uint32_t)0x7FC00000 // NaN!
-#define FLOAT32_NAN BitsToFloat(FLOAT32_NAN_BITS)
diff --git a/NorthstarDLL/core/math/color.cpp b/NorthstarDLL/core/math/color.cpp
deleted file mode 100644
index 7b98043a..00000000
--- a/NorthstarDLL/core/math/color.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-
-// clang-format off
-namespace NS::Colors
-{
- Color SCRIPT_UI (100, 255, 255);
- Color SCRIPT_CL (100, 255, 100);
- Color SCRIPT_SV (255, 100, 255);
- Color NATIVE_UI (50 , 150, 150);
- Color NATIVE_CL (50 , 150, 50 );
- Color NATIVE_SV (150, 50 , 150);
- Color NATIVE_ENGINE (252, 133, 153);
- Color FILESYSTEM (0 , 150, 150);
- Color RPAK (255, 190, 0 );
- Color NORTHSTAR (66 , 72 , 128);
- Color ECHO (150, 150, 159);
- Color PLUGINSYS (244, 60 , 14);
- Color PLUGIN (244, 106, 14);
-
- Color TRACE (0 , 255, 255);
- Color DEBUG (0 , 255, 255);
- Color INFO (16 , 160, 16 );
- Color WARN (255, 255, 0 );
- Color ERR (255, 50 , 50 );
- Color CRIT (255, 0 , 0 );
- Color OFF (0 , 0 , 0 );
-};
-// clang-format on
diff --git a/NorthstarDLL/core/math/color.h b/NorthstarDLL/core/math/color.h
deleted file mode 100644
index 013c4e4c..00000000
--- a/NorthstarDLL/core/math/color.h
+++ /dev/null
@@ -1,199 +0,0 @@
-#pragma once
-
-struct color24
-{
- uint8_t r, g, b;
-};
-
-typedef struct color32_s
-{
- bool operator!=(const struct color32_s& other) const
- {
- return r != other.r || g != other.g || b != other.b || a != other.a;
- }
- inline unsigned* asInt(void)
- {
- return reinterpret_cast<unsigned*>(this);
- }
- inline const unsigned* asInt(void) const
- {
- return reinterpret_cast<const unsigned*>(this);
- }
- inline void Copy(const color32_s& rhs)
- {
- *asInt() = *rhs.asInt();
- }
-
- uint8_t r, g, b, a;
-} color32;
-
-struct SourceColor
-{
- unsigned char R;
- unsigned char G;
- unsigned char B;
- unsigned char A;
-
- SourceColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
- {
- R = r;
- G = g;
- B = b;
- A = a;
- }
-
- SourceColor()
- {
- R = 0;
- G = 0;
- B = 0;
- A = 0;
- }
-};
-
-//-----------------------------------------------------------------------------
-// Purpose: Basic handler for an rgb set of colors
-// This class is fully inline
-//-----------------------------------------------------------------------------
-class Color
-{
-public:
- Color(int r, int g, int b, int a = 255)
- {
- _color[0] = (unsigned char)r;
- _color[1] = (unsigned char)g;
- _color[2] = (unsigned char)b;
- _color[3] = (unsigned char)a;
- }
- void SetColor(int _r, int _g, int _b, int _a = 0)
- {
- _color[0] = (unsigned char)_r;
- _color[1] = (unsigned char)_g;
- _color[2] = (unsigned char)_b;
- _color[3] = (unsigned char)_a;
- }
- void GetColor(int& _r, int& _g, int& _b, int& _a) const
- {
- _r = _color[0];
- _g = _color[1];
- _b = _color[2];
- _a = _color[3];
- }
- int GetValue(int index) const
- {
- return _color[index];
- }
- void SetRawColor(int color32)
- {
- *((int*)this) = color32;
- }
- int GetRawColor(void) const
- {
- return *((int*)this);
- }
-
- inline int r() const
- {
- return _color[0];
- }
- inline int g() const
- {
- return _color[1];
- }
- inline int b() const
- {
- return _color[2];
- }
- inline int a() const
- {
- return _color[3];
- }
-
- unsigned char& operator[](int index)
- {
- return _color[index];
- }
-
- const unsigned char& operator[](int index) const
- {
- return _color[index];
- }
-
- bool operator==(const Color& rhs) const
- {
- return (*((int*)this) == *((int*)&rhs));
- }
-
- bool operator!=(const Color& rhs) const
- {
- return !(operator==(rhs));
- }
-
- Color& operator=(const Color& rhs)
- {
- SetRawColor(rhs.GetRawColor());
- return *this;
- }
-
- Color& operator=(const color32& rhs)
- {
- _color[0] = rhs.r;
- _color[1] = rhs.g;
- _color[2] = rhs.b;
- _color[3] = rhs.a;
- return *this;
- }
-
- color32 ToColor32(void) const
- {
- color32 newColor {};
- newColor.r = _color[0];
- newColor.g = _color[1];
- newColor.b = _color[2];
- newColor.a = _color[3];
- return newColor;
- }
-
- std::string ToANSIColor()
- {
- std::string out = "\033[38;2;";
- out += std::to_string(_color[0]) + ";";
- out += std::to_string(_color[1]) + ";";
- out += std::to_string(_color[2]) + ";";
- out += "49m";
- return out;
- }
-
- SourceColor ToSourceColor()
- {
- return SourceColor(_color[0], _color[1], _color[2], _color[3]);
- }
-
-private:
- unsigned char _color[4];
-};
-
-namespace NS::Colors
-{
- extern Color SCRIPT_UI;
- extern Color SCRIPT_CL;
- extern Color SCRIPT_SV;
- extern Color NATIVE_UI;
- extern Color NATIVE_CL;
- extern Color NATIVE_SV;
- extern Color NATIVE_ENGINE;
- extern Color FILESYSTEM;
- extern Color RPAK;
- extern Color NORTHSTAR;
- extern Color ECHO;
- extern Color PLUGINSYS;
- extern Color PLUGIN;
-
- extern Color TRACE;
- extern Color DEBUG;
- extern Color INFO;
- extern Color WARN;
- extern Color ERR;
- extern Color CRIT;
- extern Color OFF;
-}; // namespace NS::Colors
diff --git a/NorthstarDLL/core/math/vector.h b/NorthstarDLL/core/math/vector.h
deleted file mode 100644
index 8684908f..00000000
--- a/NorthstarDLL/core/math/vector.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <cmath>
-
-#pragma once
-
-union Vector3
-{
- struct
- {
- float x;
- float y;
- float z;
- };
-
- float raw[3];
-
- void MakeValid()
- {
- for (auto& fl : raw)
- if (std::isnan(fl))
- fl = 0;
- }
-
- // todo: more operators maybe
- bool operator==(const Vector3& other)
- {
- return x == other.x && y == other.y && z == other.z;
- }
-};
-
-union QAngle
-{
- struct
- {
- float x;
- float y;
- float z;
- float w;
- };
-
- float raw[4];
-
- // todo: more operators maybe
- bool operator==(const QAngle& other)
- {
- return x == other.x && y == other.y && z == other.z && w == other.w;
- }
-};
diff --git a/NorthstarDLL/core/memalloc.cpp b/NorthstarDLL/core/memalloc.cpp
deleted file mode 100644
index 0a75bc2b..00000000
--- a/NorthstarDLL/core/memalloc.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "core/memalloc.h"
-#include "core/tier0.h"
-
-// TODO: rename to malloc and free after removing statically compiled .libs
-
-extern "C" void* _malloc_base(size_t n)
-{
- // allocate into static buffer if g_pMemAllocSingleton isn't initialised
- if (!g_pMemAllocSingleton)
- TryCreateGlobalMemAlloc();
-
- return g_pMemAllocSingleton->m_vtable->Alloc(g_pMemAllocSingleton, n);
-}
-
-/*extern "C" void* malloc(size_t n)
-{
- return _malloc_base(n);
-}*/
-
-extern "C" void _free_base(void* p)
-{
- if (!g_pMemAllocSingleton)
- TryCreateGlobalMemAlloc();
-
- g_pMemAllocSingleton->m_vtable->Free(g_pMemAllocSingleton, p);
-}
-
-extern "C" void* _realloc_base(void* oldPtr, size_t size)
-{
- if (!g_pMemAllocSingleton)
- TryCreateGlobalMemAlloc();
-
- return g_pMemAllocSingleton->m_vtable->Realloc(g_pMemAllocSingleton, oldPtr, size);
-}
-
-extern "C" void* _calloc_base(size_t n, size_t size)
-{
- size_t bytes = n * size;
- void* memory = _malloc_base(bytes);
- if (memory)
- {
- memset(memory, 0, bytes);
- }
- return memory;
-}
-
-extern "C" char* _strdup_base(const char* src)
-{
- char* str;
- char* p;
- int len = 0;
-
- while (src[len])
- len++;
- str = reinterpret_cast<char*>(_malloc_base(len + 1));
- p = str;
- while (*src)
- *p++ = *src++;
- *p = '\0';
- return str;
-}
-
-void* operator new(size_t n)
-{
- return _malloc_base(n);
-}
-
-void operator delete(void* p) noexcept
-{
- _free_base(p);
-} // /FORCE:MULTIPLE
diff --git a/NorthstarDLL/core/memalloc.h b/NorthstarDLL/core/memalloc.h
deleted file mode 100644
index 2f383335..00000000
--- a/NorthstarDLL/core/memalloc.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-#include "rapidjson/document.h"
-// #include "include/rapidjson/allocators.h"
-
-extern "C" void* _malloc_base(size_t size);
-extern "C" void* _calloc_base(size_t const count, size_t const size);
-extern "C" void* _realloc_base(void* block, size_t size);
-extern "C" void* _recalloc_base(void* const block, size_t const count, size_t const size);
-extern "C" void _free_base(void* const block);
-extern "C" char* _strdup_base(const char* src);
-
-void* operator new(size_t n);
-void operator delete(void* p) noexcept;
-
-// void* malloc(size_t n);
-
-class SourceAllocator
-{
-public:
- static const bool kNeedFree = true;
- void* Malloc(size_t size)
- {
- if (size) // behavior of malloc(0) is implementation defined.
- return _malloc_base(size);
- else
- return NULL; // standardize to returning NULL.
- }
- void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
- {
- (void)originalSize;
- if (newSize == 0)
- {
- _free_base(originalPtr);
- return NULL;
- }
- return _realloc_base(originalPtr, newSize);
- }
- static void Free(void* ptr)
- {
- _free_base(ptr);
- }
-};
-
-typedef rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<SourceAllocator>, SourceAllocator> rapidjson_document;
-// typedef rapidjson::GenericDocument<rapidjson::UTF8<>, SourceAllocator, SourceAllocator> rapidjson_document;
-// typedef rapidjson::Document rapidjson_document;
-// using MyDocument = rapidjson::GenericDocument<rapidjson::UTF8<>, MemoryAllocator>;
-// using rapidjson_document = rapidjson::GenericDocument<rapidjson::UTF8<>, SourceAllocator, SourceAllocator>;
diff --git a/NorthstarDLL/core/memory.cpp b/NorthstarDLL/core/memory.cpp
deleted file mode 100644
index 3770586f..00000000
--- a/NorthstarDLL/core/memory.cpp
+++ /dev/null
@@ -1,347 +0,0 @@
-#include "memory.h"
-
-CMemoryAddress::CMemoryAddress() : m_nAddress(0) {}
-CMemoryAddress::CMemoryAddress(const uintptr_t nAddress) : m_nAddress(nAddress) {}
-CMemoryAddress::CMemoryAddress(const void* pAddress) : m_nAddress(reinterpret_cast<uintptr_t>(pAddress)) {}
-
-// operators
-CMemoryAddress::operator uintptr_t() const
-{
- return m_nAddress;
-}
-
-CMemoryAddress::operator void*() const
-{
- return reinterpret_cast<void*>(m_nAddress);
-}
-
-CMemoryAddress::operator bool() const
-{
- return m_nAddress != 0;
-}
-
-bool CMemoryAddress::operator==(const CMemoryAddress& other) const
-{
- return m_nAddress == other.m_nAddress;
-}
-
-bool CMemoryAddress::operator!=(const CMemoryAddress& other) const
-{
- return m_nAddress != other.m_nAddress;
-}
-
-bool CMemoryAddress::operator==(const uintptr_t& addr) const
-{
- return m_nAddress == addr;
-}
-
-bool CMemoryAddress::operator!=(const uintptr_t& addr) const
-{
- return m_nAddress != addr;
-}
-
-CMemoryAddress CMemoryAddress::operator+(const CMemoryAddress& other) const
-{
- return Offset(other.m_nAddress);
-}
-
-CMemoryAddress CMemoryAddress::operator-(const CMemoryAddress& other) const
-{
- return CMemoryAddress(m_nAddress - other.m_nAddress);
-}
-
-CMemoryAddress CMemoryAddress::operator+(const uintptr_t& addr) const
-{
- return Offset(addr);
-}
-
-CMemoryAddress CMemoryAddress::operator-(const uintptr_t& addr) const
-{
- return CMemoryAddress(m_nAddress - addr);
-}
-
-CMemoryAddress CMemoryAddress::operator*() const
-{
- return Deref();
-}
-
-// traversal
-CMemoryAddress CMemoryAddress::Offset(const uintptr_t nOffset) const
-{
- return CMemoryAddress(m_nAddress + nOffset);
-}
-
-CMemoryAddress CMemoryAddress::Deref(const int nNumDerefs) const
-{
- uintptr_t ret = m_nAddress;
- for (int i = 0; i < nNumDerefs; i++)
- ret = *reinterpret_cast<uintptr_t*>(ret);
-
- return CMemoryAddress(ret);
-}
-
-// patching
-void CMemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize)
-{
- if (nSize)
- WriteProcessMemory(GetCurrentProcess(), reinterpret_cast<LPVOID>(m_nAddress), pBytes, nSize, NULL);
-}
-
-void CMemoryAddress::Patch(const std::initializer_list<uint8_t> bytes)
-{
- uint8_t* pBytes = new uint8_t[bytes.size()];
-
- int i = 0;
- for (const uint8_t& byte : bytes)
- pBytes[i++] = byte;
-
- Patch(pBytes, bytes.size());
- delete[] pBytes;
-}
-
-inline std::vector<uint8_t> HexBytesToString(const char* pHexString)
-{
- std::vector<uint8_t> ret;
-
- int size = strlen(pHexString);
- for (int i = 0; i < size; i++)
- {
- // If this is a space character, ignore it
- if (isspace(pHexString[i]))
- continue;
-
- if (i < size - 1)
- {
- BYTE result = 0;
- for (int j = 0; j < 2; j++)
- {
- int val = 0;
- char c = *(pHexString + i + j);
- if (c >= 'a')
- {
- val = c - 'a' + 0xA;
- }
- else if (c >= 'A')
- {
- val = c - 'A' + 0xA;
- }
- else if (isdigit(c))
- {
- val = c - '0';
- }
- else
- {
- assert_msg(false, "Failed to parse invalid hex string.");
- val = -1;
- }
-
- result += (j == 0) ? val * 16 : val;
- }
- ret.push_back(result);
- }
-
- i++;
- }
-
- return ret;
-}
-
-void CMemoryAddress::Patch(const char* pBytes)
-{
- std::vector<uint8_t> vBytes = HexBytesToString(pBytes);
- Patch(vBytes.data(), vBytes.size());
-}
-
-void CMemoryAddress::NOP(const size_t nSize)
-{
- uint8_t* pBytes = new uint8_t[nSize];
-
- memset(pBytes, 0x90, nSize);
- Patch(pBytes, nSize);
-
- delete[] pBytes;
-}
-
-bool CMemoryAddress::IsMemoryReadable(const size_t nSize)
-{
- static SYSTEM_INFO sysInfo;
- if (!sysInfo.dwPageSize)
- GetSystemInfo(&sysInfo);
-
- MEMORY_BASIC_INFORMATION memInfo;
- if (!VirtualQuery(reinterpret_cast<LPCVOID>(m_nAddress), &memInfo, sizeof(memInfo)))
- return false;
-
- return memInfo.RegionSize >= nSize && memInfo.State & MEM_COMMIT && !(memInfo.Protect & PAGE_NOACCESS);
-}
-
-CModule::CModule(const HMODULE pModule)
-{
- MODULEINFO mInfo {0};
-
- if (pModule && pModule != INVALID_HANDLE_VALUE)
- GetModuleInformation(GetCurrentProcess(), pModule, &mInfo, sizeof(MODULEINFO));
-
- m_nModuleSize = static_cast<size_t>(mInfo.SizeOfImage);
- m_pModuleBase = reinterpret_cast<uintptr_t>(mInfo.lpBaseOfDll);
- m_nAddress = m_pModuleBase;
-
- if (!m_nModuleSize || !m_pModuleBase)
- return;
-
- m_pDOSHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(m_pModuleBase);
- m_pNTHeaders = reinterpret_cast<IMAGE_NT_HEADERS64*>(m_pModuleBase + m_pDOSHeader->e_lfanew);
-
- const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(m_pNTHeaders); // Get first image section.
-
- for (WORD i = 0; i < m_pNTHeaders->FileHeader.NumberOfSections; i++) // Loop through the sections.
- {
- const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section.
-
- ModuleSections_t moduleSection = ModuleSections_t(
- std::string(reinterpret_cast<const char*>(hCurrentSection.Name)),
- static_cast<uintptr_t>(m_pModuleBase + hCurrentSection.VirtualAddress),
- hCurrentSection.SizeOfRawData);
-
- if (!strcmp((const char*)hCurrentSection.Name, ".text"))
- m_ExecutableCode = moduleSection;
- else if (!strcmp((const char*)hCurrentSection.Name, ".pdata"))
- m_ExceptionTable = moduleSection;
- else if (!strcmp((const char*)hCurrentSection.Name, ".data"))
- m_RunTimeData = moduleSection;
- else if (!strcmp((const char*)hCurrentSection.Name, ".rdata"))
- m_ReadOnlyData = moduleSection;
-
- m_vModuleSections.push_back(moduleSection); // Push back a struct with the section data.
- }
-}
-
-CModule::CModule(const char* pModuleName) : CModule(GetModuleHandleA(pModuleName)) {}
-
-CMemoryAddress CModule::GetExport(const char* pExportName)
-{
- return CMemoryAddress(reinterpret_cast<uintptr_t>(GetProcAddress(reinterpret_cast<HMODULE>(m_nAddress), pExportName)));
-}
-
-CMemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask)
-{
- if (!m_ExecutableCode.IsSectionValid())
- return CMemoryAddress();
-
- uint64_t nBase = static_cast<uint64_t>(m_ExecutableCode.m_pSectionBase);
- uint64_t nSize = static_cast<uint64_t>(m_ExecutableCode.m_nSectionSize);
-
- const uint8_t* pData = reinterpret_cast<uint8_t*>(nBase);
- const uint8_t* pEnd = pData + static_cast<uint32_t>(nSize) - strlen(pMask);
-
- int nMasks[64]; // 64*16 = enough masks for 1024 bytes.
- int iNumMasks = static_cast<int>(ceil(static_cast<float>(strlen(pMask)) / 16.f));
-
- memset(nMasks, '\0', iNumMasks * sizeof(int));
- for (intptr_t i = 0; i < iNumMasks; ++i)
- {
- for (intptr_t j = strnlen(pMask + i * 16, 16) - 1; j >= 0; --j)
- {
- if (pMask[i * 16 + j] == 'x')
- {
- _bittestandset(reinterpret_cast<LONG*>(&nMasks[i]), j);
- }
- }
- }
- __m128i xmm1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pPattern));
- __m128i xmm2, xmm3, msks;
- for (; pData != pEnd; _mm_prefetch(reinterpret_cast<const char*>(++pData + 64), _MM_HINT_NTA))
- {
- if (pPattern[0] == pData[0])
- {
- xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pData));
- msks = _mm_cmpeq_epi8(xmm1, xmm2);
- if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0])
- {
- for (uintptr_t i = 1; i < static_cast<uintptr_t>(iNumMasks); ++i)
- {
- xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pData + i * 16)));
- xmm3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pPattern + i * 16)));
- msks = _mm_cmpeq_epi8(xmm2, xmm3);
- if ((_mm_movemask_epi8(msks) & nMasks[i]) == nMasks[i])
- {
- if ((i + 1) == iNumMasks)
- {
- return CMemoryAddress(const_cast<uint8_t*>(pData));
- }
- }
- else
- goto CONTINUE;
- }
-
- return CMemoryAddress((&*(const_cast<uint8_t*>(pData))));
- }
- }
-
- CONTINUE:;
- }
-
- return CMemoryAddress();
-}
-
-inline std::pair<std::vector<uint8_t>, std::string> MaskedBytesFromPattern(const char* pPatternString)
-{
- std::vector<uint8_t> vRet;
- std::string sMask;
-
- int size = strlen(pPatternString);
- for (int i = 0; i < size; i++)
- {
- // If this is a space character, ignore it
- if (isspace(pPatternString[i]))
- continue;
-
- if (pPatternString[i] == '?')
- {
- // Add a wildcard
- vRet.push_back(0);
- sMask.append("?");
- }
- else if (i < size - 1)
- {
- BYTE result = 0;
- for (int j = 0; j < 2; j++)
- {
- int val = 0;
- char c = *(pPatternString + i + j);
- if (c >= 'a')
- {
- val = c - 'a' + 0xA;
- }
- else if (c >= 'A')
- {
- val = c - 'A' + 0xA;
- }
- else if (isdigit(c))
- {
- val = c - '0';
- }
- else
- {
- assert_msg(false, "Failed to parse invalid pattern string.");
- val = -1;
- }
-
- result += (j == 0) ? val * 16 : val;
- }
-
- vRet.push_back(result);
- sMask.append("x");
- }
-
- i++;
- }
-
- return std::make_pair(vRet, sMask);
-}
-
-CMemoryAddress CModule::FindPattern(const char* pPattern)
-{
- const auto pattern = MaskedBytesFromPattern(pPattern);
- return FindPattern(pattern.first.data(), pattern.second.c_str());
-}
diff --git a/NorthstarDLL/core/memory.h b/NorthstarDLL/core/memory.h
deleted file mode 100644
index a978963e..00000000
--- a/NorthstarDLL/core/memory.h
+++ /dev/null
@@ -1,90 +0,0 @@
-#pragma once
-
-class CMemoryAddress
-{
-public:
- uintptr_t m_nAddress;
-
-public:
- CMemoryAddress();
- CMemoryAddress(const uintptr_t nAddress);
- CMemoryAddress(const void* pAddress);
-
- // operators
- operator uintptr_t() const;
- operator void*() const;
- operator bool() const;
-
- bool operator==(const CMemoryAddress& other) const;
- bool operator!=(const CMemoryAddress& other) const;
- bool operator==(const uintptr_t& addr) const;
- bool operator!=(const uintptr_t& addr) const;
-
- CMemoryAddress operator+(const CMemoryAddress& other) const;
- CMemoryAddress operator-(const CMemoryAddress& other) const;
- CMemoryAddress operator+(const uintptr_t& other) const;
- CMemoryAddress operator-(const uintptr_t& other) const;
- CMemoryAddress operator*() const;
-
- template <typename T> T RCast()
- {
- return reinterpret_cast<T>(m_nAddress);
- }
-
- // traversal
- CMemoryAddress Offset(const uintptr_t nOffset) const;
- CMemoryAddress Deref(const int nNumDerefs = 1) const;
-
- // patching
- void Patch(const uint8_t* pBytes, const size_t nSize);
- void Patch(const std::initializer_list<uint8_t> bytes);
- void Patch(const char* pBytes);
- void NOP(const size_t nSize);
-
- bool IsMemoryReadable(const size_t nSize);
-};
-
-// based on https://github.com/Mauler125/r5sdk/blob/master/r5dev/public/include/module.h
-class CModule : public CMemoryAddress
-{
-public:
- struct ModuleSections_t
- {
- ModuleSections_t(void) = default;
- ModuleSections_t(const std::string& svSectionName, uintptr_t pSectionBase, size_t nSectionSize)
- : m_svSectionName(svSectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize)
- {
- }
-
- bool IsSectionValid(void) const
- {
- return m_nSectionSize != 0;
- }
-
- std::string m_svSectionName; // Name of section.
- uintptr_t m_pSectionBase {}; // Start address of section.
- size_t m_nSectionSize {}; // Size of section.
- };
-
- ModuleSections_t m_ExecutableCode;
- ModuleSections_t m_ExceptionTable;
- ModuleSections_t m_RunTimeData;
- ModuleSections_t m_ReadOnlyData;
-
-private:
- std::string m_svModuleName;
- uintptr_t m_pModuleBase {};
- DWORD m_nModuleSize {};
- IMAGE_NT_HEADERS64* m_pNTHeaders = nullptr;
- IMAGE_DOS_HEADER* m_pDOSHeader = nullptr;
- std::vector<ModuleSections_t> m_vModuleSections;
-
-public:
- CModule() = delete; // no default, we need a module name
- CModule(const HMODULE pModule);
- CModule(const char* pModuleName);
-
- CMemoryAddress GetExport(const char* pExportName);
- CMemoryAddress FindPattern(const uint8_t* pPattern, const char* pMask);
- CMemoryAddress FindPattern(const char* pPattern);
-};
diff --git a/NorthstarDLL/core/sourceinterface.cpp b/NorthstarDLL/core/sourceinterface.cpp
deleted file mode 100644
index 5a72beb0..00000000
--- a/NorthstarDLL/core/sourceinterface.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "sourceinterface.h"
-#include "logging/sourceconsole.h"
-
-AUTOHOOK_INIT()
-
-// really wanted to do a modular callback system here but honestly couldn't be bothered so hardcoding stuff for now: todo later
-
-// clang-format off
-AUTOHOOK_PROCADDRESS(ClientCreateInterface, client.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
-{
- void* ret = ClientCreateInterface(pName, pReturnCode);
- spdlog::info("CreateInterface CLIENT {}", pName);
-
- if (!strcmp(pName, "GameClientExports001"))
- InitialiseConsoleOnInterfaceCreation();
-
- return ret;
-}
-
-// clang-format off
-AUTOHOOK_PROCADDRESS(ServerCreateInterface, server.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
-{
- void* ret = ServerCreateInterface(pName, pReturnCode);
- spdlog::info("CreateInterface SERVER {}", pName);
-
- return ret;
-}
-
-// clang-format off
-AUTOHOOK_PROCADDRESS(EngineCreateInterface, engine.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
-{
- void* ret = EngineCreateInterface(pName, pReturnCode);
- spdlog::info("CreateInterface ENGINE {}", pName);
-
- return ret;
-}
-
-// clang-format off
-ON_DLL_LOAD("client.dll", ClientInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(client.dll)}
-ON_DLL_LOAD("server.dll", ServerInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(server.dll)}
-ON_DLL_LOAD("engine.dll", EngineInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(engine.dll)}
-// clang-format on
diff --git a/NorthstarDLL/core/sourceinterface.h b/NorthstarDLL/core/sourceinterface.h
deleted file mode 100644
index 7b5e81f3..00000000
--- a/NorthstarDLL/core/sourceinterface.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-#include <string>
-
-// literally just copied from ttf2sdk definition
-typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);
-
-template <typename T> class SourceInterface
-{
-private:
- T* m_interface;
-
-public:
- SourceInterface(const std::string& moduleName, const std::string& interfaceName)
- {
- HMODULE handle = GetModuleHandleA(moduleName.c_str());
- CreateInterfaceFn createInterface = (CreateInterfaceFn)GetProcAddress(handle, "CreateInterface");
- m_interface = (T*)createInterface(interfaceName.c_str(), NULL);
- if (m_interface == nullptr)
- spdlog::error("Failed to call CreateInterface for %s in %s", interfaceName, moduleName);
- }
-
- T* operator->() const
- {
- return m_interface;
- }
-
- operator T*() const
- {
- return m_interface;
- }
-};
diff --git a/NorthstarDLL/core/structs.h b/NorthstarDLL/core/structs.h
deleted file mode 100644
index 037233a6..00000000
--- a/NorthstarDLL/core/structs.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-//clang-format off
-// About this file:
-// This file contains several macros used to define reversed structs
-// The reason we use these macros is to make it easier to update existing structs
-// when new fields are reversed
-// This means we dont have to manually add padding, and recalculate when updating
-
-// Technical note:
-// While functionally, these structs act like a regular struct, they are actually
-// defined as unions with anonymous structs in them.
-// This means that each field is essentially an offset into a union.
-// We acknowledge that this goes against C++'s active-member guideline for unions
-// However, this is not such a big deal here as these structs will not be constructed
-
-// Usage:
-// To use these macros, define a struct like so:
-/*
-OFFSET_STRUCT(Name)
-{
- STRUCT_SIZE(0x100) // Total in-memory struct size
- FIELD(0x0, int field) // offset, signature
-}
-*/
-
-#define OFFSET_STRUCT(name) union name
-#define STRUCT_SIZE(size) char __size[size];
-#define STRUCT_FIELD_OFFSET(offset, signature) \
- struct \
- { \
- char CONCAT2(pad, __LINE__)[offset]; \
- signature; \
- };
-
-// Special case for a 0-offset field
-#define STRUCT_FIELD_NOOFFSET(offset, signature) signature;
-
-// Just puts two tokens next to each other, but
-// allows us to force the preprocessor to do another pass
-#define FX(f, x) f x
-
-// Macro used to detect if the given offset is 0 or not
-#define TEST_0 ,
-// MSVC does no preprocessing of integer literals.
-// On other compilers `0x0` gets processed into `0`
-#define TEST_0x0 ,
-
-// Concats the first and third argument and drops everything else
-// Used with preprocessor expansion in later passes to move the third argument to the fourth and change the value
-#define ZERO_P_I(a, b, c, ...) a##c
-
-// We use FX to prepare to use ZERO_P_I.
-// The right block contains 3 arguments:
-// NIF_
-// CONCAT2(TEST_, offset)
-// 1
-//
-// If offset is not 0 (or 0x0) the preprocessor replaces
-// it with nothing and the third argument stays 1
-//
-// If the offset is 0, TEST_0 expands to , and 1 becomes the fourth argument
-//
-// With those arguments we call ZERO_P_I and the first and third arugment get concat.
-// We either end up with:
-// NIF_ (if offset is 0) or
-// NIF_1 (if offset is not 0)
-#define IF_ZERO(m) FX(ZERO_P_I, (NIF_, CONCAT2(TEST_, m), 1))
-
-// These macros are used to branch after we processed if the offset is zero or not
-#define NIF_(t, ...) t
-#define NIF_1(t, ...) __VA_ARGS__
-
-// FIELD(S), generates an anonymous struct when a non 0 offset is given, otherwise just a signature
-#define FIELD(offset, signature) IF_ZERO(offset)(STRUCT_FIELD_NOOFFSET, STRUCT_FIELD_OFFSET)(offset, signature)
-#define FIELDS FIELD
-
-//clang-format on
diff --git a/NorthstarDLL/core/tier0.cpp b/NorthstarDLL/core/tier0.cpp
deleted file mode 100644
index 1f59722c..00000000
--- a/NorthstarDLL/core/tier0.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "tier0.h"
-
-IMemAlloc* g_pMemAllocSingleton = nullptr;
-
-CommandLineType CommandLine;
-Plat_FloatTimeType Plat_FloatTime;
-ThreadInServerFrameThreadType ThreadInServerFrameThread;
-
-typedef IMemAlloc* (*CreateGlobalMemAllocType)();
-CreateGlobalMemAllocType CreateGlobalMemAlloc;
-
-// needs to be a seperate function, since memalloc.cpp calls it
-void TryCreateGlobalMemAlloc()
-{
- // init memalloc stuff
- CreateGlobalMemAlloc =
- reinterpret_cast<CreateGlobalMemAllocType>(GetProcAddress(GetModuleHandleA("tier0.dll"), "CreateGlobalMemAlloc"));
- g_pMemAllocSingleton = CreateGlobalMemAlloc(); // if it already exists, this returns the preexisting IMemAlloc instance
-}
-
-ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module))
-{
- // shouldn't be necessary, but do this just in case
- TryCreateGlobalMemAlloc();
-
- // setup tier0 funcs
- CommandLine = module.GetExport("CommandLine").RCast<CommandLineType>();
- Plat_FloatTime = module.GetExport("Plat_FloatTime").RCast<Plat_FloatTimeType>();
- ThreadInServerFrameThread = module.GetExport("ThreadInServerFrameThread").RCast<ThreadInServerFrameThreadType>();
-}
diff --git a/NorthstarDLL/core/tier0.h b/NorthstarDLL/core/tier0.h
deleted file mode 100644
index cc9af39e..00000000
--- a/NorthstarDLL/core/tier0.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#pragma once
-
-class IMemAlloc
-{
-public:
- struct VTable
- {
- void* unknown[1]; // alloc debug
- void* (*Alloc)(IMemAlloc* memAlloc, size_t nSize);
- void* unknown2[1]; // realloc debug
- void* (*Realloc)(IMemAlloc* memAlloc, void* pMem, size_t nSize);
- void* unknown3[1]; // free #1
- void (*Free)(IMemAlloc* memAlloc, void* pMem);
- void* unknown4[2]; // nullsubs, maybe CrtSetDbgFlag
- size_t (*GetSize)(IMemAlloc* memAlloc, void* pMem);
- void* unknown5[9]; // they all do literally nothing
- void (*DumpStats)(IMemAlloc* memAlloc);
- void (*DumpStatsFileBase)(IMemAlloc* memAlloc, const char* pchFileBase);
- void* unknown6[4];
- int (*heapchk)(IMemAlloc* memAlloc);
- };
-
- VTable* m_vtable;
-};
-
-class CCommandLine
-{
-public:
- // based on the defs in the 2013 source sdk, but for some reason has an extra function (may be another CreateCmdLine overload?)
- // these seem to line up with what they should be though
- virtual void CreateCmdLine(const char* commandline) = 0;
- virtual void CreateCmdLine(int argc, char** argv) = 0;
- virtual void unknown() = 0;
- virtual const char* GetCmdLine(void) const = 0;
-
- virtual const char* CheckParm(const char* psz, const char** ppszValue = 0) const = 0;
- virtual void RemoveParm() const = 0;
- virtual void AppendParm(const char* pszParm, const char* pszValues) = 0;
-
- virtual const char* ParmValue(const char* psz, const char* pDefaultVal = 0) const = 0;
- virtual int ParmValue(const char* psz, int nDefaultVal) const = 0;
- virtual float ParmValue(const char* psz, float flDefaultVal) const = 0;
-
- virtual int ParmCount() const = 0;
- virtual int FindParm(const char* psz) const = 0;
- virtual const char* GetParm(int nIndex) const = 0;
- virtual void SetParm(int nIndex, char const* pParm) = 0;
-
- // virtual const char** GetParms() const {}
-};
-
-extern IMemAlloc* g_pMemAllocSingleton;
-
-typedef CCommandLine* (*CommandLineType)();
-extern CommandLineType CommandLine;
-
-typedef double (*Plat_FloatTimeType)();
-extern Plat_FloatTimeType Plat_FloatTime;
-
-typedef bool (*ThreadInServerFrameThreadType)();
-extern ThreadInServerFrameThreadType ThreadInServerFrameThread;
-
-void TryCreateGlobalMemAlloc();
diff --git a/NorthstarDLL/core/vanilla.h b/NorthstarDLL/core/vanilla.h
deleted file mode 100644
index b0797803..00000000
--- a/NorthstarDLL/core/vanilla.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-
-/// Determines if we are in vanilla-compatibility mode.
-/// In this mode we shouldn't auth with Atlas, which prevents users from joining a
-/// non-trusted server. This means that we can unrestrict client/server commands
-/// as well as various other small changes for compatibility
-class VanillaCompatibility
-{
-public:
- void SetVanillaCompatibility(bool isVanilla)
- {
- static bool bInitialised = false;
- if (bInitialised)
- return;
-
- bInitialised = true;
- m_bIsVanillaCompatible = isVanilla;
- }
-
- bool GetVanillaCompatibility()
- {
- return m_bIsVanillaCompatible;
- }
-
-private:
- bool m_bIsVanillaCompatible = false;
-};
-
-inline VanillaCompatibility* g_pVanillaCompatibility;
diff --git a/NorthstarDLL/dedicated/dedicated.cpp b/NorthstarDLL/dedicated/dedicated.cpp
deleted file mode 100644
index df6e5787..00000000
--- a/NorthstarDLL/dedicated/dedicated.cpp
+++ /dev/null
@@ -1,296 +0,0 @@
-#include "dedicated.h"
-#include "dedicatedlogtoclient.h"
-#include "core/tier0.h"
-#include "shared/playlist.h"
-#include "engine/r2engine.h"
-#include "engine/hoststate.h"
-#include "server/auth/serverauthentication.h"
-#include "masterserver/masterserver.h"
-#include "util/printcommands.h"
-
-AUTOHOOK_INIT()
-
-bool IsDedicatedServer()
-{
- static bool result = strstr(GetCommandLineA(), "-dedicated");
- return result;
-}
-
-// CDedidcatedExports defs
-struct CDedicatedExports; // forward declare
-
-typedef void (*DedicatedSys_PrintfType)(CDedicatedExports* dedicated, const char* msg);
-typedef void (*DedicatedRunServerType)(CDedicatedExports* dedicated);
-
-// would've liked to just do this as a class but have not been able to get it to work
-struct CDedicatedExports
-{
- void* vtable; // because it's easier, we just set this to &this, since CDedicatedExports has no props we care about other than funcs
-
- char unused[56];
-
- DedicatedSys_PrintfType Sys_Printf;
- DedicatedRunServerType RunServer;
-};
-
-void Sys_Printf(CDedicatedExports* dedicated, const char* msg)
-{
- spdlog::info("[DEDICATED SERVER] {}", msg);
-}
-
-void RunServer(CDedicatedExports* dedicated)
-{
- spdlog::info("CDedicatedExports::RunServer(): starting");
- spdlog::info(CommandLine()->GetCmdLine());
-
- // initialise engine
- g_pEngine->Frame();
-
- // add +map if no map loading command is present
- // don't manually execute this from cbuf as users may have it in their startup args anyway, easier just to run from stuffcmds if present
- if (!CommandLine()->CheckParm("+map") && !CommandLine()->CheckParm("+launchplaylist"))
- CommandLine()->AppendParm("+map", g_pCVar->FindVar("match_defaultMap")->GetString());
-
- // re-run commandline
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "stuffcmds", cmd_source_t::kCommandSrcCode);
- Cbuf_Execute();
-
- // main loop
- double frameTitle = 0;
- while (g_pEngine->m_nQuitting == EngineQuitState::QUIT_NOTQUITTING)
- {
- double frameStart = Plat_FloatTime();
- g_pEngine->Frame();
-
- std::this_thread::sleep_for(
- std::chrono::duration<double, std::ratio<1>>(g_pGlobals->m_flTickInterval - fmin(Plat_FloatTime() - frameStart, 0.25)));
- }
-}
-
-// use server presence to update window title
-class DedicatedConsoleServerPresence : public ServerPresenceReporter
-{
- void ReportPresence(const ServerPresence* pServerPresence) override
- {
- SetConsoleTitleA(fmt::format(
- "{} - {} {}/{} players ({})",
- pServerPresence->m_sServerName,
- pServerPresence->m_MapName,
- pServerPresence->m_iPlayerCount,
- pServerPresence->m_iMaxPlayers,
- pServerPresence->m_PlaylistName)
- .c_str());
- }
-};
-
-HANDLE consoleInputThreadHandle = NULL;
-DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter)
-{
- while (!g_pEngine || !g_pHostState || g_pHostState->m_iCurrentState != HostState_t::HS_RUN)
- Sleep(1000);
-
- // Bind stdin to receive console input.
- FILE* fp = nullptr;
- freopen_s(&fp, "CONIN$", "r", stdin);
-
- spdlog::info("Ready to receive console commands.");
-
- {
- // Process console input
- std::string input;
- while (g_pEngine && g_pEngine->m_nQuitting == EngineQuitState::QUIT_NOTQUITTING && std::getline(std::cin, input))
- {
- input += "\n";
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), input.c_str(), cmd_source_t::kCommandSrcCode);
- TryPrintCvarHelpForCommand(input.c_str()); // this needs to be done on main thread, unstable in this one
- }
- }
-
- return 0;
-}
-
-// clang-format off
-AUTOHOOK(IsGameActiveWindow, engine.dll + 0x1CDC80,
-bool,, ())
-// clang-format on
-{
- return true;
-}
-
-ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModule module))
-{
- spdlog::info("InitialiseDedicated");
-
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
-
- // Host_Init
- // prevent a particle init that relies on client dll
- module.Offset(0x156799).NOP(5);
-
- // Host_Init
- // don't call Key_Init to avoid loading some extra rsons from rpak (will be necessary to boot if we ever wanna disable rpaks entirely on
- // dedi)
- module.Offset(0x1565B0).NOP(5);
-
- {
- // CModAppSystemGroup::Create
- // force the engine into dedicated mode by changing the first comparison to IsServerOnly to an assignment
- CMemoryAddress base = module.Offset(0x1C4EBD);
-
- // cmp => mov
- base.Offset(1).Patch("C6 87");
-
- // 00 => 01
- base.Offset(7).Patch("01");
- }
-
- // Some init that i'm not sure of that crashes
- // nop the call to it
- module.Offset(0x156A63).NOP(5);
-
- // runframeserver
- // nop some access violations
- module.Offset(0x159819).NOP(17);
-
- module.Offset(0x156B4C).NOP(7);
-
- // previously patched these, took me a couple weeks to figure out they were the issue
- // removing these will mess up register state when this function is over, so we'll write HS_RUN to the wrong address
- // so uhh, don't do that
- // NSMem::NOP(ea + 0x156B4C + 7, 8);
- module.Offset(0x156B4C).Offset(15).NOP(9);
-
- // HostState_State_NewGame
- // nop an access violation
- module.Offset(0xB934C).NOP(9);
-
- // CEngineAPI::Connect
- // remove call to Shader_Connect
- module.Offset(0x1C4D7D).NOP(5);
-
- // Host_Init
- // remove call to ui loading stuff
- module.Offset(0x156595).NOP(5);
-
- // some function that gets called from RunFrameServer
- // nop a function that makes requests to stryder, this will eventually access violation if left alone and isn't necessary anyway
- module.Offset(0x15A0BB).NOP(5);
-
- // RunFrameServer
- // nop a function that access violations
- module.Offset(0x159BF3).NOP(5);
-
- // func that checks if origin is inited
- // always return 1
- module.Offset(0x183B70).Patch("B0 01 C3"); // mov al,01 ret
-
- // HostState_State_ChangeLevel
- // nop clientinterface call
- module.Offset(0x1552ED).NOP(16);
-
- // HostState_State_ChangeLevel
- // nop clientinterface call
- module.Offset(0x155363).NOP(16);
-
- // IVideoMode::CreateGameWindow
- // nop call to ShowWindow
- module.Offset(0x1CD146).NOP(5);
-
- CDedicatedExports* dedicatedExports = new CDedicatedExports;
- dedicatedExports->vtable = dedicatedExports;
- dedicatedExports->Sys_Printf = Sys_Printf;
- dedicatedExports->RunServer = RunServer;
-
- *module.Offset(0x13F0B668).RCast<CDedicatedExports**>() = dedicatedExports;
-
- // extra potential patches:
- // nop engine.dll+1c67d1 and +1c67d8 to skip videomode creategamewindow
- // also look into launcher.dll+d381, seems to cause renderthread to get made
- // this crashes HARD if no window which makes sense tbh
- // also look into materialsystem + 5B344 since it seems to be the base of all the renderthread stuff
-
- // big note: datatable gets registered in window creation
- // make sure it still gets registered
-
- // add cmdline args that are good for dedi
- CommandLine()->AppendParm("-nomenuvid", 0);
- CommandLine()->AppendParm("-nosound", 0);
- CommandLine()->AppendParm("-windowed", 0);
- CommandLine()->AppendParm("-nomessagebox", 0);
- CommandLine()->AppendParm("+host_preload_shaders", "0");
- CommandLine()->AppendParm("+net_usesocketsforloopback", "1");
- CommandLine()->AppendParm("+community_frame_run", "0");
-
- // use presence reporter for console title
- DedicatedConsoleServerPresence* presenceReporter = new DedicatedConsoleServerPresence;
- g_pServerPresence->AddPresenceReporter(presenceReporter);
-
- // setup dedicated printing to client
- RegisterCustomSink(std::make_shared<DedicatedServerLogToClientSink>());
-
- // Disable Quick Edit mode to reduce chance of user unintentionally hanging their server by selecting something.
- if (!CommandLine()->CheckParm("-bringbackquickedit"))
- {
- HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE);
- DWORD mode = 0;
-
- if (GetConsoleMode(stdIn, &mode))
- {
- if (mode & ENABLE_QUICK_EDIT_MODE)
- {
- mode &= ~ENABLE_QUICK_EDIT_MODE;
- mode &= ~ENABLE_MOUSE_INPUT;
-
- mode |= ENABLE_PROCESSED_INPUT;
-
- SetConsoleMode(stdIn, mode);
- }
- }
- }
- else
- spdlog::info("Quick Edit enabled by user request");
-
- // create console input thread
- if (!CommandLine()->CheckParm("-noconsoleinput"))
- consoleInputThreadHandle = CreateThread(0, 0, ConsoleInputThread, 0, 0, NULL);
- else
- spdlog::info("Console input disabled by user request");
-}
-
-ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, (CModule module))
-{
- // disable origin on dedicated
- // for any big ea lawyers, this can't be used to play the game without origin, game will throw a fit if you try to do anything without
- // an origin id as a client for dedi it's fine though, game doesn't care if origin is disabled as long as there's only a server
- module.GetExport("Tier0_InitOrigin").Patch("C3");
-}
-
-// clang-format off
-AUTOHOOK(PrintSquirrelError, server.dll + 0x794D0,
-void, __fastcall, (void* sqvm))
-// clang-format on
-{
- PrintSquirrelError(sqvm);
-
- // close dedicated server if a fatal error is hit
- // atm, this will crash if not aborted, so this just closes more gracefully
- static ConVar* Cvar_fatal_script_errors = g_pCVar->FindVar("fatal_script_errors");
- if (Cvar_fatal_script_errors->GetBool())
- {
- NS::log::FlushLoggers();
- abort();
- }
-}
-
-ON_DLL_LOAD_DEDI("server.dll", DedicatedServerGameDLL, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(server.dll)
-
- if (CommandLine()->CheckParm("-nopakdedi"))
- {
- module.Offset(0x6BA350).Patch("C3"); // dont load skins.rson from rpak if we don't have rpaks, as loading it will cause a crash
- module.Offset(0x6BA300).Patch(
- "B8 C8 00 00 00 C3"); // return 200 as the number of skins from server.dll + 6BA300, this is the normal value read from
- // skins.rson and should be updated when we need it more modular
- }
-}
diff --git a/NorthstarDLL/dedicated/dedicated.h b/NorthstarDLL/dedicated/dedicated.h
deleted file mode 100644
index 82806763..00000000
--- a/NorthstarDLL/dedicated/dedicated.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-bool IsDedicatedServer();
diff --git a/NorthstarDLL/dedicated/dedicatedlogtoclient.cpp b/NorthstarDLL/dedicated/dedicatedlogtoclient.cpp
deleted file mode 100644
index bf2cf77a..00000000
--- a/NorthstarDLL/dedicated/dedicatedlogtoclient.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "dedicatedlogtoclient.h"
-#include "engine/r2engine.h"
-
-void (*CGameClient__ClientPrintf)(CBaseClient* pClient, const char* fmt, ...);
-
-void DedicatedServerLogToClientSink::custom_sink_it_(const custom_log_msg& msg)
-{
- if (*g_pServerState == server_state_t::ss_dead)
- return;
-
- enum class eSendPrintsToClient
- {
- NONE = -1,
- FIRST,
- ALL
- };
-
- static const ConVar* Cvar_dedi_sendPrintsToClient = g_pCVar->FindVar("dedi_sendPrintsToClient");
- eSendPrintsToClient eSendPrints = static_cast<eSendPrintsToClient>(Cvar_dedi_sendPrintsToClient->GetInt());
- if (eSendPrints == eSendPrintsToClient::NONE)
- return;
-
- std::string sLogMessage = fmt::format("[DEDICATED SERVER] [{}] {}", level_names[msg.level], msg.payload);
- for (int i = 0; i < g_pGlobals->m_nMaxClients; i++)
- {
- CBaseClient* pClient = &g_pClientArray[i];
-
- if (pClient->m_Signon >= eSignonState::CONNECTED)
- {
- CGameClient__ClientPrintf(pClient, sLogMessage.c_str());
-
- if (eSendPrints == eSendPrintsToClient::FIRST)
- break;
- }
- }
-}
-
-void DedicatedServerLogToClientSink::sink_it_(const spdlog::details::log_msg& msg)
-{
- throw std::runtime_error("sink_it_ called on DedicatedServerLogToClientSink with pure log_msg. This is an error!");
-}
-
-void DedicatedServerLogToClientSink::flush_() {}
-
-ON_DLL_LOAD_DEDI("engine.dll", DedicatedServerLogToClient, (CModule module))
-{
- CGameClient__ClientPrintf = module.Offset(0x1016A0).RCast<void (*)(CBaseClient*, const char*, ...)>();
-}
diff --git a/NorthstarDLL/dedicated/dedicatedlogtoclient.h b/NorthstarDLL/dedicated/dedicatedlogtoclient.h
deleted file mode 100644
index 82f4c56b..00000000
--- a/NorthstarDLL/dedicated/dedicatedlogtoclient.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-#include "logging/logging.h"
-#include "core/convar/convar.h"
-
-class DedicatedServerLogToClientSink : public CustomSink
-{
-protected:
- void custom_sink_it_(const custom_log_msg& msg);
- void sink_it_(const spdlog::details::log_msg& msg) override;
- void flush_() override;
-};
diff --git a/NorthstarDLL/dedicated/dedicatedmaterialsystem.cpp b/NorthstarDLL/dedicated/dedicatedmaterialsystem.cpp
deleted file mode 100644
index 01078086..00000000
--- a/NorthstarDLL/dedicated/dedicatedmaterialsystem.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "dedicated.h"
-#include "core/tier0.h"
-
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(D3D11CreateDevice, materialsystem_dx11.dll + 0xD9A0E,
-HRESULT, __stdcall, (
- void* pAdapter,
- int DriverType,
- HMODULE Software,
- UINT Flags,
- int* pFeatureLevels,
- UINT FeatureLevels,
- UINT SDKVersion,
- void** ppDevice,
- int* pFeatureLevel,
- void** ppImmediateContext))
-// clang-format on
-{
- // note: this is super duper temp pretty much just messing around with it
- // does run surprisingly well on dedi for a software driver tho if you ignore the +1gb ram usage at times, seems like dedi doesn't
- // really call gpu much even with renderthread still being a thing will be using this hook for actual d3d stubbing and stuff later
-
- // note: this has been succeeded by the d3d11 and gfsdk stubs, and is only being kept around for posterity and as a fallback option
- if (CommandLine()->CheckParm("-softwared3d11"))
- DriverType = 5; // D3D_DRIVER_TYPE_WARP
-
- return D3D11CreateDevice(
- pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, ppDevice, pFeatureLevel, ppImmediateContext);
-}
-
-ON_DLL_LOAD_DEDI("materialsystem_dx11.dll", DedicatedServerMaterialSystem, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- // CMaterialSystem::FindMaterial
- // make the game always use the error material
- module.Offset(0x5F0F1).Patch("E9 34 03 00");
-}
diff --git a/NorthstarDLL/dllmain.cpp b/NorthstarDLL/dllmain.cpp
deleted file mode 100644
index 3d9bdc97..00000000
--- a/NorthstarDLL/dllmain.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "dllmain.h"
-#include "logging/logging.h"
-#include "logging/crashhandler.h"
-#include "core/memalloc.h"
-#include "core/vanilla.h"
-#include "config/profile.h"
-#include "plugins/plugin_abi.h"
-#include "plugins/plugins.h"
-#include "plugins/pluginbackend.h"
-#include "util/version.h"
-#include "squirrel/squirrel.h"
-#include "server/serverpresence.h"
-
-#include "rapidjson/document.h"
-#include "rapidjson/stringbuffer.h"
-#include "rapidjson/writer.h"
-#include "rapidjson/error/en.h"
-
-#include <string.h>
-#include <filesystem>
-
-BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
-{
- switch (ul_reason_for_call)
- {
- case DLL_PROCESS_ATTACH:
- case DLL_THREAD_ATTACH:
- case DLL_THREAD_DETACH:
- case DLL_PROCESS_DETACH:
- break;
- }
-
- return TRUE;
-}
-
-bool InitialiseNorthstar()
-{
- static bool bInitialised = false;
- if (bInitialised)
- return false;
-
- bInitialised = true;
-
- InitialiseNorthstarPrefix();
-
- // initialise the console if needed (-northstar needs this)
- InitialiseConsole();
- // initialise logging before most other things so that they can use spdlog and it have the proper formatting
- InitialiseLogging();
- InitialiseVersion();
- CreateLogFiles();
-
- g_pCrashHandler = new CCrashHandler();
- bool bAllFatal = strstr(GetCommandLineA(), "-crash_handle_all") != NULL;
- g_pCrashHandler->SetAllFatal(bAllFatal);
-
- // determine if we are in vanilla-compatibility mode
- g_pVanillaCompatibility = new VanillaCompatibility();
- g_pVanillaCompatibility->SetVanillaCompatibility(strstr(GetCommandLineA(), "-vanilla") != NULL);
-
- // Write launcher version to log
- StartupLog();
-
- InstallInitialHooks();
-
- g_pServerPresence = new ServerPresenceManager();
-
- g_pPluginManager = new PluginManager();
- g_pPluginCommunicationhandler = new PluginCommunicationHandler();
- g_pPluginManager->LoadPlugins();
-
- InitialiseSquirrelManagers();
-
- // Fix some users' failure to connect to respawn datacenters
- SetEnvironmentVariableA("OPENSSL_ia32cap", "~0x200000200000000");
-
- curl_global_init_mem(CURL_GLOBAL_DEFAULT, _malloc_base, _free_base, _realloc_base, _strdup_base, _calloc_base);
-
- // run callbacks for any libraries that are already loaded by now
- CallAllPendingDLLLoadCallbacks();
-
- return true;
-}
diff --git a/NorthstarDLL/dllmain.h b/NorthstarDLL/dllmain.h
deleted file mode 100644
index 0debf379..00000000
--- a/NorthstarDLL/dllmain.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-extern "C" __declspec(dllexport) bool InitialiseNorthstar();
diff --git a/NorthstarDLL/engine/host.cpp b/NorthstarDLL/engine/host.cpp
deleted file mode 100644
index dacb8fc1..00000000
--- a/NorthstarDLL/engine/host.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "core/convar/convar.h"
-#include "mods/modmanager.h"
-#include "util/printcommands.h"
-#include "util/printmaps.h"
-#include "shared/misccommands.h"
-#include "r2engine.h"
-#include "core/tier0.h"
-
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(Host_Init, engine.dll + 0x155EA0,
-void, __fastcall, (bool bDedicated))
-// clang-format on
-{
- spdlog::info("Host_Init()");
- Host_Init(bDedicated);
- FixupCvarFlags();
- // need to initialise these after host_init since they do stuff to preexisting concommands/convars without being client/server specific
- InitialiseCommandPrint();
- InitialiseMapsPrint();
- // client/server autoexecs on necessary platforms
- // dedi needs autoexec_ns_server on boot, while non-dedi will run it on on listen server start
- if (bDedicated)
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode);
- else
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_client", cmd_source_t::kCommandSrcCode);
-}
-
-ON_DLL_LOAD("engine.dll", Host_Init, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/engine/hoststate.cpp b/NorthstarDLL/engine/hoststate.cpp
deleted file mode 100644
index ec728afb..00000000
--- a/NorthstarDLL/engine/hoststate.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-#include "engine/hoststate.h"
-#include "masterserver/masterserver.h"
-#include "server/auth/serverauthentication.h"
-#include "server/serverpresence.h"
-#include "shared/playlist.h"
-#include "core/tier0.h"
-#include "engine/r2engine.h"
-#include "shared/exploit_fixes/ns_limits.h"
-#include "squirrel/squirrel.h"
-#include "plugins/plugins.h"
-#include "plugins/pluginbackend.h"
-
-AUTOHOOK_INIT()
-
-CHostState* g_pHostState;
-
-std::string sLastMode;
-
-VAR_AT(engine.dll + 0x13FA6070, ConVar*, Cvar_hostport);
-FUNCTION_AT(engine.dll + 0x1232C0, void, __fastcall, _Cmd_Exec_f, (const CCommand& arg, bool bOnlyIfExists, bool bUseWhitelists));
-
-void ServerStartingOrChangingMap()
-{
- ConVar* Cvar_mp_gamemode = g_pCVar->FindVar("mp_gamemode");
-
- // directly call _Cmd_Exec_f to avoid weirdness with ; being in mp_gamemode potentially
- // if we ran exec {mp_gamemode} and mp_gamemode contained semicolons, this could be used to execute more commands
- char* commandBuf[1040]; // assumedly this is the size of CCommand since we don't have an actual constructor
- memset(commandBuf, 0, sizeof(commandBuf));
- CCommand tempCommand = *(CCommand*)&commandBuf;
- if (sLastMode.length() &&
- CCommand__Tokenize(tempCommand, fmt::format("exec server/cleanup_gamemode_{}", sLastMode).c_str(), cmd_source_t::kCommandSrcCode))
- _Cmd_Exec_f(tempCommand, false, false);
-
- memset(commandBuf, 0, sizeof(commandBuf));
- if (CCommand__Tokenize(
- tempCommand,
- fmt::format("exec server/setup_gamemode_{}", sLastMode = Cvar_mp_gamemode->GetString()).c_str(),
- cmd_source_t::kCommandSrcCode))
- {
- _Cmd_Exec_f(tempCommand, false, false);
- }
-
- Cbuf_Execute(); // exec everything right now
-
- // net_data_block_enabled is required for sp, force it if we're on an sp map
- // sucks for security but just how it be
- if (!strncmp(g_pHostState->m_levelName, "sp_", 3))
- {
- g_pCVar->FindVar("net_data_block_enabled")->SetValue(true);
- g_pServerAuthentication->m_bStartingLocalSPGame = true;
- }
- else
- g_pServerAuthentication->m_bStartingLocalSPGame = false;
-}
-
-// clang-format off
-AUTOHOOK(CHostState__State_NewGame, engine.dll + 0x16E7D0,
-void, __fastcall, (CHostState* self))
-// clang-format on
-{
- spdlog::info("HostState: NewGame");
-
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode);
- Cbuf_Execute();
-
- // need to do this to ensure we don't go to private match
- if (g_pServerAuthentication->m_bNeedLocalAuthForNewgame)
- R2::SetCurrentPlaylist("tdm");
-
- ServerStartingOrChangingMap();
-
- double dStartTime = Plat_FloatTime();
- CHostState__State_NewGame(self);
- spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime);
-
- // setup server presence
- g_pServerPresence->CreatePresence();
- g_pServerPresence->SetMap(g_pHostState->m_levelName, true);
- g_pServerPresence->SetPlaylist(R2::GetCurrentPlaylistName());
- g_pServerPresence->SetPort(Cvar_hostport->GetInt());
-
- g_pServerAuthentication->m_bNeedLocalAuthForNewgame = false;
-}
-
-// clang-format off
-AUTOHOOK(CHostState__State_LoadGame, engine.dll + 0x16E730,
-void, __fastcall, (CHostState* self))
-// clang-format on
-{
- // singleplayer server starting
- // useless in 99% of cases but without it things could potentially break very much
-
- spdlog::info("HostState: LoadGame");
-
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode);
- Cbuf_Execute();
-
- // this is normally done in ServerStartingOrChangingMap(), but seemingly the map name isn't set at this point
- g_pCVar->FindVar("net_data_block_enabled")->SetValue(true);
- g_pServerAuthentication->m_bStartingLocalSPGame = true;
-
- double dStartTime = Plat_FloatTime();
- CHostState__State_LoadGame(self);
- spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime);
-
- // no server presence, can't do it because no map name in hoststate
- // and also not super important for sp saves really
-
- g_pServerAuthentication->m_bNeedLocalAuthForNewgame = false;
-}
-
-// clang-format off
-AUTOHOOK(CHostState__State_ChangeLevelMP, engine.dll + 0x16E520,
-void, __fastcall, (CHostState* self))
-// clang-format on
-{
- spdlog::info("HostState: ChangeLevelMP");
-
- ServerStartingOrChangingMap();
-
- double dStartTime = Plat_FloatTime();
- CHostState__State_ChangeLevelMP(self);
- spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime);
-
- g_pServerPresence->SetMap(g_pHostState->m_levelName);
-}
-
-// clang-format off
-AUTOHOOK(CHostState__State_GameShutdown, engine.dll + 0x16E640,
-void, __fastcall, (CHostState* self))
-// clang-format on
-{
- spdlog::info("HostState: GameShutdown");
-
- g_pServerPresence->DestroyPresence();
-
- CHostState__State_GameShutdown(self);
-
- // run gamemode cleanup cfg now instead of when we start next map
- if (sLastMode.length())
- {
- char* commandBuf[1040]; // assumedly this is the size of CCommand since we don't have an actual constructor
- memset(commandBuf, 0, sizeof(commandBuf));
- CCommand tempCommand = *(CCommand*)&commandBuf;
- if (CCommand__Tokenize(
- tempCommand, fmt::format("exec server/cleanup_gamemode_{}", sLastMode).c_str(), cmd_source_t::kCommandSrcCode))
- {
- _Cmd_Exec_f(tempCommand, false, false);
- Cbuf_Execute();
- }
-
- sLastMode.clear();
- }
-}
-
-// clang-format off
-AUTOHOOK(CHostState__FrameUpdate, engine.dll + 0x16DB00,
-void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime))
-// clang-format on
-{
- CHostState__FrameUpdate(self, flCurrentTime, flFrameTime);
-
- if (*g_pServerState == server_state_t::ss_active)
- {
- // update server presence
- g_pServerPresence->RunFrame(flCurrentTime);
-
- // update limits for frame
- g_pServerLimits->RunFrame(flCurrentTime, flFrameTime);
- }
-
- // Run Squirrel message buffer
- if (g_pSquirrel<ScriptContext::UI>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::UI>->m_pSQVM->sqvm != nullptr)
- g_pSquirrel<ScriptContext::UI>->ProcessMessageBuffer();
-
- if (g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm != nullptr)
- g_pSquirrel<ScriptContext::CLIENT>->ProcessMessageBuffer();
-
- if (g_pSquirrel<ScriptContext::SERVER>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm != nullptr)
- g_pSquirrel<ScriptContext::SERVER>->ProcessMessageBuffer();
-
- g_pPluginManager->RunFrame();
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- g_pHostState = module.Offset(0x7CF180).RCast<CHostState*>();
-}
diff --git a/NorthstarDLL/engine/hoststate.h b/NorthstarDLL/engine/hoststate.h
deleted file mode 100644
index 290903ab..00000000
--- a/NorthstarDLL/engine/hoststate.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-enum class HostState_t
-{
- HS_NEW_GAME = 0,
- HS_LOAD_GAME,
- HS_CHANGE_LEVEL_SP,
- HS_CHANGE_LEVEL_MP,
- HS_RUN,
- HS_GAME_SHUTDOWN,
- HS_SHUTDOWN,
- HS_RESTART,
-};
-
-struct CHostState
-{
-public:
- HostState_t m_iCurrentState;
- HostState_t m_iNextState;
-
- float m_vecLocation[3];
- float m_angLocation[3];
-
- char m_levelName[32];
- char m_mapGroupName[32];
- char m_landmarkName[32];
- char m_saveName[32];
- float m_flShortFrameTime; // run a few one-tick frames to avoid large timesteps while loading assets
-
- bool m_activeGame;
- bool m_bRememberLocation;
- bool m_bBackgroundLevel;
- bool m_bWaitingForConnection;
- bool m_bLetToolsOverrideLoadGameEnts; // During a load game, this tells Foundry to override ents that are selected in Hammer.
- bool m_bSplitScreenConnect;
- bool m_bGameHasShutDownAndFlushedMemory; // This is false once we load a map into memory, and set to true once the map is unloaded
- // and all memory flushed
- bool m_bWorkshopMapDownloadPending;
-};
-
-extern CHostState* g_pHostState;
diff --git a/NorthstarDLL/engine/r2engine.cpp b/NorthstarDLL/engine/r2engine.cpp
deleted file mode 100644
index 88500376..00000000
--- a/NorthstarDLL/engine/r2engine.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "r2engine.h"
-
-Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer;
-Cbuf_AddTextType Cbuf_AddText;
-Cbuf_ExecuteType Cbuf_Execute;
-
-bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, cmd_source_t commandSource);
-
-CEngine* g_pEngine;
-
-void (*CBaseClient__Disconnect)(void* self, uint32_t unknownButAlways1, const char* reason, ...);
-CBaseClient* g_pClientArray;
-
-server_state_t* g_pServerState;
-
-char* g_pModName =
- nullptr; // we cant set this up here atm since we dont have an offset to it in engine, instead we store it in IsRespawnMod
-
-CGlobalVars* g_pGlobals;
-
-ON_DLL_LOAD("engine.dll", R2Engine, (CModule module))
-{
- Cbuf_GetCurrentPlayer = module.Offset(0x120630).RCast<Cbuf_GetCurrentPlayerType>();
- Cbuf_AddText = module.Offset(0x1203B0).RCast<Cbuf_AddTextType>();
- Cbuf_Execute = module.Offset(0x1204B0).RCast<Cbuf_ExecuteType>();
-
- CCommand__Tokenize = module.Offset(0x418380).RCast<bool (*)(CCommand&, const char*, cmd_source_t)>();
-
- g_pEngine = module.Offset(0x7D70C8).Deref().RCast<CEngine*>();
-
- CBaseClient__Disconnect = module.Offset(0x1012C0).RCast<void (*)(void*, uint32_t, const char*, ...)>();
- g_pClientArray = module.Offset(0x12A53F90).RCast<CBaseClient*>();
-
- g_pServerState = module.Offset(0x12A53D48).RCast<server_state_t*>();
-
- g_pGlobals = module.Offset(0x7C6F70).RCast<CGlobalVars*>();
-}
diff --git a/NorthstarDLL/engine/r2engine.h b/NorthstarDLL/engine/r2engine.h
deleted file mode 100644
index e3bcc37e..00000000
--- a/NorthstarDLL/engine/r2engine.h
+++ /dev/null
@@ -1,260 +0,0 @@
-#pragma once
-#include "shared/keyvalues.h"
-
-// Cbuf
-enum class ECommandTarget_t
-{
- CBUF_FIRST_PLAYER = 0,
- CBUF_LAST_PLAYER = 1, // MAX_SPLITSCREEN_CLIENTS - 1, MAX_SPLITSCREEN_CLIENTS = 2
- CBUF_SERVER = CBUF_LAST_PLAYER + 1,
-
- CBUF_COUNT,
-};
-
-enum class cmd_source_t
-{
- // Added to the console buffer by gameplay code. Generally unrestricted.
- kCommandSrcCode,
-
- // Sent from code via engine->ClientCmd, which is restricted to commands visible
- // via FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS.
- kCommandSrcClientCmd,
-
- // Typed in at the console or via a user key-bind. Generally unrestricted, although
- // the client will throttle commands sent to the server this way to 16 per second.
- kCommandSrcUserInput,
-
- // Came in over a net connection as a clc_stringcmd
- // host_client will be valid during this state.
- //
- // Restricted to FCVAR_GAMEDLL commands (but not convars) and special non-ConCommand
- // server commands hardcoded into gameplay code (e.g. "joingame")
- kCommandSrcNetClient,
-
- // Received from the server as the client
- //
- // Restricted to commands with FCVAR_SERVER_CAN_EXECUTE
- kCommandSrcNetServer,
-
- // Being played back from a demo file
- //
- // Not currently restricted by convar flag, but some commands manually ignore calls
- // from this source. FIXME: Should be heavily restricted as demo commands can come
- // from untrusted sources.
- kCommandSrcDemoFile,
-
- // Invalid value used when cleared
- kCommandSrcInvalid = -1
-};
-
-typedef ECommandTarget_t (*Cbuf_GetCurrentPlayerType)();
-extern Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer;
-
-typedef void (*Cbuf_AddTextType)(ECommandTarget_t eTarget, const char* text, cmd_source_t source);
-extern Cbuf_AddTextType Cbuf_AddText;
-
-typedef void (*Cbuf_ExecuteType)();
-extern Cbuf_ExecuteType Cbuf_Execute;
-
-extern bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, cmd_source_t commandSource);
-
-// CEngine
-
-enum EngineQuitState
-{
- QUIT_NOTQUITTING = 0,
- QUIT_TODESKTOP,
- QUIT_RESTART
-};
-
-enum class EngineState_t
-{
- DLL_INACTIVE = 0, // no dll
- DLL_ACTIVE, // engine is focused
- DLL_CLOSE, // closing down dll
- DLL_RESTART, // engine is shutting down but will restart right away
- DLL_PAUSED, // engine is paused, can become active from this state
-};
-
-class CEngine
-{
-public:
- virtual void unknown() = 0; // unsure if this is where
- virtual bool Load(bool dedicated, const char* baseDir) = 0;
- virtual void Unload() = 0;
- virtual void SetNextState(EngineState_t iNextState) = 0;
- virtual EngineState_t GetState() = 0;
- virtual void Frame() = 0;
- virtual double GetFrameTime() = 0;
- virtual float GetCurTime() = 0;
-
- EngineQuitState m_nQuitting;
- EngineState_t m_nDllState;
- EngineState_t m_nNextDllState;
- double m_flCurrentTime;
- float m_flFrameTime;
- double m_flPreviousTime;
- float m_flFilteredTime;
- float m_flMinFrameTime; // Expected duration of a frame, or zero if it is unlimited.
-};
-
-extern CEngine* g_pEngine;
-
-extern void (*CBaseClient__Disconnect)(void* self, uint32_t unknownButAlways1, const char* reason, ...);
-
-#pragma once
-typedef enum
-{
- NA_NULL = 0,
- NA_LOOPBACK,
- NA_IP,
-} netadrtype_t;
-
-#pragma pack(push, 1)
-typedef struct netadr_s
-{
- netadrtype_t type;
- unsigned char ip[16]; // IPv6
- // IPv4's 127.0.0.1 is [::ffff:127.0.0.1], that is:
- // 00 00 00 00 00 00 00 00 00 00 FF FF 7F 00 00 01
- unsigned short port;
-} netadr_t;
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-typedef struct netpacket_s
-{
- netadr_t adr; // sender address
- // int source; // received source
- char unk[10];
- double received_time;
- unsigned char* data; // pointer to raw packet data
- void* message; // easy bitbuf data access // 'inpacket.message' etc etc (pointer)
- char unk2[16];
- int size;
-
- // bf_read message; // easy bitbuf data access // 'inpacket.message' etc etc (pointer)
- // int size; // size in bytes
- // int wiresize; // size in bytes before decompression
- // bool stream; // was send as stream
- // struct netpacket_s* pNext; // for internal use, should be NULL in public
-} netpacket_t;
-#pragma pack(pop)
-
-// #56169 $DB69 PData size
-// #512 $200 Trailing data
-// #100 $64 Safety buffer
-const int PERSISTENCE_MAX_SIZE = 0xDDCD;
-
-// note: NOT_READY and READY are the only entries we have here that are defined by the vanilla game
-// entries after this are custom and used to determine the source of persistence, e.g. whether it is local or remote
-enum class ePersistenceReady : char
-{
- NOT_READY,
- READY = 3,
- READY_INSECURE = 3,
- READY_REMOTE
-};
-
-enum class eSignonState : int
-{
- NONE = 0, // no state yet; about to connect
- CHALLENGE = 1, // client challenging server; all OOB packets
- CONNECTED = 2, // client is connected to server; netchans ready
- NEW = 3, // just got serverinfo and string tables
- PRESPAWN = 4, // received signon buffers
- GETTINGDATA = 5, // respawn-defined signonstate, assumedly this is for persistence
- SPAWN = 6, // ready to receive entity packets
- FIRSTSNAP = 7, // another respawn-defined one
- FULL = 8, // we are fully connected; first non-delta packet received
- CHANGELEVEL = 9, // server is changing level; please wait
-};
-
-// clang-format off
-OFFSET_STRUCT(CBaseClient)
-{
- STRUCT_SIZE(0x2D728)
- FIELD(0x16, char m_Name[64])
- FIELD(0x258, KeyValues* m_ConVars)
- FIELD(0x2A0, eSignonState m_Signon)
- FIELD(0x358, char m_ClanTag[16])
- FIELD(0x484, bool m_bFakePlayer)
- FIELD(0x4A0, ePersistenceReady m_iPersistenceReady)
- FIELD(0x4FA, char m_PersistenceBuffer[PERSISTENCE_MAX_SIZE])
- FIELD(0xF500, char m_UID[32])
-};
-// clang-format on
-
-extern CBaseClient* g_pClientArray;
-
-enum server_state_t
-{
- ss_dead = 0, // Dead
- ss_loading, // Spawning
- ss_active, // Running
- ss_paused, // Running, but paused
-};
-
-extern server_state_t* g_pServerState;
-
-extern char* g_pModName;
-
-// clang-format off
-OFFSET_STRUCT(CGlobalVars)
-{
- FIELD(0x0,
- // Absolute time (per frame still - Use Plat_FloatTime() for a high precision real time
- // perf clock, but not that it doesn't obey host_timescale/host_framerate)
- double m_flRealTime);
-
- FIELDS(0x8,
- // Absolute frame counter - continues to increase even if game is paused
- int m_nFrameCount;
-
- // Non-paused frametime
- float m_flAbsoluteFrameTime;
-
- // Current time
- //
- // On the client, this (along with tickcount) takes a different meaning based on what
- // piece of code you're in:
- //
- // - While receiving network packets (like in PreDataUpdate/PostDataUpdate and proxies),
- // this is set to the SERVER TICKCOUNT for that packet. There is no interval between
- // the server ticks.
- // [server_current_Tick * tick_interval]
- //
- // - While rendering, this is the exact client clock
- // [client_current_tick * tick_interval + interpolation_amount]
- //
- // - During prediction, this is based on the client's current tick:
- // [client_current_tick * tick_interval]
- float m_flCurTime;
- )
-
- FIELDS(0x30,
- // Time spent on last server or client frame (has nothing to do with think intervals)
- float m_flFrameTime;
-
- // current maxplayers setting
- int m_nMaxClients;
- )
-
- FIELDS(0x3C,
- // Simulation ticks - does not increase when game is paused
- uint32_t m_nTickCount; // this is weird and doesn't seem to increase once per frame?
-
- // Simulation tick interval
- float m_flTickInterval;
- )
-
- FIELDS(0x60,
- const char* m_pMapName;
- int m_nMapVersion;
- )
-
- //FIELD(0x98, double m_flRealTime); // again?
-};
-// clang-format on
-
-extern CGlobalVars* g_pGlobals;
diff --git a/NorthstarDLL/engine/runframe.cpp b/NorthstarDLL/engine/runframe.cpp
deleted file mode 100644
index ddfd9253..00000000
--- a/NorthstarDLL/engine/runframe.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "engine/r2engine.h"
-#include "server/r2server.h"
-#include "hoststate.h"
-#include "server/serverpresence.h"
-
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(CEngine__Frame, engine.dll + 0x1C8650,
-void, __fastcall, (CEngine* self))
-// clang-format on
-{
- CEngine__Frame(self);
-}
-
-ON_DLL_LOAD("engine.dll", RunFrame, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/logging/crashhandler.cpp b/NorthstarDLL/logging/crashhandler.cpp
deleted file mode 100644
index a01de5a1..00000000
--- a/NorthstarDLL/logging/crashhandler.cpp
+++ /dev/null
@@ -1,590 +0,0 @@
-#include "crashhandler.h"
-#include "config/profile.h"
-#include "dedicated/dedicated.h"
-#include "util/version.h"
-#include "mods/modmanager.h"
-#include "plugins/plugins.h"
-
-#include <minidumpapiset.h>
-
-#define CRASHHANDLER_MAX_FRAMES 32
-#define CRASHHANDLER_GETMODULEHANDLE_FAIL "GetModuleHandleExA failed!"
-
-//-----------------------------------------------------------------------------
-// Purpose: Vectored exception callback
-//-----------------------------------------------------------------------------
-LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* pExceptionInfo)
-{
- g_pCrashHandler->Lock();
-
- g_pCrashHandler->SetExceptionInfos(pExceptionInfo);
-
- // Check if we should handle this
- // NOTE [Fifty]: This gets called before even a try{} catch() {} can handle an exception
- // we don't handle these unless "-crash_handle_all" is passed as a launch arg
- if (!g_pCrashHandler->IsExceptionFatal() && !g_pCrashHandler->GetAllFatal())
- {
- g_pCrashHandler->Unlock();
- return EXCEPTION_CONTINUE_SEARCH;
- }
-
- // Don't run if a debbuger is attached
- if (IsDebuggerPresent())
- {
- g_pCrashHandler->Unlock();
- return EXCEPTION_CONTINUE_SEARCH;
- }
-
- // Prevent recursive calls
- if (g_pCrashHandler->GetState())
- {
- g_pCrashHandler->Unlock();
- ExitProcess(1);
- }
-
- g_pCrashHandler->SetState(true);
-
- // Needs to be called first as we use the members this sets later on
- g_pCrashHandler->SetCrashedModule();
-
- // Format
- g_pCrashHandler->FormatException();
- g_pCrashHandler->FormatCallstack();
- g_pCrashHandler->FormatRegisters();
- g_pCrashHandler->FormatLoadedMods();
- g_pCrashHandler->FormatLoadedPlugins();
- g_pCrashHandler->FormatModules();
-
- // Flush
- NS::log::FlushLoggers();
-
- // Write minidump
- g_pCrashHandler->WriteMinidump();
-
- // Show message box
- g_pCrashHandler->ShowPopUpMessage();
-
- g_pCrashHandler->Unlock();
-
- // We showed the "Northstar has crashed" message box
- // make sure we terminate
- if (!g_pCrashHandler->IsExceptionFatal())
- ExitProcess(1);
-
- return EXCEPTION_EXECUTE_HANDLER;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: console control signal handler
-//-----------------------------------------------------------------------------
-BOOL WINAPI ConsoleCtrlRoutine(DWORD dwCtrlType)
-{
- // NOTE [Fifty]: When closing the process by closing the console we don't want
- // to trigger the crash handler so we remove it
- switch (dwCtrlType)
- {
- case CTRL_CLOSE_EVENT:
- spdlog::info("Exiting due to console close...");
- delete g_pCrashHandler;
- g_pCrashHandler = nullptr;
- std::exit(EXIT_SUCCESS);
- return TRUE;
- }
-
- return FALSE;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-//-----------------------------------------------------------------------------
-CCrashHandler::CCrashHandler()
- : m_hExceptionFilter(nullptr)
- , m_pExceptionInfos(nullptr)
- , m_bHasSetConsolehandler(false)
- , m_bAllExceptionsFatal(false)
- , m_bHasShownCrashMsg(false)
- , m_bState(false)
-{
- Init();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Destructor
-//-----------------------------------------------------------------------------
-CCrashHandler::~CCrashHandler()
-{
- Shutdown();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Initilazes crash handler
-//-----------------------------------------------------------------------------
-void CCrashHandler::Init()
-{
- m_hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter);
- m_bHasSetConsolehandler = SetConsoleCtrlHandler(ConsoleCtrlRoutine, TRUE);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Shutdowns crash handler
-//-----------------------------------------------------------------------------
-void CCrashHandler::Shutdown()
-{
- if (m_hExceptionFilter)
- {
- RemoveVectoredExceptionHandler(m_hExceptionFilter);
- m_hExceptionFilter = nullptr;
- }
-
- if (m_bHasSetConsolehandler)
- {
- SetConsoleCtrlHandler(ConsoleCtrlRoutine, FALSE);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets the exception info
-//-----------------------------------------------------------------------------
-void CCrashHandler::SetExceptionInfos(EXCEPTION_POINTERS* pExceptionPointers)
-{
- m_pExceptionInfos = pExceptionPointers;
-}
-//-----------------------------------------------------------------------------
-// Purpose: Sets the exception stirngs for message box
-//-----------------------------------------------------------------------------
-void CCrashHandler::SetCrashedModule()
-{
- LPCSTR pCrashAddress = static_cast<LPCSTR>(m_pExceptionInfos->ExceptionRecord->ExceptionAddress);
- HMODULE hCrashedModule;
- if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, pCrashAddress, &hCrashedModule))
- {
- m_svCrashedModule = CRASHHANDLER_GETMODULEHANDLE_FAIL;
- m_svCrashedOffset = "";
-
- DWORD dwErrorID = GetLastError();
- if (dwErrorID != 0)
- {
- LPSTR pszBuffer;
- DWORD dwSize = FormatMessageA(
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- dwErrorID,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPSTR)&pszBuffer,
- 0,
- NULL);
-
- if (dwSize > 0)
- {
- m_svError = pszBuffer;
- LocalFree(pszBuffer);
- }
- }
-
- return;
- }
-
- // Get module filename
- CHAR szCrashedModulePath[MAX_PATH];
- GetModuleFileNameExA(GetCurrentProcess(), hCrashedModule, szCrashedModulePath, sizeof(szCrashedModulePath));
-
- const CHAR* pszCrashedModuleFileName = strrchr(szCrashedModulePath, '\\') + 1;
-
- // Get relative address
- LPCSTR pModuleBase = reinterpret_cast<LPCSTR>(pCrashAddress - reinterpret_cast<LPCSTR>(hCrashedModule));
-
- m_svCrashedModule = pszCrashedModuleFileName;
- m_svCrashedOffset = fmt::format("{:#x}", reinterpret_cast<DWORD64>(pModuleBase));
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets the exception null terminated stirng
-//-----------------------------------------------------------------------------
-
-const CHAR* CCrashHandler::GetExceptionString() const
-{
- return GetExceptionString(m_pExceptionInfos->ExceptionRecord->ExceptionCode);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets the exception null terminated stirng
-//-----------------------------------------------------------------------------
-const CHAR* CCrashHandler::GetExceptionString(DWORD dwExceptionCode) const
-{
- // clang-format off
- switch (dwExceptionCode)
- {
- case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION";
- case EXCEPTION_DATATYPE_MISALIGNMENT: return "EXCEPTION_DATATYPE_MISALIGNMENT";
- case EXCEPTION_BREAKPOINT: return "EXCEPTION_BREAKPOINT";
- case EXCEPTION_SINGLE_STEP: return "EXCEPTION_SINGLE_STEP";
- case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
- case EXCEPTION_FLT_DENORMAL_OPERAND: return "EXCEPTION_FLT_DENORMAL_OPERAND";
- case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
- case EXCEPTION_FLT_INEXACT_RESULT: return "EXCEPTION_FLT_INEXACT_RESULT";
- case EXCEPTION_FLT_INVALID_OPERATION: return "EXCEPTION_FLT_INVALID_OPERATION";
- case EXCEPTION_FLT_OVERFLOW: return "EXCEPTION_FLT_OVERFLOW";
- case EXCEPTION_FLT_STACK_CHECK: return "EXCEPTION_FLT_STACK_CHECK";
- case EXCEPTION_FLT_UNDERFLOW: return "EXCEPTION_FLT_UNDERFLOW";
- case EXCEPTION_INT_DIVIDE_BY_ZERO: return "EXCEPTION_INT_DIVIDE_BY_ZERO";
- case EXCEPTION_INT_OVERFLOW: return "EXCEPTION_INT_OVERFLOW";
- case EXCEPTION_PRIV_INSTRUCTION: return "EXCEPTION_PRIV_INSTRUCTION";
- case EXCEPTION_IN_PAGE_ERROR: return "EXCEPTION_IN_PAGE_ERROR";
- case EXCEPTION_ILLEGAL_INSTRUCTION: return "EXCEPTION_ILLEGAL_INSTRUCTION";
- case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
- case EXCEPTION_STACK_OVERFLOW: return "EXCEPTION_STACK_OVERFLOW";
- case EXCEPTION_INVALID_DISPOSITION: return "EXCEPTION_INVALID_DISPOSITION";
- case EXCEPTION_GUARD_PAGE: return "EXCEPTION_GUARD_PAGE";
- case EXCEPTION_INVALID_HANDLE: return "EXCEPTION_INVALID_HANDLE";
- case 3765269347: return "RUNTIME_EXCEPTION";
- }
- // clang-format on
- return "UNKNOWN_EXCEPTION";
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns true if exception is known
-//-----------------------------------------------------------------------------
-bool CCrashHandler::IsExceptionFatal() const
-{
- return IsExceptionFatal(m_pExceptionInfos->ExceptionRecord->ExceptionCode);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns true if exception is known
-//-----------------------------------------------------------------------------
-bool CCrashHandler::IsExceptionFatal(DWORD dwExceptionCode) const
-{
- // clang-format off
- switch (dwExceptionCode)
- {
- case EXCEPTION_ACCESS_VIOLATION:
- case EXCEPTION_DATATYPE_MISALIGNMENT:
- case EXCEPTION_BREAKPOINT:
- case EXCEPTION_SINGLE_STEP:
- case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
- case EXCEPTION_FLT_DENORMAL_OPERAND:
- case EXCEPTION_FLT_DIVIDE_BY_ZERO:
- case EXCEPTION_FLT_INEXACT_RESULT:
- case EXCEPTION_FLT_INVALID_OPERATION:
- case EXCEPTION_FLT_OVERFLOW:
- case EXCEPTION_FLT_STACK_CHECK:
- case EXCEPTION_FLT_UNDERFLOW:
- case EXCEPTION_INT_DIVIDE_BY_ZERO:
- case EXCEPTION_INT_OVERFLOW:
- case EXCEPTION_PRIV_INSTRUCTION:
- case EXCEPTION_IN_PAGE_ERROR:
- case EXCEPTION_ILLEGAL_INSTRUCTION:
- case EXCEPTION_NONCONTINUABLE_EXCEPTION:
- case EXCEPTION_STACK_OVERFLOW:
- case EXCEPTION_INVALID_DISPOSITION:
- case EXCEPTION_GUARD_PAGE:
- case EXCEPTION_INVALID_HANDLE:
- return true;
- }
- // clang-format on
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Shows a message box
-//-----------------------------------------------------------------------------
-void CCrashHandler::ShowPopUpMessage()
-{
- if (m_bHasShownCrashMsg)
- return;
-
- m_bHasShownCrashMsg = true;
-
- if (!IsDedicatedServer())
- {
- std::string svMessage = fmt::format(
- "Northstar has crashed! Crash info can be found at {}/logs!\n\n{}\n{} + {}",
- GetNorthstarPrefix(),
- GetExceptionString(),
- m_svCrashedModule,
- m_svCrashedOffset);
-
- MessageBoxA(GetForegroundWindow(), svMessage.c_str(), "Northstar has crashed!", MB_ICONERROR | MB_OK);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatException()
-{
- spdlog::error("-------------------------------------------");
- spdlog::error("Northstar has crashed!");
- spdlog::error("\tVersion: {}", version);
- if (!m_svError.empty())
- {
- spdlog::info("\tEncountered an error when gathering crash information!");
- spdlog::info("\tWinApi Error: {}", m_svError.c_str());
- }
- spdlog::error("\t{}", GetExceptionString());
-
- DWORD dwExceptionCode = m_pExceptionInfos->ExceptionRecord->ExceptionCode;
- if (dwExceptionCode == EXCEPTION_ACCESS_VIOLATION || dwExceptionCode == EXCEPTION_IN_PAGE_ERROR)
- {
- ULONG_PTR uExceptionInfo0 = m_pExceptionInfos->ExceptionRecord->ExceptionInformation[0];
- ULONG_PTR uExceptionInfo1 = m_pExceptionInfos->ExceptionRecord->ExceptionInformation[1];
-
- if (!uExceptionInfo0)
- spdlog::error("\tAttempted to read from: {:#x}", uExceptionInfo1);
- else if (uExceptionInfo0 == 1)
- spdlog::error("\tAttempted to write to: {:#x}", uExceptionInfo1);
- else if (uExceptionInfo0 == 8)
- spdlog::error("\tData Execution Prevention (DEP) at: {:#x}", uExceptionInfo1);
- else
- spdlog::error("\tUnknown access violation at: {:#x}", uExceptionInfo1);
- }
-
- spdlog::error("\tAt: {} + {}", m_svCrashedModule, m_svCrashedOffset);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatCallstack()
-{
- spdlog::error("Callstack:");
-
- PVOID pFrames[CRASHHANDLER_MAX_FRAMES];
-
- int iFrames = RtlCaptureStackBackTrace(0, CRASHHANDLER_MAX_FRAMES, pFrames, NULL);
-
- // Above call gives us frames after the crash occured, we only want to print the ones starting from where
- // the exception was called
- bool bSkipExceptionHandlingFrames = true;
-
- // We ran into an error when getting the offset, just print all frames
- if (m_svCrashedOffset.empty())
- bSkipExceptionHandlingFrames = false;
-
- for (int i = 0; i < iFrames; i++)
- {
- const CHAR* pszModuleFileName;
-
- LPCSTR pAddress = static_cast<LPCSTR>(pFrames[i]);
- HMODULE hModule;
- if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, pAddress, &hModule))
- {
- pszModuleFileName = CRASHHANDLER_GETMODULEHANDLE_FAIL;
- // If we fail here it's too late to do any damage control
- }
- else
- {
- CHAR szModulePath[MAX_PATH];
- GetModuleFileNameExA(GetCurrentProcess(), hModule, szModulePath, sizeof(szModulePath));
- pszModuleFileName = strrchr(szModulePath, '\\') + 1;
- }
-
- // Get relative address
- LPCSTR pCrashOffset = reinterpret_cast<LPCSTR>(pAddress - reinterpret_cast<LPCSTR>(hModule));
- std::string svCrashOffset = fmt::format("{:#x}", reinterpret_cast<DWORD64>(pCrashOffset));
-
- // Should we log this frame
- if (bSkipExceptionHandlingFrames)
- {
- if (m_svCrashedModule == pszModuleFileName && m_svCrashedOffset == svCrashOffset)
- {
- bSkipExceptionHandlingFrames = false;
- }
- else
- {
- continue;
- }
- }
-
- // Log module + offset
- spdlog::error("\t{} + {:#x}", pszModuleFileName, reinterpret_cast<DWORD64>(pCrashOffset));
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatFlags(const CHAR* pszRegister, DWORD nValue)
-{
- spdlog::error("\t{}: {:#b}", pszRegister, nValue);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatIntReg(const CHAR* pszRegister, DWORD64 nValue)
-{
- spdlog::error("\t{}: {:#x}", pszRegister, nValue);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatFloatReg(const CHAR* pszRegister, M128A nValue)
-{
- DWORD nVec[4] = {
- static_cast<DWORD>(nValue.Low & UINT_MAX),
- static_cast<DWORD>(nValue.Low >> 32),
- static_cast<DWORD>(nValue.High & UINT_MAX),
- static_cast<DWORD>(nValue.High >> 32)};
-
- spdlog::error(
- "\t{}: [ {:G}, {:G}, {:G}, {:G} ]; [ {:#x}, {:#x}, {:#x}, {:#x} ]",
- pszRegister,
- static_cast<float>(nVec[0]),
- static_cast<float>(nVec[1]),
- static_cast<float>(nVec[2]),
- static_cast<float>(nVec[3]),
- nVec[0],
- nVec[1],
- nVec[2],
- nVec[3]);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatRegisters()
-{
- spdlog::error("Registers:");
-
- PCONTEXT pContext = m_pExceptionInfos->ContextRecord;
-
- FormatFlags("Flags:", pContext->ContextFlags);
-
- FormatIntReg("Rax", pContext->Rax);
- FormatIntReg("Rcx", pContext->Rcx);
- FormatIntReg("Rdx", pContext->Rdx);
- FormatIntReg("Rbx", pContext->Rbx);
- FormatIntReg("Rsp", pContext->Rsp);
- FormatIntReg("Rbp", pContext->Rbp);
- FormatIntReg("Rsi", pContext->Rsi);
- FormatIntReg("Rdi", pContext->Rdi);
- FormatIntReg("R8 ", pContext->R8);
- FormatIntReg("R9 ", pContext->R9);
- FormatIntReg("R10", pContext->R10);
- FormatIntReg("R11", pContext->R11);
- FormatIntReg("R12", pContext->R12);
- FormatIntReg("R13", pContext->R13);
- FormatIntReg("R14", pContext->R14);
- FormatIntReg("R15", pContext->R15);
- FormatIntReg("Rip", pContext->Rip);
-
- FormatFloatReg("Xmm0 ", pContext->Xmm0);
- FormatFloatReg("Xmm1 ", pContext->Xmm1);
- FormatFloatReg("Xmm2 ", pContext->Xmm2);
- FormatFloatReg("Xmm3 ", pContext->Xmm3);
- FormatFloatReg("Xmm4 ", pContext->Xmm4);
- FormatFloatReg("Xmm5 ", pContext->Xmm5);
- FormatFloatReg("Xmm6 ", pContext->Xmm6);
- FormatFloatReg("Xmm7 ", pContext->Xmm7);
- FormatFloatReg("Xmm8 ", pContext->Xmm8);
- FormatFloatReg("Xmm9 ", pContext->Xmm9);
- FormatFloatReg("Xmm10", pContext->Xmm10);
- FormatFloatReg("Xmm11", pContext->Xmm11);
- FormatFloatReg("Xmm12", pContext->Xmm12);
- FormatFloatReg("Xmm13", pContext->Xmm13);
- FormatFloatReg("Xmm14", pContext->Xmm14);
- FormatFloatReg("Xmm15", pContext->Xmm15);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatLoadedMods()
-{
- if (g_pModManager)
- {
- spdlog::error("Enabled mods:");
- for (const Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- spdlog::error("\t{}", mod.Name);
- }
-
- spdlog::error("Disabled mods:");
- for (const Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (mod.m_bEnabled)
- continue;
-
- spdlog::error("\t{}", mod.Name);
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatLoadedPlugins()
-{
- if (g_pPluginManager)
- {
- spdlog::error("Loaded Plugins:");
- for (const Plugin& plugin : g_pPluginManager->m_vLoadedPlugins)
- {
- spdlog::error("\t{}", plugin.name);
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrashHandler::FormatModules()
-{
- spdlog::error("Loaded modules:");
- HMODULE hModules[1024];
- DWORD cbNeeded;
-
- if (EnumProcessModules(GetCurrentProcess(), hModules, sizeof(hModules), &cbNeeded))
- {
- for (DWORD i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
- {
- CHAR szModulePath[MAX_PATH];
- if (GetModuleFileNameExA(GetCurrentProcess(), hModules[i], szModulePath, sizeof(szModulePath)))
- {
- const CHAR* pszModuleFileName = strrchr(szModulePath, '\\') + 1;
- spdlog::error("\t{}", pszModuleFileName);
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Writes minidump to disk
-//-----------------------------------------------------------------------------
-void CCrashHandler::WriteMinidump()
-{
- time_t time = std::time(nullptr);
- tm currentTime = *std::localtime(&time);
- std::stringstream stream;
- stream << std::put_time(&currentTime, (GetNorthstarPrefix() + "/logs/nsdump%Y-%m-%d %H-%M-%S.dmp").c_str());
-
- HANDLE hMinidumpFile = CreateFileA(stream.str().c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
- if (hMinidumpFile)
- {
- MINIDUMP_EXCEPTION_INFORMATION dumpExceptionInfo;
- dumpExceptionInfo.ThreadId = GetCurrentThreadId();
- dumpExceptionInfo.ExceptionPointers = m_pExceptionInfos;
- dumpExceptionInfo.ClientPointers = false;
-
- MiniDumpWriteDump(
- GetCurrentProcess(),
- GetCurrentProcessId(),
- hMinidumpFile,
- MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
- &dumpExceptionInfo,
- nullptr,
- nullptr);
- CloseHandle(hMinidumpFile);
- }
- else
- spdlog::error("Failed to write minidump file {}!", stream.str());
-}
-
-//-----------------------------------------------------------------------------
-CCrashHandler* g_pCrashHandler = nullptr;
diff --git a/NorthstarDLL/logging/crashhandler.h b/NorthstarDLL/logging/crashhandler.h
deleted file mode 100644
index c059a8ca..00000000
--- a/NorthstarDLL/logging/crashhandler.h
+++ /dev/null
@@ -1,97 +0,0 @@
-#pragma once
-
-#include <mutex>
-
-//-----------------------------------------------------------------------------
-// Purpose: Exception handling
-//-----------------------------------------------------------------------------
-class CCrashHandler
-{
-public:
- CCrashHandler();
- ~CCrashHandler();
-
- void Init();
- void Shutdown();
-
- void Lock()
- {
- m_Mutex.lock();
- }
-
- void Unlock()
- {
- m_Mutex.unlock();
- }
-
- void SetState(bool bState)
- {
- m_bState = bState;
- }
-
- bool GetState() const
- {
- return m_bState;
- }
-
- void SetAllFatal(bool bState)
- {
- m_bAllExceptionsFatal = bState;
- }
-
- bool GetAllFatal() const
- {
- return m_bAllExceptionsFatal;
- }
-
- //-----------------------------------------------------------------------------
- // Exception helpers
- //-----------------------------------------------------------------------------
- void SetExceptionInfos(EXCEPTION_POINTERS* pExceptionPointers);
-
- void SetCrashedModule();
-
- const CHAR* GetExceptionString() const;
- const CHAR* GetExceptionString(DWORD dwExceptionCode) const;
-
- bool IsExceptionFatal() const;
- bool IsExceptionFatal(DWORD dwExceptionCode) const;
-
- //-----------------------------------------------------------------------------
- // Formatting
- //-----------------------------------------------------------------------------
- void ShowPopUpMessage();
-
- void FormatException();
- void FormatCallstack();
- void FormatFlags(const CHAR* pszRegister, DWORD nValue);
- void FormatIntReg(const CHAR* pszRegister, DWORD64 nValue);
- void FormatFloatReg(const CHAR* pszRegister, M128A nValue);
- void FormatRegisters();
- void FormatLoadedMods();
- void FormatLoadedPlugins();
- void FormatModules();
-
- //-----------------------------------------------------------------------------
- // Minidump
- //-----------------------------------------------------------------------------
- void WriteMinidump();
-
-private:
- PVOID m_hExceptionFilter;
- EXCEPTION_POINTERS* m_pExceptionInfos;
-
- bool m_bHasSetConsolehandler;
- bool m_bAllExceptionsFatal;
- bool m_bHasShownCrashMsg;
- bool m_bState;
-
- std::string m_svCrashedModule;
- std::string m_svCrashedOffset;
-
- std::string m_svError;
-
- std::mutex m_Mutex;
-};
-
-extern CCrashHandler* g_pCrashHandler;
diff --git a/NorthstarDLL/logging/logging.cpp b/NorthstarDLL/logging/logging.cpp
deleted file mode 100644
index 3416bb8c..00000000
--- a/NorthstarDLL/logging/logging.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-#include "logging.h"
-#include "core/convar/convar.h"
-#include "core/convar/concommand.h"
-#include "config/profile.h"
-#include "core/tier0.h"
-#include "util/version.h"
-#include "spdlog/sinks/basic_file_sink.h"
-
-#include <winternl.h>
-#include <cstdlib>
-#include <iomanip>
-#include <sstream>
-
-AUTOHOOK_INIT()
-
-std::vector<std::shared_ptr<ColoredLogger>> loggers {};
-
-namespace NS::log
-{
- std::shared_ptr<ColoredLogger> SCRIPT_UI;
- std::shared_ptr<ColoredLogger> SCRIPT_CL;
- std::shared_ptr<ColoredLogger> SCRIPT_SV;
-
- std::shared_ptr<ColoredLogger> NATIVE_UI;
- std::shared_ptr<ColoredLogger> NATIVE_CL;
- std::shared_ptr<ColoredLogger> NATIVE_SV;
- std::shared_ptr<ColoredLogger> NATIVE_EN;
-
- std::shared_ptr<ColoredLogger> fs;
- std::shared_ptr<ColoredLogger> rpak;
- std::shared_ptr<ColoredLogger> echo;
-
- std::shared_ptr<ColoredLogger> NORTHSTAR;
- std::shared_ptr<ColoredLogger> PLUGINSYS;
-}; // namespace NS::log
-
-// This needs to be called after hooks are loaded so we can access the command line args
-void CreateLogFiles()
-{
- if (strstr(GetCommandLineA(), "-disablelogs"))
- {
- spdlog::default_logger()->set_level(spdlog::level::off);
- }
- else
- {
- try
- {
- // todo: might be good to delete logs that are too old
- time_t time = std::time(nullptr);
- tm currentTime = *std::localtime(&time);
- std::stringstream stream;
-
- stream << std::put_time(&currentTime, (GetNorthstarPrefix() + "/logs/nslog%Y-%m-%d %H-%M-%S.txt").c_str());
- auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false);
- sink->set_pattern("[%Y-%m-%d] [%H:%M:%S] [%n] [%l] %v");
- for (auto& logger : loggers)
- {
- logger->sinks().push_back(sink);
- }
- spdlog::flush_on(spdlog::level::info);
- }
- catch (...)
- {
- spdlog::error("Failed creating log file!");
- MessageBoxA(
- 0, "Failed creating log file! Make sure the profile directory is writable.", "Northstar Warning", MB_ICONWARNING | MB_OK);
- }
- }
-}
-
-void ExternalConsoleSink::sink_it_(const spdlog::details::log_msg& msg)
-{
- throw std::runtime_error("sink_it_ called on SourceConsoleSink with pure log_msg. This is an error!");
-}
-
-void ExternalConsoleSink::custom_sink_it_(const custom_log_msg& msg)
-{
- spdlog::memory_buf_t formatted;
- spdlog::sinks::base_sink<std::mutex>::formatter_->format(msg, formatted);
-
- std::string out = "";
- // if ansi colour is turned off, just use WriteConsoleA and return
- if (!g_bSpdLog_UseAnsiColor)
- {
- out += fmt::to_string(formatted);
- }
-
- // print to the console with colours
- else
- {
- // get message string
- std::string str = fmt::to_string(formatted);
-
- std::string levelColor = m_LogColours[msg.level];
- std::string name {msg.logger_name.begin(), msg.logger_name.end()};
-
- std::string name_str = "[NAME]";
- int name_pos = str.find(name_str);
- str.replace(name_pos, name_str.length(), msg.origin->ANSIColor + "[" + name + "]" + default_color);
-
- std::string level_str = "[LVL]";
- int level_pos = str.find(level_str);
- str.replace(level_pos, level_str.length(), levelColor + "[" + std::string(level_names[msg.level]) + "]" + default_color);
-
- out += str;
- }
- // print the string to the console - this is definitely bad i think
- HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
- auto ignored = WriteConsoleA(handle, out.c_str(), std::strlen(out.c_str()), nullptr, nullptr);
- (void)ignored;
-}
-
-void ExternalConsoleSink::flush_()
-{
- std::cout << std::flush;
-}
-
-void CustomSink::custom_log(const custom_log_msg& msg)
-{
- std::lock_guard<std::mutex> lock(mutex_);
- custom_sink_it_(msg);
-}
-
-void InitialiseConsole()
-{
- if (AllocConsole() == FALSE)
- {
- std::cout << "[*] Failed to create a console window, maybe a console already exists?" << std::endl;
- }
- else
- {
- freopen("CONOUT$", "w", stdout);
- freopen("CONOUT$", "w", stderr);
- }
-
- // this if statement is adapted from r5sdk
- if (!strstr(GetCommandLineA(), "-noansiclr"))
- {
- g_bSpdLog_UseAnsiColor = true;
- DWORD dwMode = 0;
- HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
-
- GetConsoleMode(hOutput, &dwMode);
- dwMode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
-
- if (!SetConsoleMode(hOutput, dwMode)) // Some editions of Windows have 'VirtualTerminalLevel' disabled by default.
- {
- // If 'VirtualTerminalLevel' can't be set, just disable ANSI color, since it wouldnt work anyway.
- spdlog::warn("could not set VirtualTerminalLevel. Disabling color output");
- g_bSpdLog_UseAnsiColor = false;
- }
- }
-}
-
-void RegisterLogger(std::shared_ptr<ColoredLogger> logger)
-{
- loggers.push_back(logger);
-}
-
-void RegisterCustomSink(std::shared_ptr<CustomSink> sink)
-{
- for (auto& logger : loggers)
- {
- logger->custom_sinks_.push_back(sink);
- }
-};
-
-void InitialiseLogging()
-{
- // create a logger, and set it to default
- NS::log::NORTHSTAR = std::make_shared<ColoredLogger>("NORTHSTAR", NS::Colors::NORTHSTAR, true);
- NS::log::NORTHSTAR->sinks().clear();
- loggers.push_back(NS::log::NORTHSTAR);
- spdlog::set_default_logger(NS::log::NORTHSTAR);
-
- // create our console sink
- auto sink = std::make_shared<ExternalConsoleSink>();
- // set the pattern
- if (g_bSpdLog_UseAnsiColor)
- // dont put the log level in the pattern if we are using colours, as the colour will show the log level
- sink->set_pattern("[%H:%M:%S] [NAME] [LVL] %v");
- else
- sink->set_pattern("[%H:%M:%S] [%n] [%l] %v");
-
- // add our sink to the logger
- NS::log::NORTHSTAR->custom_sinks_.push_back(sink);
-
- NS::log::SCRIPT_UI = std::make_shared<ColoredLogger>("SCRIPT UI", NS::Colors::SCRIPT_UI);
- NS::log::SCRIPT_CL = std::make_shared<ColoredLogger>("SCRIPT CL", NS::Colors::SCRIPT_CL);
- NS::log::SCRIPT_SV = std::make_shared<ColoredLogger>("SCRIPT SV", NS::Colors::SCRIPT_SV);
-
- NS::log::NATIVE_UI = std::make_shared<ColoredLogger>("NATIVE UI", NS::Colors::NATIVE_UI);
- NS::log::NATIVE_CL = std::make_shared<ColoredLogger>("NATIVE CL", NS::Colors::NATIVE_CL);
- NS::log::NATIVE_SV = std::make_shared<ColoredLogger>("NATIVE SV", NS::Colors::NATIVE_SV);
- NS::log::NATIVE_EN = std::make_shared<ColoredLogger>("NATIVE EN", NS::Colors::NATIVE_ENGINE);
-
- NS::log::fs = std::make_shared<ColoredLogger>("FILESYSTM", NS::Colors::FILESYSTEM);
- NS::log::rpak = std::make_shared<ColoredLogger>("RPAK_FSYS", NS::Colors::RPAK);
- NS::log::echo = std::make_shared<ColoredLogger>("ECHO", NS::Colors::ECHO);
-
- NS::log::PLUGINSYS = std::make_shared<ColoredLogger>("PLUGINSYS", NS::Colors::PLUGINSYS);
-
- loggers.push_back(NS::log::SCRIPT_UI);
- loggers.push_back(NS::log::SCRIPT_CL);
- loggers.push_back(NS::log::SCRIPT_SV);
-
- loggers.push_back(NS::log::NATIVE_UI);
- loggers.push_back(NS::log::NATIVE_CL);
- loggers.push_back(NS::log::NATIVE_SV);
- loggers.push_back(NS::log::NATIVE_EN);
-
- loggers.push_back(NS::log::PLUGINSYS);
-
- loggers.push_back(NS::log::fs);
- loggers.push_back(NS::log::rpak);
- loggers.push_back(NS::log::echo);
-}
-
-void NS::log::FlushLoggers()
-{
- for (auto& logger : loggers)
- logger->flush();
-
- spdlog::default_logger()->flush();
-}
-
-// Wine specific functions
-typedef const char*(CDECL* wine_get_host_version_type)(const char**, const char**);
-wine_get_host_version_type wine_get_host_version;
-
-typedef const char*(CDECL* wine_get_build_id_type)(void);
-wine_get_build_id_type wine_get_build_id;
-
-// Not exported Winapi methods
-typedef NTSTATUS(WINAPI* RtlGetVersion_type)(PRTL_OSVERSIONINFOW);
-RtlGetVersion_type RtlGetVersion;
-
-void StartupLog()
-{
- spdlog::info("NorthstarLauncher version: {}", version);
- spdlog::info("Command line: {}", GetCommandLineA());
- spdlog::info("Using profile: {}", GetNorthstarPrefix());
-
- HMODULE ntdll = GetModuleHandleA("ntdll.dll");
- if (!ntdll)
- {
- // How did we get here
- spdlog::info("Operating System: Unknown");
- return;
- }
-
- wine_get_host_version = (wine_get_host_version_type)GetProcAddress(ntdll, "wine_get_host_version");
- if (wine_get_host_version)
- {
- // Load the rest of the functions we need
- wine_get_build_id = (wine_get_build_id_type)GetProcAddress(ntdll, "wine_get_build_id");
-
- const char* sysname;
- wine_get_host_version(&sysname, NULL);
-
- spdlog::info("Operating System: {} (Wine)", sysname);
- spdlog::info("Wine build: {}", wine_get_build_id());
-
- // STEAM_COMPAT_TOOL_PATHS is a colon separated lists of all compat tool paths used
- // The first one tends to be the Proton path itself
- // We extract the basename out of it to get the name used
- char* compatToolPtr = std::getenv("STEAM_COMPAT_TOOL_PATHS");
- if (compatToolPtr)
- {
- std::string_view compatToolPath(compatToolPtr);
-
- auto protonBasenameEnd = compatToolPath.find(":");
- if (protonBasenameEnd == std::string_view::npos)
- protonBasenameEnd = 0;
- auto protonBasenameStart = compatToolPath.rfind("/", protonBasenameEnd) + 1;
- if (protonBasenameStart == std::string_view::npos)
- protonBasenameStart = 0;
-
- spdlog::info("Proton build: {}", compatToolPath.substr(protonBasenameStart, protonBasenameEnd - protonBasenameStart));
- }
- }
- else
- {
- // We are real Windows (hopefully)
- const char* win_ver = "Unknown";
-
- RTL_OSVERSIONINFOW osvi;
- osvi.dwOSVersionInfoSize = sizeof(osvi);
-
- RtlGetVersion = (RtlGetVersion_type)GetProcAddress(ntdll, "RtlGetVersion");
- if (RtlGetVersion && !RtlGetVersion(&osvi))
- {
- // Version reference table
- // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa#remarks
- spdlog::info("Operating System: Windows (NT{}.{})", osvi.dwMajorVersion, osvi.dwMinorVersion);
- }
- else
- {
- spdlog::info("Operating System: Windows");
- }
- }
-}
diff --git a/NorthstarDLL/logging/logging.h b/NorthstarDLL/logging/logging.h
deleted file mode 100644
index 5056af27..00000000
--- a/NorthstarDLL/logging/logging.h
+++ /dev/null
@@ -1,136 +0,0 @@
-#pragma once
-#include "spdlog/sinks/base_sink.h"
-#include "spdlog/logger.h"
-#include "squirrel/squirrel.h"
-#include "core/math/color.h"
-
-void CreateLogFiles();
-void InitialiseLogging();
-void InitialiseConsole();
-void StartupLog();
-
-class ColoredLogger;
-
-struct custom_log_msg : spdlog::details::log_msg
-{
-public:
- custom_log_msg(ColoredLogger* origin, spdlog::details::log_msg msg) : origin(origin), spdlog::details::log_msg(msg) {}
-
- ColoredLogger* origin;
-};
-
-class CustomSink : public spdlog::sinks::base_sink<std::mutex>
-{
-public:
- void custom_log(const custom_log_msg& msg);
- virtual void custom_sink_it_(const custom_log_msg& msg)
- {
- throw std::runtime_error("Pure virtual call to CustomSink::custom_sink_it_");
- }
-};
-
-class ColoredLogger : public spdlog::logger
-{
-public:
- std::string ANSIColor;
- SourceColor SRCColor;
-
- std::vector<std::shared_ptr<CustomSink>> custom_sinks_;
-
- ColoredLogger(std::string name, Color color, bool first = false) : spdlog::logger(*spdlog::default_logger())
- {
- name_ = std::move(name);
- if (!first)
- {
- custom_sinks_ = dynamic_pointer_cast<ColoredLogger>(spdlog::default_logger())->custom_sinks_;
- }
-
- ANSIColor = color.ToANSIColor();
- SRCColor = color.ToSourceColor();
- }
-
- void sink_it_(const spdlog::details::log_msg& msg)
- {
- custom_log_msg custom_msg {this, msg};
-
- // Ugh
- for (auto& sink : sinks_)
- {
- SPDLOG_TRY
- {
- sink->log(custom_msg);
- }
- SPDLOG_LOGGER_CATCH()
- }
-
- for (auto& sink : custom_sinks_)
- {
- SPDLOG_TRY
- {
- sink->custom_log(custom_msg);
- }
- SPDLOG_LOGGER_CATCH()
- }
-
- if (should_flush_(custom_msg))
- {
- flush_();
- }
- }
-};
-
-namespace NS::log
-{
- // Squirrel
- extern std::shared_ptr<ColoredLogger> SCRIPT_UI;
- extern std::shared_ptr<ColoredLogger> SCRIPT_CL;
- extern std::shared_ptr<ColoredLogger> SCRIPT_SV;
-
- // Native code
- extern std::shared_ptr<ColoredLogger> NATIVE_UI;
- extern std::shared_ptr<ColoredLogger> NATIVE_CL;
- extern std::shared_ptr<ColoredLogger> NATIVE_SV;
- extern std::shared_ptr<ColoredLogger> NATIVE_EN;
-
- // File system
- extern std::shared_ptr<ColoredLogger> fs;
- // RPak
- extern std::shared_ptr<ColoredLogger> rpak;
- // Echo
- extern std::shared_ptr<ColoredLogger> echo;
-
- extern std::shared_ptr<ColoredLogger> NORTHSTAR;
-
- extern std::shared_ptr<ColoredLogger> PLUGINSYS;
-
- void FlushLoggers();
-}; // namespace NS::log
-
-void RegisterCustomSink(std::shared_ptr<CustomSink> sink);
-void RegisterLogger(std::shared_ptr<ColoredLogger> logger);
-
-inline bool g_bSpdLog_UseAnsiColor = true;
-
-// Could maybe use some different names here, idk
-static const char* level_names[] {"trac", "dbug", "info", "warn", "errr", "crit", "off"};
-
-// spdlog logger, for cool colour things
-class ExternalConsoleSink : public CustomSink
-{
-private:
- std::map<spdlog::level::level_enum, std::string> m_LogColours = {
- {spdlog::level::trace, NS::Colors::TRACE.ToANSIColor()},
- {spdlog::level::debug, NS::Colors::DEBUG.ToANSIColor()},
- {spdlog::level::info, NS::Colors::INFO.ToANSIColor()},
- {spdlog::level::warn, NS::Colors::WARN.ToANSIColor()},
- {spdlog::level::err, NS::Colors::ERR.ToANSIColor()},
- {spdlog::level::critical, NS::Colors::CRIT.ToANSIColor()},
- {spdlog::level::off, NS::Colors::OFF.ToANSIColor()}};
-
- std::string default_color = "\033[39;49m";
-
-protected:
- void sink_it_(const spdlog::details::log_msg& msg) override;
- void custom_sink_it_(const custom_log_msg& msg);
- void flush_() override;
-};
diff --git a/NorthstarDLL/logging/loghooks.cpp b/NorthstarDLL/logging/loghooks.cpp
deleted file mode 100644
index 7efb5b99..00000000
--- a/NorthstarDLL/logging/loghooks.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-#include "logging.h"
-#include "loghooks.h"
-#include "core/convar/convar.h"
-#include "core/convar/concommand.h"
-#include "core/math/bitbuf.h"
-#include "config/profile.h"
-#include "core/tier0.h"
-#include "squirrel/squirrel.h"
-#include <iomanip>
-#include <sstream>
-
-AUTOHOOK_INIT()
-
-ConVar* Cvar_spewlog_enable;
-ConVar* Cvar_cl_showtextmsg;
-
-enum class TextMsgPrintType_t
-{
- HUD_PRINTNOTIFY = 1,
- HUD_PRINTCONSOLE,
- HUD_PRINTTALK,
- HUD_PRINTCENTER
-};
-
-class ICenterPrint
-{
-public:
- virtual void ctor() = 0;
- virtual void Clear(void) = 0;
- virtual void ColorPrint(int r, int g, int b, int a, wchar_t* text) = 0;
- virtual void ColorPrint(int r, int g, int b, int a, char* text) = 0;
- virtual void Print(wchar_t* text) = 0;
- virtual void Print(char* text) = 0;
- virtual void SetTextColor(int r, int g, int b, int a) = 0;
-};
-
-enum class SpewType_t
-{
- SPEW_MESSAGE = 0,
-
- SPEW_WARNING,
- SPEW_ASSERT,
- SPEW_ERROR,
- SPEW_LOG,
-
- SPEW_TYPE_COUNT
-};
-
-const std::unordered_map<SpewType_t, const char*> PrintSpewTypes = {
- {SpewType_t::SPEW_MESSAGE, "SPEW_MESSAGE"},
- {SpewType_t::SPEW_WARNING, "SPEW_WARNING"},
- {SpewType_t::SPEW_ASSERT, "SPEW_ASSERT"},
- {SpewType_t::SPEW_ERROR, "SPEW_ERROR"},
- {SpewType_t::SPEW_LOG, "SPEW_LOG"}};
-
-// these are used to define the base text colour for these things
-const std::unordered_map<SpewType_t, spdlog::level::level_enum> PrintSpewLevels = {
- {SpewType_t::SPEW_MESSAGE, spdlog::level::level_enum::info},
- {SpewType_t::SPEW_WARNING, spdlog::level::level_enum::warn},
- {SpewType_t::SPEW_ASSERT, spdlog::level::level_enum::err},
- {SpewType_t::SPEW_ERROR, spdlog::level::level_enum::err},
- {SpewType_t::SPEW_LOG, spdlog::level::level_enum::info}};
-
-const std::unordered_map<SpewType_t, const char> PrintSpewTypes_Short = {
- {SpewType_t::SPEW_MESSAGE, 'M'},
- {SpewType_t::SPEW_WARNING, 'W'},
- {SpewType_t::SPEW_ASSERT, 'A'},
- {SpewType_t::SPEW_ERROR, 'E'},
- {SpewType_t::SPEW_LOG, 'L'}};
-
-ICenterPrint* pInternalCenterPrint = NULL;
-
-// clang-format off
-AUTOHOOK(TextMsg, client.dll + 0x198710,
-void,, (BFRead* msg))
-// clang-format on
-{
- TextMsgPrintType_t msg_dest = (TextMsgPrintType_t)msg->ReadByte();
-
- char text[256];
- msg->ReadString(text, sizeof(text));
-
- if (!Cvar_cl_showtextmsg->GetBool())
- return;
-
- switch (msg_dest)
- {
- case TextMsgPrintType_t::HUD_PRINTCENTER:
- pInternalCenterPrint->Print(text);
- break;
-
- default:
- spdlog::warn("Unimplemented TextMsg type {}! printing to console", msg_dest);
- [[fallthrough]];
-
- case TextMsgPrintType_t::HUD_PRINTCONSOLE:
- auto endpos = strlen(text);
- if (text[endpos - 1] == '\n')
- text[endpos - 1] = '\0'; // cut off repeated newline
-
- spdlog::info(text);
- break;
- }
-}
-
-// clang-format off
-AUTOHOOK(Hook_fprintf, engine.dll + 0x51B1F0,
-int,, (void* const stream, const char* const format, ...))
-// clang-format on
-{
- va_list va;
- va_start(va, format);
-
- SQChar buf[1024];
- int charsWritten = vsnprintf_s(buf, _TRUNCATE, format, va);
-
- if (charsWritten > 0)
- {
- if (buf[charsWritten - 1] == '\n')
- buf[charsWritten - 1] = '\0';
- NS::log::NATIVE_EN->info("{}", buf);
- }
-
- va_end(va);
- return 0;
-}
-
-// clang-format off
-AUTOHOOK(ConCommand_echo, engine.dll + 0x123680,
-void,, (const CCommand& arg))
-// clang-format on
-{
- if (arg.ArgC() >= 2)
- NS::log::echo->info("{}", arg.ArgS());
-}
-
-// clang-format off
-AUTOHOOK(EngineSpewFunc, engine.dll + 0x11CA80,
-void, __fastcall, (void* pEngineServer, SpewType_t type, const char* format, va_list args))
-// clang-format on
-{
- if (!Cvar_spewlog_enable->GetBool())
- return;
-
- const char* typeStr = PrintSpewTypes.at(type);
- char formatted[2048] = {0};
- bool bShouldFormat = true;
-
- // because titanfall 2 is quite possibly the worst thing to yet exist, it sometimes gives invalid specifiers which will crash
- // ttf2sdk had a way to prevent them from crashing but it doesnt work in debug builds
- // so we use this instead
- for (int i = 0; format[i]; i++)
- {
- if (format[i] == '%')
- {
- switch (format[i + 1])
- {
- // this is fucking awful lol
- case 'd':
- case 'i':
- case 'u':
- case 'x':
- case 'X':
- case 'f':
- case 'F':
- case 'g':
- case 'G':
- case 'a':
- case 'A':
- case 'c':
- case 's':
- case 'p':
- case 'n':
- case '%':
- case '-':
- case '+':
- case ' ':
- case '#':
- case '*':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- break;
-
- default:
- {
- bShouldFormat = false;
- break;
- }
- }
- }
- }
-
- if (bShouldFormat)
- vsnprintf(formatted, sizeof(formatted), format, args);
- else
- spdlog::warn("Failed to format {} \"{}\"", typeStr, format);
-
- auto endpos = strlen(formatted);
- if (formatted[endpos - 1] == '\n')
- formatted[endpos - 1] = '\0'; // cut off repeated newline
-
- NS::log::NATIVE_SV->log(PrintSpewLevels.at(type), "{}", formatted);
-}
-
-// used for printing the output of status
-// clang-format off
-AUTOHOOK(Status_ConMsg, engine.dll + 0x15ABD0,
-void,, (const char* text, ...))
-// clang-format on
-{
- char formatted[2048];
- va_list list;
-
- va_start(list, text);
- vsprintf_s(formatted, text, list);
- va_end(list);
-
- auto endpos = strlen(formatted);
- if (formatted[endpos - 1] == '\n')
- formatted[endpos - 1] = '\0'; // cut off repeated newline
-
- spdlog::info(formatted);
-}
-
-// clang-format off
-AUTOHOOK(CClientState_ProcessPrint, engine.dll + 0x1A1530,
-bool,, (void* thisptr, uintptr_t msg))
-// clang-format on
-{
- char* text = *(char**)(msg + 0x20);
-
- auto endpos = strlen(text);
- if (text[endpos - 1] == '\n')
- text[endpos - 1] = '\0'; // cut off repeated newline
-
- spdlog::info(text);
- return true;
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", EngineSpewFuncHooks, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
-
- Cvar_spewlog_enable = new ConVar("spewlog_enable", "0", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged");
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientPrintHooks, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(client.dll)
-
- Cvar_cl_showtextmsg = new ConVar("cl_showtextmsg", "1", FCVAR_NONE, "Enable/disable text messages printing on the screen.");
- pInternalCenterPrint = module.Offset(0x216E940).RCast<ICenterPrint*>();
-}
diff --git a/NorthstarDLL/logging/loghooks.h b/NorthstarDLL/logging/loghooks.h
deleted file mode 100644
index 6f70f09b..00000000
--- a/NorthstarDLL/logging/loghooks.h
+++ /dev/null
@@ -1 +0,0 @@
-#pragma once
diff --git a/NorthstarDLL/logging/sourceconsole.cpp b/NorthstarDLL/logging/sourceconsole.cpp
deleted file mode 100644
index e436d1d4..00000000
--- a/NorthstarDLL/logging/sourceconsole.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "core/convar/convar.h"
-#include "sourceconsole.h"
-#include "core/sourceinterface.h"
-#include "core/convar/concommand.h"
-#include "util/printcommands.h"
-
-SourceInterface<CGameConsole>* g_pSourceGameConsole;
-
-void ConCommand_toggleconsole(const CCommand& arg)
-{
- if ((*g_pSourceGameConsole)->IsConsoleVisible())
- (*g_pSourceGameConsole)->Hide();
- else
- (*g_pSourceGameConsole)->Activate();
-}
-
-void ConCommand_showconsole(const CCommand& arg)
-{
- (*g_pSourceGameConsole)->Activate();
-}
-
-void ConCommand_hideconsole(const CCommand& arg)
-{
- (*g_pSourceGameConsole)->Hide();
-}
-
-void SourceConsoleSink::custom_sink_it_(const custom_log_msg& msg)
-{
- if (!(*g_pSourceGameConsole)->m_bInitialized)
- return;
-
- spdlog::memory_buf_t formatted;
- spdlog::sinks::base_sink<std::mutex>::formatter_->format(msg, formatted);
-
- // get message string
- std::string str = fmt::to_string(formatted);
-
- SourceColor levelColor = m_LogColours[msg.level];
- std::string name {msg.logger_name.begin(), msg.logger_name.end()};
-
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->ColorPrint(msg.origin->SRCColor, ("[" + name + "]").c_str());
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->Print(" ");
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->ColorPrint(levelColor, ("[" + std::string(level_names[msg.level]) + "]").c_str());
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->Print(" ");
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->Print(fmt::to_string(formatted).c_str());
-}
-
-void SourceConsoleSink::sink_it_(const spdlog::details::log_msg& msg)
-{
- throw std::runtime_error("sink_it_ called on SourceConsoleSink with pure log_msg. This is an error!");
-}
-
-void SourceConsoleSink::flush_() {}
-
-// clang-format off
-HOOK(OnCommandSubmittedHook, OnCommandSubmitted,
-void, __fastcall, (CConsoleDialog* consoleDialog, const char* pCommand))
-// clang-format on
-{
- consoleDialog->m_pConsolePanel->Print("] ");
- consoleDialog->m_pConsolePanel->Print(pCommand);
- consoleDialog->m_pConsolePanel->Print("\n");
-
- TryPrintCvarHelpForCommand(pCommand);
-
- OnCommandSubmitted(consoleDialog, pCommand);
-}
-
-// called from sourceinterface.cpp in client createinterface hooks, on GameClientExports001
-void InitialiseConsoleOnInterfaceCreation()
-{
- (*g_pSourceGameConsole)->Initialize();
- // hook OnCommandSubmitted so we print inputted commands
- OnCommandSubmittedHook.Dispatch((LPVOID)(*g_pSourceGameConsole)->m_pConsole->m_vtable->OnCommandSubmitted);
-
- auto consoleSink = std::make_shared<SourceConsoleSink>();
- if (g_bSpdLog_UseAnsiColor)
- consoleSink->set_pattern("%v"); // no need to include the level in the game console, the text colour signifies it anyway
- else
- consoleSink->set_pattern("[%n] [%l] %v"); // no colour, so we should show the level for colourblind people
- RegisterCustomSink(consoleSink);
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("client.dll", SourceConsole, ConCommand, (CModule module))
-{
- g_pSourceGameConsole = new SourceInterface<CGameConsole>("client.dll", "GameConsole004");
-
- RegisterConCommand("toggleconsole", ConCommand_toggleconsole, "Show/hide the console.", FCVAR_DONTRECORD);
- RegisterConCommand("showconsole", ConCommand_showconsole, "Show the console.", FCVAR_DONTRECORD);
- RegisterConCommand("hideconsole", ConCommand_hideconsole, "Hide the console.", FCVAR_DONTRECORD);
-}
diff --git a/NorthstarDLL/logging/sourceconsole.h b/NorthstarDLL/logging/sourceconsole.h
deleted file mode 100644
index 44d73843..00000000
--- a/NorthstarDLL/logging/sourceconsole.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#pragma once
-#include "core/sourceinterface.h"
-#include "spdlog/sinks/base_sink.h"
-#include <map>
-
-class EditablePanel
-{
-public:
- virtual ~EditablePanel() = 0;
- unsigned char unknown[0x2B0];
-};
-
-class IConsoleDisplayFunc
-{
-public:
- virtual void ColorPrint(const SourceColor& clr, const char* pMessage) = 0;
- virtual void Print(const char* pMessage) = 0;
- virtual void DPrint(const char* pMessage) = 0;
-};
-
-class CConsolePanel : public EditablePanel, public IConsoleDisplayFunc
-{
-};
-
-class CConsoleDialog
-{
-public:
- struct VTable
- {
- void* unknown[298];
- void (*OnCommandSubmitted)(CConsoleDialog* consoleDialog, const char* pCommand);
- };
-
- VTable* m_vtable;
- unsigned char unknown[0x398];
- CConsolePanel* m_pConsolePanel;
-};
-
-class CGameConsole
-{
-public:
- virtual ~CGameConsole() = 0;
-
- // activates the console, makes it visible and brings it to the foreground
- virtual void Activate() = 0;
-
- virtual void Initialize() = 0;
-
- // hides the console
- virtual void Hide() = 0;
-
- // clears the console
- virtual void Clear() = 0;
-
- // return true if the console has focus
- virtual bool IsConsoleVisible() = 0;
-
- virtual void SetParent(int parent) = 0;
-
- bool m_bInitialized;
- CConsoleDialog* m_pConsole;
-};
-
-extern SourceInterface<CGameConsole>* g_pSourceGameConsole;
-
-// spdlog logger
-class SourceConsoleSink : public CustomSink
-{
-private:
- std::map<spdlog::level::level_enum, SourceColor> m_LogColours = {
- {spdlog::level::trace, NS::Colors::TRACE.ToSourceColor()},
- {spdlog::level::debug, NS::Colors::DEBUG.ToSourceColor()},
- {spdlog::level::info, NS::Colors::INFO.ToSourceColor()},
- {spdlog::level::warn, NS::Colors::WARN.ToSourceColor()},
- {spdlog::level::err, NS::Colors::ERR.ToSourceColor()},
- {spdlog::level::critical, NS::Colors::CRIT.ToSourceColor()},
- {spdlog::level::off, NS::Colors::OFF.ToSourceColor()}};
-
-protected:
- void custom_sink_it_(const custom_log_msg& msg);
- void sink_it_(const spdlog::details::log_msg& msg) override;
- void flush_() override;
-};
-
-void InitialiseConsoleOnInterfaceCreation();
diff --git a/NorthstarDLL/masterserver/masterserver.cpp b/NorthstarDLL/masterserver/masterserver.cpp
deleted file mode 100644
index aa248464..00000000
--- a/NorthstarDLL/masterserver/masterserver.cpp
+++ /dev/null
@@ -1,1459 +0,0 @@
-#include "masterserver/masterserver.h"
-#include "core/convar/concommand.h"
-#include "shared/playlist.h"
-#include "server/auth/serverauthentication.h"
-#include "core/tier0.h"
-#include "core/vanilla.h"
-#include "engine/r2engine.h"
-#include "mods/modmanager.h"
-#include "shared/misccommands.h"
-#include "util/version.h"
-#include "server/auth/bansystem.h"
-#include "dedicated/dedicated.h"
-
-#include "rapidjson/document.h"
-#include "rapidjson/stringbuffer.h"
-#include "rapidjson/writer.h"
-#include "rapidjson/error/en.h"
-
-#include <cstring>
-#include <regex>
-
-using namespace std::chrono_literals;
-
-MasterServerManager* g_pMasterServerManager;
-
-ConVar* Cvar_ns_masterserver_hostname;
-ConVar* Cvar_ns_curl_log_enable;
-
-RemoteServerInfo::RemoteServerInfo(
- const char* newId,
- const char* newName,
- const char* newDescription,
- const char* newMap,
- const char* newPlaylist,
- const char* newRegion,
- int newPlayerCount,
- int newMaxPlayers,
- bool newRequiresPassword)
-{
- // passworded servers don't have public ips
- requiresPassword = newRequiresPassword;
-
- strncpy_s((char*)id, sizeof(id), newId, sizeof(id) - 1);
- strncpy_s((char*)name, sizeof(name), newName, sizeof(name) - 1);
-
- description = std::string(newDescription);
-
- strncpy_s((char*)map, sizeof(map), newMap, sizeof(map) - 1);
- strncpy_s((char*)playlist, sizeof(playlist), newPlaylist, sizeof(playlist) - 1);
-
- strncpy((char*)region, newRegion, sizeof(region));
- region[sizeof(region) - 1] = 0;
-
- playerCount = newPlayerCount;
- maxPlayers = newMaxPlayers;
-}
-
-void SetCommonHttpClientOptions(CURL* curl)
-{
- curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
- curl_easy_setopt(curl, CURLOPT_VERBOSE, Cvar_ns_curl_log_enable->GetBool());
- curl_easy_setopt(curl, CURLOPT_USERAGENT, &NSUserAgent);
- // Timeout since the MS has fucky async functions without await, making curl hang due to a successful connection but no response for ~90
- // seconds.
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
- // curl_easy_setopt(curl, CURLOPT_STDERR, stdout);
- if (CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work
- {
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- }
-}
-
-void MasterServerManager::ClearServerList()
-{
- // this doesn't really do anything lol, probably isn't threadsafe
- m_bRequestingServerList = true;
-
- m_vRemoteServers.clear();
-
- m_bRequestingServerList = false;
-}
-
-size_t CurlWriteToStringBufferCallback(char* contents, size_t size, size_t nmemb, void* userp)
-{
- ((std::string*)userp)->append((char*)contents, size * nmemb);
- return size * nmemb;
-}
-
-void MasterServerManager::AuthenticateOriginWithMasterServer(const char* uid, const char* originToken)
-{
- if (m_bOriginAuthWithMasterServerInProgress || g_pVanillaCompatibility->GetVanillaCompatibility())
- return;
-
- // do this here so it's instantly set
- m_bOriginAuthWithMasterServerInProgress = true;
- std::string uidStr(uid);
- std::string tokenStr(originToken);
-
- m_bOriginAuthWithMasterServerSuccessful = false;
- m_sOriginAuthWithMasterServerErrorCode = "";
- m_sOriginAuthWithMasterServerErrorMessage = "";
-
- std::thread requestThread(
- [this, uidStr, tokenStr]()
- {
- spdlog::info("Trying to authenticate with northstar masterserver for user {}", uidStr);
-
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
- std::string readBuffer;
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format("{}/client/origin_auth?id={}&token={}", Cvar_ns_masterserver_hostname->GetString(), uidStr, tokenStr).c_str());
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- {
- m_bSuccessfullyConnected = true;
-
- rapidjson_document originAuthInfo;
- originAuthInfo.Parse(readBuffer.c_str());
-
- if (originAuthInfo.HasParseError())
- {
- spdlog::error(
- "Failed reading origin auth info response: encountered parse error \"{}\"",
- rapidjson::GetParseError_En(originAuthInfo.GetParseError()));
- goto REQUEST_END_CLEANUP;
- }
-
- if (!originAuthInfo.IsObject() || !originAuthInfo.HasMember("success"))
- {
- spdlog::error("Failed reading origin auth info response: malformed response object {}", readBuffer);
- goto REQUEST_END_CLEANUP;
- }
-
- if (originAuthInfo["success"].IsTrue() && originAuthInfo.HasMember("token") && originAuthInfo["token"].IsString())
- {
- strncpy_s(
- m_sOwnClientAuthToken,
- sizeof(m_sOwnClientAuthToken),
- originAuthInfo["token"].GetString(),
- sizeof(m_sOwnClientAuthToken) - 1);
- spdlog::info("Northstar origin authentication completed successfully!");
- m_bOriginAuthWithMasterServerSuccessful = true;
- }
- else
- {
- spdlog::error("Northstar origin authentication failed");
-
- if (originAuthInfo.HasMember("error") && originAuthInfo["error"].IsObject())
- {
-
- if (originAuthInfo["error"].HasMember("enum") && originAuthInfo["error"]["enum"].IsString())
- {
- m_sOriginAuthWithMasterServerErrorCode = originAuthInfo["error"]["enum"].GetString();
- }
-
- if (originAuthInfo["error"].HasMember("msg") && originAuthInfo["error"]["msg"].IsString())
- {
- m_sOriginAuthWithMasterServerErrorMessage = originAuthInfo["error"]["msg"].GetString();
- }
- }
- }
- }
- else
- {
- spdlog::error("Failed performing northstar origin auth: error {}", curl_easy_strerror(result));
- m_bSuccessfullyConnected = false;
- }
-
- // we goto this instead of returning so we always hit this
- REQUEST_END_CLEANUP:
- m_bOriginAuthWithMasterServerInProgress = false;
- m_bOriginAuthWithMasterServerDone = true;
- curl_easy_cleanup(curl);
- });
-
- requestThread.detach();
-}
-
-void MasterServerManager::RequestServerList()
-{
- // do this here so it's instantly set on call for scripts
- m_bScriptRequestingServerList = true;
-
- std::thread requestThread(
- [this]()
- {
- // make sure we never have 2 threads writing at once
- // i sure do hope this is actually threadsafe
- while (m_bRequestingServerList)
- Sleep(100);
-
- m_bRequestingServerList = true;
- m_bScriptRequestingServerList = true;
-
- spdlog::info("Requesting server list from {}", Cvar_ns_masterserver_hostname->GetString());
-
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/servers", Cvar_ns_masterserver_hostname->GetString()).c_str());
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- {
- m_bSuccessfullyConnected = true;
-
- rapidjson_document serverInfoJson;
- serverInfoJson.Parse(readBuffer.c_str());
-
- if (serverInfoJson.HasParseError())
- {
- spdlog::error(
- "Failed reading masterserver response: encountered parse error \"{}\"",
- rapidjson::GetParseError_En(serverInfoJson.GetParseError()));
- goto REQUEST_END_CLEANUP;
- }
-
- if (serverInfoJson.IsObject() && serverInfoJson.HasMember("error"))
- {
- spdlog::error("Failed reading masterserver response: got fastify error response");
- spdlog::error(readBuffer);
- goto REQUEST_END_CLEANUP;
- }
-
- if (!serverInfoJson.IsArray())
- {
- spdlog::error("Failed reading masterserver response: root object is not an array");
- goto REQUEST_END_CLEANUP;
- }
-
- rapidjson::GenericArray<false, rapidjson_document::GenericValue> serverArray = serverInfoJson.GetArray();
-
- spdlog::info("Got {} servers", serverArray.Size());
-
- for (auto& serverObj : serverArray)
- {
- if (!serverObj.IsObject())
- {
- spdlog::error("Failed reading masterserver response: member of server array is not an object");
- goto REQUEST_END_CLEANUP;
- }
-
- // todo: verify json props are fine before adding to m_remoteServers
- if (!serverObj.HasMember("id") || !serverObj["id"].IsString() || !serverObj.HasMember("name") ||
- !serverObj["name"].IsString() || !serverObj.HasMember("description") || !serverObj["description"].IsString() ||
- !serverObj.HasMember("map") || !serverObj["map"].IsString() || !serverObj.HasMember("playlist") ||
- !serverObj["playlist"].IsString() || !serverObj.HasMember("playerCount") || !serverObj["playerCount"].IsNumber() ||
- !serverObj.HasMember("maxPlayers") || !serverObj["maxPlayers"].IsNumber() || !serverObj.HasMember("hasPassword") ||
- !serverObj["hasPassword"].IsBool() || !serverObj.HasMember("modInfo") || !serverObj["modInfo"].HasMember("Mods") ||
- !serverObj["modInfo"]["Mods"].IsArray())
- {
- spdlog::error("Failed reading masterserver response: malformed server object");
- continue;
- };
-
- const char* id = serverObj["id"].GetString();
-
- RemoteServerInfo* newServer = nullptr;
-
- bool createNewServerInfo = true;
- for (RemoteServerInfo& server : m_vRemoteServers)
- {
- // if server already exists, update info rather than adding to it
- if (!strncmp((const char*)server.id, id, 32))
- {
- server = RemoteServerInfo(
- id,
- serverObj["name"].GetString(),
- serverObj["description"].GetString(),
- serverObj["map"].GetString(),
- serverObj["playlist"].GetString(),
- (serverObj.HasMember("region") && serverObj["region"].IsString()) ? serverObj["region"].GetString() : "",
- serverObj["playerCount"].GetInt(),
- serverObj["maxPlayers"].GetInt(),
- serverObj["hasPassword"].IsTrue());
- newServer = &server;
- createNewServerInfo = false;
- break;
- }
- }
-
- // server didn't exist
- if (createNewServerInfo)
- newServer = &m_vRemoteServers.emplace_back(
- id,
- serverObj["name"].GetString(),
- serverObj["description"].GetString(),
- serverObj["map"].GetString(),
- serverObj["playlist"].GetString(),
- (serverObj.HasMember("region") && serverObj["region"].IsString()) ? serverObj["region"].GetString() : "",
- serverObj["playerCount"].GetInt(),
- serverObj["maxPlayers"].GetInt(),
- serverObj["hasPassword"].IsTrue());
-
- newServer->requiredMods.clear();
- for (auto& requiredMod : serverObj["modInfo"]["Mods"].GetArray())
- {
- RemoteModInfo modInfo;
-
- if (!requiredMod.HasMember("RequiredOnClient") || !requiredMod["RequiredOnClient"].IsTrue())
- continue;
-
- if (!requiredMod.HasMember("Name") || !requiredMod["Name"].IsString())
- continue;
- modInfo.Name = requiredMod["Name"].GetString();
-
- if (!requiredMod.HasMember("Version") || !requiredMod["Version"].IsString())
- continue;
- modInfo.Version = requiredMod["Version"].GetString();
-
- newServer->requiredMods.push_back(modInfo);
- }
- // Can probably re-enable this later with a -verbose flag, but slows down loading of the server browser quite a bit as
- // is
- // spdlog::info(
- // "Server {} on map {} with playlist {} has {}/{} players", serverObj["name"].GetString(),
- // serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(),
- // serverObj["maxPlayers"].GetInt());
- }
-
- std::sort(
- m_vRemoteServers.begin(),
- m_vRemoteServers.end(),
- [](RemoteServerInfo& a, RemoteServerInfo& b) { return a.playerCount > b.playerCount; });
- }
- else
- {
- spdlog::error("Failed requesting servers: error {}", curl_easy_strerror(result));
- m_bSuccessfullyConnected = false;
- }
-
- // we goto this instead of returning so we always hit this
- REQUEST_END_CLEANUP:
- m_bRequestingServerList = false;
- m_bScriptRequestingServerList = false;
- curl_easy_cleanup(curl);
- });
-
- requestThread.detach();
-}
-
-void MasterServerManager::RequestMainMenuPromos()
-{
- m_bHasMainMenuPromoData = false;
-
- std::thread requestThread(
- [this]()
- {
- while (m_bOriginAuthWithMasterServerInProgress || !m_bOriginAuthWithMasterServerDone)
- Sleep(500);
-
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(
- curl, CURLOPT_URL, fmt::format("{}/client/mainmenupromos", Cvar_ns_masterserver_hostname->GetString()).c_str());
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- {
- m_bSuccessfullyConnected = true;
-
- rapidjson_document mainMenuPromoJson;
- mainMenuPromoJson.Parse(readBuffer.c_str());
-
- if (mainMenuPromoJson.HasParseError())
- {
- spdlog::error(
- "Failed reading masterserver main menu promos response: encountered parse error \"{}\"",
- rapidjson::GetParseError_En(mainMenuPromoJson.GetParseError()));
- goto REQUEST_END_CLEANUP;
- }
-
- if (!mainMenuPromoJson.IsObject())
- {
- spdlog::error("Failed reading masterserver main menu promos response: root object is not an object");
- goto REQUEST_END_CLEANUP;
- }
-
- if (mainMenuPromoJson.HasMember("error"))
- {
- spdlog::error("Failed reading masterserver response: got fastify error response");
- spdlog::error(readBuffer);
- goto REQUEST_END_CLEANUP;
- }
-
- if (!mainMenuPromoJson.HasMember("newInfo") || !mainMenuPromoJson["newInfo"].IsObject() ||
- !mainMenuPromoJson["newInfo"].HasMember("Title1") || !mainMenuPromoJson["newInfo"]["Title1"].IsString() ||
- !mainMenuPromoJson["newInfo"].HasMember("Title2") || !mainMenuPromoJson["newInfo"]["Title2"].IsString() ||
- !mainMenuPromoJson["newInfo"].HasMember("Title3") || !mainMenuPromoJson["newInfo"]["Title3"].IsString() ||
-
- !mainMenuPromoJson.HasMember("largeButton") || !mainMenuPromoJson["largeButton"].IsObject() ||
- !mainMenuPromoJson["largeButton"].HasMember("Title") || !mainMenuPromoJson["largeButton"]["Title"].IsString() ||
- !mainMenuPromoJson["largeButton"].HasMember("Text") || !mainMenuPromoJson["largeButton"]["Text"].IsString() ||
- !mainMenuPromoJson["largeButton"].HasMember("Url") || !mainMenuPromoJson["largeButton"]["Url"].IsString() ||
- !mainMenuPromoJson["largeButton"].HasMember("ImageIndex") ||
- !mainMenuPromoJson["largeButton"]["ImageIndex"].IsNumber() ||
-
- !mainMenuPromoJson.HasMember("smallButton1") || !mainMenuPromoJson["smallButton1"].IsObject() ||
- !mainMenuPromoJson["smallButton1"].HasMember("Title") || !mainMenuPromoJson["smallButton1"]["Title"].IsString() ||
- !mainMenuPromoJson["smallButton1"].HasMember("Url") || !mainMenuPromoJson["smallButton1"]["Url"].IsString() ||
- !mainMenuPromoJson["smallButton1"].HasMember("ImageIndex") ||
- !mainMenuPromoJson["smallButton1"]["ImageIndex"].IsNumber() ||
-
- !mainMenuPromoJson.HasMember("smallButton2") || !mainMenuPromoJson["smallButton2"].IsObject() ||
- !mainMenuPromoJson["smallButton2"].HasMember("Title") || !mainMenuPromoJson["smallButton2"]["Title"].IsString() ||
- !mainMenuPromoJson["smallButton2"].HasMember("Url") || !mainMenuPromoJson["smallButton2"]["Url"].IsString() ||
- !mainMenuPromoJson["smallButton2"].HasMember("ImageIndex") ||
- !mainMenuPromoJson["smallButton2"]["ImageIndex"].IsNumber())
- {
- spdlog::error("Failed reading masterserver main menu promos response: malformed json object");
- goto REQUEST_END_CLEANUP;
- }
-
- m_sMainMenuPromoData.newInfoTitle1 = mainMenuPromoJson["newInfo"]["Title1"].GetString();
- m_sMainMenuPromoData.newInfoTitle2 = mainMenuPromoJson["newInfo"]["Title2"].GetString();
- m_sMainMenuPromoData.newInfoTitle3 = mainMenuPromoJson["newInfo"]["Title3"].GetString();
-
- m_sMainMenuPromoData.largeButtonTitle = mainMenuPromoJson["largeButton"]["Title"].GetString();
- m_sMainMenuPromoData.largeButtonText = mainMenuPromoJson["largeButton"]["Text"].GetString();
- m_sMainMenuPromoData.largeButtonUrl = mainMenuPromoJson["largeButton"]["Url"].GetString();
- m_sMainMenuPromoData.largeButtonImageIndex = mainMenuPromoJson["largeButton"]["ImageIndex"].GetInt();
-
- m_sMainMenuPromoData.smallButton1Title = mainMenuPromoJson["smallButton1"]["Title"].GetString();
- m_sMainMenuPromoData.smallButton1Url = mainMenuPromoJson["smallButton1"]["Url"].GetString();
- m_sMainMenuPromoData.smallButton1ImageIndex = mainMenuPromoJson["smallButton1"]["ImageIndex"].GetInt();
-
- m_sMainMenuPromoData.smallButton2Title = mainMenuPromoJson["smallButton2"]["Title"].GetString();
- m_sMainMenuPromoData.smallButton2Url = mainMenuPromoJson["smallButton2"]["Url"].GetString();
- m_sMainMenuPromoData.smallButton2ImageIndex = mainMenuPromoJson["smallButton2"]["ImageIndex"].GetInt();
-
- m_bHasMainMenuPromoData = true;
- }
- else
- {
- spdlog::error("Failed requesting main menu promos: error {}", curl_easy_strerror(result));
- m_bSuccessfullyConnected = false;
- }
-
- REQUEST_END_CLEANUP:
- // nothing lol
- curl_easy_cleanup(curl);
- });
-
- requestThread.detach();
-}
-
-void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* playerToken)
-{
- // dont wait, just stop if we're trying to do 2 auth requests at once
- if (m_bAuthenticatingWithGameServer || g_pVanillaCompatibility->GetVanillaCompatibility())
- return;
-
- m_bAuthenticatingWithGameServer = true;
- m_bScriptAuthenticatingWithGameServer = true;
- m_bSuccessfullyAuthenticatedWithGameServer = false;
- m_sAuthFailureReason = "Authentication Failed";
-
- std::string uidStr(uid);
- std::string tokenStr(playerToken);
-
- std::thread requestThread(
- [this, uidStr, tokenStr]()
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format("{}/client/auth_with_self?id={}&playerToken={}", Cvar_ns_masterserver_hostname->GetString(), uidStr, tokenStr)
- .c_str());
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- {
- m_bSuccessfullyConnected = true;
-
- rapidjson_document authInfoJson;
- authInfoJson.Parse(readBuffer.c_str());
-
- if (authInfoJson.HasParseError())
- {
- spdlog::error(
- "Failed reading masterserver authentication response: encountered parse error \"{}\"",
- rapidjson::GetParseError_En(authInfoJson.GetParseError()));
- goto REQUEST_END_CLEANUP;
- }
-
- if (!authInfoJson.IsObject())
- {
- spdlog::error("Failed reading masterserver authentication response: root object is not an object");
- goto REQUEST_END_CLEANUP;
- }
-
- if (authInfoJson.HasMember("error"))
- {
- spdlog::error("Failed reading masterserver response: got fastify error response");
- spdlog::error(readBuffer);
-
- if (authInfoJson["error"].HasMember("msg"))
- m_sAuthFailureReason = authInfoJson["error"]["msg"].GetString();
- else if (authInfoJson["error"].HasMember("enum"))
- m_sAuthFailureReason = authInfoJson["error"]["enum"].GetString();
- else
- m_sAuthFailureReason = "No error message provided";
-
- goto REQUEST_END_CLEANUP;
- }
-
- if (!authInfoJson["success"].IsTrue())
- {
- spdlog::error("Authentication with masterserver failed: \"success\" is not true");
- goto REQUEST_END_CLEANUP;
- }
-
- if (!authInfoJson.HasMember("success") || !authInfoJson.HasMember("id") || !authInfoJson["id"].IsString() ||
- !authInfoJson.HasMember("authToken") || !authInfoJson["authToken"].IsString() ||
- !authInfoJson.HasMember("persistentData") || !authInfoJson["persistentData"].IsArray())
- {
- spdlog::error("Failed reading masterserver authentication response: malformed json object");
- goto REQUEST_END_CLEANUP;
- }
-
- RemoteAuthData newAuthData {};
- strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), authInfoJson["id"].GetString(), sizeof(newAuthData.uid) - 1);
-
- newAuthData.pdataSize = authInfoJson["persistentData"].GetArray().Size();
- newAuthData.pdata = new char[newAuthData.pdataSize];
- // memcpy(newAuthData.pdata, authInfoJson["persistentData"].GetString(), newAuthData.pdataSize);
-
- int i = 0;
- // note: persistentData is a uint8array because i had problems getting strings to behave, it sucks but it's just how it be
- // unfortunately potentially refactor later
- for (auto& byte : authInfoJson["persistentData"].GetArray())
- {
- if (!byte.IsUint() || byte.GetUint() > 255)
- {
- spdlog::error("Failed reading masterserver authentication response: malformed json object");
- goto REQUEST_END_CLEANUP;
- }
-
- newAuthData.pdata[i++] = static_cast<char>(byte.GetUint());
- }
-
- std::lock_guard<std::mutex> guard(g_pServerAuthentication->m_AuthDataMutex);
- g_pServerAuthentication->m_RemoteAuthenticationData.clear();
- g_pServerAuthentication->m_RemoteAuthenticationData.insert(
- std::make_pair(authInfoJson["authToken"].GetString(), newAuthData));
-
- m_bSuccessfullyAuthenticatedWithGameServer = true;
- }
- else
- {
- spdlog::error("Failed authenticating with own server: error {}", curl_easy_strerror(result));
- m_bSuccessfullyConnected = false;
- m_bSuccessfullyAuthenticatedWithGameServer = false;
- m_bScriptAuthenticatingWithGameServer = false;
- }
-
- REQUEST_END_CLEANUP:
- m_bAuthenticatingWithGameServer = false;
- m_bScriptAuthenticatingWithGameServer = false;
-
- if (m_bNewgameAfterSelfAuth)
- {
- // pretty sure this is threadsafe?
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "ns_end_reauth_and_leave_to_lobby", cmd_source_t::kCommandSrcCode);
- m_bNewgameAfterSelfAuth = false;
- }
-
- curl_easy_cleanup(curl);
- });
-
- requestThread.detach();
-}
-
-void MasterServerManager::AuthenticateWithServer(const char* uid, const char* playerToken, RemoteServerInfo server, const char* password)
-{
- // dont wait, just stop if we're trying to do 2 auth requests at once
- if (m_bAuthenticatingWithGameServer || g_pVanillaCompatibility->GetVanillaCompatibility())
- return;
-
- m_bAuthenticatingWithGameServer = true;
- m_bScriptAuthenticatingWithGameServer = true;
- m_bSuccessfullyAuthenticatedWithGameServer = false;
- m_sAuthFailureReason = "Authentication Failed";
-
- std::string uidStr(uid);
- std::string tokenStr(playerToken);
- std::string serverIdStr(server.id);
- std::string passwordStr(password);
-
- std::thread requestThread(
- [this, uidStr, tokenStr, serverIdStr, passwordStr, server]()
- {
- // esnure that any persistence saving is done, so we know masterserver has newest
- while (m_bSavingPersistentData)
- Sleep(100);
-
- spdlog::info("Attempting authentication with server of id \"{}\"", serverIdStr);
-
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- {
- char* escapedPassword = curl_easy_escape(curl, passwordStr.c_str(), passwordStr.length());
-
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/client/auth_with_server?id={}&playerToken={}&server={}&password={}",
- Cvar_ns_masterserver_hostname->GetString(),
- uidStr,
- tokenStr,
- serverIdStr,
- escapedPassword)
- .c_str());
-
- curl_free(escapedPassword);
- }
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- {
- m_bSuccessfullyConnected = true;
-
- rapidjson_document connectionInfoJson;
- connectionInfoJson.Parse(readBuffer.c_str());
-
- if (connectionInfoJson.HasParseError())
- {
- spdlog::error(
- "Failed reading masterserver authentication response: encountered parse error \"{}\"",
- rapidjson::GetParseError_En(connectionInfoJson.GetParseError()));
- goto REQUEST_END_CLEANUP;
- }
-
- if (!connectionInfoJson.IsObject())
- {
- spdlog::error("Failed reading masterserver authentication response: root object is not an object");
- goto REQUEST_END_CLEANUP;
- }
-
- if (connectionInfoJson.HasMember("error"))
- {
- spdlog::error("Failed reading masterserver response: got fastify error response");
- spdlog::error(readBuffer);
-
- if (connectionInfoJson["error"].HasMember("msg"))
- m_sAuthFailureReason = connectionInfoJson["error"]["msg"].GetString();
- else if (connectionInfoJson["error"].HasMember("enum"))
- m_sAuthFailureReason = connectionInfoJson["error"]["enum"].GetString();
- else
- m_sAuthFailureReason = "No error message provided";
-
- goto REQUEST_END_CLEANUP;
- }
-
- if (!connectionInfoJson["success"].IsTrue())
- {
- spdlog::error("Authentication with masterserver failed: \"success\" is not true");
- goto REQUEST_END_CLEANUP;
- }
-
- if (!connectionInfoJson.HasMember("success") || !connectionInfoJson.HasMember("ip") ||
- !connectionInfoJson["ip"].IsString() || !connectionInfoJson.HasMember("port") ||
- !connectionInfoJson["port"].IsNumber() || !connectionInfoJson.HasMember("authToken") ||
- !connectionInfoJson["authToken"].IsString())
- {
- spdlog::error("Failed reading masterserver authentication response: malformed json object");
- goto REQUEST_END_CLEANUP;
- }
-
- m_pendingConnectionInfo.ip.S_un.S_addr = inet_addr(connectionInfoJson["ip"].GetString());
- m_pendingConnectionInfo.port = (unsigned short)connectionInfoJson["port"].GetUint();
-
- strncpy_s(
- m_pendingConnectionInfo.authToken,
- sizeof(m_pendingConnectionInfo.authToken),
- connectionInfoJson["authToken"].GetString(),
- sizeof(m_pendingConnectionInfo.authToken) - 1);
-
- m_bHasPendingConnectionInfo = true;
- m_bSuccessfullyAuthenticatedWithGameServer = true;
-
- m_currentServer = server;
- m_sCurrentServerPassword = passwordStr;
- }
- else
- {
- spdlog::error("Failed authenticating with server: error {}", curl_easy_strerror(result));
- m_bSuccessfullyConnected = false;
- m_bSuccessfullyAuthenticatedWithGameServer = false;
- m_bScriptAuthenticatingWithGameServer = false;
- }
-
- REQUEST_END_CLEANUP:
- m_bAuthenticatingWithGameServer = false;
- m_bScriptAuthenticatingWithGameServer = false;
- curl_easy_cleanup(curl);
- });
-
- requestThread.detach();
-}
-
-void MasterServerManager::WritePlayerPersistentData(const char* playerId, const char* pdata, size_t pdataSize)
-{
- // still call this if we don't have a server id, since lobbies that aren't port forwarded need to be able to call it
- m_bSavingPersistentData = true;
- if (!pdataSize)
- {
- spdlog::warn("attempted to write pdata of size 0!");
- return;
- }
-
- std::string strPlayerId(playerId);
- std::string strPdata(pdata, pdataSize);
-
- std::thread requestThread(
- [this, strPlayerId, strPdata, pdataSize]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/accounts/write_persistence?id={}&serverId={}",
- Cvar_ns_masterserver_hostname->GetString(),
- strPlayerId,
- m_sOwnServerId)
- .c_str());
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- curl_mime* mime = curl_mime_init(curl);
- curl_mimepart* part = curl_mime_addpart(mime);
-
- curl_mime_data(part, strPdata.c_str(), pdataSize);
- curl_mime_name(part, "pdata");
- curl_mime_filename(part, "file.pdata");
- curl_mime_type(part, "application/octet-stream");
-
- curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- m_bSuccessfullyConnected = true;
- else
- m_bSuccessfullyConnected = false;
-
- curl_easy_cleanup(curl);
-
- m_bSavingPersistentData = false;
- });
-
- requestThread.detach();
-}
-
-void MasterServerManager::ProcessConnectionlessPacketSigreq1(std::string data)
-{
- rapidjson_document obj;
- obj.Parse(data);
-
- if (obj.HasParseError())
- {
- // note: it's okay to print the data as-is since we've already checked that it actually came from Atlas
- spdlog::error("invalid Atlas connectionless packet request ({}): {}", data, GetParseError_En(obj.GetParseError()));
- return;
- }
-
- if (!obj.HasMember("type") || !obj["type"].IsString())
- {
- spdlog::error("invalid Atlas connectionless packet request ({}): missing type", data);
- return;
- }
-
- std::string type = obj["type"].GetString();
-
- if (type == "connect")
- {
- if (!obj.HasMember("token") || !obj["token"].IsString())
- {
- spdlog::error("failed to handle Atlas connect request: missing or invalid connection token field");
- return;
- }
- std::string token = obj["token"].GetString();
-
- if (!m_handledServerConnections.contains(token))
- m_handledServerConnections.insert(token);
- else
- return; // already handled
-
- spdlog::info("handling Atlas connect request {}", data);
-
- if (!obj.HasMember("uid") || !obj["uid"].IsUint64())
- {
- spdlog::error("failed to handle Atlas connect request {}: missing or invalid uid field", token);
- return;
- }
- uint64_t uid = obj["uid"].GetUint64();
-
- std::string username;
- if (obj.HasMember("username") && obj["username"].IsString())
- username = obj["username"].GetString();
-
- std::string reject;
- if (!g_pBanSystem->IsUIDAllowed(uid))
- reject = "Banned from this server.";
-
- std::string pdata;
- if (reject == "")
- {
- spdlog::info("getting pdata for connection {} (uid={} username={})", token, uid, username);
-
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format("{}/server/connect?serverId={}&token={}", Cvar_ns_masterserver_hostname->GetString(), m_sOwnServerId, token)
- .c_str());
-
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &pdata);
-
- CURLcode result = curl_easy_perform(curl);
- if (result != CURLcode::CURLE_OK)
- {
- spdlog::error("failed to make Atlas connect pdata request {}: {}", token, curl_easy_strerror(result));
- curl_easy_cleanup(curl);
- return;
- }
-
- long respStatus = -1;
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &respStatus);
-
- curl_easy_cleanup(curl);
-
- if (respStatus != 200)
- {
- rapidjson_document obj;
- obj.Parse(pdata.c_str());
-
- if (!obj.HasParseError() && obj.HasMember("error") && obj["error"].IsObject())
- spdlog::error(
- "failed to make Atlas connect pdata request {}: response status {}, error: {} ({})",
- token,
- respStatus,
- ((obj["error"].HasMember("enum") && obj["error"]["enum"].IsString()) ? obj["error"]["enum"].GetString() : ""),
- ((obj["error"].HasMember("msg") && obj["error"]["msg"].IsString()) ? obj["error"]["msg"].GetString() : ""));
- else
- spdlog::error("failed to make Atlas connect pdata request {}: response status {}", token, respStatus);
- return;
- }
-
- if (!pdata.length())
- {
- spdlog::error("failed to make Atlas connect pdata request {}: pdata response is empty", token);
- return;
- }
-
- if (pdata.length() > PERSISTENCE_MAX_SIZE)
- {
- spdlog::error(
- "failed to make Atlas connect pdata request {}: pdata is too large (max={} len={})",
- token,
- PERSISTENCE_MAX_SIZE,
- pdata.length());
- return;
- }
- }
-
- if (reject == "")
- spdlog::info("accepting connection {} (uid={} username={}) with {} bytes of pdata", token, uid, username, pdata.length());
- else
- spdlog::info("rejecting connection {} (uid={} username={}) with reason \"{}\"", token, uid, username, reject);
-
- if (reject == "")
- g_pServerAuthentication->AddRemotePlayer(token, uid, username, pdata);
-
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- char* rejectEnc = curl_easy_escape(curl, reject.c_str(), reject.length());
- if (!rejectEnc)
- {
- spdlog::error("failed to handle Atlas connect request {}: failed to escape reject", token);
- return;
- }
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/connect?serverId={}&token={}&reject={}",
- Cvar_ns_masterserver_hostname->GetString(),
- m_sOwnServerId,
- token,
- rejectEnc)
- .c_str());
- curl_free(rejectEnc);
-
- // note: we don't actually have any POST data, so we can't use CURLOPT_POST or the behavior is undefined (e.g., hangs in wine)
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
-
- std::string buf;
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
-
- CURLcode result = curl_easy_perform(curl);
- if (result != CURLcode::CURLE_OK)
- {
- spdlog::error("failed to respond to Atlas connect request {}: {}", token, curl_easy_strerror(result));
- curl_easy_cleanup(curl);
- return;
- }
-
- long respStatus = -1;
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &respStatus);
-
- curl_easy_cleanup(curl);
-
- if (respStatus != 200)
- {
- rapidjson_document obj;
- obj.Parse(buf.c_str());
-
- if (!obj.HasParseError() && obj.HasMember("error") && obj["error"].IsObject())
- spdlog::error(
- "failed to respond to Atlas connect request {}: response status {}, error: {} ({})",
- token,
- respStatus,
- ((obj["error"].HasMember("enum") && obj["error"]["enum"].IsString()) ? obj["error"]["enum"].GetString() : ""),
- ((obj["error"].HasMember("msg") && obj["error"]["msg"].IsString()) ? obj["error"]["msg"].GetString() : ""));
- else
- spdlog::error("failed to respond to Atlas connect request {}: response status {}", token, respStatus);
- return;
- }
- }
-
- return;
- }
-
- spdlog::error("invalid Atlas connectionless packet request: unknown type {}", type);
-}
-
-void ConCommand_ns_fetchservers(const CCommand& args)
-{
- g_pMasterServerManager->RequestServerList();
-}
-
-MasterServerManager::MasterServerManager() : m_pendingConnectionInfo {}, m_sOwnServerId {""}, m_sOwnClientAuthToken {""} {}
-
-ON_DLL_LOAD_RELIESON("engine.dll", MasterServer, (ConCommand, ServerPresence), (CModule module))
-{
- g_pMasterServerManager = new MasterServerManager;
-
- Cvar_ns_masterserver_hostname = new ConVar("ns_masterserver_hostname", "127.0.0.1", FCVAR_NONE, "");
- Cvar_ns_curl_log_enable = new ConVar("ns_curl_log_enable", "0", FCVAR_NONE, "Whether curl should log to the console");
-
- RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "Fetch all servers from the masterserver", FCVAR_CLIENTDLL);
-
- MasterServerPresenceReporter* presenceReporter = new MasterServerPresenceReporter;
- g_pServerPresence->AddPresenceReporter(presenceReporter);
-}
-
-void MasterServerPresenceReporter::CreatePresence(const ServerPresence* pServerPresence)
-{
- m_nNumRegistrationAttempts = 0;
-}
-
-void MasterServerPresenceReporter::ReportPresence(const ServerPresence* pServerPresence)
-{
- // make a copy of presence for multithreading purposes
- ServerPresence threadedPresence(pServerPresence);
-
- if (!*g_pMasterServerManager->m_sOwnServerId)
- {
- // Don't try if we've reached the max registration attempts.
- // In the future, we should probably allow servers to re-authenticate after a while if the MS was down.
- if (m_nNumRegistrationAttempts >= MAX_REGISTRATION_ATTEMPTS)
- {
- return;
- }
-
- // Make sure to wait til the cooldown is over for DUPLICATE_SERVER failures.
- if (Plat_FloatTime() < m_fNextAddServerAttemptTime)
- {
- return;
- }
-
- // If we're not running any InternalAddServer() attempt in the background.
- if (!addServerFuture.valid())
- {
- // Launch an attempt to add the local server to the master server.
- InternalAddServer(pServerPresence);
- }
- }
- else
- {
- // If we're not running any InternalUpdateServer() attempt in the background.
- if (!updateServerFuture.valid())
- {
- // Launch an attempt to update the local server on the master server.
- InternalUpdateServer(pServerPresence);
- }
- }
-}
-
-void MasterServerPresenceReporter::DestroyPresence(const ServerPresence* pServerPresence)
-{
- // Don't call this if we don't have a server id.
- if (!*g_pMasterServerManager->m_sOwnServerId)
- {
- return;
- }
-
- // Not bothering with better thread safety in this case since DestroyPresence() is called when the game is shutting down.
- *g_pMasterServerManager->m_sOwnServerId = 0;
-
- std::thread requestThread(
- [this]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/remove_server?id={}", Cvar_ns_masterserver_hostname->GetString(), g_pMasterServerManager->m_sOwnServerId)
- .c_str());
-
- CURLcode result = curl_easy_perform(curl);
- curl_easy_cleanup(curl);
- });
-
- requestThread.detach();
-}
-
-void MasterServerPresenceReporter::RunFrame(double flCurrentTime, const ServerPresence* pServerPresence)
-{
- // Check if we're already running an InternalAddServer() call in the background.
- // If so, grab the result if it's ready.
- if (addServerFuture.valid())
- {
- std::future_status status = addServerFuture.wait_for(0ms);
- if (status != std::future_status::ready)
- {
- // Still running, no need to do anything.
- return;
- }
-
- // Check the result.
- auto resultData = addServerFuture.get();
-
- g_pMasterServerManager->m_bSuccessfullyConnected = resultData.result != MasterServerReportPresenceResult::FailedNoConnect;
-
- switch (resultData.result)
- {
- case MasterServerReportPresenceResult::Success:
- // Copy over the server id and auth token granted by the MS.
- strncpy_s(
- g_pMasterServerManager->m_sOwnServerId,
- sizeof(g_pMasterServerManager->m_sOwnServerId),
- resultData.id.value().c_str(),
- sizeof(g_pMasterServerManager->m_sOwnServerId) - 1);
- strncpy_s(
- g_pMasterServerManager->m_sOwnServerAuthToken,
- sizeof(g_pMasterServerManager->m_sOwnServerAuthToken),
- resultData.serverAuthToken.value().c_str(),
- sizeof(g_pMasterServerManager->m_sOwnServerAuthToken) - 1);
- break;
- case MasterServerReportPresenceResult::FailedNoRetry:
- case MasterServerReportPresenceResult::FailedNoConnect:
- // If we failed to connect to the master server, or failed with no retry, stop trying.
- m_nNumRegistrationAttempts = MAX_REGISTRATION_ATTEMPTS;
- break;
- case MasterServerReportPresenceResult::Failed:
- ++m_nNumRegistrationAttempts;
- break;
- case MasterServerReportPresenceResult::FailedDuplicateServer:
- ++m_nNumRegistrationAttempts;
- // Wait at least twenty seconds until we re-attempt to add the server.
- m_fNextAddServerAttemptTime = Plat_FloatTime() + 20.0f;
- break;
- }
-
- if (m_nNumRegistrationAttempts >= MAX_REGISTRATION_ATTEMPTS)
- {
- spdlog::log(
- IsDedicatedServer() ? spdlog::level::level_enum::err : spdlog::level::level_enum::warn,
- "Reached max ms server registration attempts.");
- }
- }
- else if (updateServerFuture.valid())
- {
- // Check if the InternalUpdateServer() call completed.
- std::future_status status = updateServerFuture.wait_for(0ms);
- if (status != std::future_status::ready)
- {
- // Still running, no need to do anything.
- return;
- }
-
- auto resultData = updateServerFuture.get();
- if (resultData.result == MasterServerReportPresenceResult::Success)
- {
- if (resultData.id)
- {
- strncpy_s(
- g_pMasterServerManager->m_sOwnServerId,
- sizeof(g_pMasterServerManager->m_sOwnServerId),
- resultData.id.value().c_str(),
- sizeof(g_pMasterServerManager->m_sOwnServerId) - 1);
- }
-
- if (resultData.serverAuthToken)
- {
- strncpy_s(
- g_pMasterServerManager->m_sOwnServerAuthToken,
- sizeof(g_pMasterServerManager->m_sOwnServerAuthToken),
- resultData.serverAuthToken.value().c_str(),
- sizeof(g_pMasterServerManager->m_sOwnServerAuthToken) - 1);
- }
- }
- }
-}
-
-void MasterServerPresenceReporter::InternalAddServer(const ServerPresence* pServerPresence)
-{
- const ServerPresence threadedPresence(pServerPresence);
- // Never call this with an ongoing InternalAddServer() call.
- assert(!addServerFuture.valid());
-
- g_pMasterServerManager->m_sOwnServerId[0] = 0;
- g_pMasterServerManager->m_sOwnServerAuthToken[0] = 0;
-
- std::string modInfo = g_pMasterServerManager->m_sOwnModInfoJson;
- std::string hostname = Cvar_ns_masterserver_hostname->GetString();
-
- spdlog::info("Attempting to register the local server to the master server.");
-
- addServerFuture = std::async(
- std::launch::async,
- [threadedPresence, modInfo, hostname, pServerPresence]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- curl_mime* mime = curl_mime_init(curl);
- curl_mimepart* part = curl_mime_addpart(mime);
-
- // Lambda to quickly cleanup resources and return a value.
- auto ReturnCleanup =
- [curl, mime](MasterServerReportPresenceResult result, const char* id = "", const char* serverAuthToken = "")
- {
- curl_easy_cleanup(curl);
- curl_mime_free(mime);
-
- MasterServerPresenceReporter::ReportPresenceResultData data;
- data.result = result;
- data.id = id;
- data.serverAuthToken = serverAuthToken;
-
- return data;
- };
-
- // don't log errors if we wouldn't actually show up in the server list anyway (stop tickets)
- // except for dedis, for which this error logging is actually pretty important
- bool shouldLogError = IsDedicatedServer() || (!strstr(pServerPresence->m_MapName, "mp_lobby") &&
- strstr(pServerPresence->m_PlaylistName, "private_match"));
-
- curl_mime_data(part, modInfo.c_str(), modInfo.size());
- curl_mime_name(part, "modinfo");
- curl_mime_filename(part, "modinfo.json");
- curl_mime_type(part, "application/json");
-
- curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
-
- // format every paramter because computers hate me
- {
- char* nameEscaped = curl_easy_escape(curl, threadedPresence.m_sServerName.c_str(), 0);
- char* descEscaped = curl_easy_escape(curl, threadedPresence.m_sServerDesc.c_str(), 0);
- char* mapEscaped = curl_easy_escape(curl, threadedPresence.m_MapName, 0);
- char* playlistEscaped = curl_easy_escape(curl, threadedPresence.m_PlaylistName, 0);
- char* passwordEscaped = curl_easy_escape(curl, threadedPresence.m_Password, 0);
-
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/"
- "add_server?port={}&authPort=udp&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}",
- hostname.c_str(),
- threadedPresence.m_iPort,
- nameEscaped,
- descEscaped,
- mapEscaped,
- playlistEscaped,
- threadedPresence.m_iMaxPlayers,
- passwordEscaped)
- .c_str());
-
- curl_free(nameEscaped);
- curl_free(descEscaped);
- curl_free(mapEscaped);
- curl_free(playlistEscaped);
- curl_free(passwordEscaped);
- }
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- {
- rapidjson_document serverAddedJson;
- serverAddedJson.Parse(readBuffer.c_str());
-
- // If we could not parse the JSON or it isn't an object, assume the MS is either wrong or we're completely out of date.
- // No retry.
- if (serverAddedJson.HasParseError())
- {
- if (shouldLogError)
- spdlog::error(
- "Failed reading masterserver authentication response: encountered parse error \"{}\"",
- rapidjson::GetParseError_En(serverAddedJson.GetParseError()));
- return ReturnCleanup(MasterServerReportPresenceResult::FailedNoRetry);
- }
-
- if (!serverAddedJson.IsObject())
- {
- if (shouldLogError)
- spdlog::error("Failed reading masterserver authentication response: root object is not an object");
- return ReturnCleanup(MasterServerReportPresenceResult::FailedNoRetry);
- }
-
- if (serverAddedJson.HasMember("error"))
- {
- if (shouldLogError)
- {
- spdlog::error("Failed reading masterserver response: got fastify error response");
- spdlog::error(readBuffer);
- }
-
- // If this is DUPLICATE_SERVER, we'll retry adding the server every 20 seconds.
- // The master server will only update its internal server list and clean up dead servers on certain events.
- // And then again, only if a player requests the server list after the cooldown (1 second by default), or a server is
- // added/updated/removed. In any case this needs to be fixed in the master server rewrite.
- if (serverAddedJson["error"].HasMember("enum") &&
- strcmp(serverAddedJson["error"]["enum"].GetString(), "DUPLICATE_SERVER") == 0)
- {
- if (shouldLogError)
- spdlog::error("Cooling down while the master server cleans the dead server entry, if any.");
- return ReturnCleanup(MasterServerReportPresenceResult::FailedDuplicateServer);
- }
-
- // Retry until we reach max retries.
- return ReturnCleanup(MasterServerReportPresenceResult::Failed);
- }
-
- if (!serverAddedJson["success"].IsTrue())
- {
- if (shouldLogError)
- spdlog::error("Adding server to masterserver failed: \"success\" is not true");
- return ReturnCleanup(MasterServerReportPresenceResult::FailedNoRetry);
- }
-
- if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString() ||
- !serverAddedJson.HasMember("serverAuthToken") || !serverAddedJson["serverAuthToken"].IsString())
- {
- if (shouldLogError)
- spdlog::error("Failed reading masterserver response: malformed json object");
- return ReturnCleanup(MasterServerReportPresenceResult::FailedNoRetry);
- }
-
- spdlog::info("Successfully registered the local server to the master server.");
- return ReturnCleanup(
- MasterServerReportPresenceResult::Success,
- serverAddedJson["id"].GetString(),
- serverAddedJson["serverAuthToken"].GetString());
- }
- else
- {
- if (shouldLogError)
- spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result));
- return ReturnCleanup(MasterServerReportPresenceResult::FailedNoConnect);
- }
- });
-}
-
-void MasterServerPresenceReporter::InternalUpdateServer(const ServerPresence* pServerPresence)
-{
- const ServerPresence threadedPresence(pServerPresence);
-
- // Never call this with an ongoing InternalUpdateServer() call.
- assert(!updateServerFuture.valid());
-
- const std::string serverId = g_pMasterServerManager->m_sOwnServerId;
- const std::string hostname = Cvar_ns_masterserver_hostname->GetString();
- const std::string modinfo = g_pMasterServerManager->m_sOwnModInfoJson;
-
- updateServerFuture = std::async(
- std::launch::async,
- [threadedPresence, serverId, hostname, modinfo]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- // Lambda to quickly cleanup resources and return a value.
- auto ReturnCleanup = [curl](MasterServerReportPresenceResult result, const char* id = "", const char* serverAuthToken = "")
- {
- curl_easy_cleanup(curl);
-
- MasterServerPresenceReporter::ReportPresenceResultData data;
- data.result = result;
-
- if (id != nullptr)
- {
- data.id = id;
- }
-
- if (serverAuthToken != nullptr)
- {
- data.serverAuthToken = serverAuthToken;
- }
-
- return data;
- };
-
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
-
- // send all registration info so we have all necessary info to reregister our server if masterserver goes down,
- // without a restart this isn't threadsafe :terror:
- {
- char* nameEscaped = curl_easy_escape(curl, threadedPresence.m_sServerName.c_str(), 0);
- char* descEscaped = curl_easy_escape(curl, threadedPresence.m_sServerDesc.c_str(), 0);
- char* mapEscaped = curl_easy_escape(curl, threadedPresence.m_MapName, 0);
- char* playlistEscaped = curl_easy_escape(curl, threadedPresence.m_PlaylistName, 0);
- char* passwordEscaped = curl_easy_escape(curl, threadedPresence.m_Password, 0);
-
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/"
- "update_values?id={}&port={}&authPort=udp&name={}&description={}&map={}&playlist={}&playerCount={}&"
- "maxPlayers={}&password={}",
- hostname.c_str(),
- serverId.c_str(),
- threadedPresence.m_iPort,
- nameEscaped,
- descEscaped,
- mapEscaped,
- playlistEscaped,
- threadedPresence.m_iPlayerCount,
- threadedPresence.m_iMaxPlayers,
- passwordEscaped)
- .c_str());
-
- curl_free(nameEscaped);
- curl_free(descEscaped);
- curl_free(mapEscaped);
- curl_free(playlistEscaped);
- curl_free(passwordEscaped);
- }
-
- curl_mime* mime = curl_mime_init(curl);
- curl_mimepart* part = curl_mime_addpart(mime);
-
- curl_mime_data(part, modinfo.c_str(), modinfo.size());
- curl_mime_name(part, "modinfo");
- curl_mime_filename(part, "modinfo.json");
- curl_mime_type(part, "application/json");
-
- curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- {
- rapidjson_document serverAddedJson;
- serverAddedJson.Parse(readBuffer.c_str());
-
- const char* updatedId = nullptr;
- const char* updatedAuthToken = nullptr;
-
- if (!serverAddedJson.HasParseError() && serverAddedJson.IsObject())
- {
- if (serverAddedJson.HasMember("id") && serverAddedJson["id"].IsString())
- {
- updatedId = serverAddedJson["id"].GetString();
- }
-
- if (serverAddedJson.HasMember("serverAuthToken") && serverAddedJson["serverAuthToken"].IsString())
- {
- updatedAuthToken = serverAddedJson["serverAuthToken"].GetString();
- }
- }
-
- return ReturnCleanup(MasterServerReportPresenceResult::Success, updatedId, updatedAuthToken);
- }
- else
- {
- spdlog::warn("Heartbeat failed with error {}", curl_easy_strerror(result));
- return ReturnCleanup(MasterServerReportPresenceResult::Failed);
- }
- });
-}
diff --git a/NorthstarDLL/masterserver/masterserver.h b/NorthstarDLL/masterserver/masterserver.h
deleted file mode 100644
index 570db619..00000000
--- a/NorthstarDLL/masterserver/masterserver.h
+++ /dev/null
@@ -1,199 +0,0 @@
-#pragma once
-
-#include "core/convar/convar.h"
-#include "server/serverpresence.h"
-#include <winsock2.h>
-#include <string>
-#include <cstring>
-#include <future>
-#include <unordered_set>
-
-extern ConVar* Cvar_ns_masterserver_hostname;
-extern ConVar* Cvar_ns_curl_log_enable;
-
-struct RemoteModInfo
-{
-public:
- std::string Name;
- std::string Version;
-};
-
-class RemoteServerInfo
-{
-public:
- char id[33]; // 32 bytes + nullterminator
-
- // server info
- char name[64];
- std::string description;
- char map[32];
- char playlist[16];
- char region[32];
- std::vector<RemoteModInfo> requiredMods;
-
- int playerCount;
- int maxPlayers;
-
- // connection stuff
- bool requiresPassword;
-
-public:
- RemoteServerInfo(
- const char* newId,
- const char* newName,
- const char* newDescription,
- const char* newMap,
- const char* newPlaylist,
- const char* newRegion,
- int newPlayerCount,
- int newMaxPlayers,
- bool newRequiresPassword);
-};
-
-struct RemoteServerConnectionInfo
-{
-public:
- char authToken[32];
-
- in_addr ip;
- unsigned short port;
-};
-
-struct MainMenuPromoData
-{
-public:
- std::string newInfoTitle1;
- std::string newInfoTitle2;
- std::string newInfoTitle3;
-
- std::string largeButtonTitle;
- std::string largeButtonText;
- std::string largeButtonUrl;
- int largeButtonImageIndex;
-
- std::string smallButton1Title;
- std::string smallButton1Url;
- int smallButton1ImageIndex;
-
- std::string smallButton2Title;
- std::string smallButton2Url;
- int smallButton2ImageIndex;
-};
-
-class MasterServerManager
-{
-private:
- bool m_bRequestingServerList = false;
- bool m_bAuthenticatingWithGameServer = false;
-
-public:
- char m_sOwnServerId[33];
- char m_sOwnServerAuthToken[33];
- char m_sOwnClientAuthToken[33];
-
- std::string m_sOwnModInfoJson;
-
- bool m_bOriginAuthWithMasterServerDone = false;
- bool m_bOriginAuthWithMasterServerInProgress = false;
-
- bool m_bOriginAuthWithMasterServerSuccessful = false;
- std::string m_sOriginAuthWithMasterServerErrorCode = "";
- std::string m_sOriginAuthWithMasterServerErrorMessage = "";
-
- bool m_bSavingPersistentData = false;
-
- bool m_bScriptRequestingServerList = false;
- bool m_bSuccessfullyConnected = true;
-
- bool m_bNewgameAfterSelfAuth = false;
- bool m_bScriptAuthenticatingWithGameServer = false;
- bool m_bSuccessfullyAuthenticatedWithGameServer = false;
- std::string m_sAuthFailureReason {};
-
- bool m_bHasPendingConnectionInfo = false;
- RemoteServerConnectionInfo m_pendingConnectionInfo;
-
- std::vector<RemoteServerInfo> m_vRemoteServers;
-
- bool m_bHasMainMenuPromoData = false;
- MainMenuPromoData m_sMainMenuPromoData;
-
- std::optional<RemoteServerInfo> m_currentServer;
- std::string m_sCurrentServerPassword;
-
- std::unordered_set<std::string> m_handledServerConnections;
-
-public:
- MasterServerManager();
-
- void ClearServerList();
- void RequestServerList();
- void RequestMainMenuPromos();
- void AuthenticateOriginWithMasterServer(const char* uid, const char* originToken);
- void AuthenticateWithOwnServer(const char* uid, const char* playerToken);
- void AuthenticateWithServer(const char* uid, const char* playerToken, RemoteServerInfo server, const char* password);
- void WritePlayerPersistentData(const char* playerId, const char* pdata, size_t pdataSize);
- void ProcessConnectionlessPacketSigreq1(std::string req);
-};
-
-extern MasterServerManager* g_pMasterServerManager;
-extern ConVar* Cvar_ns_masterserver_hostname;
-
-/** Result returned in the std::future of a MasterServerPresenceReporter::ReportPresence() call. */
-enum class MasterServerReportPresenceResult
-{
- // Adding this server to the MS was successful.
- Success,
- // We failed to add this server to the MS and should retry.
- Failed,
- // We failed to add this server to the MS and shouldn't retry.
- FailedNoRetry,
- // We failed to even reach the MS.
- FailedNoConnect,
- // We failed to add the server because an existing server with the same ip:port exists.
- FailedDuplicateServer,
-};
-
-class MasterServerPresenceReporter : public ServerPresenceReporter
-{
-public:
- /** Full data returned in the std::future of a MasterServerPresenceReporter::ReportPresence() call. */
- struct ReportPresenceResultData
- {
- MasterServerReportPresenceResult result;
-
- std::optional<std::string> id;
- std::optional<std::string> serverAuthToken;
- };
-
- const int MAX_REGISTRATION_ATTEMPTS = 5;
-
- // Called to initialise the master server presence reporter's state.
- void CreatePresence(const ServerPresence* pServerPresence) override;
-
- // Run on an internal to either add the server to the MS or update it.
- void ReportPresence(const ServerPresence* pServerPresence) override;
-
- // Called when we need to remove the server from the master server.
- void DestroyPresence(const ServerPresence* pServerPresence) override;
-
- // Called every frame.
- void RunFrame(double flCurrentTime, const ServerPresence* pServerPresence) override;
-
-protected:
- // Contains the async logic to add the server to the MS.
- void InternalAddServer(const ServerPresence* pServerPresence);
-
- // Contains the async logic to update the server on the MS.
- void InternalUpdateServer(const ServerPresence* pServerPresence);
-
- // The future used for InternalAddServer() calls.
- std::future<ReportPresenceResultData> addServerFuture;
-
- // The future used for InternalAddServer() calls.
- std::future<ReportPresenceResultData> updateServerFuture;
-
- int m_nNumRegistrationAttempts;
-
- double m_fNextAddServerAttemptTime;
-};
diff --git a/NorthstarDLL/mods/autodownload/moddownloader.cpp b/NorthstarDLL/mods/autodownload/moddownloader.cpp
deleted file mode 100644
index 165399e3..00000000
--- a/NorthstarDLL/mods/autodownload/moddownloader.cpp
+++ /dev/null
@@ -1,638 +0,0 @@
-#include "moddownloader.h"
-#include <rapidjson/fwd.h>
-#include <mz_strm_mem.h>
-#include <mz.h>
-#include <mz_strm.h>
-#include <mz_zip.h>
-#include <mz_compat.h>
-#include <thread>
-#include <future>
-#include <bcrypt.h>
-#include <winternl.h>
-#include <fstream>
-
-ModDownloader* g_pModDownloader;
-
-ModDownloader::ModDownloader()
-{
- spdlog::info("Mod downloader initialized");
-
- // Initialise mods list URI
- char* clachar = strstr(GetCommandLineA(), CUSTOM_MODS_URL_FLAG);
- if (clachar)
- {
- std::string url;
- int iFlagStringLength = strlen(CUSTOM_MODS_URL_FLAG);
- std::string cla = std::string(clachar);
- if (strncmp(cla.substr(iFlagStringLength, 1).c_str(), "\"", 1))
- {
- int space = cla.find(" ");
- url = cla.substr(iFlagStringLength, space - iFlagStringLength);
- }
- else
- {
- std::string quote = "\"";
- int quote1 = cla.find(quote);
- int quote2 = (cla.substr(quote1 + 1)).find(quote);
- url = cla.substr(quote1 + 1, quote2);
- }
- spdlog::info("Found custom verified mods URL in command line argument: {}", url);
- modsListUrl = strdup(url.c_str());
- }
- else
- {
- spdlog::info("Custom verified mods URL not found in command line arguments, using default URL.");
- modsListUrl = strdup(DEFAULT_MODS_LIST_URL);
- }
-}
-
-size_t WriteToString(void* ptr, size_t size, size_t count, void* stream)
-{
- ((std::string*)stream)->append((char*)ptr, 0, size * count);
- return size * count;
-}
-
-void ModDownloader::FetchModsListFromAPI()
-{
- std::thread requestThread(
- [this]()
- {
- CURLcode result;
- CURL* easyhandle;
- rapidjson::Document verifiedModsJson;
- std::string url = modsListUrl;
-
- curl_global_init(CURL_GLOBAL_ALL);
- easyhandle = curl_easy_init();
- std::string readBuffer;
-
- // Fetching mods list from GitHub repository
- curl_easy_setopt(easyhandle, CURLOPT_CUSTOMREQUEST, "GET");
- curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, 30L);
- curl_easy_setopt(easyhandle, CURLOPT_URL, url.c_str());
- curl_easy_setopt(easyhandle, CURLOPT_FAILONERROR, 1L);
- curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &readBuffer);
- curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteToString);
- result = curl_easy_perform(easyhandle);
-
- if (result == CURLcode::CURLE_OK)
- {
- spdlog::info("Mods list successfully fetched.");
- }
- else
- {
- spdlog::error("Fetching mods list failed: {}", curl_easy_strerror(result));
- goto REQUEST_END_CLEANUP;
- }
-
- // Load mods list into local state
- spdlog::info("Loading mods configuration...");
- verifiedModsJson.Parse(readBuffer);
- for (auto i = verifiedModsJson.MemberBegin(); i != verifiedModsJson.MemberEnd(); ++i)
- {
- std::string name = i->name.GetString();
- std::string dependency = i->value["DependencyPrefix"].GetString();
-
- std::unordered_map<std::string, VerifiedModVersion> modVersions;
- rapidjson::Value& versions = i->value["Versions"];
- assert(versions.IsArray());
- for (auto& attribute : versions.GetArray())
- {
- assert(attribute.IsObject());
- std::string version = attribute["Version"].GetString();
- std::string checksum = attribute["Checksum"].GetString();
- modVersions.insert({version, {.checksum = checksum}});
- }
-
- VerifiedModDetails modConfig = {.dependencyPrefix = dependency, .versions = modVersions};
- verifiedMods.insert({name, modConfig});
- spdlog::info("==> Loaded configuration for mod \"" + name + "\"");
- }
-
- spdlog::info("Done loading verified mods list.");
-
- REQUEST_END_CLEANUP:
- curl_easy_cleanup(easyhandle);
- });
- requestThread.detach();
-}
-
-size_t WriteData(void* ptr, size_t size, size_t nmemb, FILE* stream)
-{
- size_t written;
- written = fwrite(ptr, size, nmemb, stream);
- return written;
-}
-
-int ModDownloader::ModFetchingProgressCallback(
- void* ptr, curl_off_t totalDownloadSize, curl_off_t finishedDownloadSize, curl_off_t totalToUpload, curl_off_t nowUploaded)
-{
- if (totalDownloadSize != 0 && finishedDownloadSize != 0)
- {
- ModDownloader* instance = static_cast<ModDownloader*>(ptr);
- auto currentDownloadProgress = roundf(static_cast<float>(finishedDownloadSize) / totalDownloadSize * 100);
- instance->modState.progress = finishedDownloadSize;
- instance->modState.total = totalDownloadSize;
- instance->modState.ratio = currentDownloadProgress;
- }
-
- return 0;
-}
-
-std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view modName, std::string_view modVersion)
-{
- // Retrieve mod prefix from local mods list, or use mod name as mod prefix if bypass flag is set
- std::string modPrefix = strstr(GetCommandLineA(), VERIFICATION_FLAG) ? modName.data() : verifiedMods[modName.data()].dependencyPrefix;
- // Build archive distant URI
- std::string archiveName = std::format("{}-{}.zip", modPrefix, modVersion.data());
- std::string url = STORE_URL + archiveName;
- spdlog::info(std::format("Fetching mod archive from {}", url));
-
- // Download destination
- std::filesystem::path downloadPath = std::filesystem::temp_directory_path() / archiveName;
- spdlog::info(std::format("Downloading archive to {}", downloadPath.generic_string()));
-
- // Update state
- modState.state = DOWNLOADING;
-
- // Download the actual archive
- bool failed = false;
- FILE* fp = fopen(downloadPath.generic_string().c_str(), "wb");
- CURLcode result;
- CURL* easyhandle;
- easyhandle = curl_easy_init();
-
- curl_easy_setopt(easyhandle, CURLOPT_URL, url.data());
- curl_easy_setopt(easyhandle, CURLOPT_FAILONERROR, 1L);
- curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, fp);
- curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteData);
- curl_easy_setopt(easyhandle, CURLOPT_NOPROGRESS, 0L);
- curl_easy_setopt(easyhandle, CURLOPT_XFERINFOFUNCTION, ModDownloader::ModFetchingProgressCallback);
- curl_easy_setopt(easyhandle, CURLOPT_XFERINFODATA, this);
- result = curl_easy_perform(easyhandle);
-
- if (result == CURLcode::CURLE_OK)
- {
- spdlog::info("Mod archive successfully fetched.");
- goto REQUEST_END_CLEANUP;
- }
- else
- {
- spdlog::error("Fetching mod archive failed: {}", curl_easy_strerror(result));
- failed = true;
- goto REQUEST_END_CLEANUP;
- }
-
-REQUEST_END_CLEANUP:
- curl_easy_cleanup(easyhandle);
- fclose(fp);
- return failed ? std::optional<fs::path>() : std::optional<fs::path>(downloadPath);
-}
-
-bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecksum)
-{
- if (strstr(GetCommandLineA(), VERIFICATION_FLAG))
- {
- spdlog::info("Bypassing mod verification due to flag set up.");
- return true;
- }
-
- // Update state
- modState.state = CHECKSUMING;
-
- NTSTATUS status;
- BCRYPT_ALG_HANDLE algorithmHandle = NULL;
- BCRYPT_HASH_HANDLE hashHandle = NULL;
- std::vector<uint8_t> hash;
- DWORD hashLength = 0;
- DWORD resultLength = 0;
- std::stringstream ss;
-
- constexpr size_t bufferSize {1 << 12};
- std::vector<char> buffer(bufferSize, '\0');
- std::ifstream fp(modPath.generic_string(), std::ios::binary);
-
- // Open an algorithm handle
- // This sample passes BCRYPT_HASH_REUSABLE_FLAG with BCryptAlgorithmProvider(...) to load a provider which supports reusable hash
- status = BCryptOpenAlgorithmProvider(
- &algorithmHandle, // Alg Handle pointer
- BCRYPT_SHA256_ALGORITHM, // Cryptographic Algorithm name (null terminated unicode string)
- NULL, // Provider name; if null, the default provider is loaded
- BCRYPT_HASH_REUSABLE_FLAG); // Flags; Loads a provider which supports reusable hash
- if (!NT_SUCCESS(status))
- {
- modState.state = MOD_CORRUPTED;
- goto cleanup;
- }
-
- // Obtain the length of the hash
- status = BCryptGetProperty(
- algorithmHandle, // Handle to a CNG object
- BCRYPT_HASH_LENGTH, // Property name (null terminated unicode string)
- (PBYTE)&hashLength, // Address of the output buffer which recieves the property value
- sizeof(hashLength), // Size of the buffer in bytes
- &resultLength, // Number of bytes that were copied into the buffer
- 0); // Flags
- if (!NT_SUCCESS(status))
- {
- // goto cleanup;
- modState.state = MOD_CORRUPTED;
- return false;
- }
-
- // Create a hash handle
- status = BCryptCreateHash(
- algorithmHandle, // Handle to an algorithm provider
- &hashHandle, // A pointer to a hash handle - can be a hash or hmac object
- NULL, // Pointer to the buffer that recieves the hash/hmac object
- 0, // Size of the buffer in bytes
- NULL, // A pointer to a key to use for the hash or MAC
- 0, // Size of the key in bytes
- 0); // Flags
- if (!NT_SUCCESS(status))
- {
- modState.state = MOD_CORRUPTED;
- goto cleanup;
- }
-
- // Hash archive content
- if (!fp.is_open())
- {
- spdlog::error("Unable to open archive.");
- modState.state = FAILED_READING_ARCHIVE;
- return false;
- }
- fp.seekg(0, fp.beg);
- while (fp.good())
- {
- fp.read(buffer.data(), bufferSize);
- std::streamsize bytesRead = fp.gcount();
- if (bytesRead > 0)
- {
- status = BCryptHashData(hashHandle, (PBYTE)buffer.data(), bytesRead, 0);
- if (!NT_SUCCESS(status))
- {
- modState.state = MOD_CORRUPTED;
- goto cleanup;
- }
- }
- }
-
- hash = std::vector<uint8_t>(hashLength);
-
- // Obtain the hash of the message(s) into the hash buffer
- status = BCryptFinishHash(
- hashHandle, // Handle to the hash or MAC object
- hash.data(), // A pointer to a buffer that receives the hash or MAC value
- hashLength, // Size of the buffer in bytes
- 0); // Flags
- if (!NT_SUCCESS(status))
- {
- modState.state = MOD_CORRUPTED;
- goto cleanup;
- }
-
- // Convert hash to string using bytes raw values
- ss << std::hex << std::setfill('0');
- for (int i = 0; i < hashLength; i++)
- {
- ss << std::hex << std::setw(2) << static_cast<int>(hash.data()[i]);
- }
-
- spdlog::info("Expected checksum: {}", expectedChecksum.data());
- spdlog::info("Computed checksum: {}", ss.str());
- return expectedChecksum.compare(ss.str()) == 0;
-
-cleanup:
- if (NULL != hashHandle)
- {
- BCryptDestroyHash(hashHandle); // Handle to hash/MAC object which needs to be destroyed
- }
-
- if (NULL != algorithmHandle)
- {
- BCryptCloseAlgorithmProvider(
- algorithmHandle, // Handle to the algorithm provider which needs to be closed
- 0); // Flags
- }
-
- return false;
-}
-
-bool ModDownloader::IsModAuthorized(std::string_view modName, std::string_view modVersion)
-{
- if (strstr(GetCommandLineA(), VERIFICATION_FLAG))
- {
- spdlog::info("Bypassing mod verification due to flag set up.");
- return true;
- }
-
- if (!verifiedMods.contains(modName.data()))
- {
- return false;
- }
-
- std::unordered_map<std::string, VerifiedModVersion> versions = verifiedMods[modName.data()].versions;
- return versions.count(modVersion.data()) != 0;
-}
-
-int GetModArchiveSize(unzFile file, unz_global_info64 info)
-{
- int totalSize = 0;
-
- for (int i = 0; i < info.number_entry; i++)
- {
- char zipFilename[256];
- unz_file_info64 fileInfo;
- unzGetCurrentFileInfo64(file, &fileInfo, zipFilename, sizeof(zipFilename), NULL, 0, NULL, 0);
-
- totalSize += fileInfo.uncompressed_size;
-
- if ((i + 1) < info.number_entry)
- {
- unzGoToNextFile(file);
- }
- }
-
- // Reset file pointer for archive extraction
- unzGoToFirstFile(file);
-
- return totalSize;
-}
-
-void ModDownloader::ExtractMod(fs::path modPath)
-{
- unzFile file;
- std::string name;
- fs::path modDirectory;
-
- file = unzOpen(modPath.generic_string().c_str());
- if (file == NULL)
- {
- spdlog::error("Cannot open archive located at {}.", modPath.generic_string());
- modState.state = FAILED_READING_ARCHIVE;
- goto EXTRACTION_CLEANUP;
- }
-
- unz_global_info64 gi;
- int status;
- status = unzGetGlobalInfo64(file, &gi);
- if (status != UNZ_OK)
- {
- spdlog::error("Failed getting information from archive (error code: {})", status);
- modState.state = FAILED_READING_ARCHIVE;
- goto EXTRACTION_CLEANUP;
- }
-
- // Update state
- modState.state = EXTRACTING;
- modState.total = GetModArchiveSize(file, gi);
- modState.progress = 0;
-
- // Mod directory name (removing the ".zip" fom the archive name)
- name = modPath.filename().string();
- name = name.substr(0, name.length() - 4);
- modDirectory = GetRemoteModFolderPath() / name;
-
- for (int i = 0; i < gi.number_entry; i++)
- {
- char zipFilename[256];
- unz_file_info64 fileInfo;
- status = unzGetCurrentFileInfo64(file, &fileInfo, zipFilename, sizeof(zipFilename), NULL, 0, NULL, 0);
-
- // Extract file
- {
- std::error_code ec;
- fs::path fileDestination = modDirectory / zipFilename;
- spdlog::info("=> {}", fileDestination.generic_string());
-
- // Create parent directory if needed
- if (!std::filesystem::exists(fileDestination.parent_path()))
- {
- spdlog::info("Parent directory does not exist, creating it.", fileDestination.generic_string());
- if (!std::filesystem::create_directories(fileDestination.parent_path(), ec) && ec.value() != 0)
- {
- spdlog::error("Parent directory ({}) creation failed.", fileDestination.parent_path().generic_string());
- modState.state = FAILED_WRITING_TO_DISK;
- goto EXTRACTION_CLEANUP;
- }
- }
-
- // If current file is a directory, create directory...
- if (fileDestination.generic_string().back() == '/')
- {
- // Create directory
- if (!std::filesystem::create_directory(fileDestination, ec) && ec.value() != 0)
- {
- spdlog::error("Directory creation failed: {}", ec.message());
- modState.state = FAILED_WRITING_TO_DISK;
- goto EXTRACTION_CLEANUP;
- }
- }
- // ...else create file
- else
- {
- // Ensure file is in zip archive
- if (unzLocateFile(file, zipFilename, 0) != UNZ_OK)
- {
- spdlog::error("File \"{}\" was not found in archive.", zipFilename);
- modState.state = FAILED_READING_ARCHIVE;
- goto EXTRACTION_CLEANUP;
- }
-
- // Create file
- const int bufferSize = 8192;
- void* buffer;
- int err = UNZ_OK;
- FILE* fout = NULL;
-
- // Open zip file to prepare its extraction
- status = unzOpenCurrentFile(file);
- if (status != UNZ_OK)
- {
- spdlog::error("Could not open file {} from archive.", zipFilename);
- modState.state = FAILED_READING_ARCHIVE;
- goto EXTRACTION_CLEANUP;
- }
-
- // Create destination file
- fout = fopen(fileDestination.generic_string().c_str(), "wb");
- if (fout == NULL)
- {
- spdlog::error("Failed creating destination file.");
- modState.state = FAILED_WRITING_TO_DISK;
- goto EXTRACTION_CLEANUP;
- }
-
- // Allocate memory for buffer
- buffer = (void*)malloc(bufferSize);
- if (buffer == NULL)
- {
- spdlog::error("Error while allocating memory.");
- modState.state = FAILED_WRITING_TO_DISK;
- goto EXTRACTION_CLEANUP;
- }
-
- // Extract file to destination
- do
- {
- err = unzReadCurrentFile(file, buffer, bufferSize);
- if (err < 0)
- {
- spdlog::error("error {} with zipfile in unzReadCurrentFile", err);
- break;
- }
- if (err > 0)
- {
- if (fwrite(buffer, (unsigned)err, 1, fout) != 1)
- {
- spdlog::error("error in writing extracted file\n");
- err = UNZ_ERRNO;
- break;
- }
- }
-
- // Update extraction stats
- modState.progress += bufferSize;
- modState.ratio = roundf(static_cast<float>(modState.progress) / modState.total * 100);
- } while (err > 0);
-
- if (err != UNZ_OK)
- {
- spdlog::error("An error occurred during file extraction (code: {})", err);
- modState.state = FAILED_WRITING_TO_DISK;
- goto EXTRACTION_CLEANUP;
- }
- err = unzCloseCurrentFile(file);
- if (err != UNZ_OK)
- {
- spdlog::error("error {} with zipfile in unzCloseCurrentFile", err);
- }
-
- // Cleanup
- if (fout)
- fclose(fout);
- }
- }
-
- // Go to next file
- if ((i + 1) < gi.number_entry)
- {
- status = unzGoToNextFile(file);
- if (status != UNZ_OK)
- {
- spdlog::error("Error while browsing archive files (error code: {}).", status);
- goto EXTRACTION_CLEANUP;
- }
- }
- }
-
-EXTRACTION_CLEANUP:
- if (unzClose(file) != MZ_OK)
- {
- spdlog::error("Failed closing mod archive after extraction.");
- }
-}
-
-void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
-{
- // Check if mod can be auto-downloaded
- if (!IsModAuthorized(std::string_view(modName), std::string_view(modVersion)))
- {
- spdlog::warn("Tried to download a mod that is not verified, aborting.");
- return;
- }
-
- std::thread requestThread(
- [this, modName, modVersion]()
- {
- fs::path archiveLocation;
-
- // Download mod archive
- std::string expectedHash = verifiedMods[modName].versions[modVersion].checksum;
- std::optional<fs::path> fetchingResult = FetchModFromDistantStore(std::string_view(modName), std::string_view(modVersion));
- if (!fetchingResult.has_value())
- {
- spdlog::error("Something went wrong while fetching archive, aborting.");
- modState.state = MOD_FETCHING_FAILED;
- goto REQUEST_END_CLEANUP;
- }
- archiveLocation = fetchingResult.value();
- if (!IsModLegit(archiveLocation, std::string_view(expectedHash)))
- {
- spdlog::warn("Archive hash does not match expected checksum, aborting.");
- modState.state = MOD_CORRUPTED;
- goto REQUEST_END_CLEANUP;
- }
-
- // Extract downloaded mod archive
- ExtractMod(archiveLocation);
-
- REQUEST_END_CLEANUP:
- try
- {
- remove(archiveLocation);
- }
- catch (const std::exception& a)
- {
- spdlog::error("Error while removing downloaded archive: {}", a.what());
- }
-
- modState.state = DONE;
- spdlog::info("Done downloading {}.", modName);
- });
-
- requestThread.detach();
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ModDownloader, (ConCommand), (CModule module))
-{
- g_pModDownloader = new ModDownloader();
- g_pModDownloader->FetchModsListFromAPI();
-}
-
-ADD_SQFUNC(
- "bool", NSIsModDownloadable, "string name, string version", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- g_pSquirrel<context>->newarray(sqvm, 0);
-
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
- const SQChar* modVersion = g_pSquirrel<context>->getstring(sqvm, 2);
-
- bool result = g_pModDownloader->IsModAuthorized(modName, modVersion);
- g_pSquirrel<context>->pushbool(sqvm, result);
-
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("void", NSDownloadMod, "string name, string version", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
- const SQChar* modVersion = g_pSquirrel<context>->getstring(sqvm, 2);
- g_pModDownloader->DownloadMod(modName, modVersion);
-
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("ModInstallState", NSGetModInstallState, "", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- g_pSquirrel<context>->pushnewstructinstance(sqvm, 4);
-
- // state
- g_pSquirrel<context>->pushinteger(sqvm, g_pModDownloader->modState.state);
- g_pSquirrel<context>->sealstructslot(sqvm, 0);
-
- // progress
- g_pSquirrel<context>->pushinteger(sqvm, g_pModDownloader->modState.progress);
- g_pSquirrel<context>->sealstructslot(sqvm, 1);
-
- // total
- g_pSquirrel<context>->pushinteger(sqvm, g_pModDownloader->modState.total);
- g_pSquirrel<context>->sealstructslot(sqvm, 2);
-
- // ratio
- g_pSquirrel<context>->pushfloat(sqvm, g_pModDownloader->modState.ratio);
- g_pSquirrel<context>->sealstructslot(sqvm, 3);
-
- return SQRESULT_NOTNULL;
-}
diff --git a/NorthstarDLL/mods/autodownload/moddownloader.h b/NorthstarDLL/mods/autodownload/moddownloader.h
deleted file mode 100644
index 5302c21e..00000000
--- a/NorthstarDLL/mods/autodownload/moddownloader.h
+++ /dev/null
@@ -1,151 +0,0 @@
-class ModDownloader
-{
-private:
- const char* VERIFICATION_FLAG = "-disablemodverification";
- const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl=";
- const char* STORE_URL = "https://gcdn.thunderstore.io/live/repository/packages/";
- const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/master/verified-mods.json";
- char* modsListUrl;
-
- struct VerifiedModVersion
- {
- std::string checksum;
- };
- struct VerifiedModDetails
- {
- std::string dependencyPrefix;
- std::unordered_map<std::string, VerifiedModVersion> versions = {};
- };
- std::unordered_map<std::string, VerifiedModDetails> verifiedMods = {};
-
- /**
- * Mod archive download callback.
- *
- * This function is called by curl as it's downloading the mod archive; this
- * will retrieve the current `ModDownloader` instance and update its `modState`
- * member accordingly.
- */
- static int ModFetchingProgressCallback(
- void* ptr, curl_off_t totalDownloadSize, curl_off_t finishedDownloadSize, curl_off_t totalToUpload, curl_off_t nowUploaded);
-
- /**
- * Downloads a mod archive from distant store.
- *
- * This rebuilds the URI of the mod archive using both a predefined store URI
- * and the mod dependency string from the `verifiedMods` variable, or using
- * input mod name as mod dependency string if bypass flag is set up; fetched
- * archive is then stored in a temporary location.
- *
- * If something went wrong during archive download, this will return an empty
- * optional object.
- *
- * @param modName name of the mod to be downloaded
- * @param modVersion version of the mod to be downloaded
- * @returns location of the downloaded archive
- */
- std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, std::string_view modVersion);
-
- /**
- * Tells if a mod archive has not been corrupted.
- *
- * The mod validation procedure includes computing the SHA256 hash of the final
- * archive, which is stored in the verified mods list. This hash is used by this
- * very method to ensure the archive downloaded from the Internet is the exact
- * same that has been manually verified.
- *
- * @param modPath path of the archive to check
- * @param expectedChecksum checksum the archive should have
- * @returns whether archive is legit
- */
- bool IsModLegit(fs::path modPath, std::string_view expectedChecksum);
-
- /**
- * Extracts a mod archive to the game folder.
- *
- * This extracts a downloaded mod archive from its original location to the
- * current game profile, in the remote mods folder.
- *
- * @param modPath location of the downloaded archive
- * @returns nothing
- */
- void ExtractMod(fs::path modPath);
-
-public:
- ModDownloader();
-
- /**
- * Retrieves the verified mods list from the central authority.
- *
- * The Northstar auto-downloading feature does NOT allow automatically installing
- * all mods for various (notably security) reasons; mods that are candidate to
- * auto-downloading are rather listed on a GitHub repository
- * (https://raw.githubusercontent.com/R2Northstar/VerifiedMods/master/verified-mods.json),
- * which this method gets via a HTTP call to load into local state.
- *
- * If list fetching fails, local mods list will be initialized as empty, thus
- * preventing any mod from being auto-downloaded.
- *
- * @returns nothing
- */
- void FetchModsListFromAPI();
-
- /**
- * Checks whether a mod is verified.
- *
- * A mod is deemed verified/authorized through a manual validation process that is
- * described here: https://github.com/R2Northstar/VerifiedMods; in practice, a mod
- * is considered authorized if their name AND exact version appear in the
- * `verifiedMods` variable.
- *
- * @param modName name of the mod to be checked
- * @param modVersion version of the mod to be checked, must follow semantic versioning
- * @returns whether the mod is authorized and can be auto-downloaded
- */
- bool IsModAuthorized(std::string_view modName, std::string_view modVersion);
-
- /**
- * Downloads a given mod from Thunderstore API to local game profile.
- *
- * @param modName name of the mod to be downloaded
- * @param modVersion version of the mod to be downloaded
- * @returns nothing
- **/
- void DownloadMod(std::string modName, std::string modVersion);
-
- enum ModInstallState
- {
- // Normal installation process
- DOWNLOADING,
- CHECKSUMING,
- EXTRACTING,
- DONE, // Everything went great, mod can be used in-game
-
- // Errors
- FAILED, // Generic error message, should be avoided as much as possible
- FAILED_READING_ARCHIVE,
- FAILED_WRITING_TO_DISK,
- MOD_FETCHING_FAILED,
- MOD_CORRUPTED, // Downloaded archive checksum does not match verified hash
- NO_DISK_SPACE_AVAILABLE,
- NOT_FOUND // Mod is not currently being auto-downloaded
- };
-
- struct MOD_STATE
- {
- ModInstallState state;
- int progress;
- int total;
- float ratio;
- } modState = {};
-
- /**
- * Cancels installation of the mod.
- *
- * Prevents installation of the mod currently being installed, no matter the install
- * progress (downloading, checksuming, extracting), and frees all resources currently
- * being used in this purpose.
- *
- * @returns nothing
- */
- void CancelDownload();
-};
diff --git a/NorthstarDLL/mods/compiled/kb_act.cpp b/NorthstarDLL/mods/compiled/kb_act.cpp
deleted file mode 100644
index 6117fd28..00000000
--- a/NorthstarDLL/mods/compiled/kb_act.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "mods/modmanager.h"
-#include "core/filesystem/filesystem.h"
-
-#include <fstream>
-
-const char* KB_ACT_PATH = "scripts\\kb_act.lst";
-
-// compiles the file kb_act.lst, that defines entries for keybindings in the options menu
-void ModManager::BuildKBActionsList()
-{
- spdlog::info("Building kb_act.lst");
-
- fs::create_directories(GetCompiledAssetsPath() / "scripts");
- std::ofstream soCompiledKeys(GetCompiledAssetsPath() / KB_ACT_PATH, std::ios::binary);
-
- // write vanilla file's content to compiled file
- soCompiledKeys << ReadVPKOriginalFile(KB_ACT_PATH);
-
- for (Mod& mod : m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- // write content of each modded file to compiled file
- std::ifstream siModKeys(mod.m_ModDirectory / "kb_act.lst");
-
- if (siModKeys.good())
- soCompiledKeys << siModKeys.rdbuf() << std::endl;
-
- siModKeys.close();
- }
-
- soCompiledKeys.close();
-
- // push to overrides
- ModOverrideFile overrideFile;
- overrideFile.m_pOwningMod = nullptr;
- overrideFile.m_Path = KB_ACT_PATH;
-
- if (m_ModFiles.find(KB_ACT_PATH) == m_ModFiles.end())
- m_ModFiles.insert(std::make_pair(KB_ACT_PATH, overrideFile));
- else
- m_ModFiles[KB_ACT_PATH] = overrideFile;
-}
diff --git a/NorthstarDLL/mods/compiled/modkeyvalues.cpp b/NorthstarDLL/mods/compiled/modkeyvalues.cpp
deleted file mode 100644
index e44a81d3..00000000
--- a/NorthstarDLL/mods/compiled/modkeyvalues.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-#include "mods/modmanager.h"
-#include "core/filesystem/filesystem.h"
-
-#include <fstream>
-
-AUTOHOOK_INIT()
-
-void ModManager::TryBuildKeyValues(const char* filename)
-{
- spdlog::info("Building KeyValues for file {}", filename);
-
- std::string normalisedPath = g_pModManager->NormaliseModFilePath(fs::path(filename));
- fs::path compiledPath = GetCompiledAssetsPath() / filename;
- fs::path compiledDir = compiledPath.parent_path();
- fs::create_directories(compiledDir);
-
- fs::path kvPath(filename);
- std::string ogFilePath = "mod_original_";
- ogFilePath += kvPath.filename().string();
-
- std::string newKvs = "// AUTOGENERATED: MOD PATCH KV\n";
-
- int patchNum = 0;
-
- // copy over patch kv files, and add #bases to new file, last mods' patches should be applied first
- // note: #include should be identical but it's actually just broken, thanks respawn
- for (int64_t i = m_LoadedMods.size() - 1; i > -1; i--)
- {
- if (!m_LoadedMods[i].m_bEnabled)
- continue;
-
- size_t fileHash = STR_HASH(normalisedPath);
- auto modKv = m_LoadedMods[i].KeyValues.find(fileHash);
- if (modKv != m_LoadedMods[i].KeyValues.end())
- {
- // should result in smth along the lines of #include "mod_patch_5_mp_weapon_car.txt"
-
- std::string patchFilePath = "mod_patch_";
- patchFilePath += std::to_string(patchNum++);
- patchFilePath += "_";
- patchFilePath += kvPath.filename().string();
-
- newKvs += "#base \"";
- newKvs += patchFilePath;
- newKvs += "\"\n";
-
- fs::remove(compiledDir / patchFilePath);
-
- fs::copy_file(m_LoadedMods[i].m_ModDirectory / "keyvalues" / filename, compiledDir / patchFilePath);
- }
- }
-
- // add original #base last, #bases don't override preexisting keys, including the ones we've just done
- newKvs += "#base \"";
- newKvs += ogFilePath;
- newKvs += "\"\n";
-
- // load original file, so we can parse out the name of the root obj (e.g. WeaponData for weapons)
- std::string originalFile = ReadVPKOriginalFile(filename);
-
- if (!originalFile.length())
- {
- spdlog::warn("Tried to patch kv {} but no base kv was found!", ogFilePath);
- return;
- }
-
- char rootName[64];
- memset(rootName, 0, sizeof(rootName));
-
- // iterate until we hit an ascii char that isn't in a # command or comment to get root obj name
- int i = 0;
- while (!(originalFile[i] >= 65 && originalFile[i] <= 122))
- {
- // if we hit a comment or # thing, iterate until end of line
- if (originalFile[i] == '/' || originalFile[i] == '#')
- while (originalFile[i] != '\n')
- i++;
-
- i++;
- }
-
- int j = 0;
- for (int j = 0; originalFile[i] >= 65 && originalFile[i] <= 122; j++)
- rootName[j] = originalFile[i++];
-
- // empty kv, all the other stuff gets #base'd
- newKvs += rootName;
- newKvs += "\n{\n}\n";
-
- std::ofstream originalFileWriteStream(compiledDir / ogFilePath, std::ios::binary);
- originalFileWriteStream << originalFile;
- originalFileWriteStream.close();
-
- std::ofstream writeStream(compiledPath, std::ios::binary);
- writeStream << newKvs;
- writeStream.close();
-
- ModOverrideFile overrideFile;
- overrideFile.m_pOwningMod = nullptr;
- overrideFile.m_Path = normalisedPath;
-
- if (m_ModFiles.find(normalisedPath) == m_ModFiles.end())
- m_ModFiles.insert(std::make_pair(normalisedPath, overrideFile));
- else
- m_ModFiles[normalisedPath] = overrideFile;
-}
diff --git a/NorthstarDLL/mods/compiled/modpdef.cpp b/NorthstarDLL/mods/compiled/modpdef.cpp
deleted file mode 100644
index d268a063..00000000
--- a/NorthstarDLL/mods/compiled/modpdef.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-#include "mods/modmanager.h"
-#include "core/filesystem/filesystem.h"
-
-#include <map>
-#include <sstream>
-#include <fstream>
-
-const fs::path MOD_PDEF_SUFFIX = "cfg/server/persistent_player_data_version_231.pdef";
-const char* VPK_PDEF_PATH = "cfg/server/persistent_player_data_version_231.pdef";
-
-void ModManager::BuildPdef()
-{
- spdlog::info("Building persistent_player_data_version_231.pdef...");
-
- fs::path MOD_PDEF_PATH = fs::path(GetCompiledAssetsPath() / MOD_PDEF_SUFFIX);
-
- fs::remove(MOD_PDEF_PATH);
- std::string pdef = ReadVPKOriginalFile(VPK_PDEF_PATH);
-
- for (Mod& mod : m_LoadedMods)
- {
- if (!mod.m_bEnabled || !mod.Pdiff.size())
- continue;
-
- // this code probably isn't going to be pretty lol
- // refer to shared/pjson.js for an actual okish parser of the pdiff format
- // but pretty much, $ENUM_ADD blocks define members added to preexisting enums
- // $PROP_START ends the custom stuff, and from there it's just normal props we append to the pdef
-
- std::map<std::string, std::vector<std::string>> enumAdds;
-
- // read pdiff
- bool inEnum = false;
- bool inProp = false;
- std::string currentEnum;
- std::string currentLine;
- std::istringstream pdiffStream(mod.Pdiff);
-
- while (std::getline(pdiffStream, currentLine))
- {
- if (inProp)
- {
- // just append to pdef here
- pdef += currentLine;
- pdef += '\n';
- continue;
- }
-
- // trim leading whitespace
- size_t start = currentLine.find_first_not_of(" \n\r\t\f\v");
- size_t end = currentLine.find("//");
- if (end == std::string::npos)
- end = currentLine.size() - 1; // last char
-
- if (!currentLine.size() || !currentLine.compare(start, 2, "//"))
- continue;
-
- if (inEnum)
- {
- if (!currentLine.compare(start, 9, "$ENUM_END"))
- inEnum = false;
- else
- enumAdds[currentEnum].push_back(currentLine); // only need to push_back current line, if there's syntax errors then game
- // pdef parser will handle them
- }
- else if (!currentLine.compare(start, 9, "$ENUM_ADD"))
- {
- inEnum = true;
- currentEnum = currentLine.substr(start + 10 /*$ENUM_ADD + 1*/, currentLine.size() - end - (start + 10));
- enumAdds.insert(std::make_pair(currentEnum, std::vector<std::string>()));
- }
- else if (!currentLine.compare(start, 11, "$PROP_START"))
- {
- inProp = true;
- pdef += "\n// $PROP_START ";
- pdef += mod.Name;
- pdef += "\n";
- }
- }
-
- // add new members to preexisting enums
- // note: this code could 100% be messed up if people put //$ENUM_START comments and the like
- // could make it protect against this, but honestly not worth atm
- for (auto enumAdd : enumAdds)
- {
- std::string addStr;
- for (std::string enumMember : enumAdd.second)
- {
- addStr += enumMember;
- addStr += '\n';
- }
-
- // start of enum we're adding to
- std::string startStr = "$ENUM_START ";
- startStr += enumAdd.first;
-
- // insert enum values into enum
- size_t insertIdx = pdef.find("$ENUM_END", pdef.find(startStr));
- pdef.reserve(addStr.size());
- pdef.insert(insertIdx, addStr);
- }
- }
-
- fs::create_directories(MOD_PDEF_PATH.parent_path());
-
- std::ofstream writeStream(MOD_PDEF_PATH, std::ios::binary);
- writeStream << pdef;
- writeStream.close();
-
- ModOverrideFile overrideFile;
- overrideFile.m_pOwningMod = nullptr;
- overrideFile.m_Path = VPK_PDEF_PATH;
-
- if (m_ModFiles.find(VPK_PDEF_PATH) == m_ModFiles.end())
- m_ModFiles.insert(std::make_pair(VPK_PDEF_PATH, overrideFile));
- else
- m_ModFiles[VPK_PDEF_PATH] = overrideFile;
-}
diff --git a/NorthstarDLL/mods/compiled/modscriptsrson.cpp b/NorthstarDLL/mods/compiled/modscriptsrson.cpp
deleted file mode 100644
index d130745f..00000000
--- a/NorthstarDLL/mods/compiled/modscriptsrson.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include "mods/modmanager.h"
-#include "core/filesystem/filesystem.h"
-#include "squirrel/squirrel.h"
-
-#include <fstream>
-
-const std::string MOD_SCRIPTS_RSON_SUFFIX = "scripts/vscripts/scripts.rson";
-const char* VPK_SCRIPTS_RSON_PATH = "scripts\\vscripts\\scripts.rson";
-
-void ModManager::BuildScriptsRson()
-{
- spdlog::info("Building custom scripts.rson");
- fs::path MOD_SCRIPTS_RSON_PATH = fs::path(GetCompiledAssetsPath() / MOD_SCRIPTS_RSON_SUFFIX);
- fs::remove(MOD_SCRIPTS_RSON_PATH);
-
- 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)
- {
- if (!mod.m_bEnabled)
- continue;
-
- // this isn't needed at all, just nice to have imo
- scriptsRson += "// MOD: ";
- scriptsRson += mod.Name;
- scriptsRson += ":\n\n";
-
- for (ModScript& script : mod.Scripts)
- {
- /* should create something with this format for each script
- When: "CONTEXT"
- Scripts:
- [
- _coolscript.gnut
- ]*/
-
- scriptsRson += "When: \"";
- scriptsRson += script.RunOn;
- scriptsRson += "\"\n";
-
- scriptsRson += "Scripts:\n[\n\t";
- scriptsRson += script.Path;
- scriptsRson += "\n]\n\n";
- }
- }
-
- fs::create_directories(MOD_SCRIPTS_RSON_PATH.parent_path());
-
- std::ofstream writeStream(MOD_SCRIPTS_RSON_PATH, std::ios::binary);
- writeStream << scriptsRson;
- writeStream.close();
-
- ModOverrideFile overrideFile;
- overrideFile.m_pOwningMod = nullptr;
- overrideFile.m_Path = VPK_SCRIPTS_RSON_PATH;
-
- if (m_ModFiles.find(VPK_SCRIPTS_RSON_PATH) == m_ModFiles.end())
- m_ModFiles.insert(std::make_pair(VPK_SCRIPTS_RSON_PATH, overrideFile));
- else
- m_ModFiles[VPK_SCRIPTS_RSON_PATH] = overrideFile;
-
- // todo: for preventing dupe scripts in scripts.rson, we could actually parse when conditions with the squirrel vm, just need a way to
- // get a result out of squirrelmanager.ExecuteCode this would probably be the best way to do this, imo
-}
diff --git a/NorthstarDLL/mods/modmanager.cpp b/NorthstarDLL/mods/modmanager.cpp
deleted file mode 100644
index 8a0eb71d..00000000
--- a/NorthstarDLL/mods/modmanager.cpp
+++ /dev/null
@@ -1,1150 +0,0 @@
-#include "modmanager.h"
-#include "core/convar/convar.h"
-#include "core/convar/concommand.h"
-#include "client/audio.h"
-#include "masterserver/masterserver.h"
-#include "core/filesystem/filesystem.h"
-#include "core/filesystem/rpakfilesystem.h"
-#include "config/profile.h"
-
-#include "rapidjson/error/en.h"
-#include "rapidjson/document.h"
-#include "rapidjson/ostreamwrapper.h"
-#include "rapidjson/prettywriter.h"
-#include <filesystem>
-#include <fstream>
-#include <string>
-#include <sstream>
-#include <vector>
-#include <regex>
-
-ModManager* g_pModManager;
-
-Mod::Mod(fs::path modDir, char* jsonBuf)
-{
- m_bWasReadSuccessfully = false;
-
- m_ModDirectory = modDir;
-
- rapidjson_document modJson;
- modJson.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(jsonBuf);
-
- spdlog::info("Loading mod file at path '{}'", modDir.string());
-
- // fail if parse error
- if (modJson.HasParseError())
- {
- spdlog::error(
- "Failed reading mod file {}: encountered parse error \"{}\" at offset {}",
- (modDir / "mod.json").string(),
- GetParseError_En(modJson.GetParseError()),
- modJson.GetErrorOffset());
- return;
- }
-
- // fail if it's not a json obj (could be an array, string, etc)
- if (!modJson.IsObject())
- {
- spdlog::error("Failed reading mod file {}: file is not a JSON object", (modDir / "mod.json").string());
- return;
- }
-
- // basic mod info
- // name is required
- if (!modJson.HasMember("Name"))
- {
- spdlog::error("Failed reading mod file {}: missing required member \"Name\"", (modDir / "mod.json").string());
- return;
- }
-
- Name = modJson["Name"].GetString();
- spdlog::info("Loading mod '{}'", Name);
-
- // Don't load blacklisted mods
- if (!strstr(GetCommandLineA(), "-nomodblacklist") && MODS_BLACKLIST.find(Name) != std::end(MODS_BLACKLIST))
- {
- spdlog::warn("Skipping blacklisted mod \"{}\"!", Name);
- return;
- }
-
- if (modJson.HasMember("Description"))
- Description = modJson["Description"].GetString();
- else
- Description = "";
-
- if (modJson.HasMember("Version"))
- Version = modJson["Version"].GetString();
- else
- {
- Version = "0.0.0";
- spdlog::warn("Mod file {} is missing a version, consider adding a version", (modDir / "mod.json").string());
- }
-
- if (modJson.HasMember("DownloadLink"))
- DownloadLink = modJson["DownloadLink"].GetString();
- else
- DownloadLink = "";
-
- if (modJson.HasMember("RequiredOnClient"))
- RequiredOnClient = modJson["RequiredOnClient"].GetBool();
- else
- RequiredOnClient = false;
-
- if (modJson.HasMember("LoadPriority"))
- LoadPriority = modJson["LoadPriority"].GetInt();
- else
- {
- spdlog::info("Mod file {} is missing a LoadPriority, consider adding one", (modDir / "mod.json").string());
- LoadPriority = 0;
- }
-
- // Parse all array fields
- ParseConVars(modJson);
- ParseConCommands(modJson);
- ParseScripts(modJson);
- ParseLocalization(modJson);
- ParseDependencies(modJson);
- ParsePluginDependencies(modJson);
- ParseInitScript(modJson);
-
- // A mod is remote if it's located in the remote mods folder
- m_bIsRemote = m_ModDirectory.generic_string().find(GetRemoteModFolderPath().generic_string()) != std::string::npos;
-
- m_bWasReadSuccessfully = true;
-}
-
-void Mod::ParseConVars(rapidjson_document& json)
-{
- if (!json.HasMember("ConVars"))
- return;
-
- if (!json["ConVars"].IsArray())
- {
- spdlog::warn("'ConVars' field is not an array, skipping...");
- return;
- }
-
- for (auto& convarObj : json["ConVars"].GetArray())
- {
- if (!convarObj.IsObject())
- {
- spdlog::warn("ConVar is not an object, skipping...");
- continue;
- }
- if (!convarObj.HasMember("Name"))
- {
- spdlog::warn("ConVar does not have a Name, skipping...");
- continue;
- }
- // from here on, the ConVar can be referenced by name in logs
- if (!convarObj.HasMember("DefaultValue"))
- {
- spdlog::warn("ConVar '{}' does not have a DefaultValue, skipping...", convarObj["Name"].GetString());
- continue;
- }
-
- // have to allocate this manually, otherwise convar registration will break
- // unfortunately this causes us to leak memory on reload, unsure of a way around this rn
- ModConVar* convar = new ModConVar;
- convar->Name = convarObj["Name"].GetString();
- convar->DefaultValue = convarObj["DefaultValue"].GetString();
-
- if (convarObj.HasMember("HelpString"))
- convar->HelpString = convarObj["HelpString"].GetString();
- else
- convar->HelpString = "";
-
- convar->Flags = FCVAR_NONE;
-
- if (convarObj.HasMember("Flags"))
- {
- // read raw integer flags
- if (convarObj["Flags"].IsInt())
- convar->Flags = convarObj["Flags"].GetInt();
- else if (convarObj["Flags"].IsString())
- {
- // parse cvar flags from string
- // example string: ARCHIVE_PLAYERPROFILE | GAMEDLL
- convar->Flags |= ParseConVarFlagsString(convar->Name, convarObj["Flags"].GetString());
- }
- }
-
- ConVars.push_back(convar);
-
- spdlog::info("'{}' contains ConVar '{}'", Name, convar->Name);
- }
-}
-
-void Mod::ParseConCommands(rapidjson_document& json)
-{
- if (!json.HasMember("ConCommands"))
- return;
-
- if (!json["ConCommands"].IsArray())
- {
- spdlog::warn("'ConCommands' field is not an array, skipping...");
- return;
- }
-
- for (auto& concommandObj : json["ConCommands"].GetArray())
- {
- if (!concommandObj.IsObject())
- {
- spdlog::warn("ConCommand is not an object, skipping...");
- continue;
- }
- if (!concommandObj.HasMember("Name"))
- {
- spdlog::warn("ConCommand does not have a Name, skipping...");
- continue;
- }
- // from here on, the ConCommand can be referenced by name in logs
- if (!concommandObj.HasMember("Function"))
- {
- spdlog::warn("ConCommand '{}' does not have a Function, skipping...", concommandObj["Name"].GetString());
- continue;
- }
- if (!concommandObj.HasMember("Context"))
- {
- spdlog::warn("ConCommand '{}' does not have a Context, skipping...", concommandObj["Name"].GetString());
- continue;
- }
-
- // have to allocate this manually, otherwise concommand registration will break
- // unfortunately this causes us to leak memory on reload, unsure of a way around this rn
- ModConCommand* concommand = new ModConCommand;
- concommand->Name = concommandObj["Name"].GetString();
- concommand->Function = concommandObj["Function"].GetString();
- concommand->Context = ScriptContextFromString(concommandObj["Context"].GetString());
- if (concommand->Context == ScriptContext::INVALID)
- {
- spdlog::warn("ConCommand '{}' has invalid context '{}', skipping...", concommand->Name, concommandObj["Context"].GetString());
- continue;
- }
-
- if (concommandObj.HasMember("HelpString"))
- concommand->HelpString = concommandObj["HelpString"].GetString();
- else
- concommand->HelpString = "";
-
- concommand->Flags = FCVAR_NONE;
-
- if (concommandObj.HasMember("Flags"))
- {
- // read raw integer flags
- if (concommandObj["Flags"].IsInt())
- {
- concommand->Flags = concommandObj["Flags"].GetInt();
- }
- else if (concommandObj["Flags"].IsString())
- {
- // parse cvar flags from string
- // example string: ARCHIVE_PLAYERPROFILE | GAMEDLL
- concommand->Flags |= ParseConVarFlagsString(concommand->Name, concommandObj["Flags"].GetString());
- }
- }
-
- ConCommands.push_back(concommand);
-
- spdlog::info("'{}' contains ConCommand '{}'", Name, concommand->Name);
- }
-}
-
-void Mod::ParseScripts(rapidjson_document& json)
-{
- if (!json.HasMember("Scripts"))
- return;
-
- if (!json["Scripts"].IsArray())
- {
- spdlog::warn("'Scripts' field is not an array, skipping...");
- return;
- }
-
- for (auto& scriptObj : json["Scripts"].GetArray())
- {
- if (!scriptObj.IsObject())
- {
- spdlog::warn("Script is not an object, skipping...");
- continue;
- }
- if (!scriptObj.HasMember("Path"))
- {
- spdlog::warn("Script does not have a Path, skipping...");
- continue;
- }
- // from here on, the Path for a script is used as it's name in logs
- if (!scriptObj.HasMember("RunOn"))
- {
- // "a RunOn" sounds dumb but anything else doesn't match the format of the warnings...
- // this is the best i could think of within 20 seconds
- spdlog::warn("Script '{}' does not have a RunOn field, skipping...", scriptObj["Path"].GetString());
- continue;
- }
-
- ModScript script;
-
- script.Path = scriptObj["Path"].GetString();
- script.RunOn = scriptObj["RunOn"].GetString();
-
- if (scriptObj.HasMember("ServerCallback"))
- {
- if (scriptObj["ServerCallback"].IsObject())
- {
- ModScriptCallback callback;
- callback.Context = ScriptContext::SERVER;
-
- if (scriptObj["ServerCallback"].HasMember("Before"))
- {
- if (scriptObj["ServerCallback"]["Before"].IsString())
- callback.BeforeCallback = scriptObj["ServerCallback"]["Before"].GetString();
- else
- spdlog::warn("'Before' ServerCallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- if (scriptObj["ServerCallback"].HasMember("After"))
- {
- if (scriptObj["ServerCallback"]["After"].IsString())
- callback.AfterCallback = scriptObj["ServerCallback"]["After"].GetString();
- else
- spdlog::warn("'After' ServerCallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- if (scriptObj["ServerCallback"].HasMember("Destroy"))
- {
- if (scriptObj["ServerCallback"]["Destroy"].IsString())
- callback.DestroyCallback = scriptObj["ServerCallback"]["Destroy"].GetString();
- else
- spdlog::warn(
- "'Destroy' ServerCallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- script.Callbacks.push_back(callback);
- }
- else
- {
- spdlog::warn("ServerCallback for script '{}' is not an object, skipping...", scriptObj["Path"].GetString());
- }
- }
-
- if (scriptObj.HasMember("ClientCallback"))
- {
- if (scriptObj["ClientCallback"].IsObject())
- {
- ModScriptCallback callback;
- callback.Context = ScriptContext::CLIENT;
-
- if (scriptObj["ClientCallback"].HasMember("Before"))
- {
- if (scriptObj["ClientCallback"]["Before"].IsString())
- callback.BeforeCallback = scriptObj["ClientCallback"]["Before"].GetString();
- else
- spdlog::warn("'Before' ClientCallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- if (scriptObj["ClientCallback"].HasMember("After"))
- {
- if (scriptObj["ClientCallback"]["After"].IsString())
- callback.AfterCallback = scriptObj["ClientCallback"]["After"].GetString();
- else
- spdlog::warn("'After' ClientCallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- if (scriptObj["ClientCallback"].HasMember("Destroy"))
- {
- if (scriptObj["ClientCallback"]["Destroy"].IsString())
- callback.DestroyCallback = scriptObj["ClientCallback"]["Destroy"].GetString();
- else
- spdlog::warn(
- "'Destroy' ClientCallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- script.Callbacks.push_back(callback);
- }
- else
- {
- spdlog::warn("ClientCallback for script '{}' is not an object, skipping...", scriptObj["Path"].GetString());
- }
- }
-
- if (scriptObj.HasMember("UICallback"))
- {
- if (scriptObj["UICallback"].IsObject())
- {
- ModScriptCallback callback;
- callback.Context = ScriptContext::UI;
-
- if (scriptObj["UICallback"].HasMember("Before"))
- {
- if (scriptObj["UICallback"]["Before"].IsString())
- callback.BeforeCallback = scriptObj["UICallback"]["Before"].GetString();
- else
- spdlog::warn("'Before' UICallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- if (scriptObj["UICallback"].HasMember("After"))
- {
- if (scriptObj["UICallback"]["After"].IsString())
- callback.AfterCallback = scriptObj["UICallback"]["After"].GetString();
- else
- spdlog::warn("'After' UICallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- if (scriptObj["UICallback"].HasMember("Destroy"))
- {
- if (scriptObj["UICallback"]["Destroy"].IsString())
- callback.DestroyCallback = scriptObj["UICallback"]["Destroy"].GetString();
- else
- spdlog::warn("'Destroy' UICallback for script '{}' is not a string, skipping...", scriptObj["Path"].GetString());
- }
-
- script.Callbacks.push_back(callback);
- }
- else
- {
- spdlog::warn("UICallback for script '{}' is not an object, skipping...", scriptObj["Path"].GetString());
- }
- }
-
- Scripts.push_back(script);
-
- spdlog::info("'{}' contains Script '{}'", Name, script.Path);
- }
-}
-
-void Mod::ParseLocalization(rapidjson_document& json)
-{
- if (!json.HasMember("Localisation"))
- return;
-
- if (!json["Localisation"].IsArray())
- {
- spdlog::warn("'Localisation' field is not an array, skipping...");
- return;
- }
-
- for (auto& localisationStr : json["Localisation"].GetArray())
- {
- if (!localisationStr.IsString())
- {
- // not a string but we still GetString() to log it :trol:
- spdlog::warn("Localisation '{}' is not a string, skipping...", localisationStr.GetString());
- continue;
- }
-
- LocalisationFiles.push_back(localisationStr.GetString());
-
- spdlog::info("'{}' registered Localisation '{}'", Name, localisationStr.GetString());
- }
-}
-
-void Mod::ParseDependencies(rapidjson_document& json)
-{
- if (!json.HasMember("Dependencies"))
- return;
-
- if (!json["Dependencies"].IsObject())
- {
- spdlog::warn("'Dependencies' field is not an object, skipping...");
- return;
- }
-
- for (auto v = json["Dependencies"].MemberBegin(); v != json["Dependencies"].MemberEnd(); v++)
- {
- if (!v->name.IsString())
- {
- spdlog::warn("Dependency constant '{}' is not a string, skipping...", v->name.GetString());
- continue;
- }
- if (!v->value.IsString())
- {
- spdlog::warn("Dependency constant '{}' is not a string, skipping...", v->value.GetString());
- continue;
- }
-
- if (DependencyConstants.find(v->name.GetString()) != DependencyConstants.end() &&
- v->value.GetString() != DependencyConstants[v->name.GetString()])
- {
- // this is fatal because otherwise the mod will probably try to use functions that dont exist,
- // which will cause errors further down the line that are harder to debug
- spdlog::error(
- "'{}' attempted to register a dependency constant '{}' for '{}' that already exists for '{}'. "
- "Change the constant name.",
- Name,
- v->name.GetString(),
- v->value.GetString(),
- DependencyConstants[v->name.GetString()]);
- return;
- }
-
- if (DependencyConstants.find(v->name.GetString()) == DependencyConstants.end())
- DependencyConstants.emplace(v->name.GetString(), v->value.GetString());
-
- spdlog::info("'{}' registered dependency constant '{}' for mod '{}'", Name, v->name.GetString(), v->value.GetString());
- }
-}
-
-void Mod::ParsePluginDependencies(rapidjson_document& json)
-{
- if (!json.HasMember("PluginDependencies"))
- return;
-
- if (!json["PluginDependencies"].IsArray())
- {
- spdlog::warn("'PluginDependencies' field is not an object, skipping...");
- return;
- }
-
- for (auto& name : json["PluginDependencies"].GetArray())
- {
- if (!name.IsString())
- continue;
-
- spdlog::info("Plugin Constant {} defined by {}", name.GetString(), Name);
-
- PluginDependencyConstants.push_back(name.GetString());
- }
-}
-
-void Mod::ParseInitScript(rapidjson_document& json)
-{
- if (!json.HasMember("InitScript"))
- return;
-
- if (!json["InitScript"].IsString())
- {
- spdlog::warn("'InitScript' field is not a string, skipping...");
- return;
- }
-
- initScript = json["InitScript"].GetString();
-}
-
-ModManager::ModManager()
-{
- // precaculated string hashes
- // note: use backslashes for these, since we use lexically_normal for file paths which uses them
- m_hScriptsRsonHash = STR_HASH("scripts\\vscripts\\scripts.rson");
- m_hPdefHash = STR_HASH(
- "cfg\\server\\persistent_player_data_version_231.pdef" // this can have multiple versions, but we use 231 so that's what we hash
- );
- m_hKBActHash = STR_HASH("scripts\\kb_act.lst");
-
- LoadMods();
-}
-
-struct Test
-{
- std::string funcName;
- ScriptContext context;
-};
-
-template <ScriptContext context> auto ModConCommandCallback_Internal(std::string name, const CCommand& command)
-{
- if (g_pSquirrel<context>->m_pSQVM && g_pSquirrel<context>->m_pSQVM)
- {
- if (command.ArgC() == 1)
- {
- g_pSquirrel<context>->AsyncCall(name);
- }
- else
- {
- std::vector<std::string> args;
- args.reserve(command.ArgC());
- for (int i = 1; i < command.ArgC(); i++)
- args.push_back(command.Arg(i));
- g_pSquirrel<context>->AsyncCall(name, args);
- }
- }
- else
- {
- spdlog::warn("ConCommand `{}` was called while the associated Squirrel VM `{}` was unloaded", name, GetContextName(context));
- }
-}
-
-auto ModConCommandCallback(const CCommand& command)
-{
- ModConCommand* found = nullptr;
- auto commandString = std::string(command.GetCommandString());
-
- // Finding the first space to remove the command's name
- auto firstSpace = commandString.find(' ');
- if (firstSpace)
- {
- commandString = commandString.substr(0, firstSpace);
- }
-
- // Find the mod this command belongs to
- for (auto& mod : g_pModManager->m_LoadedMods)
- {
- auto res = std::find_if(
- mod.ConCommands.begin(),
- mod.ConCommands.end(),
- [&commandString](const ModConCommand* other) { return other->Name == commandString; });
- if (res != mod.ConCommands.end())
- {
- found = *res;
- break;
- }
- }
- if (!found)
- return;
-
- switch (found->Context)
- {
- case ScriptContext::CLIENT:
- ModConCommandCallback_Internal<ScriptContext::CLIENT>(found->Function, command);
- break;
- case ScriptContext::SERVER:
- ModConCommandCallback_Internal<ScriptContext::SERVER>(found->Function, command);
- break;
- case ScriptContext::UI:
- ModConCommandCallback_Internal<ScriptContext::UI>(found->Function, command);
- break;
- };
-}
-
-void ModManager::LoadMods()
-{
- if (m_bHasLoadedMods)
- UnloadMods();
-
- std::vector<fs::path> modDirs;
-
- // ensure dirs exist
- fs::remove_all(GetCompiledAssetsPath());
- fs::create_directories(GetModFolderPath());
- fs::create_directories(GetThunderstoreModFolderPath());
- fs::create_directories(GetRemoteModFolderPath());
-
- m_DependencyConstants.clear();
-
- // read enabled mods cfg
- std::ifstream enabledModsStream(GetNorthstarPrefix() + "/enabledmods.json");
- std::stringstream enabledModsStringStream;
-
- if (!enabledModsStream.fail())
- {
- while (enabledModsStream.peek() != EOF)
- enabledModsStringStream << (char)enabledModsStream.get();
-
- enabledModsStream.close();
- m_EnabledModsCfg.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(
- enabledModsStringStream.str().c_str());
-
- m_bHasEnabledModsCfg = m_EnabledModsCfg.IsObject();
- }
-
- // get mod directories
- std::filesystem::directory_iterator classicModsDir = fs::directory_iterator(GetModFolderPath());
- std::filesystem::directory_iterator remoteModsDir = fs::directory_iterator(GetRemoteModFolderPath());
- std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath());
-
- for (fs::directory_entry dir : classicModsDir)
- if (fs::exists(dir.path() / "mod.json"))
- modDirs.push_back(dir.path());
-
- // Special case for Thunderstore and remote mods directories
- // Set up regex for `AUTHOR-MOD-VERSION` pattern
- std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))");
-
- for (fs::directory_iterator dirIterator : {thunderstoreModsDir, remoteModsDir})
- {
- for (fs::directory_entry dir : dirIterator)
- {
- fs::path modsDir = dir.path() / "mods"; // Check for mods folder in the Thunderstore mod
- // Use regex to match `AUTHOR-MOD-VERSION` pattern
- if (!std::regex_match(dir.path().string(), pattern))
- {
- spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string());
- continue; // skip loading mod that doesn't match
- }
- if (fs::exists(modsDir) && fs::is_directory(modsDir))
- {
- for (fs::directory_entry subDir : fs::directory_iterator(modsDir))
- {
- if (fs::exists(subDir.path() / "mod.json"))
- {
- modDirs.push_back(subDir.path());
- }
- }
- }
- }
- }
-
- for (fs::path modDir : modDirs)
- {
- // read mod json file
- std::ifstream jsonStream(modDir / "mod.json");
- std::stringstream jsonStringStream;
-
- // fail if no mod json
- if (jsonStream.fail())
- {
- spdlog::warn(
- "Mod file at '{}' does not exist or could not be read, is it installed correctly?", (modDir / "mod.json").string());
- continue;
- }
-
- while (jsonStream.peek() != EOF)
- jsonStringStream << (char)jsonStream.get();
-
- jsonStream.close();
-
- Mod mod(modDir, (char*)jsonStringStream.str().c_str());
-
- for (auto& pair : mod.DependencyConstants)
- {
- if (m_DependencyConstants.find(pair.first) != m_DependencyConstants.end() && m_DependencyConstants[pair.first] != pair.second)
- {
- spdlog::error(
- "'{}' attempted to register a dependency constant '{}' for '{}' that already exists for '{}'. "
- "Change the constant name.",
- mod.Name,
- pair.first,
- pair.second,
- m_DependencyConstants[pair.first]);
- mod.m_bWasReadSuccessfully = false;
- break;
- }
- if (m_DependencyConstants.find(pair.first) == m_DependencyConstants.end())
- m_DependencyConstants.emplace(pair);
- }
-
- for (std::string& dependency : mod.PluginDependencyConstants)
- {
- m_PluginDependencyConstants.insert(dependency);
- }
-
- if (m_bHasEnabledModsCfg && m_EnabledModsCfg.HasMember(mod.Name.c_str()))
- mod.m_bEnabled = m_EnabledModsCfg[mod.Name.c_str()].IsTrue();
- else
- mod.m_bEnabled = true;
-
- if (mod.m_bWasReadSuccessfully)
- {
- if (mod.m_bEnabled)
- spdlog::info("'{}' loaded successfully, version {}", mod.Name, mod.Version);
- else
- spdlog::info("'{}' loaded successfully, version {} (DISABLED)", mod.Name, mod.Version);
-
- m_LoadedMods.push_back(mod);
- }
- else
- spdlog::warn("Mod file at '{}' failed to load", (modDir / "mod.json").string());
- }
-
- // sort by load prio, lowest-highest
- std::sort(m_LoadedMods.begin(), m_LoadedMods.end(), [](Mod& a, Mod& b) { return a.LoadPriority < b.LoadPriority; });
-
- // This is used to check if some mods have a folder but no entry in enabledmods.json
- bool newModsDetected = false;
-
- for (Mod& mod : m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- // Add mod entry to enabledmods.json if it doesn't exist
- if (!mod.m_bIsRemote && !m_EnabledModsCfg.HasMember(mod.Name.c_str()))
- {
- m_EnabledModsCfg.AddMember(rapidjson_document::StringRefType(mod.Name.c_str()), true, m_EnabledModsCfg.GetAllocator());
- newModsDetected = true;
- }
-
- // register convars
- // for reloads, this is sorta barebones, when we have a good findconvar method, we could probably reset flags and stuff on
- // preexisting convars note: we don't delete convars if they already exist because they're used for script stuff, unfortunately this
- // causes us to leak memory on reload, but not much, potentially find a way to not do this at some point
- for (ModConVar* convar : mod.ConVars)
- {
- // make sure convar isn't registered yet, unsure if necessary but idk what
- // behaviour is for defining same convar multiple times
- if (!g_pCVar->FindVar(convar->Name.c_str()))
- {
- new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str());
- }
- }
-
- for (ModConCommand* command : mod.ConCommands)
- {
- // make sure command isnt't registered multiple times.
- if (!g_pCVar->FindCommand(command->Name.c_str()))
- {
- ConCommand* newCommand = new ConCommand();
- std::string funcName = command->Function;
- RegisterConCommand(command->Name.c_str(), ModConCommandCallback, command->HelpString.c_str(), command->Flags);
- }
- }
-
- // read vpk paths
- if (fs::exists(mod.m_ModDirectory / "vpk"))
- {
- // read vpk cfg
- std::ifstream vpkJsonStream(mod.m_ModDirectory / "vpk/vpk.json");
- std::stringstream vpkJsonStringStream;
-
- bool bUseVPKJson = false;
- rapidjson::Document dVpkJson;
-
- if (!vpkJsonStream.fail())
- {
- while (vpkJsonStream.peek() != EOF)
- vpkJsonStringStream << (char)vpkJsonStream.get();
-
- vpkJsonStream.close();
- dVpkJson.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(
- vpkJsonStringStream.str().c_str());
-
- bUseVPKJson = !dVpkJson.HasParseError() && dVpkJson.IsObject();
- }
-
- for (fs::directory_entry file : fs::directory_iterator(mod.m_ModDirectory / "vpk"))
- {
- // a bunch of checks to make sure we're only adding dir vpks and their paths are good
- // note: the game will literally only load vpks with the english prefix
- if (fs::is_regular_file(file) && file.path().extension() == ".vpk" &&
- file.path().string().find("english") != std::string::npos &&
- file.path().string().find(".bsp.pak000_dir") != std::string::npos)
- {
- std::string formattedPath = file.path().filename().string();
-
- // this really fucking sucks but it'll work
- std::string vpkName = formattedPath.substr(strlen("english"), formattedPath.find(".bsp") - 3);
-
- ModVPKEntry& modVpk = mod.Vpks.emplace_back();
- modVpk.m_bAutoLoad = !bUseVPKJson || (dVpkJson.HasMember("Preload") && dVpkJson["Preload"].IsObject() &&
- dVpkJson["Preload"].HasMember(vpkName) && dVpkJson["Preload"][vpkName].IsTrue());
- modVpk.m_sVpkPath = (file.path().parent_path() / vpkName).string();
-
- if (m_bHasLoadedMods && modVpk.m_bAutoLoad)
- (*g_pFilesystem)->m_vtable->MountVPK(*g_pFilesystem, vpkName.c_str());
- }
- }
- }
-
- // read rpak paths
- if (fs::exists(mod.m_ModDirectory / "paks"))
- {
- // read rpak cfg
- std::ifstream rpakJsonStream(mod.m_ModDirectory / "paks/rpak.json");
- std::stringstream rpakJsonStringStream;
-
- bool bUseRpakJson = false;
- rapidjson::Document dRpakJson;
-
- if (!rpakJsonStream.fail())
- {
- while (rpakJsonStream.peek() != EOF)
- rpakJsonStringStream << (char)rpakJsonStream.get();
-
- rpakJsonStream.close();
- dRpakJson.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(
- rpakJsonStringStream.str().c_str());
-
- bUseRpakJson = !dRpakJson.HasParseError() && dRpakJson.IsObject();
- }
-
- // read pak aliases
- if (bUseRpakJson && dRpakJson.HasMember("Aliases") && dRpakJson["Aliases"].IsObject())
- {
- for (rapidjson::Value::ConstMemberIterator iterator = dRpakJson["Aliases"].MemberBegin();
- iterator != dRpakJson["Aliases"].MemberEnd();
- iterator++)
- {
- if (!iterator->name.IsString() || !iterator->value.IsString())
- continue;
-
- mod.RpakAliases.insert(std::make_pair(iterator->name.GetString(), iterator->value.GetString()));
- }
- }
-
- for (fs::directory_entry file : fs::directory_iterator(mod.m_ModDirectory / "paks"))
- {
- // ensure we're only loading rpaks
- if (fs::is_regular_file(file) && file.path().extension() == ".rpak")
- {
- std::string pakName(file.path().filename().string());
-
- ModRpakEntry& modPak = mod.Rpaks.emplace_back();
- modPak.m_bAutoLoad =
- !bUseRpakJson || (dRpakJson.HasMember("Preload") && dRpakJson["Preload"].IsObject() &&
- dRpakJson["Preload"].HasMember(pakName) && dRpakJson["Preload"][pakName].IsTrue());
-
- // postload things
- if (!bUseRpakJson ||
- (dRpakJson.HasMember("Postload") && dRpakJson["Postload"].IsObject() && dRpakJson["Postload"].HasMember(pakName)))
- modPak.m_sLoadAfterPak = dRpakJson["Postload"][pakName].GetString();
-
- modPak.m_sPakName = pakName;
-
- // read header of file and get the starpak paths
- // this is done here as opposed to on starpak load because multiple rpaks can load a starpak
- // and there is seemingly no good way to tell which rpak is causing the load of a starpak :/
-
- std::ifstream rpakStream(file.path(), std::ios::binary);
-
- // seek to the point in the header where the starpak reference size is
- rpakStream.seekg(0x38, std::ios::beg);
- int starpaksSize = 0;
- rpakStream.read((char*)&starpaksSize, 2);
-
- // seek to just after the header
- rpakStream.seekg(0x58, std::ios::beg);
- // read the starpak reference(s)
- std::vector<char> buf(starpaksSize);
- rpakStream.read(buf.data(), starpaksSize);
-
- rpakStream.close();
-
- // split the starpak reference(s) into strings to hash
- std::string str = "";
- for (int i = 0; i < starpaksSize; i++)
- {
- // if the current char is null, that signals the end of the current starpak path
- if (buf[i] != 0x00)
- {
- str += buf[i];
- }
- else
- {
- // only add the string we are making if it isnt empty
- if (!str.empty())
- {
- mod.StarpakPaths.push_back(STR_HASH(str));
- spdlog::info("Mod {} registered starpak '{}'", mod.Name, str);
- str = "";
- }
- }
- }
-
- // not using atm because we need to resolve path to rpak
- // if (m_hasLoadedMods && modPak.m_bAutoLoad)
- // g_pPakLoadManager->LoadPakAsync(pakName.c_str());
- }
- }
- }
-
- // read keyvalues paths
- if (fs::exists(mod.m_ModDirectory / "keyvalues"))
- {
- for (fs::directory_entry file : fs::recursive_directory_iterator(mod.m_ModDirectory / "keyvalues"))
- {
- if (fs::is_regular_file(file))
- {
- std::string kvStr =
- g_pModManager->NormaliseModFilePath(file.path().lexically_relative(mod.m_ModDirectory / "keyvalues"));
- mod.KeyValues.emplace(STR_HASH(kvStr), kvStr);
- }
- }
- }
-
- // read pdiff
- if (fs::exists(mod.m_ModDirectory / "mod.pdiff"))
- {
- std::ifstream pdiffStream(mod.m_ModDirectory / "mod.pdiff");
-
- if (!pdiffStream.fail())
- {
- std::stringstream pdiffStringStream;
- while (pdiffStream.peek() != EOF)
- pdiffStringStream << (char)pdiffStream.get();
-
- pdiffStream.close();
-
- mod.Pdiff = pdiffStringStream.str();
- }
- }
-
- // read bink video paths
- if (fs::exists(mod.m_ModDirectory / "media"))
- {
- for (fs::directory_entry file : fs::recursive_directory_iterator(mod.m_ModDirectory / "media"))
- if (fs::is_regular_file(file) && file.path().extension() == ".bik")
- mod.BinkVideos.push_back(file.path().filename().string());
- }
-
- // try to load audio
- if (fs::exists(mod.m_ModDirectory / "audio"))
- {
- for (fs::directory_entry file : fs::directory_iterator(mod.m_ModDirectory / "audio"))
- {
- if (fs::is_regular_file(file) && file.path().extension().string() == ".json")
- {
- if (!g_CustomAudioManager.TryLoadAudioOverride(file.path()))
- {
- spdlog::warn("Mod {} has an invalid audio def {}", mod.Name, file.path().filename().string());
- continue;
- }
- }
- }
- }
- }
-
- // If there are new mods, we write entries accordingly in enabledmods.json
- if (newModsDetected)
- {
- std::ofstream writeStream(GetNorthstarPrefix() + "/enabledmods.json");
- rapidjson::OStreamWrapper writeStreamWrapper(writeStream);
- rapidjson::PrettyWriter<rapidjson::OStreamWrapper> writer(writeStreamWrapper);
- m_EnabledModsCfg.Accept(writer);
- }
-
- // in a seperate loop because we register mod files in reverse order, since mods loaded later should have their files prioritised
- for (int64_t i = m_LoadedMods.size() - 1; i > -1; i--)
- {
- if (!m_LoadedMods[i].m_bEnabled)
- continue;
-
- if (fs::exists(m_LoadedMods[i].m_ModDirectory / MOD_OVERRIDE_DIR))
- {
- for (fs::directory_entry file : fs::recursive_directory_iterator(m_LoadedMods[i].m_ModDirectory / MOD_OVERRIDE_DIR))
- {
- std::string path =
- g_pModManager->NormaliseModFilePath(file.path().lexically_relative(m_LoadedMods[i].m_ModDirectory / MOD_OVERRIDE_DIR));
- if (file.is_regular_file() && m_ModFiles.find(path) == m_ModFiles.end())
- {
- ModOverrideFile modFile;
- modFile.m_pOwningMod = &m_LoadedMods[i];
- modFile.m_Path = path;
- m_ModFiles.insert(std::make_pair(path, modFile));
- }
- }
- }
- }
-
- // build modinfo obj for masterserver
- rapidjson_document modinfoDoc;
- auto& alloc = modinfoDoc.GetAllocator();
- modinfoDoc.SetObject();
- modinfoDoc.AddMember("Mods", rapidjson::kArrayType, alloc);
-
- int currentModIndex = 0;
- for (Mod& mod : m_LoadedMods)
- {
- if (!mod.m_bEnabled || (!mod.RequiredOnClient && !mod.Pdiff.size()))
- continue;
-
- modinfoDoc["Mods"].PushBack(rapidjson::kObjectType, modinfoDoc.GetAllocator());
- modinfoDoc["Mods"][currentModIndex].AddMember("Name", rapidjson::StringRef(&mod.Name[0]), modinfoDoc.GetAllocator());
- modinfoDoc["Mods"][currentModIndex].AddMember("Version", rapidjson::StringRef(&mod.Version[0]), modinfoDoc.GetAllocator());
- modinfoDoc["Mods"][currentModIndex].AddMember("RequiredOnClient", mod.RequiredOnClient, modinfoDoc.GetAllocator());
- modinfoDoc["Mods"][currentModIndex].AddMember("Pdiff", rapidjson::StringRef(&mod.Pdiff[0]), modinfoDoc.GetAllocator());
-
- currentModIndex++;
- }
-
- rapidjson::StringBuffer buffer;
- buffer.Clear();
- rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
- modinfoDoc.Accept(writer);
- g_pMasterServerManager->m_sOwnModInfoJson = std::string(buffer.GetString());
-
- m_bHasLoadedMods = true;
-}
-
-void ModManager::UnloadMods()
-{
- // clean up stuff from mods before we unload
- m_ModFiles.clear();
- fs::remove_all(GetCompiledAssetsPath());
-
- g_CustomAudioManager.ClearAudioOverrides();
-
- if (!m_bHasEnabledModsCfg)
- m_EnabledModsCfg.SetObject();
-
- for (Mod& mod : m_LoadedMods)
- {
- // remove all built kvs
- for (std::pair<size_t, std::string> kvPaths : mod.KeyValues)
- fs::remove(GetCompiledAssetsPath() / fs::path(kvPaths.second).lexically_relative(mod.m_ModDirectory));
-
- mod.KeyValues.clear();
-
- // write to m_enabledModsCfg
- // should we be doing this here or should scripts be doing this manually?
- // main issue with doing this here is when we reload mods for connecting to a server, we write enabled mods, which isn't necessarily
- // what we wanna do
- if (!m_EnabledModsCfg.HasMember(mod.Name.c_str()))
- m_EnabledModsCfg.AddMember(rapidjson_document::StringRefType(mod.Name.c_str()), false, m_EnabledModsCfg.GetAllocator());
-
- m_EnabledModsCfg[mod.Name.c_str()].SetBool(mod.m_bEnabled);
- }
-
- std::ofstream writeStream(GetNorthstarPrefix() + "/enabledmods.json");
- rapidjson::OStreamWrapper writeStreamWrapper(writeStream);
- rapidjson::PrettyWriter<rapidjson::OStreamWrapper> writer(writeStreamWrapper);
- m_EnabledModsCfg.Accept(writer);
-
- // do we need to dealloc individual entries in m_loadedMods? idk, rework
- m_LoadedMods.clear();
-}
-
-std::string ModManager::NormaliseModFilePath(const fs::path path)
-{
- std::string str = path.lexically_normal().string();
-
- // force to lowercase
- for (char& c : str)
- if (c <= 'Z' && c >= 'A')
- c = c - ('Z' - 'z');
-
- return str;
-}
-
-void ModManager::CompileAssetsForFile(const char* filename)
-{
- size_t fileHash = STR_HASH(NormaliseModFilePath(fs::path(filename)));
-
- if (fileHash == m_hScriptsRsonHash)
- BuildScriptsRson();
- else if (fileHash == m_hPdefHash)
- BuildPdef();
- else if (fileHash == m_hKBActHash)
- BuildKBActionsList();
- else
- {
- // check if we should build keyvalues, depending on whether any of our mods have patch kvs for this file
- for (Mod& mod : m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- if (mod.KeyValues.find(fileHash) != mod.KeyValues.end())
- {
- TryBuildKeyValues(filename);
- return;
- }
- }
- }
-}
-
-void ConCommand_reload_mods(const CCommand& args)
-{
- g_pModManager->LoadMods();
-}
-
-fs::path GetModFolderPath()
-{
- return fs::path(GetNorthstarPrefix() + MOD_FOLDER_SUFFIX);
-}
-fs::path GetThunderstoreModFolderPath()
-{
- return fs::path(GetNorthstarPrefix() + THUNDERSTORE_MOD_FOLDER_SUFFIX);
-}
-fs::path GetRemoteModFolderPath()
-{
- return fs::path(GetNorthstarPrefix() + REMOTE_MOD_FOLDER_SUFFIX);
-}
-fs::path GetCompiledAssetsPath()
-{
- return fs::path(GetNorthstarPrefix() + COMPILED_ASSETS_SUFFIX);
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ModManager, (ConCommand, MasterServer), (CModule module))
-{
- g_pModManager = new ModManager;
-
- RegisterConCommand("reload_mods", ConCommand_reload_mods, "reloads mods", FCVAR_NONE);
-}
diff --git a/NorthstarDLL/mods/modmanager.h b/NorthstarDLL/mods/modmanager.h
deleted file mode 100644
index 233f004d..00000000
--- a/NorthstarDLL/mods/modmanager.h
+++ /dev/null
@@ -1,187 +0,0 @@
-#pragma once
-#include "core/convar/convar.h"
-#include "core/memalloc.h"
-#include "squirrel/squirrel.h"
-
-#include "rapidjson/document.h"
-#include <string>
-#include <vector>
-#include <filesystem>
-#include <unordered_set>
-
-const std::string MOD_FOLDER_SUFFIX = "\\mods";
-const std::string THUNDERSTORE_MOD_FOLDER_SUFFIX = "\\packages";
-const std::string REMOTE_MOD_FOLDER_SUFFIX = "\\runtime\\remote\\mods";
-const fs::path MOD_OVERRIDE_DIR = "mod";
-const std::string COMPILED_ASSETS_SUFFIX = "\\runtime\\compiled";
-
-const std::set<std::string> MODS_BLACKLIST = {"Mod Settings"};
-
-struct ModConVar
-{
-public:
- std::string Name;
- std::string DefaultValue;
- std::string HelpString;
- int Flags;
-};
-
-struct ModConCommand
-{
-public:
- std::string Name;
- std::string Function;
- std::string HelpString;
- ScriptContext Context;
- int Flags;
-};
-
-struct ModScriptCallback
-{
-public:
- ScriptContext Context;
-
- // called before the codecallback is executed
- std::string BeforeCallback;
- // called after the codecallback has finished executing
- std::string AfterCallback;
- // called right before the vm is destroyed.
- std::string DestroyCallback;
-};
-
-struct ModScript
-{
-public:
- std::string Path;
- std::string RunOn;
-
- std::vector<ModScriptCallback> Callbacks;
-};
-
-// these are pretty much identical, could refactor to use the same stuff?
-struct ModVPKEntry
-{
-public:
- bool m_bAutoLoad;
- std::string m_sVpkPath;
-};
-
-struct ModRpakEntry
-{
-public:
- bool m_bAutoLoad;
- std::string m_sPakName;
- std::string m_sLoadAfterPak;
-};
-
-class Mod
-{
-public:
- // runtime stuff
- bool m_bEnabled = true;
- bool m_bWasReadSuccessfully = false;
- fs::path m_ModDirectory;
- bool m_bIsRemote;
-
- // mod.json stuff:
-
- // the mod's name
- std::string Name;
- // the mod's description
- std::string Description;
- // the mod's version, should be in semver
- std::string Version;
- // a download link to the mod, for clients that try to join without the mod
- std::string DownloadLink;
-
- // whether clients need the mod to join servers running this mod
- bool RequiredOnClient;
- // the priority for this mod's files, mods with prio 0 are loaded first, then 1, then 2, etc
- int LoadPriority;
-
- // custom scripts used by the mod
- std::vector<ModScript> Scripts;
- // convars created by the mod
- std::vector<ModConVar*> ConVars;
- // concommands created by the mod
- std::vector<ModConCommand*> ConCommands;
- // custom localisation files created by the mod
- std::vector<std::string> LocalisationFiles;
- // custom script init.nut
- std::string initScript;
-
- // other files:
-
- std::vector<ModVPKEntry> Vpks;
- std::unordered_map<size_t, std::string> KeyValues;
- std::vector<std::string> BinkVideos;
- std::string Pdiff; // only need one per mod
-
- std::vector<ModRpakEntry> Rpaks;
- std::unordered_map<std::string, std::string>
- RpakAliases; // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite
- std::vector<size_t> StarpakPaths; // starpaks that this mod contains
- // there seems to be no nice way to get the rpak that is causing the load of a starpak?
- // hashed with STR_HASH
-
- std::unordered_map<std::string, std::string> DependencyConstants;
- std::vector<std::string> PluginDependencyConstants;
-
-public:
- Mod(fs::path modPath, char* jsonBuf);
-
-private:
- void ParseConVars(rapidjson_document& json);
- void ParseConCommands(rapidjson_document& json);
- void ParseScripts(rapidjson_document& json);
- void ParseLocalization(rapidjson_document& json);
- void ParseDependencies(rapidjson_document& json);
- void ParsePluginDependencies(rapidjson_document& json);
- void ParseInitScript(rapidjson_document& json);
-};
-
-struct ModOverrideFile
-{
-public:
- Mod* m_pOwningMod;
- fs::path m_Path;
-};
-
-class ModManager
-{
-private:
- bool m_bHasLoadedMods = false;
- bool m_bHasEnabledModsCfg;
- rapidjson_document m_EnabledModsCfg;
-
- // precalculated hashes
- size_t m_hScriptsRsonHash;
- size_t m_hPdefHash;
- size_t m_hKBActHash;
-
-public:
- std::vector<Mod> m_LoadedMods;
- std::unordered_map<std::string, ModOverrideFile> m_ModFiles;
- std::unordered_map<std::string, std::string> m_DependencyConstants;
- std::unordered_set<std::string> m_PluginDependencyConstants;
-
-public:
- ModManager();
- void LoadMods();
- void UnloadMods();
- std::string NormaliseModFilePath(const fs::path path);
- void CompileAssetsForFile(const char* filename);
-
- // compile asset type stuff, these are done in files under runtime/compiled/
- void BuildScriptsRson();
- void TryBuildKeyValues(const char* filename);
- void BuildPdef();
- void BuildKBActionsList();
-};
-
-fs::path GetModFolderPath();
-fs::path GetRemoteModFolderPath();
-fs::path GetThunderstoreModFolderPath();
-fs::path GetCompiledAssetsPath();
-
-extern ModManager* g_pModManager;
diff --git a/NorthstarDLL/mods/modsavefiles.cpp b/NorthstarDLL/mods/modsavefiles.cpp
deleted file mode 100644
index 68e33864..00000000
--- a/NorthstarDLL/mods/modsavefiles.cpp
+++ /dev/null
@@ -1,572 +0,0 @@
-#include <filesystem>
-#include <sstream>
-#include <fstream>
-#include "squirrel/squirrel.h"
-#include "util/utils.h"
-#include "mods/modmanager.h"
-#include "modsavefiles.h"
-#include "rapidjson/document.h"
-#include "rapidjson/writer.h"
-#include "rapidjson/stringbuffer.h"
-#include "config/profile.h"
-#include "core/tier0.h"
-#include "rapidjson/error/en.h"
-#include "scripts/scriptjson.h"
-
-SaveFileManager* g_pSaveFileManager;
-int MAX_FOLDER_SIZE = 52428800; // 50MB (50 * 1024 * 1024)
-fs::path savePath;
-
-/// <summary></summary>
-/// <param name="dir">The directory we want the size of.</param>
-/// <param name="file">The file we're excluding from the calculation.</param>
-/// <returns>The size of the contents of the current directory, excluding a specific file.</returns>
-uintmax_t GetSizeOfFolderContentsMinusFile(fs::path dir, std::string file)
-{
- uintmax_t result = 0;
- for (const auto& entry : fs::directory_iterator(dir))
- {
- if (entry.path().filename() == file)
- continue;
- // fs::file_size may not work on directories - but does in some cases.
- // per cppreference.com, it's "implementation-defined".
- try
- {
- result += fs::file_size(entry.path());
- }
- catch (fs::filesystem_error& e)
- {
- if (entry.is_directory())
- {
- result += GetSizeOfFolderContentsMinusFile(entry.path(), "");
- }
- }
- }
- return result;
-}
-
-uintmax_t GetSizeOfFolder(fs::path dir)
-{
- uintmax_t result = 0;
- for (const auto& entry : fs::directory_iterator(dir))
- {
- // fs::file_size may not work on directories - but does in some cases.
- // per cppreference.com, it's "implementation-defined".
- try
- {
- result += fs::file_size(entry.path());
- }
- catch (fs::filesystem_error& e)
- {
- if (entry.is_directory())
- {
- result += GetSizeOfFolderContentsMinusFile(entry.path(), "");
- }
- }
- }
- return result;
-}
-
-// Saves a file asynchronously.
-template <ScriptContext context> void SaveFileManager::SaveFileAsync(fs::path file, std::string contents)
-{
- auto mutex = std::ref(fileMutex);
- std::thread writeThread(
- [mutex, file, contents]()
- {
- try
- {
- mutex.get().lock();
-
- fs::path dir = file.parent_path();
- // this actually allows mods to go over the limit, but not by much
- // the limit is to prevent mods from taking gigabytes of space,
- // we don't need to be particularly strict.
- if (GetSizeOfFolderContentsMinusFile(dir, file.filename().string()) + contents.length() > MAX_FOLDER_SIZE)
- {
- // tbh, you're either trying to fill the hard drive or use so much data, you SHOULD be congratulated.
- spdlog::error(fmt::format("Mod spamming save requests? Folder limit bypassed despite previous checks. Not saving."));
- mutex.get().unlock();
- return;
- }
-
- std::ofstream fileStr(file);
- if (fileStr.fail())
- {
- mutex.get().unlock();
- return;
- }
-
- fileStr.write(contents.c_str(), contents.length());
- fileStr.close();
-
- mutex.get().unlock();
- // side-note: this causes a leak?
- // when a file is added to the map, it's never removed.
- // no idea how to fix this - because we have no way to check if there are other threads waiting to use this file(?)
- // tried to use m.try_lock(), but it's unreliable, it seems.
- }
- catch (std::exception ex)
- {
- spdlog::error("SAVE FAILED!");
- mutex.get().unlock();
- spdlog::error(ex.what());
- }
- });
-
- writeThread.detach();
-}
-
-// Loads a file asynchronously.
-template <ScriptContext context> int SaveFileManager::LoadFileAsync(fs::path file)
-{
- int handle = ++m_iLastRequestHandle;
- auto mutex = std::ref(fileMutex);
- std::thread readThread(
- [mutex, file, handle]()
- {
- try
- {
- mutex.get().lock();
-
- std::ifstream fileStr(file);
- if (fileStr.fail())
- {
- spdlog::error("A file was supposed to be loaded but we can't access it?!");
-
- g_pSquirrel<context>->AsyncCall("NSHandleLoadResult", handle, false, "");
- mutex.get().unlock();
- return;
- }
-
- std::stringstream stringStream;
- stringStream << fileStr.rdbuf();
-
- g_pSquirrel<context>->AsyncCall("NSHandleLoadResult", handle, true, stringStream.str());
-
- fileStr.close();
- mutex.get().unlock();
- // side-note: this causes a leak?
- // when a file is added to the map, it's never removed.
- // no idea how to fix this - because we have no way to check if there are other threads waiting to use this file(?)
- // tried to use m.try_lock(), but it's unreliable, it seems.
- }
- catch (std::exception ex)
- {
- spdlog::error("LOAD FAILED!");
- g_pSquirrel<context>->AsyncCall("NSHandleLoadResult", handle, false, "");
- mutex.get().unlock();
- spdlog::error(ex.what());
- }
- });
-
- readThread.detach();
- return handle;
-}
-
-// Deletes a file asynchronously.
-template <ScriptContext context> void SaveFileManager::DeleteFileAsync(fs::path file)
-{
- // P.S. I don't like how we have to async delete calls but we do.
- auto m = std::ref(fileMutex);
- std::thread deleteThread(
- [m, file]()
- {
- try
- {
- m.get().lock();
-
- fs::remove(file);
-
- m.get().unlock();
- // side-note: this causes a leak?
- // when a file is added to the map, it's never removed.
- // no idea how to fix this - because we have no way to check if there are other threads waiting to use this file(?)
- // tried to use m.try_lock(), but it's unreliable, it seems.
- }
- catch (std::exception ex)
- {
- spdlog::error("DELETE FAILED!");
- m.get().unlock();
- spdlog::error(ex.what());
- }
- });
-
- deleteThread.detach();
-}
-
-// Checks if a file contains null characters.
-bool ContainsInvalidChars(std::string str)
-{
- // we don't allow null characters either, even if they're ASCII characters because idk if people can
- // use it to circumvent the file extension suffix.
- return std::any_of(str.begin(), str.end(), [](char c) { return c == '\0'; });
-}
-
-// Checks if the relative path (param) remains inside the mod directory (dir).
-// Paths are restricted to ASCII because encoding is fucked and we decided we won't bother.
-bool IsPathSafe(const std::string param, fs::path dir)
-{
- try
- {
- auto const normRoot = fs::weakly_canonical(dir);
- auto const normChild = fs::weakly_canonical(dir / param);
-
- auto itr = std::search(normChild.begin(), normChild.end(), normRoot.begin(), normRoot.end());
- // we return if the file is safe (inside the directory) and uses only ASCII chars in the path.
- return itr == normChild.begin() && std::none_of(
- param.begin(),
- param.end(),
- [](char c)
- {
- unsigned char unsignedC = static_cast<unsigned char>(c);
- return unsignedC > 127 || unsignedC < 0;
- });
- }
- catch (fs::filesystem_error err)
- {
- return false;
- }
-}
-
-// void NSSaveFile( string file, string data )
-ADD_SQFUNC("void", NSSaveFile, "string file, string data", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm);
- if (mod == nullptr)
- {
- g_pSquirrel<context>->raiseerror(sqvm, "Has to be called from a mod function!");
- return SQRESULT_ERROR;
- }
-
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- std::string fileName = g_pSquirrel<context>->getstring(sqvm, 1);
- if (!IsPathSafe(fileName, dir))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "File name invalid ({})! Make sure it does not contain any non-ASCII character, and results in a path inside your mod's "
- "save folder.",
- fileName,
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
-
- std::string content = g_pSquirrel<context>->getstring(sqvm, 2);
- if (ContainsInvalidChars(content))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm, fmt::format("File contents may not contain NUL/\\0 characters! Make sure your strings are valid!", mod->Name).c_str());
- return SQRESULT_ERROR;
- }
-
- fs::create_directories(dir);
- // this actually allows mods to go over the limit, but not by much
- // the limit is to prevent mods from taking gigabytes of space,
- // this ain't a cloud service.
- if (GetSizeOfFolderContentsMinusFile(dir, fileName) + content.length() > MAX_FOLDER_SIZE)
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "The mod {} has reached the maximum folder size.\n\nAsk the mod developer to optimize their data usage,"
- "or increase the maximum folder size using the -maxfoldersize launch parameter.",
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSaveFileManager->SaveFileAsync<context>(dir / fileName, content);
-
- return SQRESULT_NULL;
-}
-
-// void NSSaveJSONFile(string file, table data)
-ADD_SQFUNC("void", NSSaveJSONFile, "string file, table data", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm);
- if (mod == nullptr)
- {
- g_pSquirrel<context>->raiseerror(sqvm, "Has to be called from a mod function!");
- return SQRESULT_ERROR;
- }
-
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- std::string fileName = g_pSquirrel<context>->getstring(sqvm, 1);
- if (!IsPathSafe(fileName, dir))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "File name invalid ({})! Make sure it does not contain any non-ASCII character, and results in a path inside your mod's "
- "save folder.",
- fileName,
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
-
- // Note - this cannot be done in the async func since the table may get garbage collected.
- // This means that especially large tables may still clog up the system.
- std::string content = EncodeJSON<context>(sqvm);
- if (ContainsInvalidChars(content))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm, fmt::format("File contents may not contain NUL/\\0 characters! Make sure your strings are valid!", mod->Name).c_str());
- return SQRESULT_ERROR;
- }
-
- fs::create_directories(dir);
- // this actually allows mods to go over the limit, but not by much
- // the limit is to prevent mods from taking gigabytes of space,
- // this ain't a cloud service.
- if (GetSizeOfFolderContentsMinusFile(dir, fileName) + content.length() > MAX_FOLDER_SIZE)
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "The mod {} has reached the maximum folder size.\n\nAsk the mod developer to optimize their data usage,"
- "or increase the maximum folder size using the -maxfoldersize launch parameter.",
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSaveFileManager->SaveFileAsync<context>(dir / fileName, content);
-
- return SQRESULT_NULL;
-}
-
-// int NS_InternalLoadFile(string file)
-ADD_SQFUNC("int", NS_InternalLoadFile, "string file", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm, 1); // the function that called NSLoadFile :)
- if (mod == nullptr)
- {
- g_pSquirrel<context>->raiseerror(sqvm, "Has to be called from a mod function!");
- return SQRESULT_ERROR;
- }
-
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- std::string fileName = g_pSquirrel<context>->getstring(sqvm, 1);
- if (!IsPathSafe(fileName, dir))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "File name invalid ({})! Make sure it does not contain any non-ASCII character, and results in a path inside your mod's "
- "save folder.",
- fileName,
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, g_pSaveFileManager->LoadFileAsync<context>(dir / fileName));
-
- return SQRESULT_NOTNULL;
-}
-
-// bool NSDoesFileExist(string file)
-ADD_SQFUNC("bool", NSDoesFileExist, "string file", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm);
-
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- std::string fileName = g_pSquirrel<context>->getstring(sqvm, 1);
- if (!IsPathSafe(fileName, dir))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "File name invalid ({})! Make sure it does not contain any non-ASCII character, and results in a path inside your mod's "
- "save folder.",
- fileName,
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSquirrel<context>->pushbool(sqvm, fs::exists(dir / (fileName)));
- return SQRESULT_NOTNULL;
-}
-
-// int NSGetFileSize(string file)
-ADD_SQFUNC("int", NSGetFileSize, "string file", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm);
-
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- std::string fileName = g_pSquirrel<context>->getstring(sqvm, 1);
- if (!IsPathSafe(fileName, dir))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "File name invalid ({})! Make sure it does not contain any non-ASCII character, and results in a path inside your mod's "
- "save folder.",
- fileName,
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
- try
- {
- // throws if file does not exist
- // we don't want stuff such as "file does not exist, file is unavailable" to be lethal, so we just try/catch fs errors
- g_pSquirrel<context>->pushinteger(sqvm, (int)(fs::file_size(dir / fileName) / 1024));
- }
- catch (std::filesystem::filesystem_error const& ex)
- {
- spdlog::error("GET FILE SIZE FAILED! Is the path valid?");
- g_pSquirrel<context>->raiseerror(sqvm, ex.what());
- return SQRESULT_ERROR;
- }
- return SQRESULT_NOTNULL;
-}
-
-// void NSDeleteFile(string file)
-ADD_SQFUNC("void", NSDeleteFile, "string file", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm);
-
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- std::string fileName = g_pSquirrel<context>->getstring(sqvm, 1);
- if (!IsPathSafe(fileName, dir))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "File name invalid ({})! Make sure it does not contain any non-ASCII character, and results in a path inside your mod's "
- "save folder.",
- fileName,
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSaveFileManager->DeleteFileAsync<context>(dir / fileName);
- return SQRESULT_NOTNULL;
-}
-
-// The param is not optional because that causes issues :)
-ADD_SQFUNC("array<string>", NS_InternalGetAllFiles, "string path", "", ScriptContext::CLIENT | ScriptContext::UI | ScriptContext::SERVER)
-{
- // depth 1 because this should always get called from Northstar.Custom
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm, 1);
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- std::string pathStr = g_pSquirrel<context>->getstring(sqvm, 1);
- fs::path path = dir;
- if (pathStr != "")
- path = dir / pathStr;
- if (!IsPathSafe(pathStr, dir))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "File name invalid ({})! Make sure it does not contain any non-ASCII character, and results in a path inside your mod's "
- "save folder.",
- pathStr,
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
- try
- {
- g_pSquirrel<context>->newarray(sqvm, 0);
- for (const auto& entry : fs::directory_iterator(path))
- {
- g_pSquirrel<context>->pushstring(sqvm, entry.path().filename().string().c_str());
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- }
- return SQRESULT_NOTNULL;
- }
- catch (std::exception ex)
- {
- spdlog::error("DIR ITERATE FAILED! Is the path valid?");
- g_pSquirrel<context>->raiseerror(sqvm, ex.what());
- return SQRESULT_ERROR;
- }
-}
-
-ADD_SQFUNC("bool", NSIsFolder, "string path", "", ScriptContext::CLIENT | ScriptContext::UI | ScriptContext::SERVER)
-{
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm);
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- std::string pathStr = g_pSquirrel<context>->getstring(sqvm, 1);
- fs::path path = dir;
- if (pathStr != "")
- path = dir / pathStr;
- if (!IsPathSafe(pathStr, dir))
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "File name invalid ({})! Make sure it does not contain any non-ASCII character, and results in a path inside your mod's "
- "save folder.",
- pathStr,
- mod->Name)
- .c_str());
- return SQRESULT_ERROR;
- }
- try
- {
- g_pSquirrel<context>->pushbool(sqvm, fs::is_directory(path));
- return SQRESULT_NOTNULL;
- }
- catch (std::exception ex)
- {
- spdlog::error("DIR READ FAILED! Is the path valid?");
- spdlog::info(path.string());
- g_pSquirrel<context>->raiseerror(sqvm, ex.what());
- return SQRESULT_ERROR;
- }
-}
-
-// side note, expensive.
-ADD_SQFUNC("int", NSGetTotalSpaceRemaining, "", "", ScriptContext::CLIENT | ScriptContext::UI | ScriptContext::SERVER)
-{
- Mod* mod = g_pSquirrel<context>->getcallingmod(sqvm);
- fs::path dir = savePath / fs::path(mod->m_ModDirectory).filename();
- g_pSquirrel<context>->pushinteger(sqvm, (MAX_FOLDER_SIZE - GetSizeOfFolder(dir)) / 1024);
- return SQRESULT_NOTNULL;
-}
-
-// ok, I'm just gonna explain what the fuck is going on here because this
-// is the pinnacle of my stupidity and I do not want to touch this ever
-// again, yet someone will eventually have to maintain this.
-template <ScriptContext context> std::string EncodeJSON(HSquirrelVM* sqvm)
-{
- // new rapidjson
- rapidjson_document doc;
- doc.SetObject();
-
- // get the SECOND param
- SQTable* table = sqvm->_stackOfCurrentFunction[2]._VAL.asTable;
- // take the table and copy it's contents over into the rapidjson_document
- EncodeJSONTable<context>(table, &doc, doc.GetAllocator());
-
- // convert JSON document to string
- rapidjson::StringBuffer buffer;
- rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
- doc.Accept(writer);
-
- // return the converted string
- return buffer.GetString();
-}
-
-ON_DLL_LOAD("engine.dll", ModSaveFFiles_Init, (CModule module))
-{
- savePath = fs::path(GetNorthstarPrefix()) / "save_data";
- g_pSaveFileManager = new SaveFileManager;
- int parm = CommandLine()->FindParm("-maxfoldersize");
- if (parm)
- MAX_FOLDER_SIZE = std::stoi(CommandLine()->GetParm(parm));
-}
-
-int GetMaxSaveFolderSize()
-{
- return MAX_FOLDER_SIZE;
-}
diff --git a/NorthstarDLL/mods/modsavefiles.h b/NorthstarDLL/mods/modsavefiles.h
deleted file mode 100644
index f9d39723..00000000
--- a/NorthstarDLL/mods/modsavefiles.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-int GetMaxSaveFolderSize();
-bool ContainsInvalidChars(std::string str);
-
-class SaveFileManager
-{
-public:
- template <ScriptContext context> void SaveFileAsync(fs::path file, std::string content);
- template <ScriptContext context> int LoadFileAsync(fs::path file);
- template <ScriptContext context> void DeleteFileAsync(fs::path file);
- // Future proofed in that if we ever get multi-threaded SSDs this code will take advantage of them.
- std::mutex fileMutex;
-
-private:
- int m_iLastRequestHandle = 0;
-};
diff --git a/NorthstarDLL/ns_version.h b/NorthstarDLL/ns_version.h
deleted file mode 100644
index d30594fb..00000000
--- a/NorthstarDLL/ns_version.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-#ifndef NORTHSTAR_VERSION
-// Turning off clang-format here so it doesn't mess with style as it needs to be this way for regex-ing with CI
-// clang-format off
-#define NORTHSTAR_VERSION 0,0,0,1
-// clang-format on
-#endif
diff --git a/NorthstarDLL/pch.h b/NorthstarDLL/pch.h
deleted file mode 100644
index b9ba0e08..00000000
--- a/NorthstarDLL/pch.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef PCH_H
-#define PCH_H
-
-#define WIN32_LEAN_AND_MEAN
-#define _CRT_SECURE_NO_WARNINGS
-#define RAPIDJSON_NOMEMBERITERATORCLASS // need this for rapidjson
-#define NOMINMAX // this too
-#define _WINSOCK_DEPRECATED_NO_WARNINGS // temp because i'm very lazy and want to use inet_addr, remove later
-#define RAPIDJSON_HAS_STDSTRING 1
-
-// add headers that you want to pre-compile here
-#include "core/memalloc.h"
-
-#include <windows.h>
-#include <psapi.h>
-#include <set>
-#include <map>
-#include <filesystem>
-#include <sstream>
-
-namespace fs = std::filesystem;
-
-#define EXPORT extern "C" __declspec(dllexport)
-
-typedef void (*callable)();
-typedef void (*callable_v)(void* v);
-
-// clang-format off
-#define assert_msg(exp, msg) assert((exp, msg))
-//clang-format on
-
-#include "core/macros.h"
-
-#include "core/structs.h"
-#include "core/math/color.h"
-
-#include "spdlog/spdlog.h"
-#include "logging/logging.h"
-#include "MinHook.h"
-#include "curl/curl.h"
-#include "core/hooks.h"
-#include "core/memory.h"
-
-#endif
diff --git a/NorthstarDLL/plugins/plugin_abi.h b/NorthstarDLL/plugins/plugin_abi.h
deleted file mode 100644
index 16b26a1c..00000000
--- a/NorthstarDLL/plugins/plugin_abi.h
+++ /dev/null
@@ -1,151 +0,0 @@
-#pragma once
-#include "squirrel/squirrelclasstypes.h"
-
-#define ABI_VERSION 3
-
-enum PluginLoadDLL
-{
- ENGINE = 0,
- CLIENT,
- SERVER
-};
-
-enum ObjectType
-{
- CONCOMMANDS = 0,
- CONVAR = 1,
-};
-
-struct SquirrelFunctions
-{
- RegisterSquirrelFuncType RegisterSquirrelFunc;
- sq_defconstType __sq_defconst;
-
- sq_compilebufferType __sq_compilebuffer;
- sq_callType __sq_call;
- sq_raiseerrorType __sq_raiseerror;
- sq_compilefileType __sq_compilefile;
-
- sq_newarrayType __sq_newarray;
- sq_arrayappendType __sq_arrayappend;
-
- sq_newtableType __sq_newtable;
- sq_newslotType __sq_newslot;
-
- sq_pushroottableType __sq_pushroottable;
- sq_pushstringType __sq_pushstring;
- sq_pushintegerType __sq_pushinteger;
- sq_pushfloatType __sq_pushfloat;
- sq_pushboolType __sq_pushbool;
- sq_pushassetType __sq_pushasset;
- sq_pushvectorType __sq_pushvector;
- sq_pushobjectType __sq_pushobject;
-
- sq_getstringType __sq_getstring;
- sq_getintegerType __sq_getinteger;
- sq_getfloatType __sq_getfloat;
- sq_getboolType __sq_getbool;
- sq_getType __sq_get;
- sq_getassetType __sq_getasset;
- sq_getuserdataType __sq_getuserdata;
- sq_getvectorType __sq_getvector;
- sq_getthisentityType __sq_getthisentity;
- sq_getobjectType __sq_getobject;
-
- sq_stackinfosType __sq_stackinfos;
-
- sq_createuserdataType __sq_createuserdata;
- sq_setuserdatatypeidType __sq_setuserdatatypeid;
- sq_getfunctionType __sq_getfunction;
-
- sq_schedule_call_externalType __sq_schedule_call_external;
-
- sq_getentityfrominstanceType __sq_getentityfrominstance;
- sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity;
-
- sq_pushnewstructinstanceType __sq_pushnewstructinstance;
- sq_sealstructslotType __sq_sealstructslot;
-};
-
-struct MessageSource
-{
- const char* file;
- const char* func;
- int line;
-};
-
-// This is a modified version of spdlog::details::log_msg
-// This is so that we can make it cross DLL boundaries
-struct LogMsg
-{
- int level;
- uint64_t timestamp;
- const char* msg;
- MessageSource source;
- int pluginHandle;
-};
-
-extern "C"
-{
- typedef void (*loggerfunc_t)(LogMsg* msg);
- typedef void (*PLUGIN_RELAY_INVITE_TYPE)(const char* invite);
- typedef void* (*CreateObjectFunc)(ObjectType type);
-
- typedef void (*PluginFnCommandCallback_t)(void* command);
- typedef void (*PluginConCommandConstructorType)(
- void* newCommand, const char* name, PluginFnCommandCallback_t callback, const char* helpString, int flags, void* parent);
- typedef void (*PluginConVarRegisterType)(
- void* pConVar,
- const char* pszName,
- const char* pszDefaultValue,
- int nFlags,
- const char* pszHelpString,
- bool bMin,
- float fMin,
- bool bMax,
- float fMax,
- void* pCallback);
- typedef void (*PluginConVarMallocType)(void* pConVarMaloc, int a2, int a3);
-}
-
-struct PluginNorthstarData
-{
- const char* version;
- HMODULE northstarModule;
- int pluginHandle;
-};
-
-struct PluginInitFuncs
-{
- loggerfunc_t logger;
- PLUGIN_RELAY_INVITE_TYPE relayInviteFunc;
- CreateObjectFunc createObject;
-};
-
-struct PluginEngineData
-{
- PluginConCommandConstructorType ConCommandConstructor;
- PluginConVarMallocType conVarMalloc;
- PluginConVarRegisterType conVarRegister;
- void* ConVar_Vtable;
- void* IConVar_Vtable;
- void* g_pCVar;
-};
-
-/// <summary> Async communication within the plugin system
-/// Due to the asynchronous nature of plugins, combined with the limitations of multi-compiler support
-/// and the custom memory allocator used by r2, is it difficult to safely get data across DLL boundaries
-/// from Northstar to plugin unless Northstar can own that memory.
-/// This means that plugins should manage their own memory and can only receive data from northstar using one of the functions below.
-/// These should be exports of the plugin DLL. If they are not exported, they will not be called.
-/// Note that it is not required to have these exports if you do not use them.
-/// </summary>
-
-// Northstar -> Plugin
-typedef void (*PLUGIN_INIT_TYPE)(PluginInitFuncs* funcs, PluginNorthstarData* data);
-typedef void (*PLUGIN_INIT_SQVM_TYPE)(SquirrelFunctions* funcs);
-typedef void (*PLUGIN_INFORM_SQVM_CREATED_TYPE)(ScriptContext context, CSquirrelVM* sqvm);
-typedef void (*PLUGIN_INFORM_SQVM_DESTROYED_TYPE)(ScriptContext context);
-
-typedef void (*PLUGIN_INFORM_DLL_LOAD_TYPE)(const char* dll, PluginEngineData* data, void* dllPtr);
-typedef void (*PLUGIN_RUNFRAME)();
diff --git a/NorthstarDLL/plugins/pluginbackend.cpp b/NorthstarDLL/plugins/pluginbackend.cpp
deleted file mode 100644
index 850394ac..00000000
--- a/NorthstarDLL/plugins/pluginbackend.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "pluginbackend.h"
-#include "plugin_abi.h"
-#include "server/serverpresence.h"
-#include "masterserver/masterserver.h"
-#include "squirrel/squirrel.h"
-#include "plugins.h"
-
-#include "core/convar/concommand.h"
-
-#include <filesystem>
-
-#define EXPORT extern "C" __declspec(dllexport)
-
-AUTOHOOK_INIT()
-
-PluginCommunicationHandler* g_pPluginCommunicationhandler;
-
-static PluginDataRequest storedRequest {PluginDataRequestType::END, (PluginRespondDataCallable) nullptr};
-
-void PluginCommunicationHandler::RunFrame()
-{
- std::lock_guard<std::mutex> lock(requestMutex);
- if (!requestQueue.empty())
- {
- storedRequest = requestQueue.front();
- switch (storedRequest.type)
- {
- default:
- spdlog::error("{} was called with invalid request type '{}'", __FUNCTION__, static_cast<int>(storedRequest.type));
- }
- requestQueue.pop();
- }
-}
-
-void PluginCommunicationHandler::PushRequest(PluginDataRequestType type, PluginRespondDataCallable func)
-{
- std::lock_guard<std::mutex> lock(requestMutex);
- requestQueue.push(PluginDataRequest {type, func});
-}
-
-void InformPluginsDLLLoad(fs::path dllPath, void* address)
-{
- std::string dllName = dllPath.filename().string();
-
- void* data = NULL;
- if (strncmp(dllName.c_str(), "engine.dll", 10) == 0)
- data = &g_pPluginCommunicationhandler->m_sEngineData;
-
- g_pPluginManager->InformDLLLoad(dllName.c_str(), data, address);
-}
diff --git a/NorthstarDLL/plugins/pluginbackend.h b/NorthstarDLL/plugins/pluginbackend.h
deleted file mode 100644
index 45cd42f3..00000000
--- a/NorthstarDLL/plugins/pluginbackend.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-#include "plugin_abi.h"
-
-#include <queue>
-#include <mutex>
-
-enum PluginDataRequestType
-{
- END = 0,
-};
-
-union PluginRespondDataCallable
-{
- // Empty for now
- void* UNUSED;
-};
-
-class PluginDataRequest
-{
-public:
- PluginDataRequestType type;
- PluginRespondDataCallable func;
- PluginDataRequest(PluginDataRequestType type, PluginRespondDataCallable func) : type(type), func(func) {}
-};
-
-class PluginCommunicationHandler
-{
-public:
- void RunFrame();
- void PushRequest(PluginDataRequestType type, PluginRespondDataCallable func);
-
-public:
- std::queue<PluginDataRequest> requestQueue = {};
- std::mutex requestMutex;
-
- PluginEngineData m_sEngineData {};
-};
-
-void InformPluginsDLLLoad(fs::path dllPath, void* address);
-extern PluginCommunicationHandler* g_pPluginCommunicationhandler;
diff --git a/NorthstarDLL/plugins/plugins.cpp b/NorthstarDLL/plugins/plugins.cpp
deleted file mode 100644
index 72b64566..00000000
--- a/NorthstarDLL/plugins/plugins.cpp
+++ /dev/null
@@ -1,340 +0,0 @@
-#include "plugins.h"
-#include "config/profile.h"
-
-#include "squirrel/squirrel.h"
-#include "plugins.h"
-#include "masterserver/masterserver.h"
-#include "core/convar/convar.h"
-#include "server/serverpresence.h"
-#include <optional>
-#include <regex>
-
-#include "util/version.h"
-#include "pluginbackend.h"
-#include "util/wininfo.h"
-#include "logging/logging.h"
-#include "dedicated/dedicated.h"
-
-PluginManager* g_pPluginManager;
-
-void freeLibrary(HMODULE hLib)
-{
- if (!FreeLibrary(hLib))
- {
- spdlog::error("There was an error while trying to free library");
- }
-}
-
-EXPORT void PLUGIN_LOG(LogMsg* msg)
-{
- spdlog::source_loc src {};
- src.filename = msg->source.file;
- src.funcname = msg->source.func;
- src.line = msg->source.line;
- auto&& logger = g_pPluginManager->m_vLoadedPlugins[msg->pluginHandle].logger;
- logger->log(src, (spdlog::level::level_enum)msg->level, msg->msg);
-}
-
-EXPORT void* CreateObject(ObjectType type)
-{
- switch (type)
- {
- case ObjectType::CONVAR:
- return (void*)new ConVar;
- case ObjectType::CONCOMMANDS:
- return (void*)new ConCommand;
- default:
- return NULL;
- }
-}
-
-std::optional<Plugin> PluginManager::LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data)
-{
-
- Plugin plugin {};
-
- std::string pathstring = path.string();
- std::wstring wpath = path.wstring();
-
- LPCWSTR wpptr = wpath.c_str();
- HMODULE datafile = LoadLibraryExW(wpptr, 0, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); // Load the DLL as a data file
- if (datafile == NULL)
- {
- NS::log::PLUGINSYS->info("Failed to load library '{}': ", std::system_category().message(GetLastError()));
- return std::nullopt;
- }
- HRSRC manifestResource = FindResourceW(datafile, MAKEINTRESOURCEW(IDR_RCDATA1), RT_RCDATA);
-
- if (manifestResource == NULL)
- {
- NS::log::PLUGINSYS->info("Could not find manifest for library '{}'", pathstring);
- freeLibrary(datafile);
- return std::nullopt;
- }
- HGLOBAL myResourceData = LoadResource(datafile, manifestResource);
- if (myResourceData == NULL)
- {
- NS::log::PLUGINSYS->error("Failed to load manifest from library '{}'", pathstring);
- freeLibrary(datafile);
- return std::nullopt;
- }
- int manifestSize = SizeofResource(datafile, manifestResource);
- std::string manifest = std::string((const char*)LockResource(myResourceData), 0, manifestSize);
- freeLibrary(datafile);
-
- rapidjson_document manifestJSON;
- manifestJSON.Parse(manifest.c_str());
-
- if (manifestJSON.HasParseError())
- {
- NS::log::PLUGINSYS->error("Manifest for '{}' was invalid", pathstring);
- return std::nullopt;
- }
- if (!manifestJSON.HasMember("name"))
- {
- NS::log::PLUGINSYS->error("'{}' is missing a name in its manifest", pathstring);
- return std::nullopt;
- }
- if (!manifestJSON.HasMember("displayname"))
- {
- NS::log::PLUGINSYS->error("'{}' is missing a displayname in its manifest", pathstring);
- return std::nullopt;
- }
- if (!manifestJSON.HasMember("description"))
- {
- NS::log::PLUGINSYS->error("'{}' is missing a description in its manifest", pathstring);
- return std::nullopt;
- }
- if (!manifestJSON.HasMember("api_version"))
- {
- NS::log::PLUGINSYS->error("'{}' is missing a api_version in its manifest", pathstring);
- return std::nullopt;
- }
- if (!manifestJSON.HasMember("version"))
- {
- NS::log::PLUGINSYS->error("'{}' is missing a version in its manifest", pathstring);
- return std::nullopt;
- }
- if (!manifestJSON.HasMember("run_on_server"))
- {
- NS::log::PLUGINSYS->error("'{}' is missing 'run_on_server' in its manifest", pathstring);
- return std::nullopt;
- }
- if (!manifestJSON.HasMember("run_on_client"))
- {
- NS::log::PLUGINSYS->error("'{}' is missing 'run_on_client' in its manifest", pathstring);
- return std::nullopt;
- }
- auto test = manifestJSON["api_version"].GetString();
- if (strcmp(manifestJSON["api_version"].GetString(), std::to_string(ABI_VERSION).c_str()))
- {
- NS::log::PLUGINSYS->error(
- "'{}' has an incompatible API version number in its manifest. Current ABI version is '{}'", pathstring, ABI_VERSION);
- return std::nullopt;
- }
- // Passed all checks, going to actually load it now
-
- HMODULE pluginLib =
- LoadLibraryExW(wpptr, 0, LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); // Load the DLL with lib folders
- if (pluginLib == NULL)
- {
- NS::log::PLUGINSYS->info("Failed to load library '{}': ", std::system_category().message(GetLastError()));
- return std::nullopt;
- }
- plugin.init = (PLUGIN_INIT_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT");
- if (plugin.init == NULL)
- {
- NS::log::PLUGINSYS->info("Library '{}' has no function 'PLUGIN_INIT'", pathstring);
- return std::nullopt;
- }
- NS::log::PLUGINSYS->info("Succesfully loaded {}", pathstring);
-
- plugin.name = manifestJSON["name"].GetString();
- plugin.displayName = manifestJSON["displayname"].GetString();
- plugin.description = manifestJSON["description"].GetString();
- plugin.api_version = manifestJSON["api_version"].GetString();
- plugin.version = manifestJSON["version"].GetString();
-
- plugin.run_on_client = manifestJSON["run_on_client"].GetBool();
- plugin.run_on_server = manifestJSON["run_on_server"].GetBool();
-
- if (!plugin.run_on_server && IsDedicatedServer())
- return std::nullopt;
-
- if (manifestJSON.HasMember("dependencyName"))
- {
- plugin.dependencyName = manifestJSON["dependencyName"].GetString();
- }
- else
- {
- plugin.dependencyName = plugin.name;
- }
-
- if (std::find_if(
- plugin.dependencyName.begin(),
- plugin.dependencyName.end(),
- [&](char c) -> bool { return !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'); }) !=
- plugin.dependencyName.end())
- {
- NS::log::PLUGINSYS->warn("Dependency string \"{}\" in {} is not valid a squirrel constant!", plugin.dependencyName, plugin.name);
- }
-
- plugin.init_sqvm_client = (PLUGIN_INIT_SQVM_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT_SQVM_CLIENT");
- plugin.init_sqvm_server = (PLUGIN_INIT_SQVM_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT_SQVM_SERVER");
- plugin.inform_sqvm_created = (PLUGIN_INFORM_SQVM_CREATED_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_SQVM_CREATED");
- plugin.inform_sqvm_destroyed = (PLUGIN_INFORM_SQVM_DESTROYED_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_SQVM_DESTROYED");
-
- plugin.inform_dll_load = (PLUGIN_INFORM_DLL_LOAD_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_DLL_LOAD");
-
- plugin.run_frame = (PLUGIN_RUNFRAME)GetProcAddress(pluginLib, "PLUGIN_RUNFRAME");
-
- plugin.handle = m_vLoadedPlugins.size();
- plugin.logger = std::make_shared<ColoredLogger>(plugin.displayName.c_str(), NS::Colors::PLUGIN);
- RegisterLogger(plugin.logger);
- NS::log::PLUGINSYS->info("Loading plugin {} version {}", plugin.displayName, plugin.version);
- m_vLoadedPlugins.push_back(plugin);
-
- plugin.init(funcs, data);
-
- return plugin;
-}
-
-inline void FindPlugins(fs::path pluginPath, std::vector<fs::path>& paths)
-{
- // ensure dirs exist
- if (!fs::exists(pluginPath) || !fs::is_directory(pluginPath))
- {
- return;
- }
-
- for (const fs::directory_entry& entry : fs::directory_iterator(pluginPath))
- {
- if (fs::is_regular_file(entry) && entry.path().extension() == ".dll")
- paths.emplace_back(entry.path());
- }
-}
-
-bool PluginManager::LoadPlugins()
-{
- if (strstr(GetCommandLineA(), "-noplugins") != NULL)
- {
- NS::log::PLUGINSYS->warn("-noplugins detected; skipping loading plugins");
- return false;
- }
-
- fs::create_directories(GetThunderstoreModFolderPath());
-
- std::vector<fs::path> paths;
-
- pluginPath = GetNorthstarPrefix() + "\\plugins";
-
- PluginNorthstarData data {};
- std::string ns_version {version};
-
- PluginInitFuncs funcs {};
- funcs.logger = PLUGIN_LOG;
- funcs.relayInviteFunc = nullptr;
- funcs.createObject = CreateObject;
-
- data.version = ns_version.c_str();
- data.northstarModule = g_NorthstarModule;
-
- fs::path libPath = fs::absolute(pluginPath + "\\lib");
- if (fs::exists(libPath) && fs::is_directory(libPath))
- AddDllDirectory(libPath.wstring().c_str());
-
- FindPlugins(pluginPath, paths);
-
- // Special case for Thunderstore mods dir
- std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath());
- // Set up regex for `AUTHOR-MOD-VERSION` pattern
- std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))");
- for (fs::directory_entry dir : thunderstoreModsDir)
- {
- fs::path pluginsDir = dir.path() / "plugins";
- // Use regex to match `AUTHOR-MOD-VERSION` pattern
- if (!std::regex_match(dir.path().string(), pattern))
- {
- spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string());
- continue; // skip loading package that doesn't match
- }
-
- fs::path libDir = fs::absolute(pluginsDir / "lib");
- if (fs::exists(libDir) && fs::is_directory(libDir))
- AddDllDirectory(libDir.wstring().c_str());
-
- FindPlugins(pluginsDir, paths);
- }
-
- if (paths.empty())
- {
- NS::log::PLUGINSYS->warn("Could not find any plugins. Skipped loading plugins");
- return false;
- }
-
- for (fs::path path : paths)
- {
- if (LoadPlugin(path, &funcs, &data))
- data.pluginHandle += 1;
- }
- return true;
-}
-
-void PluginManager::InformSQVMLoad(ScriptContext context, SquirrelFunctions* s)
-{
- for (auto plugin : m_vLoadedPlugins)
- {
- if (context == ScriptContext::CLIENT && plugin.init_sqvm_client != NULL)
- {
- plugin.init_sqvm_client(s);
- }
- else if (context == ScriptContext::SERVER && plugin.init_sqvm_server != NULL)
- {
- plugin.init_sqvm_server(s);
- }
- }
-}
-
-void PluginManager::InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm)
-{
- for (auto plugin : m_vLoadedPlugins)
- {
- if (plugin.inform_sqvm_created != NULL)
- {
- plugin.inform_sqvm_created(context, sqvm);
- }
- }
-}
-
-void PluginManager::InformSQVMDestroyed(ScriptContext context)
-{
- for (auto plugin : m_vLoadedPlugins)
- {
- if (plugin.inform_sqvm_destroyed != NULL)
- {
- plugin.inform_sqvm_destroyed(context);
- }
- }
-}
-
-void PluginManager::InformDLLLoad(const char* dll, void* data, void* dllPtr)
-{
- for (auto plugin : m_vLoadedPlugins)
- {
- if (plugin.inform_dll_load != NULL)
- {
- plugin.inform_dll_load(dll, (PluginEngineData*)data, dllPtr);
- }
- }
-}
-
-void PluginManager::RunFrame()
-{
- for (auto plugin : m_vLoadedPlugins)
- {
- if (plugin.run_frame != NULL)
- {
- plugin.run_frame();
- }
- }
-}
diff --git a/NorthstarDLL/plugins/plugins.h b/NorthstarDLL/plugins/plugins.h
deleted file mode 100644
index 4e841f27..00000000
--- a/NorthstarDLL/plugins/plugins.h
+++ /dev/null
@@ -1,59 +0,0 @@
-#pragma once
-#include "plugin_abi.h"
-
-const int IDR_RCDATA1 = 101;
-
-class Plugin
-{
-public:
- std::string name;
- std::string displayName;
- std::string dependencyName;
- std::string description;
-
- std::string api_version;
- std::string version;
-
- // For now this is just implemented as the index into the plugins array
- // Maybe a bit shit but it works
- int handle;
-
- std::shared_ptr<ColoredLogger> logger;
-
- bool run_on_client = false;
- bool run_on_server = false;
-
-public:
- PLUGIN_INIT_TYPE init;
- PLUGIN_INIT_SQVM_TYPE init_sqvm_client;
- PLUGIN_INIT_SQVM_TYPE init_sqvm_server;
- PLUGIN_INFORM_SQVM_CREATED_TYPE inform_sqvm_created;
- PLUGIN_INFORM_SQVM_DESTROYED_TYPE inform_sqvm_destroyed;
-
- PLUGIN_INFORM_DLL_LOAD_TYPE inform_dll_load;
-
- PLUGIN_RUNFRAME run_frame;
-};
-
-class PluginManager
-{
-public:
- std::vector<Plugin> m_vLoadedPlugins;
-
-public:
- bool LoadPlugins();
- std::optional<Plugin> LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data);
-
- void InformSQVMLoad(ScriptContext context, SquirrelFunctions* s);
- void InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm);
- void InformSQVMDestroyed(ScriptContext context);
-
- void InformDLLLoad(const char* dll, void* data, void* dllPtr);
-
- void RunFrame();
-
-private:
- std::string pluginPath;
-};
-
-extern PluginManager* g_pPluginManager;
diff --git a/NorthstarDLL/resource1.h b/NorthstarDLL/resource1.h
deleted file mode 100644
index bb584502..00000000
--- a/NorthstarDLL/resource1.h
+++ /dev/null
@@ -1,16 +0,0 @@
-//{{NO_DEPENDENCIES}}
-// Microsoft Visual C++ generated include file.
-// Used by resources.rc
-//
-#define IDI_ICON1 101
-
-// Next default values for new objects
-//
-#ifdef APSTUDIO_INVOKED
-#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 102
-#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1001
-#define _APS_NEXT_SYMED_VALUE 101
-#endif
-#endif
diff --git a/NorthstarDLL/resources.rc b/NorthstarDLL/resources.rc
deleted file mode 100644
index 7e996617..00000000
--- a/NorthstarDLL/resources.rc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Microsoft Visual C++ generated resource script.
-//
-#include "resource1.h"
-#include "../NorthstarDLL/ns_version.h"
-
-#define APSTUDIO_READONLY_SYMBOLS
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 2 resource.
-//
-#include "winres.h"
-
-/////////////////////////////////////////////////////////////////////////////
-#undef APSTUDIO_READONLY_SYMBOLS
-
-#ifdef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// TEXTINCLUDE
-//
-
-1 TEXTINCLUDE
-BEGIN
- "resource1.h\0"
-END
-
-2 TEXTINCLUDE
-BEGIN
- "#include ""winres.h""\r\n"
- "\0"
-END
-
-3 TEXTINCLUDE
-BEGIN
- "\r\n"
- "\0"
-END
-
-#endif // APSTUDIO_INVOKED
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Version
-//
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION NORTHSTAR_VERSION
- PRODUCTVERSION NORTHSTAR_VERSION
- FILEFLAGSMASK 0x3fL
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x40004L
- FILETYPE 0x1L
- FILESUBTYPE 0x0L
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "080904b0"
- BEGIN
- VALUE "CompanyName", "Northstar Developers"
- VALUE "FileVersion", "DEV"
- VALUE "InternalName", "Northstar.dll"
- VALUE "LegalCopyright", "Copyright (C) 2021"
- VALUE "OriginalFilename", "Northstar.dll"
- VALUE "ProductVersion", "DEV"
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x809, 1200
- END
-END
-
-/////////////////////////////////////////////////////////////////////////////
-
-
diff --git a/NorthstarDLL/scripts/client/clientchathooks.cpp b/NorthstarDLL/scripts/client/clientchathooks.cpp
deleted file mode 100644
index e084f47e..00000000
--- a/NorthstarDLL/scripts/client/clientchathooks.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "util/utils.h"
-
-#include "server/serverchathooks.h"
-#include "client/localchatwriter.h"
-
-#include <rapidjson/document.h>
-
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(CHudChat__AddGameLine, client.dll + 0x22E580,
-void, __fastcall, (void* self, const char* message, int inboxId, bool isTeam, bool isDead))
-// clang-format on
-{
- // This hook is called for each HUD, but we only want our logic to run once.
- if (self != *CHudChat::allHuds)
- return;
-
- int senderId = inboxId & CUSTOM_MESSAGE_INDEX_MASK;
- bool isAnonymous = senderId == 0;
- bool isCustom = isAnonymous || (inboxId & CUSTOM_MESSAGE_INDEX_BIT);
-
- // Type is set to 0 for non-custom messages, custom messages have a type encoded as the first byte
- int type = 0;
- const char* payload = message;
- if (isCustom)
- {
- type = message[0];
- payload = message + 1;
- }
-
- RemoveAsciiControlSequences(const_cast<char*>(message), true);
-
- SQRESULT result = g_pSquirrel<ScriptContext::CLIENT>->Call(
- "CHudChat_ProcessMessageStartThread", static_cast<int>(senderId) - 1, payload, isTeam, isDead, type);
- if (result == SQRESULT_ERROR)
- for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
- CHudChat__AddGameLine(hud, message, inboxId, isTeam, isDead);
-}
-
-ADD_SQFUNC("void", NSChatWrite, "int context, string text", "", ScriptContext::CLIENT)
-{
- int chatContext = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 1);
- const char* str = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 2);
-
- LocalChatWriter((LocalChatWriter::Context)chatContext).Write(str);
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("void", NSChatWriteRaw, "int context, string text", "", ScriptContext::CLIENT)
-{
- int chatContext = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 1);
- const char* str = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 2);
-
- LocalChatWriter((LocalChatWriter::Context)chatContext).InsertText(str);
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("void", NSChatWriteLine, "int context, string text", "", ScriptContext::CLIENT)
-{
- int chatContext = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 1);
- const char* str = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 2);
-
- LocalChatWriter((LocalChatWriter::Context)chatContext).WriteLine(str);
- return SQRESULT_NULL;
-}
-
-ON_DLL_LOAD_CLIENT("client.dll", ClientChatHooks, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/scripts/client/cursorposition.cpp b/NorthstarDLL/scripts/client/cursorposition.cpp
deleted file mode 100644
index c0e8623c..00000000
--- a/NorthstarDLL/scripts/client/cursorposition.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "util/wininfo.h"
-
-ADD_SQFUNC("vector ornull", NSGetCursorPosition, "", "", ScriptContext::UI)
-{
- RECT rcClient;
- POINT p;
- if (GetCursorPos(&p) && ScreenToClient(*g_gameHWND, &p) && GetClientRect(*g_gameHWND, &rcClient))
- {
- if (GetAncestor(GetForegroundWindow(), GA_ROOTOWNER) != *g_gameHWND)
- return SQRESULT_NULL;
-
- g_pSquirrel<context>->pushvector(
- sqvm,
- {p.x > 0 ? p.x > rcClient.right ? rcClient.right : (float)p.x : 0,
- p.y > 0 ? p.y > rcClient.bottom ? rcClient.bottom : (float)p.y : 0,
- 0});
- return SQRESULT_NOTNULL;
- }
- g_pSquirrel<context>->raiseerror(sqvm, "Failed retrieving cursor position of game window");
- return SQRESULT_ERROR;
-}
diff --git a/NorthstarDLL/scripts/client/scriptbrowserhooks.cpp b/NorthstarDLL/scripts/client/scriptbrowserhooks.cpp
deleted file mode 100644
index 86b4a356..00000000
--- a/NorthstarDLL/scripts/client/scriptbrowserhooks.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-
-AUTOHOOK_INIT()
-
-bool* bIsOriginOverlayEnabled;
-
-// clang-format off
-AUTOHOOK(OpenExternalWebBrowser, engine.dll + 0x184E40,
-void, __fastcall, (char* pUrl, char flags))
-// clang-format on
-{
- bool bIsOriginOverlayEnabledOriginal = *bIsOriginOverlayEnabled;
- if (flags & 2 && !strncmp(pUrl, "http", 4)) // custom force external browser flag
- *bIsOriginOverlayEnabled = false; // if this bool is false, game will use an external browser rather than the origin overlay one
-
- OpenExternalWebBrowser(pUrl, flags);
- *bIsOriginOverlayEnabled = bIsOriginOverlayEnabledOriginal;
-}
-
-ON_DLL_LOAD_CLIENT("engine.dll", ScriptExternalBrowserHooks, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- bIsOriginOverlayEnabled = module.Offset(0x13978255).RCast<bool*>();
-}
diff --git a/NorthstarDLL/scripts/client/scriptmainmenupromos.cpp b/NorthstarDLL/scripts/client/scriptmainmenupromos.cpp
deleted file mode 100644
index ecb47af7..00000000
--- a/NorthstarDLL/scripts/client/scriptmainmenupromos.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "masterserver/masterserver.h"
-
-// mirror this in script
-enum eMainMenuPromoDataProperty
-{
- newInfoTitle1,
- newInfoTitle2,
- newInfoTitle3,
-
- largeButtonTitle,
- largeButtonText,
- largeButtonUrl,
- largeButtonImageIndex,
-
- smallButton1Title,
- smallButton1Url,
- smallButton1ImageIndex,
-
- smallButton2Title,
- smallButton2Url,
- smallButton2ImageIndex
-};
-ADD_SQFUNC("void", NSRequestCustomMainMenuPromos, "", "", ScriptContext::UI)
-{
- g_pMasterServerManager->RequestMainMenuPromos();
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("bool", NSHasCustomMainMenuPromoData, "", "", ScriptContext::UI)
-{
- g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, g_pMasterServerManager->m_bHasMainMenuPromoData);
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("var", NSGetCustomMainMenuPromoData, "int promoDataKey", "", ScriptContext::UI)
-{
- if (!g_pMasterServerManager->m_bHasMainMenuPromoData)
- return SQRESULT_NULL;
-
- switch (g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1))
- {
- case eMainMenuPromoDataProperty::newInfoTitle1:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.newInfoTitle1.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::newInfoTitle2:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.newInfoTitle2.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::newInfoTitle3:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.newInfoTitle3.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::largeButtonTitle:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.largeButtonTitle.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::largeButtonText:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.largeButtonText.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::largeButtonUrl:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.largeButtonUrl.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::largeButtonImageIndex:
- {
- g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.largeButtonImageIndex);
- break;
- }
-
- case eMainMenuPromoDataProperty::smallButton1Title:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton1Title.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::smallButton1Url:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton1Url.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::smallButton1ImageIndex:
- {
- g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton1ImageIndex);
- break;
- }
-
- case eMainMenuPromoDataProperty::smallButton2Title:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton2Title.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::smallButton2Url:
- {
- g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton2Url.c_str());
- break;
- }
-
- case eMainMenuPromoDataProperty::smallButton2ImageIndex:
- {
- g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton2ImageIndex);
- break;
- }
- }
-
- return SQRESULT_NOTNULL;
-}
diff --git a/NorthstarDLL/scripts/client/scriptmodmenu.cpp b/NorthstarDLL/scripts/client/scriptmodmenu.cpp
deleted file mode 100644
index a88478fb..00000000
--- a/NorthstarDLL/scripts/client/scriptmodmenu.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-#include "mods/modmanager.h"
-#include "squirrel/squirrel.h"
-
-ADD_SQFUNC("array<string>", NSGetModNames, "", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- g_pSquirrel<context>->newarray(sqvm, 0);
-
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- g_pSquirrel<context>->pushstring(sqvm, mod.Name.c_str());
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- }
-
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("bool", NSIsModEnabled, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
-
- // manual lookup, not super performant but eh not a big deal
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.Name.compare(modName))
- {
- g_pSquirrel<context>->pushbool(sqvm, mod.m_bEnabled);
- return SQRESULT_NOTNULL;
- }
- }
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("void", NSSetModEnabled, "string modName, bool enabled", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
- const SQBool enabled = g_pSquirrel<context>->getbool(sqvm, 2);
-
- // manual lookup, not super performant but eh not a big deal
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.Name.compare(modName))
- {
- mod.m_bEnabled = enabled;
- return SQRESULT_NULL;
- }
- }
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("string", NSGetModDescriptionByModName, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
-
- // manual lookup, not super performant but eh not a big deal
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.Name.compare(modName))
- {
- g_pSquirrel<context>->pushstring(sqvm, mod.Description.c_str());
- return SQRESULT_NOTNULL;
- }
- }
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("string", NSGetModVersionByModName, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
-
- // manual lookup, not super performant but eh not a big deal
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.Name.compare(modName))
- {
- g_pSquirrel<context>->pushstring(sqvm, mod.Version.c_str());
- return SQRESULT_NOTNULL;
- }
- }
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("string", NSGetModDownloadLinkByModName, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
-
- // manual lookup, not super performant but eh not a big deal
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.Name.compare(modName))
- {
- g_pSquirrel<context>->pushstring(sqvm, mod.DownloadLink.c_str());
- return SQRESULT_NOTNULL;
- }
- }
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("int", NSGetModLoadPriority, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
-
- // manual lookup, not super performant but eh not a big deal
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.Name.compare(modName))
- {
- g_pSquirrel<context>->pushinteger(sqvm, mod.LoadPriority);
- return SQRESULT_NOTNULL;
- }
- }
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("bool", NSIsModRequiredOnClient, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
-
- // manual lookup, not super performant but eh not a big deal
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.Name.compare(modName))
- {
- g_pSquirrel<context>->pushbool(sqvm, mod.RequiredOnClient);
- return SQRESULT_NOTNULL;
- }
- }
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC(
- "array<string>", NSGetModConvarsByModName, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI)
-{
- const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1);
- g_pSquirrel<context>->newarray(sqvm, 0);
-
- // manual lookup, not super performant but eh not a big deal
- for (Mod& mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.Name.compare(modName))
- {
- for (ModConVar* cvar : mod.ConVars)
- {
- g_pSquirrel<context>->pushstring(sqvm, cvar->Name.c_str());
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- }
-
- return SQRESULT_NOTNULL;
- }
- }
-
- return SQRESULT_NOTNULL; // return empty array
-}
-
-ADD_SQFUNC("void", NSReloadMods, "", "", ScriptContext::UI)
-{
- g_pModManager->LoadMods();
- return SQRESULT_NULL;
-}
diff --git a/NorthstarDLL/scripts/client/scriptoriginauth.cpp b/NorthstarDLL/scripts/client/scriptoriginauth.cpp
deleted file mode 100644
index 420c4872..00000000
--- a/NorthstarDLL/scripts/client/scriptoriginauth.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "masterserver/masterserver.h"
-#include "engine/r2engine.h"
-#include "client/r2client.h"
-
-ADD_SQFUNC("bool", NSIsMasterServerAuthenticated, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->pushbool(sqvm, g_pMasterServerManager->m_bOriginAuthWithMasterServerDone);
- return SQRESULT_NOTNULL;
-}
-
-/*
-global struct MasterServerAuthResult
-{
- bool success
- string errorCode
- string errorMessage
-}
-*/
-
-ADD_SQFUNC("MasterServerAuthResult", NSGetMasterServerAuthResult, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->pushnewstructinstance(sqvm, 3);
-
- g_pSquirrel<context>->pushbool(sqvm, g_pMasterServerManager->m_bOriginAuthWithMasterServerSuccessful);
- g_pSquirrel<context>->sealstructslot(sqvm, 0);
-
- g_pSquirrel<context>->pushstring(sqvm, g_pMasterServerManager->m_sOriginAuthWithMasterServerErrorCode.c_str(), -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 1);
-
- g_pSquirrel<context>->pushstring(sqvm, g_pMasterServerManager->m_sOriginAuthWithMasterServerErrorMessage.c_str(), -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 2);
-
- return SQRESULT_NOTNULL;
-}
diff --git a/NorthstarDLL/scripts/client/scriptserverbrowser.cpp b/NorthstarDLL/scripts/client/scriptserverbrowser.cpp
deleted file mode 100644
index a142c3f4..00000000
--- a/NorthstarDLL/scripts/client/scriptserverbrowser.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "masterserver/masterserver.h"
-#include "server/auth/serverauthentication.h"
-#include "engine/r2engine.h"
-#include "client/r2client.h"
-
-// functions for viewing server browser
-
-ADD_SQFUNC("void", NSRequestServerList, "", "", ScriptContext::UI)
-{
- g_pMasterServerManager->RequestServerList();
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("bool", NSIsRequestingServerList, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->pushbool(sqvm, g_pMasterServerManager->m_bScriptRequestingServerList);
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("bool", NSMasterServerConnectionSuccessful, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->pushbool(sqvm, g_pMasterServerManager->m_bSuccessfullyConnected);
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("int", NSGetServerCount, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->pushinteger(sqvm, g_pMasterServerManager->m_vRemoteServers.size());
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("void", NSClearRecievedServerList, "", "", ScriptContext::UI)
-{
- g_pMasterServerManager->ClearServerList();
- return SQRESULT_NULL;
-}
-
-// functions for authenticating with servers
-
-ADD_SQFUNC("void", NSTryAuthWithServer, "int serverIndex, string password = ''", "", ScriptContext::UI)
-{
- SQInteger serverIndex = g_pSquirrel<context>->getinteger(sqvm, 1);
- const SQChar* password = g_pSquirrel<context>->getstring(sqvm, 2);
-
- if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size())
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "Tried to auth with server index {} when only {} servers are available",
- serverIndex,
- g_pMasterServerManager->m_vRemoteServers.size())
- .c_str());
- return SQRESULT_ERROR;
- }
-
- // send off persistent data first, don't worry about server/client stuff, since m_additionalPlayerData should only have entries when
- // we're a local server note: this seems like it could create a race condition, test later
- for (auto& pair : g_pServerAuthentication->m_PlayerAuthenticationData)
- g_pServerAuthentication->WritePersistentData(pair.first);
-
- // do auth
- g_pMasterServerManager->AuthenticateWithServer(
- g_pLocalPlayerUserID,
- g_pMasterServerManager->m_sOwnClientAuthToken,
- g_pMasterServerManager->m_vRemoteServers[serverIndex],
- (char*)password);
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("bool", NSIsAuthenticatingWithServer, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->pushbool(sqvm, g_pMasterServerManager->m_bScriptAuthenticatingWithGameServer);
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("bool", NSWasAuthSuccessful, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->pushbool(sqvm, g_pMasterServerManager->m_bSuccessfullyAuthenticatedWithGameServer);
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("void", NSConnectToAuthedServer, "", "", ScriptContext::UI)
-{
- if (!g_pMasterServerManager->m_bHasPendingConnectionInfo)
- {
- g_pSquirrel<context>->raiseerror(
- sqvm, fmt::format("Tried to connect to authed server before any pending connection info was available").c_str());
- return SQRESULT_ERROR;
- }
-
- RemoteServerConnectionInfo& info = g_pMasterServerManager->m_pendingConnectionInfo;
-
- // set auth token, then try to connect
- // i'm honestly not entirely sure how silentconnect works regarding ports and encryption so using connect for now
- g_pCVar->FindVar("serverfilter")->SetValue(info.authToken);
- Cbuf_AddText(
- Cbuf_GetCurrentPlayer(),
- fmt::format(
- "connect {}.{}.{}.{}:{}",
- info.ip.S_un.S_un_b.s_b1,
- info.ip.S_un.S_un_b.s_b2,
- info.ip.S_un.S_un_b.s_b3,
- info.ip.S_un.S_un_b.s_b4,
- info.port)
- .c_str(),
- cmd_source_t::kCommandSrcCode);
-
- g_pMasterServerManager->m_bHasPendingConnectionInfo = false;
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("void", NSTryAuthWithLocalServer, "", "", ScriptContext::UI)
-{
- // do auth request
- g_pMasterServerManager->AuthenticateWithOwnServer(g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken);
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("void", NSCompleteAuthWithLocalServer, "", "", ScriptContext::UI)
-{
- // literally just set serverfilter
- // note: this assumes we have no authdata other than our own
- if (g_pServerAuthentication->m_RemoteAuthenticationData.size())
- g_pCVar->FindVar("serverfilter")->SetValue(g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first.c_str());
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("string", NSGetAuthFailReason, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->pushstring(sqvm, g_pMasterServerManager->m_sAuthFailureReason.c_str(), -1);
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("array<ServerInfo>", NSGetGameServers, "", "", ScriptContext::UI)
-{
- g_pSquirrel<context>->newarray(sqvm, 0);
- for (size_t i = 0; i < g_pMasterServerManager->m_vRemoteServers.size(); i++)
- {
- const RemoteServerInfo& remoteServer = g_pMasterServerManager->m_vRemoteServers[i];
-
- g_pSquirrel<context>->pushnewstructinstance(sqvm, 11);
-
- // index
- g_pSquirrel<context>->pushinteger(sqvm, i);
- g_pSquirrel<context>->sealstructslot(sqvm, 0);
-
- // id
- g_pSquirrel<context>->pushstring(sqvm, remoteServer.id, -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 1);
-
- // name
- g_pSquirrel<context>->pushstring(sqvm, remoteServer.name, -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 2);
-
- // description
- g_pSquirrel<context>->pushstring(sqvm, remoteServer.description.c_str(), -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 3);
-
- // map
- g_pSquirrel<context>->pushstring(sqvm, remoteServer.map, -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 4);
-
- // playlist
- g_pSquirrel<context>->pushstring(sqvm, remoteServer.playlist, -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 5);
-
- // playerCount
- g_pSquirrel<context>->pushinteger(sqvm, remoteServer.playerCount);
- g_pSquirrel<context>->sealstructslot(sqvm, 6);
-
- // maxPlayerCount
- g_pSquirrel<context>->pushinteger(sqvm, remoteServer.maxPlayers);
- g_pSquirrel<context>->sealstructslot(sqvm, 7);
-
- // requiresPassword
- g_pSquirrel<context>->pushbool(sqvm, remoteServer.requiresPassword);
- g_pSquirrel<context>->sealstructslot(sqvm, 8);
-
- // region
- g_pSquirrel<context>->pushstring(sqvm, remoteServer.region, -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 9);
-
- // requiredMods
- g_pSquirrel<context>->newarray(sqvm);
- for (const RemoteModInfo& mod : remoteServer.requiredMods)
- {
- g_pSquirrel<context>->pushnewstructinstance(sqvm, 2);
-
- // name
- g_pSquirrel<context>->pushstring(sqvm, mod.Name.c_str(), -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 0);
-
- // version
- g_pSquirrel<context>->pushstring(sqvm, mod.Version.c_str(), -1);
- g_pSquirrel<context>->sealstructslot(sqvm, 1);
-
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- }
- g_pSquirrel<context>->sealstructslot(sqvm, 10);
-
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- }
- return SQRESULT_NOTNULL;
-}
diff --git a/NorthstarDLL/scripts/client/scriptservertoclientstringcommand.cpp b/NorthstarDLL/scripts/client/scriptservertoclientstringcommand.cpp
deleted file mode 100644
index a3a81c8a..00000000
--- a/NorthstarDLL/scripts/client/scriptservertoclientstringcommand.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "core/convar/convar.h"
-#include "core/convar/concommand.h"
-
-void ConCommand_ns_script_servertoclientstringcommand(const CCommand& arg)
-{
- if (g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM)
- g_pSquirrel<ScriptContext::CLIENT>->Call("NSClientCodeCallback_RecievedServerToClientStringCommand", arg.ArgS());
-}
-
-ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ScriptServerToClientStringCommand, ClientSquirrel, (CModule module))
-{
- RegisterConCommand(
- "ns_script_servertoclientstringcommand",
- ConCommand_ns_script_servertoclientstringcommand,
- "",
- FCVAR_CLIENTDLL | FCVAR_SERVER_CAN_EXECUTE);
-}
diff --git a/NorthstarDLL/scripts/scriptdatatables.cpp b/NorthstarDLL/scripts/scriptdatatables.cpp
deleted file mode 100644
index 87a26dca..00000000
--- a/NorthstarDLL/scripts/scriptdatatables.cpp
+++ /dev/null
@@ -1,909 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "core/filesystem/rpakfilesystem.h"
-#include "core/convar/convar.h"
-#include "dedicated/dedicated.h"
-#include "core/filesystem/filesystem.h"
-#include "core/math/vector.h"
-#include "core/tier0.h"
-#include "engine/r2engine.h"
-#include <iostream>
-#include <sstream>
-#include <map>
-#include <fstream>
-#include <filesystem>
-
-const uint64_t USERDATA_TYPE_DATATABLE = 0xFFF7FFF700000004;
-const uint64_t USERDATA_TYPE_DATATABLE_CUSTOM = 0xFFFCFFFC12345678;
-
-enum class DatatableType : int
-{
- BOOL = 0,
- INT,
- FLOAT,
- VECTOR,
- STRING,
- ASSET,
- UNK_STRING // unknown but deffo a string type
-};
-
-struct ColumnInfo
-{
- char* name;
- DatatableType type;
- int offset;
-};
-
-struct Datatable
-{
- int numColumns;
- int numRows;
- ColumnInfo* columnInfo;
- char* data; // actually data pointer
- int rowInfo;
-};
-
-ConVar* Cvar_ns_prefer_datatable_from_disk;
-
-template <ScriptContext context> Datatable* (*SQ_GetDatatableInternal)(HSquirrelVM* sqvm);
-
-struct CSVData
-{
- std::string m_sAssetName;
- std::string m_sCSVString;
- char* m_pDataBuf;
- size_t m_nDataBufSize;
-
- std::vector<char*> columns;
- std::vector<std::vector<char*>> dataPointers;
-};
-
-std::unordered_map<std::string, CSVData> CSVCache;
-
-Vector3 StringToVector(char* pString)
-{
- Vector3 vRet;
-
- int length = 0;
- while (pString[length])
- {
- if ((pString[length] == '<') || (pString[length] == '>'))
- pString[length] = '\0';
- length++;
- }
-
- int startOfFloat = 1;
- int currentIndex = 1;
-
- while (pString[currentIndex] && (pString[currentIndex] != ','))
- currentIndex++;
- pString[currentIndex] = '\0';
- vRet.x = std::stof(&pString[startOfFloat]);
- startOfFloat = ++currentIndex;
-
- while (pString[currentIndex] && (pString[currentIndex] != ','))
- currentIndex++;
- pString[currentIndex] = '\0';
- vRet.y = std::stof(&pString[startOfFloat]);
- startOfFloat = ++currentIndex;
-
- while (pString[currentIndex] && (pString[currentIndex] != ','))
- currentIndex++;
- pString[currentIndex] = '\0';
- vRet.z = std::stof(&pString[startOfFloat]);
- startOfFloat = ++currentIndex;
-
- return vRet;
-}
-
-// var function GetDataTable( asset path )
-REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- const char* pAssetName;
- g_pSquirrel<context>->getasset(sqvm, 2, &pAssetName);
-
- if (strncmp(pAssetName, "datatable/", 10))
- {
- g_pSquirrel<context>->raiseerror(sqvm, fmt::format("Asset \"{}\" doesn't start with \"datatable/\"", pAssetName).c_str());
- return SQRESULT_ERROR;
- }
- else if (!Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->LoadFile(pAssetName))
- return g_pSquirrel<context>->m_funcOriginals["GetDataTable"](sqvm);
- // either we prefer disk datatables, or we're loading a datatable that wasn't found in rpak
- else
- {
- std::string sAssetPath(fmt::format("scripts/{}", pAssetName));
-
- // first, check the cache
- if (CSVCache.find(pAssetName) != CSVCache.end())
- {
- CSVData** pUserdata = g_pSquirrel<context>->template createuserdata<CSVData*>(sqvm, sizeof(CSVData*));
- g_pSquirrel<context>->setuserdatatypeid(sqvm, -1, USERDATA_TYPE_DATATABLE_CUSTOM);
- *pUserdata = &CSVCache[pAssetName];
-
- return SQRESULT_NOTNULL;
- }
-
- // check files on disk
- // we don't use .rpak as the extension for on-disk datatables, so we need to replace .rpak with .csv in the filename we're reading
- fs::path diskAssetPath("scripts");
- if (fs::path(pAssetName).extension() == ".rpak")
- diskAssetPath /= fs::path(pAssetName).remove_filename() / (fs::path(pAssetName).stem().string() + ".csv");
- else
- diskAssetPath /= fs::path(pAssetName);
-
- std::string sDiskAssetPath(diskAssetPath.string());
- if ((*g_pFilesystem)->m_vtable2->FileExists(&(*g_pFilesystem)->m_vtable2, sDiskAssetPath.c_str(), "GAME"))
- {
- std::string sTableCSV = ReadVPKFile(sDiskAssetPath.c_str());
- if (!sTableCSV.size())
- {
- g_pSquirrel<context>->raiseerror(sqvm, fmt::format("Datatable \"{}\" is empty", pAssetName).c_str());
- return SQRESULT_ERROR;
- }
-
- // somewhat shit, but ensure we end with a newline to make parsing easier
- if (sTableCSV[sTableCSV.length() - 1] != '\n')
- sTableCSV += '\n';
-
- CSVData csv;
- csv.m_sAssetName = pAssetName;
- csv.m_sCSVString = sTableCSV;
- csv.m_nDataBufSize = sTableCSV.size();
- csv.m_pDataBuf = new char[csv.m_nDataBufSize];
- memcpy(csv.m_pDataBuf, &sTableCSV[0], csv.m_nDataBufSize);
-
- // parse the csv
- // csvs are essentially comma and newline-deliniated sets of strings for parsing, only thing we need to worry about is quoted
- // entries when we parse an element of the csv, rather than allocating an entry for it, we just convert that element to a
- // null-terminated string i.e., store the ptr to the first char of it, then make the comma that delinates it a nullchar
-
- bool bHasColumns = false;
- bool bInQuotes = false;
-
- std::vector<char*> vCurrentRow;
- char* pElemStart = csv.m_pDataBuf;
- char* pElemEnd = nullptr;
-
- for (int i = 0; i < csv.m_nDataBufSize; i++)
- {
- if (csv.m_pDataBuf[i] == '\r' && csv.m_pDataBuf[i + 1] == '\n')
- {
- if (!pElemEnd)
- pElemEnd = csv.m_pDataBuf + i;
-
- continue; // next iteration can handle the \n
- }
-
- // newline, end of a row
- if (csv.m_pDataBuf[i] == '\n')
- {
- // shouldn't have newline in string
- if (bInQuotes)
- {
- g_pSquirrel<context>->raiseerror(sqvm, "Unexpected \\n in string");
- return SQRESULT_ERROR;
- }
-
- // push last entry to current row
- if (pElemEnd)
- *pElemEnd = '\0';
- else
- csv.m_pDataBuf[i] = '\0';
-
- vCurrentRow.push_back(pElemStart);
-
- // newline, push last line to csv data and go from there
- if (!bHasColumns)
- {
- bHasColumns = true;
- csv.columns = vCurrentRow;
- }
- else
- csv.dataPointers.push_back(vCurrentRow);
-
- vCurrentRow.clear();
- // put start of current element at char after newline
- pElemStart = csv.m_pDataBuf + i + 1;
- pElemEnd = nullptr;
- }
- // we're starting or ending a quoted string
- else if (csv.m_pDataBuf[i] == '"')
- {
- // start quoted string
- if (!bInQuotes)
- {
- // shouldn't have quoted strings in column names
- if (!bHasColumns)
- {
- g_pSquirrel<context>->raiseerror(sqvm, "Unexpected \" in column name");
- return SQRESULT_ERROR;
- }
-
- bInQuotes = true;
- // put start of current element at char after string begin
- pElemStart = csv.m_pDataBuf + i + 1;
- }
- // end quoted string
- else
- {
- pElemEnd = csv.m_pDataBuf + i;
- bInQuotes = false;
- }
- }
- // don't parse commas in quotes
- else if (bInQuotes)
- {
- continue;
- }
- // comma, push new entry to current row
- else if (csv.m_pDataBuf[i] == ',')
- {
- if (pElemEnd)
- *pElemEnd = '\0';
- else
- csv.m_pDataBuf[i] = '\0';
-
- vCurrentRow.push_back(pElemStart);
- // put start of next element at char after comma
- pElemStart = csv.m_pDataBuf + i + 1;
- pElemEnd = nullptr;
- }
- }
-
- // add to cache and return
- CSVData** pUserdata = g_pSquirrel<context>->template createuserdata<CSVData*>(sqvm, sizeof(CSVData*));
- g_pSquirrel<context>->setuserdatatypeid(sqvm, -1, USERDATA_TYPE_DATATABLE_CUSTOM);
- CSVCache[pAssetName] = csv;
- *pUserdata = &CSVCache[pAssetName];
-
- return SQRESULT_NOTNULL;
- }
- // the file doesn't exist on disk, check rpak if we haven't already
- else if (Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->LoadFile(pAssetName))
- return g_pSquirrel<context>->m_funcOriginals["GetDataTable"](sqvm);
- // the file doesn't exist at all, error
- else
- {
- g_pSquirrel<context>->raiseerror(sqvm, fmt::format("Datatable {} not found", pAssetName).c_str());
- return SQRESULT_ERROR;
- }
- }
-}
-
-// int function GetDataTableColumnByName( var datatable, string columnName )
-REPLACE_SQFUNC(GetDataTableColumnByName, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableColumnByName"](sqvm);
-
- CSVData* csv = *pData;
- const char* pColumnName = g_pSquirrel<context>->getstring(sqvm, 2);
-
- for (int i = 0; i < csv->columns.size(); i++)
- {
- if (!strcmp(csv->columns[i], pColumnName))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- // column not found
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowCount( var datatable )
-REPLACE_SQFUNC(GetDataTableRowCount, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDatatableRowCount"](sqvm);
-
- CSVData* csv = *pData;
- g_pSquirrel<context>->pushinteger(sqvm, csv->dataPointers.size());
- return SQRESULT_NOTNULL;
-}
-
-// string function GetDataTableString( var datatable, int row, int col )
-REPLACE_SQFUNC(GetDataTableString, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableString"](sqvm);
-
- CSVData* csv = *pData;
- const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3);
- if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size())
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size())
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSquirrel<context>->pushstring(sqvm, csv->dataPointers[nRow][nCol], -1);
- return SQRESULT_NOTNULL;
-}
-
-// asset function GetDataTableAsset( var datatable, int row, int col )
-REPLACE_SQFUNC(GetDataTableAsset, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableAsset"](sqvm);
-
- CSVData* csv = *pData;
- const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3);
- if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size())
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size())
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSquirrel<context>->pushasset(sqvm, csv->dataPointers[nRow][nCol], -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableInt( var datatable, int row, int col )
-REPLACE_SQFUNC(GetDataTableInt, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableInt"](sqvm);
-
- CSVData* csv = *pData;
- const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3);
- if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size())
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size())
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, std::stoi(csv->dataPointers[nRow][nCol]));
- return SQRESULT_NOTNULL;
-}
-
-// float function GetDataTableFloat( var datatable, int row, int col )
-REPLACE_SQFUNC(GetDataTableFloat, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableFloat"](sqvm);
-
- CSVData* csv = *pData;
- const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3);
- if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size())
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size())
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSquirrel<context>->pushfloat(sqvm, std::stof(csv->dataPointers[nRow][nCol]));
- return SQRESULT_NOTNULL;
-}
-
-// bool function GetDataTableBool( var datatable, int row, int col )
-REPLACE_SQFUNC(GetDataTableBool, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableBool"](sqvm);
-
- CSVData* csv = *pData;
- const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3);
- if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size())
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size())
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSquirrel<context>->pushbool(sqvm, std::stoi(csv->dataPointers[nRow][nCol]));
- return SQRESULT_NOTNULL;
-}
-
-// vector function GetDataTableVector( var datatable, int row, int col )
-REPLACE_SQFUNC(GetDataTableVector, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableVector"](sqvm);
-
- CSVData* csv = *pData;
- const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3);
- if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size())
- {
- g_pSquirrel<context>->raiseerror(
- sqvm,
- fmt::format(
- "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size())
- .c_str());
- return SQRESULT_ERROR;
- }
-
- g_pSquirrel<context>->pushvector(sqvm, StringToVector(csv->dataPointers[nRow][nCol]));
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowMatchingStringValue( var datatable, int col, string value )
-REPLACE_SQFUNC(GetDataTableRowMatchingStringValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingStringValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const char* pStringVal = g_pSquirrel<context>->getstring(sqvm, 3);
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (!strcmp(csv->dataPointers[i][nCol], pStringVal))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowMatchingAssetValue( var datatable, int col, asset value )
-REPLACE_SQFUNC(GetDataTableMatchingAssetValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingAssetValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const char* pStringVal;
- g_pSquirrel<context>->getasset(sqvm, 3, &pStringVal);
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (!strcmp(csv->dataPointers[i][nCol], pStringVal))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowMatchingFloatValue( var datatable, int col, float value )
-REPLACE_SQFUNC(GetDataTableRowMatchingFloatValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingFloatValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const float flFloatVal = g_pSquirrel<context>->getfloat(sqvm, 3);
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (flFloatVal == std::stof(csv->dataPointers[i][nCol]))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowMatchingIntValue( var datatable, int col, int value )
-REPLACE_SQFUNC(GetDataTableRowMatchingIntValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingIntValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nIntVal = g_pSquirrel<context>->getinteger(sqvm, 3);
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (nIntVal == std::stoi(csv->dataPointers[i][nCol]))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowMatchingVectorValue( var datatable, int col, vector value )
-REPLACE_SQFUNC(GetDataTableRowMatchingVectorValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingVectorValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const Vector3 vVectorVal = g_pSquirrel<context>->getvector(sqvm, 3);
-
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (vVectorVal == StringToVector(csv->dataPointers[i][nCol]))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowGreaterThanOrEqualToIntValue( var datatable, int col, int value )
-REPLACE_SQFUNC(GetDataTableRowGreaterThanOrEqualToIntValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowGreaterThanOrEqualToIntValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nIntVal = g_pSquirrel<context>->getinteger(sqvm, 3);
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (nIntVal >= std::stoi(csv->dataPointers[i][nCol]))
- {
- spdlog::info("datatable not loaded");
- g_pSquirrel<context>->pushinteger(sqvm, 1);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowLessThanOrEqualToIntValue( var datatable, int col, int value )
-REPLACE_SQFUNC(GetDataTableRowLessThanOrEqualToIntValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowLessThanOrEqualToIntValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const int nIntVal = g_pSquirrel<context>->getinteger(sqvm, 3);
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (nIntVal <= std::stoi(csv->dataPointers[i][nCol]))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowGreaterThanOrEqualToFloatValue( var datatable, int col, float value )
-REPLACE_SQFUNC(GetDataTableRowGreaterThanOrEqualToFloatValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowGreaterThanOrEqualToFloatValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const float flFloatVal = g_pSquirrel<context>->getfloat(sqvm, 3);
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (flFloatVal >= std::stof(csv->dataPointers[i][nCol]))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-// int function GetDataTableRowLessThanOrEqualToFloatValue( var datatable, int col, float value )
-REPLACE_SQFUNC(GetDataTableRowLessThanOrEqualToFloatValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER))
-{
- CSVData** pData;
- uint64_t typeId;
- g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId);
-
- if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM)
- return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowLessThanOrEqualToFloatValue"](sqvm);
-
- CSVData* csv = *pData;
- int nCol = g_pSquirrel<context>->getinteger(sqvm, 2);
- const float flFloatVal = g_pSquirrel<context>->getfloat(sqvm, 3);
- for (int i = 0; i < csv->dataPointers.size(); i++)
- {
- if (flFloatVal <= std::stof(csv->dataPointers[i][nCol]))
- {
- g_pSquirrel<context>->pushinteger(sqvm, i);
- return SQRESULT_NOTNULL;
- }
- }
-
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
-}
-
-std::string DataTableToString(Datatable* datatable)
-{
- std::string sCSVString;
-
- // write columns
- bool bShouldComma = false;
- for (int i = 0; i < datatable->numColumns; i++)
- {
- if (bShouldComma)
- sCSVString += ',';
- else
- bShouldComma = true;
-
- sCSVString += datatable->columnInfo[i].name;
- }
-
- // write rows
- for (int row = 0; row < datatable->numRows; row++)
- {
- sCSVString += '\n';
-
- bool bShouldComma = false;
- for (int col = 0; col < datatable->numColumns; col++)
- {
- if (bShouldComma)
- sCSVString += ',';
- else
- bShouldComma = true;
-
- // output typed data
- ColumnInfo column = datatable->columnInfo[col];
- const void* pUntypedVal = datatable->data + column.offset + row * datatable->rowInfo;
- switch (column.type)
- {
- case DatatableType::BOOL:
- {
- sCSVString += *(bool*)pUntypedVal ? '1' : '0';
- break;
- }
-
- case DatatableType::INT:
- {
- sCSVString += std::to_string(*(int*)pUntypedVal);
- break;
- }
-
- case DatatableType::FLOAT:
- {
- sCSVString += std::to_string(*(float*)pUntypedVal);
- break;
- }
-
- case DatatableType::VECTOR:
- {
- Vector3* pVector = (Vector3*)(pUntypedVal);
- sCSVString += fmt::format("<{},{},{}>", pVector->x, pVector->y, pVector->z);
- break;
- }
-
- case DatatableType::STRING:
- case DatatableType::ASSET:
- case DatatableType::UNK_STRING:
- {
- sCSVString += fmt::format("\"{}\"", *(char**)pUntypedVal);
- break;
- }
- }
- }
- }
-
- return sCSVString;
-}
-
-void DumpDatatable(const char* pDatatablePath)
-{
- Datatable* pDatatable = (Datatable*)g_pPakLoadManager->LoadFile(pDatatablePath);
- if (!pDatatable)
- {
- spdlog::error("couldn't load datatable {} (rpak containing it may not be loaded?)", pDatatablePath);
- return;
- }
-
- std::string sOutputPath(fmt::format("{}/scripts/datatable/{}.csv", g_pModName, fs::path(pDatatablePath).stem().string()));
- std::string sDatatableContents(DataTableToString(pDatatable));
-
- fs::create_directories(fs::path(sOutputPath).remove_filename());
- std::ofstream outputStream(sOutputPath);
- outputStream.write(sDatatableContents.c_str(), sDatatableContents.size());
- outputStream.close();
-
- spdlog::info("dumped datatable {} {} to {}", pDatatablePath, (void*)pDatatable, sOutputPath);
-}
-
-void ConCommand_dump_datatable(const CCommand& args)
-{
- if (args.ArgC() < 2)
- {
- spdlog::info("usage: dump_datatable datatable/tablename.rpak");
- return;
- }
-
- DumpDatatable(args.Arg(1));
-}
-
-void ConCommand_dump_datatables(const CCommand& args)
-{
- // likely not a comprehensive list, might be missing a couple?
- static const std::vector<const char*> VANILLA_DATATABLE_PATHS = {
- "datatable/burn_meter_rewards.rpak",
- "datatable/burn_meter_store.rpak",
- "datatable/calling_cards.rpak",
- "datatable/callsign_icons.rpak",
- "datatable/camo_skins.rpak",
- "datatable/default_pilot_loadouts.rpak",
- "datatable/default_titan_loadouts.rpak",
- "datatable/faction_leaders.rpak",
- "datatable/fd_awards.rpak",
- "datatable/features_mp.rpak",
- "datatable/non_loadout_weapons.rpak",
- "datatable/pilot_abilities.rpak",
- "datatable/pilot_executions.rpak",
- "datatable/pilot_passives.rpak",
- "datatable/pilot_properties.rpak",
- "datatable/pilot_weapons.rpak",
- "datatable/pilot_weapon_features.rpak",
- "datatable/pilot_weapon_mods.rpak",
- "datatable/pilot_weapon_mods_common.rpak",
- "datatable/playlist_items.rpak",
- "datatable/titans_mp.rpak",
- "datatable/titan_abilities.rpak",
- "datatable/titan_executions.rpak",
- "datatable/titan_fd_upgrades.rpak",
- "datatable/titan_nose_art.rpak",
- "datatable/titan_passives.rpak",
- "datatable/titan_primary_mods.rpak",
- "datatable/titan_primary_mods_common.rpak",
- "datatable/titan_primary_weapons.rpak",
- "datatable/titan_properties.rpak",
- "datatable/titan_skins.rpak",
- "datatable/titan_voices.rpak",
- "datatable/unlocks_faction_level.rpak",
- "datatable/unlocks_fd_titan_level.rpak",
- "datatable/unlocks_player_level.rpak",
- "datatable/unlocks_random.rpak",
- "datatable/unlocks_titan_level.rpak",
- "datatable/unlocks_weapon_level_pilot.rpak",
- "datatable/weapon_skins.rpak",
- "datatable/xp_per_faction_level.rpak",
- "datatable/xp_per_fd_titan_level.rpak",
- "datatable/xp_per_player_level.rpak",
- "datatable/xp_per_titan_level.rpak",
- "datatable/xp_per_weapon_level.rpak",
- "datatable/faction_leaders_dropship_anims.rpak",
- "datatable/score_events.rpak",
- "datatable/startpoints.rpak",
- "datatable/sp_levels.rpak",
- "datatable/community_entries.rpak",
- "datatable/spotlight_images.rpak",
- "datatable/death_hints_mp.rpak",
- "datatable/flightpath_assets.rpak",
- "datatable/earn_meter_mp.rpak",
- "datatable/battle_chatter_voices.rpak",
- "datatable/battle_chatter.rpak",
- "datatable/titan_os_conversations.rpak",
- "datatable/faction_dialogue.rpak",
- "datatable/grunt_chatter_mp.rpak",
- "datatable/spectre_chatter_mp.rpak",
- "datatable/pain_death_sounds.rpak",
- "datatable/caller_ids_mp.rpak"};
-
- for (const char* datatable : VANILLA_DATATABLE_PATHS)
- DumpDatatable(datatable);
-}
-
-ON_DLL_LOAD_RELIESON("server.dll", ServerScriptDatatables, ServerSquirrel, (CModule module))
-{
- SQ_GetDatatableInternal<ScriptContext::SERVER> = module.Offset(0x1250f0).RCast<Datatable* (*)(HSquirrelVM*)>();
-}
-
-ON_DLL_LOAD_RELIESON("client.dll", ClientScriptDatatables, ClientSquirrel, (CModule module))
-{
- SQ_GetDatatableInternal<ScriptContext::CLIENT> = module.Offset(0x1C9070).RCast<Datatable* (*)(HSquirrelVM*)>();
- SQ_GetDatatableInternal<ScriptContext::UI> = SQ_GetDatatableInternal<ScriptContext::CLIENT>;
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", SharedScriptDataTables, ConVar, (CModule module))
-{
- Cvar_ns_prefer_datatable_from_disk = new ConVar(
- "ns_prefer_datatable_from_disk",
- IsDedicatedServer() && CommandLine()->CheckParm("-nopakdedi") ? "1" : "0",
- FCVAR_NONE,
- "whether to prefer loading datatables from disk, rather than rpak");
-
- RegisterConCommand("dump_datatables", ConCommand_dump_datatables, "dumps all datatables from a hardcoded list", FCVAR_NONE);
- RegisterConCommand("dump_datatable", ConCommand_dump_datatable, "dump a datatable", FCVAR_NONE);
-}
diff --git a/NorthstarDLL/scripts/scripthttprequesthandler.cpp b/NorthstarDLL/scripts/scripthttprequesthandler.cpp
deleted file mode 100644
index aa75127a..00000000
--- a/NorthstarDLL/scripts/scripthttprequesthandler.cpp
+++ /dev/null
@@ -1,585 +0,0 @@
-#include "scripthttprequesthandler.h"
-#include "util/version.h"
-#include "squirrel/squirrel.h"
-#include "core/tier0.h"
-
-HttpRequestHandler* g_httpRequestHandler;
-
-bool IsHttpDisabled()
-{
- const static bool bIsHttpDisabled = CommandLine()->FindParm("-disablehttprequests");
- return bIsHttpDisabled;
-}
-
-bool IsLocalHttpAllowed()
-{
- const static bool bIsLocalHttpAllowed = CommandLine()->FindParm("-allowlocalhttp");
- return bIsLocalHttpAllowed;
-}
-
-bool DisableHttpSsl()
-{
- const static bool bDisableHttpSsl = CommandLine()->FindParm("-disablehttpssl");
- return bDisableHttpSsl;
-}
-
-HttpRequestHandler::HttpRequestHandler()
-{
- // Cache the launch parameters as early as possible in order to avoid possible exploits that change them at runtime.
- IsHttpDisabled();
- IsLocalHttpAllowed();
- DisableHttpSsl();
-}
-
-void HttpRequestHandler::StartHttpRequestHandler()
-{
- if (IsRunning())
- {
- spdlog::warn("%s was called while IsRunning() is true!", __FUNCTION__);
- return;
- }
-
- m_bIsHttpRequestHandlerRunning = true;
- spdlog::info("HttpRequestHandler started.");
-}
-
-void HttpRequestHandler::StopHttpRequestHandler()
-{
- if (!IsRunning())
- {
- spdlog::warn("%s was called while IsRunning() is false", __FUNCTION__);
- return;
- }
-
- m_bIsHttpRequestHandlerRunning = false;
- spdlog::info("HttpRequestHandler stopped.");
-}
-
-bool IsHttpDestinationHostAllowed(const std::string& host, std::string& outHostname, std::string& outAddress, std::string& outPort)
-{
- CURLU* url = curl_url();
- if (!url)
- {
- spdlog::error("Failed to call curl_url() for http request.");
- return false;
- }
-
- if (curl_url_set(url, CURLUPART_URL, host.c_str(), CURLU_DEFAULT_SCHEME) != CURLUE_OK)
- {
- spdlog::error("Failed to parse destination URL for http request.");
-
- curl_url_cleanup(url);
- return false;
- }
-
- char* urlHostname = nullptr;
- if (curl_url_get(url, CURLUPART_HOST, &urlHostname, 0) != CURLUE_OK)
- {
- spdlog::error("Failed to parse hostname from destination URL for http request.");
-
- curl_url_cleanup(url);
- return false;
- }
-
- char* urlScheme = nullptr;
- if (curl_url_get(url, CURLUPART_SCHEME, &urlScheme, CURLU_DEFAULT_SCHEME) != CURLUE_OK)
- {
- spdlog::error("Failed to parse scheme from destination URL for http request.");
-
- curl_url_cleanup(url);
- curl_free(urlHostname);
- return false;
- }
-
- char* urlPort = nullptr;
- if (curl_url_get(url, CURLUPART_PORT, &urlPort, CURLU_DEFAULT_PORT) != CURLUE_OK)
- {
- spdlog::error("Failed to parse port from destination URL for http request.");
-
- curl_url_cleanup(url);
- curl_free(urlHostname);
- curl_free(urlScheme);
- return false;
- }
-
- // Resolve the hostname into an address.
- addrinfo* result;
- addrinfo hints;
- std::memset(&hints, 0, sizeof(addrinfo));
- hints.ai_family = AF_UNSPEC;
-
- if (getaddrinfo(urlHostname, urlScheme, &hints, &result) != 0)
- {
- spdlog::error("Failed to resolve http request destination {} using getaddrinfo().", urlHostname);
-
- curl_url_cleanup(url);
- curl_free(urlHostname);
- curl_free(urlScheme);
- curl_free(urlPort);
- return false;
- }
-
- bool bFoundIPv6 = false;
- sockaddr_in* sockaddr_ipv4 = nullptr;
- for (addrinfo* info = result; info; info = info->ai_next)
- {
- if (info->ai_family == AF_INET)
- {
- sockaddr_ipv4 = (sockaddr_in*)info->ai_addr;
- break;
- }
-
- bFoundIPv6 = bFoundIPv6 || info->ai_family == AF_INET6;
- }
-
- if (sockaddr_ipv4 == nullptr)
- {
- if (bFoundIPv6)
- {
- spdlog::error("Only IPv4 destinations are supported for HTTP requests. To allow IPv6, launch the game using -allowlocalhttp.");
- }
- else
- {
- spdlog::error("Failed to resolve http request destination {} into a valid IPv4 address.", urlHostname);
- }
-
- curl_free(urlHostname);
- curl_free(urlScheme);
- curl_free(urlPort);
- curl_url_cleanup(url);
-
- return false;
- }
-
- // Fast checks for private ranges of IPv4.
- // clang-format off
- {
- auto addrBytes = sockaddr_ipv4->sin_addr.S_un.S_un_b;
-
- if (addrBytes.s_b1 == 10 // 10.0.0.0 - 10.255.255.255 (Class A Private)
- || addrBytes.s_b1 == 172 && addrBytes.s_b2 >= 16 && addrBytes.s_b2 <= 31 // 172.16.0.0 - 172.31.255.255 (Class B Private)
- || addrBytes.s_b1 == 192 && addrBytes.s_b2 == 168 // 192.168.0.0 - 192.168.255.255 (Class C Private)
- || addrBytes.s_b1 == 192 && addrBytes.s_b2 == 0 && addrBytes.s_b3 == 0 // 192.0.0.0 - 192.0.0.255 (IETF Assignment)
- || addrBytes.s_b1 == 192 && addrBytes.s_b2 == 0 && addrBytes.s_b3 == 2 // 192.0.2.0 - 192.0.2.255 (TEST-NET-1)
- || addrBytes.s_b1 == 192 && addrBytes.s_b2 == 88 && addrBytes.s_b3 == 99 // 192.88.99.0 - 192.88.99.255 (IPv4-IPv6 Relay)
- || addrBytes.s_b1 == 192 && addrBytes.s_b2 >= 18 && addrBytes.s_b2 <= 19 // 192.18.0.0 - 192.19.255.255 (Internet Benchmark)
- || addrBytes.s_b1 == 192 && addrBytes.s_b2 == 51 && addrBytes.s_b3 == 100 // 192.51.100.0 - 192.51.100.255 (TEST-NET-2)
- || addrBytes.s_b1 == 203 && addrBytes.s_b2 == 0 && addrBytes.s_b3 == 113 // 203.0.113.0 - 203.0.113.255 (TEST-NET-3)
- || addrBytes.s_b1 == 169 && addrBytes.s_b2 == 254 // 169.254.00 - 169.254.255.255 (Link-local/APIPA)
- || addrBytes.s_b1 == 127 // 127.0.0.0 - 127.255.255.255 (Loopback)
- || addrBytes.s_b1 == 0 // 0.0.0.0 - 0.255.255.255 (Current network)
- || addrBytes.s_b1 == 100 && addrBytes.s_b2 >= 64 && addrBytes.s_b2 <= 127 // 100.64.0.0 - 100.127.255.255 (Shared address space)
- || sockaddr_ipv4->sin_addr.S_un.S_addr == 0xFFFFFFFF // 255.255.255.255 (Broadcast)
- || addrBytes.s_b1 >= 224 && addrBytes.s_b2 <= 239 // 224.0.0.0 - 239.255.255.255 (Multicast)
- || addrBytes.s_b1 == 233 && addrBytes.s_b2 == 252 && addrBytes.s_b3 == 0 // 233.252.0.0 - 233.252.0.255 (MCAST-TEST-NET)
- || addrBytes.s_b1 >= 240 && addrBytes.s_b4 <= 254) // 240.0.0.0 - 255.255.255.254 (Future Use Class E)
- {
- curl_free(urlHostname);
- curl_free(urlScheme);
- curl_free(urlPort);
- curl_url_cleanup(url);
-
- return false;
- }
- }
-
- // clang-format on
-
- char resolvedStr[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, &sockaddr_ipv4->sin_addr, resolvedStr, INET_ADDRSTRLEN);
-
- // Use the resolved address as the new request host.
- outHostname = urlHostname;
- outAddress = resolvedStr;
- outPort = urlPort;
-
- freeaddrinfo(result);
-
- curl_free(urlHostname);
- curl_free(urlScheme);
- curl_free(urlPort);
- curl_url_cleanup(url);
-
- return true;
-}
-
-size_t HttpCurlWriteToStringBufferCallback(char* contents, size_t size, size_t nmemb, void* userp)
-{
- ((std::string*)userp)->append((char*)contents, size * nmemb);
- return size * nmemb;
-}
-
-template <ScriptContext context> int HttpRequestHandler::MakeHttpRequest(const HttpRequest& requestParameters)
-{
- if (!IsRunning())
- {
- spdlog::warn("%s was called while IsRunning() is false!", __FUNCTION__);
- return -1;
- }
-
- if (IsHttpDisabled())
- {
- spdlog::warn("NS_InternalMakeHttpRequest called while the game is running with -disablehttprequests."
- " Please check if requests are allowed using NSIsHttpEnabled() first.");
- return -1;
- }
-
- bool bAllowLocalHttp = IsLocalHttpAllowed();
-
- // This handle will be returned to Squirrel so it can wait for the response and assign a callback for it.
- int handle = ++m_iLastRequestHandle;
-
- std::thread requestThread(
- [this, handle, requestParameters, bAllowLocalHttp]()
- {
- std::string hostname, resolvedAddress, resolvedPort;
-
- if (!bAllowLocalHttp)
- {
- if (!IsHttpDestinationHostAllowed(requestParameters.baseUrl, hostname, resolvedAddress, resolvedPort))
- {
- spdlog::warn(
- "HttpRequestHandler::MakeHttpRequest attempted to make a request to a private network. This is only allowed when "
- "running the game with -allowlocalhttp.");
- g_pSquirrel<context>->AsyncCall(
- "NSHandleFailedHttpRequest",
- handle,
- (int)0,
- "Cannot make HTTP requests to private network hosts without -allowlocalhttp. Check your console for more "
- "information.");
- return;
- }
- }
-
- CURL* curl = curl_easy_init();
- if (!curl)
- {
- spdlog::error("HttpRequestHandler::MakeHttpRequest failed to init libcurl for request.");
- g_pSquirrel<context>->AsyncCall(
- "NSHandleFailedHttpRequest", handle, static_cast<int>(CURLE_FAILED_INIT), curl_easy_strerror(CURLE_FAILED_INIT));
- return;
- }
-
- // HEAD has no body.
- if (requestParameters.method == HttpRequestMethod::HRM_HEAD)
- {
- curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
- }
-
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, HttpRequestMethod::ToString(requestParameters.method).c_str());
-
- // Only resolve to IPv4 if we don't allow private network requests.
- curl_slist* host = nullptr;
- if (!bAllowLocalHttp)
- {
- curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
- host = curl_slist_append(host, fmt::format("{}:{}:{}", hostname, resolvedPort, resolvedAddress).c_str());
- curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
- }
-
- // Ensure we only allow HTTP or HTTPS.
- curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
-
- // Allow redirects
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3L);
-
- // Check if the url already contains a query.
- // If so, we'll know to append with & instead of start with ?
- std::string queryUrl = requestParameters.baseUrl;
- bool bUrlContainsQuery = false;
-
- // If this fails, just ignore the parsing and trust what the user wants to query.
- // Probably will fail but handling it here would be annoying.
- CURLU* curlUrl = curl_url();
- if (curlUrl)
- {
- if (curl_url_set(curlUrl, CURLUPART_URL, queryUrl.c_str(), CURLU_DEFAULT_SCHEME) == CURLUE_OK)
- {
- char* currentQuery;
- if (curl_url_get(curlUrl, CURLUPART_QUERY, &currentQuery, 0) == CURLUE_OK)
- {
- if (currentQuery && std::strlen(currentQuery) != 0)
- {
- bUrlContainsQuery = true;
- }
- }
-
- curl_free(currentQuery);
- }
-
- curl_url_cleanup(curlUrl);
- }
-
- // GET requests, or POST-like requests with an empty body, can have query parameters.
- // Append them to the base url.
- if (HttpRequestMethod::CanHaveQueryParameters(requestParameters.method) &&
- !HttpRequestMethod::UsesCurlPostOptions(requestParameters.method) ||
- requestParameters.body.empty())
- {
- bool isFirstValue = true;
- for (const auto& kv : requestParameters.queryParameters)
- {
- char* key = curl_easy_escape(curl, kv.first.c_str(), kv.first.length());
-
- for (const std::string& queryValue : kv.second)
- {
- char* value = curl_easy_escape(curl, queryValue.c_str(), queryValue.length());
-
- if (isFirstValue && !bUrlContainsQuery)
- {
- queryUrl.append(fmt::format("?{}={}", key, value));
- isFirstValue = false;
- }
- else
- {
- queryUrl.append(fmt::format("&{}={}", key, value));
- }
-
- curl_free(value);
- }
-
- curl_free(key);
- }
- }
-
- // If this method uses POST-like curl options, set those and set the body.
- // The body won't be sent if it's empty anyway, meaning the query parameters above, if any, would be.
- if (HttpRequestMethod::UsesCurlPostOptions(requestParameters.method))
- {
- // Grab the body and set it as a POST field
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
-
- curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, requestParameters.body.length());
- curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, requestParameters.body.c_str());
- }
-
- // Set the full URL for this http request.
- curl_easy_setopt(curl, CURLOPT_URL, queryUrl.c_str());
-
- std::string bodyBuffer;
- std::string headerBuffer;
-
- // Set up buffers to write the response headers and body.
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpCurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &bodyBuffer);
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HttpCurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headerBuffer);
-
- // Add all the headers for the request.
- curl_slist* headers = nullptr;
-
- // Content-Type header for POST-like requests.
- if (HttpRequestMethod::UsesCurlPostOptions(requestParameters.method) && !requestParameters.body.empty())
- {
- headers = curl_slist_append(headers, fmt::format("Content-Type: {}", requestParameters.contentType).c_str());
- }
-
- for (const auto& kv : requestParameters.headers)
- {
- for (const std::string& headerValue : kv.second)
- {
- headers = curl_slist_append(headers, fmt::format("{}: {}", kv.first, headerValue).c_str());
- }
- }
-
- if (headers != nullptr)
- {
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- }
-
- // Disable SSL checks if requested by the user.
- if (DisableHttpSsl())
- {
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 0L);
- }
-
- // Enforce the Northstar user agent, unless an override was specified.
- if (requestParameters.userAgent.empty())
- {
- curl_easy_setopt(curl, CURLOPT_USERAGENT, &NSUserAgent);
- }
- else
- {
- curl_easy_setopt(curl, CURLOPT_USERAGENT, requestParameters.userAgent.c_str());
- }
-
- // Set the timeout for this request. Max 60 seconds so mods can't just spin up native threads all the time.
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, std::clamp<long>(requestParameters.timeout, 1, 60));
-
- CURLcode result = curl_easy_perform(curl);
- if (IsRunning())
- {
- if (result == CURLE_OK)
- {
- // While the curl request is OK, it could return a non success code.
- // Squirrel side will handle firing the correct callback.
- long httpCode = 0;
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
- g_pSquirrel<context>->AsyncCall(
- "NSHandleSuccessfulHttpRequest", handle, static_cast<int>(httpCode), bodyBuffer, headerBuffer);
- }
- else
- {
- // Pass CURL result code & error.
- spdlog::error(
- "curl_easy_perform() failed with code {}, error: {}", static_cast<int>(result), curl_easy_strerror(result));
-
- // If it's an SSL issue, tell the user they may disable SSL checks using -disablehttpssl.
- if (result == CURLE_PEER_FAILED_VERIFICATION || result == CURLE_SSL_CERTPROBLEM ||
- result == CURLE_SSL_INVALIDCERTSTATUS)
- {
- spdlog::error("You can try disabling SSL verifications for this issue using the -disablehttpssl launch argument. "
- "Keep in mind this is potentially dangerous!");
- }
-
- g_pSquirrel<context>->AsyncCall(
- "NSHandleFailedHttpRequest", handle, static_cast<int>(result), curl_easy_strerror(result));
- }
- }
-
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- curl_slist_free_all(host);
- });
-
- requestThread.detach();
- return handle;
-}
-
-// int NS_InternalMakeHttpRequest(int method, string baseUrl, table<string, string> headers, table<string, string> queryParams,
-// string contentType, string body, int timeout, string userAgent)
-template <ScriptContext context> SQRESULT SQ_InternalMakeHttpRequest(HSquirrelVM* sqvm)
-{
- if (!g_httpRequestHandler || !g_httpRequestHandler->IsRunning())
- {
- spdlog::warn("NS_InternalMakeHttpRequest called while the http request handler isn't running.");
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
- }
-
- if (IsHttpDisabled())
- {
- spdlog::warn("NS_InternalMakeHttpRequest called while the game is running with -disablehttprequests."
- " Please check if requests are allowed using NSIsHttpEnabled() first.");
- g_pSquirrel<context>->pushinteger(sqvm, -1);
- return SQRESULT_NOTNULL;
- }
-
- HttpRequest request;
- request.method = static_cast<HttpRequestMethod::Type>(g_pSquirrel<context>->getinteger(sqvm, 1));
- request.baseUrl = g_pSquirrel<context>->getstring(sqvm, 2);
-
- // Read the tables for headers and query parameters.
- SQTable* headerTable = sqvm->_stackOfCurrentFunction[3]._VAL.asTable;
- for (int idx = 0; idx < headerTable->_numOfNodes; ++idx)
- {
- tableNode* node = &headerTable->_nodes[idx];
-
- if (node->key._Type == OT_STRING && node->val._Type == OT_ARRAY)
- {
- SQArray* valueArray = node->val._VAL.asArray;
- std::vector<std::string> headerValues;
-
- for (int vIdx = 0; vIdx < valueArray->_usedSlots; ++vIdx)
- {
- if (valueArray->_values[vIdx]._Type == OT_STRING)
- {
- headerValues.push_back(valueArray->_values[vIdx]._VAL.asString->_val);
- }
- }
-
- request.headers[node->key._VAL.asString->_val] = headerValues;
- }
- }
-
- SQTable* queryTable = sqvm->_stackOfCurrentFunction[4]._VAL.asTable;
- for (int idx = 0; idx < queryTable->_numOfNodes; ++idx)
- {
- tableNode* node = &queryTable->_nodes[idx];
-
- if (node->key._Type == OT_STRING && node->val._Type == OT_ARRAY)
- {
- SQArray* valueArray = node->val._VAL.asArray;
- std::vector<std::string> queryValues;
-
- for (int vIdx = 0; vIdx < valueArray->_usedSlots; ++vIdx)
- {
- if (valueArray->_values[vIdx]._Type == OT_STRING)
- {
- queryValues.push_back(valueArray->_values[vIdx]._VAL.asString->_val);
- }
- }
-
- request.queryParameters[node->key._VAL.asString->_val] = queryValues;
- }
- }
-
- request.contentType = g_pSquirrel<context>->getstring(sqvm, 5);
- request.body = g_pSquirrel<context>->getstring(sqvm, 6);
- request.timeout = g_pSquirrel<context>->getinteger(sqvm, 7);
- request.userAgent = g_pSquirrel<context>->getstring(sqvm, 8);
-
- int handle = g_httpRequestHandler->MakeHttpRequest<context>(request);
- g_pSquirrel<context>->pushinteger(sqvm, handle);
- return SQRESULT_NOTNULL;
-}
-
-// bool NSIsHttpEnabled()
-template <ScriptContext context> SQRESULT SQ_IsHttpEnabled(HSquirrelVM* sqvm)
-{
- g_pSquirrel<context>->pushbool(sqvm, !IsHttpDisabled());
- return SQRESULT_NOTNULL;
-}
-
-// bool NSIsLocalHttpAllowed()
-template <ScriptContext context> SQRESULT SQ_IsLocalHttpAllowed(HSquirrelVM* sqvm)
-{
- g_pSquirrel<context>->pushbool(sqvm, IsLocalHttpAllowed());
- return SQRESULT_NOTNULL;
-}
-
-template <ScriptContext context> void HttpRequestHandler::RegisterSQFuncs()
-{
- g_pSquirrel<context>->AddFuncRegistration(
- "int",
- "NS_InternalMakeHttpRequest",
- "int method, string baseUrl, table<string, array<string> > headers, table<string, array<string> > queryParams, string contentType, "
- "string body, "
- "int timeout, string userAgent",
- "[Internal use only] Passes the HttpRequest struct fields to be reconstructed in native and used for an http request",
- SQ_InternalMakeHttpRequest<context>);
-
- g_pSquirrel<context>->AddFuncRegistration(
- "bool",
- "NSIsHttpEnabled",
- "",
- "Whether or not HTTP requests are enabled. You can opt-out by starting the game with -disablehttprequests.",
- SQ_IsHttpEnabled<context>);
-
- g_pSquirrel<context>->AddFuncRegistration(
- "bool",
- "NSIsLocalHttpAllowed",
- "",
- "Whether or not HTTP requests can be made to a private network address. You can enable this by starting the game with "
- "-allowlocalhttp.",
- SQ_IsLocalHttpAllowed<context>);
-}
-
-ON_DLL_LOAD_RELIESON("client.dll", HttpRequestHandler_ClientInit, ClientSquirrel, (CModule module))
-{
- g_httpRequestHandler->RegisterSQFuncs<ScriptContext::CLIENT>();
- g_httpRequestHandler->RegisterSQFuncs<ScriptContext::UI>();
-}
-
-ON_DLL_LOAD_RELIESON("server.dll", HttpRequestHandler_ServerInit, ServerSquirrel, (CModule module))
-{
- g_httpRequestHandler->RegisterSQFuncs<ScriptContext::SERVER>();
-}
-
-ON_DLL_LOAD("engine.dll", HttpRequestHandler_Init, (CModule module))
-{
- g_httpRequestHandler = new HttpRequestHandler;
- g_httpRequestHandler->StartHttpRequestHandler();
-}
diff --git a/NorthstarDLL/scripts/scripthttprequesthandler.h b/NorthstarDLL/scripts/scripthttprequesthandler.h
deleted file mode 100644
index f3921f4e..00000000
--- a/NorthstarDLL/scripts/scripthttprequesthandler.h
+++ /dev/null
@@ -1,130 +0,0 @@
-#pragma once
-
-enum class ScriptContext;
-
-// These definitions below should match on the Squirrel side so we can easily pass them along through a function.
-
-/**
- * Allowed methods for an HttpRequest.
- */
-namespace HttpRequestMethod
-{
- enum Type
- {
- HRM_GET = 0,
- HRM_POST = 1,
- HRM_HEAD = 2,
- HRM_PUT = 3,
- HRM_DELETE = 4,
- HRM_PATCH = 5,
- HRM_OPTIONS = 6,
- };
-
- /** Returns the HTTP string representation of the given method. */
- inline std::string ToString(HttpRequestMethod::Type method)
- {
- switch (method)
- {
- case HttpRequestMethod::HRM_GET:
- return "GET";
- case HttpRequestMethod::HRM_POST:
- return "POST";
- case HttpRequestMethod::HRM_HEAD:
- return "HEAD";
- case HttpRequestMethod::HRM_PUT:
- return "PUT";
- case HttpRequestMethod::HRM_DELETE:
- return "DELETE";
- case HttpRequestMethod::HRM_PATCH:
- return "PATCH";
- case HttpRequestMethod::HRM_OPTIONS:
- return "OPTIONS";
- default:
- return "INVALID";
- }
- }
-
- /** Whether or not the given method should be treated like a POST for curlopts. */
- bool UsesCurlPostOptions(HttpRequestMethod::Type method)
- {
- switch (method)
- {
- case HttpRequestMethod::HRM_POST:
- case HttpRequestMethod::HRM_PUT:
- case HttpRequestMethod::HRM_DELETE:
- case HttpRequestMethod::HRM_PATCH:
- return true;
- default:
- return false;
- }
- }
-
- /** Whether or not the given http request method can have query parameters in the URL. */
- bool CanHaveQueryParameters(HttpRequestMethod::Type method)
- {
- return method == HttpRequestMethod::HRM_GET || UsesCurlPostOptions(method);
- }
-}; // namespace HttpRequestMethod
-
-/** Contains data about an http request that has been queued. */
-struct HttpRequest
-{
- /** Method used for this http request. */
- HttpRequestMethod::Type method;
-
- /** Base URL of this http request. */
- std::string baseUrl;
-
- /** Headers used for this http request. Some may get overridden or ignored. */
- std::unordered_map<std::string, std::vector<std::string>> headers;
-
- /** Query parameters for this http request. */
- std::unordered_map<std::string, std::vector<std::string>> queryParameters;
-
- /** The content type of this http request. Defaults to text/plain & UTF-8 charset. */
- std::string contentType = "text/plain; charset=utf-8";
-
- /** The body of this http request. If set, will override queryParameters.*/
- std::string body;
-
- /** The timeout for the http request, in seconds. Must be between 1 and 60. */
- int timeout;
-
- /** If set, the override to use for the User-Agent header. */
- std::string userAgent;
-};
-
-/**
- * Handles making HTTP requests and sending the responses back to Squirrel.
- */
-class HttpRequestHandler
-{
-public:
- HttpRequestHandler();
-
- // Start/Stop the HTTP request handler. Right now this doesn't do much.
- void StartHttpRequestHandler();
- void StopHttpRequestHandler();
-
- // Whether or not this http request handler is currently running.
- bool IsRunning() const
- {
- return m_bIsHttpRequestHandlerRunning;
- }
-
- /**
- * Creates a new thread to execute an HTTP request.
- * @param requestParameters The parameters to use for this http request.
- * @returns The handle for the http request being sent, or -1 if the request failed.
- */
- template <ScriptContext context> int MakeHttpRequest(const HttpRequest& requestParameters);
-
- /** Registers the HTTP request Squirrel functions for the given script context. */
- template <ScriptContext context> void RegisterSQFuncs();
-
-private:
- int m_iLastRequestHandle = 0;
- std::atomic_bool m_bIsHttpRequestHandlerRunning = false;
-};
-
-extern HttpRequestHandler* g_httpRequestHandler;
diff --git a/NorthstarDLL/scripts/scriptjson.cpp b/NorthstarDLL/scripts/scriptjson.cpp
deleted file mode 100644
index 06bda6f4..00000000
--- a/NorthstarDLL/scripts/scriptjson.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-#include "squirrel/squirrel.h"
-
-#include "rapidjson/error/en.h"
-#include "rapidjson/document.h"
-#include "rapidjson/writer.h"
-#include "rapidjson/stringbuffer.h"
-
-#ifdef _MSC_VER
-#undef GetObject // fuck microsoft developers
-#endif
-
-template <ScriptContext context> void
-DecodeJsonArray(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* arr)
-{
- g_pSquirrel<context>->newarray(sqvm, 0);
-
- for (auto& itr : arr->GetArray())
- {
- switch (itr.GetType())
- {
- case rapidjson::kObjectType:
- DecodeJsonTable<context>(sqvm, &itr);
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- break;
- case rapidjson::kArrayType:
- DecodeJsonArray<context>(sqvm, &itr);
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- break;
- case rapidjson::kStringType:
- g_pSquirrel<context>->pushstring(sqvm, itr.GetString(), -1);
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- break;
- case rapidjson::kTrueType:
- case rapidjson::kFalseType:
- g_pSquirrel<context>->pushbool(sqvm, itr.GetBool());
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- break;
- case rapidjson::kNumberType:
- if (itr.IsDouble() || itr.IsFloat())
- g_pSquirrel<context>->pushfloat(sqvm, itr.GetFloat());
- else
- g_pSquirrel<context>->pushinteger(sqvm, itr.GetInt());
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- break;
- }
- }
-}
-
-template <ScriptContext context> void
-DecodeJsonTable(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj)
-{
- g_pSquirrel<context>->newtable(sqvm);
-
- for (auto itr = obj->MemberBegin(); itr != obj->MemberEnd(); itr++)
- {
- switch (itr->value.GetType())
- {
- case rapidjson::kObjectType:
- g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1);
- DecodeJsonTable<context>(
- sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr->value);
- g_pSquirrel<context>->newslot(sqvm, -3, false);
- break;
- case rapidjson::kArrayType:
- g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1);
- DecodeJsonArray<context>(
- sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr->value);
- g_pSquirrel<context>->newslot(sqvm, -3, false);
- break;
- case rapidjson::kStringType:
- g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1);
- g_pSquirrel<context>->pushstring(sqvm, itr->value.GetString(), -1);
-
- g_pSquirrel<context>->newslot(sqvm, -3, false);
- break;
- case rapidjson::kTrueType:
- case rapidjson::kFalseType:
- g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1);
- g_pSquirrel<context>->pushbool(sqvm, itr->value.GetBool());
- g_pSquirrel<context>->newslot(sqvm, -3, false);
- break;
- case rapidjson::kNumberType:
- if (itr->value.IsDouble() || itr->value.IsFloat())
- {
- g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1);
- g_pSquirrel<context>->pushfloat(sqvm, itr->value.GetFloat());
- }
- else
- {
- g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1);
- g_pSquirrel<context>->pushinteger(sqvm, itr->value.GetInt());
- }
- g_pSquirrel<context>->newslot(sqvm, -3, false);
- break;
- }
- }
-}
-
-template <ScriptContext context> void EncodeJSONTable(
- SQTable* table,
- rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj,
- rapidjson::MemoryPoolAllocator<SourceAllocator>& allocator)
-{
- for (int i = 0; i < table->_numOfNodes; i++)
- {
- tableNode* node = &table->_nodes[i];
- if (node->key._Type == OT_STRING)
- {
- rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>> newObj(rapidjson::kObjectType);
- rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>> newArray(rapidjson::kArrayType);
-
- switch (node->val._Type)
- {
- case OT_STRING:
- obj->AddMember(
- rapidjson::StringRef(node->key._VAL.asString->_val), rapidjson::StringRef(node->val._VAL.asString->_val), allocator);
- break;
- case OT_INTEGER:
- obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), node->val._VAL.asInteger, allocator);
- break;
- case OT_FLOAT:
- obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), node->val._VAL.asFloat, allocator);
- break;
- case OT_BOOL:
- if (node->val._VAL.asInteger)
- {
- obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), true, allocator);
- }
- else
- {
- obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), false, allocator);
- }
- break;
- case OT_TABLE:
- EncodeJSONTable<context>(node->val._VAL.asTable, &newObj, allocator);
- obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), newObj, allocator);
- break;
- case OT_ARRAY:
- EncodeJSONArray<context>(node->val._VAL.asArray, &newArray, allocator);
- obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), newArray, allocator);
- break;
- default:
- spdlog::warn("SQ_EncodeJSON: squirrel type {} not supported", SQTypeNameFromID(node->val._Type));
- break;
- }
- }
- }
-}
-
-template <ScriptContext context> void EncodeJSONArray(
- SQArray* arr,
- rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj,
- rapidjson::MemoryPoolAllocator<SourceAllocator>& allocator)
-{
- for (int i = 0; i < arr->_usedSlots; i++)
- {
- SQObject* node = &arr->_values[i];
-
- rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>> newObj(rapidjson::kObjectType);
- rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>> newArray(rapidjson::kArrayType);
-
- switch (node->_Type)
- {
- case OT_STRING:
- obj->PushBack(rapidjson::StringRef(node->_VAL.asString->_val), allocator);
- break;
- case OT_INTEGER:
- obj->PushBack(node->_VAL.asInteger, allocator);
- break;
- case OT_FLOAT:
- obj->PushBack(node->_VAL.asFloat, allocator);
- break;
- case OT_BOOL:
- if (node->_VAL.asInteger)
- obj->PushBack(rapidjson::StringRef("true"), allocator);
- else
- obj->PushBack(rapidjson::StringRef("false"), allocator);
- break;
- case OT_TABLE:
- EncodeJSONTable<context>(node->_VAL.asTable, &newObj, allocator);
- obj->PushBack(newObj, allocator);
- break;
- case OT_ARRAY:
- EncodeJSONArray<context>(node->_VAL.asArray, &newArray, allocator);
- obj->PushBack(newArray, allocator);
- break;
- default:
- spdlog::info("SQ encode Json type {} not supported", SQTypeNameFromID(node->_Type));
- }
- }
-}
-
-ADD_SQFUNC(
- "table",
- DecodeJSON,
- "string json, bool fatalParseErrors = false",
- "converts a json string to a squirrel table",
- ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
-{
- const char* pJson = g_pSquirrel<context>->getstring(sqvm, 1);
- const bool bFatalParseErrors = g_pSquirrel<context>->getbool(sqvm, 2);
-
- rapidjson_document doc;
- doc.Parse(pJson);
- if (doc.HasParseError())
- {
- g_pSquirrel<context>->newtable(sqvm);
-
- std::string sErrorString = fmt::format(
- "Failed parsing json file: encountered parse error \"{}\" at offset {}",
- GetParseError_En(doc.GetParseError()),
- doc.GetErrorOffset());
-
- if (bFatalParseErrors)
- {
- g_pSquirrel<context>->raiseerror(sqvm, sErrorString.c_str());
- return SQRESULT_ERROR;
- }
-
- spdlog::warn(sErrorString);
- return SQRESULT_NOTNULL;
- }
-
- DecodeJsonTable<context>(sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&doc);
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC(
- "string",
- EncodeJSON,
- "table data",
- "converts a squirrel table to a json string",
- ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
-{
- rapidjson_document doc;
- doc.SetObject();
-
- // temp until this is just the func parameter type
- HSquirrelVM* vm = (HSquirrelVM*)sqvm;
- SQTable* table = vm->_stackOfCurrentFunction[1]._VAL.asTable;
- EncodeJSONTable<context>(table, &doc, doc.GetAllocator());
-
- rapidjson::StringBuffer buffer;
- rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
- doc.Accept(writer);
- const char* pJsonString = buffer.GetString();
-
- g_pSquirrel<context>->pushstring(sqvm, pJsonString, -1);
- return SQRESULT_NOTNULL;
-}
diff --git a/NorthstarDLL/scripts/scriptjson.h b/NorthstarDLL/scripts/scriptjson.h
deleted file mode 100644
index b747106b..00000000
--- a/NorthstarDLL/scripts/scriptjson.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "rapidjson/document.h"
-#include "rapidjson/writer.h"
-#include "rapidjson/stringbuffer.h"
-
-template <ScriptContext context> void EncodeJSONTable(
- SQTable* table,
- rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj,
- rapidjson::MemoryPoolAllocator<SourceAllocator>& allocator);
-
-template <ScriptContext context> void
-DecodeJsonTable(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj);
diff --git a/NorthstarDLL/scripts/scriptutility.cpp b/NorthstarDLL/scripts/scriptutility.cpp
deleted file mode 100644
index 4b92fa02..00000000
--- a/NorthstarDLL/scripts/scriptutility.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "client/r2client.h"
-#include "engine/r2engine.h"
-
-// asset function StringToAsset( string assetName )
-ADD_SQFUNC(
- "asset",
- StringToAsset,
- "string assetName",
- "converts a given string to an asset",
- ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
-{
- g_pSquirrel<context>->pushasset(sqvm, g_pSquirrel<context>->getstring(sqvm, 1), -1);
- return SQRESULT_NOTNULL;
-}
-
-// string function NSGetLocalPlayerUID()
-ADD_SQFUNC(
- "string", NSGetLocalPlayerUID, "", "Returns the local player's uid.", ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
-{
- if (g_pLocalPlayerUserID)
- {
- g_pSquirrel<context>->pushstring(sqvm, g_pLocalPlayerUserID);
- return SQRESULT_NOTNULL;
- }
-
- return SQRESULT_NULL;
-}
diff --git a/NorthstarDLL/scripts/server/miscserverfixes.cpp b/NorthstarDLL/scripts/server/miscserverfixes.cpp
deleted file mode 100644
index 48c2c111..00000000
--- a/NorthstarDLL/scripts/server/miscserverfixes.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-
-ON_DLL_LOAD("server.dll", MiscServerFixes, (CModule module))
-{
- // nop out call to VGUI shutdown since it crashes the game when quitting from the console
- module.Offset(0x154A96).NOP(5);
-}
diff --git a/NorthstarDLL/scripts/server/miscserverscript.cpp b/NorthstarDLL/scripts/server/miscserverscript.cpp
deleted file mode 100644
index ed6e4800..00000000
--- a/NorthstarDLL/scripts/server/miscserverscript.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "masterserver/masterserver.h"
-#include "server/auth/serverauthentication.h"
-#include "dedicated/dedicated.h"
-#include "client/r2client.h"
-#include "server/r2server.h"
-
-#include <filesystem>
-
-ADD_SQFUNC("void", NSEarlyWritePlayerPersistenceForLeave, "entity player", "", ScriptContext::SERVER)
-{
- const CBasePlayer* pPlayer = g_pSquirrel<context>->template getentity<CBasePlayer>(sqvm, 1);
- if (!pPlayer)
- {
- spdlog::warn("NSEarlyWritePlayerPersistenceForLeave got null player");
-
- g_pSquirrel<context>->pushbool(sqvm, false);
- return SQRESULT_NOTNULL;
- }
-
- CBaseClient* pClient = &g_pClientArray[pPlayer->m_nPlayerIndex - 1];
- if (g_pServerAuthentication->m_PlayerAuthenticationData.find(pClient) == g_pServerAuthentication->m_PlayerAuthenticationData.end())
- {
- g_pSquirrel<context>->pushbool(sqvm, false);
- return SQRESULT_NOTNULL;
- }
-
- g_pServerAuthentication->m_PlayerAuthenticationData[pClient].needPersistenceWriteOnLeave = false;
- g_pServerAuthentication->WritePersistentData(pClient);
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC("bool", NSIsWritingPlayerPersistence, "", "", ScriptContext::SERVER)
-{
- g_pSquirrel<context>->pushbool(sqvm, g_pMasterServerManager->m_bSavingPersistentData);
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("bool", NSIsPlayerLocalPlayer, "entity player", "", ScriptContext::SERVER)
-{
- const CBasePlayer* pPlayer = g_pSquirrel<ScriptContext::SERVER>->template getentity<CBasePlayer>(sqvm, 1);
- if (!pPlayer)
- {
- spdlog::warn("NSIsPlayerLocalPlayer got null player");
-
- g_pSquirrel<context>->pushbool(sqvm, false);
- return SQRESULT_NOTNULL;
- }
-
- CBaseClient* pClient = &g_pClientArray[pPlayer->m_nPlayerIndex - 1];
- g_pSquirrel<context>->pushbool(sqvm, !strcmp(g_pLocalPlayerUserID, pClient->m_UID));
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC("bool", NSIsDedicated, "", "", ScriptContext::SERVER)
-{
- g_pSquirrel<context>->pushbool(sqvm, IsDedicatedServer());
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC(
- "bool",
- NSDisconnectPlayer,
- "entity player, string reason",
- "Disconnects the player from the server with the given reason",
- ScriptContext::SERVER)
-{
- const CBasePlayer* pPlayer = g_pSquirrel<context>->template getentity<CBasePlayer>(sqvm, 1);
- const char* reason = g_pSquirrel<context>->getstring(sqvm, 2);
-
- if (!pPlayer)
- {
- spdlog::warn("Attempted to call NSDisconnectPlayer() with null player.");
-
- g_pSquirrel<context>->pushbool(sqvm, false);
- return SQRESULT_NOTNULL;
- }
-
- // Shouldn't happen but I like sanity checks.
- CBaseClient* pClient = &g_pClientArray[pPlayer->m_nPlayerIndex - 1];
- if (!pClient)
- {
- spdlog::warn("NSDisconnectPlayer(): player entity has null CBaseClient!");
-
- g_pSquirrel<context>->pushbool(sqvm, false);
- return SQRESULT_NOTNULL;
- }
-
- if (reason)
- {
- CBaseClient__Disconnect(pClient, 1, reason);
- }
- else
- {
- CBaseClient__Disconnect(pClient, 1, "Disconnected by the server.");
- }
-
- g_pSquirrel<context>->pushbool(sqvm, true);
- return SQRESULT_NOTNULL;
-}
diff --git a/NorthstarDLL/scripts/server/scriptuserinfo.cpp b/NorthstarDLL/scripts/server/scriptuserinfo.cpp
deleted file mode 100644
index c53a9d22..00000000
--- a/NorthstarDLL/scripts/server/scriptuserinfo.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "squirrel/squirrel.h"
-#include "engine/r2engine.h"
-#include "server/r2server.h"
-
-// clang-format off
-ADD_SQFUNC("string", GetUserInfoKVString_Internal, "entity player, string key, string defaultValue = \"\"",
- "Gets the string value of a given player's userinfo convar by name", ScriptContext::SERVER)
-// clang-format on
-{
- const CBasePlayer* pPlayer = g_pSquirrel<ScriptContext::SERVER>->template getentity<CBasePlayer>(sqvm, 1);
- if (!pPlayer)
- {
- g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, "player is null");
- return SQRESULT_ERROR;
- }
-
- const char* pKey = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 2);
- const char* pDefaultValue = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 3);
-
- const char* pResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetString(pKey, pDefaultValue);
- g_pSquirrel<ScriptContext::SERVER>->pushstring(sqvm, pResult);
- return SQRESULT_NOTNULL;
-}
-
-// clang-format off
-ADD_SQFUNC("asset", GetUserInfoKVAsset_Internal, "entity player, string key, asset defaultValue = $\"\"",
- "Gets the asset value of a given player's userinfo convar by name", ScriptContext::SERVER)
-// clang-format on
-{
- const CBasePlayer* pPlayer = g_pSquirrel<ScriptContext::SERVER>->template getentity<CBasePlayer>(sqvm, 1);
- if (!pPlayer)
- {
- g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, "player is null");
- return SQRESULT_ERROR;
- }
-
- const char* pKey = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 2);
- const char* pDefaultValue;
- g_pSquirrel<ScriptContext::SERVER>->getasset(sqvm, 3, &pDefaultValue);
-
- const char* pResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetString(pKey, pDefaultValue);
- g_pSquirrel<ScriptContext::SERVER>->pushasset(sqvm, pResult);
- return SQRESULT_NOTNULL;
-}
-
-// clang-format off
-ADD_SQFUNC("int", GetUserInfoKVInt_Internal, "entity player, string key, int defaultValue = 0",
- "Gets the int value of a given player's userinfo convar by name", ScriptContext::SERVER)
-// clang-format on
-{
- const CBasePlayer* pPlayer = g_pSquirrel<ScriptContext::SERVER>->template getentity<CBasePlayer>(sqvm, 1);
- if (!pPlayer)
- {
- g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, "player is null");
- return SQRESULT_ERROR;
- }
-
- const char* pKey = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 2);
- const int iDefaultValue = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 3);
-
- const int iResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetInt(pKey, iDefaultValue);
- g_pSquirrel<ScriptContext::SERVER>->pushinteger(sqvm, iResult);
- return SQRESULT_NOTNULL;
-}
-
-// clang-format off
-ADD_SQFUNC("float", GetUserInfoKVFloat_Internal, "entity player, string key, float defaultValue = 0",
- "Gets the float value of a given player's userinfo convar by name", ScriptContext::SERVER)
-// clang-format on
-{
- const CBasePlayer* pPlayer = g_pSquirrel<ScriptContext::SERVER>->getentity<CBasePlayer>(sqvm, 1);
- if (!pPlayer)
- {
- g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, "player is null");
- return SQRESULT_ERROR;
- }
-
- const char* pKey = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 2);
- const float flDefaultValue = g_pSquirrel<ScriptContext::SERVER>->getfloat(sqvm, 3);
-
- const float flResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetFloat(pKey, flDefaultValue);
- g_pSquirrel<ScriptContext::SERVER>->pushfloat(sqvm, flResult);
- return SQRESULT_NOTNULL;
-}
-
-// clang-format off
-ADD_SQFUNC("bool", GetUserInfoKVBool_Internal, "entity player, string key, bool defaultValue = false",
- "Gets the bool value of a given player's userinfo convar by name", ScriptContext::SERVER)
-// clang-format on
-{
- const CBasePlayer* pPlayer = g_pSquirrel<ScriptContext::SERVER>->getentity<CBasePlayer>(sqvm, 1);
- if (!pPlayer)
- {
- g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, "player is null");
- return SQRESULT_ERROR;
- }
-
- const char* pKey = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 2);
- const bool bDefaultValue = g_pSquirrel<ScriptContext::SERVER>->getbool(sqvm, 3);
-
- const bool bResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetInt(pKey, bDefaultValue);
- g_pSquirrel<ScriptContext::SERVER>->pushbool(sqvm, bResult);
- return SQRESULT_NOTNULL;
-}
diff --git a/NorthstarDLL/server/alltalk.cpp b/NorthstarDLL/server/alltalk.cpp
deleted file mode 100644
index 74119309..00000000
--- a/NorthstarDLL/server/alltalk.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "core/convar/convar.h"
-#include "engine/r2engine.h"
-
-size_t __fastcall ShouldAllowAlltalk()
-{
- // this needs to return a 64 bit integer where 0 = true and 1 = false
- static ConVar* Cvar_sv_alltalk = g_pCVar->FindVar("sv_alltalk");
- if (Cvar_sv_alltalk->GetBool())
- return 0;
-
- // lobby should default to alltalk, otherwise don't allow it
- return strcmp(g_pGlobals->m_pMapName, "mp_lobby");
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ServerAllTalk, ConVar, (CModule module))
-{
- // replace strcmp function called in CClient::ProcessVoiceData with our own code that calls ShouldAllowAllTalk
- CMemoryAddress base = module.Offset(0x1085FA);
-
- base.Patch("48 B8"); // mov rax, 64 bit int
- // (uint8_t*)&ShouldAllowAlltalk doesn't work for some reason? need to make it a uint64 first
- uint64_t pShouldAllowAllTalk = reinterpret_cast<uint64_t>(ShouldAllowAlltalk);
- base.Offset(0x2).Patch((uint8_t*)&pShouldAllowAllTalk, 8);
- base.Offset(0xA).Patch("FF D0"); // call rax
-
- // nop until compare (test eax, eax)
- base.Offset(0xC).NOP(0x7);
-}
diff --git a/NorthstarDLL/server/auth/bansystem.cpp b/NorthstarDLL/server/auth/bansystem.cpp
deleted file mode 100644
index a45cde93..00000000
--- a/NorthstarDLL/server/auth/bansystem.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-#include "bansystem.h"
-#include "serverauthentication.h"
-#include "core/convar/concommand.h"
-#include "server/r2server.h"
-#include "engine/r2engine.h"
-#include "client/r2client.h"
-#include "config/profile.h"
-
-#include <filesystem>
-
-const char* BANLIST_PATH_SUFFIX = "/banlist.txt";
-const char BANLIST_COMMENT_CHAR = '#';
-
-ServerBanSystem* g_pBanSystem;
-
-void ServerBanSystem::OpenBanlist()
-{
- std::ifstream banlistStream(GetNorthstarPrefix() + "/banlist.txt");
-
- if (!banlistStream.fail())
- {
- std::string line;
- while (std::getline(banlistStream, line))
- {
- // ignore line if first char is # or line is empty
- if (line == "" || line.front() == BANLIST_COMMENT_CHAR)
- continue;
-
- // remove tabs which shouldnt be there but maybe someone did the funny
- line.erase(std::remove(line.begin(), line.end(), '\t'), line.end());
- // remove spaces to allow for spaces before uids
- line.erase(std::remove(line.begin(), line.end(), ' '), line.end());
-
- // check if line is empty to allow for newlines in the file
- if (line == "")
- continue;
-
- // for inline comments like: 123123123 #banned for unfunny
- std::string uid = line.substr(0, line.find(BANLIST_COMMENT_CHAR));
-
- m_vBannedUids.push_back(strtoull(uid.c_str(), nullptr, 10));
- }
-
- banlistStream.close();
- }
-
- // open write stream for banlist // dont do this to allow for all time access
- // m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary | std::ofstream::app);
-}
-
-void ServerBanSystem::ReloadBanlist()
-{
- std::ifstream fsBanlist(GetNorthstarPrefix() + "/banlist.txt");
-
- if (!fsBanlist.fail())
- {
- std::string line;
- // since we wanna use this as the reload func we need to clear the list
- m_vBannedUids.clear();
- while (std::getline(fsBanlist, line))
- m_vBannedUids.push_back(strtoull(line.c_str(), nullptr, 10));
-
- fsBanlist.close();
- }
-}
-
-void ServerBanSystem::ClearBanlist()
-{
- m_vBannedUids.clear();
-
- // reopen the file, don't provide std::ofstream::app so it clears on open
- m_sBanlistStream.close();
- m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary);
- m_sBanlistStream.close();
-}
-
-void ServerBanSystem::BanUID(uint64_t uid)
-{
- // checking if last char is \n to make sure uids arent getting fucked
- std::ifstream fsBanlist(GetNorthstarPrefix() + "/banlist.txt");
- std::string content((std::istreambuf_iterator<char>(fsBanlist)), (std::istreambuf_iterator<char>()));
- fsBanlist.close();
-
- m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary | std::ofstream::app);
- if (content.back() != '\n')
- m_sBanlistStream << std::endl;
-
- m_vBannedUids.push_back(uid);
- m_sBanlistStream << std::to_string(uid) << std::endl;
- m_sBanlistStream.close();
- spdlog::info("{} was banned", uid);
-}
-
-void ServerBanSystem::UnbanUID(uint64_t uid)
-{
- auto findResult = std::find(m_vBannedUids.begin(), m_vBannedUids.end(), uid);
- if (findResult == m_vBannedUids.end())
- return;
-
- m_vBannedUids.erase(findResult);
-
- std::vector<std::string> banlistText;
- std::ifstream fs_readBanlist(GetNorthstarPrefix() + "/banlist.txt");
-
- if (!fs_readBanlist.fail())
- {
- std::string line;
- while (std::getline(fs_readBanlist, line))
- {
- // support for comments and newlines added in https://github.com/R2Northstar/NorthstarLauncher/pull/227
-
- std::string modLine = line; // copy the line into a free var that we can fuck with, line will be the original
-
- // remove tabs which shouldnt be there but maybe someone did the funny
- modLine.erase(std::remove(modLine.begin(), modLine.end(), '\t'), modLine.end());
- // remove spaces to allow for spaces before uids
- modLine.erase(std::remove(modLine.begin(), modLine.end(), ' '), modLine.end());
-
- // ignore line if first char is # or empty line, just add it
- if (line.front() == BANLIST_COMMENT_CHAR || modLine == "")
- {
- banlistText.push_back(line);
- continue;
- }
-
- // for inline comments like: 123123123 #banned for unfunny
- std::string lineUid = line.substr(0, line.find(BANLIST_COMMENT_CHAR));
- // have to erase spaces or else inline comments will fuck up the uid finding
- lineUid.erase(std::remove(lineUid.begin(), lineUid.end(), '\t'), lineUid.end());
- lineUid.erase(std::remove(lineUid.begin(), lineUid.end(), ' '), lineUid.end());
-
- // if the uid in the line is the uid we wanna unban
- if (std::to_string(uid) == lineUid)
- {
- // comment the uid out
- line.insert(0, "# ");
-
- // add a comment with unban date
- // not necessary but i feel like this makes it better
- std::time_t t = std::time(0);
- std::tm* now = std::localtime(&t);
-
- std::ostringstream unbanComment;
-
- //{y}/{m}/{d} {h}:{m}
- unbanComment << " # unban date: ";
- unbanComment << now->tm_year + 1900 << "-"; // this lib is so fucking awful
- unbanComment << std::setw(2) << std::setfill('0') << now->tm_mon + 1 << "-";
- unbanComment << std::setw(2) << std::setfill('0') << now->tm_mday << " ";
- unbanComment << std::setw(2) << std::setfill('0') << now->tm_hour << ":";
- unbanComment << std::setw(2) << std::setfill('0') << now->tm_min;
-
- line.append(unbanComment.str());
- }
-
- banlistText.push_back(line);
- }
-
- fs_readBanlist.close();
- }
-
- // open write stream for banlist // without append so we clear the file
- if (m_sBanlistStream.is_open())
- m_sBanlistStream.close();
- m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary);
-
- for (std::string updatedLine : banlistText)
- m_sBanlistStream << updatedLine << std::endl;
-
- m_sBanlistStream.close();
- spdlog::info("{} was unbanned", uid);
-}
-
-bool ServerBanSystem::IsUIDAllowed(uint64_t uid)
-{
- uint64_t localPlayerUserID = strtoull(g_pLocalPlayerUserID, nullptr, 10);
- if (localPlayerUserID == uid)
- return true;
-
- ReloadBanlist(); // Reload to have up to date list on join
- return std::find(m_vBannedUids.begin(), m_vBannedUids.end(), uid) == m_vBannedUids.end();
-}
-
-void ConCommand_ban(const CCommand& args)
-{
- if (args.ArgC() < 2)
- return;
-
- for (int i = 0; i < g_pGlobals->m_nMaxClients; i++)
- {
- CBaseClient* player = &g_pClientArray[i];
-
- if (!strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1)))
- {
- g_pBanSystem->BanUID(strtoull(player->m_UID, nullptr, 10));
- CBaseClient__Disconnect(player, 1, "Banned from server");
- break;
- }
- }
-}
-
-void ConCommand_unban(const CCommand& args)
-{
- if (args.ArgC() < 2)
- return;
-
- // assumedly the player being unbanned here wasn't already connected, so don't need to iterate over players or anything
- g_pBanSystem->UnbanUID(strtoull(args.Arg(1), nullptr, 10));
-}
-
-void ConCommand_clearbanlist(const CCommand& args)
-{
- g_pBanSystem->ClearBanlist();
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", BanSystem, ConCommand, (CModule module))
-{
- g_pBanSystem = new ServerBanSystem;
- g_pBanSystem->OpenBanlist();
-
- RegisterConCommand("ban", ConCommand_ban, "bans a given player by uid or name", FCVAR_GAMEDLL);
- RegisterConCommand("unban", ConCommand_unban, "unbans a given player by uid", FCVAR_GAMEDLL);
- RegisterConCommand("clearbanlist", ConCommand_clearbanlist, "clears all uids on the banlist", FCVAR_GAMEDLL);
-}
diff --git a/NorthstarDLL/server/auth/bansystem.h b/NorthstarDLL/server/auth/bansystem.h
deleted file mode 100644
index d6ac5a4f..00000000
--- a/NorthstarDLL/server/auth/bansystem.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#include <fstream>
-
-class ServerBanSystem
-{
-private:
- std::ofstream m_sBanlistStream;
- std::vector<uint64_t> m_vBannedUids;
-
-public:
- void OpenBanlist();
- void ReloadBanlist();
- void ClearBanlist();
- void BanUID(uint64_t uid);
- void UnbanUID(uint64_t uid);
- bool IsUIDAllowed(uint64_t uid);
-};
-
-extern ServerBanSystem* g_pBanSystem;
diff --git a/NorthstarDLL/server/auth/serverauthentication.cpp b/NorthstarDLL/server/auth/serverauthentication.cpp
deleted file mode 100644
index 0d46426f..00000000
--- a/NorthstarDLL/server/auth/serverauthentication.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-#include "serverauthentication.h"
-#include "shared/exploit_fixes/ns_limits.h"
-#include "core/convar/cvar.h"
-#include "core/convar/convar.h"
-#include "masterserver/masterserver.h"
-#include "server/serverpresence.h"
-#include "engine/hoststate.h"
-#include "bansystem.h"
-#include "core/convar/concommand.h"
-#include "dedicated/dedicated.h"
-#include "config/profile.h"
-#include "core/tier0.h"
-#include "engine/r2engine.h"
-#include "client/r2client.h"
-#include "server/r2server.h"
-
-#include <fstream>
-#include <filesystem>
-#include <string>
-#include <thread>
-
-AUTOHOOK_INIT()
-
-// global vars
-ServerAuthenticationManager* g_pServerAuthentication;
-CBaseServer__RejectConnectionType CBaseServer__RejectConnection;
-
-void ServerAuthenticationManager::AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata)
-{
- std::string uidS = std::to_string(uid);
-
- RemoteAuthData newAuthData {};
- strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), uidS.c_str(), uidS.length());
- strncpy_s(newAuthData.username, sizeof(newAuthData.username), username.c_str(), username.length());
- newAuthData.pdata = new char[pdata.length()];
- newAuthData.pdataSize = pdata.length();
- memcpy(newAuthData.pdata, pdata.c_str(), newAuthData.pdataSize);
-
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
- m_RemoteAuthenticationData[token] = newAuthData;
-}
-
-void ServerAuthenticationManager::AddPlayer(CBaseClient* pPlayer, const char* pToken)
-{
- PlayerAuthenticationData additionalData;
-
- auto remoteAuthData = m_RemoteAuthenticationData.find(pToken);
- if (remoteAuthData != m_RemoteAuthenticationData.end())
- additionalData.pdataSize = remoteAuthData->second.pdataSize;
- else
- additionalData.pdataSize = PERSISTENCE_MAX_SIZE;
-
- additionalData.usingLocalPdata = pPlayer->m_iPersistenceReady == ePersistenceReady::READY_INSECURE;
-
- m_PlayerAuthenticationData.insert(std::make_pair(pPlayer, additionalData));
-}
-
-void ServerAuthenticationManager::RemovePlayer(CBaseClient* pPlayer)
-{
- if (m_PlayerAuthenticationData.count(pPlayer))
- m_PlayerAuthenticationData.erase(pPlayer);
-}
-
-bool ServerAuthenticationManager::VerifyPlayerName(const char* pAuthToken, const char* pName, char pOutVerifiedName[64])
-{
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
-
- // always use name from masterserver if available
- // use of strncpy_s here should verify that this is always nullterminated within valid buffer size
- auto authData = m_RemoteAuthenticationData.find(pAuthToken);
- if (authData != m_RemoteAuthenticationData.end() && *authData->second.username)
- strncpy_s(pOutVerifiedName, 64, authData->second.username, 63);
- else
- strncpy_s(pOutVerifiedName, 64, pName, 63);
-
- // now, check that whatever name we have is actually valid
- // first, make sure it's >1 char
- if (!*pOutVerifiedName)
- return false;
-
- // next, make sure it's within a valid range of ascii characters
- for (int i = 0; pOutVerifiedName[i]; i++)
- {
- if (pOutVerifiedName[i] < 32 || pOutVerifiedName[i] > 126)
- return false;
- }
-
- return true;
-}
-
-bool ServerAuthenticationManager::IsDuplicateAccount(CBaseClient* pPlayer, const char* pPlayerUid)
-{
- if (m_bAllowDuplicateAccounts)
- return false;
-
- bool bHasUidPlayer = false;
- for (int i = 0; i < g_pGlobals->m_nMaxClients; i++)
- if (&g_pClientArray[i] != pPlayer && !strcmp(pPlayerUid, g_pClientArray[i].m_UID))
- return true;
-
- return false;
-}
-
-bool ServerAuthenticationManager::CheckAuthentication(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken)
-{
- std::string sUid = std::to_string(iUid);
-
- // check whether this player's authentication is valid but don't actually write anything to the player, we'll do that later
- // if we don't need auth this is valid
- if (Cvar_ns_auth_allow_insecure->GetBool())
- return true;
-
- // local server that doesn't need auth (probably sp) and local player
- if (m_bStartingLocalSPGame && !strcmp(sUid.c_str(), g_pLocalPlayerUserID))
- return true;
-
- // don't allow duplicate accounts
- if (IsDuplicateAccount(pPlayer, sUid.c_str()))
- return false;
-
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
- auto authData = m_RemoteAuthenticationData.find(pAuthToken);
- if (authData != m_RemoteAuthenticationData.end() && !strcmp(sUid.c_str(), authData->second.uid))
- return true;
-
- return false;
-}
-
-void ServerAuthenticationManager::AuthenticatePlayer(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken)
-{
- // for bot players, generate a new uid
- if (pPlayer->m_bFakePlayer)
- iUid = 0; // is this a good way of doing things :clueless:
-
- std::string sUid = std::to_string(iUid);
-
- // copy uuid
- strcpy(pPlayer->m_UID, sUid.c_str());
-
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
- auto authData = m_RemoteAuthenticationData.find(pAuthToken);
- if (authData != m_RemoteAuthenticationData.end())
- {
- // if we're resetting let script handle the reset with InitPersistentData() on connect
- if (!m_bForceResetLocalPlayerPersistence || strcmp(sUid.c_str(), g_pLocalPlayerUserID))
- {
- // copy pdata into buffer
- memcpy(pPlayer->m_PersistenceBuffer, authData->second.pdata, authData->second.pdataSize);
- }
-
- // set persistent data as ready
- pPlayer->m_iPersistenceReady = ePersistenceReady::READY_REMOTE;
- }
- // we probably allow insecure at this point, but make sure not to write anyway if not insecure
- else if (Cvar_ns_auth_allow_insecure->GetBool() || pPlayer->m_bFakePlayer)
- {
- // set persistent data as ready
- // note: actual placeholder persistent data is populated in script with InitPersistentData()
- pPlayer->m_iPersistenceReady = ePersistenceReady::READY_INSECURE;
- }
-}
-
-bool ServerAuthenticationManager::RemovePlayerAuthData(CBaseClient* pPlayer)
-{
- if (!Cvar_ns_erase_auth_info->GetBool()) // keep auth data forever
- return false;
-
- // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect
- if (m_bNeedLocalAuthForNewgame && !strcmp(pPlayer->m_UID, g_pLocalPlayerUserID))
- return false;
-
- // we don't have our auth token at this point, so lookup authdata by uid
- for (auto& auth : m_RemoteAuthenticationData)
- {
- if (!strcmp(pPlayer->m_UID, auth.second.uid))
- {
- // pretty sure this is fine, since we don't iterate after the erase
- // i think if we iterated after it'd be undefined behaviour tho
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
-
- delete[] auth.second.pdata;
- m_RemoteAuthenticationData.erase(auth.first);
- return true;
- }
- }
-
- return false;
-}
-
-void ServerAuthenticationManager::WritePersistentData(CBaseClient* pPlayer)
-{
- if (pPlayer->m_iPersistenceReady == ePersistenceReady::READY_REMOTE)
- {
- g_pMasterServerManager->WritePlayerPersistentData(
- pPlayer->m_UID, (const char*)pPlayer->m_PersistenceBuffer, m_PlayerAuthenticationData[pPlayer].pdataSize);
- }
- else if (Cvar_ns_auth_allow_insecure_write->GetBool())
- {
- // todo: write pdata to disk here
- }
-}
-
-// auth hooks
-
-// store these in vars so we can use them in CBaseClient::Connect
-// this is fine because ptrs won't decay by the time we use this, just don't use it outside of calls from cbaseclient::connectclient
-char* pNextPlayerToken;
-uint64_t iNextPlayerUid;
-
-// clang-format off
-AUTOHOOK(CBaseServer__ConnectClient, engine.dll + 0x114430,
-void*,, (
- void* self,
- void* addr,
- void* a3,
- uint32_t a4,
- uint32_t a5,
- int32_t a6,
- void* a7,
- char* playerName,
- char* serverFilter,
- void* a10,
- char a11,
- void* a12,
- char a13,
- char a14,
- int64_t uid,
- uint32_t a16,
- uint32_t a17))
-// clang-format on
-{
- // auth tokens are sent with serverfilter, can't be accessed from player struct to my knowledge, so have to do this here
- pNextPlayerToken = serverFilter;
- iNextPlayerUid = uid;
-
- return CBaseServer__ConnectClient(self, addr, a3, a4, a5, a6, a7, playerName, serverFilter, a10, a11, a12, a13, a14, uid, a16, a17);
-}
-
-ConVar* Cvar_ns_allowuserclantags;
-
-// clang-format off
-AUTOHOOK(CBaseClient__Connect, engine.dll + 0x101740,
-bool,, (CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, void* a5, char pDisconnectReason[256], void* a7))
-// clang-format on
-{
- const char* pAuthenticationFailure = nullptr;
- char pVerifiedName[64];
-
- if (!bFakePlayer)
- {
- if (!g_pServerAuthentication->VerifyPlayerName(pNextPlayerToken, pName, pVerifiedName))
- pAuthenticationFailure = "Invalid Name.";
- else if (!g_pBanSystem->IsUIDAllowed(iNextPlayerUid))
- pAuthenticationFailure = "Banned From server.";
- else if (!g_pServerAuthentication->CheckAuthentication(self, iNextPlayerUid, pNextPlayerToken))
- pAuthenticationFailure = "Authentication Failed.";
- }
- else // need to copy name for bots still
- strncpy_s(pVerifiedName, pName, 63);
-
- if (pAuthenticationFailure)
- {
- spdlog::info("{}'s (uid {}) connection was rejected: \"{}\"", pName, iNextPlayerUid, pAuthenticationFailure);
-
- strncpy_s(pDisconnectReason, 256, pAuthenticationFailure, 255);
- return false;
- }
-
- // try to actually connect the player
- if (!CBaseClient__Connect(self, pVerifiedName, pNetChannel, bFakePlayer, a5, pDisconnectReason, a7))
- return false;
-
- // we already know this player's authentication data is legit, actually write it to them now
- g_pServerAuthentication->AuthenticatePlayer(self, iNextPlayerUid, pNextPlayerToken);
-
- g_pServerAuthentication->AddPlayer(self, pNextPlayerToken);
- g_pServerLimits->AddPlayer(self);
-
- return true;
-}
-
-// clang-format off
-AUTOHOOK(CBaseClient__ActivatePlayer, engine.dll + 0x100F80,
-void,, (CBaseClient* self))
-// clang-format on
-{
- // if we're authed, write our persistent data
- // RemovePlayerAuthData returns true if it removed successfully, i.e. on first call only, and we only want to write on >= second call
- // (since this func is called on map loads)
- if (self->m_iPersistenceReady >= ePersistenceReady::READY && !g_pServerAuthentication->RemovePlayerAuthData(self))
- {
- g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = false;
- g_pServerAuthentication->WritePersistentData(self);
- g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size());
- }
-
- CBaseClient__ActivatePlayer(self);
-}
-
-// clang-format off
-AUTOHOOK(_CBaseClient__Disconnect, engine.dll + 0x1012C0,
-void,, (CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...))
-// clang-format on
-{
- // have to manually format message because can't pass varargs to original func
- char buf[1024];
-
- va_list va;
- va_start(va, pReason);
- vsprintf(buf, pReason, va);
- va_end(va);
-
- // this reason is used while connecting to a local server, hacky, but just ignore it
- if (strcmp(pReason, "Connection closing"))
- {
- spdlog::info("Player {} disconnected: \"{}\"", self->m_Name, buf);
-
- // dcing, write persistent data
- if (g_pServerAuthentication->m_PlayerAuthenticationData[self].needPersistenceWriteOnLeave)
- g_pServerAuthentication->WritePersistentData(self);
-
- memset(self->m_PersistenceBuffer, 0, g_pServerAuthentication->m_PlayerAuthenticationData[self].pdataSize);
- g_pServerAuthentication->RemovePlayerAuthData(self); // won't do anything 99% of the time, but just in case
-
- g_pServerAuthentication->RemovePlayer(self);
- g_pServerLimits->RemovePlayer(self);
- }
-
- g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size());
-
- _CBaseClient__Disconnect(self, unknownButAlways1, buf);
-}
-
-void ConCommand_ns_resetpersistence(const CCommand& args)
-{
- if (*g_pServerState == server_state_t::ss_active)
- {
- spdlog::error("ns_resetpersistence must be entered from the main menu");
- return;
- }
-
- spdlog::info("resetting persistence on next lobby load...");
- g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = true;
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, (ConCommand, ConVar), (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- g_pServerAuthentication = new ServerAuthenticationManager;
-
- g_pServerAuthentication->Cvar_ns_erase_auth_info =
- new ConVar("ns_erase_auth_info", "1", FCVAR_GAMEDLL, "Whether auth info should be erased from this server on disconnect or crash");
- g_pServerAuthentication->Cvar_ns_auth_allow_insecure =
- new ConVar("ns_auth_allow_insecure", "0", FCVAR_GAMEDLL, "Whether this server will allow unauthenicated players to connect");
- g_pServerAuthentication->Cvar_ns_auth_allow_insecure_write = new ConVar(
- "ns_auth_allow_insecure_write",
- "0",
- FCVAR_GAMEDLL,
- "Whether the pdata of unauthenticated clients will be written to disk when changed");
-
- RegisterConCommand(
- "ns_resetpersistence", ConCommand_ns_resetpersistence, "resets your pdata when you next enter the lobby", FCVAR_NONE);
-
- // patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token
- module.Offset(0x114655).Patch("EB");
-
- // patch to disable fairfight marking players as cheaters and kicking them
- module.Offset(0x101012).Patch("E9 90 00");
-
- CBaseServer__RejectConnection = module.Offset(0x1182E0).RCast<CBaseServer__RejectConnectionType>();
-
- if (CommandLine()->CheckParm("-allowdupeaccounts"))
- {
- // patch to allow same of multiple account
- module.Offset(0x114510).Patch("EB");
-
- g_pServerAuthentication->m_bAllowDuplicateAccounts = true;
- }
-}
diff --git a/NorthstarDLL/server/auth/serverauthentication.h b/NorthstarDLL/server/auth/serverauthentication.h
deleted file mode 100644
index 996d20e1..00000000
--- a/NorthstarDLL/server/auth/serverauthentication.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#pragma once
-#include "core/convar/convar.h"
-#include "engine/r2engine.h"
-#include <unordered_map>
-#include <string>
-
-struct RemoteAuthData
-{
- char uid[33];
- char username[64];
-
- // pdata
- char* pdata;
- size_t pdataSize;
-};
-
-struct PlayerAuthenticationData
-{
- bool usingLocalPdata;
- size_t pdataSize;
- bool needPersistenceWriteOnLeave = true;
-};
-
-typedef int64_t (*CBaseServer__RejectConnectionType)(void* a1, unsigned int a2, void* a3, const char* a4, ...);
-extern CBaseServer__RejectConnectionType CBaseServer__RejectConnection;
-
-class ServerAuthenticationManager
-{
-public:
- ConVar* Cvar_ns_erase_auth_info;
- ConVar* Cvar_ns_auth_allow_insecure;
- ConVar* Cvar_ns_auth_allow_insecure_write;
-
- std::mutex m_AuthDataMutex;
- std::unordered_map<std::string, RemoteAuthData> m_RemoteAuthenticationData;
- std::unordered_map<CBaseClient*, PlayerAuthenticationData> m_PlayerAuthenticationData;
-
- bool m_bAllowDuplicateAccounts = false;
- bool m_bNeedLocalAuthForNewgame = false;
- bool m_bForceResetLocalPlayerPersistence = false;
- bool m_bStartingLocalSPGame = false;
-
-public:
- void AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata);
-
- void AddPlayer(CBaseClient* pPlayer, const char* pAuthToken);
- void RemovePlayer(CBaseClient* pPlayer);
-
- bool VerifyPlayerName(const char* pAuthToken, const char* pName, char pOutVerifiedName[64]);
- bool IsDuplicateAccount(CBaseClient* pPlayer, const char* pUid);
- bool CheckAuthentication(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken);
-
- void AuthenticatePlayer(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken);
- bool RemovePlayerAuthData(CBaseClient* pPlayer);
- void WritePersistentData(CBaseClient* pPlayer);
-};
-
-extern ServerAuthenticationManager* g_pServerAuthentication;
diff --git a/NorthstarDLL/server/buildainfile.cpp b/NorthstarDLL/server/buildainfile.cpp
deleted file mode 100644
index a7f59961..00000000
--- a/NorthstarDLL/server/buildainfile.cpp
+++ /dev/null
@@ -1,395 +0,0 @@
-#include "core/convar/convar.h"
-#include "engine/hoststate.h"
-#include "engine/r2engine.h"
-
-#include <fstream>
-#include <filesystem>
-
-AUTOHOOK_INIT()
-
-const int AINET_VERSION_NUMBER = 57;
-const int AINET_SCRIPT_VERSION_NUMBER = 21;
-const int PLACEHOLDER_CRC = 0;
-const int MAX_HULLS = 5;
-
-#pragma pack(push, 1)
-struct CAI_NodeLink
-{
- short srcId;
- short destId;
- bool hulls[MAX_HULLS];
- char unk0;
- char unk1; // maps => unk0 on disk
- char unk2[5];
- int64_t flags;
-};
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-struct CAI_NodeLinkDisk
-{
- short srcId;
- short destId;
- char unk0;
- bool hulls[MAX_HULLS];
-};
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-struct CAI_Node
-{
- int index; // not present on disk
- float x;
- float y;
- float z;
- float hulls[MAX_HULLS];
- float yaw;
-
- int unk0; // always 2 in buildainfile, maps directly to unk0 in disk struct
- int unk1; // maps directly to unk1 in disk struct
- int unk2[MAX_HULLS]; // maps directly to unk2 in disk struct, despite being ints rather than shorts
-
- // view server.dll+393672 for context and death wish
- char unk3[MAX_HULLS]; // hell on earth, should map to unk3 on disk
- char pad[3]; // aligns next bytes
- float unk4[MAX_HULLS]; // i have no fucking clue, calculated using some kind of demon hell function float magic
-
- CAI_NodeLink** links;
- char unk5[16];
- int linkcount;
- int unk11; // bad name lmao
- short unk6; // should match up to unk4 on disk
- char unk7[16]; // padding until next bit
- short unk8; // should match up to unk5 on disk
- char unk9[8]; // padding until next bit
- char unk10[8]; // should match up to unk6 on disk
-};
-#pragma pack(pop)
-
-// the way CAI_Nodes are represented in on-disk ain files
-#pragma pack(push, 1)
-struct CAI_NodeDisk
-{
- float x;
- float y;
- float z;
- float yaw;
- float hulls[MAX_HULLS];
-
- char unk0;
- int unk1;
- short unk2[MAX_HULLS];
- char unk3[MAX_HULLS];
- short unk4;
- short unk5;
- char unk6[8];
-}; // total size of 68 bytes
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-struct UnkNodeStruct0
-{
- int index;
- char unk0;
- char unk1; // maps to unk1 on disk
- char pad0[2]; // padding to +8
-
- float x;
- float y;
- float z;
-
- char pad5[4];
- int* unk2; // maps to unk5 on disk;
- char pad1[16]; // pad to +48
- int unkcount0; // maps to unkcount0 on disk
-
- char pad2[4]; // pad to +56
- int* unk3;
- char pad3[16]; // pad to +80
- int unkcount1;
-
- char pad4[132];
- char unk5;
-};
-#pragma pack(pop)
-
-int* pUnkStruct0Count;
-UnkNodeStruct0*** pppUnkNodeStruct0s;
-
-#pragma pack(push, 1)
-struct UnkLinkStruct1
-{
- short unk0;
- short unk1;
- int unk2;
- char unk3;
- char unk4;
- char unk5;
-};
-#pragma pack(pop)
-
-int* pUnkLinkStruct1Count;
-UnkLinkStruct1*** pppUnkStruct1s;
-
-#pragma pack(push, 1)
-struct CAI_ScriptNode
-{
- float x;
- float y;
- float z;
- uint64_t scriptdata;
-};
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-struct CAI_Network
-{
- // +0
- char unk0[8];
- // +8
- int linkcount; // this is uninitialised and never set on ain build, fun!
- // +12
- char unk1[124];
- // +136
- int zonecount;
- // +140
- char unk2[16];
- // +156
- int unk5; // unk8 on disk
- // +160
- char unk6[4];
- // +164
- int hintcount;
- // +168
- short hints[2000]; // these probably aren't actually hints, but there's 1 of them per hint so idk
- // +4168
- int scriptnodecount;
- // +4172
- CAI_ScriptNode scriptnodes[4000];
- // +84172
- int nodecount;
- // +84176
- CAI_Node** nodes;
-};
-#pragma pack(pop)
-
-ConVar* Cvar_ns_ai_dumpAINfileFromLoad;
-
-void DumpAINInfo(CAI_Network* aiNetwork)
-{
- fs::path writePath(fmt::format("{}/maps/graphs", g_pModName));
- writePath /= g_pGlobals->m_pMapName;
- writePath += ".ain";
-
- // dump from memory
- spdlog::info("writing ain file {}", writePath.string());
- spdlog::info("");
- spdlog::info("");
- spdlog::info("");
- spdlog::info("");
- spdlog::info("");
-
- std::ofstream writeStream(writePath, std::ofstream::binary);
- spdlog::info("writing ainet version: {}", AINET_VERSION_NUMBER);
- writeStream.write((char*)&AINET_VERSION_NUMBER, sizeof(int));
-
- int mapVersion = g_pGlobals->m_nMapVersion;
- spdlog::info("writing map version: {}", mapVersion);
- writeStream.write((char*)&mapVersion, sizeof(int));
- spdlog::info("writing placeholder crc: {}", PLACEHOLDER_CRC);
- writeStream.write((char*)&PLACEHOLDER_CRC, sizeof(int));
-
- int calculatedLinkcount = 0;
-
- // path nodes
- spdlog::info("writing nodecount: {}", aiNetwork->nodecount);
- writeStream.write((char*)&aiNetwork->nodecount, sizeof(int));
-
- for (int i = 0; i < aiNetwork->nodecount; i++)
- {
- // construct on-disk node struct
- CAI_NodeDisk diskNode;
- diskNode.x = aiNetwork->nodes[i]->x;
- diskNode.y = aiNetwork->nodes[i]->y;
- diskNode.z = aiNetwork->nodes[i]->z;
- diskNode.yaw = aiNetwork->nodes[i]->yaw;
- memcpy(diskNode.hulls, aiNetwork->nodes[i]->hulls, sizeof(diskNode.hulls));
- diskNode.unk0 = (char)aiNetwork->nodes[i]->unk0;
- diskNode.unk1 = aiNetwork->nodes[i]->unk1;
-
- for (int j = 0; j < MAX_HULLS; j++)
- {
- diskNode.unk2[j] = (short)aiNetwork->nodes[i]->unk2[j];
- spdlog::info((short)aiNetwork->nodes[i]->unk2[j]);
- }
-
- memcpy(diskNode.unk3, aiNetwork->nodes[i]->unk3, sizeof(diskNode.unk3));
- diskNode.unk4 = aiNetwork->nodes[i]->unk6;
- diskNode.unk5 =
- -1; // aiNetwork->nodes[i]->unk8; // this field is wrong, however, it's always -1 in vanilla navmeshes anyway, so no biggie
- memcpy(diskNode.unk6, aiNetwork->nodes[i]->unk10, sizeof(diskNode.unk6));
-
- spdlog::info("writing node {} from {} to {:x}", aiNetwork->nodes[i]->index, (void*)aiNetwork->nodes[i], writeStream.tellp());
- writeStream.write((char*)&diskNode, sizeof(CAI_NodeDisk));
-
- calculatedLinkcount += aiNetwork->nodes[i]->linkcount;
- }
-
- // links
- spdlog::info("linkcount: {}", aiNetwork->linkcount);
- spdlog::info("calculated total linkcount: {}", calculatedLinkcount);
-
- calculatedLinkcount /= 2;
- if (Cvar_ns_ai_dumpAINfileFromLoad->GetBool())
- {
- if (aiNetwork->linkcount == calculatedLinkcount)
- spdlog::info("caculated linkcount is normal!");
- else
- spdlog::warn("calculated linkcount has weird value! this is expected on build!");
- }
-
- spdlog::info("writing linkcount: {}", calculatedLinkcount);
- writeStream.write((char*)&calculatedLinkcount, sizeof(int));
-
- for (int i = 0; i < aiNetwork->nodecount; i++)
- {
- for (int j = 0; j < aiNetwork->nodes[i]->linkcount; j++)
- {
- // skip links that don't originate from current node
- if (aiNetwork->nodes[i]->links[j]->srcId != aiNetwork->nodes[i]->index)
- continue;
-
- CAI_NodeLinkDisk diskLink;
- diskLink.srcId = aiNetwork->nodes[i]->links[j]->srcId;
- diskLink.destId = aiNetwork->nodes[i]->links[j]->destId;
- diskLink.unk0 = aiNetwork->nodes[i]->links[j]->unk1;
- memcpy(diskLink.hulls, aiNetwork->nodes[i]->links[j]->hulls, sizeof(diskLink.hulls));
-
- spdlog::info("writing link {} => {} to {:x}", diskLink.srcId, diskLink.destId, writeStream.tellp());
- writeStream.write((char*)&diskLink, sizeof(CAI_NodeLinkDisk));
- }
- }
-
- // don't know what this is, it's likely a block from tf1 that got deprecated? should just be 1 int per node
- spdlog::info("writing {:x} bytes for unknown block at {:x}", aiNetwork->nodecount * sizeof(uint32_t), writeStream.tellp());
- uint32_t* unkNodeBlock = new uint32_t[aiNetwork->nodecount];
- memset(unkNodeBlock, 0, aiNetwork->nodecount * sizeof(uint32_t));
- writeStream.write((char*)unkNodeBlock, aiNetwork->nodecount * sizeof(uint32_t));
- delete[] unkNodeBlock;
-
- // TODO: this is traverse nodes i think? these aren't used in tf2 ains so we can get away with just writing count=0 and skipping
- // but ideally should actually dump these
- spdlog::info("writing {} traversal nodes at {:x}...", 0, writeStream.tellp());
- short traverseNodeCount = 0;
- writeStream.write((char*)&traverseNodeCount, sizeof(short));
- // only write count since count=0 means we don't have to actually do anything here
-
- // TODO: ideally these should be actually dumped, but they're always 0 in tf2 from what i can tell
- spdlog::info("writing {} bytes for unknown hull block at {:x}", MAX_HULLS * 8, writeStream.tellp());
- char* unkHullBlock = new char[MAX_HULLS * 8];
- memset(unkHullBlock, 0, MAX_HULLS * 8);
- writeStream.write(unkHullBlock, MAX_HULLS * 8);
- delete[] unkHullBlock;
-
- // unknown struct that's seemingly node-related
- spdlog::info("writing {} unknown node structs at {:x}", *pUnkStruct0Count, writeStream.tellp());
- writeStream.write((char*)pUnkStruct0Count, sizeof(*pUnkStruct0Count));
- for (int i = 0; i < *pUnkStruct0Count; i++)
- {
- spdlog::info("writing unknown node struct {} at {:x}", i, writeStream.tellp());
- UnkNodeStruct0* nodeStruct = (*pppUnkNodeStruct0s)[i];
-
- writeStream.write((char*)&nodeStruct->index, sizeof(nodeStruct->index));
- writeStream.write((char*)&nodeStruct->unk1, sizeof(nodeStruct->unk1));
-
- writeStream.write((char*)&nodeStruct->x, sizeof(nodeStruct->x));
- writeStream.write((char*)&nodeStruct->y, sizeof(nodeStruct->y));
- writeStream.write((char*)&nodeStruct->z, sizeof(nodeStruct->z));
-
- writeStream.write((char*)&nodeStruct->unkcount0, sizeof(nodeStruct->unkcount0));
- for (int j = 0; j < nodeStruct->unkcount0; j++)
- {
- short unk2Short = (short)nodeStruct->unk2[j];
- writeStream.write((char*)&unk2Short, sizeof(unk2Short));
- }
-
- writeStream.write((char*)&nodeStruct->unkcount1, sizeof(nodeStruct->unkcount1));
- for (int j = 0; j < nodeStruct->unkcount1; j++)
- {
- short unk3Short = (short)nodeStruct->unk3[j];
- writeStream.write((char*)&unk3Short, sizeof(unk3Short));
- }
-
- writeStream.write((char*)&nodeStruct->unk5, sizeof(nodeStruct->unk5));
- }
-
- // unknown struct that's seemingly link-related
- spdlog::info("writing {} unknown link structs at {:x}", *pUnkLinkStruct1Count, writeStream.tellp());
- writeStream.write((char*)pUnkLinkStruct1Count, sizeof(*pUnkLinkStruct1Count));
- for (int i = 0; i < *pUnkLinkStruct1Count; i++)
- {
- // disk and memory structs are literally identical here so just directly write
- spdlog::info("writing unknown link struct {} at {:x}", i, writeStream.tellp());
- writeStream.write((char*)(*pppUnkStruct1s)[i], sizeof(*(*pppUnkStruct1s)[i]));
- }
-
- // some weird int idk what this is used for
- writeStream.write((char*)&aiNetwork->unk5, sizeof(aiNetwork->unk5));
-
- // tf2-exclusive stuff past this point, i.e. ain v57 only
- spdlog::info("writing {} script nodes at {:x}", aiNetwork->scriptnodecount, writeStream.tellp());
- writeStream.write((char*)&aiNetwork->scriptnodecount, sizeof(aiNetwork->scriptnodecount));
- for (int i = 0; i < aiNetwork->scriptnodecount; i++)
- {
- // disk and memory structs are literally identical here so just directly write
- spdlog::info("writing script node {} at {:x}", i, writeStream.tellp());
- writeStream.write((char*)&aiNetwork->scriptnodes[i], sizeof(aiNetwork->scriptnodes[i]));
- }
-
- spdlog::info("writing {} hints at {:x}", aiNetwork->hintcount, writeStream.tellp());
- writeStream.write((char*)&aiNetwork->hintcount, sizeof(aiNetwork->hintcount));
- for (int i = 0; i < aiNetwork->hintcount; i++)
- {
- spdlog::info("writing hint data {} at {:x}", i, writeStream.tellp());
- writeStream.write((char*)&aiNetwork->hints[i], sizeof(aiNetwork->hints[i]));
- }
-
- writeStream.close();
-}
-
-// clang-format off
-AUTOHOOK(CAI_NetworkBuilder__Build, server.dll + 0x385E20,
-void, __fastcall, (void* builder, CAI_Network* aiNetwork, void* unknown))
-// clang-format on
-{
- CAI_NetworkBuilder__Build(builder, aiNetwork, unknown);
-
- DumpAINInfo(aiNetwork);
-}
-
-// clang-format off
-AUTOHOOK(LoadAINFile, server.dll + 0x3933A0,
-void, __fastcall, (void* aimanager, void* buf, const char* filename))
-// clang-format on
-{
- LoadAINFile(aimanager, buf, filename);
-
- if (Cvar_ns_ai_dumpAINfileFromLoad->GetBool())
- {
- spdlog::info("running DumpAINInfo for loaded file {}", filename);
- DumpAINInfo(*(CAI_Network**)((char*)aimanager + 2536));
- }
-}
-
-ON_DLL_LOAD("server.dll", BuildAINFile, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- Cvar_ns_ai_dumpAINfileFromLoad = new ConVar(
- "ns_ai_dumpAINfileFromLoad", "0", FCVAR_NONE, "For debugging: whether we should dump ain data for ains loaded from disk");
-
- pUnkStruct0Count = module.Offset(0x1063BF8).RCast<int*>();
- pppUnkNodeStruct0s = module.Offset(0x1063BE0).RCast<UnkNodeStruct0***>();
- pUnkLinkStruct1Count = module.Offset(0x1063AA8).RCast<int*>();
- pppUnkStruct1s = module.Offset(0x1063A90).RCast<UnkLinkStruct1***>();
-}
diff --git a/NorthstarDLL/server/r2server.cpp b/NorthstarDLL/server/r2server.cpp
deleted file mode 100644
index c52f396e..00000000
--- a/NorthstarDLL/server/r2server.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "r2server.h"
-
-CBaseEntity* (*Server_GetEntityByIndex)(int index);
-CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex);
-
-ON_DLL_LOAD("server.dll", R2GameServer, (CModule module))
-{
- Server_GetEntityByIndex = module.Offset(0xFB820).RCast<CBaseEntity* (*)(int)>();
- UTIL_PlayerByIndex = module.Offset(0x26AA10).RCast<CBasePlayer*(__fastcall*)(int)>();
-}
diff --git a/NorthstarDLL/server/r2server.h b/NorthstarDLL/server/r2server.h
deleted file mode 100644
index c40cdc1f..00000000
--- a/NorthstarDLL/server/r2server.h
+++ /dev/null
@@ -1,106 +0,0 @@
-#pragma once
-
-#include "core/math/vector.h"
-
-// server entity stuff
-class CBaseEntity;
-extern CBaseEntity* (*Server_GetEntityByIndex)(int index);
-
-// clang-format off
-OFFSET_STRUCT(CBasePlayer)
-{
- FIELD(0x58, uint32_t m_nPlayerIndex)
-
- FIELD(0x23E8, bool m_grappleActive)
- FIELD(0x1D08, uint32_t m_platformUserId)
- FIELD(0x1D10, int32_t m_classModsActive)
- FIELD(0x1D8C, int32_t m_posClassModsActive)
- FIELD(0x1DCC, bool m_passives)
- FIELD(0x4948, int32_t m_selectedOffhand)
- FIELD(0x1358, int32_t m_selectedOffhandPendingHybridAction)
- FIELD(0x1E88, int32_t m_playerFlags)
- FIELD(0x26A8, int32_t m_lastUCmdSimulationTicks)
- FIELD(0x26AC, float m_lastUCmdSimulationRemainderTime)
- FIELD(0x1F04, int32_t m_remoteTurret)
- FIELD(0x414, int32_t m_hGroundEntity)
- FIELD(0x13B8, int32_t m_titanSoul)
- FIELD(0x2054, int32_t m_petTitan)
- FIELD(0x4D4, int32_t m_iHealth)
- FIELD(0x4D0, int32_t m_iMaxHealth)
- FIELD(0x4F1, int32_t m_lifeState)
- FIELD(0x50C, float m_flMaxspeed)
- FIELD(0x298, int32_t m_fFlags)
- FIELD(0x1F64, int32_t m_iObserverMode)
- FIELD(0x1F6C, int32_t m_hObserverTarget)
- FIELD(0x2098, int32_t m_hViewModel)
- FIELD(0x27E4, int32_t m_ubEFNointerpParity)
- FIELD(0x1FA4, int32_t m_activeBurnCardIndex)
- FIELD(0x1B68, int32_t m_hColorCorrectionCtrl)
- FIELD(0x19E0, int32_t m_PlayerFog__m_hCtrl)
- FIELD(0x26BC, bool m_bShouldDrawPlayerWhileUsingViewEntity)
- FIELD(0x2848, char m_title[32])
- FIELD(0x2964, bool m_useCredit)
- FIELD(0x1F40, float m_damageImpulseNoDecelEndTime)
- FIELD(0x1E8C, bool m_hasMic)
- FIELD(0x1E8D, bool m_inPartyChat)
- FIELD(0x1E90, float m_playerMoveSpeedScale)
- FIELD(0x1F58, float m_flDeathTime)
- FIELD(0x25A8, bool m_iSpawnParity)
- FIELD(0x102284, Vector3 m_upDir)
- FIELD(0x259C, float m_lastDodgeTime)
- FIELD(0x22E0, bool m_wallHanging)
- FIELD(0x22EC, int32_t m_traversalType)
- FIELD(0x22F0, int32_t m_traversalState)
- FIELD(0x2328, Vector3 m_traversalRefPos)
- FIELD(0x231C, Vector3 m_traversalForwardDir)
- FIELD(0x2354, float m_traversalYawDelta)
- FIELD(0x2358, int32_t m_traversalYawPoseParameter)
- FIELD(0x2050, int32_t m_grappleHook)
- FIELD(0x27C0, int32_t m_autoSprintForced)
- FIELD(0x27C4, bool m_fIsSprinting)
- FIELD(0x27CC, float m_sprintStartedTime)
- FIELD(0x27D0, float m_sprintStartedFrac)
- FIELD(0x27D4, float m_sprintEndedTime)
- FIELD(0x27D8, float m_sprintEndedFrac)
- FIELD(0x27DC, float m_stickySprintStartTime)
- FIELD(0x2998, float m_smartAmmoPreviousHighestLockOnMeFractionValue)
- FIELD(0x23FC, int32_t m_activeZipline)
- FIELD(0x2400, bool m_ziplineReverse)
- FIELD(0x2410, int32_t m_ziplineState)
- FIELD(0x2250, int32_t m_duckState)
- FIELD(0x2254, Vector3 m_StandHullMin)
- FIELD(0x2260, Vector3 m_StandHullMax)
- FIELD(0x226C, Vector3 m_DuckHullMin)
- FIELD(0x2278, Vector3 m_DuckHullMax)
- FIELD(0x205C, int32_t m_xp)
- FIELD(0x2060, int32_t m_generation)
- FIELD(0x2064, int32_t m_rank)
- FIELD(0x2068, int32_t m_serverForceIncreasePlayerListGenerationParity)
- FIELD(0x206C, bool m_isPlayingRanked)
- FIELD(0x2070, float m_skill_mu)
- FIELD(0x1E80, int32_t m_titanSoulBeingRodeoed)
- FIELD(0x1E84, int32_t m_entitySyncingWithMe)
- FIELD(0x2078, float m_nextTitanRespawnAvailable)
- FIELD(0x1C90, bool m_hasBadReputation)
- FIELD(0x1C91, char m_communityName[64])
- FIELD(0x1CD1, char m_communityClanTag[16])
- FIELD(0x1CE1, char m_factionName[16])
- FIELD(0x1CF1, char m_hardwareIcon[16])
- FIELD(0x1D01, bool m_happyHourActive)
- FIELD(0x1EF4, int32_t m_gestureAutoKillBitfield)
- FIELD(0x2EA8, int32_t m_pilotClassIndex)
- FIELD(0x100490, Vector3 m_vecAbsOrigin)
- FIELD(0x25BE, bool m_isPerformingBoostAction)
- FIELD(0x240C, bool m_ziplineValid3pWeaponLayerAnim)
- FIELD(0x345C, int32_t m_playerScriptNetDataGlobal)
- FIELD(0x1598, int32_t m_bZooming)
- FIELD(0x1599, bool m_zoomToggleOn)
- FIELD(0x159C, float m_zoomBaseFrac)
- FIELD(0x15A0, float m_zoomBaseTime)
- FIELD(0x15A4, float m_zoomFullStartTime)
- FIELD(0xA04, int32_t m_camoIndex)
- FIELD(0xA08, int32_t m_decalIndex)
-};
-// clang-format on
-
-extern CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex);
diff --git a/NorthstarDLL/server/serverchathooks.cpp b/NorthstarDLL/server/serverchathooks.cpp
deleted file mode 100644
index d3ac4776..00000000
--- a/NorthstarDLL/server/serverchathooks.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-#include "serverchathooks.h"
-#include "shared/exploit_fixes/ns_limits.h"
-#include "squirrel/squirrel.h"
-#include "server/r2server.h"
-#include "util/utils.h"
-
-#include <rapidjson/document.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
-
-AUTOHOOK_INIT()
-
-class CServerGameDLL;
-
-class CRecipientFilter
-{
- char unknown[58];
-};
-
-CServerGameDLL* g_pServerGameDLL;
-
-void(__fastcall* CServerGameDLL__OnReceivedSayTextMessage)(
- CServerGameDLL* self, unsigned int senderPlayerId, const char* text, int channelId);
-
-void(__fastcall* CRecipientFilter__Construct)(CRecipientFilter* self);
-void(__fastcall* CRecipientFilter__Destruct)(CRecipientFilter* self);
-void(__fastcall* CRecipientFilter__AddAllPlayers)(CRecipientFilter* self);
-void(__fastcall* CRecipientFilter__AddRecipient)(CRecipientFilter* self, const CBasePlayer* player);
-void(__fastcall* CRecipientFilter__MakeReliable)(CRecipientFilter* self);
-
-void(__fastcall* UserMessageBegin)(CRecipientFilter* filter, const char* messagename);
-void(__fastcall* MessageEnd)();
-void(__fastcall* MessageWriteByte)(int iValue);
-void(__fastcall* MessageWriteString)(const char* sz);
-void(__fastcall* MessageWriteBool)(bool bValue);
-
-bool bShouldCallSayTextHook = false;
-// clang-format off
-AUTOHOOK(_CServerGameDLL__OnReceivedSayTextMessage, server.dll + 0x1595C0,
-void, __fastcall, (CServerGameDLL* self, unsigned int senderPlayerId, const char* text, bool isTeam))
-// clang-format on
-{
- RemoveAsciiControlSequences(const_cast<char*>(text), true);
-
- // MiniHook doesn't allow calling the base function outside of anywhere but the hook function.
- // To allow bypassing the hook, isSkippingHook can be set.
- if (bShouldCallSayTextHook)
- {
- bShouldCallSayTextHook = false;
- _CServerGameDLL__OnReceivedSayTextMessage(self, senderPlayerId, text, isTeam);
- return;
- }
-
- // check chat ratelimits
- if (!g_pServerLimits->CheckChatLimits(&g_pClientArray[senderPlayerId - 1]))
- return;
-
- SQRESULT result = g_pSquirrel<ScriptContext::SERVER>->Call(
- "CServerGameDLL_ProcessMessageStartThread", static_cast<int>(senderPlayerId) - 1, text, isTeam);
-
- if (result == SQRESULT_ERROR)
- _CServerGameDLL__OnReceivedSayTextMessage(self, senderPlayerId, text, isTeam);
-}
-
-void ChatSendMessage(unsigned int playerIndex, const char* text, bool isTeam)
-{
- bShouldCallSayTextHook = true;
- CServerGameDLL__OnReceivedSayTextMessage(
- g_pServerGameDLL,
- // Ensure the first bit isn't set, since this indicates a custom message
- (playerIndex + 1) & CUSTOM_MESSAGE_INDEX_MASK,
- text,
- isTeam);
-}
-
-void ChatBroadcastMessage(int fromPlayerIndex, int toPlayerIndex, const char* text, bool isTeam, bool isDead, CustomMessageType messageType)
-{
- CBasePlayer* toPlayer = NULL;
- if (toPlayerIndex >= 0)
- {
- toPlayer = UTIL_PlayerByIndex(toPlayerIndex + 1);
- if (toPlayer == NULL)
- return;
- }
-
- // Build a new string where the first byte is the message type
- char sendText[256];
- sendText[0] = (char)messageType;
- strncpy_s(sendText + 1, 255, text, 254);
-
- // Anonymous custom messages use playerId=0, non-anonymous ones use a player ID with the first bit set
- unsigned int fromPlayerId = fromPlayerIndex < 0 ? 0 : ((fromPlayerIndex + 1) | CUSTOM_MESSAGE_INDEX_BIT);
-
- CRecipientFilter filter;
- CRecipientFilter__Construct(&filter);
- if (toPlayer == NULL)
- {
- CRecipientFilter__AddAllPlayers(&filter);
- }
- else
- {
- CRecipientFilter__AddRecipient(&filter, toPlayer);
- }
- CRecipientFilter__MakeReliable(&filter);
-
- UserMessageBegin(&filter, "SayText");
- MessageWriteByte(fromPlayerId);
- MessageWriteString(sendText);
- MessageWriteBool(isTeam);
- MessageWriteBool(isDead);
- MessageEnd();
-
- CRecipientFilter__Destruct(&filter);
-}
-
-ADD_SQFUNC("void", NSSendMessage, "int playerIndex, string text, bool isTeam", "", ScriptContext::SERVER)
-{
- int playerIndex = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 1);
- const char* text = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 2);
- bool isTeam = g_pSquirrel<ScriptContext::SERVER>->getbool(sqvm, 3);
-
- ChatSendMessage(playerIndex, text, isTeam);
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC(
- "void",
- NSBroadcastMessage,
- "int fromPlayerIndex, int toPlayerIndex, string text, bool isTeam, bool isDead, int messageType",
- "",
- ScriptContext::SERVER)
-{
- int fromPlayerIndex = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 1);
- int toPlayerIndex = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 2);
- const char* text = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 3);
- bool isTeam = g_pSquirrel<ScriptContext::SERVER>->getbool(sqvm, 4);
- bool isDead = g_pSquirrel<ScriptContext::SERVER>->getbool(sqvm, 5);
- int messageType = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 6);
-
- if (messageType < 1)
- {
- g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, fmt::format("Invalid message type {}", messageType).c_str());
- return SQRESULT_ERROR;
- }
-
- ChatBroadcastMessage(fromPlayerIndex, toPlayerIndex, text, isTeam, isDead, (CustomMessageType)messageType);
-
- return SQRESULT_NULL;
-}
-
-ON_DLL_LOAD("engine.dll", EngineServerChatHooks, (CModule module))
-{
- g_pServerGameDLL = module.Offset(0x13F0AA98).RCast<CServerGameDLL*>();
-}
-
-ON_DLL_LOAD_RELIESON("server.dll", ServerChatHooks, ServerSquirrel, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(server.dll)
-
- CServerGameDLL__OnReceivedSayTextMessage =
- module.Offset(0x1595C0).RCast<void(__fastcall*)(CServerGameDLL*, unsigned int, const char*, int)>();
- CRecipientFilter__Construct = module.Offset(0x1E9440).RCast<void(__fastcall*)(CRecipientFilter*)>();
- CRecipientFilter__Destruct = module.Offset(0x1E9700).RCast<void(__fastcall*)(CRecipientFilter*)>();
- CRecipientFilter__AddAllPlayers = module.Offset(0x1E9940).RCast<void(__fastcall*)(CRecipientFilter*)>();
- CRecipientFilter__AddRecipient = module.Offset(0x1E9B30).RCast<void(__fastcall*)(CRecipientFilter*, const CBasePlayer*)>();
- CRecipientFilter__MakeReliable = module.Offset(0x1EA4E0).RCast<void(__fastcall*)(CRecipientFilter*)>();
-
- UserMessageBegin = module.Offset(0x15C520).RCast<void(__fastcall*)(CRecipientFilter*, const char*)>();
- MessageEnd = module.Offset(0x158880).RCast<void(__fastcall*)()>();
- MessageWriteByte = module.Offset(0x158A90).RCast<void(__fastcall*)(int)>();
- MessageWriteString = module.Offset(0x158D00).RCast<void(__fastcall*)(const char*)>();
- MessageWriteBool = module.Offset(0x158A00).RCast<void(__fastcall*)(bool)>();
-}
diff --git a/NorthstarDLL/server/serverchathooks.h b/NorthstarDLL/server/serverchathooks.h
deleted file mode 100644
index d033e769..00000000
--- a/NorthstarDLL/server/serverchathooks.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-#include <rapidjson/document.h>
-#include <rapidjson/stringbuffer.h>
-
-enum class CustomMessageType : char
-{
- Chat = 1,
- Whisper = 2
-};
-
-constexpr unsigned char CUSTOM_MESSAGE_INDEX_BIT = 0b10000000;
-constexpr unsigned char CUSTOM_MESSAGE_INDEX_MASK = (unsigned char)~CUSTOM_MESSAGE_INDEX_BIT;
-
-// Send a vanilla chat message as if it was from the player.
-void ChatSendMessage(unsigned int playerIndex, const char* text, bool isteam);
-
-// Send a custom message.
-// fromPlayerIndex: set to -1 for a [SERVER] message, or another value to send from a specific player
-// toPlayerIndex: set to -1 to send to all players, or another value to send to a single player
-// isTeam: display a [TEAM] badge
-// isDead: display a [DEAD] badge
-// messageType: send a specific message type
-void ChatBroadcastMessage(
- int fromPlayerIndex, int toPlayerIndex, const char* text, bool isTeam, bool isDead, CustomMessageType messageType);
diff --git a/NorthstarDLL/server/servernethooks.cpp b/NorthstarDLL/server/servernethooks.cpp
deleted file mode 100644
index 148b735f..00000000
--- a/NorthstarDLL/server/servernethooks.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-#include "core/convar/convar.h"
-#include "engine/r2engine.h"
-#include "shared/exploit_fixes/ns_limits.h"
-#include "masterserver/masterserver.h"
-
-#include <string>
-#include <thread>
-#include <bcrypt.h>
-
-AUTOHOOK_INIT()
-
-static ConVar* Cvar_net_debug_atlas_packet;
-static ConVar* Cvar_net_debug_atlas_packet_insecure;
-
-static BCRYPT_ALG_HANDLE HMACSHA256;
-constexpr size_t HMACSHA256_LEN = 256 / 8;
-
-static bool InitHMACSHA256()
-{
- NTSTATUS status;
- DWORD hashLength = 0;
- ULONG hashLengthSz = 0;
-
- if ((status = BCryptOpenAlgorithmProvider(&HMACSHA256, BCRYPT_SHA256_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG)))
- {
- spdlog::error("failed to initialize HMAC-SHA256: BCryptOpenAlgorithmProvider: error 0x{:08X}", (ULONG)status);
- return false;
- }
-
- if ((status = BCryptGetProperty(HMACSHA256, BCRYPT_HASH_LENGTH, (PUCHAR)&hashLength, sizeof(hashLength), &hashLengthSz, 0)))
- {
- spdlog::error("failed to initialize HMAC-SHA256: BCryptGetProperty(BCRYPT_HASH_LENGTH): error 0x{:08X}", (ULONG)status);
- return false;
- }
-
- if (hashLength != HMACSHA256_LEN)
- {
- spdlog::error("failed to initialize HMAC-SHA256: BCryptGetProperty(BCRYPT_HASH_LENGTH): unexpected value {}", hashLength);
- return false;
- }
-
- return true;
-}
-
-// compare the HMAC-SHA256(data, key) against sig (note: all strings are treated as raw binary data)
-static bool VerifyHMACSHA256(std::string key, std::string sig, std::string data)
-{
- uint8_t invalid = 1;
- char hash[HMACSHA256_LEN];
-
- NTSTATUS status;
- BCRYPT_HASH_HANDLE h = NULL;
-
- if ((status = BCryptCreateHash(HMACSHA256, &h, NULL, 0, (PUCHAR)key.c_str(), (ULONG)key.length(), 0)))
- {
- spdlog::error("failed to verify HMAC-SHA256: BCryptCreateHash: error 0x{:08X}", (ULONG)status);
- goto cleanup;
- }
-
- if ((status = BCryptHashData(h, (PUCHAR)data.c_str(), (ULONG)data.length(), 0)))
- {
- spdlog::error("failed to verify HMAC-SHA256: BCryptHashData: error 0x{:08X}", (ULONG)status);
- goto cleanup;
- }
-
- if ((status = BCryptFinishHash(h, (PUCHAR)&hash, (ULONG)sizeof(hash), 0)))
- {
- spdlog::error("failed to verify HMAC-SHA256: BCryptFinishHash: error 0x{:08X}", (ULONG)status);
- goto cleanup;
- }
-
- // constant-time compare
- if (sig.length() == sizeof(hash))
- {
- invalid = 0;
- for (size_t i = 0; i < sizeof(hash); i++)
- invalid |= (uint8_t)(sig[i]) ^ (uint8_t)(hash[i]);
- }
-
-cleanup:
- if (h)
- BCryptDestroyHash(h);
- return !invalid;
-}
-
-// v1 HMACSHA256-signed masterserver request (HMAC-SHA256(JSONData, MasterServerToken) + JSONData)
-static void ProcessAtlasConnectionlessPacketSigreq1(netpacket_t* packet, bool dbg, std::string pType, std::string pData)
-{
- if (pData.length() < HMACSHA256_LEN)
- {
- if (dbg)
- spdlog::warn("ignoring Atlas connectionless packet (size={} type={}): invalid: too short for signature", packet->size, pType);
- return;
- }
-
- std::string pSig; // is binary data, not actually an ASCII string
- pSig = pData.substr(0, HMACSHA256_LEN);
- pData = pData.substr(HMACSHA256_LEN);
-
- if (!g_pMasterServerManager || !g_pMasterServerManager->m_sOwnServerAuthToken[0])
- {
- if (dbg)
- spdlog::warn(
- "ignoring Atlas connectionless packet (size={} type={}): invalid (data={}): no masterserver token yet",
- packet->size,
- pType,
- pData);
- return;
- }
-
- if (!VerifyHMACSHA256(std::string(g_pMasterServerManager->m_sOwnServerAuthToken), pSig, pData))
- {
- if (!Cvar_net_debug_atlas_packet_insecure->GetBool())
- {
- if (dbg)
- spdlog::warn(
- "ignoring Atlas connectionless packet (size={} type={}): invalid: invalid signature (key={})",
- packet->size,
- pType,
- std::string(g_pMasterServerManager->m_sOwnServerAuthToken));
- return;
- }
- spdlog::warn(
- "processing Atlas connectionless packet (size={} type={}) with invalid signature due to net_debug_atlas_packet_insecure",
- packet->size,
- pType);
- }
-
- if (dbg)
- spdlog::info("got Atlas connectionless packet (size={} type={} data={})", packet->size, pType, pData);
-
- std::thread t(&MasterServerManager::ProcessConnectionlessPacketSigreq1, g_pMasterServerManager, pData);
- t.detach();
-
- return;
-}
-
-static void ProcessAtlasConnectionlessPacket(netpacket_t* packet)
-{
- bool dbg = Cvar_net_debug_atlas_packet->GetBool();
-
- // extract kind, null-terminated type, data
- std::string pType, pData;
- for (int i = 5; i < packet->size; i++)
- {
- if (packet->data[i] == '\x00')
- {
- pType.assign((char*)(&packet->data[5]), (size_t)(i - 5));
- if (i + 1 < packet->size)
- pData.assign((char*)(&packet->data[i + 1]), (size_t)(packet->size - i - 1));
- break;
- }
- }
-
- // note: all Atlas connectionless packets should be idempotent so multiple attempts can be made to mitigate packet loss
- // note: all long-running Atlas connectionless packet handlers should be started in a new thread (with copies of the data) to avoid
- // blocking networking
-
- // v1 HMACSHA256-signed masterserver request
- if (pType == "sigreq1")
- {
- ProcessAtlasConnectionlessPacketSigreq1(packet, dbg, pType, pData);
- return;
- }
-
- if (dbg)
- spdlog::warn("ignoring Atlas connectionless packet (size={} type={}): unknown type", packet->size, pType);
- return;
-}
-
-AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800, bool, , (void* a1, netpacket_t* packet))
-{
- // packet->data consists of 0xFFFFFFFF (int32 -1) to indicate packets aren't split, followed by a header consisting of a single
- // character, which is used to uniquely identify the packet kind. Most kinds follow this with a null-terminated string payload
- // then an arbitrary amoount of data.
-
- // T (no rate limits since we authenticate packets before doing anything expensive)
- if (4 < packet->size && packet->data[4] == 'T')
- {
- ProcessAtlasConnectionlessPacket(packet);
- return false;
- }
-
- // check rate limits for the original unconnected packets
- if (!g_pServerLimits->CheckConnectionlessPacketLimits(packet))
- return false;
-
- // A, H, I, N
- return ProcessConnectionlessPacket(a1, packet);
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ServerNetHooks, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
-
- if (!InitHMACSHA256())
- throw std::runtime_error("failed to initialize bcrypt");
-
- if (!VerifyHMACSHA256(
- "test",
- "\x88\xcd\x21\x08\xb5\x34\x7d\x97\x3c\xf3\x9c\xdf\x90\x53\xd7\xdd\x42\x70\x48\x76\xd8\xc9\xa9\xbd\x8e\x2d\x16\x82\x59\xd3\xdd"
- "\xf7",
- "test"))
- throw std::runtime_error("bcrypt HMAC-SHA256 is broken");
-
- Cvar_net_debug_atlas_packet = new ConVar(
- "net_debug_atlas_packet",
- "0",
- FCVAR_NONE,
- "Whether to log detailed debugging information for Atlas connectionless packets (warning: this allows unlimited amounts of "
- "arbitrary data to be logged)");
-
- Cvar_net_debug_atlas_packet_insecure = new ConVar(
- "net_debug_atlas_packet_insecure",
- "0",
- FCVAR_NONE,
- "Whether to disable signature verification for Atlas connectionless packets (DANGEROUS: this allows anyone to impersonate Atlas)");
-}
diff --git a/NorthstarDLL/server/serverpresence.cpp b/NorthstarDLL/server/serverpresence.cpp
deleted file mode 100644
index 159b9f30..00000000
--- a/NorthstarDLL/server/serverpresence.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-#include "serverpresence.h"
-#include "shared/playlist.h"
-#include "core/tier0.h"
-#include "core/convar/convar.h"
-
-#include <regex>
-
-ServerPresenceManager* g_pServerPresence;
-
-ConVar* Cvar_hostname;
-
-// Convert a hex digit char to integer.
-inline int hctod(char c)
-{
- if (c >= 'A' && c <= 'F')
- {
- return c - 'A' + 10;
- }
- else if (c >= 'a' && c <= 'f')
- {
- return c - 'a' + 10;
- }
- else
- {
- return c - '0';
- }
-}
-
-// This function interprets all 4-hexadecimal-digit unicode codepoint characters like \u4E2D to UTF-8 encoding.
-std::string UnescapeUnicode(const std::string& str)
-{
- std::string result;
-
- std::regex r("\\\\u([a-f\\d]{4})", std::regex::icase);
- auto matches_begin = std::sregex_iterator(str.begin(), str.end(), r);
- auto matches_end = std::sregex_iterator();
- std::smatch last_match;
-
- for (std::sregex_iterator i = matches_begin; i != matches_end; ++i)
- {
- last_match = *i;
- result.append(last_match.prefix());
- unsigned int cp = 0;
- for (int i = 2; i <= 5; ++i)
- {
- cp *= 16;
- cp += hctod(last_match.str()[i]);
- }
- if (cp <= 0x7F)
- {
- result.push_back(cp);
- }
- else if (cp <= 0x7FF)
- {
- result.push_back((cp >> 6) | 0b11000000 & (~(1 << 5)));
- result.push_back(cp & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- }
- else if (cp <= 0xFFFF)
- {
- result.push_back((cp >> 12) | 0b11100000 & (~(1 << 4)));
- result.push_back((cp >> 6) & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- result.push_back(cp & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- }
- }
-
- if (!last_match.ready())
- return str;
- else
- result.append(last_match.suffix());
-
- return result;
-}
-
-void ServerPresenceManager::CreateConVars()
-{
- // clang-format off
- // register convars
- Cvar_ns_server_presence_update_rate = new ConVar(
- "ns_server_presence_update_rate", "5000", FCVAR_GAMEDLL, "How often we update our server's presence on server lists in ms");
-
- Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "This server's name", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) {
- g_pServerPresence->SetName(UnescapeUnicode(g_pServerPresence->Cvar_ns_server_name->GetString()));
-
- // update engine hostname cvar
- Cvar_hostname->SetValue(g_pServerPresence->Cvar_ns_server_name->GetString());
- });
-
- Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "This server's description", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) {
- g_pServerPresence->SetDescription(UnescapeUnicode(g_pServerPresence->Cvar_ns_server_desc->GetString()));
- });
-
- Cvar_ns_server_password = new ConVar("ns_server_password", "", FCVAR_GAMEDLL, "This server's password", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) {
- g_pServerPresence->SetPassword(g_pServerPresence->Cvar_ns_server_password->GetString());
- });
-
- Cvar_ns_report_server_to_masterserver = new ConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, "Whether we should report this server to the masterserver");
- Cvar_ns_report_sp_server_to_masterserver = new ConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, "Whether we should report this server to the masterserver, when started in singleplayer");
- // clang-format on
-}
-
-void ServerPresenceManager::AddPresenceReporter(ServerPresenceReporter* reporter)
-{
- m_vPresenceReporters.push_back(reporter);
-}
-
-void ServerPresenceManager::CreatePresence()
-{
- // reset presence fields that rely on runtime server state
- // these being: port, map/playlist name, and playercount/maxplayers
- m_ServerPresence.m_iPort = 0;
-
- m_ServerPresence.m_iPlayerCount = 0; // this should actually be 0 at this point, so shouldn't need updating later
- m_ServerPresence.m_iMaxPlayers = 0;
-
- memset(m_ServerPresence.m_MapName, 0, sizeof(m_ServerPresence.m_MapName));
- memset(m_ServerPresence.m_PlaylistName, 0, sizeof(m_ServerPresence.m_PlaylistName));
- m_ServerPresence.m_bIsSingleplayerServer = false;
-
- m_bHasPresence = true;
- m_bFirstPresenceUpdate = true;
-
- // code that's calling this should set up the reset fields at this point
-}
-
-void ServerPresenceManager::DestroyPresence()
-{
- m_bHasPresence = false;
-
- for (ServerPresenceReporter* reporter : m_vPresenceReporters)
- reporter->DestroyPresence(&m_ServerPresence);
-}
-
-void ServerPresenceManager::RunFrame(double flCurrentTime)
-{
- if (!m_bHasPresence || !Cvar_ns_report_server_to_masterserver->GetBool()) // don't run until we actually have server presence
- return;
-
- // don't run if we're sp and don't want to report sp
- if (m_ServerPresence.m_bIsSingleplayerServer && !Cvar_ns_report_sp_server_to_masterserver->GetBool())
- return;
-
- // Call RunFrame() so that reporters can, for example, handle std::future results as soon as they arrive.
- for (ServerPresenceReporter* reporter : m_vPresenceReporters)
- reporter->RunFrame(flCurrentTime, &m_ServerPresence);
-
- // run on a specified delay
- if ((flCurrentTime - m_flLastPresenceUpdate) * 1000 < Cvar_ns_server_presence_update_rate->GetFloat())
- return;
-
- // is this the first frame we're updating this presence?
- if (m_bFirstPresenceUpdate)
- {
- // let reporters setup/clear any state
- for (ServerPresenceReporter* reporter : m_vPresenceReporters)
- reporter->CreatePresence(&m_ServerPresence);
-
- m_bFirstPresenceUpdate = false;
- }
-
- m_flLastPresenceUpdate = flCurrentTime;
-
- for (ServerPresenceReporter* reporter : m_vPresenceReporters)
- reporter->ReportPresence(&m_ServerPresence);
-}
-
-void ServerPresenceManager::SetPort(const int iPort)
-{
- // update port
- m_ServerPresence.m_iPort = iPort;
-}
-
-void ServerPresenceManager::SetName(const std::string sServerNameUnicode)
-{
- // update name
- m_ServerPresence.m_sServerName = sServerNameUnicode;
-}
-
-void ServerPresenceManager::SetDescription(const std::string sServerDescUnicode)
-{
- // update desc
- m_ServerPresence.m_sServerDesc = sServerDescUnicode;
-}
-
-void ServerPresenceManager::SetPassword(const char* pPassword)
-{
- // update password
- strncpy_s(m_ServerPresence.m_Password, sizeof(m_ServerPresence.m_Password), pPassword, sizeof(m_ServerPresence.m_Password) - 1);
-}
-
-void ServerPresenceManager::SetMap(const char* pMapName, bool isInitialising)
-{
- // if the server is initialising (i.e. this is first map) on sp, set the server to sp
- if (isInitialising)
- m_ServerPresence.m_bIsSingleplayerServer = !strncmp(pMapName, "sp_", 3);
-
- // update map
- strncpy_s(m_ServerPresence.m_MapName, sizeof(m_ServerPresence.m_MapName), pMapName, sizeof(m_ServerPresence.m_MapName) - 1);
-}
-
-void ServerPresenceManager::SetPlaylist(const char* pPlaylistName)
-{
- // update playlist
- strncpy_s(
- m_ServerPresence.m_PlaylistName,
- sizeof(m_ServerPresence.m_PlaylistName),
- pPlaylistName,
- sizeof(m_ServerPresence.m_PlaylistName) - 1);
-
- // update maxplayers
- const char* pMaxPlayers = R2::GetCurrentPlaylistVar("max_players", true);
-
- // can be null in some situations, so default 6
- if (pMaxPlayers)
- m_ServerPresence.m_iMaxPlayers = std::stoi(pMaxPlayers);
- else
- m_ServerPresence.m_iMaxPlayers = 6;
-}
-
-void ServerPresenceManager::SetPlayerCount(const int iPlayerCount)
-{
- m_ServerPresence.m_iPlayerCount = iPlayerCount;
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ServerPresence, ConVar, (CModule module))
-{
- g_pServerPresence->CreateConVars();
- Cvar_hostname = module.Offset(0x1315BAE8).Deref().RCast<ConVar*>();
-}
diff --git a/NorthstarDLL/server/serverpresence.h b/NorthstarDLL/server/serverpresence.h
deleted file mode 100644
index c644cc37..00000000
--- a/NorthstarDLL/server/serverpresence.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma once
-#include "core/convar/convar.h"
-
-struct ServerPresence
-{
-public:
- int m_iPort;
-
- std::string m_sServerId;
-
- std::string m_sServerName;
- std::string m_sServerDesc;
- char m_Password[256]; // probably bigger than will ever be used in practice, lol
-
- char m_MapName[32];
- char m_PlaylistName[64];
- bool m_bIsSingleplayerServer; // whether the server started in sp
-
- int m_iPlayerCount;
- int m_iMaxPlayers;
-
- ServerPresence()
- {
- memset(this, 0, sizeof(this));
- }
-
- ServerPresence(const ServerPresence* obj)
- {
- m_iPort = obj->m_iPort;
-
- m_sServerId = obj->m_sServerId;
-
- m_sServerName = obj->m_sServerName;
- m_sServerDesc = obj->m_sServerDesc;
- memcpy(m_Password, obj->m_Password, sizeof(m_Password));
-
- memcpy(m_MapName, obj->m_MapName, sizeof(m_MapName));
- memcpy(m_PlaylistName, obj->m_PlaylistName, sizeof(m_PlaylistName));
-
- m_iPlayerCount = obj->m_iPlayerCount;
- m_iMaxPlayers = obj->m_iMaxPlayers;
- }
-};
-
-class ServerPresenceReporter
-{
-public:
- virtual void CreatePresence(const ServerPresence* pServerPresence) {}
- virtual void ReportPresence(const ServerPresence* pServerPresence) {}
- virtual void DestroyPresence(const ServerPresence* pServerPresence) {}
- virtual void RunFrame(double flCurrentTime, const ServerPresence* pServerPresence) {}
-};
-
-class ServerPresenceManager
-{
-private:
- ServerPresence m_ServerPresence;
-
- bool m_bHasPresence = false;
- bool m_bFirstPresenceUpdate = false;
-
- std::vector<ServerPresenceReporter*> m_vPresenceReporters;
-
- double m_flLastPresenceUpdate = 0;
- ConVar* Cvar_ns_server_presence_update_rate;
-
- ConVar* Cvar_ns_server_name;
- ConVar* Cvar_ns_server_desc;
- ConVar* Cvar_ns_server_password;
-
- ConVar* Cvar_ns_report_server_to_masterserver;
- ConVar* Cvar_ns_report_sp_server_to_masterserver;
-
-public:
- void AddPresenceReporter(ServerPresenceReporter* reporter);
-
- void CreateConVars();
-
- void CreatePresence();
- void DestroyPresence();
- void RunFrame(double flCurrentTime);
-
- void SetPort(const int iPort);
-
- void SetName(const std::string sServerNameUnicode);
- void SetDescription(const std::string sServerDescUnicode);
- void SetPassword(const char* pPassword);
-
- void SetMap(const char* pMapName, bool isInitialising = false);
- void SetPlaylist(const char* pPlaylistName);
- void SetPlayerCount(const int iPlayerCount);
-};
-
-extern ServerPresenceManager* g_pServerPresence;
diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp b/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp
deleted file mode 100644
index 8064d5ac..00000000
--- a/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp
+++ /dev/null
@@ -1,461 +0,0 @@
-#include "core/convar/cvar.h"
-#include "ns_limits.h"
-#include "dedicated/dedicated.h"
-#include "core/tier0.h"
-#include "engine/r2engine.h"
-#include "client/r2client.h"
-#include "core/math/vector.h"
-#include "core/vanilla.h"
-
-AUTOHOOK_INIT()
-
-ConVar* Cvar_ns_exploitfixes_log;
-ConVar* Cvar_ns_should_log_all_clientcommands;
-
-ConVar* Cvar_sv_cheats;
-
-#define BLOCKED_INFO(s) \
- ( \
- [=]() -> bool \
- { \
- if (Cvar_ns_exploitfixes_log->GetBool()) \
- { \
- std::stringstream stream; \
- stream << "ExploitFixes.cpp: " << BLOCK_PREFIX << s; \
- spdlog::error(stream.str()); \
- } \
- return false; \
- }())
-
-// block bad netmessages
-// Servers can literally request a screenshot from any client, yeah no
-// clang-format off
-AUTOHOOK(CLC_Screenshot_WriteToBuffer, engine.dll + 0x22AF20,
-bool, __fastcall, (void* thisptr, void* buffer)) // 48 89 5C 24 ? 57 48 83 EC 20 8B 42 10
-// clang-format on
-{
- if (g_pVanillaCompatibility->GetVanillaCompatibility())
- return CLC_Screenshot_WriteToBuffer(thisptr, buffer);
- return false;
-}
-
-// clang-format off
-AUTOHOOK(CLC_Screenshot_ReadFromBuffer, engine.dll + 0x221F00,
-bool, __fastcall, (void* thisptr, void* buffer)) // 48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B DA 48 8B 52 38
-// clang-format on
-{
- if (g_pVanillaCompatibility->GetVanillaCompatibility())
- return CLC_Screenshot_ReadFromBuffer(thisptr, buffer);
- return false;
-}
-
-// This is unused ingame and a big client=>server=>client exploit vector
-// clang-format off
-AUTOHOOK(Base_CmdKeyValues_ReadFromBuffer, engine.dll + 0x220040,
-bool, __fastcall, (void* thisptr, void* buffer)) // 40 55 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 5D 70
-// clang-format on
-{
- return false;
-}
-
-// clang-format off
-AUTOHOOK(CClient_ProcessSetConVar, engine.dll + 0x75CF0,
-bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10
-// clang-format on
-{
-
- constexpr int ENTRY_STR_LEN = 260;
- struct SetConVarEntry
- {
- char name[ENTRY_STR_LEN];
- char val[ENTRY_STR_LEN];
- };
-
- struct NET_SetConVar
- {
- void* vtable;
- void* unk1;
- void* unk2;
- void* m_pMessageHandler;
- SetConVarEntry* m_ConVars; // convar entry array
- void* unk5; // these 2 unks are just vector capacity or whatever
- void* unk6;
- int m_ConVars_count; // amount of cvar entries in array (this will not be out of bounds)
- };
-
- auto msg = (NET_SetConVar*)pMsg;
- bool bIsServerFrame = ThreadInServerFrameThread();
-
- std::string BLOCK_PREFIX =
- std::string {"NET_SetConVar ("} + (bIsServerFrame ? "server" : "client") + "): Blocked dangerous/invalid msg: ";
-
- if (bIsServerFrame)
- {
- constexpr int SETCONVAR_SANITY_AMOUNT_LIMIT = 69;
- if (msg->m_ConVars_count < 1 || msg->m_ConVars_count > SETCONVAR_SANITY_AMOUNT_LIMIT)
- {
- return BLOCKED_INFO("Invalid m_ConVars_count (" << msg->m_ConVars_count << ")");
- }
- }
-
- for (int i = 0; i < msg->m_ConVars_count; i++)
- {
- auto entry = msg->m_ConVars + i;
-
- // Safety check for memory access
- if (CMemoryAddress(entry).IsMemoryReadable(sizeof(*entry)))
- {
- // Find null terminators
- bool nameValid = false, valValid = false;
- for (int i = 0; i < ENTRY_STR_LEN; i++)
- {
- if (!entry->name[i])
- nameValid = true;
- if (!entry->val[i])
- valValid = true;
- }
-
- if (!nameValid || !valValid)
- return BLOCKED_INFO("Missing null terminators");
-
- ConVar* pVar = g_pCVar->FindVar(entry->name);
-
- if (pVar)
- {
- memcpy(
- entry->name,
- pVar->m_ConCommandBase.m_pszName,
- strlen(pVar->m_ConCommandBase.m_pszName) + 1); // Force name to match case
-
- int iFlags = bIsServerFrame ? FCVAR_USERINFO : FCVAR_REPLICATED;
- if (!pVar->IsFlagSet(iFlags))
- return BLOCKED_INFO(
- "Invalid flags (" << std::hex << "0x" << pVar->m_ConCommandBase.m_nFlags << "), var is " << entry->name);
- }
- }
- else
- {
- return BLOCKED_INFO("Unreadable memory at " << (void*)entry); // Not risking that one, they all gotta be readable
- }
- }
-
- return CClient_ProcessSetConVar(msg);
-}
-
-// prevent invalid user CMDs
-// clang-format off
-AUTOHOOK(CClient_ProcessUsercmds, engine.dll + 0x1040F0,
-bool, __fastcall, (void* thisptr, void* pMsg)) // 40 55 56 48 83 EC 58
-// clang-format on
-{
- struct CLC_Move
- {
- BYTE gap0[24];
- void* m_pMessageHandler;
- int m_nBackupCommands;
- int m_nNewCommands;
- int m_nLength;
- // bf_read m_DataIn;
- // bf_write m_DataOut;
- };
-
- auto msg = (CLC_Move*)pMsg;
-
- const char* BLOCK_PREFIX = "ProcessUserCmds: ";
-
- if (msg->m_nBackupCommands < 0)
- {
- return BLOCKED_INFO("Invalid m_nBackupCommands (" << msg->m_nBackupCommands << ")");
- }
-
- if (msg->m_nNewCommands < 0)
- {
- return BLOCKED_INFO("Invalid m_nNewCommands (" << msg->m_nNewCommands << ")");
- }
-
- if (msg->m_nLength <= 0)
- return BLOCKED_INFO("Invalid message length (" << msg->m_nLength << ")");
-
- return CClient_ProcessUsercmds(thisptr, pMsg);
-}
-
-// clang-format off
-AUTOHOOK(ReadUsercmd, server.dll + 0x2603F0,
-void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 ? 53 55 56 57
-// clang-format on
-{
- // Let normal usercmd read happen first, it's safe
- ReadUsercmd(buf, pCmd_move, pCmd_from);
-
- // Now let's make sure the CMD we read isnt messed up to prevent numerous exploits (including server crashing)
- struct alignas(4) SV_CUserCmd
- {
- DWORD command_number;
- DWORD tick_count;
- float command_time;
- Vector3 worldViewAngles;
- BYTE gap18[4];
- Vector3 localViewAngles;
- Vector3 attackangles;
- Vector3 move;
- DWORD buttons;
- BYTE impulse;
- short weaponselect;
- DWORD meleetarget;
- BYTE gap4C[24];
- char headoffset;
- BYTE gap65[11];
- Vector3 cameraPos;
- Vector3 cameraAngles;
- BYTE gap88[4];
- int tickSomething;
- DWORD dword90;
- DWORD predictedServerEventAck;
- DWORD dword98;
- float frameTime;
- };
-
- auto cmd = (SV_CUserCmd*)pCmd_move;
- auto fromCmd = (SV_CUserCmd*)pCmd_from;
-
- std::string BLOCK_PREFIX =
- "ReadUsercmd (command_number delta: " + std::to_string(cmd->command_number - fromCmd->command_number) + "): ";
-
- // fix invalid player angles
- cmd->worldViewAngles.MakeValid();
- cmd->attackangles.MakeValid();
- cmd->localViewAngles.MakeValid();
-
- // Fix invalid camera angles
- cmd->cameraPos.MakeValid();
- cmd->cameraAngles.MakeValid();
-
- // Fix invaid movement vector
- cmd->move.MakeValid();
-
- if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0)
- {
- BLOCKED_INFO(
- "Bogus cmd timing (tick_count: " << cmd->tick_count << ", frameTime: " << cmd->frameTime
- << ", commandTime : " << cmd->command_time << ")");
- goto INVALID_CMD; // No simulation of bogus-timed cmds
- }
-
- return;
-
-INVALID_CMD:
-
- // Fix any gameplay-affecting cmd properties
- // NOTE: Currently tickcount/frametime is set to 0, this ~shouldn't~ cause any problems
- cmd->worldViewAngles = cmd->localViewAngles = cmd->attackangles = cmd->cameraAngles = {0, 0, 0};
- cmd->tick_count = cmd->frameTime = 0;
- cmd->move = cmd->cameraPos = {0, 0, 0};
- cmd->buttons = 0;
- cmd->meleetarget = 0;
-}
-
-// ensure that GetLocalBaseClient().m_bRestrictServerCommands is set correctly, which the return value of this function controls
-// this is IsValveMod in source, but we're making it IsRespawnMod now since valve didn't make this one
-// clang-format off
-AUTOHOOK(IsRespawnMod, engine.dll + 0x1C6360,
-bool, __fastcall, (const char* pModName)) // 48 83 EC 28 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 85 C0 74 63
-// clang-format on
-{
- // somewhat temp, store the modname here, since we don't have a proper ptr in engine to it rn
- int iSize = strlen(pModName);
- g_pModName = new char[iSize + 1];
- strcpy(g_pModName, pModName);
-
- if (g_pVanillaCompatibility->GetVanillaCompatibility())
- return false;
-
- return (!strcmp("r2", pModName) || !strcmp("r1", pModName)) && !CommandLine()->CheckParm("-norestrictservercommands");
-}
-
-// ratelimit stringcmds, and prevent remote clients from calling commands that they shouldn't
-// clang-format off
-AUTOHOOK(CGameClient__ExecuteStringCommand, engine.dll + 0x1022E0,
-bool, __fastcall, (CBaseClient* self, uint32_t unknown, const char* pCommandString))
-// clang-format on
-{
- if (Cvar_ns_should_log_all_clientcommands->GetBool())
- spdlog::info("player {} (UID: {}) sent command: \"{}\"", self->m_Name, self->m_UID, pCommandString);
-
- if (!g_pServerLimits->CheckStringCommandLimits(self))
- {
- CBaseClient__Disconnect(self, 1, "Sent too many stringcmd commands");
- return false;
- }
-
- // verify the command we're trying to execute is FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS, if it's a concommand
- char* commandBuf[1040]; // assumedly this is the size of CCommand since we don't have an actual constructor
- memset(commandBuf, 0, sizeof(commandBuf));
- CCommand tempCommand = *(CCommand*)&commandBuf;
-
- if (!CCommand__Tokenize(tempCommand, pCommandString, cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC())
- return false;
-
- ConCommand* command = g_pCVar->FindCommand(tempCommand.Arg(0));
-
- // if the command doesn't exist pass it on to ExecuteStringCommand for script clientcommands and stuff
- if (command && !command->IsFlagSet(FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS))
- {
- // ensure FCVAR_GAMEDLL concommands without FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS can't be executed by remote clients
- if (IsDedicatedServer())
- return false;
-
- if (strcmp(self->m_UID, g_pLocalPlayerUserID))
- return false;
- }
-
- // check for and block abusable legacy portal 2 commands
- // these aren't actually concommands weirdly enough, they seem to just be hardcoded
- if (!Cvar_sv_cheats->GetBool())
- {
- constexpr const char* blockedCommands[] = {
- "emit", // Sound-playing exploit (likely for Portal 2 coop devs testing splitscreen sound or something)
-
- // These both execute a command for every single entity for some reason, nice one valve
- "pre_go_to_hub",
- "pre_go_to_calibration",
-
- "end_movie", // Calls "__MovieFinished" script function, not sure exactly what this does but it certainly isn't needed
- "load_recent_checkpoint" // This is the instant-respawn exploit, literally just calls RespawnPlayer()
- };
-
- int iCmdLength = strlen(tempCommand.Arg(0));
-
- bool bIsBadCommand = false;
- for (auto& blockedCommand : blockedCommands)
- {
- if (iCmdLength != strlen(blockedCommand))
- continue;
-
- for (int i = 0; tempCommand.Arg(0)[i]; i++)
- if (tolower(tempCommand.Arg(0)[i]) != blockedCommand[i])
- goto NEXT_COMMAND; // break out of this loop, then go to next command
-
- // this is a command we need to block
- return false;
- NEXT_COMMAND:;
- }
- }
-
- return CGameClient__ExecuteStringCommand(self, unknown, pCommandString);
-}
-
-// prevent clients from crashing servers through overflowing CNetworkStringTableContainer::WriteBaselines
-bool bWasWritingStringTableSuccessful;
-
-// clang-format off
-AUTOHOOK(CBaseClient__SendServerInfo, engine.dll + 0x104FB0,
-void, __fastcall, (void* self))
-// clang-format on
-{
- bWasWritingStringTableSuccessful = true;
- CBaseClient__SendServerInfo(self);
- if (!bWasWritingStringTableSuccessful)
- CBaseClient__Disconnect(
- self, 1, "Overflowed CNetworkStringTableContainer::WriteBaselines, try restarting your client and reconnecting");
-}
-
-// return null when GetEntByIndex is passed an index >= 0x4000
-// this is called from exactly 1 script clientcommand that can be given an arbitrary index, and going above 0x4000 crashes
-// clang-format off
-AUTOHOOK(GetEntByIndex, server.dll + 0x2A8A50,
-void*, __fastcall, (int i))
-// clang-format on
-{
- const int MAX_ENT_IDX = 0x4000;
-
- if (i >= MAX_ENT_IDX)
- {
- spdlog::warn("GetEntByIndex {} is out of bounds (max {})", i, MAX_ENT_IDX);
- return nullptr;
- }
-
- return GetEntByIndex(i);
-}
-// clang-format off
-AUTOHOOK(CL_CopyExistingEntity, engine.dll + 0x6F940,
-bool, __fastcall, (void* a1))
-// clang-format on
-{
- struct CEntityReadInfo
- {
- BYTE gap[40];
- int nNewEntity;
- };
-
- CEntityReadInfo* pReadInfo = (CEntityReadInfo*)a1;
- if (pReadInfo->nNewEntity >= 0x1000 || pReadInfo->nNewEntity < 0)
- {
- // Value isn't sanitized in release builds for
- // every game powered by the Source Engine 1
- // causing read/write outside of array bounds.
- // This defect has let to the achievement of a
- // full-chain RCE exploit. We hook and perform
- // sanity checks for the value of m_nNewEntity
- // here to prevent this behavior from happening.
- return false;
- }
-
- return CL_CopyExistingEntity(a1);
-}
-
-ON_DLL_LOAD("engine.dll", EngineExploitFixes, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
-
- // allow client/ui to run clientcommands despite restricting servercommands
- module.Offset(0x4FB65).Patch("EB 11");
- module.Offset(0x4FBAC).Patch("EB 16");
-
- // patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails
- {
- CMemoryAddress writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).m_nAddress);
-
- CMemoryAddress addr = module.Offset(0x234ED2);
- addr.Patch("C7 05");
- addr.Offset(2).Patch((BYTE*)&writeAddress, sizeof(writeAddress));
-
- addr.Offset(6).Patch("00 00 00 00");
-
- addr.Offset(10).NOP(5);
- }
-}
-
-ON_DLL_LOAD_RELIESON("server.dll", ServerExploitFixes, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(server.dll)
-
- // ret at the start of CServerGameClients::ClientCommandKeyValues as it has no benefit and is forwarded to client (i.e. security issue)
- // this prevents the attack vector of client=>server=>client, however server=>client also has clientside patches
- module.Offset(0x153920).Patch("C3");
-
- // Dumb ANTITAMPER patches (they negatively impact performance and security)
- constexpr const char* ANTITAMPER_EXPORTS[] = {
- "ANTITAMPER_SPOTCHECK_CODEMARKER",
- "ANTITAMPER_TESTVALUE_CODEMARKER",
- "ANTITAMPER_TRIGGER_CODEMARKER",
- };
-
- // Prevent these from actually doing anything
- for (auto exportName : ANTITAMPER_EXPORTS)
- {
- CMemoryAddress exportAddr = module.GetExport(exportName);
- if (exportAddr)
- {
- // Just return, none of them have any args or are userpurge
- exportAddr.Patch("C3");
- spdlog::info("Patched AntiTamper function export \"{}\"", exportName);
- }
- }
-
- Cvar_ns_exploitfixes_log =
- new ConVar("ns_exploitfixes_log", "1", FCVAR_GAMEDLL, "Whether to log whenever ExploitFixes.cpp blocks/corrects something");
- Cvar_ns_should_log_all_clientcommands =
- new ConVar("ns_should_log_all_clientcommands", "0", FCVAR_NONE, "Whether to log all clientcommands");
-
- Cvar_sv_cheats = g_pCVar->FindVar("sv_cheats");
-}
diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp b/NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp
deleted file mode 100644
index ccb6ac18..00000000
--- a/NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-
-AUTOHOOK_INIT()
-
-static constexpr int LZSS_LOOKSHIFT = 4;
-
-struct lzss_header_t
-{
- unsigned int id;
- unsigned int actualSize;
-};
-
-// Rewrite of CLZSS::SafeUncompress to fix a vulnerability where malicious compressed payloads could cause the decompressor to try to read
-// out of the bounds of the output buffer.
-// clang-format off
-AUTOHOOK(CLZSS__SafeDecompress, engine.dll + 0x432A10,
-unsigned int, __fastcall, (void* self, const unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize))
-// clang-format on
-{
- unsigned int totalBytes = 0;
- int getCmdByte = 0;
- int cmdByte = 0;
-
- lzss_header_t header = *(lzss_header_t*)pInput;
-
- if (!pInput || !header.actualSize || header.id != 0x53535A4C || header.actualSize > unBufSize)
- return 0;
-
- pInput += sizeof(lzss_header_t);
-
- for (;;)
- {
- if (!getCmdByte)
- cmdByte = *pInput++;
-
- getCmdByte = (getCmdByte + 1) & 0x07;
-
- if (cmdByte & 0x01)
- {
- int position = *pInput++ << LZSS_LOOKSHIFT;
- position |= (*pInput >> LZSS_LOOKSHIFT);
- position += 1;
- int count = (*pInput++ & 0x0F) + 1;
- if (count == 1)
- break;
-
- // Ensure reference chunk exists entirely within our buffer
- if (position > totalBytes)
- return 0;
-
- totalBytes += count;
- if (totalBytes > unBufSize)
- return 0;
-
- unsigned char* pSource = pOutput - position;
- for (int i = 0; i < count; i++)
- *pOutput++ = *pSource++;
- }
- else
- {
- totalBytes++;
- if (totalBytes > unBufSize)
- return 0;
-
- *pOutput++ = *pInput++;
- }
- cmdByte = cmdByte >> 1;
- }
-
- if (totalBytes != header.actualSize)
- return 0;
-
- return totalBytes;
-}
-
-ON_DLL_LOAD("engine.dll", ExploitFixes_LZSS, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp b/NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp
deleted file mode 100644
index 3d97f750..00000000
--- a/NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-
-AUTOHOOK_INIT()
-
-INT64(__fastcall* sub_F1320)(DWORD a1, char* a2);
-
-// Reimplementation of an exploitable UTF decoding function in titanfall
-bool __fastcall CheckUTF8Valid(INT64* a1, DWORD* a2, char* strData)
-{
- DWORD v3; // eax
- char* v4; // rbx
- char v5; // si
- char* _strData; // rdi
- char* v7; // rbp
- char v11; // al
- DWORD v12; // er9
- DWORD v13; // ecx
- DWORD v14; // edx
- DWORD v15; // er8
- int v16; // eax
- DWORD v17; // er9
- int v18; // eax
- DWORD v19; // er9
- DWORD v20; // ecx
- int v21; // eax
- int v22; // er9
- DWORD v23; // edx
- int v24; // eax
- int v25; // er9
- DWORD v26; // er9
- DWORD v27; // er10
- DWORD v28; // ecx
- DWORD v29; // edx
- DWORD v30; // er8
- int v31; // eax
- DWORD v32; // er10
- int v33; // eax
- DWORD v34; // er10
- DWORD v35; // ecx
- int v36; // eax
- int v37; // er10
- DWORD v38; // edx
- int v39; // eax
- int v40; // er10
- DWORD v41; // er10
- INT64 v43; // r8
- INT64 v44; // rdx
- INT64 v45; // rcx
- INT64 v46; // rax
- INT64 v47; // rax
- char v48; // al
- INT64 v49; // r8
- INT64 v50; // rdx
- INT64 v51; // rcx
- INT64 v52; // rax
- INT64 v53; // rax
-
- v3 = a2[2];
- v4 = (char*)(a1[1] + *a2);
- v5 = 0;
- _strData = strData;
- v7 = &v4[*((UINT16*)a2 + 2)];
- if (v3 >= 2)
- {
- ++v4;
- --v7;
- if (v3 != 2)
- {
- while (1)
- {
- if (!CMemoryAddress(v4).IsMemoryReadable(1))
- return false; // INVALID
-
- v11 = *v4++; // crash potential
- if (v11 != 92)
- goto LABEL_6;
- v11 = *v4++;
- if (v11 == 110)
- break;
- switch (v11)
- {
- case 't':
- v11 = 9;
- goto LABEL_6;
- case 'r':
- v11 = 13;
- goto LABEL_6;
- case 'b':
- v11 = 8;
- goto LABEL_6;
- case 'f':
- v11 = 12;
- goto LABEL_6;
- }
- if (v11 != 117)
- goto LABEL_6;
- v12 = *v4 | 0x20;
- v13 = v4[1] | 0x20;
- v14 = v4[2] | 0x20;
- v15 = v4[3] | 0x20;
- v16 = 87;
- if (v12 <= 0x39)
- v16 = 48;
- v17 = v12 - v16;
- v18 = 87;
- v19 = v17 << 12;
- if (v13 <= 0x39)
- v18 = 48;
- v20 = v13 - v18;
- v21 = 87;
- v22 = (v20 << 8) | v19;
- if (v14 <= 0x39)
- v21 = 48;
- v23 = v14 - v21;
- v24 = 87;
- v25 = (16 * v23) | v22;
- if (v15 <= 0x39)
- v24 = 48;
- v4 += 4;
- v26 = (v15 - v24) | v25;
- if (v26 - 55296 <= 0x7FF)
- {
- if (v26 >= 0xDC00)
- return true;
- if (*v4 != 92 || v4[1] != 117)
- return true;
-
- v27 = v4[2] | 0x20;
- v28 = v4[3] | 0x20;
- v29 = v4[4] | 0x20;
- v30 = v4[5] | 0x20;
- v31 = 87;
- if (v27 <= 0x39)
- v31 = 48;
- v32 = v27 - v31;
- v33 = 87;
- v34 = v32 << 12;
- if (v28 <= 0x39)
- v33 = 48;
- v35 = v28 - v33;
- v36 = 87;
- v37 = (v35 << 8) | v34;
- if (v29 <= 0x39)
- v36 = 48;
- v38 = v29 - v36;
- v39 = 87;
- v40 = (16 * v38) | v37;
- if (v30 <= 0x39)
- v39 = 48;
- v4 += 6;
- v41 = ((v30 - v39) | v40) - 56320;
- if (v41 > 0x3FF)
- return true;
- v26 = v41 | ((v26 - 55296) << 10);
- }
- _strData += (DWORD)sub_F1320(v26, _strData);
- LABEL_7:
- if (v4 == v7)
- goto LABEL_48;
- }
- v11 = 10;
- LABEL_6:
- v5 |= v11;
- *_strData++ = v11;
- goto LABEL_7;
- }
- }
-LABEL_48:
- return true;
-}
-
-// prevent utf8 parser from crashing when provided bad data, which can be sent through user-controlled openinvites
-// clang-format off
-AUTOHOOK(Rson_ParseUTF8, engine.dll + 0xEF670,
-bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) // 48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC 20 8B 1A
-// clang-format on
-{
- static void* targetRetAddr = CModule("engine.dll").FindPattern("84 C0 75 2C 49 8B 16");
-
- // only call if we're parsing utf8 data from the network (i.e. communities), otherwise we get perf issues
- void* pReturnAddress =
-#ifdef _MSC_VER
- _ReturnAddress()
-#else
- __builtin_return_address(0)
-#endif
- ;
-
- if (pReturnAddress == targetRetAddr && !CheckUTF8Valid(a1, a2, strData))
- return false;
-
- return Rson_ParseUTF8(a1, a2, strData);
-}
-
-ON_DLL_LOAD("engine.dll", EngineExploitFixes_UTF8Parser, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- sub_F1320 = module.FindPattern("83 F9 7F 77 08 88 0A").RCast<INT64(__fastcall*)(DWORD, char*)>();
-}
diff --git a/NorthstarDLL/shared/exploit_fixes/ns_limits.cpp b/NorthstarDLL/shared/exploit_fixes/ns_limits.cpp
deleted file mode 100644
index bd855ee4..00000000
--- a/NorthstarDLL/shared/exploit_fixes/ns_limits.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-#include "ns_limits.h"
-#include "engine/hoststate.h"
-#include "client/r2client.h"
-#include "engine/r2engine.h"
-#include "server/r2server.h"
-#include "core/tier0.h"
-#include "core/math/vector.h"
-#include "server/auth/serverauthentication.h"
-
-AUTOHOOK_INIT()
-
-ServerLimitsManager* g_pServerLimits;
-
-float (*CEngineServer__GetTimescale)();
-
-// todo: make this work on higher timescales, also possibly disable when sv_cheats is set
-void ServerLimitsManager::RunFrame(double flCurrentTime, float flFrameTime)
-{
- if (Cvar_sv_antispeedhack_enable->GetBool())
- {
- // for each player, set their usercmd processing budget for the frame to the last frametime for the server
- for (int i = 0; i < g_pGlobals->m_nMaxClients; i++)
- {
- CBaseClient* player = &g_pClientArray[i];
-
- if (m_PlayerLimitData.find(player) != m_PlayerLimitData.end())
- {
- PlayerLimitData* pLimitData = &g_pServerLimits->m_PlayerLimitData[player];
- if (pLimitData->flFrameUserCmdBudget < g_pGlobals->m_flTickInterval * Cvar_sv_antispeedhack_maxtickbudget->GetFloat())
- {
- pLimitData->flFrameUserCmdBudget += g_pServerLimits->Cvar_sv_antispeedhack_budgetincreasemultiplier->GetFloat() *
- fmax(flFrameTime, g_pGlobals->m_flFrameTime * CEngineServer__GetTimescale());
- }
- }
- }
- }
-}
-
-void ServerLimitsManager::AddPlayer(CBaseClient* player)
-{
- PlayerLimitData limitData;
- limitData.flFrameUserCmdBudget =
- g_pGlobals->m_flTickInterval * CEngineServer__GetTimescale() * Cvar_sv_antispeedhack_maxtickbudget->GetFloat();
-
- m_PlayerLimitData.insert(std::make_pair(player, limitData));
-}
-
-void ServerLimitsManager::RemovePlayer(CBaseClient* player)
-{
- if (m_PlayerLimitData.find(player) != m_PlayerLimitData.end())
- m_PlayerLimitData.erase(player);
-}
-
-bool ServerLimitsManager::CheckStringCommandLimits(CBaseClient* player)
-{
- if (CVar_sv_quota_stringcmdspersecond->GetInt() != -1)
- {
- // note: this isn't super perfect, legit clients can trigger it in lobby if they try, mostly good enough tho imo
- if (Plat_FloatTime() - m_PlayerLimitData[player].lastClientCommandQuotaStart >= 1.0)
- {
- // reset quota
- m_PlayerLimitData[player].lastClientCommandQuotaStart = Plat_FloatTime();
- m_PlayerLimitData[player].numClientCommandsInQuota = 0;
- }
-
- m_PlayerLimitData[player].numClientCommandsInQuota++;
- if (m_PlayerLimitData[player].numClientCommandsInQuota > CVar_sv_quota_stringcmdspersecond->GetInt())
- {
- // too many stringcmds, dc player
- return false;
- }
- }
-
- return true;
-}
-
-bool ServerLimitsManager::CheckChatLimits(CBaseClient* player)
-{
- if (Plat_FloatTime() - m_PlayerLimitData[player].lastSayTextLimitStart >= 1.0)
- {
- m_PlayerLimitData[player].lastSayTextLimitStart = Plat_FloatTime();
- m_PlayerLimitData[player].sayTextLimitCount = 0;
- }
-
- if (m_PlayerLimitData[player].sayTextLimitCount >= Cvar_sv_max_chat_messages_per_sec->GetInt())
- return false;
-
- m_PlayerLimitData[player].sayTextLimitCount++;
- return true;
-}
-
-// clang-format off
-AUTOHOOK(CNetChan__ProcessMessages, engine.dll + 0x2140A0,
-char, __fastcall, (void* self, void* buf))
-// clang-format on
-{
- enum eNetChanLimitMode
- {
- NETCHANLIMIT_WARN,
- NETCHANLIMIT_KICK
- };
-
- double startTime = Plat_FloatTime();
- char ret = CNetChan__ProcessMessages(self, buf);
-
- // check processing limits, unless we're in a level transition
- if (g_pHostState->m_iCurrentState == HostState_t::HS_RUN && ThreadInServerFrameThread())
- {
- // player that sent the message
- CBaseClient* sender = *(CBaseClient**)((char*)self + 368);
-
- // if no sender, return
- // relatively certain this is fine?
- if (!sender || !g_pServerLimits->m_PlayerLimitData.count(sender))
- return ret;
-
- // reset every second
- if (startTime - g_pServerLimits->m_PlayerLimitData[sender].lastNetChanProcessingLimitStart >= 1.0 ||
- g_pServerLimits->m_PlayerLimitData[sender].lastNetChanProcessingLimitStart == -1.0)
- {
- g_pServerLimits->m_PlayerLimitData[sender].lastNetChanProcessingLimitStart = startTime;
- g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime = 0.0;
- }
- g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime += (Plat_FloatTime() * 1000) - (startTime * 1000);
-
- if (g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime >=
- g_pServerLimits->Cvar_net_chan_limit_msec_per_sec->GetInt())
- {
- spdlog::warn(
- "Client {} hit netchan processing limit with {}ms of processing time this second (max is {})",
- (char*)sender + 0x16,
- g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime,
- g_pServerLimits->Cvar_net_chan_limit_msec_per_sec->GetInt());
-
- // never kick local player
- if (g_pServerLimits->Cvar_net_chan_limit_mode->GetInt() != NETCHANLIMIT_WARN && strcmp(g_pLocalPlayerUserID, sender->m_UID))
- {
- CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit");
- return false;
- }
- }
- }
-
- return ret;
-}
-
-bool ServerLimitsManager::CheckConnectionlessPacketLimits(netpacket_t* packet)
-{
- static const ConVar* Cvar_net_data_block_enabled = g_pCVar->FindVar("net_data_block_enabled");
-
- // don't ratelimit datablock packets as long as datablock is enabled
- if (packet->adr.type == NA_IP &&
- (!(packet->data[4] == 'N' && Cvar_net_data_block_enabled->GetBool()) || !Cvar_net_data_block_enabled->GetBool()))
- {
- // bad lookup: optimise later tm
- UnconnectedPlayerLimitData* sendData = nullptr;
- for (UnconnectedPlayerLimitData& foundSendData : g_pServerLimits->m_UnconnectedPlayerLimitData)
- {
- if (!memcmp(packet->adr.ip, foundSendData.ip, 16))
- {
- sendData = &foundSendData;
- break;
- }
- }
-
- if (!sendData)
- {
- sendData = &g_pServerLimits->m_UnconnectedPlayerLimitData.emplace_back();
- memcpy(sendData->ip, packet->adr.ip, 16);
- }
-
- if (Plat_FloatTime() < sendData->timeoutEnd)
- return false;
-
- if (Plat_FloatTime() - sendData->lastQuotaStart >= 1.0)
- {
- sendData->lastQuotaStart = Plat_FloatTime();
- sendData->packetCount = 0;
- }
-
- sendData->packetCount++;
-
- if (sendData->packetCount >= g_pServerLimits->Cvar_sv_querylimit_per_sec->GetInt())
- {
- spdlog::warn(
- "Client went over connectionless ratelimit of {} per sec with packet of type {}",
- g_pServerLimits->Cvar_sv_querylimit_per_sec->GetInt(),
- packet->data[4]);
-
- // timeout for a minute
- sendData->timeoutEnd = Plat_FloatTime() + 60.0;
- return false;
- }
- }
-
- return true;
-}
-
-// this is weird and i'm not sure if it's correct, so not using for now
-/*AUTOHOOK(CBasePlayer__PhysicsSimulate, server.dll + 0x5A6E50, bool, __fastcall, (void* self, int a2, char a3))
-{
- spdlog::info("CBasePlayer::PhysicsSimulate");
- return CBasePlayer__PhysicsSimulate(self, a2, a3);
-}*/
-
-struct alignas(4) SV_CUserCmd
-{
- DWORD command_number;
- DWORD tick_count;
- float command_time;
- Vector3 worldViewAngles;
- BYTE gap18[4];
- Vector3 localViewAngles;
- Vector3 attackangles;
- Vector3 move;
- DWORD buttons;
- BYTE impulse;
- short weaponselect;
- DWORD meleetarget;
- BYTE gap4C[24];
- char headoffset;
- BYTE gap65[11];
- Vector3 cameraPos;
- Vector3 cameraAngles;
- BYTE gap88[4];
- int tickSomething;
- DWORD dword90;
- DWORD predictedServerEventAck;
- DWORD dword98;
- float frameTime;
-};
-
-// clang-format off
-AUTOHOOK(CPlayerMove__RunCommand, server.dll + 0x5B8100,
-void, __fastcall, (void* self, CBasePlayer* player, SV_CUserCmd* pUserCmd, uint64_t a4))
-// clang-format on
-{
- if (g_pServerLimits->Cvar_sv_antispeedhack_enable->GetBool())
- {
- CBaseClient* pClient = &g_pClientArray[player->m_nPlayerIndex - 1];
-
- if (g_pServerLimits->m_PlayerLimitData.find(pClient) != g_pServerLimits->m_PlayerLimitData.end())
- {
- PlayerLimitData* pLimitData = &g_pServerLimits->m_PlayerLimitData[pClient];
-
- pLimitData->flFrameUserCmdBudget = fmax(0.0, pLimitData->flFrameUserCmdBudget - pUserCmd->frameTime);
-
- if (pLimitData->flFrameUserCmdBudget <= 0.0)
- {
- spdlog::warn("player {} went over usercmd budget ({})", pClient->m_Name, pLimitData->flFrameUserCmdBudget);
- return;
- }
- }
- }
-
- CPlayerMove__RunCommand(self, player, pUserCmd, a4);
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ServerLimits, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
-
- g_pServerLimits = new ServerLimitsManager;
-
- g_pServerLimits->CVar_sv_quota_stringcmdspersecond = new ConVar(
- "sv_quota_stringcmdspersecond",
- "60",
- FCVAR_GAMEDLL,
- "How many string commands per second clients are allowed to submit, 0 to disallow all string commands, -1 to disable");
- g_pServerLimits->Cvar_net_chan_limit_mode =
- new ConVar("net_chan_limit_mode", "0", FCVAR_GAMEDLL, "The mode for netchan processing limits: 0 = warn, 1 = kick");
- g_pServerLimits->Cvar_net_chan_limit_msec_per_sec = new ConVar(
- "net_chan_limit_msec_per_sec",
- "100",
- FCVAR_GAMEDLL,
- "Netchannel processing is limited to so many milliseconds, abort connection if exceeding budget");
- g_pServerLimits->Cvar_sv_querylimit_per_sec = new ConVar("sv_querylimit_per_sec", "15", FCVAR_GAMEDLL, "");
- g_pServerLimits->Cvar_sv_max_chat_messages_per_sec = new ConVar("sv_max_chat_messages_per_sec", "5", FCVAR_GAMEDLL, "");
- g_pServerLimits->Cvar_sv_antispeedhack_enable =
- new ConVar("sv_antispeedhack_enable", "0", FCVAR_NONE, "whether to enable antispeedhack protections");
- g_pServerLimits->Cvar_sv_antispeedhack_maxtickbudget = new ConVar(
- "sv_antispeedhack_maxtickbudget",
- "64",
- FCVAR_GAMEDLL,
- "Maximum number of client-issued usercmd ticks that can be replayed in packet loss conditions");
- g_pServerLimits->Cvar_sv_antispeedhack_budgetincreasemultiplier = new ConVar(
- "sv_antispeedhack_budgetincreasemultiplier",
- "1",
- FCVAR_GAMEDLL,
- "Increase usercmd processing budget by tickinterval * value per tick");
-
- CEngineServer__GetTimescale = module.Offset(0x240840).RCast<float (*)()>();
-}
-
-ON_DLL_LOAD("server.dll", ServerLimitsServer, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(server.dll)
-}
diff --git a/NorthstarDLL/shared/exploit_fixes/ns_limits.h b/NorthstarDLL/shared/exploit_fixes/ns_limits.h
deleted file mode 100644
index 546fec6f..00000000
--- a/NorthstarDLL/shared/exploit_fixes/ns_limits.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-#include "engine/r2engine.h"
-#include "core/convar/convar.h"
-#include <unordered_map>
-
-struct PlayerLimitData
-{
- double lastClientCommandQuotaStart = -1.0;
- int numClientCommandsInQuota = 0;
-
- double lastNetChanProcessingLimitStart = -1.0;
- double netChanProcessingLimitTime = 0.0;
-
- double lastSayTextLimitStart = -1.0;
- int sayTextLimitCount = 0;
-
- float flFrameUserCmdBudget = 0.0;
-};
-
-struct UnconnectedPlayerLimitData
-{
- char ip[16];
- double lastQuotaStart = 0.0;
- int packetCount = 0;
- double timeoutEnd = -1.0;
-};
-
-class ServerLimitsManager
-{
-public:
- ConVar* CVar_sv_quota_stringcmdspersecond;
- ConVar* Cvar_net_chan_limit_mode;
- ConVar* Cvar_net_chan_limit_msec_per_sec;
- ConVar* Cvar_sv_querylimit_per_sec;
- ConVar* Cvar_sv_max_chat_messages_per_sec;
- ConVar* Cvar_sv_antispeedhack_enable;
- ConVar* Cvar_sv_antispeedhack_maxtickbudget;
- ConVar* Cvar_sv_antispeedhack_budgetincreasemultiplier;
-
- std::unordered_map<CBaseClient*, PlayerLimitData> m_PlayerLimitData;
- std::vector<UnconnectedPlayerLimitData> m_UnconnectedPlayerLimitData;
-
-public:
- void RunFrame(double flCurrentTime, float flFrameTime);
- void AddPlayer(CBaseClient* player);
- void RemovePlayer(CBaseClient* player);
- bool CheckStringCommandLimits(CBaseClient* player);
- bool CheckChatLimits(CBaseClient* player);
- bool CheckConnectionlessPacketLimits(netpacket_t* packet);
-};
-
-extern ServerLimitsManager* g_pServerLimits;
diff --git a/NorthstarDLL/shared/keyvalues.cpp b/NorthstarDLL/shared/keyvalues.cpp
deleted file mode 100644
index 88753723..00000000
--- a/NorthstarDLL/shared/keyvalues.cpp
+++ /dev/null
@@ -1,1322 +0,0 @@
-#include "keyvalues.h"
-#include <winnt.h>
-
-// implementation of the ConVar class
-// heavily based on https://github.com/Mauler125/r5sdk/blob/master/r5dev/vpc/keyvalues.cpp
-
-typedef int HKeySymbol;
-#define INVALID_KEY_SYMBOL (-1)
-
-#define MAKE_3_BYTES_FROM_1_AND_2(x1, x2) ((((uint16_t)x2) << 8) | (uint8_t)(x1))
-#define SPLIT_3_BYTES_INTO_1_AND_2(x1, x2, x3) \
- do \
- { \
- x1 = (uint8_t)(x3); \
- x2 = (uint16_t)((x3) >> 8); \
- } while (0)
-
-struct CKeyValuesSystem
-{
-public:
- struct __VTable
- {
- char pad0[8 * 3]; // 2 methods
- HKeySymbol (*GetSymbolForString)(CKeyValuesSystem* self, const char* name, bool bCreate);
- const char* (*GetStringForSymbol)(CKeyValuesSystem* self, HKeySymbol symbol);
- char pad1[8 * 5];
- HKeySymbol (*GetSymbolForStringCaseSensitive)(
- CKeyValuesSystem* self, HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate);
- };
-
- const __VTable* m_pVtable;
-};
-
-int (*V_UTF8ToUnicode)(const char* pUTF8, wchar_t* pwchDest, int cubDestSizeInBytes);
-int (*V_UnicodeToUTF8)(const wchar_t* pUnicode, char* pUTF8, int cubDestSizeInBytes);
-CKeyValuesSystem* (*KeyValuesSystem)();
-
-KeyValues::KeyValues() {} // default constructor for copying and such
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-// Input : *pszSetName -
-//-----------------------------------------------------------------------------
-KeyValues::KeyValues(const char* pszSetName)
-{
- Init();
- SetName(pszSetName);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-// Input : *pszSetName -
-// *pszFirstKey -
-// *pszFirstValue -
-//-----------------------------------------------------------------------------
-KeyValues::KeyValues(const char* pszSsetName, const char* pszFirstKey, const char* pszFirstValue)
-{
- Init();
- SetName(pszSsetName);
- SetString(pszFirstKey, pszFirstValue);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-// Input : *pszSetName -
-// *pszFirstKey -
-// *pwszFirstValue -
-//-----------------------------------------------------------------------------
-KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, const wchar_t* pwszFirstValue)
-{
- Init();
- SetName(pszSetName);
- SetWString(pszFirstKey, pwszFirstValue);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-// Input : *pszSetName -
-// *pszFirstKey -
-// iFirstValue -
-//-----------------------------------------------------------------------------
-KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue)
-{
- Init();
- SetName(pszSetName);
- SetInt(pszFirstKey, iFirstValue);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-// Input : *pszSetName -
-// *pszFirstKey -
-// *pszFirstValue -
-// *pszSecondKey -
-// *pszSecondValue -
-//-----------------------------------------------------------------------------
-KeyValues::KeyValues(
- const char* pszSetName, const char* pszFirstKey, const char* pszFirstValue, const char* pszSecondKey, const char* pszSecondValue)
-{
- Init();
- SetName(pszSetName);
- SetString(pszFirstKey, pszFirstValue);
- SetString(pszSecondKey, pszSecondValue);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-// Input : *pszSetName -
-// *pszFirstKey -
-// iFirstValue -
-// *pszSecondKey -
-// iSecondValue -
-//-----------------------------------------------------------------------------
-KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue, const char* pszSecondKey, int iSecondValue)
-{
- Init();
- SetName(pszSetName);
- SetInt(pszFirstKey, iFirstValue);
- SetInt(pszSecondKey, iSecondValue);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Destructor
-//-----------------------------------------------------------------------------
-KeyValues::~KeyValues(void)
-{
- RemoveEverything();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Initialize member variables
-//-----------------------------------------------------------------------------
-void KeyValues::Init(void)
-{
- m_iKeyName = 0;
- m_iKeyNameCaseSensitive1 = 0;
- m_iKeyNameCaseSensitive2 = 0;
- m_iDataType = TYPE_NONE;
-
- m_pSub = nullptr;
- m_pPeer = nullptr;
- m_pChain = nullptr;
-
- m_sValue = nullptr;
- m_wsValue = nullptr;
- m_pValue = nullptr;
-
- m_bHasEscapeSequences = 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Clear out all subkeys, and the current value
-//-----------------------------------------------------------------------------
-void KeyValues::Clear(void)
-{
- delete m_pSub;
- m_pSub = nullptr;
- m_iDataType = TYPE_NONE;
-}
-
-//-----------------------------------------------------------------------------
-// for backwards compat - we used to need this to force the free to run from the same DLL
-// as the alloc
-//-----------------------------------------------------------------------------
-void KeyValues::DeleteThis(void)
-{
- delete this;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: remove everything
-//-----------------------------------------------------------------------------
-void KeyValues::RemoveEverything(void)
-{
- KeyValues* dat;
- KeyValues* datNext = nullptr;
- for (dat = m_pSub; dat != nullptr; dat = datNext)
- {
- datNext = dat->m_pPeer;
- dat->m_pPeer = nullptr;
- delete dat;
- }
-
- for (dat = m_pPeer; dat && dat != this; dat = datNext)
- {
- datNext = dat->m_pPeer;
- dat->m_pPeer = nullptr;
- delete dat;
- }
-
- delete[] m_sValue;
- m_sValue = nullptr;
- delete[] m_wsValue;
- m_wsValue = nullptr;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Find a keyValue, create it if it is not found.
-// Set bCreate to true to create the key if it doesn't already exist
-// (which ensures a valid pointer will be returned)
-// Input : *pszKeyName -
-// bCreate -
-// Output : *KeyValues
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::FindKey(const char* pszKeyName, bool bCreate)
-{
- assert_msg(this, "Member function called on NULL KeyValues");
-
- if (!pszKeyName || !*pszKeyName)
- return this;
-
- const char* pSubStr = strchr(pszKeyName, '/');
- const char* pSearchStr = pszKeyName;
- if (pSubStr && !*(pSubStr + 1))
- {
- // if key name is just '/', then use it as a key directly
- pSearchStr = pSubStr;
- pSubStr = nullptr;
- }
-
- HKeySymbol iSearchStr = KeyValuesSystem()->m_pVtable->GetSymbolForString(KeyValuesSystem(), pSearchStr, bCreate);
- if (iSearchStr == INVALID_KEY_SYMBOL)
- {
- // not found, couldn't possibly be in key value list
- return nullptr;
- }
-
- KeyValues* pLastKVs = nullptr;
- KeyValues* pCurrentKVs;
- // find the searchStr in the current peer list
- for (pCurrentKVs = m_pSub; pCurrentKVs != nullptr; pCurrentKVs = pCurrentKVs->m_pPeer)
- {
- pLastKVs = pCurrentKVs; // record the last item looked at (for if we need to append to the end of the list)
-
- // symbol compare
- if (pLastKVs->m_iKeyName == (uint32_t)iSearchStr)
- break;
- }
-
- if (!pCurrentKVs && m_pChain)
- pCurrentKVs = m_pChain->FindKey(pSearchStr, false);
-
- // make sure a key was found
- if (!pCurrentKVs)
- {
- if (bCreate)
- {
- // we need to create a new key
- pCurrentKVs = new KeyValues(pSearchStr);
- // Assert(dat != NULL);
-
- // insert new key at end of list
- if (pLastKVs)
- pLastKVs->m_pPeer = pCurrentKVs;
- else
- m_pSub = pCurrentKVs;
-
- pCurrentKVs->m_pPeer = nullptr;
-
- // a key graduates to be a submsg as soon as it's m_pSub is set
- // this should be the only place m_pSub is set
- m_iDataType = TYPE_NONE;
- }
- else
- {
- return nullptr;
- }
- }
-
- // if we've still got a subStr we need to keep looking deeper in the tree
- if (pSubStr)
- {
- // recursively chain down through the paths in the string
- return pCurrentKVs->FindKey(pSubStr + 1, bCreate);
- }
-
- return pCurrentKVs;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Locate last child. Returns NULL if we have no children
-// Output : *KeyValues
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::FindLastSubKey(void) const
-{
- // No children?
- if (m_pSub == nullptr)
- return nullptr;
-
- // Scan for the last one
- KeyValues* pLastChild = m_pSub;
- while (pLastChild->m_pPeer)
- pLastChild = pLastChild->m_pPeer;
- return pLastChild;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Adds a subkey. Make sure the subkey isn't a child of some other keyvalues
-// Input : *pSubKey -
-//-----------------------------------------------------------------------------
-void KeyValues::AddSubKey(KeyValues* pSubkey)
-{
- // Make sure the subkey isn't a child of some other keyvalues
- assert(pSubkey != nullptr);
- assert(pSubkey->m_pPeer == nullptr);
-
- // add into subkey list
- if (m_pSub == nullptr)
- {
- m_pSub = pSubkey;
- }
- else
- {
- KeyValues* pTempDat = m_pSub;
- while (pTempDat->GetNextKey() != nullptr)
- {
- pTempDat = pTempDat->GetNextKey();
- }
-
- pTempDat->SetNextKey(pSubkey);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Remove a subkey from the list
-// Input : *pSubKey -
-//-----------------------------------------------------------------------------
-void KeyValues::RemoveSubKey(KeyValues* pSubKey)
-{
- if (!pSubKey)
- return;
-
- // check the list pointer
- if (m_pSub == pSubKey)
- {
- m_pSub = pSubKey->m_pPeer;
- }
- else
- {
- // look through the list
- KeyValues* kv = m_pSub;
- while (kv->m_pPeer)
- {
- if (kv->m_pPeer == pSubKey)
- {
- kv->m_pPeer = pSubKey->m_pPeer;
- break;
- }
-
- kv = kv->m_pPeer;
- }
- }
-
- pSubKey->m_pPeer = nullptr;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Insert a subkey at index
-// Input : nIndex -
-// *pSubKey -
-//-----------------------------------------------------------------------------
-void KeyValues::InsertSubKey(int nIndex, KeyValues* pSubKey)
-{
- // Sub key must be valid and not part of another chain
- assert(pSubKey && pSubKey->m_pPeer == nullptr);
-
- if (nIndex == 0)
- {
- pSubKey->m_pPeer = m_pSub;
- m_pSub = pSubKey;
- return;
- }
- else
- {
- int nCurrentIndex = 0;
- for (KeyValues* pIter = GetFirstSubKey(); pIter != nullptr; pIter = pIter->GetNextKey())
- {
- ++nCurrentIndex;
- if (nCurrentIndex == nIndex)
- {
- pSubKey->m_pPeer = pIter->m_pPeer;
- pIter->m_pPeer = pSubKey;
- return;
- }
- }
- // Index is out of range if we get here
- assert(0);
- return;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Checks if key contains a subkey
-// Input : *pSubKey -
-// Output : true if contains, false otherwise
-//-----------------------------------------------------------------------------
-bool KeyValues::ContainsSubKey(KeyValues* pSubKey)
-{
- for (KeyValues* pIter = GetFirstSubKey(); pIter != nullptr; pIter = pIter->GetNextKey())
- {
- if (pSubKey == pIter)
- {
- return true;
- }
- }
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Swaps existing subkey with another
-// Input : *pExistingSubkey -
-// *pNewSubKey -
-//-----------------------------------------------------------------------------
-void KeyValues::SwapSubKey(KeyValues* pExistingSubkey, KeyValues* pNewSubKey)
-{
- assert(pExistingSubkey != nullptr && pNewSubKey != nullptr);
-
- // Make sure the new sub key isn't a child of some other keyvalues
- assert(pNewSubKey->m_pPeer == nullptr);
-
- // Check the list pointer
- if (m_pSub == pExistingSubkey)
- {
- pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer;
- pExistingSubkey->m_pPeer = nullptr;
- m_pSub = pNewSubKey;
- }
- else
- {
- // Look through the list
- KeyValues* kv = m_pSub;
- while (kv->m_pPeer)
- {
- if (kv->m_pPeer == pExistingSubkey)
- {
- pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer;
- pExistingSubkey->m_pPeer = nullptr;
- kv->m_pPeer = pNewSubKey;
- break;
- }
-
- kv = kv->m_pPeer;
- }
- // Existing sub key should always be found, otherwise it's a bug in the calling code.
- assert(kv->m_pPeer != nullptr);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Elides subkey
-// Input : *pSubKey -
-//-----------------------------------------------------------------------------
-void KeyValues::ElideSubKey(KeyValues* pSubKey)
-{
- // This pointer's "next" pointer needs to be fixed up when we elide the key
- KeyValues** ppPointerToFix = &m_pSub;
- for (KeyValues* pKeyIter = m_pSub; pKeyIter != nullptr; ppPointerToFix = &pKeyIter->m_pPeer, pKeyIter = pKeyIter->GetNextKey())
- {
- if (pKeyIter == pSubKey)
- {
- if (pSubKey->m_pSub == nullptr)
- {
- // No children, simply remove the key
- *ppPointerToFix = pSubKey->m_pPeer;
- delete pSubKey;
- }
- else
- {
- *ppPointerToFix = pSubKey->m_pSub;
- // Attach the remainder of this chain to the last child of pSubKey
- KeyValues* pChildIter = pSubKey->m_pSub;
- while (pChildIter->m_pPeer != nullptr)
- {
- pChildIter = pChildIter->m_pPeer;
- }
- // Now points to the last child of pSubKey
- pChildIter->m_pPeer = pSubKey->m_pPeer;
- // Detach the node to be elided
- pSubKey->m_pSub = nullptr;
- pSubKey->m_pPeer = nullptr;
- delete pSubKey;
- }
- return;
- }
- }
- // Key not found; that's caller error.
- assert(0);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Check if a keyName has no value assigned to it.
-// Input : *pszKeyName -
-// Output : true on success, false otherwise
-//-----------------------------------------------------------------------------
-bool KeyValues::IsEmpty(const char* pszKeyName)
-{
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (!pKey)
- return true;
-
- if (pKey->m_iDataType == TYPE_NONE && pKey->m_pSub == nullptr)
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: gets the first true sub key
-// Output : *KeyValues
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::GetFirstTrueSubKey(void) const
-{
- assert_msg(this, "Member function called on NULL KeyValues");
- KeyValues* pRet = this ? m_pSub : nullptr;
- while (pRet && pRet->m_iDataType != TYPE_NONE)
- pRet = pRet->m_pPeer;
-
- return pRet;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: gets the next true sub key
-// Output : *KeyValues
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::GetNextTrueSubKey(void) const
-{
- assert_msg(this, "Member function called on NULL KeyValues");
- KeyValues* pRet = this ? m_pPeer : nullptr;
- while (pRet && pRet->m_iDataType != TYPE_NONE)
- pRet = pRet->m_pPeer;
-
- return pRet;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: gets the first value
-// Output : *KeyValues
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::GetFirstValue(void) const
-{
- assert_msg(this, "Member function called on NULL KeyValues");
- KeyValues* pRet = this ? m_pSub : nullptr;
- while (pRet && pRet->m_iDataType == TYPE_NONE)
- pRet = pRet->m_pPeer;
-
- return pRet;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: gets the next value
-// Output : *KeyValues
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::GetNextValue(void) const
-{
- assert_msg(this, "Member function called on NULL KeyValues");
- KeyValues* pRet = this ? m_pPeer : nullptr;
- while (pRet && pRet->m_iDataType == TYPE_NONE)
- pRet = pRet->m_pPeer;
-
- return pRet;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return the first subkey in the list
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::GetFirstSubKey() const
-{
- assert_msg(this, "Member function called on NULL KeyValues");
- return this ? m_pSub : nullptr;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return the next subkey
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::GetNextKey() const
-{
- assert_msg(this, "Member function called on NULL KeyValues");
- return this ? m_pPeer : nullptr;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the name of the current key section
-// Output : const char*
-//-----------------------------------------------------------------------------
-const char* KeyValues::GetName(void) const
-{
- return KeyValuesSystem()->m_pVtable->GetStringForSymbol(
- KeyValuesSystem(), MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2));
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the integer value of a keyName. Default value is returned
-// if the keyName can't be found.
-// Input : *pszKeyName -
-// nDefaultValue -
-// Output : int
-//-----------------------------------------------------------------------------
-int KeyValues::GetInt(const char* pszKeyName, int iDefaultValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (pKey)
- {
- switch (pKey->m_iDataType)
- {
- case TYPE_STRING:
- return atoi(pKey->m_sValue);
- case TYPE_WSTRING:
- return _wtoi(pKey->m_wsValue);
- case TYPE_FLOAT:
- return static_cast<int>(pKey->m_flValue);
- case TYPE_UINT64:
- // can't convert, since it would lose data
- assert(0);
- return 0;
- case TYPE_INT:
- case TYPE_PTR:
- default:
- return pKey->m_iValue;
- };
- }
- return iDefaultValue;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the integer value of a keyName. Default value is returned
-// if the keyName can't be found.
-// Input : *pszKeyName -
-// nDefaultValue -
-// Output : uint64_t
-//-----------------------------------------------------------------------------
-uint64_t KeyValues::GetUint64(const char* pszKeyName, uint64_t nDefaultValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (pKey)
- {
- switch (pKey->m_iDataType)
- {
- case TYPE_STRING:
- {
- uint64_t uiResult = 0ull;
- sscanf(pKey->m_sValue, "%lld", &uiResult);
- return uiResult;
- }
- case TYPE_WSTRING:
- {
- uint64_t uiResult = 0ull;
- swscanf(pKey->m_wsValue, L"%lld", &uiResult);
- return uiResult;
- }
- case TYPE_FLOAT:
- return static_cast<int>(pKey->m_flValue);
- case TYPE_UINT64:
- return *reinterpret_cast<uint64_t*>(pKey->m_sValue);
- case TYPE_PTR:
- return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pKey->m_pValue));
- case TYPE_INT:
- default:
- return pKey->m_iValue;
- };
- }
- return nDefaultValue;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the pointer value of a keyName. Default value is returned
-// if the keyName can't be found.
-// Input : *pszKeyName -
-// pDefaultValue -
-// Output : void*
-//-----------------------------------------------------------------------------
-void* KeyValues::GetPtr(const char* pszKeyName, void* pDefaultValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (pKey)
- {
- switch (pKey->m_iDataType)
- {
- case TYPE_PTR:
- return pKey->m_pValue;
-
- case TYPE_WSTRING:
- case TYPE_STRING:
- case TYPE_FLOAT:
- case TYPE_INT:
- case TYPE_UINT64:
- default:
- return nullptr;
- };
- }
- return pDefaultValue;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the float value of a keyName. Default value is returned
-// if the keyName can't be found.
-// Input : *pszKeyName -
-// flDefaultValue -
-// Output : float
-//-----------------------------------------------------------------------------
-float KeyValues::GetFloat(const char* pszKeyName, float flDefaultValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (pKey)
- {
- switch (pKey->m_iDataType)
- {
- case TYPE_STRING:
- return static_cast<float>(atof(pKey->m_sValue));
- case TYPE_WSTRING:
- return static_cast<float>(_wtof(pKey->m_wsValue)); // no wtof
- case TYPE_FLOAT:
- return pKey->m_flValue;
- case TYPE_INT:
- return static_cast<float>(pKey->m_iValue);
- case TYPE_UINT64:
- return static_cast<float>((*(reinterpret_cast<uint64_t*>(pKey->m_sValue))));
- case TYPE_PTR:
- default:
- return 0.0f;
- };
- }
- return flDefaultValue;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the string pointer of a keyName. Default value is returned
-// if the keyName can't be found.
-// // Input : *pszKeyName -
-// pszDefaultValue -
-// Output : const char*
-//-----------------------------------------------------------------------------
-const char* KeyValues::GetString(const char* pszKeyName, const char* pszDefaultValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (pKey)
- {
- // convert the data to string form then return it
- char buf[64];
- switch (pKey->m_iDataType)
- {
- case TYPE_FLOAT:
- snprintf(buf, sizeof(buf), "%f", pKey->m_flValue);
- SetString(pszKeyName, buf);
- break;
- case TYPE_PTR:
- snprintf(buf, sizeof(buf), "%lld", reinterpret_cast<uint64_t>(pKey->m_pValue));
- SetString(pszKeyName, buf);
- break;
- case TYPE_INT:
- snprintf(buf, sizeof(buf), "%d", pKey->m_iValue);
- SetString(pszKeyName, buf);
- break;
- case TYPE_UINT64:
- snprintf(buf, sizeof(buf), "%lld", *(reinterpret_cast<uint64_t*>(pKey->m_sValue)));
- SetString(pszKeyName, buf);
- break;
- case TYPE_COLOR:
- snprintf(buf, sizeof(buf), "%d %d %d %d", pKey->m_Color[0], pKey->m_Color[1], pKey->m_Color[2], pKey->m_Color[3]);
- SetString(pszKeyName, buf);
- break;
-
- case TYPE_WSTRING:
- {
- // convert the string to char *, set it for future use, and return it
- char wideBuf[512];
- int result = V_UnicodeToUTF8(pKey->m_wsValue, wideBuf, 512);
- if (result)
- {
- // note: this will copy wideBuf
- SetString(pszKeyName, wideBuf);
- }
- else
- {
- return pszDefaultValue;
- }
- break;
- }
- case TYPE_STRING:
- break;
- default:
- return pszDefaultValue;
- };
-
- return pKey->m_sValue;
- }
- return pszDefaultValue;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the wide string pointer of a keyName. Default value is returned
-// if the keyName can't be found.
-// // Input : *pszKeyName -
-// pwszDefaultValue -
-// Output : const wchar_t*
-//-----------------------------------------------------------------------------
-const wchar_t* KeyValues::GetWString(const char* pszKeyName, const wchar_t* pwszDefaultValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (pKey)
- {
- wchar_t wbuf[64];
- switch (pKey->m_iDataType)
- {
- case TYPE_FLOAT:
- swprintf(wbuf, ARRAYSIZE(wbuf), L"%f", pKey->m_flValue);
- SetWString(pszKeyName, wbuf);
- break;
- case TYPE_PTR:
- swprintf(wbuf, ARRAYSIZE(wbuf), L"%lld", static_cast<int64_t>(reinterpret_cast<size_t>(pKey->m_pValue)));
- SetWString(pszKeyName, wbuf);
- break;
- case TYPE_INT:
- swprintf(wbuf, ARRAYSIZE(wbuf), L"%d", pKey->m_iValue);
- SetWString(pszKeyName, wbuf);
- break;
- case TYPE_UINT64:
- {
- swprintf(wbuf, ARRAYSIZE(wbuf), L"%lld", *(reinterpret_cast<uint64_t*>(pKey->m_sValue)));
- SetWString(pszKeyName, wbuf);
- }
- break;
- case TYPE_COLOR:
- swprintf(wbuf, ARRAYSIZE(wbuf), L"%d %d %d %d", pKey->m_Color[0], pKey->m_Color[1], pKey->m_Color[2], pKey->m_Color[3]);
- SetWString(pszKeyName, wbuf);
- break;
-
- case TYPE_WSTRING:
- break;
- case TYPE_STRING:
- {
- size_t bufSize = strlen(pKey->m_sValue) + 1;
- wchar_t* pWBuf = new wchar_t[bufSize];
- int result = V_UTF8ToUnicode(pKey->m_sValue, pWBuf, static_cast<int>(bufSize * sizeof(wchar_t)));
- if (result >= 0) // may be a zero length string
- {
- SetWString(pszKeyName, pWBuf);
- delete[] pWBuf;
- }
- else
- {
- delete[] pWBuf;
- return pwszDefaultValue;
- }
-
- break;
- }
- default:
- return pwszDefaultValue;
- };
-
- return reinterpret_cast<const wchar_t*>(pKey->m_wsValue);
- }
- return pwszDefaultValue;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets a color
-// Input : *pszKeyName -
-// &defaultColor -
-// Output : Color
-//-----------------------------------------------------------------------------
-Color KeyValues::GetColor(const char* pszKeyName, const Color& defaultColor)
-{
- Color color = defaultColor;
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (pKey)
- {
- if (pKey->m_iDataType == TYPE_COLOR)
- {
- color[0] = pKey->m_Color[0];
- color[1] = pKey->m_Color[1];
- color[2] = pKey->m_Color[2];
- color[3] = pKey->m_Color[3];
- }
- else if (pKey->m_iDataType == TYPE_FLOAT)
- {
- color[0] = static_cast<unsigned char>(pKey->m_flValue);
- }
- else if (pKey->m_iDataType == TYPE_INT)
- {
- color[0] = static_cast<unsigned char>(pKey->m_iValue);
- }
- else if (pKey->m_iDataType == TYPE_STRING)
- {
- // parse the colors out of the string
- float a = 0, b = 0, c = 0, d = 0;
- sscanf(pKey->m_sValue, "%f %f %f %f", &a, &b, &c, &d);
- color[0] = static_cast<unsigned char>(a);
- color[1] = static_cast<unsigned char>(b);
- color[2] = static_cast<unsigned char>(c);
- color[3] = static_cast<unsigned char>(d);
- }
- }
- return color;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the data type of the value stored in a keyName
-// Input : *pszKeyName -
-//-----------------------------------------------------------------------------
-KeyValuesTypes_t KeyValues::GetDataType(const char* pszKeyName)
-{
- KeyValues* pKey = FindKey(pszKeyName, false);
- if (pKey)
- return static_cast<KeyValuesTypes_t>(pKey->m_iDataType);
-
- return TYPE_NONE;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the data type of the value stored in this keyName
-//-----------------------------------------------------------------------------
-KeyValuesTypes_t KeyValues::GetDataType(void) const
-{
- return static_cast<KeyValuesTypes_t>(m_iDataType);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set the integer value of a keyName.
-// Input : *pszKeyName -
-// iValue -
-//-----------------------------------------------------------------------------
-void KeyValues::SetInt(const char* pszKeyName, int iValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, true);
- if (pKey)
- {
- pKey->m_iValue = iValue;
- pKey->m_iDataType = TYPE_INT;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set the integer value of a keyName.
-//-----------------------------------------------------------------------------
-void KeyValues::SetUint64(const char* pszKeyName, uint64_t nValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, true);
-
- if (pKey)
- {
- // delete the old value
- delete[] pKey->m_sValue;
- // make sure we're not storing the WSTRING - as we're converting over to STRING
- delete[] pKey->m_wsValue;
- pKey->m_wsValue = nullptr;
-
- pKey->m_sValue = new char[sizeof(uint64_t)];
- *(reinterpret_cast<uint64_t*>(pKey->m_sValue)) = nValue;
- pKey->m_iDataType = TYPE_UINT64;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set the float value of a keyName.
-// Input : *pszKeyName -
-// flValue -
-//-----------------------------------------------------------------------------
-void KeyValues::SetFloat(const char* pszKeyName, float flValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, true);
- if (pKey)
- {
- pKey->m_flValue = flValue;
- pKey->m_iDataType = TYPE_FLOAT;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set the name value of a keyName.
-// Input : *pszSetName -
-//-----------------------------------------------------------------------------
-void KeyValues::SetName(const char* pszSetName)
-{
- HKeySymbol hCaseSensitiveKeyName = INVALID_KEY_SYMBOL, hCaseInsensitiveKeyName = INVALID_KEY_SYMBOL;
- hCaseSensitiveKeyName =
- KeyValuesSystem()->m_pVtable->GetSymbolForStringCaseSensitive(KeyValuesSystem(), hCaseInsensitiveKeyName, pszSetName, false);
-
- m_iKeyName = hCaseInsensitiveKeyName;
- SPLIT_3_BYTES_INTO_1_AND_2(m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2, hCaseSensitiveKeyName);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set the pointer value of a keyName.
-// Input : *pszKeyName -
-// *pValue -
-//-----------------------------------------------------------------------------
-void KeyValues::SetPtr(const char* pszKeyName, void* pValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, true);
-
- if (pKey)
- {
- pKey->m_pValue = pValue;
- pKey->m_iDataType = TYPE_PTR;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set the string value (internal)
-// Input : *pszValue -
-//-----------------------------------------------------------------------------
-void KeyValues::SetStringValue(char const* pszValue)
-{
- // delete the old value
- delete[] m_sValue;
- // make sure we're not storing the WSTRING - as we're converting over to STRING
- delete[] m_wsValue;
- m_wsValue = nullptr;
-
- if (!pszValue)
- {
- // ensure a valid value
- pszValue = "";
- }
-
- // allocate memory for the new value and copy it in
- size_t len = strlen(pszValue);
- m_sValue = new char[len + 1];
- memcpy(m_sValue, pszValue, len + 1);
-
- m_iDataType = TYPE_STRING;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets this key's peer to the KeyValues passed in
-// Input : *pDat -
-//-----------------------------------------------------------------------------
-void KeyValues::SetNextKey(KeyValues* pDat)
-{
- m_pPeer = pDat;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set the string value of a keyName.
-// Input : *pszKeyName -
-// *pszValue -
-//-----------------------------------------------------------------------------
-void KeyValues::SetString(const char* pszKeyName, const char* pszValue)
-{
- if (KeyValues* pKey = FindKey(pszKeyName, true))
- {
- pKey->SetStringValue(pszValue);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set the string value of a keyName.
-// Input : *pszKeyName -
-// *pwszValue -
-//-----------------------------------------------------------------------------
-void KeyValues::SetWString(const char* pszKeyName, const wchar_t* pwszValue)
-{
- KeyValues* pKey = FindKey(pszKeyName, true);
- if (pKey)
- {
- // delete the old value
- delete[] pKey->m_wsValue;
- // make sure we're not storing the STRING - as we're converting over to WSTRING
- delete[] pKey->m_sValue;
- pKey->m_sValue = nullptr;
-
- if (!pwszValue)
- {
- // ensure a valid value
- pwszValue = L"";
- }
-
- // allocate memory for the new value and copy it in
- size_t len = wcslen(pwszValue);
- pKey->m_wsValue = new wchar_t[len + 1];
- memcpy(pKey->m_wsValue, pwszValue, (len + 1) * sizeof(wchar_t));
-
- pKey->m_iDataType = TYPE_WSTRING;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets a color
-// Input : *pszKeyName -
-// color -
-//-----------------------------------------------------------------------------
-void KeyValues::SetColor(const char* pszKeyName, Color color)
-{
- KeyValues* pKey = FindKey(pszKeyName, true);
-
- if (pKey)
- {
- pKey->m_iDataType = TYPE_COLOR;
- pKey->m_Color[0] = color[0];
- pKey->m_Color[1] = color[1];
- pKey->m_Color[2] = color[2];
- pKey->m_Color[3] = color[3];
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &src -
-//-----------------------------------------------------------------------------
-void KeyValues::RecursiveCopyKeyValues(KeyValues& src)
-{
- // garymcthack - need to check this code for possible buffer overruns.
-
- m_iKeyName = src.m_iKeyName;
- m_iKeyNameCaseSensitive1 = src.m_iKeyNameCaseSensitive1;
- m_iKeyNameCaseSensitive2 = src.m_iKeyNameCaseSensitive2;
-
- if (!src.m_pSub)
- {
- m_iDataType = src.m_iDataType;
- char buf[256];
- switch (src.m_iDataType)
- {
- case TYPE_NONE:
- break;
- case TYPE_STRING:
- if (src.m_sValue)
- {
- size_t len = strlen(src.m_sValue) + 1;
- m_sValue = new char[len];
- strncpy(m_sValue, src.m_sValue, len);
- }
- break;
- case TYPE_INT:
- {
- m_iValue = src.m_iValue;
- snprintf(buf, sizeof(buf), "%d", m_iValue);
- size_t len = strlen(buf) + 1;
- m_sValue = new char[len];
- strncpy(m_sValue, buf, len);
- }
- break;
- case TYPE_FLOAT:
- {
- m_flValue = src.m_flValue;
- snprintf(buf, sizeof(buf), "%f", m_flValue);
- size_t len = strlen(buf) + 1;
- m_sValue = new char[len];
- strncpy(m_sValue, buf, len);
- }
- break;
- case TYPE_PTR:
- {
- m_pValue = src.m_pValue;
- }
- break;
- case TYPE_UINT64:
- {
- m_sValue = new char[sizeof(uint64_t)];
- memcpy(m_sValue, src.m_sValue, sizeof(uint64_t));
- }
- break;
- case TYPE_COLOR:
- {
- m_Color[0] = src.m_Color[0];
- m_Color[1] = src.m_Color[1];
- m_Color[2] = src.m_Color[2];
- m_Color[3] = src.m_Color[3];
- }
- break;
-
- default:
- {
- // do nothing . .what the heck is this?
- assert(0);
- }
- break;
- }
- }
-
- // Handle the immediate child
- if (src.m_pSub)
- {
- m_pSub = new KeyValues;
-
- m_pSub->Init();
- m_pSub->SetName(nullptr);
-
- m_pSub->RecursiveCopyKeyValues(*src.m_pSub);
- }
-
- // Handle the immediate peer
- if (src.m_pPeer)
- {
- m_pPeer = new KeyValues;
-
- m_pPeer->Init();
- m_pPeer->SetName(nullptr);
-
- m_pPeer->RecursiveCopyKeyValues(*src.m_pPeer);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Make a new copy of all subkeys, add them all to the passed-in keyvalues
-// Input : *pParent -
-//-----------------------------------------------------------------------------
-void KeyValues::CopySubkeys(KeyValues* pParent) const
-{
- // recursively copy subkeys
- // Also maintain ordering....
- KeyValues* pPrev = nullptr;
- for (KeyValues* pSub = m_pSub; pSub != nullptr; pSub = pSub->m_pPeer)
- {
- // take a copy of the subkey
- KeyValues* pKey = pSub->MakeCopy();
-
- // add into subkey list
- if (pPrev)
- {
- pPrev->m_pPeer = pKey;
- }
- else
- {
- pParent->m_pSub = pKey;
- }
- pKey->m_pPeer = nullptr;
- pPrev = pKey;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Makes a copy of the whole key-value pair set
-// Output : KeyValues*
-//-----------------------------------------------------------------------------
-KeyValues* KeyValues::MakeCopy(void) const
-{
- KeyValues* pNewKeyValue = new KeyValues;
-
- pNewKeyValue->Init();
- pNewKeyValue->SetName(GetName());
-
- // copy data
- pNewKeyValue->m_iDataType = m_iDataType;
- switch (m_iDataType)
- {
- case TYPE_STRING:
- {
- if (m_sValue)
- {
- size_t len = strlen(m_sValue);
- assert(!pNewKeyValue->m_sValue);
- pNewKeyValue->m_sValue = new char[len + 1];
- memcpy(pNewKeyValue->m_sValue, m_sValue, len + 1);
- }
- }
- break;
- case TYPE_WSTRING:
- {
- if (m_wsValue)
- {
- size_t len = wcslen(m_wsValue);
- pNewKeyValue->m_wsValue = new wchar_t[len + 1];
- memcpy(pNewKeyValue->m_wsValue, m_wsValue, len + 1 * sizeof(wchar_t));
- }
- }
- break;
-
- case TYPE_INT:
- pNewKeyValue->m_iValue = m_iValue;
- break;
-
- case TYPE_FLOAT:
- pNewKeyValue->m_flValue = m_flValue;
- break;
-
- case TYPE_PTR:
- pNewKeyValue->m_pValue = m_pValue;
- break;
-
- case TYPE_COLOR:
- pNewKeyValue->m_Color[0] = m_Color[0];
- pNewKeyValue->m_Color[1] = m_Color[1];
- pNewKeyValue->m_Color[2] = m_Color[2];
- pNewKeyValue->m_Color[3] = m_Color[3];
- break;
-
- case TYPE_UINT64:
- pNewKeyValue->m_sValue = new char[sizeof(uint64_t)];
- memcpy(pNewKeyValue->m_sValue, m_sValue, sizeof(uint64_t));
- break;
- };
-
- // recursively copy subkeys
- CopySubkeys(pNewKeyValue);
- return pNewKeyValue;
-}
-
-ON_DLL_LOAD("vstdlib.dll", KeyValues, (CModule module))
-{
- V_UTF8ToUnicode = module.GetExport("V_UTF8ToUnicode").RCast<int (*)(const char*, wchar_t*, int)>();
- V_UnicodeToUTF8 = module.GetExport("V_UnicodeToUTF8").RCast<int (*)(const wchar_t*, char*, int)>();
- KeyValuesSystem = module.GetExport("KeyValuesSystem").RCast<CKeyValuesSystem* (*)()>();
-}
-
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(KeyValues__LoadFromBuffer, engine.dll + 0x426C30,
-char, __fastcall, (KeyValues* self, const char* pResourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7))
-// clang-format on
-{
- static void* pSavedFilesystemPtr = nullptr;
-
- // this is just to allow playlists to get a valid pFileSystem ptr for kv building, other functions that call this particular overload of
- // LoadFromBuffer seem to get called on network stuff exclusively not exactly sure what the address wanted here is, so just taking it
- // from a function call that always happens before playlists is loaded
-
- // note: would be better if we could serialize this to disk for playlists, as this method breaks saving playlists in demos
- if (pFileSystem != nullptr)
- pSavedFilesystemPtr = pFileSystem;
- if (!pFileSystem && !strcmp(pResourceName, "playlists"))
- pFileSystem = pSavedFilesystemPtr;
-
- return KeyValues__LoadFromBuffer(self, pResourceName, pBuffer, pFileSystem, a5, a6, a7);
-}
-
-ON_DLL_LOAD("engine.dll", EngineKeyValues, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-}
diff --git a/NorthstarDLL/shared/keyvalues.h b/NorthstarDLL/shared/keyvalues.h
deleted file mode 100644
index bd62797e..00000000
--- a/NorthstarDLL/shared/keyvalues.h
+++ /dev/null
@@ -1,134 +0,0 @@
-#pragma once
-#include "core/math/color.h"
-
-enum KeyValuesTypes_t : char
-{
- TYPE_NONE = 0x0,
- TYPE_STRING = 0x1,
- TYPE_INT = 0x2,
- TYPE_FLOAT = 0x3,
- TYPE_PTR = 0x4,
- TYPE_WSTRING = 0x5,
- TYPE_COLOR = 0x6,
- TYPE_UINT64 = 0x7,
- TYPE_COMPILED_INT_BYTE = 0x8,
- TYPE_COMPILED_INT_0 = 0x9,
- TYPE_COMPILED_INT_1 = 0xA,
- TYPE_NUMTYPES = 0xB,
-};
-
-enum MergeKeyValuesOp_t
-{
- MERGE_KV_ALL,
- MERGE_KV_UPDATE, // update values are copied into storage, adding new keys to storage or updating existing ones
- MERGE_KV_DELETE, // update values specify keys that get deleted from storage
- MERGE_KV_BORROW, // update values only update existing keys in storage, keys in update that do not exist in storage are discarded
-};
-
-//-----------------------------------------------------------------------------
-// Purpose: Simple recursive data access class
-// Used in vgui for message parameters and resource files
-// Destructor deletes all child KeyValues nodes
-// Data is stored in key (string names) - (string/int/float)value pairs called nodes.
-//
-// About KeyValues Text File Format:
-
-// It has 3 control characters '{', '}' and '"'. Names and values may be quoted or
-// not. The quote '"' character must not be used within name or values, only for
-// quoting whole tokens. You may use escape sequences wile parsing and add within a
-// quoted token a \" to add quotes within your name or token. When using Escape
-// Sequence the parser must now that by setting KeyValues::UsesEscapeSequences( true ),
-// which it's off by default. Non-quoted tokens ends with a whitespace, '{', '}' and '"'.
-// So you may use '{' and '}' within quoted tokens, but not for non-quoted tokens.
-// An open bracket '{' after a key name indicates a list of subkeys which is finished
-// with a closing bracket '}'. Subkeys use the same definitions recursively.
-// Whitespaces are space, return, newline and tabulator. Allowed Escape sequences
-// are \n, \t, \\, \n and \". The number character '#' is used for macro purposes
-// (eg #include), don't use it as first character in key names.
-//-----------------------------------------------------------------------------
-class KeyValues
-{
-private:
- KeyValues(); // for internal use only
-
-public:
- // Constructors/destructors
- KeyValues(const char* pszSetName);
- KeyValues(const char* pszSetName, const char* pszFirstKey, const char* pszFirstValue);
- KeyValues(const char* pszSetName, const char* pszFirstKey, const wchar_t* pwszFirstValue);
- KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue);
- KeyValues(
- const char* pszSetName, const char* pszFirstKey, const char* pszFirstValue, const char* pszSecondKey, const char* pszSecondValue);
- KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue, const char* pszSecondKey, int iSecondValue);
- ~KeyValues(void);
-
- void Init(void);
- void Clear(void);
- void DeleteThis(void);
- void RemoveEverything();
-
- KeyValues* FindKey(const char* pKeyName, bool bCreate = false);
- KeyValues* FindLastSubKey(void) const;
-
- void AddSubKey(KeyValues* pSubkey);
- void RemoveSubKey(KeyValues* pSubKey);
- void InsertSubKey(int nIndex, KeyValues* pSubKey);
- bool ContainsSubKey(KeyValues* pSubKey);
- void SwapSubKey(KeyValues* pExistingSubkey, KeyValues* pNewSubKey);
- void ElideSubKey(KeyValues* pSubKey);
-
- // Data access
- bool IsEmpty(const char* pszKeyName);
- KeyValues* GetFirstTrueSubKey(void) const;
- KeyValues* GetNextTrueSubKey(void) const;
- KeyValues* GetFirstValue(void) const;
- KeyValues* GetNextValue(void) const;
- KeyValues* GetFirstSubKey() const;
- KeyValues* GetNextKey() const;
- const char* GetName(void) const;
- int GetInt(const char* pszKeyName, int iDefaultValue);
- uint64_t GetUint64(const char* pszKeyName, uint64_t nDefaultValue);
- void* GetPtr(const char* pszKeyName, void* pDefaultValue);
- float GetFloat(const char* pszKeyName, float flDefaultValue);
- const char* GetString(const char* pszKeyName = nullptr, const char* pszDefaultValue = "");
- const wchar_t* GetWString(const char* pszKeyName = nullptr, const wchar_t* pwszDefaultValue = L"");
- Color GetColor(const char* pszKeyName, const Color& defaultColor);
- KeyValuesTypes_t GetDataType(const char* pszKeyName);
- KeyValuesTypes_t GetDataType(void) const;
-
- // Key writing
- void SetInt(const char* pszKeyName, int iValue);
- void SetUint64(const char* pszKeyName, uint64_t nValue);
- void SetPtr(const char* pszKeyName, void* pValue);
- void SetNextKey(KeyValues* pDat);
- void SetName(const char* pszName);
- void SetString(const char* pszKeyName, const char* pszValue);
- void SetWString(const char* pszKeyName, const wchar_t* pwszValue);
- void SetStringValue(char const* pszValue);
- void SetColor(const char* pszKeyName, Color color);
- void SetFloat(const char* pszKeyName, float flValue);
-
- void RecursiveCopyKeyValues(KeyValues& src);
- void CopySubkeys(KeyValues* pParent) const;
- KeyValues* MakeCopy(void) const;
-
-public:
- uint32_t m_iKeyName : 24; // 0x0000
- uint32_t m_iKeyNameCaseSensitive1 : 8; // 0x0003
- char* m_sValue; // 0x0008
- wchar_t* m_wsValue; // 0x0010
- union // 0x0018
- {
- int m_iValue;
- float m_flValue;
- void* m_pValue;
- unsigned char m_Color[4];
- };
- char m_szShortName[8]; // 0x0020
- char m_iDataType; // 0x0028
- char m_bHasEscapeSequences; // 0x0029
- uint16_t m_iKeyNameCaseSensitive2; // 0x002A
- KeyValues* m_pPeer; // 0x0030
- KeyValues* m_pSub; // 0x0038
- KeyValues* m_pChain; // 0x0040
-};
diff --git a/NorthstarDLL/shared/maxplayers.cpp b/NorthstarDLL/shared/maxplayers.cpp
deleted file mode 100644
index 711193d4..00000000
--- a/NorthstarDLL/shared/maxplayers.cpp
+++ /dev/null
@@ -1,640 +0,0 @@
-#include "core/tier0.h"
-#include "maxplayers.h"
-
-AUTOHOOK_INIT()
-
-// never set this to anything below 32
-#define NEW_MAX_PLAYERS 64
-// dg note: the theoretical limit is actually 100, 76 works without entity issues, and 64 works without clientside prediction issues.
-
-#define PAD_NUMBER(number, boundary) (((number) + ((boundary)-1)) / (boundary)) * (boundary)
-
-// this is horrible
-constexpr int PlayerResource_Name_Start = 0; // Start of modded allocated space.
-constexpr int PlayerResource_Name_Size = ((NEW_MAX_PLAYERS + 1) * 8); // const char* m_szName[MAX_PLAYERS + 1];
-
-constexpr int PlayerResource_Ping_Start = PlayerResource_Name_Start + PlayerResource_Name_Size;
-constexpr int PlayerResource_Ping_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int m_iPing[MAX_PLAYERS + 1];
-
-constexpr int PlayerResource_Team_Start = PlayerResource_Ping_Start + PlayerResource_Ping_Size;
-constexpr int PlayerResource_Team_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int m_iTeam[MAX_PLAYERS + 1];
-
-constexpr int PlayerResource_PRHealth_Start = PlayerResource_Team_Start + PlayerResource_Team_Size;
-constexpr int PlayerResource_PRHealth_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int m_iPRHealth[MAX_PLAYERS + 1];
-
-constexpr int PlayerResource_Connected_Start = PlayerResource_PRHealth_Start + PlayerResource_PRHealth_Size;
-constexpr int PlayerResource_Connected_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int (used as a bool) m_bConnected[MAX_PLAYERS + 1];
-
-constexpr int PlayerResource_Alive_Start = PlayerResource_Connected_Start + PlayerResource_Connected_Size;
-constexpr int PlayerResource_Alive_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int (used as a bool) m_bAlive[MAX_PLAYERS + 1];
-
-constexpr int PlayerResource_BoolStats_Start = PlayerResource_Alive_Start + PlayerResource_Alive_Size;
-constexpr int PlayerResource_BoolStats_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int (used as a bool idk) m_boolStats[MAX_PLAYERS + 1];
-
-constexpr int PlayerResource_KillStats_Start = PlayerResource_BoolStats_Start + PlayerResource_BoolStats_Size;
-constexpr int PlayerResource_KillStats_Length = PAD_NUMBER((NEW_MAX_PLAYERS + 1) * 6, 4);
-constexpr int PlayerResource_KillStats_Size = (PlayerResource_KillStats_Length * 6); // int m_killStats[MAX_PLAYERS + 1][6];
-
-constexpr int PlayerResource_ScoreStats_Start = PlayerResource_KillStats_Start + PlayerResource_KillStats_Size;
-constexpr int PlayerResource_ScoreStats_Length = PAD_NUMBER((NEW_MAX_PLAYERS + 1) * 5, 4);
-constexpr int PlayerResource_ScoreStats_Size = (PlayerResource_ScoreStats_Length * 4); // int m_scoreStats[MAX_PLAYERS + 1][5];
-
-// must be the usage of the last field to account for any possible paddings
-constexpr int PlayerResource_TotalSize = PlayerResource_ScoreStats_Start + PlayerResource_ScoreStats_Size;
-
-constexpr int Team_PlayerArray_AddedLength = NEW_MAX_PLAYERS - 32;
-constexpr int Team_PlayerArray_AddedSize = PAD_NUMBER(Team_PlayerArray_AddedLength * 8, 4);
-constexpr int Team_AddedSize = Team_PlayerArray_AddedSize;
-
-bool MaxPlayersIncreaseEnabled()
-{
- static bool bMaxPlayersIncreaseEnabled = CommandLine()->CheckParm("-experimentalmaxplayersincrease");
- return bMaxPlayersIncreaseEnabled;
-}
-
-int GetMaxPlayers()
-{
- if (MaxPlayersIncreaseEnabled())
- return NEW_MAX_PLAYERS;
-
- return 32;
-}
-
-template <class T> void ChangeOffset(CMemoryAddress addr, unsigned int offset)
-{
- addr.Patch((BYTE*)&offset, sizeof(T));
-}
-
-// clang-format off
-AUTOHOOK(StringTables_CreateStringTable, engine.dll + 0x22E220,
-void*,, (void* thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags))
-// clang-format on
-{
- // Change the amount of entries to account for a bigger player amount
- if (!strcmp(name, "userinfo"))
- {
- int maxPlayersPowerOf2 = 1;
- while (maxPlayersPowerOf2 < NEW_MAX_PLAYERS)
- maxPlayersPowerOf2 <<= 1;
-
- maxentries = maxPlayersPowerOf2;
- }
-
- return StringTables_CreateStringTable(thisptr, name, maxentries, userdatafixedsize, userdatanetworkbits, flags);
-}
-
-ON_DLL_LOAD("engine.dll", MaxPlayersOverride_Engine, (CModule module))
-{
- if (!MaxPlayersIncreaseEnabled())
- return;
-
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
-
- // patch GetPlayerLimits to ignore the boundary limit
- module.Offset(0x116458).Patch("0xEB"); // jle => jmp
-
- // patch ED_Alloc to change nFirstIndex
- ChangeOffset<int>(module.Offset(0x18F46C + 1), NEW_MAX_PLAYERS + 8 + 1); // original: 41 (sv.GetMaxClients() + 1)
-
- // patch CGameServer::SpawnServer to change GetMaxClients inline
- ChangeOffset<int>(module.Offset(0x119543 + 2), NEW_MAX_PLAYERS + 8 + 1); // original: 41 (sv.GetMaxClients() + 1)
-
- // patch CGameServer::SpawnServer to change for loop
- ChangeOffset<unsigned char>(module.Offset(0x11957F + 2), NEW_MAX_PLAYERS); // original: 32
-
- // patch CGameServer::SpawnServer to change for loop (there are two)
- ChangeOffset<unsigned char>(module.Offset(0x119586 + 2), NEW_MAX_PLAYERS + 1); // original: 33 (32 + 1)
-
- // patch max players somewhere in CClientState
- ChangeOffset<unsigned char>(module.Offset(0x1A162C + 2), NEW_MAX_PLAYERS - 1); // original: 31 (32 - 1)
-
- // patch max players in userinfo stringtable creation
- /*{
- int maxPlayersPowerOf2 = 1;
- while (maxPlayersPowerOf2 < NEW_MAX_PLAYERS)
- maxPlayersPowerOf2 <<= 1;
- ChangeOffset<unsigned char>((char*)baseAddress + 0x114B79 + 3, maxPlayersPowerOf2); // original: 32
- }*/
- // this is not supposed to work at all but it does on 64 players (how)
- // proper fix below
-
- // patch max players in userinfo stringtable creation loop
- ChangeOffset<unsigned char>(module.Offset(0x114C48 + 2), NEW_MAX_PLAYERS); // original: 32
-
- // do not load prebaked SendTable message list
- module.Offset(0x75859).Patch("EB"); // jnz -> jmp
-}
-
-typedef void (*RunUserCmds_Type)(bool a1, float a2);
-RunUserCmds_Type RunUserCmds_Original;
-
-HMODULE serverBase = 0;
-auto RandomIntZeroMax = (__int64(__fastcall*)())0;
-
-// lazy rebuild
-// clang-format off
-AUTOHOOK(RunUserCmds, server.dll + 0x483D10,
-void,, (bool a1, float a2))
-// clang-format on
-{
- unsigned char v3; // bl
- int v5; // er14
- int i; // edi
- __int64 v7; // rax
- DWORD* v8; // rbx
- int v9; // edi
- __int64* v10; // rsi
- __int64 v11; // rax
- int v12; // er12
- __int64 v13; // rdi
- int v14; // ebx
- int v15; // eax
- __int64 v16; // r8
- int v17; // edx
- char v18; // r15
- char v19; // bp
- int v20; // esi
- __int64* v21; // rdi
- __int64 v22; // rcx
- bool v23; // al
- __int64 v24; // rax
- __int64 v25[NEW_MAX_PLAYERS]; // [rsp+20h] [rbp-138h] BYREF
-
- uintptr_t base = (__int64)serverBase;
- auto g_pGlobals = *(__int64*)(base + 0xBFBE08);
- __int64 globals = g_pGlobals;
-
- auto g_pEngineServer = *(__int64*)(base + 0xBFBD98);
-
- auto qword_1814D9648 = *(__int64*)(base + 0x14D9648);
- auto qword_1814DA408 = *(__int64*)(base + 0x14DA408);
- auto qword_1812107E8 = *(__int64*)(base + 0x12107E8);
- auto qword_1812105A8 = *(__int64*)(base + 0x12105A8);
-
- auto UTIL_PlayerByIndex = (__int64(__fastcall*)(int index))(base + 0x26AA10);
- auto sub_180485590 = (void(__fastcall*)(__int64))(base + 0x485590);
- auto sub_18058CD80 = (void(__fastcall*)(__int64))(base + 0x58CD80);
- auto sub_1805A6D90 = (void(__fastcall*)(__int64))(base + 0x5A6D90);
- auto sub_1805A6E50 = (bool(__fastcall*)(__int64, int, char))(base + 0x5A6E50);
- auto sub_1805A6C20 = (void(__fastcall*)(__int64))(base + 0x5A6C20);
-
- v3 = *(unsigned char*)(g_pGlobals + 73);
- if (*(DWORD*)(qword_1814D9648 + 92) &&
- ((*(unsigned __int8(__fastcall**)(__int64))(*(__int64*)g_pEngineServer + 32))(g_pEngineServer) ||
- !*(DWORD*)(qword_1814DA408 + 92)) &&
- v3)
- {
- globals = g_pGlobals;
- v5 = 1;
- for (i = 1; i <= *(DWORD*)(g_pGlobals + 52); ++i)
- {
- v7 = UTIL_PlayerByIndex(i);
- v8 = (DWORD*)v7;
- if (v7)
- {
- *(__int64*)(base + 0x1210420) = v7;
- *(float*)(g_pGlobals + 16) = a2;
- if (!a1)
- sub_18058CD80(v7);
- sub_1805A6D90((__int64)v8);
- }
- globals = g_pGlobals;
- }
- memset(v25, 0, sizeof(v25));
- v9 = 0;
- if (*(int*)(globals + 52) > 0)
- {
- v10 = v25;
- do
- {
- v11 = UTIL_PlayerByIndex(++v9);
- globals = g_pGlobals;
- *v10++ = v11;
- } while (v9 < *(DWORD*)(globals + 52));
- }
- v12 = *(DWORD*)(qword_1812107E8 + 92);
- if (*(DWORD*)(qword_1812105A8 + 92))
- {
- v13 = *(DWORD*)(globals + 52) - 1;
- if (v13 >= 1)
- {
- v14 = *(DWORD*)(globals + 52);
- do
- {
- v15 = RandomIntZeroMax();
- v16 = v25[v13--];
- v17 = v15 % v14--;
- v25[v13 + 1] = v25[v17];
- v25[v17] = v16;
- } while (v13 >= 1);
- globals = g_pGlobals;
- }
- }
- v18 = 1;
- do
- {
- v19 = 0;
- v20 = 0;
- if (*(int*)(globals + 52) > 0)
- {
- v21 = v25;
- do
- {
- v22 = *v21;
- if (*v21)
- {
- *(__int64*)(base + 0x1210420) = *v21;
- *(float*)(globals + 16) = a2;
- v23 = sub_1805A6E50(v22, v12, v18);
- globals = g_pGlobals;
- if (v23)
- v19 = 1;
- else
- *v21 = 0;
- }
- ++v20;
- ++v21;
- } while (v20 < *(DWORD*)(globals + 52));
- }
- v18 = 0;
- } while (v19);
- if (*(int*)(globals + 52) >= 1)
- {
- do
- {
- v24 = UTIL_PlayerByIndex(v5);
- if (v24)
- {
- *(__int64*)(base + 0x1210420) = v24;
- *(float*)(g_pGlobals + 16) = a2;
- sub_1805A6C20(v24);
- }
- ++v5;
- } while (v5 <= *(DWORD*)(g_pGlobals + 52));
- }
- sub_180485590(*(__int64*)(base + 0xB7B2D8));
- }
-}
-
-// clang-format off
-AUTOHOOK(SendPropArray2, server.dll + 0x12B130,
-__int64,, (__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn, unsigned char unk1))
-// clang-format on
-{
- // Change the amount of elements to account for a bigger player amount
- if (!strcmp(name, "\"player_array\""))
- elements = NEW_MAX_PLAYERS;
-
- return SendPropArray2(recvProp, elements, flags, name, proxyFn, unk1);
-}
-
-ON_DLL_LOAD("server.dll", MaxPlayersOverride_Server, (CModule module))
-{
- if (!MaxPlayersIncreaseEnabled())
- return;
-
- AUTOHOOK_DISPATCH_MODULE(server.dll)
-
- // get required data
- serverBase = (HMODULE)module.m_nAddress;
- RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax"));
-
- // patch max players amount
- ChangeOffset<unsigned char>(module.Offset(0x9A44D + 3), NEW_MAX_PLAYERS); // 0x20 (32) => 0x80 (128)
-
- // patch SpawnGlobalNonRewinding to change forced edict index
- ChangeOffset<unsigned char>(module.Offset(0x2BC403 + 2), NEW_MAX_PLAYERS + 1); // original: 33 (32 + 1)
-
- constexpr int CPlayerResource_OriginalSize = 4776;
- constexpr int CPlayerResource_AddedSize = PlayerResource_TotalSize;
- constexpr int CPlayerResource_ModifiedSize = CPlayerResource_OriginalSize + CPlayerResource_AddedSize;
-
- // CPlayerResource class allocation function - allocate a bigger amount to fit all new max player data
- ChangeOffset<unsigned int>(module.Offset(0x5C560A + 1), CPlayerResource_ModifiedSize);
-
- // DT_PlayerResource::m_iPing SendProp
- ChangeOffset<unsigned int>(module.Offset(0x5C5059 + 2), CPlayerResource_OriginalSize + PlayerResource_Ping_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C50A8 + 2), CPlayerResource_OriginalSize + PlayerResource_Ping_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C50E2 + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_iPing DataMap
- ChangeOffset<unsigned int>(module.Offset(0xB94598), CPlayerResource_OriginalSize + PlayerResource_Ping_Start);
- ChangeOffset<unsigned short>(module.Offset(0xB9459C), NEW_MAX_PLAYERS + 1);
- ChangeOffset<unsigned short>(module.Offset(0xB945C0), PlayerResource_Ping_Size);
-
- // DT_PlayerResource::m_iTeam SendProp
- ChangeOffset<unsigned int>(module.Offset(0x5C5110 + 2), CPlayerResource_OriginalSize + PlayerResource_Team_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C519C + 2), CPlayerResource_OriginalSize + PlayerResource_Team_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C517E + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_iTeam DataMap
- ChangeOffset<unsigned int>(module.Offset(0xB94600), CPlayerResource_OriginalSize + PlayerResource_Team_Start);
- ChangeOffset<unsigned short>(module.Offset(0xB94604), NEW_MAX_PLAYERS + 1);
- ChangeOffset<unsigned short>(module.Offset(0xB94628), PlayerResource_Team_Size);
-
- // DT_PlayerResource::m_iPRHealth SendProp
- ChangeOffset<unsigned int>(module.Offset(0x5C51C0 + 2), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C5204 + 2), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C523E + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_iPRHealth DataMap
- ChangeOffset<unsigned int>(module.Offset(0xB94668), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start);
- ChangeOffset<unsigned short>(module.Offset(0xB9466C), NEW_MAX_PLAYERS + 1);
- ChangeOffset<unsigned short>(module.Offset(0xB94690), PlayerResource_PRHealth_Size);
-
- // DT_PlayerResource::m_bConnected SendProp
- ChangeOffset<unsigned int>(module.Offset(0x5C526C + 2), CPlayerResource_OriginalSize + PlayerResource_Connected_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C52B4 + 2), CPlayerResource_OriginalSize + PlayerResource_Connected_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C52EE + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_bConnected DataMap
- ChangeOffset<unsigned int>(module.Offset(0xB946D0), CPlayerResource_OriginalSize + PlayerResource_Connected_Start);
- ChangeOffset<unsigned short>(module.Offset(0xB946D4), NEW_MAX_PLAYERS + 1);
- ChangeOffset<unsigned short>(module.Offset(0xB946F8), PlayerResource_Connected_Size);
-
- // DT_PlayerResource::m_bAlive SendProp
- ChangeOffset<unsigned int>(module.Offset(0x5C5321 + 2), CPlayerResource_OriginalSize + PlayerResource_Alive_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C5364 + 2), CPlayerResource_OriginalSize + PlayerResource_Alive_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C539E + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_bAlive DataMap
- ChangeOffset<unsigned int>(module.Offset(0xB94738), CPlayerResource_OriginalSize + PlayerResource_Alive_Start);
- ChangeOffset<unsigned short>(module.Offset(0xB9473C), NEW_MAX_PLAYERS + 1);
- ChangeOffset<unsigned short>(module.Offset(0xB94760), PlayerResource_Alive_Size);
-
- // DT_PlayerResource::m_boolStats SendProp
- ChangeOffset<unsigned int>(module.Offset(0x5C53CC + 2), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C5414 + 2), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C544E + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_boolStats DataMap
- ChangeOffset<unsigned int>(module.Offset(0xB947A0), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start);
- ChangeOffset<unsigned short>(module.Offset(0xB947A4), NEW_MAX_PLAYERS + 1);
- ChangeOffset<unsigned short>(module.Offset(0xB947C8), PlayerResource_BoolStats_Size);
-
- // DT_PlayerResource::m_killStats SendProp
- ChangeOffset<unsigned int>(module.Offset(0x5C547C + 2), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C54E2 + 2), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C54FE + 4), PlayerResource_KillStats_Length);
-
- // DT_PlayerResource::m_killStats DataMap
- ChangeOffset<unsigned int>(module.Offset(0xB94808), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start);
- ChangeOffset<unsigned short>(module.Offset(0xB9480C), PlayerResource_KillStats_Length);
- ChangeOffset<unsigned short>(module.Offset(0xB94830), PlayerResource_KillStats_Size);
-
- // DT_PlayerResource::m_scoreStats SendProp
- ChangeOffset<unsigned int>(module.Offset(0x5C5528 + 2), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C5576 + 2), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C5584 + 4), PlayerResource_ScoreStats_Length);
-
- // DT_PlayerResource::m_scoreStats DataMap
- ChangeOffset<unsigned int>(module.Offset(0xB94870), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start);
- ChangeOffset<unsigned short>(module.Offset(0xB94874), PlayerResource_ScoreStats_Length);
- ChangeOffset<unsigned short>(module.Offset(0xB94898), PlayerResource_ScoreStats_Size);
-
- // CPlayerResource::UpdatePlayerData - m_bConnected
- ChangeOffset<unsigned int>(module.Offset(0x5C66EE + 4), CPlayerResource_OriginalSize + PlayerResource_Connected_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C672E + 4), CPlayerResource_OriginalSize + PlayerResource_Connected_Start);
-
- // CPlayerResource::UpdatePlayerData - m_iPing
- ChangeOffset<unsigned int>(module.Offset(0x5C6394 + 4), CPlayerResource_OriginalSize + PlayerResource_Ping_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C63DB + 4), CPlayerResource_OriginalSize + PlayerResource_Ping_Start);
-
- // CPlayerResource::UpdatePlayerData - m_iTeam
- ChangeOffset<unsigned int>(module.Offset(0x5C63FD + 4), CPlayerResource_OriginalSize + PlayerResource_Team_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C6442 + 4), CPlayerResource_OriginalSize + PlayerResource_Team_Start);
-
- // CPlayerResource::UpdatePlayerData - m_iPRHealth
- ChangeOffset<unsigned int>(module.Offset(0x5C645B + 4), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C64A0 + 4), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start);
-
- // CPlayerResource::UpdatePlayerData - m_bConnected
- ChangeOffset<unsigned int>(module.Offset(0x5C64AA + 4), CPlayerResource_OriginalSize + PlayerResource_Connected_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C64F0 + 4), CPlayerResource_OriginalSize + PlayerResource_Connected_Start);
-
- // CPlayerResource::UpdatePlayerData - m_bAlive
- ChangeOffset<unsigned int>(module.Offset(0x5C650A + 4), CPlayerResource_OriginalSize + PlayerResource_Alive_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C654F + 4), CPlayerResource_OriginalSize + PlayerResource_Alive_Start);
-
- // CPlayerResource::UpdatePlayerData - m_boolStats
- ChangeOffset<unsigned int>(module.Offset(0x5C6557 + 4), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C65A5 + 4), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start);
-
- // CPlayerResource::UpdatePlayerData - m_scoreStats
- ChangeOffset<unsigned int>(module.Offset(0x5C65C2 + 3), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C65E3 + 4), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start);
-
- // CPlayerResource::UpdatePlayerData - m_killStats
- ChangeOffset<unsigned int>(module.Offset(0x5C6654 + 3), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x5C665B + 3), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start);
-
- *module.Offset(0x14E7390).RCast<DWORD*>() = 0;
- auto DT_PlayerResource_Construct = module.Offset(0x5C4FE0).RCast<__int64(__fastcall*)()>();
- DT_PlayerResource_Construct();
-
- constexpr int CTeam_OriginalSize = 3336;
- constexpr int CTeam_AddedSize = Team_AddedSize;
- constexpr int CTeam_ModifiedSize = CTeam_OriginalSize + CTeam_AddedSize;
-
- // CTeam class allocation function - allocate a bigger amount to fit all new team player data
- ChangeOffset<unsigned int>(module.Offset(0x23924A + 1), CTeam_ModifiedSize);
-
- // CTeam::CTeam - increase memset length to clean newly allocated data
- ChangeOffset<unsigned int>(module.Offset(0x2395AE + 2), 256 + CTeam_AddedSize);
-
- *module.Offset(0xC945A0).RCast<DWORD*>() = 0;
- auto DT_Team_Construct = module.Offset(0x238F50).RCast<__int64(__fastcall*)()>();
- DT_Team_Construct();
-}
-
-// clang-format off
-AUTOHOOK(RecvPropArray2, client.dll + 0x1CEDA0,
-__int64,, (__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn))
-// clang-format on
-{
- // Change the amount of elements to account for a bigger player amount
- if (!strcmp(name, "\"player_array\""))
- elements = NEW_MAX_PLAYERS;
-
- return RecvPropArray2(recvProp, elements, flags, name, proxyFn);
-}
-
-ON_DLL_LOAD("client.dll", MaxPlayersOverride_Client, (CModule module))
-{
- if (!MaxPlayersIncreaseEnabled())
- return;
-
- AUTOHOOK_DISPATCH_MODULE(client.dll)
-
- constexpr int C_PlayerResource_OriginalSize = 5768;
- constexpr int C_PlayerResource_AddedSize = PlayerResource_TotalSize;
- constexpr int C_PlayerResource_ModifiedSize = C_PlayerResource_OriginalSize + C_PlayerResource_AddedSize;
-
- // C_PlayerResource class allocation function - allocate a bigger amount to fit all new max player data
- ChangeOffset<unsigned int>(module.Offset(0x164C41 + 1), C_PlayerResource_ModifiedSize);
-
- // C_PlayerResource::C_PlayerResource - change loop end value
- ChangeOffset<unsigned char>(module.Offset(0x1640C4 + 2), NEW_MAX_PLAYERS - 32);
-
- // C_PlayerResource::C_PlayerResource - change m_szName address
- ChangeOffset<unsigned int>(
- module.Offset(0x1640D0 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // appended to the end of the class
-
- // C_PlayerResource::C_PlayerResource - change m_szName address
- ChangeOffset<unsigned int>(
- module.Offset(0x1640D0 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // appended to the end of the class
-
- // C_PlayerResource::C_PlayerResource - increase memset length to clean newly allocated data
- ChangeOffset<unsigned int>(module.Offset(0x1640D0 + 3), 2244 + C_PlayerResource_AddedSize);
-
- // C_PlayerResource::UpdatePlayerName - change m_szName address
- ChangeOffset<unsigned int>(module.Offset(0x16431F + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName - change m_szName address 1
- ChangeOffset<unsigned int>(module.Offset(0x1645B1 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName - change m_szName address 2
- ChangeOffset<unsigned int>(module.Offset(0x1645C0 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName - change m_szName address 3
- ChangeOffset<unsigned int>(module.Offset(0x1645DD + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName internal func - change m_szName address 1
- ChangeOffset<unsigned int>(module.Offset(0x164B71 + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName internal func - change m_szName address 2
- ChangeOffset<unsigned int>(module.Offset(0x164B9B + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 1
- ChangeOffset<unsigned int>(module.Offset(0x164641 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 2
- ChangeOffset<unsigned int>(module.Offset(0x164650 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 3
- ChangeOffset<unsigned int>(module.Offset(0x16466D + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 1
- ChangeOffset<unsigned int>(module.Offset(0x164BA3 + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 2
- ChangeOffset<unsigned int>(module.Offset(0x164BCE + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 3
- ChangeOffset<unsigned int>(module.Offset(0x164BE7 + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
-
- // C_PlayerResource::m_szName
- ChangeOffset<unsigned int>(module.Offset(0xc350f8), C_PlayerResource_OriginalSize + PlayerResource_Name_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc350f8 + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource size
- ChangeOffset<unsigned int>(module.Offset(0x163415 + 6), C_PlayerResource_ModifiedSize);
-
- // DT_PlayerResource::m_iPing RecvProp
- ChangeOffset<unsigned int>(module.Offset(0x163492 + 2), C_PlayerResource_OriginalSize + PlayerResource_Ping_Start);
- ChangeOffset<unsigned int>(module.Offset(0x1634D6 + 2), C_PlayerResource_OriginalSize + PlayerResource_Ping_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163515 + 5), NEW_MAX_PLAYERS + 1);
-
- // C_PlayerResource::m_iPing
- ChangeOffset<unsigned int>(module.Offset(0xc35170), C_PlayerResource_OriginalSize + PlayerResource_Ping_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc35170 + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_iTeam RecvProp
- ChangeOffset<unsigned int>(module.Offset(0x163549 + 2), C_PlayerResource_OriginalSize + PlayerResource_Team_Start);
- ChangeOffset<unsigned int>(module.Offset(0x1635C8 + 2), C_PlayerResource_OriginalSize + PlayerResource_Team_Start);
- ChangeOffset<unsigned int>(module.Offset(0x1635AD + 5), NEW_MAX_PLAYERS + 1);
-
- // C_PlayerResource::m_iTeam
- ChangeOffset<unsigned int>(module.Offset(0xc351e8), C_PlayerResource_OriginalSize + PlayerResource_Team_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc351e8 + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_iPRHealth RecvProp
- ChangeOffset<unsigned int>(module.Offset(0x1635F9 + 2), C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163625 + 2), C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163675 + 5), NEW_MAX_PLAYERS + 1);
-
- // C_PlayerResource::m_iPRHealth
- ChangeOffset<unsigned int>(module.Offset(0xc35260), C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc35260 + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_bConnected RecvProp
- ChangeOffset<unsigned int>(module.Offset(0x1636A9 + 2), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start);
- ChangeOffset<unsigned int>(module.Offset(0x1636D5 + 2), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163725 + 5), NEW_MAX_PLAYERS + 1);
-
- // C_PlayerResource::m_bConnected
- ChangeOffset<unsigned int>(module.Offset(0xc352d8), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc352d8 + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_bAlive RecvProp
- ChangeOffset<unsigned int>(module.Offset(0x163759 + 2), C_PlayerResource_OriginalSize + PlayerResource_Alive_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163785 + 2), C_PlayerResource_OriginalSize + PlayerResource_Alive_Start);
- ChangeOffset<unsigned int>(module.Offset(0x1637D5 + 5), NEW_MAX_PLAYERS + 1);
-
- // C_PlayerResource::m_bAlive
- ChangeOffset<unsigned int>(module.Offset(0xc35350), C_PlayerResource_OriginalSize + PlayerResource_Alive_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc35350 + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_boolStats RecvProp
- ChangeOffset<unsigned int>(module.Offset(0x163809 + 2), C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163835 + 2), C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163885 + 5), NEW_MAX_PLAYERS + 1);
-
- // C_PlayerResource::m_boolStats
- ChangeOffset<unsigned int>(module.Offset(0xc353c8), C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc353c8 + 4), NEW_MAX_PLAYERS + 1);
-
- // DT_PlayerResource::m_killStats RecvProp
- ChangeOffset<unsigned int>(module.Offset(0x1638B3 + 2), C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x1638E5 + 2), C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163935 + 5), PlayerResource_KillStats_Length);
-
- // C_PlayerResource::m_killStats
- ChangeOffset<unsigned int>(module.Offset(0xc35440), C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc35440 + 4), PlayerResource_KillStats_Length);
-
- // DT_PlayerResource::m_scoreStats RecvProp
- ChangeOffset<unsigned int>(module.Offset(0x163969 + 2), C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x163995 + 2), C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start);
- ChangeOffset<unsigned int>(module.Offset(0x1639E5 + 5), PlayerResource_ScoreStats_Length);
-
- // C_PlayerResource::m_scoreStats
- ChangeOffset<unsigned int>(module.Offset(0xc354b8), C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start);
- ChangeOffset<unsigned short>(module.Offset(0xc354b8 + 4), PlayerResource_ScoreStats_Length);
-
- // C_PlayerResource::GetPlayerName - change m_bConnected address
- ChangeOffset<unsigned int>(module.Offset(0x164599 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start);
-
- // C_PlayerResource::GetPlayerName2 (?) - change m_bConnected address
- ChangeOffset<unsigned int>(module.Offset(0x164629 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start);
-
- // C_PlayerResource::GetPlayerName internal func - change m_bConnected address
- ChangeOffset<unsigned int>(module.Offset(0x164B13 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start);
-
- // Some other get name func (that seems to be unused) - change m_bConnected address
- ChangeOffset<unsigned int>(module.Offset(0x164860 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start);
-
- // Some other get name func 2 (that seems to be unused too) - change m_bConnected address
- ChangeOffset<unsigned int>(module.Offset(0x164834 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start);
-
- *module.Offset(0xC35068).RCast<DWORD*>() = 0;
- auto DT_PlayerResource_Construct = module.Offset(0x163400).RCast<__int64(__fastcall*)()>();
- DT_PlayerResource_Construct();
-
- constexpr int C_Team_OriginalSize = 3200;
- constexpr int C_Team_AddedSize = Team_AddedSize;
- constexpr int C_Team_ModifiedSize = C_Team_OriginalSize + C_Team_AddedSize;
-
- // C_Team class allocation function - allocate a bigger amount to fit all new team player data
- ChangeOffset<unsigned int>(module.Offset(0x182321 + 1), C_Team_ModifiedSize);
-
- // C_Team::C_Team - increase memset length to clean newly allocated data
- ChangeOffset<unsigned int>(module.Offset(0x1804A2 + 2), 256 + C_Team_AddedSize);
-
- // DT_Team size
- ChangeOffset<unsigned int>(module.Offset(0xC3AA0C), C_Team_ModifiedSize);
-
- *module.Offset(0xC3AFF8).RCast<DWORD*>() = 0;
- auto DT_Team_Construct = module.Offset(0x17F950).RCast<__int64(__fastcall*)()>();
- DT_Team_Construct();
-}
diff --git a/NorthstarDLL/shared/maxplayers.h b/NorthstarDLL/shared/maxplayers.h
deleted file mode 100644
index 40a3ac58..00000000
--- a/NorthstarDLL/shared/maxplayers.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-int GetMaxPlayers();
diff --git a/NorthstarDLL/shared/misccommands.cpp b/NorthstarDLL/shared/misccommands.cpp
deleted file mode 100644
index 15da6767..00000000
--- a/NorthstarDLL/shared/misccommands.cpp
+++ /dev/null
@@ -1,391 +0,0 @@
-#include "misccommands.h"
-#include "core/convar/concommand.h"
-#include "shared/playlist.h"
-#include "engine/r2engine.h"
-#include "client/r2client.h"
-#include "core/tier0.h"
-#include "engine/hoststate.h"
-#include "masterserver/masterserver.h"
-#include "mods/modmanager.h"
-#include "server/auth/serverauthentication.h"
-#include "squirrel/squirrel.h"
-
-void ConCommand_force_newgame(const CCommand& arg)
-{
- if (arg.ArgC() < 2)
- return;
-
- g_pHostState->m_iNextState = HostState_t::HS_NEW_GAME;
- strncpy(g_pHostState->m_levelName, arg.Arg(1), sizeof(g_pHostState->m_levelName));
-}
-
-void ConCommand_ns_start_reauth_and_leave_to_lobby(const CCommand& arg)
-{
- // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect
- g_pMasterServerManager->m_bNewgameAfterSelfAuth = true;
- g_pMasterServerManager->AuthenticateWithOwnServer(g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken);
-}
-
-void ConCommand_ns_end_reauth_and_leave_to_lobby(const CCommand& arg)
-{
- if (g_pServerAuthentication->m_RemoteAuthenticationData.size())
- g_pCVar->FindVar("serverfilter")->SetValue(g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first.c_str());
-
- // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this
- if (g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM)
- {
- g_pServerAuthentication->m_bNeedLocalAuthForNewgame = true;
-
- // this won't set playlist correctly on remote clients, don't think they can set playlist until they've left which sorta
- // fucks things should maybe set this in HostState_NewGame?
- R2::SetCurrentPlaylist("tdm");
- strcpy(g_pHostState->m_levelName, "mp_lobby");
- g_pHostState->m_iNextState = HostState_t::HS_NEW_GAME;
- }
-}
-
-void ConCommand_cvar_setdefaultvalue(const CCommand& arg)
-{
- if (arg.ArgC() < 3)
- {
- spdlog::info("usage: cvar_setdefaultvalue mp_gamemode tdm");
- return;
- }
-
- ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1));
- if (!pCvar)
- {
- spdlog::info("usage: cvar_setdefaultvalue mp_gamemode tdm");
- return;
- }
-
- // unfortunately no way for us to not leak memory here, as default value might not be in writeable memory by default
- int nLen = strlen(arg.Arg(2));
- char* pBuf = new char[nLen + 1];
- strncpy_s(pBuf, nLen + 1, arg.Arg(2), nLen);
-
- pCvar->m_pszDefaultValue = pBuf;
-}
-
-void ConCommand_cvar_setvalueanddefaultvalue(const CCommand& arg)
-{
- if (arg.ArgC() < 3)
- {
- spdlog::info("usage: cvar_setvalueanddefaultvalue mp_gamemode tdm");
- return;
- }
-
- ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1));
- if (!pCvar)
- {
- spdlog::info("usage: cvar_setvalueanddefaultvalue mp_gamemode tdm");
- return;
- }
-
- // unfortunately no way for us to not leak memory here, as default value might not be in writeable memory by default
- int nLen = strlen(arg.Arg(2));
- char* pBuf = new char[nLen + 1];
- strncpy_s(pBuf, nLen + 1, arg.Arg(2), nLen);
-
- pCvar->m_pszDefaultValue = pBuf;
- pCvar->SetValue(pCvar->m_pszDefaultValue);
-}
-
-void ConCommand_cvar_reset(const CCommand& arg)
-{
- if (arg.ArgC() < 2)
- {
- spdlog::info("usage: cvar_reset mp_gamemode");
- return;
- }
-
- ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1));
- if (!pCvar)
- {
- spdlog::info("usage: cvar_reset mp_gamemode");
- return;
- }
-
- // reset cvar
- pCvar->SetValue(pCvar->m_pszDefaultValue);
-}
-
-void AddMiscConCommands()
-{
- RegisterConCommand(
- "force_newgame",
- ConCommand_force_newgame,
- "forces a map load through directly setting g_pHostState->m_iNextState to HS_NEW_GAME",
- FCVAR_NONE);
-
- RegisterConCommand(
- "ns_start_reauth_and_leave_to_lobby",
- ConCommand_ns_start_reauth_and_leave_to_lobby,
- "called by the server, used to reauth and return the player to lobby when leaving a game",
- FCVAR_SERVER_CAN_EXECUTE);
-
- // this is a concommand because we make a deferred call to it from another thread
- RegisterConCommand("ns_end_reauth_and_leave_to_lobby", ConCommand_ns_end_reauth_and_leave_to_lobby, "", FCVAR_NONE);
-
- RegisterConCommand(
- "cvar_setdefaultvalue",
- ConCommand_cvar_setdefaultvalue,
- "overwrites the default value of a cvar, for use with script and cvar_reset",
- FCVAR_NONE);
- RegisterConCommand(
- "cvar_setvalueanddefaultvalue",
- ConCommand_cvar_setvalueanddefaultvalue,
- "overwrites the current value and default value of a cvar, for use with script and cvar_reset",
- FCVAR_NONE);
- RegisterConCommand("cvar_reset", ConCommand_cvar_reset, "resets a cvar's value to its default value", FCVAR_NONE);
-}
-
-// fixes up various cvar flags to have more sane values
-void FixupCvarFlags()
-{
- if (CommandLine()->CheckParm("-allowdevcvars"))
- {
- // strip hidden and devonly cvar flags
- int iNumCvarsAltered = 0;
- for (auto& pair : g_pCVar->DumpToMap())
- {
- // strip flags
- int flags = pair.second->GetFlags();
- if (flags & FCVAR_DEVELOPMENTONLY)
- {
- flags &= ~FCVAR_DEVELOPMENTONLY;
- iNumCvarsAltered++;
- }
-
- if (flags & FCVAR_HIDDEN)
- {
- flags &= ~FCVAR_HIDDEN;
- iNumCvarsAltered++;
- }
-
- pair.second->m_nFlags = flags;
- }
-
- spdlog::info("Removed {} hidden/devonly cvar flags", iNumCvarsAltered);
- }
-
- // make all engine client commands FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS
- // these are usually checked through CGameClient::IsEngineClientCommand, but we get more control over this if we just do it through
- // cvar flags
- const char** ppEngineClientCommands = CModule("engine.dll").Offset(0x7C5EF0).RCast<const char**>();
-
- int i = 0;
- do
- {
- ConCommandBase* pCommand = g_pCVar->FindCommandBase(ppEngineClientCommands[i]);
- if (pCommand) // not all the commands in this array actually exist in respawn source
- pCommand->m_nFlags |= FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS;
- } while (ppEngineClientCommands[++i]);
-
- // array of cvars and the flags we want to add to them
- const std::vector<std::tuple<const char*, uint32_t>> CVAR_FIXUP_ADD_FLAGS = {
- // system commands (i.e. necessary for proper functionality)
- // servers need to be able to disconnect
- {"disconnect", FCVAR_SERVER_CAN_EXECUTE},
-
- // cheat commands
- {"give", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"give_server", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"givecurrentammo", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"takecurrentammo", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
-
- {"switchclass", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"set", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"_setClassVarServer", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
-
- {"ent_create", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"ent_throw", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"ent_setname", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"ent_teleport", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"ent_remove", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"ent_remove_all", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"ent_fire", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
-
- {"particle_create", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"particle_recreate", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"particle_kill", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
-
- {"test_setteam", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"melee_lunge_ent", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
-
- // fcvars that should be cheats
- {"net_ignoreAllSnapshots", FCVAR_CHEAT},
- {"highlight_draw", FCVAR_CHEAT},
- // these should potentially be replicated rather than cheat, like sv_footsteps is
- // however they're defined on client, so can't make replicated atm sadly
- {"cl_footstep_event_max_dist", FCVAR_CHEAT},
- {"cl_footstep_event_max_dist_titan", FCVAR_CHEAT},
- };
-
- // array of cvars and the flags we want to remove from them
- const std::vector<std::tuple<const char*, uint32_t>> CVAR_FIXUP_REMOVE_FLAGS = {
- // unsure how this command works, not even sure it's used on retail servers, deffo shouldn't be used on northstar
- {"migrateme", FCVAR_SERVER_CAN_EXECUTE | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"recheck", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, // we don't need this on northstar servers, it's for communities
-
- // unsure how these work exactly (rpt system likely somewhat stripped?), removing anyway since they won't be used
- {"rpt_client_enable", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
- {"rpt_password", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS},
-
- // these are devonly by default but should be modifyable
- // NOTE: not all of these may actually do anything or work properly in practice
- // network settings
- {"cl_updaterate_mp", FCVAR_DEVELOPMENTONLY},
- {"cl_updaterate_sp", FCVAR_DEVELOPMENTONLY},
- {"clock_bias_sp", FCVAR_DEVELOPMENTONLY},
- {"clock_bias_mp", FCVAR_DEVELOPMENTONLY},
- {"cl_interpolate", FCVAR_DEVELOPMENTONLY}, // super duper ultra fucks anims if changed
- {"cl_interpolateSoAllAnimsLoop", FCVAR_DEVELOPMENTONLY},
- {"cl_cmdrate", FCVAR_DEVELOPMENTONLY},
- {"cl_cmdbackup", FCVAR_DEVELOPMENTONLY},
- {"rate", FCVAR_DEVELOPMENTONLY},
- {"net_minroutable", FCVAR_DEVELOPMENTONLY},
- {"net_maxroutable", FCVAR_DEVELOPMENTONLY},
- {"net_lerpFields", FCVAR_DEVELOPMENTONLY},
- {"net_ignoreAllSnapshots", FCVAR_DEVELOPMENTONLY},
- {"net_chokeloop", FCVAR_DEVELOPMENTONLY},
- {"sv_unlag", FCVAR_DEVELOPMENTONLY},
- {"sv_maxunlag", FCVAR_DEVELOPMENTONLY},
- {"sv_lagpushticks", FCVAR_DEVELOPMENTONLY},
- {"sv_instancebaselines", FCVAR_DEVELOPMENTONLY},
- {"sv_voiceEcho", FCVAR_DEVELOPMENTONLY},
- {"net_compresspackets", FCVAR_DEVELOPMENTONLY},
- {"net_compresspackets_minsize", FCVAR_DEVELOPMENTONLY},
- {"net_verifyEncryption", FCVAR_DEVELOPMENTONLY}, // unsure if functional in retail
-
- // gameplay settings
- {"vel_samples", FCVAR_DEVELOPMENTONLY},
- {"vel_sampleFrequency", FCVAR_DEVELOPMENTONLY},
- {"sv_friction", FCVAR_DEVELOPMENTONLY},
- {"sv_stopspeed", FCVAR_DEVELOPMENTONLY},
- {"sv_airaccelerate", FCVAR_DEVELOPMENTONLY},
- {"sv_forceGrapplesToFail", FCVAR_DEVELOPMENTONLY},
- {"sv_maxvelocity", FCVAR_DEVELOPMENTONLY},
- {"sv_footsteps", FCVAR_DEVELOPMENTONLY},
- // these 2 are flagged as CHEAT above, could be made REPLICATED later potentially
- {"cl_footstep_event_max_dist", FCVAR_DEVELOPMENTONLY},
- {"cl_footstep_event_max_dist_titan", FCVAR_DEVELOPMENTONLY},
- {"sv_balanceTeams", FCVAR_DEVELOPMENTONLY},
- {"rodeo_enable", FCVAR_DEVELOPMENTONLY},
- {"sv_forceRodeoToFail", FCVAR_DEVELOPMENTONLY},
- {"player_find_rodeo_target_per_cmd", FCVAR_DEVELOPMENTONLY}, // todo test before merge
- {"hud_takesshots", FCVAR_DEVELOPMENTONLY}, // very likely does not work but would be cool if it did
-
- {"cam_collision", FCVAR_DEVELOPMENTONLY},
- {"cam_idealdelta", FCVAR_DEVELOPMENTONLY},
- {"cam_ideallag", FCVAR_DEVELOPMENTONLY},
-
- // graphics/visual settings
- {"mat_colorcorrection", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoRadius", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoDepthMax", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoBlurSharpness", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoIntensity", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoBias", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoDistanceLerp", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoBlurRadius", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoExponent", FCVAR_DEVELOPMENTONLY},
- {"r_hbaoDepthFadePctDefault", FCVAR_DEVELOPMENTONLY},
- {"r_drawscreenspaceparticles", FCVAR_DEVELOPMENTONLY},
- {"ui_loadingscreen_fadeout_time", FCVAR_DEVELOPMENTONLY},
- {"ui_loadingscreen_fadein_time", FCVAR_DEVELOPMENTONLY},
- {"ui_loadingscreen_transition_time", FCVAR_DEVELOPMENTONLY},
- {"ui_loadingscreen_mintransition_time", FCVAR_DEVELOPMENTONLY},
- // these 2 could be FCVAR_CHEAT, i guess?
- {"cl_draw_player_model", FCVAR_DEVELOPMENTONLY},
- {"cl_always_draw_3p_player", FCVAR_DEVELOPMENTONLY},
- {"idcolor_neutral", FCVAR_DEVELOPMENTONLY},
- {"idcolor_ally", FCVAR_DEVELOPMENTONLY},
- {"idcolor_ally_cb1", FCVAR_DEVELOPMENTONLY},
- {"idcolor_ally_cb2", FCVAR_DEVELOPMENTONLY},
- {"idcolor_ally_cb3", FCVAR_DEVELOPMENTONLY},
- {"idcolor_enemy", FCVAR_DEVELOPMENTONLY},
- {"idcolor_enemy_cb1", FCVAR_DEVELOPMENTONLY},
- {"idcolor_enemy_cb2", FCVAR_DEVELOPMENTONLY},
- {"idcolor_enemy_cb3", FCVAR_DEVELOPMENTONLY},
- {"playerListPartyColorR", FCVAR_DEVELOPMENTONLY},
- {"playerListPartyColorG", FCVAR_DEVELOPMENTONLY},
- {"playerListPartyColorB", FCVAR_DEVELOPMENTONLY},
- {"playerListUseFriendColor", FCVAR_DEVELOPMENTONLY},
- {"fx_impact_neutral", FCVAR_DEVELOPMENTONLY},
- {"fx_impact_ally", FCVAR_DEVELOPMENTONLY},
- {"fx_impact_enemy", FCVAR_DEVELOPMENTONLY},
- {"hitch_alert_color", FCVAR_DEVELOPMENTONLY},
- {"particles_cull_all", FCVAR_DEVELOPMENTONLY},
- {"particles_cull_dlights", FCVAR_DEVELOPMENTONLY},
- {"map_settings_override", FCVAR_DEVELOPMENTONLY},
- {"highlight_draw", FCVAR_DEVELOPMENTONLY},
-
- // sys/engine settings
- {"sleep_when_meeting_framerate", FCVAR_DEVELOPMENTONLY},
- {"sleep_when_meeting_framerate_headroom_ms", FCVAR_DEVELOPMENTONLY},
- {"not_focus_sleep", FCVAR_DEVELOPMENTONLY},
- {"sp_not_focus_pause", FCVAR_DEVELOPMENTONLY},
- {"joy_requireFocus", FCVAR_DEVELOPMENTONLY},
-
- {"host_thread_mode", FCVAR_DEVELOPMENTONLY},
- {"phys_enable_simd_optimizations", FCVAR_DEVELOPMENTONLY},
- {"phys_enable_experimental_optimizations", FCVAR_DEVELOPMENTONLY},
-
- {"community_frame_run", FCVAR_DEVELOPMENTONLY},
- {"sv_single_core_dedi", FCVAR_DEVELOPMENTONLY},
- {"sv_stressbots", FCVAR_DEVELOPMENTONLY},
-
- {"fatal_script_errors", FCVAR_DEVELOPMENTONLY},
- {"fatal_script_errors_client", FCVAR_DEVELOPMENTONLY},
- {"fatal_script_errors_server", FCVAR_DEVELOPMENTONLY},
- {"script_error_on_midgame_load", FCVAR_DEVELOPMENTONLY}, // idk what this is
-
- {"ai_ainRebuildOnMapStart", FCVAR_DEVELOPMENTONLY},
-
- {"save_enable", FCVAR_DEVELOPMENTONLY},
-
- // cheat commands
- {"switchclass", FCVAR_DEVELOPMENTONLY},
- {"set", FCVAR_DEVELOPMENTONLY},
- {"_setClassVarServer", FCVAR_DEVELOPMENTONLY},
-
- // reparse commands
- {"aisettings_reparse", FCVAR_DEVELOPMENTONLY},
- {"aisettings_reparse_client", FCVAR_DEVELOPMENTONLY},
- {"damagedefs_reparse", FCVAR_DEVELOPMENTONLY},
- {"damagedefs_reparse_client", FCVAR_DEVELOPMENTONLY},
- {"playerSettings_reparse", FCVAR_DEVELOPMENTONLY},
- {"_playerSettings_reparse_Server", FCVAR_DEVELOPMENTONLY},
-
- };
-
- const std::vector<std::tuple<const char*, const char*>> CVAR_FIXUP_DEFAULT_VALUES = {
- {"sv_stressbots", "0"}, // not currently used but this is probably a bad default if we get bots working
- {"cl_pred_optimize", "0"} // fixes issues with animation prediction in thirdperson
- };
-
- for (auto& fixup : CVAR_FIXUP_ADD_FLAGS)
- {
- ConCommandBase* command = g_pCVar->FindCommandBase(std::get<0>(fixup));
- if (command)
- command->m_nFlags |= std::get<1>(fixup);
- }
-
- for (auto& fixup : CVAR_FIXUP_REMOVE_FLAGS)
- {
- ConCommandBase* command = g_pCVar->FindCommandBase(std::get<0>(fixup));
- if (command)
- command->m_nFlags &= ~std::get<1>(fixup);
- }
-
- for (auto& fixup : CVAR_FIXUP_DEFAULT_VALUES)
- {
- ConVar* cvar = g_pCVar->FindVar(std::get<0>(fixup));
- if (cvar && !strcmp(cvar->GetString(), cvar->m_pszDefaultValue))
- {
- cvar->SetValue(std::get<1>(fixup));
- cvar->m_pszDefaultValue = std::get<1>(fixup);
- }
- }
-}
diff --git a/NorthstarDLL/shared/misccommands.h b/NorthstarDLL/shared/misccommands.h
deleted file mode 100644
index 07a07fb3..00000000
--- a/NorthstarDLL/shared/misccommands.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-void AddMiscConCommands();
-void FixupCvarFlags();
diff --git a/NorthstarDLL/shared/playlist.cpp b/NorthstarDLL/shared/playlist.cpp
deleted file mode 100644
index 2b9ad979..00000000
--- a/NorthstarDLL/shared/playlist.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#include "playlist.h"
-#include "core/convar/concommand.h"
-#include "core/convar/convar.h"
-#include "squirrel/squirrel.h"
-#include "engine/hoststate.h"
-#include "engine/r2engine.h"
-#include "server/serverpresence.h"
-
-AUTOHOOK_INIT()
-
-// use the R2 namespace for game funcs
-namespace R2
-{
- DEFINED_VAR_AT(engine.dll + 0x18C640, GetCurrentPlaylistName);
- DEFINED_VAR_AT(engine.dll + 0x18EB20, SetCurrentPlaylist);
- DEFINED_VAR_AT(engine.dll + 0x18ED00, SetPlaylistVarOverride);
- DEFINED_VAR_AT(engine.dll + 0x18C680, GetCurrentPlaylistVar);
-} // namespace R2
-
-ConVar* Cvar_ns_use_clc_SetPlaylistVarOverride;
-
-// clang-format off
-AUTOHOOK(clc_SetPlaylistVarOverride__Process, engine.dll + 0x222180,
-char, __fastcall, (void* a1, void* a2))
-// clang-format on
-{
- // the private_match playlist on mp_lobby is the only situation where there should be any legitimate sending of this netmessage
- if (!Cvar_ns_use_clc_SetPlaylistVarOverride->GetBool() || strcmp(R2::GetCurrentPlaylistName(), "private_match") ||
- strcmp(g_pGlobals->m_pMapName, "mp_lobby"))
- return 1;
-
- return clc_SetPlaylistVarOverride__Process(a1, a2);
-}
-
-// clang-format off
-AUTOHOOK(SetCurrentPlaylist, engine.dll + 0x18EB20,
-bool, __fastcall, (const char* pPlaylistName))
-// clang-format on
-{
- bool bSuccess = SetCurrentPlaylist(pPlaylistName);
-
- if (bSuccess)
- {
- spdlog::info("Set playlist to {}", R2::GetCurrentPlaylistName());
- g_pServerPresence->SetPlaylist(R2::GetCurrentPlaylistName());
- }
-
- return bSuccess;
-}
-
-// clang-format off
-AUTOHOOK(SetPlaylistVarOverride, engine.dll + 0x18ED00,
-void, __fastcall, (const char* pVarName, const char* pValue))
-// clang-format on
-{
- if (strlen(pValue) >= 64)
- return;
-
- SetPlaylistVarOverride(pVarName, pValue);
-}
-
-// clang-format off
-AUTOHOOK(GetCurrentPlaylistVar, engine.dll + 0x18C680,
-const char*, __fastcall, (const char* pVarName, bool bUseOverrides))
-// clang-format on
-{
- if (!bUseOverrides && !strcmp(pVarName, "max_players"))
- bUseOverrides = true;
-
- return GetCurrentPlaylistVar(pVarName, bUseOverrides);
-}
-
-// clang-format off
-AUTOHOOK(GetCurrentGamemodeMaxPlayers, engine.dll + 0x18C430,
-int, __fastcall, ())
-// clang-format on
-{
- const char* pMaxPlayers = R2::GetCurrentPlaylistVar("max_players", 0);
- if (!pMaxPlayers)
- return GetCurrentGamemodeMaxPlayers();
-
- int iMaxPlayers = atoi(pMaxPlayers);
- return iMaxPlayers;
-}
-
-void ConCommand_playlist(const CCommand& args)
-{
- if (args.ArgC() < 2)
- return;
-
- R2::SetCurrentPlaylist(args.Arg(1));
-}
-
-void ConCommand_setplaylistvaroverride(const CCommand& args)
-{
- if (args.ArgC() < 3)
- return;
-
- for (int i = 1; i < args.ArgC(); i += 2)
- R2::SetPlaylistVarOverride(args.Arg(i), args.Arg(i + 1));
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, (ConCommand, ConVar), (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- // playlist is the name of the command on respawn servers, but we already use setplaylist so can't get rid of it
- RegisterConCommand("playlist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE);
- RegisterConCommand("setplaylist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE);
- RegisterConCommand("setplaylistvaroverrides", ConCommand_setplaylistvaroverride, "sets a playlist var override", FCVAR_NONE);
-
- // note: clc_SetPlaylistVarOverride is pretty insecure, since it allows for entirely arbitrary playlist var overrides to be sent to the
- // server, this is somewhat restricted on custom servers to prevent it being done outside of private matches, but ideally it should be
- // disabled altogether, since the custom menus won't use it anyway this should only really be accepted if you want vanilla client
- // compatibility
- Cvar_ns_use_clc_SetPlaylistVarOverride = new ConVar(
- "ns_use_clc_SetPlaylistVarOverride", "0", FCVAR_GAMEDLL, "Whether the server should accept clc_SetPlaylistVarOverride messages");
-
- // patch to prevent clc_SetPlaylistVarOverride from being able to crash servers if we reach max overrides due to a call to Error (why is
- // this possible respawn, wtf) todo: add a warning for this
- module.Offset(0x18ED8D).Patch("C3");
-
- // patch to allow setplaylistvaroverride to be called before map init on dedicated and private match launched through the game
- module.Offset(0x18ED17).NOP(6);
-}
diff --git a/NorthstarDLL/shared/playlist.h b/NorthstarDLL/shared/playlist.h
deleted file mode 100644
index e56fdf96..00000000
--- a/NorthstarDLL/shared/playlist.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-// use the R2 namespace for game funcs
-namespace R2
-{
- inline const char* (*GetCurrentPlaylistName)();
- inline void (*SetCurrentPlaylist)(const char* pPlaylistName);
- inline void (*SetPlaylistVarOverride)(const char* pVarName, const char* pValue);
- inline const char* (*GetCurrentPlaylistVar)(const char* pVarName, bool bUseOverrides);
-} // namespace R2
diff --git a/NorthstarDLL/squirrel/squirrel.cpp b/NorthstarDLL/squirrel/squirrel.cpp
deleted file mode 100644
index ac9a2ce9..00000000
--- a/NorthstarDLL/squirrel/squirrel.cpp
+++ /dev/null
@@ -1,943 +0,0 @@
-#include "squirrel.h"
-#include "mods/modsavefiles.h"
-#include "logging/logging.h"
-#include "core/convar/concommand.h"
-#include "mods/modmanager.h"
-#include "dedicated/dedicated.h"
-#include "engine/r2engine.h"
-#include "core/tier0.h"
-#include "plugins/plugin_abi.h"
-#include "plugins/plugins.h"
-#include "ns_version.h"
-#include "core/vanilla.h"
-
-#include <any>
-
-AUTOHOOK_INIT()
-
-std::shared_ptr<ColoredLogger> getSquirrelLoggerByContext(ScriptContext context)
-{
- switch (context)
- {
- case ScriptContext::UI:
- return NS::log::SCRIPT_UI;
- case ScriptContext::CLIENT:
- return NS::log::SCRIPT_CL;
- case ScriptContext::SERVER:
- return NS::log::SCRIPT_SV;
- default:
- throw std::runtime_error("getSquirrelLoggerByContext called with invalid context");
- return nullptr;
- }
-}
-
-namespace NS::log
-{
- template <ScriptContext context> std::shared_ptr<spdlog::logger> squirrel_logger()
- {
- // Switch statements can't be constexpr afaik
- // clang-format off
- if constexpr (context == ScriptContext::UI) { return SCRIPT_UI; }
- if constexpr (context == ScriptContext::CLIENT) { return SCRIPT_CL; }
- if constexpr (context == ScriptContext::SERVER) { return SCRIPT_SV; }
- // clang-format on
- }
-}; // namespace NS::log
-
-const char* GetContextName(ScriptContext context)
-{
- switch (context)
- {
- case ScriptContext::CLIENT:
- return "CLIENT";
- case ScriptContext::SERVER:
- return "SERVER";
- case ScriptContext::UI:
- return "UI";
- default:
- return "UNKNOWN";
- }
-}
-
-const char* GetContextName_Short(ScriptContext context)
-{
- switch (context)
- {
- case ScriptContext::CLIENT:
- return "CL";
- case ScriptContext::SERVER:
- return "SV";
- case ScriptContext::UI:
- return "UI";
- default:
- return "??";
- }
-}
-
-eSQReturnType SQReturnTypeFromString(const char* pReturnType)
-{
- static const std::map<std::string, eSQReturnType> sqReturnTypeNameToString = {
- {"bool", eSQReturnType::Boolean},
- {"float", eSQReturnType::Float},
- {"vector", eSQReturnType::Vector},
- {"int", eSQReturnType::Integer},
- {"entity", eSQReturnType::Entity},
- {"string", eSQReturnType::String},
- {"array", eSQReturnType::Arrays},
- {"asset", eSQReturnType::Asset},
- {"table", eSQReturnType::Table}};
-
- if (sqReturnTypeNameToString.find(pReturnType) != sqReturnTypeNameToString.end())
- return sqReturnTypeNameToString.at(pReturnType);
- else
- return eSQReturnType::Default; // previous default value
-}
-
-ScriptContext ScriptContextFromString(std::string string)
-{
- if (string == "UI")
- return ScriptContext::UI;
- if (string == "CLIENT")
- return ScriptContext::CLIENT;
- if (string == "SERVER")
- return ScriptContext::SERVER;
- else
- return ScriptContext::INVALID;
-}
-
-const char* SQTypeNameFromID(int type)
-{
- switch (type)
- {
- case OT_ASSET:
- return "asset";
- case OT_INTEGER:
- return "int";
- case OT_BOOL:
- return "bool";
- case SQOBJECT_NUMERIC:
- return "float or int";
- case OT_NULL:
- return "null";
- case OT_VECTOR:
- return "vector";
- case 0:
- return "var";
- case OT_USERDATA:
- return "userdata";
- case OT_FLOAT:
- return "float";
- case OT_STRING:
- return "string";
- case OT_ARRAY:
- return "array";
- case 0x8000200:
- return "function";
- case 0x8100000:
- return "structdef";
- case OT_THREAD:
- return "thread";
- case OT_FUNCPROTO:
- return "function";
- case OT_CLAAS:
- return "class";
- case OT_WEAKREF:
- return "weakref";
- case 0x8080000:
- return "unimplemented function";
- case 0x8200000:
- return "struct instance";
- case OT_TABLE:
- return "table";
- case 0xA008000:
- return "instance";
- case OT_ENTITY:
- return "entity";
- }
- return "";
-}
-
-template <ScriptContext context> void SquirrelManager<context>::GenerateSquirrelFunctionsStruct(SquirrelFunctions* s)
-{
- s->RegisterSquirrelFunc = RegisterSquirrelFunc;
- s->__sq_defconst = __sq_defconst;
-
- s->__sq_compilebuffer = __sq_compilebuffer;
- s->__sq_call = __sq_call;
- s->__sq_raiseerror = __sq_raiseerror;
- s->__sq_compilefile = __sq_compilefile;
-
- s->__sq_newarray = __sq_newarray;
- s->__sq_arrayappend = __sq_arrayappend;
-
- s->__sq_newtable = __sq_newtable;
- s->__sq_newslot = __sq_newslot;
-
- s->__sq_pushroottable = __sq_pushroottable;
- s->__sq_pushstring = __sq_pushstring;
- s->__sq_pushinteger = __sq_pushinteger;
- s->__sq_pushfloat = __sq_pushfloat;
- s->__sq_pushbool = __sq_pushbool;
- s->__sq_pushasset = __sq_pushasset;
- s->__sq_pushvector = __sq_pushvector;
- s->__sq_pushobject = __sq_pushobject;
-
- s->__sq_getstring = __sq_getstring;
- s->__sq_getinteger = __sq_getinteger;
- s->__sq_getfloat = __sq_getfloat;
- s->__sq_getbool = __sq_getbool;
- s->__sq_get = __sq_get;
- s->__sq_getasset = __sq_getasset;
- s->__sq_getuserdata = __sq_getuserdata;
- s->__sq_getvector = __sq_getvector;
- s->__sq_getthisentity = __sq_getthisentity;
- s->__sq_getobject = __sq_getobject;
-
- s->__sq_stackinfos = __sq_stackinfos;
-
- s->__sq_createuserdata = __sq_createuserdata;
- s->__sq_setuserdatatypeid = __sq_setuserdatatypeid;
- s->__sq_getfunction = __sq_getfunction;
-
- s->__sq_schedule_call_external = AsyncCall_External;
-
- s->__sq_getentityfrominstance = __sq_getentityfrominstance;
- s->__sq_GetEntityConstant_CBaseEntity = __sq_GetEntityConstant_CBaseEntity;
-
- s->__sq_pushnewstructinstance = __sq_pushnewstructinstance;
- s->__sq_sealstructslot = __sq_sealstructslot;
-}
-
-// Allows for generating squirrelmessages from plugins.
-void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function, void* userdata)
-{
- SquirrelMessage message {};
- message.functionName = func_name;
- message.isExternal = true;
- message.externalFunc = function;
- message.userdata = userdata;
- switch (context)
- {
- case ScriptContext::CLIENT:
- g_pSquirrel<ScriptContext::CLIENT>->messageBuffer->push(message);
- break;
- case ScriptContext::SERVER:
- g_pSquirrel<ScriptContext::SERVER>->messageBuffer->push(message);
- break;
- case ScriptContext::UI:
- g_pSquirrel<ScriptContext::UI>->messageBuffer->push(message);
- break;
- }
-}
-
-// needed to define implementations for squirrelmanager outside of squirrel.h without compiler errors
-template class SquirrelManager<ScriptContext::SERVER>;
-template class SquirrelManager<ScriptContext::CLIENT>;
-template class SquirrelManager<ScriptContext::UI>;
-
-template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquirrelVM* newSqvm)
-{
- m_pSQVM = newSqvm;
-
- for (SQFuncRegistration* funcReg : m_funcRegistrations)
- {
- spdlog::info("Registering {} function {}", GetContextName(context), funcReg->squirrelFuncName);
- RegisterSquirrelFunc(m_pSQVM, funcReg, 1);
- }
-
- for (auto& pair : g_pModManager->m_DependencyConstants)
- {
- bool bWasFound = false;
-
- for (Mod& dependency : g_pModManager->m_LoadedMods)
- {
- if (!dependency.m_bEnabled)
- continue;
-
- if (dependency.Name == pair.second)
- {
- bWasFound = true;
- break;
- }
- }
-
- defconst(m_pSQVM, pair.first.c_str(), bWasFound);
- }
-
- auto loadedPlugins = &g_pPluginManager->m_vLoadedPlugins;
- for (const auto& pluginName : g_pModManager->m_PluginDependencyConstants)
- {
- auto f = [&](Plugin plugin) -> bool { return plugin.dependencyName == pluginName; };
- defconst(m_pSQVM, pluginName.c_str(), std::find_if(loadedPlugins->begin(), loadedPlugins->end(), f) != loadedPlugins->end());
- }
-
- defconst(m_pSQVM, "MAX_FOLDER_SIZE", GetMaxSaveFolderSize() / 1024);
-
- // define squirrel constants for northstar(.dll) version
- constexpr int version[4] {NORTHSTAR_VERSION};
- defconst(m_pSQVM, "NS_VERSION_MAJOR", version[0]);
- defconst(m_pSQVM, "NS_VERSION_MINOR", version[1]);
- defconst(m_pSQVM, "NS_VERSION_PATCH", version[2]);
- defconst(m_pSQVM, "NS_VERSION_DEV", version[3]);
-
- // define squirrel constant for if we are in vanilla-compatibility mode
- defconst(m_pSQVM, "VANILLA", g_pVanillaCompatibility->GetVanillaCompatibility());
-
- g_pSquirrel<context>->messageBuffer = new SquirrelMessageBuffer();
- g_pPluginManager->InformSQVMCreated(context, newSqvm);
-}
-
-template <ScriptContext context> void SquirrelManager<context>::VMDestroyed()
-{
- // Call all registered mod Destroy callbacks.
- if (g_pModManager)
- {
- NS::log::squirrel_logger<context>()->info("Calling Destroy callbacks for all loaded mods.");
-
- for (const Mod& loadedMod : g_pModManager->m_LoadedMods)
- {
- for (const ModScript& script : loadedMod.Scripts)
- {
- for (const ModScriptCallback& callback : script.Callbacks)
- {
- if (callback.Context != context || callback.DestroyCallback.length() == 0)
- {
- continue;
- }
-
- Call(callback.DestroyCallback.c_str());
- NS::log::squirrel_logger<context>()->info("Executed Destroy callback {}.", callback.DestroyCallback);
- }
- }
- }
- }
-
- g_pPluginManager->InformSQVMDestroyed(context);
-
- // Discard the previous vm and delete the message buffer.
- m_pSQVM = nullptr;
-
- delete g_pSquirrel<context>->messageBuffer;
- g_pSquirrel<context>->messageBuffer = nullptr;
-}
-
-template <ScriptContext context> void SquirrelManager<context>::ExecuteCode(const char* pCode)
-{
- if (!m_pSQVM || !m_pSQVM->sqvm)
- {
- spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context));
- return;
- }
-
- spdlog::info("Executing {} script code {} ", GetContextName(context), pCode);
-
- std::string strCode(pCode);
- CompileBufferState bufferState = CompileBufferState(strCode);
-
- SQRESULT compileResult = compilebuffer(&bufferState, "console");
- spdlog::info("sq_compilebuffer returned {}", PrintSQRESULT.at(compileResult));
-
- if (compileResult != SQRESULT_ERROR)
- {
- pushroottable(m_pSQVM->sqvm);
- SQRESULT callResult = _call(m_pSQVM->sqvm, 0);
- spdlog::info("sq_call returned {}", PrintSQRESULT.at(callResult));
- }
-}
-
-template <ScriptContext context> void SquirrelManager<context>::AddFuncRegistration(
- std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func)
-{
- SQFuncRegistration* reg = new SQFuncRegistration;
-
- reg->squirrelFuncName = new char[name.size() + 1];
- strcpy((char*)reg->squirrelFuncName, name.c_str());
- reg->cppFuncName = reg->squirrelFuncName;
-
- reg->helpText = new char[helpText.size() + 1];
- strcpy((char*)reg->helpText, helpText.c_str());
-
- reg->returnTypeString = new char[returnType.size() + 1];
- strcpy((char*)reg->returnTypeString, returnType.c_str());
- reg->returnType = SQReturnTypeFromString(returnType.c_str());
-
- reg->argTypes = new char[argTypes.size() + 1];
- strcpy((char*)reg->argTypes, argTypes.c_str());
-
- reg->funcPtr = func;
-
- m_funcRegistrations.push_back(reg);
-}
-
-template <ScriptContext context> SQRESULT SquirrelManager<context>::setupfunc(const SQChar* funcname)
-{
- pushroottable(m_pSQVM->sqvm);
- pushstring(m_pSQVM->sqvm, funcname, -1);
-
- SQRESULT result = get(m_pSQVM->sqvm, -2);
- if (result != SQRESULT_ERROR)
- pushroottable(m_pSQVM->sqvm);
-
- return result;
-}
-
-template <ScriptContext context> void SquirrelManager<context>::AddFuncOverride(std::string name, SQFunction func)
-{
- m_funcOverrides[name] = func;
-}
-
-// hooks
-bool IsUIVM(ScriptContext context, HSquirrelVM* pSqvm)
-{
- return ScriptContext(pSqvm->sharedState->cSquirrelVM->vmContext) == ScriptContext::UI;
-}
-
-template <ScriptContext context> void* (*sq_compiler_create)(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError);
-template <ScriptContext context> void* __fastcall sq_compiler_createHook(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError)
-{
- // store whether errors generated from this compile should be fatal
- if (IsUIVM(context, sqvm))
- g_pSquirrel<ScriptContext::UI>->m_bFatalCompilationErrors = bShouldThrowError;
- else
- g_pSquirrel<context>->m_bFatalCompilationErrors = bShouldThrowError;
-
- return sq_compiler_create<context>(sqvm, a2, a3, bShouldThrowError);
-}
-
-template <ScriptContext context> SQInteger (*SQPrint)(HSquirrelVM* sqvm, const char* fmt);
-template <ScriptContext context> SQInteger SQPrintHook(HSquirrelVM* sqvm, const char* fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
-
- SQChar buf[1024];
- int charsWritten = vsnprintf_s(buf, _TRUNCATE, fmt, va);
-
- if (charsWritten > 0)
- {
- if (buf[charsWritten - 1] == '\n')
- buf[charsWritten - 1] = '\0';
- g_pSquirrel<context>->logger->info("{}", buf);
- }
-
- va_end(va);
- return 0;
-}
-
-template <ScriptContext context> CSquirrelVM* (*CreateNewVM)(void* a1, ScriptContext realContext);
-template <ScriptContext context> CSquirrelVM* __fastcall CreateNewVMHook(void* a1, ScriptContext realContext)
-{
- CSquirrelVM* sqvm = CreateNewVM<context>(a1, realContext);
- if (realContext == ScriptContext::UI)
- g_pSquirrel<ScriptContext::UI>->VMCreated(sqvm);
- else
- g_pSquirrel<context>->VMCreated(sqvm);
-
- spdlog::info("CreateNewVM {} {}", GetContextName(realContext), (void*)sqvm);
- return sqvm;
-}
-
-template <ScriptContext context> bool (*CSquirrelVM_init)(CSquirrelVM* vm, ScriptContext realContext, float time);
-template <ScriptContext context> bool __fastcall CSquirrelVM_initHook(CSquirrelVM* vm, ScriptContext realContext, float time)
-{
- bool ret = CSquirrelVM_init<context>(vm, realContext, time);
- for (Mod mod : g_pModManager->m_LoadedMods)
- {
- if (mod.m_bEnabled && mod.initScript.size() != 0)
- {
- std::string name = mod.initScript.substr(mod.initScript.find_last_of('/') + 1);
- std::string path = std::string("scripts/vscripts/") + mod.initScript;
- if (g_pSquirrel<context>->compilefile(vm, path.c_str(), name.c_str(), 0))
- g_pSquirrel<context>->compilefile(vm, path.c_str(), name.c_str(), 1);
- }
- }
- return ret;
-}
-
-template <ScriptContext context> void (*DestroyVM)(void* a1, CSquirrelVM* sqvm);
-template <ScriptContext context> void __fastcall DestroyVMHook(void* a1, CSquirrelVM* sqvm)
-{
- ScriptContext realContext = context; // ui and client use the same function so we use this for prints
- if (IsUIVM(context, sqvm->sqvm))
- {
- realContext = ScriptContext::UI;
- g_pSquirrel<ScriptContext::UI>->VMDestroyed();
- DestroyVM<ScriptContext::CLIENT>(a1, sqvm); // If we pass UI here it crashes
- }
- else
- {
- g_pSquirrel<context>->VMDestroyed();
- DestroyVM<context>(a1, sqvm);
- }
-
- spdlog::info("DestroyVM {} {}", GetContextName(realContext), (void*)sqvm);
-}
-
-template <ScriptContext context> void (*SQCompileError)(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column);
-template <ScriptContext context>
-void __fastcall ScriptCompileErrorHook(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column)
-{
- bool bIsFatalError = g_pSquirrel<context>->m_bFatalCompilationErrors;
- ScriptContext realContext = context; // ui and client use the same function so we use this for prints
- if (IsUIVM(context, sqvm))
- {
- realContext = ScriptContext::UI;
- bIsFatalError = g_pSquirrel<ScriptContext::UI>->m_bFatalCompilationErrors;
- }
-
- auto logger = getSquirrelLoggerByContext(realContext);
-
- logger->error("COMPILE ERROR {}", error);
- logger->error("{} line [{}] column [{}]", file, line, column);
-
- // use disconnect to display an error message for the compile error, but only if the compilation error was fatal
- // todo, we could get this from sqvm itself probably, rather than hooking sq_compiler_create
- if (bIsFatalError)
- {
- // kill dedicated server if we hit this
- if (IsDedicatedServer())
- {
- logger->error("Exiting dedicated server, compile error is fatal");
- // flush the logger before we exit so debug things get saved to log file
- logger->flush();
- exit(EXIT_FAILURE);
- }
- else
- {
- Cbuf_AddText(
- Cbuf_GetCurrentPlayer(),
- fmt::format("disconnect \"Encountered {} script compilation error, see console for details.\"", GetContextName(realContext))
- .c_str(),
- cmd_source_t::kCommandSrcCode);
-
- // likely temp: show console so user can see any errors, as error message wont display if ui is dead
- // maybe we could disable all mods other than the coremods and try a reload before doing this?
- // could also maybe do some vgui bullshit to show something visually rather than console
- if (realContext == ScriptContext::UI)
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "showconsole", cmd_source_t::kCommandSrcCode);
- }
- }
-
- // dont call the original function since it kills game lol
-}
-
-template <ScriptContext context> int64_t (*RegisterSquirrelFunction)(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown);
-template <ScriptContext context>
-int64_t __fastcall RegisterSquirrelFunctionHook(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown)
-{
- if (IsUIVM(context, sqvm->sqvm))
- {
- if (g_pSquirrel<ScriptContext::UI>->m_funcOverrides.count(funcReg->squirrelFuncName))
- {
- g_pSquirrel<ScriptContext::UI>->m_funcOriginals[funcReg->squirrelFuncName] = funcReg->funcPtr;
- funcReg->funcPtr = g_pSquirrel<ScriptContext::UI>->m_funcOverrides[funcReg->squirrelFuncName];
- spdlog::info("Replacing {} in UI", std::string(funcReg->squirrelFuncName));
- }
-
- return g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc(sqvm, funcReg, unknown);
- }
-
- if (g_pSquirrel<context>->m_funcOverrides.find(funcReg->squirrelFuncName) != g_pSquirrel<context>->m_funcOverrides.end())
- {
- g_pSquirrel<context>->m_funcOriginals[funcReg->squirrelFuncName] = funcReg->funcPtr;
- funcReg->funcPtr = g_pSquirrel<context>->m_funcOverrides[funcReg->squirrelFuncName];
- spdlog::info("Replacing {} in Client", std::string(funcReg->squirrelFuncName));
- }
-
- return g_pSquirrel<context>->RegisterSquirrelFunc(sqvm, funcReg, unknown);
-}
-
-template <ScriptContext context> bool (*CallScriptInitCallback)(void* sqvm, const char* callback);
-template <ScriptContext context> bool __fastcall CallScriptInitCallbackHook(void* sqvm, const char* callback)
-{
- ScriptContext realContext = context;
- bool bShouldCallCustomCallbacks = true;
-
- if (context == ScriptContext::CLIENT)
- {
- if (!strcmp(callback, "UICodeCallback_UIInit"))
- realContext = ScriptContext::UI;
- else if (strcmp(callback, "ClientCodeCallback_MapSpawn"))
- bShouldCallCustomCallbacks = false;
- }
- else if (context == ScriptContext::SERVER)
- bShouldCallCustomCallbacks = !strcmp(callback, "CodeCallback_MapSpawn");
-
- if (bShouldCallCustomCallbacks)
- {
- for (Mod mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- for (ModScript script : mod.Scripts)
- {
- for (ModScriptCallback modCallback : script.Callbacks)
- {
- if (modCallback.Context == realContext && modCallback.BeforeCallback.length())
- {
- spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.BeforeCallback);
- CallScriptInitCallback<context>(sqvm, modCallback.BeforeCallback.c_str());
- }
- }
- }
- }
- }
-
- spdlog::info("{} CodeCallback {} called", GetContextName(realContext), callback);
- if (!bShouldCallCustomCallbacks)
- spdlog::info("Not executing custom callbacks for CodeCallback {}", callback);
- bool ret = CallScriptInitCallback<context>(sqvm, callback);
-
- // run after callbacks
- if (bShouldCallCustomCallbacks)
- {
- for (Mod mod : g_pModManager->m_LoadedMods)
- {
- if (!mod.m_bEnabled)
- continue;
-
- for (ModScript script : mod.Scripts)
- {
- for (ModScriptCallback modCallback : script.Callbacks)
- {
- if (modCallback.Context == realContext && modCallback.AfterCallback.length())
- {
- spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.AfterCallback);
- CallScriptInitCallback<context>(sqvm, modCallback.AfterCallback.c_str());
- }
- }
- }
- }
- }
-
- return ret;
-}
-
-template <ScriptContext context> void ConCommand_script(const CCommand& args)
-{
- g_pSquirrel<context>->ExecuteCode(args.ArgS());
-}
-
-// literal class type that wraps a constant expression string
-template <size_t N> struct TemplateStringLiteral
-{
- constexpr TemplateStringLiteral(const char (&str)[N])
- {
- std::copy_n(str, N, value);
- }
-
- char value[N];
-};
-
-template <ScriptContext context, TemplateStringLiteral funcName> SQRESULT SQ_StubbedFunc(HSquirrelVM* sqvm)
-{
- spdlog::info("Blocking call to stubbed function {} in {}", funcName.value, GetContextName(context));
- return SQRESULT_NULL;
-}
-
-template <ScriptContext context> void StubUnsafeSQFuncs()
-{
- if (!CommandLine()->CheckParm("-allowunsafesqfuncs"))
- {
- g_pSquirrel<context>->AddFuncOverride("DevTextBufferWrite", SQ_StubbedFunc<context, "DevTextBufferWrite">);
- g_pSquirrel<context>->AddFuncOverride("DevTextBufferClear", SQ_StubbedFunc<context, "DevTextBufferClear">);
- g_pSquirrel<context>->AddFuncOverride("DevTextBufferDumpToFile", SQ_StubbedFunc<context, "DevTextBufferDumpToFile">);
- g_pSquirrel<context>->AddFuncOverride("Dev_CommandLineAddParam", SQ_StubbedFunc<context, "Dev_CommandLineAddParam">);
- g_pSquirrel<context>->AddFuncOverride("DevP4Checkout", SQ_StubbedFunc<context, "DevP4Checkout">);
- g_pSquirrel<context>->AddFuncOverride("DevP4Add", SQ_StubbedFunc<context, "DevP4Add">);
- }
-}
-
-template <ScriptContext context> void SquirrelManager<context>::ProcessMessageBuffer()
-{
- while (std::optional<SquirrelMessage> maybeMessage = messageBuffer->pop())
- {
- SquirrelMessage message = maybeMessage.value();
-
- SQObject functionobj {};
- int result = sq_getfunction(m_pSQVM->sqvm, message.functionName.c_str(), &functionobj, 0);
- if (result != 0) // This func returns 0 on success for some reason
- {
- NS::log::squirrel_logger<context>()->error(
- "ProcessMessageBuffer was unable to find function with name '{}'. Is it global?", message.functionName);
- continue;
- }
-
- pushobject(m_pSQVM->sqvm, &functionobj); // Push the function object
- pushroottable(m_pSQVM->sqvm);
-
- int argsAmount = message.args.size();
-
- if (message.isExternal && message.externalFunc != NULL)
- {
- argsAmount = message.externalFunc(m_pSQVM->sqvm, message.userdata);
- }
- else
- {
- for (auto& v : message.args)
- {
- // Execute lambda to push arg to stack
- v();
- }
- }
-
- _call(m_pSQVM->sqvm, argsAmount);
- }
-}
-
-ADD_SQFUNC(
- "string",
- NSGetCurrentModName,
- "",
- "Returns the mod name of the script running this function",
- ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
-{
- int depth = g_pSquirrel<context>->getinteger(sqvm, 1);
- if (auto mod = g_pSquirrel<context>->getcallingmod(sqvm, depth); mod == nullptr)
- {
- g_pSquirrel<context>->raiseerror(sqvm, "NSGetModName was called from a non-mod script. This shouldn't be possible");
- return SQRESULT_ERROR;
- }
- else
- {
- g_pSquirrel<context>->pushstring(sqvm, mod->Name.c_str());
- }
- return SQRESULT_NOTNULL;
-}
-
-ADD_SQFUNC(
- "string",
- NSGetCallingModName,
- "int depth = 0",
- "Returns the mod name of the script running this function",
- ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
-{
- int depth = g_pSquirrel<context>->getinteger(sqvm, 1);
- if (auto mod = g_pSquirrel<context>->getcallingmod(sqvm, depth); mod == nullptr)
- {
- g_pSquirrel<context>->pushstring(sqvm, "Unknown");
- }
- else
- {
- g_pSquirrel<context>->pushstring(sqvm, mod->Name.c_str());
- }
- return SQRESULT_NOTNULL;
-}
-
-ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(client.dll)
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst = module.Offset(0x12120).RCast<sq_defconstType>();
- g_pSquirrel<ScriptContext::UI>->__sq_defconst = g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst;
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_compilebuffer = module.Offset(0x3110).RCast<sq_compilebufferType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushroottable = module.Offset(0x5860).RCast<sq_pushroottableType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_compilefile = module.Offset(0xF950).RCast<sq_compilefileType>();
- g_pSquirrel<ScriptContext::UI>->__sq_compilebuffer = g_pSquirrel<ScriptContext::CLIENT>->__sq_compilebuffer;
- g_pSquirrel<ScriptContext::UI>->__sq_pushroottable = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushroottable;
- g_pSquirrel<ScriptContext::UI>->__sq_compilefile = g_pSquirrel<ScriptContext::CLIENT>->__sq_compilefile;
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_call = module.Offset(0x8650).RCast<sq_callType>();
- g_pSquirrel<ScriptContext::UI>->__sq_call = g_pSquirrel<ScriptContext::CLIENT>->__sq_call;
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_newarray = module.Offset(0x39F0).RCast<sq_newarrayType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_arrayappend = module.Offset(0x3C70).RCast<sq_arrayappendType>();
- g_pSquirrel<ScriptContext::UI>->__sq_newarray = g_pSquirrel<ScriptContext::CLIENT>->__sq_newarray;
- g_pSquirrel<ScriptContext::UI>->__sq_arrayappend = g_pSquirrel<ScriptContext::CLIENT>->__sq_arrayappend;
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_newtable = module.Offset(0x3960).RCast<sq_newtableType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot = module.Offset(0x70B0).RCast<sq_newslotType>();
- g_pSquirrel<ScriptContext::UI>->__sq_newtable = g_pSquirrel<ScriptContext::CLIENT>->__sq_newtable;
- g_pSquirrel<ScriptContext::UI>->__sq_newslot = g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot;
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushstring = module.Offset(0x3440).RCast<sq_pushstringType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger = module.Offset(0x36A0).RCast<sq_pushintegerType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat = module.Offset(0x3800).RCast<sq_pushfloatType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool = module.Offset(0x3710).RCast<sq_pushboolType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset = module.Offset(0x3560).RCast<sq_pushassetType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushvector = module.Offset(0x3780).RCast<sq_pushvectorType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushobject = module.Offset(0x83D0).RCast<sq_pushobjectType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror = module.Offset(0x8470).RCast<sq_raiseerrorType>();
- g_pSquirrel<ScriptContext::UI>->__sq_pushstring = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushstring;
- g_pSquirrel<ScriptContext::UI>->__sq_pushinteger = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger;
- g_pSquirrel<ScriptContext::UI>->__sq_pushfloat = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat;
- g_pSquirrel<ScriptContext::UI>->__sq_pushbool = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool;
- g_pSquirrel<ScriptContext::UI>->__sq_pushvector = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushvector;
- g_pSquirrel<ScriptContext::UI>->__sq_pushasset = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset;
- g_pSquirrel<ScriptContext::UI>->__sq_pushobject = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushobject;
- g_pSquirrel<ScriptContext::UI>->__sq_raiseerror = g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror;
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getstring = module.Offset(0x60C0).RCast<sq_getstringType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getinteger = module.Offset(0x60E0).RCast<sq_getintegerType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getfloat = module.Offset(0x6100).RCast<sq_getfloatType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getbool = module.Offset(0x6130).RCast<sq_getboolType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_get = module.Offset(0x7C30).RCast<sq_getType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getasset = module.Offset(0x6010).RCast<sq_getassetType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getuserdata = module.Offset(0x63D0).RCast<sq_getuserdataType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getvector = module.Offset(0x6140).RCast<sq_getvectorType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getthisentity = module.Offset(0x12F80).RCast<sq_getthisentityType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getobject = module.Offset(0x6160).RCast<sq_getobjectType>();
- g_pSquirrel<ScriptContext::UI>->__sq_getstring = g_pSquirrel<ScriptContext::CLIENT>->__sq_getstring;
- g_pSquirrel<ScriptContext::UI>->__sq_getinteger = g_pSquirrel<ScriptContext::CLIENT>->__sq_getinteger;
- g_pSquirrel<ScriptContext::UI>->__sq_getfloat = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfloat;
- g_pSquirrel<ScriptContext::UI>->__sq_getbool = g_pSquirrel<ScriptContext::CLIENT>->__sq_getbool;
- g_pSquirrel<ScriptContext::UI>->__sq_get = g_pSquirrel<ScriptContext::CLIENT>->__sq_get;
- g_pSquirrel<ScriptContext::UI>->__sq_getasset = g_pSquirrel<ScriptContext::CLIENT>->__sq_getasset;
- g_pSquirrel<ScriptContext::UI>->__sq_getuserdata = g_pSquirrel<ScriptContext::CLIENT>->__sq_getuserdata;
- g_pSquirrel<ScriptContext::UI>->__sq_getvector = g_pSquirrel<ScriptContext::CLIENT>->__sq_getvector;
- g_pSquirrel<ScriptContext::UI>->__sq_getthisentity = g_pSquirrel<ScriptContext::CLIENT>->__sq_getthisentity;
- g_pSquirrel<ScriptContext::UI>->__sq_getobject = g_pSquirrel<ScriptContext::CLIENT>->__sq_getobject;
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_createuserdata = module.Offset(0x38D0).RCast<sq_createuserdataType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_setuserdatatypeid = module.Offset(0x6490).RCast<sq_setuserdatatypeidType>();
- g_pSquirrel<ScriptContext::UI>->__sq_createuserdata = g_pSquirrel<ScriptContext::CLIENT>->__sq_createuserdata;
- g_pSquirrel<ScriptContext::UI>->__sq_setuserdatatypeid = g_pSquirrel<ScriptContext::CLIENT>->__sq_setuserdatatypeid;
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x3E49B0).RCast<sq_GetEntityConstantType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getentityfrominstance = module.Offset(0x114F0).RCast<sq_getentityfrominstanceType>();
- g_pSquirrel<ScriptContext::UI>->__sq_GetEntityConstant_CBaseEntity =
- g_pSquirrel<ScriptContext::CLIENT>->__sq_GetEntityConstant_CBaseEntity;
- g_pSquirrel<ScriptContext::UI>->__sq_getentityfrominstance = g_pSquirrel<ScriptContext::CLIENT>->__sq_getentityfrominstance;
-
- // Message buffer stuff
- g_pSquirrel<ScriptContext::UI>->messageBuffer = g_pSquirrel<ScriptContext::CLIENT>->messageBuffer;
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x572FB0).RCast<sq_getfunctionType>();
- g_pSquirrel<ScriptContext::UI>->__sq_getfunction = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction;
- g_pSquirrel<ScriptContext::CLIENT>->__sq_stackinfos = module.Offset(0x35970).RCast<sq_stackinfosType>();
- g_pSquirrel<ScriptContext::UI>->__sq_stackinfos = g_pSquirrel<ScriptContext::CLIENT>->__sq_stackinfos;
-
- // Structs
- g_pSquirrel<ScriptContext::CLIENT>->__sq_pushnewstructinstance = module.Offset(0x5400).RCast<sq_pushnewstructinstanceType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_sealstructslot = module.Offset(0x5530).RCast<sq_sealstructslotType>();
- g_pSquirrel<ScriptContext::UI>->__sq_pushnewstructinstance = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushnewstructinstance;
- g_pSquirrel<ScriptContext::UI>->__sq_sealstructslot = g_pSquirrel<ScriptContext::CLIENT>->__sq_sealstructslot;
-
- MAKEHOOK(
- module.Offset(0x108E0),
- &RegisterSquirrelFunctionHook<ScriptContext::CLIENT>,
- &g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc);
- g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc = g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc;
-
- g_pSquirrel<ScriptContext::CLIENT>->logger = NS::log::SCRIPT_CL;
- g_pSquirrel<ScriptContext::UI>->logger = NS::log::SCRIPT_UI;
-
- // uiscript_reset concommand: don't loop forever if compilation fails
- module.Offset(0x3C6E4C).NOP(6);
-
- MAKEHOOK(module.Offset(0x8AD0), &sq_compiler_createHook<ScriptContext::CLIENT>, &sq_compiler_create<ScriptContext::CLIENT>);
-
- MAKEHOOK(module.Offset(0x12B00), &SQPrintHook<ScriptContext::CLIENT>, &SQPrint<ScriptContext::CLIENT>);
- MAKEHOOK(module.Offset(0x12BA0), &SQPrintHook<ScriptContext::UI>, &SQPrint<ScriptContext::UI>);
-
- MAKEHOOK(module.Offset(0x26130), &CreateNewVMHook<ScriptContext::CLIENT>, &CreateNewVM<ScriptContext::CLIENT>);
- MAKEHOOK(module.Offset(0x26E70), &DestroyVMHook<ScriptContext::CLIENT>, &DestroyVM<ScriptContext::CLIENT>);
- MAKEHOOK(module.Offset(0x79A50), &ScriptCompileErrorHook<ScriptContext::CLIENT>, &SQCompileError<ScriptContext::CLIENT>);
-
- MAKEHOOK(module.Offset(0x10190), &CallScriptInitCallbackHook<ScriptContext::CLIENT>, &CallScriptInitCallback<ScriptContext::CLIENT>);
-
- MAKEHOOK(module.Offset(0xE3B0), &CSquirrelVM_initHook<ScriptContext::CLIENT>, &CSquirrelVM_init<ScriptContext::CLIENT>);
-
- RegisterConCommand("script_client", ConCommand_script<ScriptContext::CLIENT>, "Executes script code on the client vm", FCVAR_CLIENTDLL);
- RegisterConCommand("script_ui", ConCommand_script<ScriptContext::UI>, "Executes script code on the ui vm", FCVAR_CLIENTDLL);
-
- StubUnsafeSQFuncs<ScriptContext::CLIENT>();
- StubUnsafeSQFuncs<ScriptContext::UI>();
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x6CB0).RCast<sq_getfunctionType>();
- g_pSquirrel<ScriptContext::UI>->__sq_getfunction = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction;
-
- SquirrelFunctions s = {};
- g_pSquirrel<ScriptContext::CLIENT>->GenerateSquirrelFunctionsStruct(&s);
- g_pPluginManager->InformSQVMLoad(ScriptContext::CLIENT, &s);
-}
-
-ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(server.dll)
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_defconst = module.Offset(0x1F550).RCast<sq_defconstType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_compilebuffer = module.Offset(0x3110).RCast<sq_compilebufferType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushroottable = module.Offset(0x5840).RCast<sq_pushroottableType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_call = module.Offset(0x8620).RCast<sq_callType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_compilefile = module.Offset(0x1CD80).RCast<sq_compilefileType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_newarray = module.Offset(0x39F0).RCast<sq_newarrayType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_arrayappend = module.Offset(0x3C70).RCast<sq_arrayappendType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_newtable = module.Offset(0x3960).RCast<sq_newtableType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_newslot = module.Offset(0x7080).RCast<sq_newslotType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushstring = module.Offset(0x3440).RCast<sq_pushstringType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushinteger = module.Offset(0x36A0).RCast<sq_pushintegerType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushfloat = module.Offset(0x3800).RCast<sq_pushfloatType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushbool = module.Offset(0x3710).RCast<sq_pushboolType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushasset = module.Offset(0x3560).RCast<sq_pushassetType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushvector = module.Offset(0x3780).RCast<sq_pushvectorType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushobject = module.Offset(0x83A0).RCast<sq_pushobjectType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_raiseerror = module.Offset(0x8440).RCast<sq_raiseerrorType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_getstring = module.Offset(0x60A0).RCast<sq_getstringType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_getinteger = module.Offset(0x60C0).RCast<sq_getintegerType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_getfloat = module.Offset(0x60E0).RCast<sq_getfloatType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_getbool = module.Offset(0x6110).RCast<sq_getboolType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_getasset = module.Offset(0x5FF0).RCast<sq_getassetType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_getuserdata = module.Offset(0x63B0).RCast<sq_getuserdataType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_getvector = module.Offset(0x6120).RCast<sq_getvectorType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_get = module.Offset(0x7C00).RCast<sq_getType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_getthisentity = module.Offset(0x203B0).RCast<sq_getthisentityType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_getobject = module.Offset(0x6140).RCast<sq_getobjectType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_createuserdata = module.Offset(0x38D0).RCast<sq_createuserdataType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_setuserdatatypeid = module.Offset(0x6470).RCast<sq_setuserdatatypeidType>();
-
- g_pSquirrel<ScriptContext::SERVER>->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x418AF0).RCast<sq_GetEntityConstantType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_getentityfrominstance = module.Offset(0x1E920).RCast<sq_getentityfrominstanceType>();
-
- g_pSquirrel<ScriptContext::SERVER>->logger = NS::log::SCRIPT_SV;
- // Message buffer stuff
- g_pSquirrel<ScriptContext::SERVER>->__sq_getfunction = module.Offset(0x6C85).RCast<sq_getfunctionType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_stackinfos = module.Offset(0x35920).RCast<sq_stackinfosType>();
-
- // Structs
- g_pSquirrel<ScriptContext::SERVER>->__sq_pushnewstructinstance = module.Offset(0x53e0).RCast<sq_pushnewstructinstanceType>();
- g_pSquirrel<ScriptContext::SERVER>->__sq_sealstructslot = module.Offset(0x5510).RCast<sq_sealstructslotType>();
-
- MAKEHOOK(
- module.Offset(0x1DD10),
- &RegisterSquirrelFunctionHook<ScriptContext::SERVER>,
- &g_pSquirrel<ScriptContext::SERVER>->RegisterSquirrelFunc);
-
- MAKEHOOK(module.Offset(0x8AA0), &sq_compiler_createHook<ScriptContext::SERVER>, &sq_compiler_create<ScriptContext::SERVER>);
-
- MAKEHOOK(module.Offset(0x1FE90), &SQPrintHook<ScriptContext::SERVER>, &SQPrint<ScriptContext::SERVER>);
- MAKEHOOK(module.Offset(0x260E0), &CreateNewVMHook<ScriptContext::SERVER>, &CreateNewVM<ScriptContext::SERVER>);
- MAKEHOOK(module.Offset(0x26E20), &DestroyVMHook<ScriptContext::SERVER>, &DestroyVM<ScriptContext::SERVER>);
- MAKEHOOK(module.Offset(0x799E0), &ScriptCompileErrorHook<ScriptContext::SERVER>, &SQCompileError<ScriptContext::SERVER>);
- MAKEHOOK(module.Offset(0x1D5C0), &CallScriptInitCallbackHook<ScriptContext::SERVER>, &CallScriptInitCallback<ScriptContext::SERVER>);
- MAKEHOOK(module.Offset(0x1B7E0), &CSquirrelVM_initHook<ScriptContext::SERVER>, &CSquirrelVM_init<ScriptContext::SERVER>);
- // FCVAR_CHEAT and FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS allows clients to execute this, but since it's unsafe we only allow it when cheats
- // are enabled for script_client and script_ui, we don't use cheats, so clients can execute them on themselves all they want
- RegisterConCommand(
- "script",
- ConCommand_script<ScriptContext::SERVER>,
- "Executes script code on the server vm",
- FCVAR_GAMEDLL | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS | FCVAR_CHEAT);
-
- StubUnsafeSQFuncs<ScriptContext::SERVER>();
-
- SquirrelFunctions s = {};
- g_pSquirrel<ScriptContext::SERVER>->GenerateSquirrelFunctionsStruct(&s);
- g_pPluginManager->InformSQVMLoad(ScriptContext::SERVER, &s);
-}
-
-void InitialiseSquirrelManagers()
-{
- g_pSquirrel<ScriptContext::CLIENT> = new SquirrelManager<ScriptContext::CLIENT>;
- g_pSquirrel<ScriptContext::UI> = new SquirrelManager<ScriptContext::UI>;
- g_pSquirrel<ScriptContext::SERVER> = new SquirrelManager<ScriptContext::SERVER>;
-}
diff --git a/NorthstarDLL/squirrel/squirrel.h b/NorthstarDLL/squirrel/squirrel.h
deleted file mode 100644
index a4932044..00000000
--- a/NorthstarDLL/squirrel/squirrel.h
+++ /dev/null
@@ -1,526 +0,0 @@
-#pragma once
-
-#include "squirrelclasstypes.h"
-#include "squirrelautobind.h"
-#include "core/math/vector.h"
-#include "plugins/plugin_abi.h"
-#include "mods/modmanager.h"
-
-/*
- definitions from hell
- required to function
-*/
-
-template <ScriptContext context, typename T> inline void SqRecurseArgs(FunctionVector& v, T& arg);
-
-template <ScriptContext context, typename T, typename... Args> inline void SqRecurseArgs(FunctionVector& v, T& arg, Args... args);
-
-/*
- sanity below
-*/
-
-// stolen from ttf2sdk: sqvm types
-typedef float SQFloat;
-typedef long SQInteger;
-typedef unsigned long SQUnsignedInteger;
-typedef char SQChar;
-typedef SQUnsignedInteger SQBool;
-
-static constexpr int operator&(ScriptContext first, ScriptContext second)
-{
- return first == second;
-}
-
-static constexpr int operator&(int first, ScriptContext second)
-{
- return first & (1 << static_cast<int>(second));
-}
-
-static constexpr int operator|(ScriptContext first, ScriptContext second)
-{
- return (1 << static_cast<int>(first)) + (1 << static_cast<int>(second));
-}
-
-static constexpr int operator|(int first, ScriptContext second)
-{
- return first + (1 << static_cast<int>(second));
-}
-
-const char* GetContextName(ScriptContext context);
-const char* GetContextName_Short(ScriptContext context);
-eSQReturnType SQReturnTypeFromString(const char* pReturnType);
-const char* SQTypeNameFromID(const int iTypeId);
-
-void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function, void* userdata);
-
-ScriptContext ScriptContextFromString(std::string string);
-
-namespace NS::log
-{
- template <ScriptContext context> std::shared_ptr<spdlog::logger> squirrel_logger();
-}; // namespace NS::log
-
-// This base class means that only the templated functions have to be rebuilt for each template instance
-// Cuts down on compile time by ~5 seconds
-class SquirrelManagerBase
-{
-protected:
- std::vector<SQFuncRegistration*> m_funcRegistrations;
-
-public:
- CSquirrelVM* m_pSQVM;
- std::map<std::string, SQFunction> m_funcOverrides = {};
- std::map<std::string, SQFunction> m_funcOriginals = {};
-
- bool m_bFatalCompilationErrors = false;
-
- std::shared_ptr<spdlog::logger> logger;
-
-#pragma region SQVM funcs
- RegisterSquirrelFuncType RegisterSquirrelFunc;
- sq_defconstType __sq_defconst;
-
- sq_compilebufferType __sq_compilebuffer;
- sq_callType __sq_call;
- sq_raiseerrorType __sq_raiseerror;
- sq_compilefileType __sq_compilefile;
-
- sq_newarrayType __sq_newarray;
- sq_arrayappendType __sq_arrayappend;
-
- sq_newtableType __sq_newtable;
- sq_newslotType __sq_newslot;
-
- sq_pushroottableType __sq_pushroottable;
- sq_pushstringType __sq_pushstring;
- sq_pushintegerType __sq_pushinteger;
- sq_pushfloatType __sq_pushfloat;
- sq_pushboolType __sq_pushbool;
- sq_pushassetType __sq_pushasset;
- sq_pushvectorType __sq_pushvector;
- sq_pushobjectType __sq_pushobject;
-
- sq_getstringType __sq_getstring;
- sq_getintegerType __sq_getinteger;
- sq_getfloatType __sq_getfloat;
- sq_getboolType __sq_getbool;
- sq_getType __sq_get;
- sq_getassetType __sq_getasset;
- sq_getuserdataType __sq_getuserdata;
- sq_getvectorType __sq_getvector;
- sq_getthisentityType __sq_getthisentity;
- sq_getobjectType __sq_getobject;
-
- sq_stackinfosType __sq_stackinfos;
-
- sq_createuserdataType __sq_createuserdata;
- sq_setuserdatatypeidType __sq_setuserdatatypeid;
- sq_getfunctionType __sq_getfunction;
-
- sq_getentityfrominstanceType __sq_getentityfrominstance;
- sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity;
-
- sq_pushnewstructinstanceType __sq_pushnewstructinstance;
- sq_sealstructslotType __sq_sealstructslot;
-
-#pragma endregion
-
-#pragma region SQVM func wrappers
- inline void defconst(CSquirrelVM* sqvm, const SQChar* pName, int nValue)
- {
- __sq_defconst(sqvm, pName, nValue);
- }
-
- inline SQRESULT
- compilebuffer(CompileBufferState* bufferState, const SQChar* bufferName = "unnamedbuffer", const SQBool bShouldThrowError = false)
- {
- return __sq_compilebuffer(m_pSQVM->sqvm, bufferState, bufferName, -1, bShouldThrowError);
- }
-
- inline SQRESULT _call(HSquirrelVM* sqvm, const SQInteger args)
- {
- return __sq_call(sqvm, args + 1, false, false);
- }
-
- inline SQInteger raiseerror(HSquirrelVM* sqvm, const SQChar* sError)
- {
- return __sq_raiseerror(sqvm, sError);
- }
-
- inline bool compilefile(CSquirrelVM* sqvm, const char* path, const char* name, int a4)
- {
- return __sq_compilefile(sqvm, path, name, a4);
- }
-
- inline void newarray(HSquirrelVM* sqvm, const SQInteger stackpos = 0)
- {
- __sq_newarray(sqvm, stackpos);
- }
-
- inline SQRESULT arrayappend(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_arrayappend(sqvm, stackpos);
- }
-
- inline SQRESULT newtable(HSquirrelVM* sqvm)
- {
- return __sq_newtable(sqvm);
- }
-
- inline SQRESULT newslot(HSquirrelVM* sqvm, SQInteger idx, SQBool bStatic)
- {
- return __sq_newslot(sqvm, idx, bStatic);
- }
-
- inline void pushroottable(HSquirrelVM* sqvm)
- {
- __sq_pushroottable(sqvm);
- }
-
- inline void pushstring(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1)
- {
- __sq_pushstring(sqvm, sVal, length);
- }
-
- inline void pushinteger(HSquirrelVM* sqvm, const SQInteger iVal)
- {
- __sq_pushinteger(sqvm, iVal);
- }
-
- inline void pushfloat(HSquirrelVM* sqvm, const SQFloat flVal)
- {
- __sq_pushfloat(sqvm, flVal);
- }
-
- inline void pushbool(HSquirrelVM* sqvm, const SQBool bVal)
- {
- __sq_pushbool(sqvm, bVal);
- }
-
- inline void pushasset(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1)
- {
- __sq_pushasset(sqvm, sVal, length);
- }
-
- inline void pushvector(HSquirrelVM* sqvm, const Vector3 pVal)
- {
- __sq_pushvector(sqvm, (float*)&pVal);
- }
-
- inline void pushobject(HSquirrelVM* sqvm, SQObject* obj)
- {
- __sq_pushobject(sqvm, obj);
- }
-
- inline const SQChar* getstring(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_getstring(sqvm, stackpos);
- }
-
- inline SQInteger getinteger(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_getinteger(sqvm, stackpos);
- }
-
- inline SQFloat getfloat(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_getfloat(sqvm, stackpos);
- }
-
- inline SQBool getbool(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_getbool(sqvm, stackpos);
- }
-
- inline SQRESULT get(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_get(sqvm, stackpos);
- }
-
- inline Vector3 getvector(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return *(Vector3*)__sq_getvector(sqvm, stackpos);
- }
-
- inline int sq_getfunction(HSquirrelVM* sqvm, const char* name, SQObject* returnObj, const char* signature)
- {
- return __sq_getfunction(sqvm, name, returnObj, signature);
- }
-
- inline SQRESULT getasset(HSquirrelVM* sqvm, const SQInteger stackpos, const char** result)
- {
- return __sq_getasset(sqvm, stackpos, result);
- }
-
- inline long long sq_stackinfos(HSquirrelVM* sqvm, int level, SQStackInfos& out)
- {
- return __sq_stackinfos(sqvm, level, &out, sqvm->_callstacksize);
- }
-
- inline Mod* getcallingmod(HSquirrelVM* sqvm, int depth = 0)
- {
- SQStackInfos stackInfo {};
- if (1 + depth >= sqvm->_callstacksize)
- {
- return nullptr;
- }
- sq_stackinfos(sqvm, 1 + depth, stackInfo);
- std::string sourceName = stackInfo._sourceName;
- std::replace(sourceName.begin(), sourceName.end(), '/', '\\');
- std::string filename = g_pModManager->NormaliseModFilePath(fs::path("scripts\\vscripts\\" + sourceName));
- if (auto res = g_pModManager->m_ModFiles.find(filename); res != g_pModManager->m_ModFiles.end())
- {
- return res->second.m_pOwningMod;
- }
- return nullptr;
- }
- template <typename T> inline SQRESULT getuserdata(HSquirrelVM* sqvm, const SQInteger stackpos, T* data, uint64_t* typeId)
- {
- return __sq_getuserdata(sqvm, stackpos, (void**)data, typeId); // this sometimes crashes idk
- }
-
- template <typename T> inline T* createuserdata(HSquirrelVM* sqvm, SQInteger size)
- {
- void* ret = __sq_createuserdata(sqvm, size);
- memset(ret, 0, size);
- return (T*)ret;
- }
-
- inline SQRESULT setuserdatatypeid(HSquirrelVM* sqvm, const SQInteger stackpos, uint64_t typeId)
- {
- return __sq_setuserdatatypeid(sqvm, stackpos, typeId);
- }
-
- template <typename T> inline SQBool getthisentity(HSquirrelVM* sqvm, T* ppEntity)
- {
- return __sq_getthisentity(sqvm, (void**)ppEntity);
- }
-
- template <typename T> inline T* getentity(HSquirrelVM* sqvm, SQInteger iStackPos)
- {
- SQObject obj;
- __sq_getobject(sqvm, iStackPos, &obj);
-
- // there are entity constants for other types, but seemingly CBaseEntity's is the only one needed
- return (T*)__sq_getentityfrominstance(m_pSQVM, &obj, __sq_GetEntityConstant_CBaseEntity());
- }
-
- inline SQRESULT pushnewstructinstance(HSquirrelVM* sqvm, const int fieldCount)
- {
- return __sq_pushnewstructinstance(sqvm, fieldCount);
- }
-
- inline SQRESULT sealstructslot(HSquirrelVM* sqvm, const int fieldIndex)
- {
- return __sq_sealstructslot(sqvm, fieldIndex);
- }
-#pragma endregion
-};
-
-template <ScriptContext context> class SquirrelManager : public virtual SquirrelManagerBase
-{
-public:
-#pragma region MessageBuffer
- SquirrelMessageBuffer* messageBuffer;
-
- template <typename... Args> SquirrelMessage AsyncCall(std::string funcname, Args... args)
- {
- // This function schedules a call to be executed on the next frame
- // This is useful for things like threads and plugins, which do not run on the main thread
- FunctionVector functionVector;
- SqRecurseArgs<context>(functionVector, args...);
- SquirrelMessage message = {funcname, functionVector};
- messageBuffer->push(message);
- return message;
- }
-
- SquirrelMessage AsyncCall(std::string funcname)
- {
- // This function schedules a call to be executed on the next frame
- // This is useful for things like threads and plugins, which do not run on the main thread
- FunctionVector functionVector = {};
- SquirrelMessage message = {funcname, functionVector};
- messageBuffer->push(message);
- return message;
- }
-
- SQRESULT Call(const char* funcname)
- {
- // Warning!
- // This function assumes the squirrel VM is stopped/blocked at the moment of call
- // Calling this function while the VM is running is likely to result in a crash due to stack destruction
- // If you want to call into squirrel asynchronously, use `AsyncCall` instead
-
- if (!m_pSQVM || !m_pSQVM->sqvm)
- {
- spdlog::error(
- "{} was called on context {} while VM was not initialized. This will crash", __FUNCTION__, GetContextName(context));
- }
-
- SQObject functionobj {};
- int result = sq_getfunction(m_pSQVM->sqvm, funcname, &functionobj, 0);
- if (result != 0) // This func returns 0 on success for some reason
- {
- NS::log::squirrel_logger<context>()->error("Call was unable to find function with name '{}'. Is it global?", funcname);
- return SQRESULT_ERROR;
- }
- pushobject(m_pSQVM->sqvm, &functionobj); // Push the function object
- pushroottable(m_pSQVM->sqvm); // Push root table
- return _call(m_pSQVM->sqvm, 0);
- }
-
- template <typename... Args> SQRESULT Call(const char* funcname, Args... args)
- {
- // Warning!
- // This function assumes the squirrel VM is stopped/blocked at the moment of call
- // Calling this function while the VM is running is likely to result in a crash due to stack destruction
- // If you want to call into squirrel asynchronously, use `schedule_call` instead
- if (!m_pSQVM || !m_pSQVM->sqvm)
- {
- spdlog::error(
- "{} was called on context {} while VM was not initialized. This will crash", __FUNCTION__, GetContextName(context));
- }
- SQObject functionobj {};
- int result = sq_getfunction(m_pSQVM->sqvm, funcname, &functionobj, 0);
- if (result != 0) // This func returns 0 on success for some reason
- {
- NS::log::squirrel_logger<context>()->error("Call was unable to find function with name '{}'. Is it global?", funcname);
- return SQRESULT_ERROR;
- }
- pushobject(m_pSQVM->sqvm, &functionobj); // Push the function object
- pushroottable(m_pSQVM->sqvm); // Push root table
-
- FunctionVector functionVector;
- SqRecurseArgs<context>(functionVector, args...);
-
- for (auto& v : functionVector)
- {
- // Execute lambda to push arg to stack
- v();
- }
-
- return _call(m_pSQVM->sqvm, functionVector.size());
- }
-
-#pragma endregion
-
-public:
- SquirrelManager()
- {
- m_pSQVM = nullptr;
- }
-
- void VMCreated(CSquirrelVM* newSqvm);
- void VMDestroyed();
- void ExecuteCode(const char* code);
- void AddFuncRegistration(std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func);
- SQRESULT setupfunc(const SQChar* funcname);
- void AddFuncOverride(std::string name, SQFunction func);
- void ProcessMessageBuffer();
- void GenerateSquirrelFunctionsStruct(SquirrelFunctions* s);
-};
-
-template <ScriptContext context> SquirrelManager<context>* g_pSquirrel;
-
-void InitialiseSquirrelManagers();
-
-/*
- Beware all ye who enter below.
- This place is not a place of honor... no highly esteemed deed is commemorated here... nothing valued is here.
- What is here was dangerous and repulsive to us. This message is a warning about danger.
-*/
-
-#pragma region MessageBuffer templates
-
-// Clang-formatting makes this whole thing unreadable
-// clang-format off
-
-#ifndef MessageBufferFuncs
-#define MessageBufferFuncs
-// Bools
-template <ScriptContext context, typename T>
-requires std::convertible_to<T, bool> && (!std::is_floating_point_v<T>) && (!std::convertible_to<T, std::string>) && (!std::convertible_to<T, int>)
-inline VoidFunction SQMessageBufferPushArg(T& arg) {
- return [arg]{ g_pSquirrel<context>->pushbool(g_pSquirrel<context>->m_pSQVM->sqvm, static_cast<bool>(arg)); };
-}
-// Vectors
-template <ScriptContext context>
-inline VoidFunction SQMessageBufferPushArg(Vector3& arg) {
- return [arg]{ g_pSquirrel<context>->pushvector(g_pSquirrel<context>->m_pSQVM->sqvm, arg); };
-}
-// Vectors
-template <ScriptContext context>
-inline VoidFunction SQMessageBufferPushArg(SQObject* arg) {
- return [arg]{ g_pSquirrel<context>->pushSQObject(g_pSquirrel<context>->m_pSQVM->sqvm, arg); };
-}
-// Ints
-template <ScriptContext context, typename T>
-requires std::convertible_to<T, int> && (!std::is_floating_point_v<T>)
-inline VoidFunction SQMessageBufferPushArg(T& arg) {
- return [arg]{ g_pSquirrel<context>->pushinteger(g_pSquirrel<context>->m_pSQVM->sqvm, static_cast<int>(arg)); };
-}
-// Floats
-template <ScriptContext context, typename T>
-requires std::convertible_to<T, float> && (std::is_floating_point_v<T>)
-inline VoidFunction SQMessageBufferPushArg(T& arg) {
- return [arg]{ g_pSquirrel<context>->pushfloat(g_pSquirrel<context>->m_pSQVM->sqvm, static_cast<float>(arg)); };
-}
-// Strings
-template <ScriptContext context, typename T>
-requires (std::convertible_to<T, std::string> || std::is_constructible_v<std::string, T>)
-inline VoidFunction SQMessageBufferPushArg(T& arg) {
- auto converted = std::string(arg);
- return [converted]{ g_pSquirrel<context>->pushstring(g_pSquirrel<context>->m_pSQVM->sqvm, converted.c_str(), converted.length()); };
-}
-// Assets
-template <ScriptContext context>
-inline VoidFunction SQMessageBufferPushArg(SquirrelAsset& arg) {
- return [arg]{ g_pSquirrel<context>->pushasset(g_pSquirrel<context>->m_pSQVM->sqvm, arg.path.c_str(), arg.path.length()); };
-}
-// Maps
-template <ScriptContext context, typename T>
-requires is_iterable<T>
-inline VoidFunction SQMessageBufferPushArg(T& arg) {
- FunctionVector localv = {};
- localv.push_back([]{g_pSquirrel<context>->newarray(g_pSquirrel<context>->m_pSQVM->sqvm, 0);});
-
- for (const auto& item : arg) {
- localv.push_back(SQMessageBufferPushArg<context>(item));
- localv.push_back([]{g_pSquirrel<context>->arrayappend(g_pSquirrel<context>->m_pSQVM->sqvm, -2);});
- }
-
- return [localv] { for (auto& func : localv) { func(); } };
-}
-// Vectors
-template <ScriptContext context, typename T>
-requires is_map<T>
-inline VoidFunction SQMessageBufferPushArg(T& map) {
- FunctionVector localv = {};
- localv.push_back([]{g_pSquirrel<context>->newtable(g_pSquirrel<context>->m_pSQVM->sqvm);});
-
- for (const auto& item : map) {
- localv.push_back(SQMessageBufferPushArg<context>(item.first));
- localv.push_back(SQMessageBufferPushArg<context>(item.second));
- localv.push_back([]{g_pSquirrel<context>->newslot(g_pSquirrel<context>->m_pSQVM->sqvm, -3, false);});
- }
-
- return [localv]{ for (auto& func : localv) { func(); } };
-}
-
-template <ScriptContext context, typename T>
-inline void SqRecurseArgs(FunctionVector& v, T& arg) {
- v.push_back(SQMessageBufferPushArg<context>(arg));
-}
-
-// This function is separated from the PushArg function so as to not generate too many template instances
-// This is the main function responsible for unrolling the argument pack
-template <ScriptContext context, typename T, typename... Args>
-inline void SqRecurseArgs(FunctionVector& v, T& arg, Args... args) {
- v.push_back(SQMessageBufferPushArg<context>(arg));
- SqRecurseArgs<context>(v, args...);
-}
-
-// clang-format on
-#endif
-
-#pragma endregion
diff --git a/NorthstarDLL/squirrel/squirrelautobind.cpp b/NorthstarDLL/squirrel/squirrelautobind.cpp
deleted file mode 100644
index c15240f5..00000000
--- a/NorthstarDLL/squirrel/squirrelautobind.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "squirrelautobind.h"
-
-SquirrelAutoBindContainer* g_pSqAutoBindContainer;
-
-ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrelAutoBind, ClientSquirrel, (CModule module))
-{
- spdlog::info("ClientSquirrelAutoBInd AutoBindFuncsVectorsize {}", g_pSqAutoBindContainer->clientSqAutoBindFuncs.size());
- for (auto& autoBindFunc : g_pSqAutoBindContainer->clientSqAutoBindFuncs)
- {
- autoBindFunc();
- }
-}
-
-ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrelAutoBind, ServerSquirrel, (CModule module))
-{
- for (auto& autoBindFunc : g_pSqAutoBindContainer->serverSqAutoBindFuncs)
- {
- autoBindFunc();
- }
-}
diff --git a/NorthstarDLL/squirrel/squirrelautobind.h b/NorthstarDLL/squirrel/squirrelautobind.h
deleted file mode 100644
index 0fc599f3..00000000
--- a/NorthstarDLL/squirrel/squirrelautobind.h
+++ /dev/null
@@ -1,76 +0,0 @@
-#pragma once
-#include <vector>
-
-typedef void (*SqAutoBindFunc)();
-
-class SquirrelAutoBindContainer
-{
-public:
- std::vector<std::function<void()>> clientSqAutoBindFuncs;
- std::vector<std::function<void()>> serverSqAutoBindFuncs;
-};
-
-extern SquirrelAutoBindContainer* g_pSqAutoBindContainer;
-
-class __squirrelautobind;
-
-#define ADD_SQFUNC(returnType, funcName, argTypes, helpText, runOnContext) \
- template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm); \
- namespace \
- { \
- __squirrelautobind CONCAT2(__squirrelautobind, __LINE__)( \
- []() \
- { \
- if constexpr ((runOnContext)&ScriptContext::UI) \
- g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( \
- returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::UI >); \
- if constexpr ((runOnContext)&ScriptContext::CLIENT) \
- g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( \
- returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::CLIENT >); \
- }, \
- []() \
- { \
- if constexpr ((runOnContext)&ScriptContext::SERVER) \
- g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration( \
- returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::SERVER >); \
- }); \
- } \
- template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm)
-
-#define REPLACE_SQFUNC(funcName, runOnContext) \
- template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm); \
- namespace \
- { \
- __squirrelautobind CONCAT2(__squirrelautobind, __LINE__)( \
- []() \
- { \
- if constexpr ((runOnContext)&ScriptContext::UI) \
- g_pSquirrel<ScriptContext::UI>->AddFuncOverride(__STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::UI >); \
- if constexpr ((runOnContext)&ScriptContext::CLIENT) \
- g_pSquirrel<ScriptContext::CLIENT>->AddFuncOverride( \
- __STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::CLIENT >); \
- }, \
- []() \
- { \
- if constexpr ((runOnContext)&ScriptContext::SERVER) \
- g_pSquirrel<ScriptContext::SERVER>->AddFuncOverride( \
- __STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::SERVER >); \
- }); \
- } \
- template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm)
-
-class __squirrelautobind
-{
-public:
- __squirrelautobind() = delete;
-
- __squirrelautobind(std::function<void()> clientAutoBindFunc, std::function<void()> serverAutoBindFunc)
- {
- // Bit hacky but we can't initialise this normally since this gets run automatically on load
- if (g_pSqAutoBindContainer == nullptr)
- g_pSqAutoBindContainer = new SquirrelAutoBindContainer();
-
- g_pSqAutoBindContainer->clientSqAutoBindFuncs.push_back(clientAutoBindFunc);
- g_pSqAutoBindContainer->serverSqAutoBindFuncs.push_back(serverAutoBindFunc);
- }
-};
diff --git a/NorthstarDLL/squirrel/squirrelclasstypes.h b/NorthstarDLL/squirrel/squirrelclasstypes.h
deleted file mode 100644
index cd777551..00000000
--- a/NorthstarDLL/squirrel/squirrelclasstypes.h
+++ /dev/null
@@ -1,248 +0,0 @@
-#pragma once
-#include "squirreldatatypes.h"
-
-#include <queue>
-
-enum SQRESULT : SQInteger
-{
- SQRESULT_ERROR = -1,
- SQRESULT_NULL = 0,
- SQRESULT_NOTNULL = 1,
-};
-
-typedef SQRESULT (*SQFunction)(HSquirrelVM* sqvm);
-
-enum class eSQReturnType
-{
- Float = 0x1,
- Vector = 0x3,
- Integer = 0x5,
- Boolean = 0x6,
- Entity = 0xD,
- String = 0x21,
- Default = 0x20,
- Arrays = 0x25,
- Asset = 0x28,
- Table = 0x26,
-};
-
-const std::map<SQRESULT, const char*> PrintSQRESULT = {
- {SQRESULT::SQRESULT_ERROR, "SQRESULT_ERROR"},
- {SQRESULT::SQRESULT_NULL, "SQRESULT_NULL"},
- {SQRESULT::SQRESULT_NOTNULL, "SQRESULT_NOTNULL"}};
-
-struct CompileBufferState
-{
- const SQChar* buffer;
- const SQChar* bufferPlusLength;
- const SQChar* bufferAgain;
-
- CompileBufferState(const std::string& code)
- {
- buffer = code.c_str();
- bufferPlusLength = code.c_str() + code.size();
- bufferAgain = code.c_str();
- }
-};
-
-struct SQFuncRegistration
-{
- const char* squirrelFuncName;
- const char* cppFuncName;
- const char* helpText;
- const char* returnTypeString;
- const char* argTypes;
- uint32_t unknown1;
- uint32_t devLevel;
- const char* shortNameMaybe;
- uint32_t unknown2;
- eSQReturnType returnType;
- uint32_t* externalBufferPointer;
- uint64_t externalBufferSize;
- uint64_t unknown3;
- uint64_t unknown4;
- SQFunction funcPtr;
-
- SQFuncRegistration()
- {
- memset(this, 0, sizeof(SQFuncRegistration));
- this->returnType = eSQReturnType::Default;
- }
-};
-
-enum class ScriptContext : int
-{
- INVALID = -1,
- SERVER,
- CLIENT,
- UI,
-};
-
-typedef std::vector<std::function<void()>> FunctionVector;
-typedef std::function<void()> VoidFunction;
-
-// clang-format off
-template <typename T>
-concept is_map =
- // Simple maps
- std::same_as<T, std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>> ||
- std::same_as<T, std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::hasher, typename T::key_equal, typename T::allocator_type>> ||
-
- // Nested maps
- std::same_as <
- std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>,
- std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>
- > ||
- std::same_as <
- std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>,
- std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>
- > ||
- std::same_as <
- std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>,
- std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>
- > ||
- std::same_as <
- std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>,
- std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>
- >
-;
-
-template<typename T>
-concept is_iterable = requires(std::ranges::range_value_t<T> x)
-{
- x.begin(); // must have `x.begin()`
- x.end(); // and `x.end()`
-};
-
-// clang-format on
-
-typedef int (*SquirrelMessage_External_Pop)(HSquirrelVM* sqvm, void* userdata);
-typedef void (*sq_schedule_call_externalType)(
- ScriptContext context, const char* funcname, SquirrelMessage_External_Pop function, void* userdata);
-
-class SquirrelMessage
-{
-public:
- std::string functionName;
- FunctionVector args;
- bool isExternal = false;
- void* userdata = NULL;
- SquirrelMessage_External_Pop externalFunc = NULL;
-};
-
-class SquirrelMessageBuffer
-{
-
-private:
- std::queue<SquirrelMessage> messages = {};
-
-public:
- std::mutex mutex;
- std::optional<SquirrelMessage> pop()
- {
- std::lock_guard<std::mutex> guard(mutex);
- if (!messages.empty())
- {
- auto message = messages.front();
- messages.pop();
- return message;
- }
- else
- {
- return std::nullopt;
- }
- }
-
- void unwind()
- {
- auto maybeMessage = this->pop();
- if (!maybeMessage)
- {
- spdlog::error("Plugin tried consuming SquirrelMessage while buffer was empty");
- return;
- }
- auto message = maybeMessage.value();
- for (auto& v : message.args)
- {
- // Execute lambda to push arg to stack
- v();
- }
- }
-
- void push(SquirrelMessage message)
- {
- std::lock_guard<std::mutex> guard(mutex);
- messages.push(message);
- }
-};
-
-// Super simple wrapper class to allow pushing Assets via call
-class SquirrelAsset
-{
-public:
- std::string path;
- SquirrelAsset(std::string path) : path(path) {};
-};
-
-#pragma region TypeDefs
-
-// core sqvm funcs
-typedef int64_t (*RegisterSquirrelFuncType)(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown);
-typedef void (*sq_defconstType)(CSquirrelVM* sqvm, const SQChar* name, int value);
-
-typedef SQRESULT (*sq_compilebufferType)(
- HSquirrelVM* sqvm, CompileBufferState* compileBuffer, const char* file, int a1, SQBool bShouldThrowError);
-typedef SQRESULT (*sq_callType)(HSquirrelVM* sqvm, SQInteger iArgs, SQBool bShouldReturn, SQBool bThrowError);
-typedef SQInteger (*sq_raiseerrorType)(HSquirrelVM* sqvm, const SQChar* pError);
-typedef bool (*sq_compilefileType)(CSquirrelVM* sqvm, const char* path, const char* name, int a4);
-
-// sq stack array funcs
-typedef void (*sq_newarrayType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQRESULT (*sq_arrayappendType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-
-// sq table funcs
-typedef SQRESULT (*sq_newtableType)(HSquirrelVM* sqvm);
-typedef SQRESULT (*sq_newslotType)(HSquirrelVM* sqvm, SQInteger idx, SQBool bStatic);
-
-// sq stack push funcs
-typedef void (*sq_pushroottableType)(HSquirrelVM* sqvm);
-typedef void (*sq_pushstringType)(HSquirrelVM* sqvm, const SQChar* pStr, SQInteger iLength);
-typedef void (*sq_pushintegerType)(HSquirrelVM* sqvm, SQInteger i);
-typedef void (*sq_pushfloatType)(HSquirrelVM* sqvm, SQFloat f);
-typedef void (*sq_pushboolType)(HSquirrelVM* sqvm, SQBool b);
-typedef void (*sq_pushassetType)(HSquirrelVM* sqvm, const SQChar* str, SQInteger iLength);
-typedef void (*sq_pushvectorType)(HSquirrelVM* sqvm, const SQFloat* pVec);
-typedef void (*sq_pushobjectType)(HSquirrelVM* sqvm, SQObject* pVec);
-
-// sq stack get funcs
-typedef const SQChar* (*sq_getstringType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQInteger (*sq_getintegerType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQFloat (*sq_getfloatType)(HSquirrelVM*, SQInteger iStackpos);
-typedef SQBool (*sq_getboolType)(HSquirrelVM*, SQInteger iStackpos);
-typedef SQRESULT (*sq_getType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQRESULT (*sq_getassetType)(HSquirrelVM* sqvm, SQInteger iStackpos, const char** pResult);
-typedef SQRESULT (*sq_getuserdataType)(HSquirrelVM* sqvm, SQInteger iStackpos, void** pData, uint64_t* pTypeId);
-typedef SQFloat* (*sq_getvectorType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQBool (*sq_getthisentityType)(HSquirrelVM*, void** ppEntity);
-typedef void (*sq_getobjectType)(HSquirrelVM*, SQInteger iStackPos, SQObject* pOutObj);
-
-typedef long long (*sq_stackinfosType)(HSquirrelVM* sqvm, int iLevel, SQStackInfos* pOutObj, int iCallStackSize);
-
-// sq stack userpointer funcs
-typedef void* (*sq_createuserdataType)(HSquirrelVM* sqvm, SQInteger iSize);
-typedef SQRESULT (*sq_setuserdatatypeidType)(HSquirrelVM* sqvm, SQInteger iStackpos, uint64_t iTypeId);
-
-// sq misc entity funcs
-typedef void* (*sq_getentityfrominstanceType)(CSquirrelVM* sqvm, SQObject* pInstance, char** ppEntityConstant);
-typedef char** (*sq_GetEntityConstantType)();
-
-typedef int (*sq_getfunctionType)(HSquirrelVM* sqvm, const char* name, SQObject* returnObj, const char* signature);
-
-// structs
-typedef SQRESULT (*sq_pushnewstructinstanceType)(HSquirrelVM* sqvm, int fieldCount);
-typedef SQRESULT (*sq_sealstructslotType)(HSquirrelVM* sqvm, int slotIndex);
-
-#pragma endregion
-
-// These "external" versions of the types are for plugins
-typedef int64_t (*RegisterSquirrelFuncType_External)(ScriptContext context, SQFuncRegistration* funcReg, char unknown);
diff --git a/NorthstarDLL/squirrel/squirreldatatypes.h b/NorthstarDLL/squirrel/squirreldatatypes.h
deleted file mode 100644
index 84ab15ec..00000000
--- a/NorthstarDLL/squirrel/squirreldatatypes.h
+++ /dev/null
@@ -1,501 +0,0 @@
-#pragma once
-/*
- This file has been generated by IDA.
- It contains local type definitions from
- the type library 'server.dll'
-*/
-
-struct HSquirrelVM;
-struct CallInfo;
-struct SQTable;
-struct SQString;
-struct SQFunctionProto;
-struct SQClosure;
-struct SQSharedState;
-struct StringTable;
-struct SQStructInstance;
-struct SQStructDef;
-struct SQNativeClosure;
-struct SQArray;
-struct tableNode;
-struct SQUserData;
-struct CSquirrelVM;
-
-typedef void (*releasehookType)(void* val, int size);
-
-// stolen from ttf2sdk: sqvm types
-typedef float SQFloat;
-typedef long SQInteger;
-typedef unsigned long SQUnsignedInteger;
-typedef char SQChar;
-typedef SQUnsignedInteger SQBool;
-
-/* 127 */
-enum SQObjectType : int
-{
- _RT_NULL = 0x1,
- _RT_INTEGER = 0x2,
- _RT_FLOAT = 0x4,
- _RT_BOOL = 0x8,
- _RT_STRING = 0x10,
- _RT_TABLE = 0x20,
- _RT_ARRAY = 0x40,
- _RT_USERDATA = 0x80,
- _RT_CLOSURE = 0x100,
- _RT_NATIVECLOSURE = 0x200,
- _RT_GENERATOR = 0x400,
- OT_USERPOINTER = 0x800,
- _RT_USERPOINTER = 0x800,
- _RT_THREAD = 0x1000,
- _RT_FUNCPROTO = 0x2000,
- _RT_CLASS = 0x4000,
- _RT_INSTANCE = 0x8000,
- _RT_WEAKREF = 0x10000,
- OT_VECTOR = 0x40000,
- SQOBJECT_CANBEFALSE = 0x1000000,
- OT_NULL = 0x1000001,
- OT_BOOL = 0x1000008,
- SQOBJECT_DELEGABLE = 0x2000000,
- SQOBJECT_NUMERIC = 0x4000000,
- OT_INTEGER = 0x5000002,
- OT_FLOAT = 0x5000004,
- SQOBJECT_REF_COUNTED = 0x8000000,
- OT_STRING = 0x8000010,
- OT_ARRAY = 0x8000040,
- OT_CLOSURE = 0x8000100,
- OT_NATIVECLOSURE = 0x8000200,
- OT_ASSET = 0x8000400,
- OT_THREAD = 0x8001000,
- OT_FUNCPROTO = 0x8002000,
- OT_CLAAS = 0x8004000,
- OT_STRUCT = 0x8200000,
- OT_WEAKREF = 0x8010000,
- OT_TABLE = 0xA000020,
- OT_USERDATA = 0xA000080,
- OT_INSTANCE = 0xA008000,
- OT_ENTITY = 0xA400000,
-};
-
-/* 156 */
-union SQObjectValue
-{
- SQString* asString;
- SQTable* asTable;
- SQClosure* asClosure;
- SQFunctionProto* asFuncProto;
- SQStructDef* asStructDef;
- long long as64Integer;
- SQNativeClosure* asNativeClosure;
- SQArray* asArray;
- HSquirrelVM* asThread;
- float asFloat;
- int asInteger;
- SQUserData* asUserdata;
- SQStructInstance* asStructInstance;
-};
-
-/* 160 */
-struct SQVector
-{
- SQObjectType _Type;
- float x;
- float y;
- float z;
-};
-
-/* 128 */
-struct SQObject
-{
- SQObjectType _Type;
- int structNumber;
- SQObjectValue _VAL;
-};
-
-/* 138 */
-struct alignas(8) SQString
-{
- void* vftable;
- int uiRef;
- int padding;
- SQString* _next_maybe;
- SQSharedState* sharedState;
- int length;
- unsigned char gap_24[4];
- char _hash[8];
- char _val[1];
-};
-
-/* 137 */
-struct alignas(8) SQTable
-{
- void* vftable;
- unsigned char gap_08[4];
- int uiRef;
- unsigned char gap_10[8];
- void* pointer_18;
- void* pointer_20;
- void* _sharedState;
- long long field_30;
- tableNode* _nodes;
- int _numOfNodes;
- int size;
- int field_48;
- int _usedNodes;
- unsigned char _gap_50[20];
- int field_64;
- unsigned char _gap_68[80];
-};
-
-/* 140 */
-struct alignas(8) SQClosure
-{
- void* vftable;
- unsigned char gap_08[4];
- int uiRef;
- void* pointer_10;
- void* pointer_18;
- void* pointer_20;
- void* sharedState;
- SQObject obj_30;
- SQObject _function;
- SQObject* _outervalues;
- unsigned char gap_58[8];
- unsigned char gap_60[96];
- SQObject* objectPointer_C0;
- unsigned char gap_C8[16];
-};
-
-/* 139 */
-struct alignas(8) SQFunctionProto
-{
- void* vftable;
- unsigned char gap_08[4];
- int uiRef;
- unsigned char gap_10[8];
- void* pointer_18;
- void* pointer_20;
- void* sharedState;
- void* pointer_30;
- SQObjectType _fileNameType;
- SQString* _fileName;
- SQObjectType _funcNameType;
- SQString* _funcName;
- SQObject obj_58;
- unsigned char gap_68[12];
- int _stacksize;
- unsigned char gap_78[48];
- int nParameters;
- unsigned char gap_AC[60];
- int nDefaultParams;
- unsigned char gap_EC[200];
-};
-
-/* 152 */
-struct SQStructDef
-{
- void* vtable;
- int uiRef;
- unsigned char padding_C[4];
- unsigned char unknown[24];
- SQSharedState* sharedState;
- SQObjectType _nameType;
- SQString* _name;
- unsigned char gap_38[16];
- SQObjectType _variableNamesType;
- SQTable* _variableNames;
- unsigned char gap_[32];
-};
-
-/* 157 */
-struct alignas(8) SQNativeClosure
-{
- void* vftable;
- int uiRef;
- unsigned char gap_C[4];
- long long value_10;
- long long value_18;
- long long value_20;
- SQSharedState* sharedState;
- char unknown_30;
- unsigned char padding_34[7];
- long long value_38;
- long long value_40;
- long long value_48;
- long long value_50;
- long long value_58;
- SQObjectType _nameType;
- SQString* _name;
- long long value_70;
- long long value_78;
- unsigned char justInCaseGap_80[300];
-};
-
-/* 162 */
-struct SQArray
-{
- void* vftable;
- int uiRef;
- unsigned char gap_24[36];
- SQObject* _values;
- int _usedSlots;
- int _allocated;
-};
-
-/* 129 */
-struct alignas(8) HSquirrelVM
-{
- void* vftable;
- int uiRef;
- unsigned char gap_8[12];
- void* _toString;
- void* _roottable_pointer;
- void* pointer_28;
- CallInfo* ci;
- CallInfo* _callstack;
- int _callstacksize;
- int _stackbase;
- SQObject* _stackOfCurrentFunction;
- SQSharedState* sharedState;
- void* pointer_58;
- void* pointer_60;
- int _top;
- SQObject* _stack;
- unsigned char gap_78[8];
- SQObject* _vargvstack;
- unsigned char gap_88[8];
- SQObject temp_reg;
- unsigned char gapA0[8];
- void* pointer_A8;
- unsigned char gap_B0[8];
- SQObject _roottable_object;
- SQObject _lasterror;
- SQObject _errorHandler;
- long long field_E8;
- int traps;
- unsigned char gap_F4[12];
- int _nnativecalls;
- int _suspended;
- int _suspended_root;
- int _unk;
- int _suspended_target;
- int trapAmount;
- int _suspend_varargs;
- int unknown_field_11C;
- SQObject object_120;
-};
-
-/* 150 */
-struct SQStructInstance
-{
- void* vftable;
- __int32 uiRef;
- BYTE gap_C[4];
- __int64 unknown_10;
- void* pointer_18;
- __int64 unknown_20;
- SQSharedState* _sharedState;
- unsigned int size;
- BYTE gap_34[4];
- SQObject data[1]; // This struct is dynamically sized, so this size is unknown
-};
-
-/* 148 */
-struct SQSharedState
-{
- unsigned char gap_0[72];
- void* unknown;
- unsigned char gap_50[16344];
- SQObjectType _unknownTableType00;
- long long _unknownTableValue00;
- unsigned char gap_4038[16];
- StringTable* _stringTable;
- unsigned char gap_4050[32];
- SQObjectType _unknownTableType0;
- long long _unknownTableValue0;
- SQObjectType _unknownObjectType1;
- long long _unknownObjectValue1;
- unsigned char gap_4090[8];
- SQObjectType _unknownArrayType2;
- long long _unknownArrayValue2;
- SQObjectType _gobalsArrayType;
- SQStructInstance* _globalsArray;
- unsigned char gap_40B8[16];
- SQObjectType _nativeClosuresType;
- SQTable* _nativeClosures;
- SQObjectType _typedConstantsType;
- SQTable* _typedConstants;
- SQObjectType _untypedConstantsType;
- SQTable* _untypedConstants;
- SQObjectType _globalsMaybeType;
- SQTable* _globals;
- SQObjectType _functionsType;
- SQTable* _functions;
- SQObjectType _structsType;
- SQTable* _structs;
- SQObjectType _typeDefsType;
- SQTable* _typeDefs;
- SQObjectType unknownTableType;
- SQTable* unknownTable;
- SQObjectType _squirrelFilesType;
- SQTable* _squirrelFiles;
- unsigned char gap_4158[80];
- SQObjectType _nativeClosures2Type;
- SQTable* _nativeClosures2;
- SQObjectType _entityTypesMaybeType;
- SQTable* _entityTypesMaybe;
- SQObjectType unknownTable2Type;
- SQTable* unknownTable2;
- unsigned char gap_41D8[72];
- SQObjectType _compilerKeywordsType;
- SQTable* _compilerKeywords;
- HSquirrelVM* _currentThreadMaybe;
- unsigned char gap_4238[8];
- SQObjectType unknownTable3Type;
- SQTable* unknownTable3;
- unsigned char gap_4250[16];
- SQObjectType unknownThreadType;
- SQTable* unknownThread;
- SQObjectType _tableNativeFunctionsType;
- SQTable* _tableNativeFunctions;
- SQObjectType _unknownTableType4;
- long long _unknownObjectValue4;
- SQObjectType _unknownObjectType5;
- long long _unknownObjectValue5;
- SQObjectType _unknownObjectType6;
- long long _unknownObjectValue6;
- SQObjectType _unknownObjectType7;
- long long _unknownObjectValue7;
- SQObjectType _unknownObjectType8;
- long long _unknownObjectValue8;
- SQObjectType _unknownObjectType9;
- long long _unknownObjectValue9;
- SQObjectType _unknownObjectType10;
- long long _unknownObjectValue10;
- SQObjectType _unknownObjectType11;
- long long _unknownObjectValue11;
- SQObjectType _unknownObjectType12;
- long long _unknownObjectValue12;
- SQObjectType _unknownObjectType13;
- long long _unknownObjectValue13;
- SQObjectType _unknownObjectType14;
- long long _unknownObjectValue14;
- SQObjectType _unknownObjectType15;
- long long _unknownObjectValue15;
- unsigned char gap_4340[16];
- void* printFunction;
- unsigned char gap_4358[16];
- void* logEntityFunction;
- unsigned char gap_4370[40];
- SQObjectType _waitStringType;
- SQString* _waitStringValue;
- SQObjectType _SpinOffAndWaitForStringType;
- SQString* _SpinOffAndWaitForStringValue;
- SQObjectType _SpinOffAndWaitForSoloStringType;
- SQString* _SpinOffAndWaitForSoloStringValue;
- SQObjectType _SpinOffStringType;
- SQString* _SpinOffStringValue;
- SQObjectType _SpinOffDelayedStringType;
- SQString* _SpinOffDelayedStringValue;
- CSquirrelVM* cSquirrelVM;
- bool enableDebugInfo; // functionality stripped
- unsigned char gap_43F1[23];
-};
-
-/* 165 */
-struct tableNode
-{
- SQObject val;
- SQObject key;
- tableNode* next;
-};
-
-/* 136 */
-struct alignas(8) CallInfo
-{
- long long ip;
- SQObject* _literals;
- SQObject obj10;
- SQObject closure;
- int _etraps[4];
- int _root;
- short _vargs_size;
- short _vargs_base;
- unsigned char gap[16];
-};
-
-/* 149 */
-struct StringTable
-{
- unsigned char gap_0[12];
- int _numofslots;
- unsigned char gap_10[200];
-};
-
-/* 141 */
-struct alignas(8) SQStackInfos
-{
- char* _name;
- char* _sourceName;
- int _line;
-};
-
-/* 151 */
-struct alignas(4) SQInstruction
-{
- int op;
- int arg1;
- int output;
- short arg2;
- short arg3;
-};
-
-/* 154 */
-struct SQLexer
-{
- unsigned char gap_0[112];
-};
-
-/* 153 */
-struct SQCompiler
-{
- unsigned char gap_0[4];
- int _token;
- unsigned char gap_8[8];
- SQObject object_10;
- SQLexer lexer;
- unsigned char gap_90[752];
- HSquirrelVM* sqvm;
- unsigned char gap_288[8];
-};
-
-/* 155 */
-struct CSquirrelVM
-{
- BYTE gap_0[8];
- HSquirrelVM* sqvm;
- BYTE gap_10[8];
- SQObject unknownObject_18;
- __int64 unknown_28;
- BYTE gap_30[12];
- __int32 vmContext;
- BYTE gap_40[648];
- char* (*formatString)(__int64 a1, const char* format, ...);
- BYTE gap_2D0[24];
-};
-
-struct SQUserData
-{
- void* vftable;
- int uiRef;
- char gap_12[4];
- long long unknown_10;
- long long unknown_18;
- long long unknown_20;
- long long sharedState;
- long long unknown_30;
- int size;
- char padding1[4];
- releasehookType releaseHook;
- long long typeId;
- char data[1];
-};
diff --git a/NorthstarDLL/util/printcommands.cpp b/NorthstarDLL/util/printcommands.cpp
deleted file mode 100644
index 34d56666..00000000
--- a/NorthstarDLL/util/printcommands.cpp
+++ /dev/null
@@ -1,285 +0,0 @@
-#include "printcommands.h"
-#include "core/convar/cvar.h"
-#include "core/convar/convar.h"
-#include "core/convar/concommand.h"
-
-void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name)
-{
- if (!command)
- {
- spdlog::info("unknown command {}", name);
- return;
- }
-
- // temp because command->IsCommand does not currently work
- ConVar* cvar = g_pCVar->FindVar(command->m_pszName);
-
- // build string for flags if not FCVAR_NONE
- std::string flagString;
- if (command->GetFlags() != FCVAR_NONE)
- {
- flagString = "( ";
-
- for (auto& flagPair : g_PrintCommandFlags)
- {
- if (command->GetFlags() & flagPair.first)
- {
- // special case, slightly hacky: PRINTABLEONLY is for commands, GAMEDLL_FOR_REMOTE_CLIENTS is for concommands, both have the
- // same value
- if (flagPair.first == FCVAR_PRINTABLEONLY)
- {
- if (cvar && !strcmp(flagPair.second, "GAMEDLL_FOR_REMOTE_CLIENTS"))
- continue;
-
- if (!cvar && !strcmp(flagPair.second, "PRINTABLEONLY"))
- continue;
- }
-
- flagString += flagPair.second;
- flagString += " ";
- }
- }
-
- flagString += ") ";
- }
-
- if (cvar)
- spdlog::info("\"{}\" = \"{}\" {}- {}", cvar->GetBaseName(), cvar->GetString(), flagString, cvar->GetHelpText());
- else
- spdlog::info("\"{}\" {} - {}", command->m_pszName, flagString, command->GetHelpText());
-}
-
-void TryPrintCvarHelpForCommand(const char* pCommand)
-{
- // try to display help text for an inputted command string from the console
- int pCommandLen = strlen(pCommand);
- char* pCvarStr = new char[pCommandLen];
- strcpy(pCvarStr, pCommand);
-
- // trim whitespace from right
- for (int i = pCommandLen - 1; i; i--)
- {
- if (isspace(pCvarStr[i]))
- pCvarStr[i] = '\0';
- else
- break;
- }
-
- // check if we're inputting a cvar, but not setting it at all
- ConVar* cvar = g_pCVar->FindVar(pCvarStr);
- if (cvar)
- PrintCommandHelpDialogue(&cvar->m_ConCommandBase, pCvarStr);
-
- delete[] pCvarStr;
-}
-
-void ConCommand_help(const CCommand& arg)
-{
- if (arg.ArgC() < 2)
- {
- spdlog::info("Usage: help <cvarname>");
- return;
- }
-
- PrintCommandHelpDialogue(g_pCVar->FindCommandBase(arg.Arg(1)), arg.Arg(1));
-}
-
-void ConCommand_find(const CCommand& arg)
-{
- if (arg.ArgC() < 2)
- {
- spdlog::info("Usage: find <string> [<string>...]");
- return;
- }
-
- char pTempName[256];
- char pTempSearchTerm[256];
-
- ConCommandBase* var;
- CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator();
- std::map<std::string, ConCommandBase*> sorted;
- for (itint->SetFirst(); itint->IsValid(); itint->Next())
- {
- var = itint->Get();
- if (!var->IsFlagSet(FCVAR_DEVELOPMENTONLY) && !var->IsFlagSet(FCVAR_HIDDEN))
- {
- sorted.insert({var->m_pszName, var});
- }
- }
- delete itint;
-
- for (auto& map : sorted)
- {
- bool bPrintCommand = true;
- for (int i = 0; i < arg.ArgC() - 1; i++)
- {
- // make lowercase to avoid case sensitivity
- strncpy_s(pTempName, sizeof(pTempName), map.second->m_pszName, sizeof(pTempName) - 1);
- strncpy_s(pTempSearchTerm, sizeof(pTempSearchTerm), arg.Arg(i + 1), sizeof(pTempSearchTerm) - 1);
-
- for (int i = 0; pTempName[i]; i++)
- pTempName[i] = tolower(pTempName[i]);
-
- for (int i = 0; pTempSearchTerm[i]; i++)
- pTempSearchTerm[i] = tolower(pTempSearchTerm[i]);
-
- if (!strstr(pTempName, pTempSearchTerm))
- {
- bPrintCommand = false;
- break;
- }
- }
-
- if (bPrintCommand)
- PrintCommandHelpDialogue(map.second, map.second->m_pszName);
- }
-}
-
-void ConCommand_findflags(const CCommand& arg)
-{
- if (arg.ArgC() < 2)
- {
- spdlog::info("Usage: findflags <string>");
- for (auto& flagPair : g_PrintCommandFlags)
- spdlog::info(" - {}", flagPair.second);
-
- return;
- }
-
- // convert input flag to uppercase
- char* upperFlag = new char[strlen(arg.Arg(1))];
- strcpy(upperFlag, arg.Arg(1));
-
- for (int i = 0; upperFlag[i]; i++)
- upperFlag[i] = toupper(upperFlag[i]);
-
- // resolve flag name => int flags
- int resolvedFlag = FCVAR_NONE;
- for (auto& flagPair : g_PrintCommandFlags)
- {
- if (!strcmp(flagPair.second, upperFlag))
- {
- resolvedFlag |= flagPair.first;
- break;
- }
- }
-
- ConCommandBase* var;
- CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator();
- std::map<std::string, ConCommandBase*> sorted;
- for (itint->SetFirst(); itint->IsValid(); itint->Next())
- {
- var = itint->Get();
- if (!var->IsFlagSet(FCVAR_DEVELOPMENTONLY) && !var->IsFlagSet(FCVAR_HIDDEN))
- {
- sorted.insert({var->m_pszName, var});
- }
- }
- delete itint;
-
- for (auto& map : sorted)
- {
- if (map.second->m_nFlags & resolvedFlag)
- PrintCommandHelpDialogue(map.second, map.second->m_pszName);
- }
-
- delete[] upperFlag;
-}
-
-void ConCommand_list(const CCommand& arg)
-{
- ConCommandBase* var;
- CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator();
- std::map<std::string, ConCommandBase*> sorted;
- for (itint->SetFirst(); itint->IsValid(); itint->Next())
- {
- var = itint->Get();
- if (!var->IsFlagSet(FCVAR_DEVELOPMENTONLY) && !var->IsFlagSet(FCVAR_HIDDEN))
- {
- sorted.insert({var->m_pszName, var});
- }
- }
- delete itint;
-
- for (auto& map : sorted)
- {
- PrintCommandHelpDialogue(map.second, map.second->m_pszName);
- }
- spdlog::info("{} total convars/concommands", sorted.size());
-}
-
-void ConCommand_differences(const CCommand& arg)
-{
- CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator();
- std::map<std::string, ConCommandBase*> sorted;
-
- for (itint->SetFirst(); itint->IsValid(); itint->Next())
- {
- ConCommandBase* var = itint->Get();
- if (!var->IsFlagSet(FCVAR_DEVELOPMENTONLY) && !var->IsFlagSet(FCVAR_HIDDEN))
- {
- sorted.insert({var->m_pszName, var});
- }
- }
- delete itint;
-
- for (auto& map : sorted)
- {
- ConVar* cvar = g_pCVar->FindVar(map.second->m_pszName);
-
- if (!cvar)
- {
- continue;
- }
-
- if (strcmp(cvar->GetString(), "FCVAR_NEVER_AS_STRING") == NULL)
- {
- continue;
- }
-
- if (strcmp(cvar->GetString(), cvar->m_pszDefaultValue) == NULL)
- {
- continue;
- }
-
- std::string formatted =
- fmt::format("\"{}\" = \"{}\" ( def. \"{}\" )", cvar->GetBaseName(), cvar->GetString(), cvar->m_pszDefaultValue);
-
- if (cvar->m_bHasMin)
- {
- formatted.append(fmt::format(" min. {}", cvar->m_fMinVal));
- }
-
- if (cvar->m_bHasMax)
- {
- formatted.append(fmt::format(" max. {}", cvar->m_fMaxVal));
- }
-
- formatted.append(fmt::format(" - {}", cvar->GetHelpText()));
- spdlog::info(formatted);
- }
-}
-
-void InitialiseCommandPrint()
-{
- RegisterConCommand(
- "convar_find", ConCommand_find, "Find convars/concommands with the specified string in their name/help text.", FCVAR_NONE);
-
- // these commands already exist, so we need to modify the preexisting command to use our func instead
- // and clear the flags also
- ConCommand* helpCommand = g_pCVar->FindCommand("help");
- helpCommand->m_nFlags = FCVAR_NONE;
- helpCommand->m_pCommandCallback = ConCommand_help;
-
- ConCommand* findCommand = g_pCVar->FindCommand("convar_findByFlags");
- findCommand->m_nFlags = FCVAR_NONE;
- findCommand->m_pCommandCallback = ConCommand_findflags;
-
- ConCommand* listCommand = g_pCVar->FindCommand("convar_list");
- listCommand->m_nFlags = FCVAR_NONE;
- listCommand->m_pCommandCallback = ConCommand_list;
-
- ConCommand* diffCommand = g_pCVar->FindCommand("convar_differences");
- diffCommand->m_nFlags = FCVAR_NONE;
- diffCommand->m_pCommandCallback = ConCommand_differences;
-}
diff --git a/NorthstarDLL/util/printcommands.h b/NorthstarDLL/util/printcommands.h
deleted file mode 100644
index cb72e5cc..00000000
--- a/NorthstarDLL/util/printcommands.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-#include "core/convar/concommand.h"
-
-void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name);
-void TryPrintCvarHelpForCommand(const char* pCommand);
-void InitialiseCommandPrint();
diff --git a/NorthstarDLL/util/printmaps.cpp b/NorthstarDLL/util/printmaps.cpp
deleted file mode 100644
index d3253605..00000000
--- a/NorthstarDLL/util/printmaps.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-#include "printmaps.h"
-#include "core/convar/convar.h"
-#include "core/convar/concommand.h"
-#include "mods/modmanager.h"
-#include "core/tier0.h"
-#include "engine/r2engine.h"
-#include "squirrel/squirrel.h"
-
-#include <filesystem>
-#include <regex>
-
-AUTOHOOK_INIT()
-
-enum class MapSource_t
-{
- VPK,
- GAMEDIR,
- MOD
-};
-
-const std::unordered_map<MapSource_t, const char*> PrintMapSource = {
- {MapSource_t::VPK, "VPK"}, {MapSource_t::MOD, "MOD"}, {MapSource_t::GAMEDIR, "R2"}};
-
-struct MapVPKInfo
-{
- std::string name;
- std::string parent;
- MapSource_t source;
-};
-
-// our current list of maps in the game
-std::vector<MapVPKInfo> vMapList;
-
-typedef void (*Host_Map_helperType)(const CCommand&, void*);
-typedef void (*Host_Changelevel_fType)(const CCommand&);
-
-Host_Map_helperType Host_Map_helper;
-Host_Changelevel_fType Host_Changelevel_f;
-
-void RefreshMapList()
-{
- // Only update the maps list every 10 seconds max to we avoid constantly reading fs
- static double fLastRefresh = -999;
-
- if (fLastRefresh + 10.0 > g_pGlobals->m_flRealTime)
- return;
-
- fLastRefresh = g_pGlobals->m_flRealTime;
-
- // Rebuild map list
- vMapList.clear();
-
- // get modded maps
- // TODO: could probably check mod vpks to get mapnames from there too?
- for (auto& modFilePair : g_pModManager->m_ModFiles)
- {
- ModOverrideFile file = modFilePair.second;
- if (file.m_Path.extension() == ".bsp" && file.m_Path.parent_path().string() == "maps") // only allow mod maps actually in /maps atm
- {
- MapVPKInfo& map = vMapList.emplace_back();
- map.name = file.m_Path.stem().string();
- map.parent = file.m_pOwningMod->Name;
- map.source = MapSource_t::MOD;
- }
- }
-
- // get maps in vpk
- {
- const int iNumRetailNonMapVpks = 1;
- static const char* const ppRetailNonMapVpks[] = {
- "englishclient_frontend.bsp.pak000_dir.vpk"}; // don't include mp_common here as it contains mp_lobby
-
- // matches directory vpks, and captures their map name in the first group
- static const std::regex rVpkMapRegex("englishclient_([a-zA-Z0-9_]+)\\.bsp\\.pak000_dir\\.vpk", std::regex::icase);
-
- for (fs::directory_entry file : fs::directory_iterator("./vpk"))
- {
- std::string pathString = file.path().filename().string();
-
- bool bIsValidMapVpk = true;
- for (int i = 0; i < iNumRetailNonMapVpks; i++)
- {
- if (!pathString.compare(ppRetailNonMapVpks[i]))
- {
- bIsValidMapVpk = false;
- break;
- }
- }
-
- if (!bIsValidMapVpk)
- continue;
-
- // run our map vpk regex on the filename
- std::smatch match;
- std::regex_match(pathString, match, rVpkMapRegex);
-
- if (match.length() < 2)
- continue;
-
- std::string mapName = match[1].str();
- // special case: englishclient_mp_common contains mp_lobby, so hardcode the name here
- if (mapName == "mp_common")
- mapName = "mp_lobby";
-
- MapVPKInfo& map = vMapList.emplace_back();
- map.name = mapName;
- map.parent = pathString;
- map.source = MapSource_t::VPK;
- }
- }
-
- // get maps in game dir
- std::string gameDir = fmt::format("{}/maps", g_pModName);
- if (!std::filesystem::exists(gameDir))
- {
- return;
- }
-
- for (fs::directory_entry file : fs::directory_iterator(gameDir))
- {
- if (file.path().extension() == ".bsp")
- {
- MapVPKInfo& map = vMapList.emplace_back();
- map.name = file.path().stem().string();
- map.parent = "R2";
- map.source = MapSource_t::GAMEDIR;
- }
- }
-}
-
-// clang-format off
-AUTOHOOK(_Host_Map_f_CompletionFunc, engine.dll + 0x161AE0,
-int, __fastcall, (const char *const cmdname, const char *const partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]))
-// clang-format on
-{
- RefreshMapList();
-
- // use a custom autocomplete func for all map loading commands
- const int cmdLength = strlen(cmdname);
- const char* query = partial + cmdLength;
- const int queryLength = strlen(query);
-
- int numMaps = 0;
- for (int i = 0; i < vMapList.size() && numMaps < COMMAND_COMPLETION_MAXITEMS; i++)
- {
- if (!strncmp(query, vMapList[i].name.c_str(), queryLength))
- {
- strcpy(commands[numMaps], cmdname);
- strncpy_s(
- commands[numMaps++] + cmdLength,
- COMMAND_COMPLETION_ITEM_LENGTH,
- &vMapList[i].name[0],
- COMMAND_COMPLETION_ITEM_LENGTH - cmdLength);
- }
- }
-
- return numMaps;
-}
-
-ADD_SQFUNC(
- "array<string>",
- NSGetLoadedMapNames,
- "",
- "Returns a string array of loaded map file names",
- ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
-{
- // Maybe we should call this on mods reload instead
- RefreshMapList();
-
- g_pSquirrel<context>->newarray(sqvm, 0);
-
- for (MapVPKInfo& map : vMapList)
- {
- g_pSquirrel<context>->pushstring(sqvm, map.name.c_str());
- g_pSquirrel<context>->arrayappend(sqvm, -2);
- }
-
- return SQRESULT_NOTNULL;
-}
-
-void ConCommand_maps(const CCommand& args)
-{
- if (args.ArgC() < 2)
- {
- spdlog::info("Usage: maps <substring>");
- spdlog::info("maps * for full listing");
- return;
- }
-
- RefreshMapList();
-
- for (MapVPKInfo& map : vMapList) // need to figure out a nice way to include parent path without making the formatting awful
- if ((*args.Arg(1) == '*' && !args.Arg(1)[1]) || strstr(map.name.c_str(), args.Arg(1)))
- spdlog::info("({}) {}", PrintMapSource.at(map.source), map.name);
-}
-
-// clang-format off
-AUTOHOOK(Host_Map_f, engine.dll + 0x15B340, void, __fastcall, (const CCommand& args))
-// clang-format on
-{
- RefreshMapList();
-
- if (args.ArgC() > 1 &&
- std::find_if(vMapList.begin(), vMapList.end(), [&](MapVPKInfo map) -> bool { return map.name == args.Arg(1); }) == vMapList.end())
- {
- spdlog::warn("Map load failed: {} not found or invalid", args.Arg(1));
- return;
- }
- else if (args.ArgC() == 1)
- {
- spdlog::warn("Map load failed: no map name provided");
- return;
- }
-
- if (*g_pServerState >= server_state_t::ss_active)
- return Host_Changelevel_f(args);
- else
- return Host_Map_helper(args, nullptr);
-}
-
-void InitialiseMapsPrint()
-{
- AUTOHOOK_DISPATCH()
-
- ConCommand* mapsCommand = g_pCVar->FindCommand("maps");
- mapsCommand->m_pCommandCallback = ConCommand_maps;
-}
-
-ON_DLL_LOAD("engine.dll", Host_Map_f, (CModule module))
-{
- Host_Map_helper = module.Offset(0x15AEF0).RCast<Host_Map_helperType>();
- Host_Changelevel_f = module.Offset(0x15AAD0).RCast<Host_Changelevel_fType>();
-}
diff --git a/NorthstarDLL/util/printmaps.h b/NorthstarDLL/util/printmaps.h
deleted file mode 100644
index b01761c0..00000000
--- a/NorthstarDLL/util/printmaps.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#pragma once
-void InitialiseMapsPrint();
diff --git a/NorthstarDLL/util/utils.cpp b/NorthstarDLL/util/utils.cpp
deleted file mode 100644
index c3f90cfa..00000000
--- a/NorthstarDLL/util/utils.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include <ctype.h>
-#include "utils.h"
-
-bool skip_valid_ansi_csi_sgr(char*& str)
-{
- if (*str++ != '\x1B')
- return false;
- if (*str++ != '[') // CSI
- return false;
- for (char* c = str; *c; c++)
- {
- if (*c >= '0' && *c <= '9')
- continue;
- if (*c == ';' || *c == ':')
- continue;
- if (*c == 'm') // SGR
- break;
- return false;
- }
- return true;
-}
-
-void RemoveAsciiControlSequences(char* str, bool allow_color_codes)
-{
- for (char *pc = str, c = *pc; c = *pc; pc++)
- {
- // skip UTF-8 characters
- int bytesToSkip = 0;
- if ((c & 0xE0) == 0xC0)
- bytesToSkip = 1; // skip 2-byte UTF-8 sequence
- else if ((c & 0xF0) == 0xE0)
- bytesToSkip = 2; // skip 3-byte UTF-8 sequence
- else if ((c & 0xF8) == 0xF0)
- bytesToSkip = 3; // skip 4-byte UTF-8 sequence
- else if ((c & 0xFC) == 0xF8)
- bytesToSkip = 4; // skip 5-byte UTF-8 sequence
- else if ((c & 0xFE) == 0xFC)
- bytesToSkip = 5; // skip 6-byte UTF-8 sequence
-
- bool invalid = false;
- char* orgpc = pc;
- for (int i = 0; i < bytesToSkip; i++)
- {
- char next = pc[1];
-
- // valid UTF-8 part
- if ((next & 0xC0) == 0x80)
- {
- pc++;
- continue;
- }
-
- // invalid UTF-8 part or encountered \0
- invalid = true;
- break;
- }
- if (invalid)
- {
- // erase the whole "UTF-8" sequence
- for (char* x = orgpc; x <= pc; x++)
- if (*x != '\0')
- *x = ' ';
- else
- break;
- }
- if (bytesToSkip > 0)
- continue; // this byte was already handled as UTF-8
-
- // an invalid control character or an UTF-8 part outside of UTF-8 sequence
- if ((iscntrl(c) && c != '\n' && c != '\r' && c != '\x1B') || (c & 0x80) != 0)
- {
- *pc = ' ';
- continue;
- }
-
- if (c == '\x1B') // separate handling for this escape sequence...
- if (allow_color_codes && skip_valid_ansi_csi_sgr(pc)) // ...which we allow for color codes...
- pc--;
- else // ...but remove it otherwise
- *pc = ' ';
- }
-}
diff --git a/NorthstarDLL/util/utils.h b/NorthstarDLL/util/utils.h
deleted file mode 100644
index 85922692..00000000
--- a/NorthstarDLL/util/utils.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-void RemoveAsciiControlSequences(char* str, bool allow_color_codes);
diff --git a/NorthstarDLL/util/version.cpp b/NorthstarDLL/util/version.cpp
deleted file mode 100644
index a947cde1..00000000
--- a/NorthstarDLL/util/version.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-#include "util/version.h"
-#include "ns_version.h"
-#include "dedicated/dedicated.h"
-
-char version[16];
-char NSUserAgent[256];
-
-void InitialiseVersion()
-{
- constexpr int northstar_version[4] {NORTHSTAR_VERSION};
- int ua_len = 0;
-
- // We actually use the rightmost integer do determine whether or not we're a debug/dev build
- // If it is set to a non-zero value, we are a dev build
- // On github CI, we set this to a 0 automatically as we replace the 0,0,0,1 with the real version number
- if (northstar_version[3])
- {
- sprintf(version, "%d.%d.%d.%d+dev", northstar_version[0], northstar_version[1], northstar_version[2], northstar_version[3]);
- ua_len += snprintf(
- NSUserAgent + ua_len,
- sizeof(NSUserAgent) - ua_len,
- "R2Northstar/%d.%d.%d+dev",
- northstar_version[0],
- northstar_version[1],
- northstar_version[2]);
- }
- else
- {
- sprintf(version, "%d.%d.%d.%d", northstar_version[0], northstar_version[1], northstar_version[2], northstar_version[3]);
- ua_len += snprintf(
- NSUserAgent + ua_len,
- sizeof(NSUserAgent) - ua_len,
- "R2Northstar/%d.%d.%d",
- northstar_version[0],
- northstar_version[1],
- northstar_version[2]);
- }
-
- if (IsDedicatedServer())
- ua_len += snprintf(NSUserAgent + ua_len, sizeof(NSUserAgent) - ua_len, " (Dedicated)");
-
- // Add the host platform info to the user agent.
- //
- // note: ntdll will always be loaded
- HMODULE ntdll = GetModuleHandleA("ntdll");
- if (ntdll)
- {
- // real win32 version info (i.e., ignore manifest)
- DWORD(WINAPI * RtlGetVersion)(LPOSVERSIONINFOEXW);
- *(FARPROC*)(&RtlGetVersion) = GetProcAddress(ntdll, "RtlGetVersion");
-
- // wine version (7.0-rc1, 7.0, 7.11, etc)
- const char*(CDECL * wine_get_version)(void);
- *(FARPROC*)(&wine_get_version) = GetProcAddress(ntdll, "wine_get_version");
-
- // human-readable build string (e.g., "wine-7.22 (Staging)")
- const char*(CDECL * wine_get_build_id)(void);
- *(FARPROC*)(&wine_get_build_id) = GetProcAddress(ntdll, "wine_get_build_id");
-
- // uname sysname (Darwin, Linux, etc) and release (kernel version string)
- void(CDECL * wine_get_host_version)(const char** sysname, const char** release);
- *(FARPROC*)(&wine_get_host_version) = GetProcAddress(ntdll, "wine_get_host_version");
-
- OSVERSIONINFOEXW osvi = {};
- const char *wine_version = NULL, *wine_build_id = NULL, *wine_sysname = NULL, *wine_release = NULL;
- if (RtlGetVersion)
- RtlGetVersion(&osvi);
- if (wine_get_version)
- wine_version = wine_get_version();
- if (wine_get_build_id)
- wine_build_id = wine_get_build_id();
- if (wine_get_host_version)
- wine_get_host_version(&wine_sysname, &wine_release);
-
- // windows version
- if (osvi.dwMajorVersion)
- ua_len += snprintf(
- NSUserAgent + ua_len,
- sizeof(NSUserAgent) - ua_len,
- " Windows/%d.%d.%d",
- osvi.dwMajorVersion,
- osvi.dwMinorVersion,
- osvi.dwBuildNumber);
-
- // wine version
- if (wine_version && wine_build_id)
- ua_len += snprintf(NSUserAgent + ua_len, sizeof(NSUserAgent) - ua_len, " Wine/%s (%s)", wine_version, wine_build_id);
-
- // wine host system version
- if (wine_sysname && wine_release)
- ua_len += snprintf(NSUserAgent + ua_len, sizeof(NSUserAgent) - ua_len, " %s/%s", wine_sysname, wine_release);
- }
-
- return;
-}
diff --git a/NorthstarDLL/util/version.h b/NorthstarDLL/util/version.h
deleted file mode 100644
index a3dcf8c7..00000000
--- a/NorthstarDLL/util/version.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-extern char version[16];
-extern char NSUserAgent[256];
-
-void InitialiseVersion();
diff --git a/NorthstarDLL/util/wininfo.cpp b/NorthstarDLL/util/wininfo.cpp
deleted file mode 100644
index 4fd64369..00000000
--- a/NorthstarDLL/util/wininfo.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-AUTOHOOK_INIT()
-
-HWND* g_gameHWND;
-HMODULE g_NorthstarModule = 0;
-
-ON_DLL_LOAD("engine.dll", WinInfo, (CModule module))
-{
- g_gameHWND = module.Offset(0x7d88a0).RCast<HWND*>();
-}
diff --git a/NorthstarDLL/util/wininfo.h b/NorthstarDLL/util/wininfo.h
deleted file mode 100644
index c56f7b87..00000000
--- a/NorthstarDLL/util/wininfo.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-extern HWND* g_gameHWND;
-extern HMODULE g_NorthstarModule;