diff options
Diffstat (limited to 'NorthstarDLL/engine')
-rw-r--r-- | NorthstarDLL/engine/host.cpp | 31 | ||||
-rw-r--r-- | NorthstarDLL/engine/hoststate.cpp | 124 | ||||
-rw-r--r-- | NorthstarDLL/engine/hoststate.h | 45 | ||||
-rw-r--r-- | NorthstarDLL/engine/r2engine.cpp | 36 | ||||
-rw-r--r-- | NorthstarDLL/engine/r2engine.h | 185 | ||||
-rw-r--r-- | NorthstarDLL/engine/runframe.cpp | 18 |
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() +} |