aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL/engine
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDLL/engine')
-rw-r--r--NorthstarDLL/engine/host.cpp31
-rw-r--r--NorthstarDLL/engine/hoststate.cpp124
-rw-r--r--NorthstarDLL/engine/hoststate.h45
-rw-r--r--NorthstarDLL/engine/r2engine.cpp36
-rw-r--r--NorthstarDLL/engine/r2engine.h185
-rw-r--r--NorthstarDLL/engine/runframe.cpp18
6 files changed, 439 insertions, 0 deletions
diff --git a/NorthstarDLL/engine/host.cpp b/NorthstarDLL/engine/host.cpp
new file mode 100644
index 00000000..a55c8dfd
--- /dev/null
+++ b/NorthstarDLL/engine/host.cpp
@@ -0,0 +1,31 @@
+#include "pch.h"
+#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)
+ R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", R2::cmd_source_t::kCommandSrcCode);
+ else
+ R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_client", R2::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
new file mode 100644
index 00000000..f2318c78
--- /dev/null
+++ b/NorthstarDLL/engine/hoststate.cpp
@@ -0,0 +1,124 @@
+#include "pch.h"
+#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"
+
+AUTOHOOK_INIT()
+
+using namespace R2;
+
+// use the R2 namespace for game funcs
+namespace R2
+{
+ CHostState* g_pHostState;
+} // namespace R2
+
+ConVar* Cvar_hostport;
+
+void ServerStartingOrChangingMap()
+{
+ // 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);
+}
+
+// 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)
+ SetCurrentPlaylist("tdm");
+
+ ServerStartingOrChangingMap();
+
+ double dStartTime = Tier0::Plat_FloatTime();
+ CHostState__State_NewGame(self);
+ spdlog::info("loading took {}s", Tier0::Plat_FloatTime() - dStartTime);
+
+ // setup server presence
+ g_pServerPresence->CreatePresence();
+ g_pServerPresence->SetMap(g_pHostState->m_levelName, true);
+ g_pServerPresence->SetPlaylist(GetCurrentPlaylistName());
+ g_pServerPresence->SetPort(Cvar_hostport->GetInt());
+
+ g_pServerAuthentication->StartPlayerAuthServer();
+ 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 = Tier0::Plat_FloatTime();
+ CHostState__State_ChangeLevelMP(self);
+ spdlog::info("loading took {}s", Tier0::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();
+ g_pServerAuthentication->StopPlayerAuthServer();
+
+ CHostState__State_GameShutdown(self);
+}
+
+// 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 (*R2::g_pServerState == R2::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();
+}
+
+ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (CModule module))
+{
+ AUTOHOOK_DISPATCH()
+
+ g_pHostState = module.Offset(0x7CF180).As<CHostState*>();
+ Cvar_hostport = module.Offset(0x13FA6070).As<ConVar*>();
+}
diff --git a/NorthstarDLL/engine/hoststate.h b/NorthstarDLL/engine/hoststate.h
new file mode 100644
index 00000000..a77385ef
--- /dev/null
+++ b/NorthstarDLL/engine/hoststate.h
@@ -0,0 +1,45 @@
+#pragma once
+
+// use the R2 namespace for game funxcs
+namespace R2
+{
+ 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;
+} // namespace R2
diff --git a/NorthstarDLL/engine/r2engine.cpp b/NorthstarDLL/engine/r2engine.cpp
new file mode 100644
index 00000000..11233a2d
--- /dev/null
+++ b/NorthstarDLL/engine/r2engine.cpp
@@ -0,0 +1,36 @@
+#include "pch.h"
+#include "r2engine.h"
+
+using namespace R2;
+
+// use the R2 namespace for game funcs
+namespace R2
+{
+ Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer;
+ Cbuf_AddTextType Cbuf_AddText;
+ Cbuf_ExecuteType Cbuf_Execute;
+
+ 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
+} // namespace R2
+
+ON_DLL_LOAD("engine.dll", R2Engine, (CModule module))
+{
+ Cbuf_GetCurrentPlayer = module.Offset(0x120630).As<Cbuf_GetCurrentPlayerType>();
+ Cbuf_AddText = module.Offset(0x1203B0).As<Cbuf_AddTextType>();
+ Cbuf_Execute = module.Offset(0x1204B0).As<Cbuf_ExecuteType>();
+
+ g_pEngine = module.Offset(0x7D70C8).Deref().As<CEngine*>();
+
+ CBaseClient__Disconnect = module.Offset(0x1012C0).As<void (*)(void*, uint32_t, const char*, ...)>();
+ g_pClientArray = module.Offset(0x12A53F90).As<CBaseClient*>();
+
+ g_pServerState = module.Offset(0x12A53D48).As<server_state_t*>();
+}
diff --git a/NorthstarDLL/engine/r2engine.h b/NorthstarDLL/engine/r2engine.h
new file mode 100644
index 00000000..68b762e1
--- /dev/null
+++ b/NorthstarDLL/engine/r2engine.h
@@ -0,0 +1,185 @@
+#pragma once
+#include "shared/keyvalues.h"
+
+// use the R2 namespace for game funcs
+namespace R2
+{
+ // 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;
+
+ // 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() {} // unsure if this is where
+ virtual bool Load(bool dedicated, const char* baseDir) {}
+ virtual void Unload() {}
+ virtual void SetNextState(EngineState_t iNextState) {}
+ virtual EngineState_t GetState() {}
+ virtual void Frame() {}
+ virtual double GetFrameTime() {}
+ virtual float GetCurTime() {}
+
+ 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
+ };
+
+ // clang-format off
+ OFFSET_STRUCT(CBaseClient)
+ {
+ STRUCT_SIZE(0x2D728)
+ FIELD(0x16, char m_Name[64])
+ FIELD(0x258, KeyValues* m_ConVars)
+ 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;
+} // namespace R2
diff --git a/NorthstarDLL/engine/runframe.cpp b/NorthstarDLL/engine/runframe.cpp
new file mode 100644
index 00000000..d81356f2
--- /dev/null
+++ b/NorthstarDLL/engine/runframe.cpp
@@ -0,0 +1,18 @@
+#include "pch.h"
+#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, (R2::CEngine* self))
+// clang-format on
+{
+ CEngine__Frame(self);
+}
+ON_DLL_LOAD("engine.dll", RunFrame, (CModule module))
+{
+ AUTOHOOK_DISPATCH()
+}