diff options
160 files changed, 10214 insertions, 9035 deletions
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj index 19444e8d..ac1b3991 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj +++ b/NorthstarDLL/NorthstarDLL.vcxproj @@ -14,9 +14,9 @@ <VCProjectVersion>16.0</VCProjectVersion> <Keyword>Win32Proj</Keyword> <ProjectGuid>{cfad2623-064f-453c-8196-79ee10292e32}</ProjectGuid> - <RootNamespace>NorthstarDLL</RootNamespace> + <RootNamespace>NorthstarDedicatedTest</RootNamespace> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> - <ProjectName>Northstar</ProjectName> + <ProjectName>NorthstarDLL</ProjectName> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> @@ -47,9 +47,11 @@ <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> + <TargetName>Northstar</TargetName> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> + <TargetName>Northstar</TargetName> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -75,6 +77,9 @@ <Command> </Command> </PreBuildEvent> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(SolutionDir)..\..\"</Command> + </PostBuildEvent> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> @@ -105,38 +110,39 @@ <Command> </Command> </PreBuildEvent> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(SolutionDir)..\..\"</Command> + </PostBuildEvent> </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="audio.h" /> <ClInclude Include="bansystem.h" /> <ClInclude Include="bitbuf.h" /> <ClInclude Include="bits.h" /> - <ClInclude Include="buildainfile.h" /> - <ClInclude Include="chatcommand.h" /> - <ClInclude Include="clientchathooks.h" /> - <ClInclude Include="debugoverlay.h" /> - <ClInclude Include="clientruihooks.h" /> - <ClInclude Include="clientvideooverrides.h" /> + <ClInclude Include="crashhandler.h" /> + <ClInclude Include="squirreldatatypes.h" /> + <ClInclude Include="limits.h" /> + <ClInclude Include="maxplayers.h" /> + <ClInclude Include="memory.h" /> + <ClInclude Include="printcommand.h" /> + <ClInclude Include="hoststate.h" /> <ClInclude Include="localchatwriter.h" /> + <ClInclude Include="printmaps.h" /> <ClInclude Include="ns_version.h" /> <ClInclude Include="plugins.h" /> <ClInclude Include="plugin_abi.h" /> - <ClInclude Include="scriptutility.h" /> - <ClInclude Include="scriptjson.h" /> + <ClInclude Include="r2client.h" /> + <ClInclude Include="r2engine.h" /> + <ClInclude Include="r2server.h" /> <ClInclude Include="serverchathooks.h" /> - <ClInclude Include="clientauthhooks.h" /> <ClInclude Include="color.h" /> <ClInclude Include="concommand.h" /> <ClInclude Include="nsprefix.h" /> - <ClInclude Include="context.h" /> <ClInclude Include="convar.h" /> <ClInclude Include="cvar.h" /> <ClInclude Include="dedicated.h" /> - <ClInclude Include="dedicatedmaterialsystem.h" /> <ClInclude Include="filesystem.h" /> - <ClInclude Include="gameutils.h" /> <ClInclude Include="hooks.h" /> - <ClInclude Include="hookutils.h" /> <ClInclude Include="include\crypto\aes_platform.h" /> <ClInclude Include="include\crypto\aria.h" /> <ClInclude Include="include\crypto\asn1.h" /> @@ -534,39 +540,26 @@ <ClInclude Include="include\spdlog\stopwatch.h" /> <ClInclude Include="include\spdlog\tweakme.h" /> <ClInclude Include="include\spdlog\version.h" /> - <ClInclude Include="keyvalues.h" /> - <ClInclude Include="languagehooks.h" /> - <ClInclude Include="latencyflex.h" /> <ClInclude Include="logging.h" /> <ClInclude Include="main.h" /> <ClInclude Include="masterserver.h" /> - <ClInclude Include="maxplayers.h" /> <ClInclude Include="memalloc.h" /> - <ClInclude Include="miscclientfixes.h" /> <ClInclude Include="misccommands.h" /> - <ClInclude Include="miscserverfixes.h" /> - <ClInclude Include="modlocalisation.h" /> <ClInclude Include="modmanager.h" /> <ClInclude Include="pch.h" /> <ClInclude Include="pdef.h" /> <ClInclude Include="playlist.h" /> - <ClInclude Include="miscserverscript.h" /> <ClInclude Include="rpakfilesystem.h" /> - <ClInclude Include="scriptbrowserhooks.h" /> - <ClInclude Include="scriptmainmenupromos.h" /> - <ClInclude Include="scriptmodmenu.h" /> - <ClInclude Include="scriptserverbrowser.h" /> <ClInclude Include="scriptsrson.h" /> <ClInclude Include="serverauthentication.h" /> - <ClInclude Include="scriptservertoclientstringcommand.h" /> + <ClInclude Include="serverpresence.h" /> <ClInclude Include="sigscanning.h" /> <ClInclude Include="sourceconsole.h" /> <ClInclude Include="sourceinterface.h" /> <ClInclude Include="squirrel.h" /> - <ClInclude Include="state.h" /> - <ClInclude Include="exploitfixes.h" /> - <ClInclude Include="exploitfixes_utf8parser.h" /> - <ClInclude Include="nsmem.h" /> + <ClInclude Include="exploitfixes_utf8parser.cpp" /> + <ClInclude Include="tier0.h" /> + <ClInclude Include="vector.h" /> <ClInclude Include="version.h" /> </ItemGroup> <ItemGroup> @@ -580,25 +573,29 @@ <ClCompile Include="clientruihooks.cpp" /> <ClCompile Include="clientvideooverrides.cpp" /> <ClCompile Include="concommand.cpp" /> + <ClCompile Include="exploitfixes_lzss.cpp" /> + <ClCompile Include="limits.cpp" /> + <ClCompile Include="memory.cpp" /> <ClCompile Include="nsprefix.cpp" /> - <ClCompile Include="context.cpp" /> <ClCompile Include="convar.cpp" /> + <ClCompile Include="crashhandler.cpp" /> <ClCompile Include="cvar.cpp" /> <ClCompile Include="debugoverlay.cpp" /> <ClCompile Include="dedicated.cpp" /> <ClCompile Include="dedicatedmaterialsystem.cpp" /> + <ClCompile Include="demofixes.cpp" /> <ClCompile Include="dllmain.cpp" /> <ClCompile Include="filesystem.cpp" /> - <ClCompile Include="gameutils.cpp" /> <ClCompile Include="hooks.cpp" /> - <ClCompile Include="hookutils.cpp" /> + <ClCompile Include="host.cpp" /> + <ClCompile Include="hoststate.cpp" /> <ClCompile Include="keyvalues.cpp" /> <ClCompile Include="latencyflex.cpp" /> <ClCompile Include="localchatwriter.cpp" /> + <ClCompile Include="printmaps.cpp" /> <ClCompile Include="maxplayers.cpp" /> <ClCompile Include="languagehooks.cpp" /> <ClCompile Include="memalloc.cpp" /> - <ClCompile Include="miscclientfixes.cpp" /> <ClCompile Include="misccommands.cpp" /> <ClCompile Include="miscserverfixes.cpp" /> <ClCompile Include="modlocalisation.cpp" /> @@ -612,9 +609,15 @@ <ClCompile Include="pdef.cpp" /> <ClCompile Include="playlist.cpp" /> <ClCompile Include="plugins.cpp" /> + <ClCompile Include="printcommands.cpp" /> + <ClCompile Include="r2client.cpp" /> + <ClCompile Include="r2engine.cpp" /> + <ClCompile Include="r2server.cpp" /> <ClCompile Include="rpakfilesystem.cpp" /> + <ClCompile Include="runframe.cpp" /> <ClCompile Include="scriptbrowserhooks.cpp" /> <ClCompile Include="scriptjson.cpp" /> + <ClCompile Include="scriptdatatables.cpp" /> <ClCompile Include="scriptmainmenupromos.cpp" /> <ClCompile Include="scriptmodmenu.cpp" /> <ClCompile Include="scriptserverbrowser.cpp" /> @@ -624,11 +627,13 @@ <ClCompile Include="miscserverscript.cpp" /> <ClCompile Include="serverchathooks.cpp" /> <ClCompile Include="scriptservertoclientstringcommand.cpp" /> + <ClCompile Include="serverpresence.cpp" /> <ClCompile Include="sigscanning.cpp" /> <ClCompile Include="sourceconsole.cpp" /> <ClCompile Include="sourceinterface.cpp" /> <ClCompile Include="squirrel.cpp" /> <ClCompile Include="exploitfixes.cpp" /> + <ClCompile Include="tier0.cpp" /> <ClCompile Include="version.cpp" /> </ItemGroup> <ItemGroup> diff --git a/NorthstarDLL/NorthstarDLL.vcxproj.filters b/NorthstarDLL/NorthstarDLL.vcxproj.filters index d0613408..293543f7 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj.filters +++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters @@ -1,1789 +1,1801 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - <Filter Include="Header Files\include"> - <UniqueIdentifier>{d4199e4b-10d2-43ce-af9c-e1fa79e1e64e}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Shared"> - <UniqueIdentifier>{4d322431-dcaa-4f75-aee0-3b6371cf52a6}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Shared\Hooks"> - <UniqueIdentifier>{94259c8c-5411-48bf-af4f-46ca32b7d0bb}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared"> - <UniqueIdentifier>{4f525372-34a8-40b3-8a95-81d77cdfcf7f}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Dedicated"> - <UniqueIdentifier>{947835db-67d6-42c0-870d-62743f85231f}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Dedicated"> - <UniqueIdentifier>{8b8ed12a-9269-4dc3-b932-0daefdf6a388}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Client"> - <UniqueIdentifier>{b6f79919-9735-476d-8798-067a75cbeca0}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Client"> - <UniqueIdentifier>{ca657be5-c2d8-4322-a689-1154aaafe57b}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared\Convar"> - <UniqueIdentifier>{a18afb37-5fdd-4340-a6b4-a6541593e398}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Shared\Convar"> - <UniqueIdentifier>{9751b551-5886-45d4-a039-cbd10445263d}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\spdlog"> - <UniqueIdentifier>{8596cc1c-0492-4467-91e3-1f03b7e19f77}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\spdlog\cfg"> - <UniqueIdentifier>{11eaa578-6336-456e-9c7c-8bd202470945}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\spdlog\fmt"> - <UniqueIdentifier>{7ecd75d2-7eee-41c4-87b6-3b7c2213f34e}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\spdlog\fmt\bundled"> - <UniqueIdentifier>{8afc70f1-639c-49ef-9348-ef6dcece114e}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\spdlog\sinks"> - <UniqueIdentifier>{398efed5-0a92-4d32-b5ba-b4a725b2a70a}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\spdlog\details"> - <UniqueIdentifier>{74567974-c66b-45ef-ab28-97b7154ca224}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared\Mods"> - <UniqueIdentifier>{3e892d07-2239-44da-9cf3-c288a34cf9a2}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Shared\Mods"> - <UniqueIdentifier>{6bbce8a5-38b4-4763-a7cb-4e98012ec245}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\rapidjson"> - <UniqueIdentifier>{4ca5392e-7d3d-4066-833f-f534cd5787c3}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\rapidjson\error"> - <UniqueIdentifier>{94b15898-ef33-41c7-995a-31791fccb7e2}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\rapidjson\internal"> - <UniqueIdentifier>{6495657f-ea55-4552-8aa7-b54eb8e86a99}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\rapidjson\msinttypes"> - <UniqueIdentifier>{85aacdee-0f92-4ec4-b20c-0739c1175055}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared\Hooks"> - <UniqueIdentifier>{4db0d1e9-9035-457f-87f1-5dc3f13b6b9e}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Shared\Mods\Compiled"> - <UniqueIdentifier>{d1f93d1e-0ecb-44fe-a277-d3e75aec2570}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared\Mods\Compiled"> - <UniqueIdentifier>{14fc0931-acad-46ec-a55e-94f4469d4235}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Server"> - <UniqueIdentifier>{3d41d3fc-8a3b-4358-b3e8-4f06dc96abfe}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Server\Authentication"> - <UniqueIdentifier>{d69760a9-d5ec-4f3e-8f43-f74041654d44}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Server"> - <UniqueIdentifier>{365e5c1f-4b2f-4d8b-a1d8-cdef401ca689}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Server\Authentication"> - <UniqueIdentifier>{24fd0855-9288-4129-93ba-c6cafdc98d1b}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared\Game Functions"> - <UniqueIdentifier>{2cbddb28-0b17-4881-847d-8773da52b268}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Shared\Game Functions"> - <UniqueIdentifier>{0c93d909-e0d6-4c35-a8a4-a13f681a1012}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\openssl"> - <UniqueIdentifier>{4cb0dd89-5f16-4549-a864-34ca3075352a}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\openssl\openssl"> - <UniqueIdentifier>{914d8b8f-6b19-4f23-b746-f40062d72906}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\openssl\internal"> - <UniqueIdentifier>{09516029-fac7-4235-ad61-402977534a0b}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\openssl\crypto"> - <UniqueIdentifier>{8cc1ae44-9dbf-4719-91a2-82e00b8d78e2}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\include\libcurl"> - <UniqueIdentifier>{ea1e17a6-40b7-4e1b-8edb-e9ae704ce604}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared\Math"> - <UniqueIdentifier>{59b0f68f-daa7-4641-b6fa-8464b56da2bb}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Shared\Math"> - <UniqueIdentifier>{44a83740-9d70-480d-9a7a-43b81f8eab9e}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared\Exploit Fixes"> - <UniqueIdentifier>{4a8a695a-a103-4b1f-b314-0ec19a253119}</UniqueIdentifier> - </Filter> - <Filter Include="Source Files\Shared\Exploit Fixes\UTF8Parser"> - <UniqueIdentifier>{b30e08b1-b962-4264-8cbb-a0a31924b93e}</UniqueIdentifier> - </Filter> - <Filter Include="Header Files\Shared\ExploitFixes"> - <UniqueIdentifier>{7f609cee-d2c0-46a2-b06e-83b9f0511915}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClInclude Include="pch.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\MinHook.h"> - <Filter>Header Files\include</Filter> - </ClInclude> - <ClInclude Include="hooks.h"> - <Filter>Header Files\Shared\Hooks</Filter> - </ClInclude> - <ClInclude Include="hookutils.h"> - <Filter>Header Files\Shared\Hooks</Filter> - </ClInclude> - <ClInclude Include="main.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="dedicated.h"> - <Filter>Header Files\Dedicated</Filter> - </ClInclude> - <ClInclude Include="sourceconsole.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="squirrel.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="sigscanning.h"> - <Filter>Header Files\Shared\Hooks</Filter> - </ClInclude> - <ClInclude Include="logging.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="context.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="sourceinterface.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="concommand.h"> - <Filter>Header Files\Shared\Convar</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\async.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\async_logger.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\async_logger-inl.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\common.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\common-inl.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\formatter.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fwd.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\logger.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\logger-inl.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\pattern_formatter.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\pattern_formatter-inl.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\spdlog.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\spdlog-inl.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\stopwatch.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\tweakme.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\version.h"> - <Filter>Header Files\include\spdlog</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\cfg\argv.h"> - <Filter>Header Files\include\spdlog\cfg</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\cfg\env.h"> - <Filter>Header Files\include\spdlog\cfg</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\cfg\helpers.h"> - <Filter>Header Files\include\spdlog\cfg</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\cfg\helpers-inl.h"> - <Filter>Header Files\include\spdlog\cfg</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bin_to_hex.h"> - <Filter>Header Files\include\spdlog\fmt</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\chrono.h"> - <Filter>Header Files\include\spdlog\fmt</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\fmt.h"> - <Filter>Header Files\include\spdlog\fmt</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\ostr.h"> - <Filter>Header Files\include\spdlog\fmt</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\chrono.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\color.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\compile.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\core.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\format.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\format-inl.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\locale.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\os.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\ostream.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\posix.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\printf.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\fmt\bundled\ranges.h"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\android_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\ansicolor_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\ansicolor_sink-inl.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\base_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\base_sink-inl.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\basic_file_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\basic_file_sink-inl.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\daily_file_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\dist_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\dup_filter_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\hourly_file_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\msvc_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\null_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\ostream_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\ringbuffer_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\rotating_file_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\rotating_file_sink-inl.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\sink-inl.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\stdout_color_sinks.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\stdout_color_sinks-inl.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\stdout_sinks.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\stdout_sinks-inl.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\syslog_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\systemd_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\tcp_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\win_eventlog_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\wincolor_sink.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\sinks\wincolor_sink-inl.h"> - <Filter>Header Files\include\spdlog\sinks</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\backtracer.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\backtracer-inl.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\circular_q.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\console_globals.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\file_helper.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\file_helper-inl.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\fmt_helper.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\log_msg.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\log_msg_buffer.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\log_msg_buffer-inl.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\log_msg-inl.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\mpmc_blocking_q.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\null_mutex.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\os.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\os-inl.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\periodic_worker.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\periodic_worker-inl.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\registry.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\registry-inl.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\synchronous_factory.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\tcp_client.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\tcp_client-windows.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\thread_pool.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\thread_pool-inl.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="include\spdlog\details\windows_include.h"> - <Filter>Header Files\include\spdlog\details</Filter> - </ClInclude> - <ClInclude Include="convar.h"> - <Filter>Header Files\Shared\Convar</Filter> - </ClInclude> - <ClInclude Include="modmanager.h"> - <Filter>Header Files\Shared\Mods</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\allocators.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\document.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\encodedstream.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\encodings.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\filereadstream.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\filewritestream.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\fwd.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\istreamwrapper.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\memorybuffer.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\memorystream.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\ostreamwrapper.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\pointer.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\prettywriter.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\rapidjson.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\reader.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\schema.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\stream.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\stringbuffer.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\writer.h"> - <Filter>Header Files\include\rapidjson</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\error\en.h"> - <Filter>Header Files\include\rapidjson\error</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\error\error.h"> - <Filter>Header Files\include\rapidjson\error</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\biginteger.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\diyfp.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\dtoa.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\ieee754.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\itoa.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\meta.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\pow10.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\regex.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\stack.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\strfunc.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\strtod.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\internal\swap.h"> - <Filter>Header Files\include\rapidjson\internal</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\msinttypes\inttypes.h"> - <Filter>Header Files\include\rapidjson\msinttypes</Filter> - </ClInclude> - <ClInclude Include="include\rapidjson\msinttypes\stdint.h"> - <Filter>Header Files\include\rapidjson\msinttypes</Filter> - </ClInclude> - <ClInclude Include="filesystem.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="scriptsrson.h"> - <Filter>Header Files\Shared\Mods\Compiled</Filter> - </ClInclude> - <ClInclude Include="serverauthentication.h"> - <Filter>Header Files\Server\Authentication</Filter> - </ClInclude> - <ClInclude Include="scriptmodmenu.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="scriptserverbrowser.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="keyvalues.h"> - <Filter>Header Files\Shared\Mods\Compiled</Filter> - </ClInclude> - <ClInclude Include="include\httplib.h"> - <Filter>Header Files\include</Filter> - </ClInclude> - <ClInclude Include="masterserver.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="chatcommand.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="modlocalisation.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="playlist.h"> - <Filter>Header Files\Server</Filter> - </ClInclude> - <ClInclude Include="dedicatedmaterialsystem.h"> - <Filter>Header Files\Dedicated</Filter> - </ClInclude> - <ClInclude Include="misccommands.h"> - <Filter>Header Files\Shared\Convar</Filter> - </ClInclude> - <ClInclude Include="miscserverscript.h"> - <Filter>Header Files\Server</Filter> - </ClInclude> - <ClInclude Include="pdef.h"> - <Filter>Header Files\Shared\Mods\Compiled</Filter> - </ClInclude> - <ClInclude Include="clientauthhooks.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="scriptbrowserhooks.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="gameutils.h"> - <Filter>Header Files\Shared\Game Functions</Filter> - </ClInclude> - <ClInclude Include="memalloc.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="scriptmainmenupromos.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="miscclientfixes.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="include\openssl\__DECC_INCLUDE_EPILOGUE.H"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\__DECC_INCLUDE_PROLOGUE.H"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\aes.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\asn1.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\asn1_mac.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\asn1err.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\asn1t.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\async.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\asyncerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\bio.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\bioerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\blowfish.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\bn.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\bnerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\buffer.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\buffererr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\camellia.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cast.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cmac.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cmp.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cmp_util.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cmperr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cms.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cmserr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\comp.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\comperr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\conf.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\conf_api.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\conferr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\configuration.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\conftypes.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\core.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\core_dispatch.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\core_names.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\core_object.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\crmf.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\crmferr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\crypto.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cryptoerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cryptoerr_legacy.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ct.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\cterr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\decoder.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\decodererr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\des.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\dh.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\dherr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\dsa.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\dsaerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\dtls1.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\e_os2.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ebcdic.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ec.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ecdh.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ecdsa.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ecerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\encoder.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\encodererr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\engine.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\engineerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\err.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ess.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\esserr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\evp.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\evperr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\fips_names.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\fipskey.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\hmac.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\http.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\httperr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\idea.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\kdf.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\kdferr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\lhash.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\macros.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\md2.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\md4.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\md5.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\mdc2.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\modes.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\obj_mac.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\objects.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\objectserr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ocsp.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ocsperr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\opensslconf.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\opensslv.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ossl_typ.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\param_build.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\params.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\pem.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\pem2.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\pemerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\pkcs7.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\pkcs7err.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\pkcs12.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\pkcs12err.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\prov_ssl.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\proverr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\provider.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\rand.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\randerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\rc2.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\rc4.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\rc5.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ripemd.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\rsa.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\rsaerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\safestack.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\seed.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\self_test.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\sha.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\srp.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\srtp.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ssl.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ssl2.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ssl3.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\sslerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\sslerr_legacy.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\stack.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\store.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\storeerr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\symhacks.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\tls1.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\trace.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ts.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\tserr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\txt_db.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\types.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\ui.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\uierr.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\whrlpool.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\x509.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\x509_vfy.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\x509err.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\x509v3.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\openssl\x509v3err.h"> - <Filter>Header Files\include\openssl\openssl</Filter> - </ClInclude> - <ClInclude Include="include\crypto\__DECC_INCLUDE_EPILOGUE.H"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\__DECC_INCLUDE_PROLOGUE.H"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\aes_platform.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\aria.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\asn1.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\asn1_dsa.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\asn1err.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\async.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\asyncerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\bioerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\bn.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\bn_conf.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\bn_dh.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\bn_srp.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\bnerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\buffererr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\chacha.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\cmll_platform.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\cmperr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\cmserr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\comperr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\conferr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\crmferr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\cryptlib.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\cryptoerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\cterr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\ctype.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\decoder.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\decodererr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\des_platform.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\dh.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\dherr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\dsa.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\dsaerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\dso_conf.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\ec.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\ecerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\ecx.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\encoder.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\encodererr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\engine.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\engineerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\err.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\ess.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\esserr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\evp.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\evperr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\httperr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\lhash.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\md32_common.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\modes.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\objects.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\objectserr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\ocsperr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\pem.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\pemerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\pkcs7.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\pkcs7err.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\pkcs12err.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\poly1305.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\ppc_arch.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\punycode.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\rand.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\rand_pool.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\randerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\rsa.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\rsaerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\security_bits.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\sha.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\siphash.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\siv.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\sm2.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\sm2err.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\sm4.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\sparc_arch.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\sparse_array.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\store.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\storeerr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\tserr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\types.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\uierr.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\x509.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\x509err.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\crypto\x509v3err.h"> - <Filter>Header Files\include\openssl\crypto</Filter> - </ClInclude> - <ClInclude Include="include\internal\__DECC_INCLUDE_EPILOGUE.H"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\__DECC_INCLUDE_PROLOGUE.H"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\asn1.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\bio.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\comp.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\conf.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\constant_time.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\core.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\cryptlib.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\dane.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\deprecated.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\der.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\dso.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\dsoerr.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\endian.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\err.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\ffc.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\ktls.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\namemap.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\nelem.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\numbers.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\o_dir.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\packet.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\param_build_set.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\passphrase.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\property.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\propertyerr.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\provider.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\refcount.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\sha3.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\sizes.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\sm3.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\sockets.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\sslconf.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\symhacks.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\thread_once.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\tlsgroups.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\tsan_assist.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="include\internal\unicode.h"> - <Filter>Header Files\include\openssl\internal</Filter> - </ClInclude> - <ClInclude Include="miscserverfixes.h"> - <Filter>Header Files\Server</Filter> - </ClInclude> - <ClInclude Include="maxplayers.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\curl.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\curlver.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\easy.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\mprintf.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\multi.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\options.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\stdcheaders.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\system.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\typecheck-gcc.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="include\libcurl\include\curl\urlapi.h"> - <Filter>Header Files\include\libcurl</Filter> - </ClInclude> - <ClInclude Include="rpakfilesystem.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="bansystem.h"> - <Filter>Header Files\Server\Authentication</Filter> - </ClInclude> - <ClInclude Include="languagehooks.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="latencyflex.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="audio.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="buildainfile.h"> - <Filter>Header Files\Server</Filter> - </ClInclude> - <ClInclude Include="bitbuf.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="nsprefix.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="cvar.h"> - <Filter>Header Files\Shared\Convar</Filter> - </ClInclude> - <ClInclude Include="color.h"> - <Filter>Header Files\Shared\Math</Filter> - </ClInclude> - <ClInclude Include="bits.h"> - <Filter>Header Files\Shared\Math</Filter> - </ClInclude> - <ClInclude Include="serverchathooks.h"> - <Filter>Header Files\Server</Filter> - </ClInclude> - <ClInclude Include="clientchathooks.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="localchatwriter.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="scriptservertoclientstringcommand.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="plugins.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="state.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="plugin_abi.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="debugoverlay.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="clientvideooverrides.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="exploitfixes_utf8parser.h"> - <Filter>Source Files\Shared\Exploit Fixes\UTF8Parser</Filter> - </ClInclude> - <ClInclude Include="version.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="clientruihooks.h"> - <Filter>Header Files\Client</Filter> - </ClInclude> - <ClInclude Include="ns_version.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="exploitfixes.h"> - <Filter>Header Files\Shared\ExploitFixes</Filter> - </ClInclude> - <ClInclude Include="nsmem.h"> - <Filter>Header Files\Shared\ExploitFixes</Filter> - </ClInclude> - <ClInclude Include="scriptutility.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - <ClInclude Include="scriptjson.h"> - <Filter>Header Files\Shared</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <ClCompile Include="dllmain.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="pch.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="hooks.cpp"> - <Filter>Source Files\Shared\Hooks</Filter> - </ClCompile> - <ClCompile Include="hookutils.cpp"> - <Filter>Source Files\Shared\Hooks</Filter> - </ClCompile> - <ClCompile Include="dedicated.cpp"> - <Filter>Source Files\Dedicated</Filter> - </ClCompile> - <ClCompile Include="sourceconsole.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="squirrel.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="sigscanning.cpp"> - <Filter>Source Files\Shared\Hooks</Filter> - </ClCompile> - <ClCompile Include="logging.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="context.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="sourceinterface.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="convar.cpp"> - <Filter>Source Files\Shared\Convar</Filter> - </ClCompile> - <ClCompile Include="concommand.cpp"> - <Filter>Source Files\Shared\Convar</Filter> - </ClCompile> - <ClCompile Include="modmanager.cpp"> - <Filter>Source Files\Shared\Mods</Filter> - </ClCompile> - <ClCompile Include="filesystem.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="scriptsrson.cpp"> - <Filter>Source Files\Shared\Mods\Compiled</Filter> - </ClCompile> - <ClCompile Include="serverauthentication.cpp"> - <Filter>Source Files\Server\Authentication</Filter> - </ClCompile> - <ClCompile Include="scriptmodmenu.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="scriptserverbrowser.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="keyvalues.cpp"> - <Filter>Source Files\Shared\Mods\Compiled</Filter> - </ClCompile> - <ClCompile Include="masterserver.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="gameutils.cpp"> - <Filter>Source Files\Shared\Game Functions</Filter> - </ClCompile> - <ClCompile Include="chatcommand.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="modlocalisation.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="playlist.cpp"> - <Filter>Source Files\Server</Filter> - </ClCompile> - <ClCompile Include="dedicatedmaterialsystem.cpp"> - <Filter>Source Files\Dedicated</Filter> - </ClCompile> - <ClCompile Include="misccommands.cpp"> - <Filter>Source Files\Shared\Convar</Filter> - </ClCompile> - <ClCompile Include="miscserverscript.cpp"> - <Filter>Source Files\Server</Filter> - </ClCompile> - <ClCompile Include="pdef.cpp"> - <Filter>Source Files\Shared\Mods\Compiled</Filter> - </ClCompile> - <ClCompile Include="clientauthhooks.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="scriptbrowserhooks.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="memalloc.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="scriptmainmenupromos.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="miscclientfixes.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="maxplayers.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="miscserverfixes.cpp"> - <Filter>Source Files\Server</Filter> - </ClCompile> - <ClCompile Include="rpakfilesystem.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="bansystem.cpp"> - <Filter>Source Files\Server\Authentication</Filter> - </ClCompile> - <ClCompile Include="languagehooks.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="latencyflex.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="audio.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="buildainfile.cpp"> - <Filter>Source Files\Server</Filter> - </ClCompile> - <ClCompile Include="nsprefix.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="cvar.cpp"> - <Filter>Source Files\Shared\Convar</Filter> - </ClCompile> - <ClCompile Include="bits.cpp"> - <Filter>Source Files\Shared\Math</Filter> - </ClCompile> - <ClCompile Include="serverchathooks.cpp"> - <Filter>Source Files\Server</Filter> - </ClCompile> - <ClCompile Include="clientchathooks.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="localchatwriter.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="scriptservertoclientstringcommand.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="plugins.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="debugoverlay.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="clientvideooverrides.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="exploitfixes.cpp"> - <Filter>Source Files\Shared\Exploit Fixes</Filter> - </ClCompile> - <ClCompile Include="version.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="clientruihooks.cpp"> - <Filter>Source Files\Client</Filter> - </ClCompile> - <ClCompile Include="scriptjson.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - <ClCompile Include="scriptutility.cpp"> - <Filter>Source Files\Shared</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <MASM Include="audio_asm.asm"> - <Filter>Source Files\Client</Filter> - </MASM> - </ItemGroup> - <ItemGroup> - <None Include="include\spdlog\fmt\bundled\LICENSE.rst"> - <Filter>Header Files\include\spdlog\fmt\bundled</Filter> - </None> - <None Include="include\openssl\asn1.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\asn1t.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\bio.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\cmp.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\cms.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\conf.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\configuration.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\crmf.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\crypto.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\ct.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\err.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\ess.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\fipskey.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\lhash.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\ocsp.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\opensslv.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\pkcs7.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\pkcs12.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\safestack.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\srp.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\ssl.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\ui.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\x509.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\x509_vfy.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\openssl\x509v3.h.in"> - <Filter>Header Files\include\openssl\openssl</Filter> - </None> - <None Include="include\crypto\bn_conf.h.in"> - <Filter>Header Files\include\openssl\crypto</Filter> - </None> - <None Include="include\crypto\dso_conf.h.in"> - <Filter>Header Files\include\openssl\crypto</Filter> - </None> - </ItemGroup> +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ <Filter Include="Header Files\include">
+ <UniqueIdentifier>{d4199e4b-10d2-43ce-af9c-e1fa79e1e64e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Client">
+ <UniqueIdentifier>{b6f79919-9735-476d-8798-067a75cbeca0}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Client">
+ <UniqueIdentifier>{ca657be5-c2d8-4322-a689-1154aaafe57b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\spdlog">
+ <UniqueIdentifier>{8596cc1c-0492-4467-91e3-1f03b7e19f77}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\spdlog\cfg">
+ <UniqueIdentifier>{11eaa578-6336-456e-9c7c-8bd202470945}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\spdlog\fmt">
+ <UniqueIdentifier>{7ecd75d2-7eee-41c4-87b6-3b7c2213f34e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\spdlog\fmt\bundled">
+ <UniqueIdentifier>{8afc70f1-639c-49ef-9348-ef6dcece114e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\spdlog\sinks">
+ <UniqueIdentifier>{398efed5-0a92-4d32-b5ba-b4a725b2a70a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\spdlog\details">
+ <UniqueIdentifier>{74567974-c66b-45ef-ab28-97b7154ca224}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\rapidjson">
+ <UniqueIdentifier>{4ca5392e-7d3d-4066-833f-f534cd5787c3}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\rapidjson\error">
+ <UniqueIdentifier>{94b15898-ef33-41c7-995a-31791fccb7e2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\rapidjson\internal">
+ <UniqueIdentifier>{6495657f-ea55-4552-8aa7-b54eb8e86a99}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\rapidjson\msinttypes">
+ <UniqueIdentifier>{85aacdee-0f92-4ec4-b20c-0739c1175055}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Server">
+ <UniqueIdentifier>{3d41d3fc-8a3b-4358-b3e8-4f06dc96abfe}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Server\Authentication">
+ <UniqueIdentifier>{d69760a9-d5ec-4f3e-8f43-f74041654d44}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Server">
+ <UniqueIdentifier>{365e5c1f-4b2f-4d8b-a1d8-cdef401ca689}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Server\Authentication">
+ <UniqueIdentifier>{24fd0855-9288-4129-93ba-c6cafdc98d1b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\openssl">
+ <UniqueIdentifier>{4cb0dd89-5f16-4549-a864-34ca3075352a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\openssl\openssl">
+ <UniqueIdentifier>{914d8b8f-6b19-4f23-b746-f40062d72906}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\openssl\internal">
+ <UniqueIdentifier>{09516029-fac7-4235-ad61-402977534a0b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\openssl\crypto">
+ <UniqueIdentifier>{8cc1ae44-9dbf-4719-91a2-82e00b8d78e2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include\libcurl">
+ <UniqueIdentifier>{ea1e17a6-40b7-4e1b-8edb-e9ae704ce604}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Client\Scripted">
+ <UniqueIdentifier>{51910ba0-2ff8-461d-9f67-8d7907b57d22}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Server\Scripted">
+ <UniqueIdentifier>{325e0d7d-6832-496d-8d8e-968fdfa5dd40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Server\Scripted">
+ <UniqueIdentifier>{802d0771-62f1-4733-89f9-57a4d8864b8d}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Console">
+ <UniqueIdentifier>{04fd662a-6e70-494c-b720-c694a5cc2fb1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Convar">
+ <UniqueIdentifier>{a18afb37-5fdd-4340-a6b4-a6541593e398}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Exploit Fixes">
+ <UniqueIdentifier>{4a8a695a-a103-4b1f-b314-0ec19a253119}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Filesystem">
+ <UniqueIdentifier>{d8a83b5e-9a23-4124-824f-eab37880cb08}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Game Functions">
+ <UniqueIdentifier>{2cbddb28-0b17-4881-847d-8773da52b268}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Hooks">
+ <UniqueIdentifier>{4db0d1e9-9035-457f-87f1-5dc3f13b6b9e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Math">
+ <UniqueIdentifier>{59b0f68f-daa7-4641-b6fa-8464b56da2bb}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Mods">
+ <UniqueIdentifier>{3e892d07-2239-44da-9cf3-c288a34cf9a2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Mods\Compiled Assets">
+ <UniqueIdentifier>{14fc0931-acad-46ec-a55e-94f4469d4235}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Dedicated Server">
+ <UniqueIdentifier>{947835db-67d6-42c0-870d-62743f85231f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Console">
+ <UniqueIdentifier>{bf0769d8-40fd-4701-85e9-7ed94aab2283}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Convar">
+ <UniqueIdentifier>{9751b551-5886-45d4-a039-cbd10445263d}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Exploit Fixes">
+ <UniqueIdentifier>{96101d42-72af-4fd1-8559-8d1d1ff66240}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Filesystem">
+ <UniqueIdentifier>{ee3ba13a-3061-41d7-981d-328ac2596fd2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Game Functions">
+ <UniqueIdentifier>{0c93d909-e0d6-4c35-a8a4-a13f681a1012}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Hooks">
+ <UniqueIdentifier>{94259c8c-5411-48bf-af4f-46ca32b7d0bb}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Math">
+ <UniqueIdentifier>{44a83740-9d70-480d-9a7a-43b81f8eab9e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Mods">
+ <UniqueIdentifier>{6bbce8a5-38b4-4763-a7cb-4e98012ec245}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Mods\Compiled Assets">
+ <UniqueIdentifier>{826d5193-3ad0-434b-ba7c-dd24ed4bbd0c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Dedicated Server">
+ <UniqueIdentifier>{0f1ba4c4-78ee-4b05-afa5-6f598063f5c1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Squirrel">
+ <UniqueIdentifier>{ca669b16-b8bb-4654-993f-fffa44c914f1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Squirrel">
+ <UniqueIdentifier>{26365f16-ff52-4e80-a01b-2ca020376c93}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Scripted">
+ <UniqueIdentifier>{7263403a-7550-4aa2-a724-f622ab200eed}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="pch.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\MinHook.h">
+ <Filter>Header Files\include</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="sourceconsole.h">
+ <Filter>Header Files\Client</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\async.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\async_logger.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\async_logger-inl.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\common.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\common-inl.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\formatter.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fwd.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\logger.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\logger-inl.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\pattern_formatter.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\pattern_formatter-inl.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\spdlog.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\spdlog-inl.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\stopwatch.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\tweakme.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\version.h">
+ <Filter>Header Files\include\spdlog</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\cfg\argv.h">
+ <Filter>Header Files\include\spdlog\cfg</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\cfg\env.h">
+ <Filter>Header Files\include\spdlog\cfg</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\cfg\helpers.h">
+ <Filter>Header Files\include\spdlog\cfg</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\cfg\helpers-inl.h">
+ <Filter>Header Files\include\spdlog\cfg</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bin_to_hex.h">
+ <Filter>Header Files\include\spdlog\fmt</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\chrono.h">
+ <Filter>Header Files\include\spdlog\fmt</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\fmt.h">
+ <Filter>Header Files\include\spdlog\fmt</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\ostr.h">
+ <Filter>Header Files\include\spdlog\fmt</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\chrono.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\color.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\compile.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\core.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\format.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\format-inl.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\locale.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\os.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\ostream.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\posix.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\printf.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\fmt\bundled\ranges.h">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\android_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\ansicolor_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\ansicolor_sink-inl.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\base_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\base_sink-inl.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\basic_file_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\basic_file_sink-inl.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\daily_file_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\dist_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\dup_filter_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\hourly_file_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\msvc_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\null_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\ostream_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\ringbuffer_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\rotating_file_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\rotating_file_sink-inl.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\sink-inl.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\stdout_color_sinks.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\stdout_color_sinks-inl.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\stdout_sinks.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\stdout_sinks-inl.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\syslog_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\systemd_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\tcp_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\win_eventlog_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\wincolor_sink.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\sinks\wincolor_sink-inl.h">
+ <Filter>Header Files\include\spdlog\sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\backtracer.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\backtracer-inl.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\circular_q.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\console_globals.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\file_helper.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\file_helper-inl.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\fmt_helper.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\log_msg.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\log_msg_buffer.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\log_msg_buffer-inl.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\log_msg-inl.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\mpmc_blocking_q.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\null_mutex.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\os.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\os-inl.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\periodic_worker.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\periodic_worker-inl.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\registry.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\registry-inl.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\synchronous_factory.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\tcp_client.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\tcp_client-windows.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\thread_pool.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\thread_pool-inl.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spdlog\details\windows_include.h">
+ <Filter>Header Files\include\spdlog\details</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\allocators.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\document.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\encodedstream.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\encodings.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\filereadstream.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\filewritestream.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\fwd.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\istreamwrapper.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\memorybuffer.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\memorystream.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\ostreamwrapper.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\pointer.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\prettywriter.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\rapidjson.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\reader.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\schema.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\stream.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\stringbuffer.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\writer.h">
+ <Filter>Header Files\include\rapidjson</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\error\en.h">
+ <Filter>Header Files\include\rapidjson\error</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\error\error.h">
+ <Filter>Header Files\include\rapidjson\error</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\biginteger.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\diyfp.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\dtoa.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\ieee754.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\itoa.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\meta.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\pow10.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\regex.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\stack.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\strfunc.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\strtod.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\internal\swap.h">
+ <Filter>Header Files\include\rapidjson\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\msinttypes\inttypes.h">
+ <Filter>Header Files\include\rapidjson\msinttypes</Filter>
+ </ClInclude>
+ <ClInclude Include="include\rapidjson\msinttypes\stdint.h">
+ <Filter>Header Files\include\rapidjson\msinttypes</Filter>
+ </ClInclude>
+ <ClInclude Include="serverauthentication.h">
+ <Filter>Header Files\Server\Authentication</Filter>
+ </ClInclude>
+ <ClInclude Include="include\httplib.h">
+ <Filter>Header Files\include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\__DECC_INCLUDE_EPILOGUE.H">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\__DECC_INCLUDE_PROLOGUE.H">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\aes.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\asn1.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\asn1_mac.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\asn1err.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\asn1t.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\async.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\asyncerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\bio.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\bioerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\blowfish.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\bn.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\bnerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\buffer.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\buffererr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\camellia.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cast.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cmac.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cmp.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cmp_util.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cmperr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cms.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cmserr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\comp.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\comperr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\conf.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\conf_api.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\conferr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\configuration.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\conftypes.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\core.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\core_dispatch.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\core_names.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\core_object.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\crmf.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\crmferr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\crypto.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cryptoerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cryptoerr_legacy.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ct.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\cterr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\decoder.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\decodererr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\des.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\dh.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\dherr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\dsa.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\dsaerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\dtls1.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\e_os2.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ebcdic.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ec.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ecdh.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ecdsa.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ecerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\encoder.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\encodererr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\engine.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\engineerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\err.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ess.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\esserr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\evp.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\evperr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\fips_names.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\fipskey.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\hmac.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\http.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\httperr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\idea.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\kdf.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\kdferr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\lhash.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\macros.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\md2.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\md4.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\md5.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\mdc2.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\modes.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\obj_mac.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\objects.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\objectserr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ocsp.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ocsperr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\opensslconf.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\opensslv.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ossl_typ.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\param_build.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\params.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\pem.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\pem2.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\pemerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\pkcs7.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\pkcs7err.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\pkcs12.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\pkcs12err.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\prov_ssl.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\proverr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\provider.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\rand.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\randerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\rc2.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\rc4.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\rc5.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ripemd.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\rsa.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\rsaerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\safestack.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\seed.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\self_test.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\sha.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\srp.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\srtp.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ssl.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ssl2.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ssl3.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\sslerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\sslerr_legacy.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\stack.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\store.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\storeerr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\symhacks.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\tls1.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\trace.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ts.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\tserr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\txt_db.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\types.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\ui.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\uierr.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\whrlpool.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\x509.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\x509_vfy.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\x509err.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\x509v3.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\openssl\x509v3err.h">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\__DECC_INCLUDE_EPILOGUE.H">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\__DECC_INCLUDE_PROLOGUE.H">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\aes_platform.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\aria.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\asn1.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\asn1_dsa.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\asn1err.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\async.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\asyncerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\bioerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\bn.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\bn_conf.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\bn_dh.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\bn_srp.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\bnerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\buffererr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\chacha.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\cmll_platform.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\cmperr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\cmserr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\comperr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\conferr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\crmferr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\cryptlib.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\cryptoerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\cterr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\ctype.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\decoder.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\decodererr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\des_platform.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\dh.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\dherr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\dsa.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\dsaerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\dso_conf.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\ec.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\ecerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\ecx.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\encoder.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\encodererr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\engine.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\engineerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\err.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\ess.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\esserr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\evp.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\evperr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\httperr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\lhash.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\md32_common.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\modes.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\objects.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\objectserr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\ocsperr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\pem.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\pemerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\pkcs7.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\pkcs7err.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\pkcs12err.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\poly1305.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\ppc_arch.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\punycode.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\rand.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\rand_pool.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\randerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\rsa.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\rsaerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\security_bits.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\sha.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\siphash.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\siv.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\sm2.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\sm2err.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\sm4.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\sparc_arch.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\sparse_array.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\store.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\storeerr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\tserr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\types.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\uierr.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\x509.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\x509err.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\crypto\x509v3err.h">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\__DECC_INCLUDE_EPILOGUE.H">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\__DECC_INCLUDE_PROLOGUE.H">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\asn1.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\bio.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\comp.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\conf.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\constant_time.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\core.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\cryptlib.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\dane.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\deprecated.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\der.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\dso.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\dsoerr.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\endian.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\err.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\ffc.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\ktls.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\namemap.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\nelem.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\numbers.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\o_dir.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\packet.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\param_build_set.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\passphrase.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\property.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\propertyerr.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\provider.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\refcount.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\sha3.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\sizes.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\sm3.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\sockets.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\sslconf.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\symhacks.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\thread_once.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\tlsgroups.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\tsan_assist.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\internal\unicode.h">
+ <Filter>Header Files\include\openssl\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\curl.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\curlver.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\easy.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\mprintf.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\multi.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\options.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\stdcheaders.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\system.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\typecheck-gcc.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="include\libcurl\include\curl\urlapi.h">
+ <Filter>Header Files\include\libcurl</Filter>
+ </ClInclude>
+ <ClInclude Include="bansystem.h">
+ <Filter>Header Files\Server\Authentication</Filter>
+ </ClInclude>
+ <ClInclude Include="audio.h">
+ <Filter>Header Files\Client</Filter>
+ </ClInclude>
+ <ClInclude Include="localchatwriter.h">
+ <Filter>Header Files\Client</Filter>
+ </ClInclude>
+ <ClInclude Include="plugins.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="plugin_abi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="version.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ns_version.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="serverchathooks.h">
+ <Filter>Header Files\Server\Scripted</Filter>
+ </ClInclude>
+ <ClInclude Include="dedicated.h">
+ <Filter>Header Files\Dedicated Server</Filter>
+ </ClInclude>
+ <ClInclude Include="nsprefix.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="exploitfixes_utf8parser.cpp">
+ <Filter>Source Files\Exploit Fixes</Filter>
+ </ClInclude>
+ <ClInclude Include="crashhandler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hoststate.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="masterserver.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="memalloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="playlist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="sourceinterface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="bits.h">
+ <Filter>Header Files\Math</Filter>
+ </ClInclude>
+ <ClInclude Include="bitbuf.h">
+ <Filter>Header Files\Math</Filter>
+ </ClInclude>
+ <ClInclude Include="convar.h">
+ <Filter>Header Files\Convar</Filter>
+ </ClInclude>
+ <ClInclude Include="concommand.h">
+ <Filter>Header Files\Convar</Filter>
+ </ClInclude>
+ <ClInclude Include="cvar.h">
+ <Filter>Header Files\Convar</Filter>
+ </ClInclude>
+ <ClInclude Include="filesystem.h">
+ <Filter>Header Files\Filesystem</Filter>
+ </ClInclude>
+ <ClInclude Include="hooks.h">
+ <Filter>Header Files\Hooks</Filter>
+ </ClInclude>
+ <ClInclude Include="limits.h">
+ <Filter>Header Files\Exploit Fixes</Filter>
+ </ClInclude>
+ <ClInclude Include="logging.h">
+ <Filter>Header Files\Console</Filter>
+ </ClInclude>
+ <ClInclude Include="misccommands.h">
+ <Filter>Header Files\Convar</Filter>
+ </ClInclude>
+ <ClInclude Include="modmanager.h">
+ <Filter>Header Files\Mods</Filter>
+ </ClInclude>
+ <ClInclude Include="pdef.h">
+ <Filter>Header Files\Mods\Compiled Assets</Filter>
+ </ClInclude>
+ <ClInclude Include="printcommand.h">
+ <Filter>Header Files\Console</Filter>
+ </ClInclude>
+ <ClInclude Include="printmaps.h">
+ <Filter>Header Files\Console</Filter>
+ </ClInclude>
+ <ClInclude Include="r2client.h">
+ <Filter>Header Files\Game Functions</Filter>
+ </ClInclude>
+ <ClInclude Include="r2engine.h">
+ <Filter>Header Files\Game Functions</Filter>
+ </ClInclude>
+ <ClInclude Include="r2server.h">
+ <Filter>Header Files\Game Functions</Filter>
+ </ClInclude>
+ <ClInclude Include="scriptsrson.h">
+ <Filter>Header Files\Mods\Compiled Assets</Filter>
+ </ClInclude>
+ <ClInclude Include="tier0.h">
+ <Filter>Header Files\Game Functions</Filter>
+ </ClInclude>
+ <ClInclude Include="sigscanning.h">
+ <Filter>Header Files\Hooks</Filter>
+ </ClInclude>
+ <ClInclude Include="rpakfilesystem.h">
+ <Filter>Header Files\Filesystem</Filter>
+ </ClInclude>
+ <ClInclude Include="color.h">
+ <Filter>Header Files\Math</Filter>
+ </ClInclude>
+ <ClInclude Include="serverpresence.h">
+ <Filter>Header Files\Server</Filter>
+ </ClInclude>
+ <ClInclude Include="memory.h">
+ <Filter>Header Files\Hooks</Filter>
+ </ClInclude>
+ <ClInclude Include="maxplayers.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="squirrel.h">
+ <Filter>Header Files\Squirrel</Filter>
+ </ClInclude>
+ <ClInclude Include="squirreldatatypes.h">
+ <Filter>Header Files\Squirrel</Filter>
+ </ClInclude>
+ <ClInclude Include="vector.h">
+ <Filter>Header Files\Math</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="pch.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dedicated.cpp">
+ <Filter>Source Files\Dedicated Server</Filter>
+ </ClCompile>
+ <ClCompile Include="sourceconsole.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="modmanager.cpp">
+ <Filter>Source Files\Mods</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptsrson.cpp">
+ <Filter>Source Files\Mods\Compiled Assets</Filter>
+ </ClCompile>
+ <ClCompile Include="serverauthentication.cpp">
+ <Filter>Source Files\Server\Authentication</Filter>
+ </ClCompile>
+ <ClCompile Include="keyvalues.cpp">
+ <Filter>Source Files\Mods\Compiled Assets</Filter>
+ </ClCompile>
+ <ClCompile Include="chatcommand.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="modlocalisation.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="dedicatedmaterialsystem.cpp">
+ <Filter>Source Files\Dedicated Server</Filter>
+ </ClCompile>
+ <ClCompile Include="pdef.cpp">
+ <Filter>Source Files\Mods\Compiled Assets</Filter>
+ </ClCompile>
+ <ClCompile Include="clientauthhooks.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="miscserverfixes.cpp">
+ <Filter>Source Files\Server</Filter>
+ </ClCompile>
+ <ClCompile Include="bansystem.cpp">
+ <Filter>Source Files\Server\Authentication</Filter>
+ </ClCompile>
+ <ClCompile Include="languagehooks.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="latencyflex.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="audio.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="buildainfile.cpp">
+ <Filter>Source Files\Server</Filter>
+ </ClCompile>
+ <ClCompile Include="localchatwriter.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="plugins.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="debugoverlay.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="clientvideooverrides.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="version.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="clientruihooks.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptmainmenupromos.cpp">
+ <Filter>Source Files\Client\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="clientchathooks.cpp">
+ <Filter>Source Files\Client\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptmodmenu.cpp">
+ <Filter>Source Files\Client\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptservertoclientstringcommand.cpp">
+ <Filter>Source Files\Client\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptserverbrowser.cpp">
+ <Filter>Source Files\Client\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptbrowserhooks.cpp">
+ <Filter>Source Files\Client\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="serverchathooks.cpp">
+ <Filter>Source Files\Server\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="miscserverscript.cpp">
+ <Filter>Source Files\Server\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="demofixes.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
+ <ClCompile Include="nsprefix.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dllmain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="crashhandler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="host.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hoststate.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="masterserver.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="maxplayers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="memalloc.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="playlist.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sourceinterface.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="r2server.cpp">
+ <Filter>Source Files\Game Functions</Filter>
+ </ClCompile>
+ <ClCompile Include="r2client.cpp">
+ <Filter>Source Files\Game Functions</Filter>
+ </ClCompile>
+ <ClCompile Include="r2engine.cpp">
+ <Filter>Source Files\Game Functions</Filter>
+ </ClCompile>
+ <ClCompile Include="rpakfilesystem.cpp">
+ <Filter>Source Files\Filesystem</Filter>
+ </ClCompile>
+ <ClCompile Include="filesystem.cpp">
+ <Filter>Source Files\Filesystem</Filter>
+ </ClCompile>
+ <ClCompile Include="exploitfixes.cpp">
+ <Filter>Source Files\Exploit Fixes</Filter>
+ </ClCompile>
+ <ClCompile Include="limits.cpp">
+ <Filter>Source Files\Exploit Fixes</Filter>
+ </ClCompile>
+ <ClCompile Include="hooks.cpp">
+ <Filter>Source Files\Hooks</Filter>
+ </ClCompile>
+ <ClCompile Include="sigscanning.cpp">
+ <Filter>Source Files\Hooks</Filter>
+ </ClCompile>
+ <ClCompile Include="bits.cpp">
+ <Filter>Source Files\Math</Filter>
+ </ClCompile>
+ <ClCompile Include="convar.cpp">
+ <Filter>Source Files\Convar</Filter>
+ </ClCompile>
+ <ClCompile Include="concommand.cpp">
+ <Filter>Source Files\Convar</Filter>
+ </ClCompile>
+ <ClCompile Include="printcommands.cpp">
+ <Filter>Source Files\Console</Filter>
+ </ClCompile>
+ <ClCompile Include="printmaps.cpp">
+ <Filter>Source Files\Console</Filter>
+ </ClCompile>
+ <ClCompile Include="cvar.cpp">
+ <Filter>Source Files\Convar</Filter>
+ </ClCompile>
+ <ClCompile Include="misccommands.cpp">
+ <Filter>Source Files\Convar</Filter>
+ </ClCompile>
+ <ClCompile Include="tier0.cpp">
+ <Filter>Source Files\Game Functions</Filter>
+ </ClCompile>
+ <ClCompile Include="logging.cpp">
+ <Filter>Source Files\Console</Filter>
+ </ClCompile>
+ <ClCompile Include="serverpresence.cpp">
+ <Filter>Source Files\Server</Filter>
+ </ClCompile>
+ <ClCompile Include="runframe.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="memory.cpp">
+ <Filter>Source Files\Hooks</Filter>
+ </ClCompile>
+ <ClCompile Include="exploitfixes_lzss.cpp">
+ <Filter>Source Files\Exploit Fixes</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptutility.cpp">
+ <Filter>Source Files\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptjson.cpp">
+ <Filter>Source Files\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="squirrel.cpp">
+ <Filter>Source Files\Squirrel</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptdatatables.cpp">
+ <Filter>Source Files\Scripted</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <MASM Include="audio_asm.asm">
+ <Filter>Source Files\Client</Filter>
+ </MASM>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="include\spdlog\fmt\bundled\LICENSE.rst">
+ <Filter>Header Files\include\spdlog\fmt\bundled</Filter>
+ </None>
+ <None Include="include\openssl\asn1.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\asn1t.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\bio.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\cmp.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\cms.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\conf.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\configuration.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\crmf.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\crypto.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\ct.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\err.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\ess.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\fipskey.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\lhash.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\ocsp.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\opensslv.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\pkcs7.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\pkcs12.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\safestack.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\srp.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\ssl.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\ui.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\x509.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\x509_vfy.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\openssl\x509v3.h.in">
+ <Filter>Header Files\include\openssl\openssl</Filter>
+ </None>
+ <None Include="include\crypto\bn_conf.h.in">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </None>
+ <None Include="include\crypto\dso_conf.h.in">
+ <Filter>Header Files\include\openssl\crypto</Filter>
+ </None>
+ </ItemGroup>
</Project>
\ No newline at end of file diff --git a/NorthstarDLL/audio.cpp b/NorthstarDLL/audio.cpp index 6d52a071..c298687a 100644 --- a/NorthstarDLL/audio.cpp +++ b/NorthstarDLL/audio.cpp @@ -1,13 +1,15 @@ #include "pch.h" #include "audio.h" #include "dedicated.h" +#include "convar.h" #include "rapidjson/error/en.h" #include <fstream> #include <iostream> #include <sstream> #include <random> -#include "convar.h" + +AUTOHOOK_INIT() extern "C" { @@ -229,10 +231,8 @@ EventOverrideData::EventOverrideData(const std::string& data, const fs::path& pa } // read from after the header first to preserve the empty header, then read the header last - wavStream.seekg(sizeof(EMPTY_WAVE), std::ios::beg); - wavStream.read(reinterpret_cast<char*>(&data[sizeof(EMPTY_WAVE)]), fileSize - sizeof(EMPTY_WAVE)); wavStream.seekg(0, std::ios::beg); - wavStream.read(reinterpret_cast<char*>(data), sizeof(EMPTY_WAVE)); + wavStream.read(reinterpret_cast<char*>(data), fileSize); wavStream.close(); spdlog::info("Finished async read of audio sample {}", pathString); @@ -315,6 +315,7 @@ void CustomAudioManager::ClearAudioOverrides() { // stop all miles sounds beforehand // miles_stop_all + MilesStopAll(); // this is cancer but it works @@ -329,9 +330,6 @@ void CustomAudioManager::ClearAudioOverrides() m_loadedAudioOverridesRegex.clear(); } -typedef bool (*LoadSampleMetadata_Type)(void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType); -LoadSampleMetadata_Type LoadSampleMetadata_Original; - 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); @@ -368,6 +366,24 @@ bool ShouldPlayAudioEvent(const char* eventName, const std::shared_ptr<EventOver return true; // good to go } +// forward declare +bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( + uintptr_t parentEvent, void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType); + +// DO NOT TOUCH THIS FUNCTION +// The actual logic of it in a separate function (forcefully not inlined) to preserve the r12 register, which holds the event pointer. +AUTOHOOK(LoadSampleMetadata, mileswin64.dll + 0xF110, +bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType)) +{ + uintptr_t parentEvent = (uintptr_t)Audio_GetParentEvent(); + + // Raw source, used for voice data only + if (audioType == 0) + return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); + + return LoadSampleMetadata_Internal(parentEvent, sample, audioBuffer, audioBufferLength, audioType); +} + // DO NOT INLINE THIS FUNCTION // See comment below. bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( @@ -398,7 +414,7 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( if (!overrideData) // not found either - return LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, audioType); + return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); else { // cache found pattern to improve performance @@ -412,7 +428,7 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( overrideData = iter->second; if (!ShouldPlayAudioEvent(eventName, overrideData)) - return LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, audioType); + return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); void* data = 0; unsigned int dataLength = 0; @@ -454,7 +470,7 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( if (!data) { spdlog::warn("Could not fetch override sample data for event {}! Using original data instead.", eventName); - return LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, audioType); + return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); } audioBuffer = data; @@ -465,51 +481,23 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( *(unsigned int*)((uintptr_t)sample + 0xF0) = audioBufferLength; // 64 - Auto-detect sample type - bool res = LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, 64); + bool res = LoadSampleMetadata(sample, audioBuffer, audioBufferLength, 64); if (!res) spdlog::error("LoadSampleMetadata failed! The game will crash :("); return res; } -// DO NOT TOUCH THIS FUNCTION -// The actual logic of it in a separate function (forcefully not inlined) to preserve the r12 register, which holds the event pointer. -bool __fastcall LoadSampleMetadata_Hook(void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType) -{ - uintptr_t parentEvent = (uintptr_t)Audio_GetParentEvent(); - - // Raw source, used for voice data only - if (audioType == 0) - return LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, audioType); - - return LoadSampleMetadata_Internal(parentEvent, sample, audioBuffer, audioBufferLength, audioType); -} - -typedef bool (*MilesLog_Type)(int level, const char* string); -MilesLog_Type MilesLog_Original; - -void __fastcall MilesLog_Hook(int level, const char* string) +AUTOHOOK(MilesLog, client.dll + 0x57DAD0, +void, __fastcall, (int level, const char* string)) { spdlog::info("[MSS] {} - {}", level, string); } -void InitialiseMilesAudioHooks(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", AudioHooks, ConVar, (CModule module)) { - Cvar_ns_print_played_sounds = new ConVar("ns_print_played_sounds", "0", FCVAR_NONE, ""); - - if (IsDedicatedServer()) - return; - - uintptr_t milesAudioBase = (uintptr_t)GetModuleHandleA("mileswin64.dll"); + AUTOHOOK_DISPATCH() - if (!milesAudioBase) - return spdlog::error("miles audio not found :terror:"); - - HookEnabler hook; - - ENABLER_CREATEHOOK( - hook, (char*)milesAudioBase + 0xF110, &LoadSampleMetadata_Hook, reinterpret_cast<LPVOID*>(&LoadSampleMetadata_Original)); - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x57DAD0, &MilesLog_Hook, reinterpret_cast<LPVOID*>(&MilesLog_Original)); - - MilesStopAll = (MilesStopAll_Type)((char*)baseAddress + 0x580850); + Cvar_ns_print_played_sounds = new ConVar("ns_print_played_sounds", "0", FCVAR_NONE, ""); + MilesStopAll = module.Offset(0x580850).As<MilesStopAll_Type>(); } diff --git a/NorthstarDLL/audio.h b/NorthstarDLL/audio.h index bf3f0d87..26cda205 100644 --- a/NorthstarDLL/audio.h +++ b/NorthstarDLL/audio.h @@ -5,8 +5,6 @@ #include <regex> #include <shared_mutex> -namespace fs = std::filesystem; - enum class AudioSelectionStrategy { INVALID = -1, @@ -46,5 +44,3 @@ class CustomAudioManager }; extern CustomAudioManager g_CustomAudioManager; - -void InitialiseMilesAudioHooks(HMODULE baseAddress); diff --git a/NorthstarDLL/bansystem.cpp b/NorthstarDLL/bansystem.cpp index df98464e..25c0e6bf 100644 --- a/NorthstarDLL/bansystem.cpp +++ b/NorthstarDLL/bansystem.cpp @@ -1,26 +1,28 @@ +#pragma once #include "pch.h" #include "bansystem.h" #include "serverauthentication.h" +#include "maxplayers.h" #include "concommand.h" -#include "miscserverscript.h" -#include <filesystem> +#include "r2server.h" +#include "r2engine.h" #include "nsprefix.h" -#include <ctime> + +#include <filesystem> const char* BANLIST_PATH_SUFFIX = "/banlist.txt"; const char BANLIST_COMMENT_CHAR = '#'; -ServerBanSystem* g_ServerBanSystem; +ServerBanSystem* g_pBanSystem; void ServerBanSystem::OpenBanlist() { - std::ifstream enabledModsStream(GetNorthstarPrefix() + "/banlist.txt"); - std::stringstream enabledModsStringStream; + std::ifstream banlistStream(GetNorthstarPrefix() + "/banlist.txt"); - if (!enabledModsStream.fail()) + if (!banlistStream.fail()) { std::string line; - while (std::getline(enabledModsStream, line)) + while (std::getline(banlistStream, line)) { // ignore line if first char is # or line is empty if (line == "" || line.front() == BANLIST_COMMENT_CHAR) @@ -41,7 +43,7 @@ void ServerBanSystem::OpenBanlist() m_vBannedUids.push_back(strtoull(uid.c_str(), nullptr, 10)); } - enabledModsStream.close(); + banlistStream.close(); } // open write stream for banlist // dont do this to allow for all time access @@ -177,45 +179,44 @@ bool ServerBanSystem::IsUIDAllowed(uint64_t uid) return std::find(m_vBannedUids.begin(), m_vBannedUids.end(), uid) == m_vBannedUids.end(); } -void BanPlayerCommand(const CCommand& args) +void ConCommand_ban(const CCommand& args) { if (args.ArgC() < 2) return; - // assuming maxplayers 32 - for (int i = 0; i < 32; i++) + for (int i = 0; i < R2::GetMaxPlayers(); i++) { - void* player = GetPlayerByIndex(i); + R2::CBaseClient* player = &R2::g_pClientArray[i]; - if (!strcmp((char*)player + 0x16, args.Arg(1)) || !strcmp((char*)player + 0xF500, args.Arg(1))) + if (!strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1))) { - g_ServerBanSystem->BanUID(strtoull((char*)player + 0xF500, nullptr, 10)); - CBaseClient__Disconnect(player, 1, "Banned from server"); + g_pBanSystem->BanUID(strtoull(player->m_UID, nullptr, 10)); + R2::CBaseClient__Disconnect(player, 1, "Banned from server"); break; } } } -void UnbanPlayerCommand(const CCommand& args) +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_ServerBanSystem->UnbanUID(strtoull(args.Arg(1), nullptr, 10)); + g_pBanSystem->UnbanUID(strtoull(args.Arg(1), nullptr, 10)); } -void ClearBanlistCommand(const CCommand& args) +void ConCommand_clearbanlist(const CCommand& args) { - g_ServerBanSystem->ClearBanlist(); + g_pBanSystem->ClearBanlist(); } -void InitialiseBanSystem(HMODULE baseAddress) +ON_DLL_LOAD_RELIESON("engine.dll", BanSystem, ConCommand, (CModule module)) { - g_ServerBanSystem = new ServerBanSystem; - g_ServerBanSystem->OpenBanlist(); + g_pBanSystem = new ServerBanSystem; + g_pBanSystem->OpenBanlist(); - RegisterConCommand("ban", BanPlayerCommand, "bans a given player by uid or name", FCVAR_GAMEDLL); - RegisterConCommand("unban", UnbanPlayerCommand, "unbans a given player by uid", FCVAR_NONE); - RegisterConCommand("clearbanlist", ClearBanlistCommand, "clears all uids on the banlist", FCVAR_NONE); + 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/bansystem.h b/NorthstarDLL/bansystem.h index 3b6ae587..6f180126 100644 --- a/NorthstarDLL/bansystem.h +++ b/NorthstarDLL/bansystem.h @@ -16,6 +16,4 @@ class ServerBanSystem bool IsUIDAllowed(uint64_t uid); }; -extern ServerBanSystem* g_ServerBanSystem; - -void InitialiseBanSystem(HMODULE baseAddress); +extern ServerBanSystem* g_pBanSystem; diff --git a/NorthstarDLL/buildainfile.cpp b/NorthstarDLL/buildainfile.cpp index 24a16f74..c61484c7 100644 --- a/NorthstarDLL/buildainfile.cpp +++ b/NorthstarDLL/buildainfile.cpp @@ -1,19 +1,19 @@ #include "pch.h" -#include "buildainfile.h" #include "convar.h" -#include "hookutils.h" +#include "hoststate.h" +#include "r2engine.h" + #include <fstream> #include <filesystem> -#include "nsmem.h" -namespace fs = std::filesystem; +AUTOHOOK_INIT() const int AINET_VERSION_NUMBER = 57; const int AINET_SCRIPT_VERSION_NUMBER = 21; -const int MAP_VERSION_TEMP = 30; const int PLACEHOLDER_CRC = 0; const int MAX_HULLS = 5; +#pragma pack(push, 1) struct CAI_NodeLink { short srcId; @@ -24,6 +24,7 @@ struct CAI_NodeLink char unk2[5]; int64_t flags; }; +#pragma pack(pop) #pragma pack(push, 1) struct CAI_NodeLinkDisk @@ -33,7 +34,9 @@ struct CAI_NodeLinkDisk char unk0; bool hulls[MAX_HULLS]; }; +#pragma pack(pop) +#pragma pack(push, 1) struct CAI_Node { int index; // not present on disk @@ -62,6 +65,7 @@ struct CAI_Node 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) @@ -81,7 +85,9 @@ struct CAI_NodeDisk short unk5; char unk6[8]; }; // total size of 68 bytes +#pragma pack(pop) +#pragma pack(push, 1) struct UnkNodeStruct0 { int index; @@ -106,10 +112,12 @@ struct UnkNodeStruct0 char pad4[132]; char unk5; }; +#pragma pack(pop) int* pUnkStruct0Count; UnkNodeStruct0*** pppUnkNodeStruct0s; +#pragma pack(push, 1) struct UnkLinkStruct1 { short unk0; @@ -119,10 +127,12 @@ struct UnkLinkStruct1 char unk4; char unk5; }; +#pragma pack(pop) int* pUnkLinkStruct1Count; UnkLinkStruct1*** pppUnkStruct1s; +#pragma pack(push, 1) struct CAI_ScriptNode { float x; @@ -130,7 +140,9 @@ struct CAI_ScriptNode float z; uint64_t scriptdata; }; +#pragma pack(pop) +#pragma pack(push, 1) struct CAI_Network { // +0 @@ -160,16 +172,16 @@ struct CAI_Network // +84176 CAI_Node** nodes; }; +#pragma pack(pop) char** pUnkServerMapversionGlobal; -char* pMapName; ConVar* Cvar_ns_ai_dumpAINfileFromLoad; void DumpAINInfo(CAI_Network* aiNetwork) { - fs::path writePath("r2/maps/graphs"); - writePath /= pMapName; + fs::path writePath(fmt::format("{}/maps/graphs", R2::g_pModName)); + writePath /= R2::g_pHostState->m_levelName; writePath += ".ain"; // dump from memory @@ -349,20 +361,16 @@ void DumpAINInfo(CAI_Network* aiNetwork) writeStream.close(); } -typedef void (*CAI_NetworkBuilder__BuildType)(void* builder, CAI_Network* aiNetwork, void* unknown); -CAI_NetworkBuilder__BuildType CAI_NetworkBuilder__Build; - -void CAI_NetworkBuilder__BuildHook(void* builder, CAI_Network* aiNetwork, void* unknown) +AUTOHOOK(CAI_NetworkBuilder__Build, server.dll + 0x385E20, +void,, (void* builder, CAI_Network* aiNetwork, void* unknown)) { CAI_NetworkBuilder__Build(builder, aiNetwork, unknown); DumpAINInfo(aiNetwork); } -typedef void (*LoadAINFileType)(void* aimanager, void* buf, const char* filename); -LoadAINFileType LoadAINFile; - -void LoadAINFileHook(void* aimanager, void* buf, const char* filename) +AUTOHOOK(LoadAINFile, server.dll + 0x3933A0, +void,, (void* aimanager, void* buf, const char* filename)) { LoadAINFile(aimanager, buf, filename); @@ -373,28 +381,16 @@ void LoadAINFileHook(void* aimanager, void* buf, const char* filename) } } -void InitialiseBuildAINFileHooks(HMODULE baseAddress) +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"); - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x385E20, &CAI_NetworkBuilder__BuildHook, reinterpret_cast<LPVOID*>(&CAI_NetworkBuilder__Build)); - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x3933A0, &LoadAINFileHook, reinterpret_cast<LPVOID*>(&LoadAINFile)); - - pUnkStruct0Count = (int*)((char*)baseAddress + 0x1063BF8); - pppUnkNodeStruct0s = (UnkNodeStruct0***)((char*)baseAddress + 0x1063BE0); - - pUnkLinkStruct1Count = (int*)((char*)baseAddress + 0x1063AA8); - pppUnkStruct1s = (UnkLinkStruct1***)((char*)baseAddress + 0x1063A90); - pUnkServerMapversionGlobal = (char**)((char*)baseAddress + 0xBFBE08); - pMapName = (char*)baseAddress + 0x1053370; - - uintptr_t base = (uintptr_t)baseAddress; - - // remove a check that prevents a logging function in link generation from working - // due to the sheer amount of logging this is a massive perf hit to generation, but spewlog_enable 0 exists so whatever - NSMem::NOP(base + 0x3889B6, 6); - NSMem::NOP(base + 0x3889BF, 6); + pUnkStruct0Count = module.Offset(0x1063BF8).As<int*>(); + pppUnkNodeStruct0s = module.Offset(0x1063BE0).As<UnkNodeStruct0***>(); + pUnkLinkStruct1Count = module.Offset(0x1063AA8).As<int*>(); + pppUnkStruct1s = module.Offset(0x1063A90).As<UnkLinkStruct1***>(); + pUnkServerMapversionGlobal = module.Offset(0xBFBE08).As<char**>(); } diff --git a/NorthstarDLL/buildainfile.h b/NorthstarDLL/buildainfile.h deleted file mode 100644 index 9ec01f18..00000000 --- a/NorthstarDLL/buildainfile.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseBuildAINFileHooks(HMODULE baseAddress); diff --git a/NorthstarDLL/chatcommand.cpp b/NorthstarDLL/chatcommand.cpp index 2aa51737..05404363 100644 --- a/NorthstarDLL/chatcommand.cpp +++ b/NorthstarDLL/chatcommand.cpp @@ -1,12 +1,11 @@ #include "pch.h" #include "convar.h" #include "concommand.h" -#include "chatcommand.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 -typedef void(__fastcall* ClientSayTextType)(void* a1, const char* message, __int64 isIngameChat, bool isTeamChat); +typedef void(__fastcall* ClientSayTextType)(void* a1, const char* message, uint64_t isIngameChat, bool isTeamChat); ClientSayTextType ClientSayText; void ConCommand_say(const CCommand& args) @@ -29,9 +28,9 @@ void ConCommand_log(const CCommand& args) } } -void InitialiseChatCommands(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", ClientChatCommand, ConCommand, (CModule module)) { - ClientSayText = (ClientSayTextType)((char*)baseAddress + 0x54780); + ClientSayText = module.Offset(0x54780).As<ClientSayTextType>(); 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/chatcommand.h b/NorthstarDLL/chatcommand.h deleted file mode 100644 index 546d0126..00000000 --- a/NorthstarDLL/chatcommand.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseChatCommands(HMODULE baseAddress); diff --git a/NorthstarDLL/clientauthhooks.cpp b/NorthstarDLL/clientauthhooks.cpp index cdabf7f2..3f3ace8e 100644 --- a/NorthstarDLL/clientauthhooks.cpp +++ b/NorthstarDLL/clientauthhooks.cpp @@ -1,12 +1,9 @@ #include "pch.h" -#include "clientauthhooks.h" -#include "hookutils.h" -#include "gameutils.h" #include "masterserver.h" #include "convar.h" +#include "r2client.h" -typedef void (*AuthWithStryderType)(void* a1); -AuthWithStryderType AuthWithStryder; +AUTOHOOK_INIT() ConVar* Cvar_ns_has_agreed_to_send_token; @@ -15,33 +12,33 @@ const int NOT_DECIDED_TO_SEND_TOKEN = 0; const int AGREED_TO_SEND_TOKEN = 1; const int DISAGREED_TO_SEND_TOKEN = 2; -void AuthWithStryderHook(void* a1) +AUTOHOOK(AuthWithStryder, engine.dll + 0x1843A0, +void,, (void* a1)) { // 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_MasterServerManager->m_bOriginAuthWithMasterServerDone && Cvar_ns_has_agreed_to_send_token->GetInt() != DISAGREED_TO_SEND_TOKEN) + 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_MasterServerManager->m_bOriginAuthWithMasterServerInProgress) - g_MasterServerManager->AuthenticateOriginWithMasterServer(g_LocalPlayerUserID, g_LocalPlayerOriginToken); + !g_pMasterServerManager->m_bOriginAuthWithMasterServerInProgress) + g_pMasterServerManager->AuthenticateOriginWithMasterServer(R2::g_pLocalPlayerUserID, R2::g_pLocalPlayerOriginToken); // invalidate key so auth will fail - *g_LocalPlayerOriginToken = 0; + *R2::g_pLocalPlayerOriginToken = 0; } AuthWithStryder(a1); } -void InitialiseClientAuthHooks(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", ClientAuthHooks, ConVar, (CModule module)) { + AUTOHOOK_DISPATCH() + // 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"); - - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1843A0, &AuthWithStryderHook, reinterpret_cast<LPVOID*>(&AuthWithStryder)); } diff --git a/NorthstarDLL/clientauthhooks.h b/NorthstarDLL/clientauthhooks.h deleted file mode 100644 index 4d7e7ccf..00000000 --- a/NorthstarDLL/clientauthhooks.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void InitialiseClientAuthHooks(HMODULE baseAddress); diff --git a/NorthstarDLL/clientchathooks.cpp b/NorthstarDLL/clientchathooks.cpp index 74418c06..647e67f1 100644 --- a/NorthstarDLL/clientchathooks.cpp +++ b/NorthstarDLL/clientchathooks.cpp @@ -1,29 +1,20 @@ #include "pch.h" -#include "clientchathooks.h" -#include <rapidjson/document.h> #include "squirrel.h" #include "serverchathooks.h" #include "localchatwriter.h" -typedef void(__fastcall* CHudChat__AddGameLineType)(void* self, const char* message, int fromPlayerId, bool isteam, bool isdead); -CHudChat__AddGameLineType CHudChat__AddGameLine; +#include <rapidjson/document.h> -struct ChatTags -{ - bool whisper; - bool team; - bool dead; -}; +AUTOHOOK_INIT() -static void CHudChat__AddGameLineHook(void* self, const char* message, int inboxId, bool isTeam, bool isDead) +AUTOHOOK(CHudChat__AddGameLine, client.dll + 0x22E580, +void,, (void* self, const char* message, int inboxId, bool isTeam, bool isDead)) { // This hook is called for each HUD, but we only want our logic to run once. if (self != *CHudChat::allHuds) - { return; - } - if (g_ClientSquirrelManager->setupfunc("CHudChat_ProcessMessageStartThread") != SQRESULT_ERROR) + if (g_pSquirrel<ScriptContext::CLIENT>->setupfunc("CHudChat_ProcessMessageStartThread") != SQRESULT_ERROR) { int senderId = inboxId & CUSTOM_MESSAGE_INDEX_MASK; bool isAnonymous = senderId == 0; @@ -38,58 +29,53 @@ static void CHudChat__AddGameLineHook(void* self, const char* message, int inbox payload = message + 1; } - g_ClientSquirrelManager->pusharg((int)senderId - 1); - g_ClientSquirrelManager->pusharg(payload); - g_ClientSquirrelManager->pusharg(isTeam); - g_ClientSquirrelManager->pusharg(isDead); - g_ClientSquirrelManager->pusharg(type); - g_ClientSquirrelManager->call(5); + g_pSquirrel<ScriptContext::CLIENT>->pushinteger(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, (int)senderId - 1); + g_pSquirrel<ScriptContext::CLIENT>->pushstring(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, payload); + g_pSquirrel<ScriptContext::CLIENT>->pushbool(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, isTeam); + g_pSquirrel<ScriptContext::CLIENT>->pushbool(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, isDead); + g_pSquirrel<ScriptContext::CLIENT>->pushinteger(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, type); + g_pSquirrel<ScriptContext::CLIENT>->call(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, 5); } else - { for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next) - { CHudChat__AddGameLine(hud, message, inboxId, isTeam, isDead); - } - } } // void NSChatWrite( int context, string str ) -static SQRESULT SQ_ChatWrite(void* sqvm) +SQRESULT SQ_ChatWrite(HSquirrelVM* sqvm) { - int context = ClientSq_getinteger(sqvm, 1); - const char* str = ClientSq_getstring(sqvm, 2); + int context = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 1); + const char* str = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 2); LocalChatWriter((LocalChatWriter::Context)context).Write(str); - return SQRESULT_NOTNULL; + return SQRESULT_NULL; } // void NSChatWriteRaw( int context, string str ) -static SQRESULT SQ_ChatWriteRaw(void* sqvm) +SQRESULT SQ_ChatWriteRaw(HSquirrelVM* sqvm) { - int context = ClientSq_getinteger(sqvm, 1); - const char* str = ClientSq_getstring(sqvm, 2); + int context = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 1); + const char* str = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 2); LocalChatWriter((LocalChatWriter::Context)context).InsertText(str); - return SQRESULT_NOTNULL; + return SQRESULT_NULL; } // void NSChatWriteLine( int context, string str ) -static SQRESULT SQ_ChatWriteLine(void* sqvm) +SQRESULT SQ_ChatWriteLine(HSquirrelVM* sqvm) { - int context = ClientSq_getinteger(sqvm, 1); - const char* str = ClientSq_getstring(sqvm, 2); + int context = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 1); + const char* str = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 2); LocalChatWriter((LocalChatWriter::Context)context).WriteLine(str); - return SQRESULT_NOTNULL; + return SQRESULT_NULL; } -void InitialiseClientChatHooks(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientChatHooks, ClientSquirrel, (CModule module)) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x22E580, &CHudChat__AddGameLineHook, reinterpret_cast<LPVOID*>(&CHudChat__AddGameLine)); + AUTOHOOK_DISPATCH() - g_ClientSquirrelManager->AddFuncRegistration("void", "NSChatWrite", "int context, string text", "", SQ_ChatWrite); - g_ClientSquirrelManager->AddFuncRegistration("void", "NSChatWriteRaw", "int context, string text", "", SQ_ChatWriteRaw); - g_ClientSquirrelManager->AddFuncRegistration("void", "NSChatWriteLine", "int context, string text", "", SQ_ChatWriteLine); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration("void", "NSChatWrite", "int context, string text", "", SQ_ChatWrite); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration("void", "NSChatWriteRaw", "int context, string text", "", SQ_ChatWriteRaw); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration("void", "NSChatWriteLine", "int context, string text", "", SQ_ChatWriteLine); } diff --git a/NorthstarDLL/clientchathooks.h b/NorthstarDLL/clientchathooks.h deleted file mode 100644 index 79a1b3e2..00000000 --- a/NorthstarDLL/clientchathooks.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "pch.h" -#include "serverchathooks.h" - -void InitialiseClientChatHooks(HMODULE baseAddress); diff --git a/NorthstarDLL/clientruihooks.cpp b/NorthstarDLL/clientruihooks.cpp index a05ef217..9d6562d2 100644 --- a/NorthstarDLL/clientruihooks.cpp +++ b/NorthstarDLL/clientruihooks.cpp @@ -1,13 +1,12 @@ #include "pch.h" -#include "clientruihooks.h" #include "convar.h" -ConVar* Cvar_rui_drawEnable; +AUTOHOOK_INIT() -typedef char (*DrawRUIFuncType)(void* a1, float* a2); -DrawRUIFuncType DrawRUIFunc; +ConVar* Cvar_rui_drawEnable; -char DrawRUIFuncHook(void* a1, float* a2) +AUTOHOOK(DrawRUIFunc, engine.dll + 0xFC500, +bool,, (void* a1, float* a2)) { if (!Cvar_rui_drawEnable->GetBool()) return 0; @@ -15,10 +14,9 @@ char DrawRUIFuncHook(void* a1, float* a2) return DrawRUIFunc(a1, a2); } -void InitialiseEngineClientRUIHooks(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", RUI, ConVar, (CModule module)) { - Cvar_rui_drawEnable = new ConVar("rui_drawEnable", "1", FCVAR_CLIENTDLL, "Controls whether RUI should be drawn"); + AUTOHOOK_DISPATCH() - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xFC500, &DrawRUIFuncHook, reinterpret_cast<LPVOID*>(&DrawRUIFunc)); + Cvar_rui_drawEnable = new ConVar("rui_drawEnable", "1", FCVAR_CLIENTDLL, "Controls whether RUI should be drawn"); } diff --git a/NorthstarDLL/clientruihooks.h b/NorthstarDLL/clientruihooks.h deleted file mode 100644 index 57b79d9b..00000000 --- a/NorthstarDLL/clientruihooks.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void InitialiseEngineClientRUIHooks(HMODULE baseAddress); diff --git a/NorthstarDLL/clientvideooverrides.cpp b/NorthstarDLL/clientvideooverrides.cpp index 659bf23c..6d5c9053 100644 --- a/NorthstarDLL/clientvideooverrides.cpp +++ b/NorthstarDLL/clientvideooverrides.cpp @@ -1,21 +1,19 @@ #include "pch.h" -#include "clientvideooverrides.h" #include "modmanager.h" -#include "nsmem.h" -typedef void* (*BinkOpenType)(const char* path, uint32_t flags); -BinkOpenType BinkOpen; +AUTOHOOK_INIT() -void* BinkOpenHook(const char* path, uint32_t flags) +AUTOHOOK_PROCADDRESS(BinkOpen, bink2w64.dll, BinkOpen, +void*,, (const char* path, uint32_t flags)) { 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_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { - if (!mod.Enabled) + if (!mod.m_bEnabled) continue; if (std::find(mod.BinkVideos.begin(), mod.BinkVideos.end(), filename) != mod.BinkVideos.end()) @@ -25,23 +23,18 @@ void* BinkOpenHook(const char* path, uint32_t flags) if (fileOwner) { // create new path - fs::path binkPath(fileOwner->ModDirectory / "media" / filename); + fs::path binkPath(fileOwner->m_ModDirectory / "media" / filename); return BinkOpen(binkPath.string().c_str(), flags); } else return BinkOpen(path, flags); } -void InitialiseEngineClientVideoOverrides(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT("client.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 - NSMem::NOP((uintptr_t)baseAddress + 0x459AD, 6); - - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA("bink2w64.dll"), "BinkOpen")), - &BinkOpenHook, - reinterpret_cast<LPVOID*>(&BinkOpen)); + module.Offset(0x459AD).NOP(6); } diff --git a/NorthstarDLL/clientvideooverrides.h b/NorthstarDLL/clientvideooverrides.h deleted file mode 100644 index 8fdbba3f..00000000 --- a/NorthstarDLL/clientvideooverrides.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void InitialiseEngineClientVideoOverrides(HMODULE baseAddress); diff --git a/NorthstarDLL/concommand.cpp b/NorthstarDLL/concommand.cpp index 1bdda91b..6d77e137 100644 --- a/NorthstarDLL/concommand.cpp +++ b/NorthstarDLL/concommand.cpp @@ -1,27 +1,8 @@ #include "pch.h" #include "concommand.h" -#include "gameutils.h" #include "misccommands.h" -#include <iostream> - -typedef void (*ConCommandConstructorType)( - ConCommand* newCommand, const char* name, void (*callback)(const CCommand&), const char* helpString, int flags, void* parent); -ConCommandConstructorType conCommandConstructor; - -void RegisterConCommand(const char* name, void (*callback)(const CCommand&), 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 InitialiseConCommands(HMODULE baseAddress) -{ - conCommandConstructor = (ConCommandConstructorType)((char*)baseAddress + 0x415F60); - AddMiscConCommands(); -} +#include <iostream> //----------------------------------------------------------------------------- // Purpose: Returns true if this is a command @@ -57,7 +38,7 @@ bool ConCommandBase::IsRegistered(void) const //----------------------------------------------------------------------------- bool ConCommandBase::IsFlagSet(int nFlags) const { - return false; // !TODO: Returning false on every query? (original implementation in Northstar before ConCommandBase refactor) + return m_nFlags & nFlags; } //----------------------------------------------------------------------------- @@ -138,3 +119,33 @@ char* ConCommandBase::CopyString(const char* szFrom) const } 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).As<ConCommandConstructorType>(); + AddMiscConCommands(); +} diff --git a/NorthstarDLL/concommand.h b/NorthstarDLL/concommand.h index 15e289d8..a02604f2 100644 --- a/NorthstarDLL/concommand.h +++ b/NorthstarDLL/concommand.h @@ -72,6 +72,19 @@ 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 (*__fastcall FnCommandCompletionCallback)(const char* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); + // From r5reloaded class ConCommandBase { @@ -113,8 +126,8 @@ class ConCommand : public ConCommandBase void Init(void); bool IsCommand(void) const; - void* m_pCommandCallback {}; // 0x0040 <- starts from 0x40 since we inherit ConCommandBase. - void* m_pCompletionCallback {}; // 0x0048 <- defaults to sub_180417410 ('xor eax, eax'). + 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 @@ -122,6 +135,5 @@ class ConCommand : public ConCommandBase }; // Size: 0x0060 void RegisterConCommand(const char* name, void (*callback)(const CCommand&), const char* helpString, int flags); -void InitialiseConCommands(HMODULE baseAddress); - -#define MAKE_CONCMD(name, helpStr, flags, fn) RegisterConCommand(name, fn, helpStr, flags); +void RegisterConCommand( + const char* name, void (*callback)(const CCommand&), const char* helpString, int flags, FnCommandCompletionCallback completionCallback); diff --git a/NorthstarDLL/context.cpp b/NorthstarDLL/context.cpp deleted file mode 100644 index 19ee85a3..00000000 --- a/NorthstarDLL/context.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "pch.h" -#include "context.h" - -const char* GetContextName(ScriptContext context) -{ - if (context == ScriptContext::CLIENT) - return "CLIENT"; - else if (context == ScriptContext::SERVER) - return "SERVER"; - else if (context == ScriptContext::UI) - return "UI"; - - return ""; -} diff --git a/NorthstarDLL/context.h b/NorthstarDLL/context.h deleted file mode 100644 index d872f738..00000000 --- a/NorthstarDLL/context.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -enum class ScriptContext : int -{ - SERVER, - CLIENT, - UI, - NONE -}; - -const char* GetContextName(ScriptContext context); diff --git a/NorthstarDLL/convar.cpp b/NorthstarDLL/convar.cpp index bf8edbf9..c8f63922 100644 --- a/NorthstarDLL/convar.cpp +++ b/NorthstarDLL/convar.cpp @@ -1,13 +1,11 @@ -#include <float.h> - #include "pch.h" #include "bits.h" #include "cvar.h" #include "convar.h" -#include "hookutils.h" -#include "gameutils.h" #include "sourceinterface.h" +#include <float.h> + typedef void (*ConVarRegisterType)( ConVar* pConVar, const char* pszName, @@ -27,25 +25,19 @@ ConVarMallocType conVarMalloc; void* g_pConVar_Vtable = nullptr; void* g_pIConVar_Vtable = nullptr; -typedef bool (*CvarIsFlagSetType)(ConVar* self, int flags); -CvarIsFlagSetType CvarIsFlagSet; - //----------------------------------------------------------------------------- // Purpose: ConVar interface initialization //----------------------------------------------------------------------------- -void InitialiseConVars(HMODULE baseAddress) +ON_DLL_LOAD("engine.dll", ConVar, (CModule module)) { - conVarMalloc = (ConVarMallocType)((char*)baseAddress + 0x415C20); - conVarRegister = (ConVarRegisterType)((char*)baseAddress + 0x417230); - - g_pConVar_Vtable = (char*)baseAddress + 0x67FD28; - g_pIConVar_Vtable = (char*)baseAddress + 0x67FDC8; + conVarMalloc = module.Offset(0x415C20).As<ConVarMallocType>(); + conVarRegister = module.Offset(0x417230).As<ConVarRegisterType>(); - g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007"); - g_pCVar = *g_pCVarInterface; + g_pConVar_Vtable = module.Offset(0x67FD28); + g_pIConVar_Vtable = module.Offset(0x67FDC8); - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417FA0, &ConVar::IsFlagSet, reinterpret_cast<LPVOID*>(&CvarIsFlagSet)); + R2::g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007"); + R2::g_pCVar = *R2::g_pCVarInterface; } //----------------------------------------------------------------------------- @@ -74,7 +66,7 @@ ConVar::ConVar( float fMin, bool bMax, float fMax, - void* pCallback) + FnChangeCallback_t pCallback) { spdlog::info("Registering Convar {}", pszName); @@ -476,16 +468,12 @@ bool ConVar::IsCommand(void) const //----------------------------------------------------------------------------- // Purpose: Test each ConVar query before setting the value. -// Input : *pConVar - nFlags +// Input : nFlags // Output : False if change is permitted, true if not. //----------------------------------------------------------------------------- -bool ConVar::IsFlagSet(ConVar* pConVar, int nFlags) +bool ConVar::IsFlagSet(int nFlags) const { - // unrestrict FCVAR_DEVELOPMENTONLY and FCVAR_HIDDEN - if (pConVar && (nFlags == FCVAR_DEVELOPMENTONLY || nFlags == FCVAR_HIDDEN)) - return false; - - return CvarIsFlagSet(pConVar, nFlags); + return m_ConCommandBase.m_nFlags & nFlags; } //----------------------------------------------------------------------------- diff --git a/NorthstarDLL/convar.h b/NorthstarDLL/convar.h index 15f1f562..5a9aa4eb 100644 --- a/NorthstarDLL/convar.h +++ b/NorthstarDLL/convar.h @@ -58,15 +58,59 @@ // 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 //----------------------------------------------------------------------------- @@ -74,6 +118,8 @@ class ConCommandBase; class ConCommand; class ConVar; +typedef void (*FnChangeCallback_t)(ConVar* var, const char* pOldValue, float flOldValue); + //----------------------------------------------------------------------------- // Purpose: A console variable //----------------------------------------------------------------------------- @@ -91,7 +137,7 @@ class ConVar float fMin, bool bMax, float fMax, - void* pCallback); + FnChangeCallback_t pCallback); ~ConVar(void); const char* GetBaseName(void) const; @@ -125,7 +171,7 @@ class ConVar bool IsRegistered(void) const; bool IsCommand(void) const; - static bool IsFlagSet(ConVar* pConVar, int nFlags); + bool IsFlagSet(int nFlags) const; struct CVValue_t { @@ -145,5 +191,3 @@ class ConVar void* m_pMalloc {}; // 0x0070 char m_pPad80[10] {}; // 0x0080 }; // Size: 0x0080 - -void InitialiseConVars(HMODULE baseAddress); diff --git a/NorthstarDLL/crashhandler.cpp b/NorthstarDLL/crashhandler.cpp new file mode 100644 index 00000000..8e083078 --- /dev/null +++ b/NorthstarDLL/crashhandler.cpp @@ -0,0 +1,216 @@ +#include "pch.h" +#include "crashhandler.h" +#include "dedicated.h" +#include "nsprefix.h" + +#include <minidumpapiset.h> + +HANDLE hExceptionFilter; + +long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) +{ + static bool logged = false; + if (logged) + return EXCEPTION_CONTINUE_SEARCH; + + if (!IsDebuggerPresent()) + { + const DWORD exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode; + if (exceptionCode != EXCEPTION_ACCESS_VIOLATION && exceptionCode != EXCEPTION_ARRAY_BOUNDS_EXCEEDED && + exceptionCode != EXCEPTION_DATATYPE_MISALIGNMENT && exceptionCode != EXCEPTION_FLT_DENORMAL_OPERAND && + exceptionCode != EXCEPTION_FLT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_FLT_INEXACT_RESULT && + exceptionCode != EXCEPTION_FLT_INVALID_OPERATION && exceptionCode != EXCEPTION_FLT_OVERFLOW && + exceptionCode != EXCEPTION_FLT_STACK_CHECK && exceptionCode != EXCEPTION_FLT_UNDERFLOW && + exceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION && exceptionCode != EXCEPTION_IN_PAGE_ERROR && + exceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_INT_OVERFLOW && + exceptionCode != EXCEPTION_INVALID_DISPOSITION && exceptionCode != EXCEPTION_NONCONTINUABLE_EXCEPTION && + exceptionCode != EXCEPTION_PRIV_INSTRUCTION && exceptionCode != EXCEPTION_STACK_OVERFLOW) + return EXCEPTION_CONTINUE_SEARCH; + + std::stringstream exceptionCause; + exceptionCause << "Cause: "; + switch (exceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_IN_PAGE_ERROR: + { + exceptionCause << "Access Violation" << std::endl; + + auto exceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0]; + auto exceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1]; + + if (!exceptionInfo0) + exceptionCause << "Attempted to read from: 0x" << (void*)exceptionInfo1; + else if (exceptionInfo0 == 1) + exceptionCause << "Attempted to write to: 0x" << (void*)exceptionInfo1; + else if (exceptionInfo0 == 8) + exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex << exceptionInfo1; + else + exceptionCause << "Unknown access violation at: 0x" << (void*)exceptionInfo1; + + break; + } + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + exceptionCause << "Array bounds exceeded"; + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + exceptionCause << "Datatype misalignment"; + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + exceptionCause << "Denormal operand"; + break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + exceptionCause << "Divide by zero (float)"; + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + exceptionCause << "Divide by zero (int)"; + break; + case EXCEPTION_FLT_INEXACT_RESULT: + exceptionCause << "Inexact result"; + break; + case EXCEPTION_FLT_INVALID_OPERATION: + exceptionCause << "Invalid operation"; + break; + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_INT_OVERFLOW: + exceptionCause << "Numeric overflow"; + break; + case EXCEPTION_FLT_UNDERFLOW: + exceptionCause << "Numeric underflow"; + break; + case EXCEPTION_FLT_STACK_CHECK: + exceptionCause << "Stack check"; + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + exceptionCause << "Illegal instruction"; + break; + case EXCEPTION_INVALID_DISPOSITION: + exceptionCause << "Invalid disposition"; + break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + exceptionCause << "Noncontinuable exception"; + break; + case EXCEPTION_PRIV_INSTRUCTION: + exceptionCause << "Priviledged instruction"; + break; + case EXCEPTION_STACK_OVERFLOW: + exceptionCause << "Stack overflow"; + break; + default: + exceptionCause << "Unknown"; + break; + } + + void* exceptionAddress = exceptionInfo->ExceptionRecord->ExceptionAddress; + + HMODULE crashedModuleHandle; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(exceptionAddress), &crashedModuleHandle); + + MODULEINFO crashedModuleInfo; + GetModuleInformation(GetCurrentProcess(), crashedModuleHandle, &crashedModuleInfo, sizeof(crashedModuleInfo)); + + char crashedModuleFullName[MAX_PATH]; + GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, crashedModuleFullName, MAX_PATH); + char* crashedModuleName = strrchr(crashedModuleFullName, '\\') + 1; + + DWORD64 crashedModuleOffset = ((DWORD64)exceptionAddress) - ((DWORD64)crashedModuleInfo.lpBaseOfDll); + CONTEXT* exceptionContext = exceptionInfo->ContextRecord; + + spdlog::error("Northstar has crashed! a minidump has been written and exception info is available below:"); + spdlog::error(exceptionCause.str()); + spdlog::error("At: {} + {}", crashedModuleName, (void*)crashedModuleOffset); + + PVOID framesToCapture[62]; + int frames = RtlCaptureStackBackTrace(0, 62, framesToCapture, NULL); + for (int i = 0; i < frames; i++) + { + HMODULE backtraceModuleHandle; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(framesToCapture[i]), &backtraceModuleHandle); + + char backtraceModuleFullName[MAX_PATH]; + GetModuleFileNameExA(GetCurrentProcess(), backtraceModuleHandle, backtraceModuleFullName, MAX_PATH); + char* backtraceModuleName = strrchr(backtraceModuleFullName, '\\') + 1; + + void* actualAddress = (void*)framesToCapture[i]; + void* relativeAddress = (void*)(uintptr_t(actualAddress) - uintptr_t(backtraceModuleHandle)); + + spdlog::error(" {} + {} ({})", backtraceModuleName, relativeAddress, actualAddress); + } + + spdlog::error("RAX: 0x{0:x}", exceptionContext->Rax); + spdlog::error("RBX: 0x{0:x}", exceptionContext->Rbx); + spdlog::error("RCX: 0x{0:x}", exceptionContext->Rcx); + spdlog::error("RDX: 0x{0:x}", exceptionContext->Rdx); + spdlog::error("RSI: 0x{0:x}", exceptionContext->Rsi); + spdlog::error("RDI: 0x{0:x}", exceptionContext->Rdi); + spdlog::error("RBP: 0x{0:x}", exceptionContext->Rbp); + spdlog::error("RSP: 0x{0:x}", exceptionContext->Rsp); + spdlog::error("R8: 0x{0:x}", exceptionContext->R8); + spdlog::error("R9: 0x{0:x}", exceptionContext->R9); + spdlog::error("R10: 0x{0:x}", exceptionContext->R10); + spdlog::error("R11: 0x{0:x}", exceptionContext->R11); + spdlog::error("R12: 0x{0:x}", exceptionContext->R12); + spdlog::error("R13: 0x{0:x}", exceptionContext->R13); + spdlog::error("R14: 0x{0:x}", exceptionContext->R14); + spdlog::error("R15: 0x{0:x}", exceptionContext->R15); + + time_t time = std::time(nullptr); + tm currentTime = *std::localtime(&time); + std::stringstream stream; + stream << std::put_time(¤tTime, (GetNorthstarPrefix() + "/logs/nsdump%Y-%m-%d %H-%M-%S.dmp").c_str()); + + auto 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 = exceptionInfo; + 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()); + + if (!IsDedicatedServer()) + MessageBoxA( + 0, "Northstar has crashed! Crash info can be found in R2Northstar/logs", "Northstar has crashed!", MB_ICONERROR | MB_OK); + } + + logged = true; + return EXCEPTION_EXECUTE_HANDLER; +} + +BOOL WINAPI ConsoleHandlerRoutine(DWORD eventCode) +{ + switch (eventCode) + { + case CTRL_CLOSE_EVENT: + // User closed console, shut everything down + spdlog::info("Exiting due to console close..."); + RemoveCrashHandler(); + exit(EXIT_SUCCESS); + return FALSE; + } + + return TRUE; +} + +void InitialiseCrashHandler() +{ + hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter); + SetConsoleCtrlHandler(ConsoleHandlerRoutine, true); +} + +void RemoveCrashHandler() +{ + RemoveVectoredExceptionHandler(hExceptionFilter); +} diff --git a/NorthstarDLL/crashhandler.h b/NorthstarDLL/crashhandler.h new file mode 100644 index 00000000..e0dc6906 --- /dev/null +++ b/NorthstarDLL/crashhandler.h @@ -0,0 +1,4 @@ +#pragma once + +void InitialiseCrashHandler(); +void RemoveCrashHandler(); diff --git a/NorthstarDLL/cvar.cpp b/NorthstarDLL/cvar.cpp index 04594b8f..787790be 100644 --- a/NorthstarDLL/cvar.cpp +++ b/NorthstarDLL/cvar.cpp @@ -23,5 +23,9 @@ std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap() return allConVars; } -SourceInterface<CCvar>* g_pCVarInterface; -CCvar* g_pCVar; +// use the R2 namespace for game funcs +namespace R2 +{ + SourceInterface<CCvar>* g_pCVarInterface; + CCvar* g_pCVar; +} // namespace R2 diff --git a/NorthstarDLL/cvar.h b/NorthstarDLL/cvar.h index a39df387..e65e5145 100644 --- a/NorthstarDLL/cvar.h +++ b/NorthstarDLL/cvar.h @@ -35,5 +35,9 @@ class CCvar std::unordered_map<std::string, ConCommandBase*> DumpToMap(); }; -extern SourceInterface<CCvar>* g_pCVarInterface; -extern CCvar* g_pCVar; +// use the R2 namespace for game funcs +namespace R2 +{ + extern SourceInterface<CCvar>* g_pCVarInterface; + extern CCvar* g_pCVar; +} // namespace R2 diff --git a/NorthstarDLL/debugoverlay.cpp b/NorthstarDLL/debugoverlay.cpp index 96da2f38..2d2d03e0 100644 --- a/NorthstarDLL/debugoverlay.cpp +++ b/NorthstarDLL/debugoverlay.cpp @@ -1,17 +1,9 @@ #include "pch.h" -#include "debugoverlay.h" #include "dedicated.h" #include "cvar.h" +#include "vector.h" -struct Vector3 -{ - float x, y, z; -}; - -struct QAngle -{ - float x, y, z, w; -}; +AUTOHOOK_INIT() enum OverlayType_t { @@ -40,7 +32,7 @@ struct OverlayBase_t int m_nServerCount; // Latch server count, too float m_flEndTime; // When does this box go away OverlayBase_t* m_pNextOverlay; - __int64 m_pUnk; + void* m_pUnk; }; struct OverlayLine_t : public OverlayBase_t @@ -76,37 +68,16 @@ struct OverlayBox_t : public OverlayBase_t int a; }; -// this is in cvar.h, don't need it here -/*class Color -{ - public: - Color(int r, int g, int b, int a) - { - _color[0] = (unsigned char)r; - _color[1] = (unsigned char)g; - _color[2] = (unsigned char)b; - _color[3] = (unsigned char)a; - } - - private: - unsigned char _color[4]; -};*/ - static HMODULE sEngineModule; -typedef void (*DrawOverlayType)(OverlayBase_t* a1); -DrawOverlayType DrawOverlay; - typedef void (*RenderLineType)(Vector3 v1, Vector3 v2, Color c, bool bZBuffer); static RenderLineType RenderLine; - typedef void (*RenderBoxType)(Vector3 vOrigin, QAngle angles, Vector3 vMins, Vector3 vMaxs, Color c, bool bZBuffer, bool bInsideOut); static RenderBoxType RenderBox; - static RenderBoxType RenderWireframeBox; -// engine.dll+0xABCB0 -void __fastcall DrawOverlayHook(OverlayBase_t* pOverlay) +AUTOHOOK(DrawOverlay, engine.dll + 0xABCB0, +void, __fastcall, (OverlayBase_t * pOverlay)) { EnterCriticalSection((LPCRITICAL_SECTION)((char*)sEngineModule + 0x10DB0A38)); // s_OverlayMutex @@ -151,24 +122,17 @@ void __fastcall DrawOverlayHook(OverlayBase_t* pOverlay) LeaveCriticalSection((LPCRITICAL_SECTION)((char*)sEngineModule + 0x10DB0A38)); } -void InitialiseDebugOverlay(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", DebugOverlay, ConVar, (CModule module)) { - if (IsDedicatedServer()) - return; - - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xABCB0, &DrawOverlayHook, reinterpret_cast<LPVOID*>(&DrawOverlay)); - - RenderLine = reinterpret_cast<RenderLineType>((char*)baseAddress + 0x192A70); - - RenderBox = reinterpret_cast<RenderBoxType>((char*)baseAddress + 0x192520); - - RenderWireframeBox = reinterpret_cast<RenderBoxType>((char*)baseAddress + 0x193DA0); + AUTOHOOK_DISPATCH() - sEngineModule = baseAddress; + RenderLine = module.Offset(0x192A70).As<RenderLineType>(); + RenderBox = module.Offset(0x192520).As<RenderBoxType>(); + RenderWireframeBox = module.Offset(0x193DA0).As<RenderBoxType>(); + sEngineModule = reinterpret_cast<HMODULE>(module.m_nAddress); // not in g_pCVar->FindVar by this point for whatever reason, so have to get from memory - ConVar* Cvar_enable_debug_overlays = (ConVar*)((char*)baseAddress + 0x10DB0990); + ConVar* Cvar_enable_debug_overlays = module.Offset(0x10DB0990).As<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/debugoverlay.h b/NorthstarDLL/debugoverlay.h deleted file mode 100644 index 40b752f3..00000000 --- a/NorthstarDLL/debugoverlay.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseDebugOverlay(HMODULE baseAddress); diff --git a/NorthstarDLL/dedicated.cpp b/NorthstarDLL/dedicated.cpp index d2a89f6a..42af9456 100644 --- a/NorthstarDLL/dedicated.cpp +++ b/NorthstarDLL/dedicated.cpp @@ -1,9 +1,16 @@ #include "pch.h" #include "dedicated.h" -#include "hookutils.h" -#include "gameutils.h" +#include "tier0.h" +#include "playlist.h" +#include "r2engine.h" +#include "hoststate.h" #include "serverauthentication.h" #include "masterserver.h" +#include "printcommand.h" + +AUTOHOOK_INIT() + +using namespace R2; bool IsDedicatedServer() { @@ -30,35 +37,26 @@ struct CDedicatedExports void Sys_Printf(CDedicatedExports* dedicated, const char* msg) { - spdlog::info("[DEDICATED PRINT] {}", msg); + spdlog::info("[DEDICATED SERVER] {}", msg); } -typedef void (*CHostState__InitType)(CHostState* self); - void RunServer(CDedicatedExports* dedicated) { spdlog::info("CDedicatedExports::RunServer(): starting"); - spdlog::info(CommandLine()->GetCmdLine()); + spdlog::info(Tier0::CommandLine()->GetCmdLine()); // initialise engine g_pEngine->Frame(); // add +map if not 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()->AppendParm("+map", g_pCVar->FindVar("match_defaultMap")->GetString()); + if (!Tier0::CommandLine()->CheckParm("+map")) + Tier0::CommandLine()->AppendParm("+map", g_pCVar->FindVar("match_defaultMap")->GetString()); - // run server autoexec and re-run commandline - Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode); + // re-run commandline Cbuf_AddText(Cbuf_GetCurrentPlayer(), "stuffcmds", cmd_source_t::kCommandSrcCode); Cbuf_Execute(); - // ensure playlist initialises right, if we've not explicitly called setplaylist - SetCurrentPlaylist(GetCurrentPlaylistName()); - - // note: we no longer manually set map and hoststate to start server in g_pHostState, we just use +map which seems to initialise stuff - // better - // get tickinterval ConVar* Cvar_base_tickinterval_mp = g_pCVar->FindVar("base_tickinterval_mp"); @@ -66,43 +64,31 @@ void RunServer(CDedicatedExports* dedicated) double frameTitle = 0; while (g_pEngine->m_nQuitting == EngineQuitState::QUIT_NOTQUITTING) { - double frameStart = Plat_FloatTime(); + double frameStart = Tier0::Plat_FloatTime(); g_pEngine->Frame(); - // only update the title after at least 500ms since the last update - if ((frameStart - frameTitle) > 0.5) - { - frameTitle = frameStart; - - // this way of getting playercount/maxplayers honestly really sucks, but not got any other methods of doing it rn - const char* maxPlayers = GetCurrentPlaylistVar("max_players", false); - if (!maxPlayers) - maxPlayers = "6"; - - SetConsoleTitleA(fmt::format( - "{} - {} {}/{} players ({})", - g_MasterServerManager->ns_auth_srvName, - g_pHostState->m_levelName, - g_ServerAuthenticationManager->m_additionalPlayerData.size(), - maxPlayers, - GetCurrentPlaylistName()) - .c_str()); - } - std::this_thread::sleep_for(std::chrono::duration<double, std::ratio<1>>( - Cvar_base_tickinterval_mp->GetFloat() - fmin(Plat_FloatTime() - frameStart, 0.25))); + Cvar_base_tickinterval_mp->GetFloat() - fmin(Tier0::Plat_FloatTime() - frameStart, 0.25))); } } -typedef bool (*IsGameActiveWindowType)(); -IsGameActiveWindowType IsGameActiveWindow; -bool IsGameActiveWindowHook() +// use server presence to update window title +class DedicatedConsoleServerPresence : public ServerPresenceReporter { - return true; -} + 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) @@ -121,139 +107,102 @@ DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter) { 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; } -#include "nsmem.h" -void InitialiseDedicated(HMODULE engineAddress) +AUTOHOOK(IsGameActiveWindow, engine.dll + 0x1CDC80, bool, , ()) +{ + return true; +} + +ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModule module)) { spdlog::info("InitialiseDedicated"); - uintptr_t ea = (uintptr_t)engineAddress; + AUTOHOOK_DISPATCH_MODULE("engine.dll") - { - // Host_Init - // prevent a particle init that relies on client dll - NSMem::NOP(ea + 0x156799, 5); - } + // 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 - auto ptr = ea + 0x1C4EBD; + MemoryAddress base = module.Offset(0x1C4EBD); // cmp => mov - NSMem::BytePatch(ptr + 1, "C6 87"); + base.Offset(1).Patch("C6 87"); // 00 => 01 - NSMem::BytePatch(ptr + 7, "01"); + base.Offset(7).Patch("01"); } - { - // Some init that i'm not sure of that crashes - // nop the call to it - NSMem::NOP(ea + 0x156A63, 5); - } - - { - // runframeserver - // nop some access violations - NSMem::NOP(ea + 0x159819, 17); - } - - { - NSMem::NOP(ea + 0x156B4C, 7); + // Some init that i'm not sure of that crashes + // nop the call to it + module.Offset(0x156A63).NOP(5); - // 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); + // runframeserver + // nop some access violations + module.Offset(0x159819).NOP(17); - NSMem::NOP(ea + 0x156B4C + 15, 9); - } + module.Offset(0x156B4C).NOP(7); - { - // HostState_State_NewGame - // nop an access violation - NSMem::NOP(ea + 0xB934C, 9); - } + // 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); - { - // CEngineAPI::Connect - // remove call to Shader_Connect - NSMem::NOP(ea + 0x1C4D7D, 5); - } + // HostState_State_NewGame + // nop an access violation + module.Offset(0xB934C).NOP(9); - // currently does not work, crashes stuff, likely gotta keep this here - //{ - // // CEngineAPI::Connect - // // remove calls to register ui rpak asset types - // NSMem::NOP(ea + 0x1C4E07, 5); - //} + // CEngineAPI::Connect + // remove call to Shader_Connect + module.Offset(0x1C4D7D).NOP(5); - { - // Host_Init - // remove call to ui loading stuff - NSMem::NOP(ea + 0x156595, 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 - NSMem::NOP(ea + 0x15A0BB, 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 - NSMem::NOP(ea + 0x159BF3, 5); - } + // RunFrameServer + // nop a function that access violations + module.Offset(0x159BF3).NOP(5); - { - // func that checks if origin is inited - // always return 1 - NSMem::BytePatch( - ea + 0x183B70, - { - 0xB0, - 0x01, // mov al,01 - 0xC3 // ret - }); - } + // 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 - NSMem::NOP(ea + 0x1552ED, 16); - } + // HostState_State_ChangeLevel + // nop clientinterface call + module.Offset(0x1552ED).NOP(16); - { - // HostState_State_ChangeLevel - // nop clientinterface call - NSMem::NOP(ea + 0x155363, 16); - } + // HostState_State_ChangeLevel + // nop clientinterface call + module.Offset(0x155363).NOP(16); - // note: previously had DisableDedicatedWindowCreation patches here, but removing those rn since they're all shit and unstable and bad - // and such check commit history if any are needed for reimplementation - { - // IVideoMode::CreateGameWindow - // nop call to ShowWindow - NSMem::NOP(ea + 0x1CD146, 5); - } + // 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; - CDedicatedExports** exports = (CDedicatedExports**)((char*)engineAddress + 0x13F0B668); - *exports = dedicatedExports; - - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)engineAddress + 0x1CDC80, &IsGameActiveWindowHook, reinterpret_cast<LPVOID*>(&IsGameActiveWindow)); + *module.Offset(0x13F0B668).As<CDedicatedExports**>() = dedicatedExports; // extra potential patches: // nop engine.dll+1c67d1 and +1c67d8 to skip videomode creategamewindow @@ -265,15 +214,19 @@ void InitialiseDedicated(HMODULE engineAddress) // 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"); + Tier0::CommandLine()->AppendParm("-nomenuvid", 0); + Tier0::CommandLine()->AppendParm("-nosound", 0); + Tier0::CommandLine()->AppendParm("-windowed", 0); + Tier0::CommandLine()->AppendParm("-nomessagebox", 0); + Tier0::CommandLine()->AppendParm("+host_preload_shaders", "0"); + Tier0::CommandLine()->AppendParm("+net_usesocketsforloopback", "1"); + + // use presence reporter for console title + DedicatedConsoleServerPresence* presenceReporter = new DedicatedConsoleServerPresence; + g_pServerPresence->AddPresenceReporter(presenceReporter); // Disable Quick Edit mode to reduce chance of user unintentionally hanging their server by selecting something. - if (!CommandLine()->CheckParm("-bringbackquickedit")) + if (!Tier0::CommandLine()->CheckParm("-bringbackquickedit")) { HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE); DWORD mode = 0; @@ -295,35 +248,40 @@ void InitialiseDedicated(HMODULE engineAddress) spdlog::info("Quick Edit enabled by user request"); // create console input thread - if (!CommandLine()->CheckParm("-noconsoleinput")) + if (!Tier0::CommandLine()->CheckParm("-noconsoleinput")) consoleInputThreadHandle = CreateThread(0, 0, ConsoleInputThread, 0, 0, NULL); else spdlog::info("Console input disabled by user request"); } -void InitialiseDedicatedOrigin(HMODULE baseAddress) +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 - - NSMem::BytePatch( - (uintptr_t)GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"), - { - 0xC3 // ret - }); + module.GetExport("Tier0_InitOrigin").Patch("C3"); } -typedef void (*PrintFatalSquirrelErrorType)(void* sqvm); -PrintFatalSquirrelErrorType PrintFatalSquirrelError; -void PrintFatalSquirrelErrorHook(void* sqvm) +AUTOHOOK(PrintSquirrelError, server.dll + 0x794D0, +void, , (void* sqvm)) { - PrintFatalSquirrelError(sqvm); - g_pEngine->m_nQuitting = EngineQuitState::QUIT_TODESKTOP; + PrintSquirrelError(sqvm); + + // close dedicated server if a fatal error is hit + static ConVar* Cvar_fatal_script_errors = g_pCVar->FindVar("fatal_script_errors"); + if (Cvar_fatal_script_errors->GetBool()) + abort(); } -void InitialiseDedicatedServerGameDLL(HMODULE baseAddress) +ON_DLL_LOAD_DEDI("server.dll", DedicatedServerGameDLL, (CModule module)) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, baseAddress + 0x794D0, &PrintFatalSquirrelErrorHook, reinterpret_cast<LPVOID*>(&PrintFatalSquirrelError)); + AUTOHOOK_DISPATCH_MODULE(server.dll) + + if (Tier0::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.h b/NorthstarDLL/dedicated.h index 0a2d247e..82806763 100644 --- a/NorthstarDLL/dedicated.h +++ b/NorthstarDLL/dedicated.h @@ -1,7 +1,3 @@ #pragma once bool IsDedicatedServer(); - -void InitialiseDedicated(HMODULE moduleAddress); -void InitialiseDedicatedOrigin(HMODULE baseAddress); -void InitialiseDedicatedServerGameDLL(HMODULE baseAddress); diff --git a/NorthstarDLL/dedicatedmaterialsystem.cpp b/NorthstarDLL/dedicatedmaterialsystem.cpp index 47440b7a..18bf6a57 100644 --- a/NorthstarDLL/dedicatedmaterialsystem.cpp +++ b/NorthstarDLL/dedicatedmaterialsystem.cpp @@ -1,24 +1,12 @@ +#pragma once #include "pch.h" #include "dedicated.h" -#include "dedicatedmaterialsystem.h" -#include "hookutils.h" -#include "gameutils.h" -#include "nsmem.h" +#include "tier0.h" -typedef HRESULT (*__stdcall D3D11CreateDeviceType)( - void* pAdapter, - int DriverType, - HMODULE Software, - UINT Flags, - int* pFeatureLevels, - UINT FeatureLevels, - UINT SDKVersion, - void** ppDevice, - int* pFeatureLevel, - void** ppImmediateContext); -D3D11CreateDeviceType D3D11CreateDevice; +AUTOHOOK_INIT() -HRESULT __stdcall D3D11CreateDeviceHook( +AUTOHOOK(D3D11CreateDevice, materialsystem_dx11.dll + 0xD9A0E, +HRESULT, __stdcall, ( void* pAdapter, int DriverType, HMODULE Software, @@ -28,95 +16,25 @@ HRESULT __stdcall D3D11CreateDeviceHook( UINT SDKVersion, void** ppDevice, int* pFeatureLevel, - void** ppImmediateContext) + void** ppImmediateContext)) { // 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 - // atm, i think the play might be to run d3d in software, and then just stub out any calls that allocate memory/use alot of resources - // (e.g. createtexture and that sorta thing) // 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")) + if (Tier0::CommandLine()->CheckParm("-softwared3d11")) DriverType = 5; // D3D_DRIVER_TYPE_WARP return D3D11CreateDevice( pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, ppDevice, pFeatureLevel, ppImmediateContext); } -void InitialiseDedicatedMaterialSystem(HMODULE baseAddress) -{ - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xD9A0E, &D3D11CreateDeviceHook, reinterpret_cast<LPVOID*>(&D3D11CreateDevice)); - - // not using these for now since they're related to nopping renderthread/gamewindow i.e. very hard - //{ - // // function that launches renderthread - // char* ptr = (char*)baseAddress + 0x87047; - // TempReadWrite rw(ptr); - // - // // make it not launch renderthread - // *ptr = (char)0x90; - // *(ptr + 1) = (char)0x90; - // *(ptr + 2) = (char)0x90; - // *(ptr + 3) = (char)0x90; - // *(ptr + 4) = (char)0x90; - // *(ptr + 5) = (char)0x90; - //} - // - //{ - // // some function that waits on renderthread job - // char* ptr = (char*)baseAddress + 0x87d00; - // TempReadWrite rw(ptr); - // - // // return immediately - // *ptr = (char)0xC3; - //} - - { - // CMaterialSystem::FindMaterial - // make the game always use the error material - NSMem::BytePatch((uintptr_t)baseAddress + 0x5F0F1, {0xE9, 0x34, 0x03, 0x00}); - } - - // previously had DisableDedicatedWindowCreation stuff here, removing for now since shit and unstable - // check commit history if needed -} - -typedef void* (*PakLoadAPI__LoadRpakType)(char* filename, void* unknown, int flags); -PakLoadAPI__LoadRpakType PakLoadAPI__LoadRpak; - -void* PakLoadAPI__LoadRpakHook(char* filename, void* unknown, int flags) -{ - spdlog::info("PakLoadAPI__LoadRpakHook {}", filename); - - // on dedi, don't load any paks that aren't required - if (strncmp(filename, "common", 6)) - return 0; - - return PakLoadAPI__LoadRpak(filename, unknown, flags); -} - -typedef void* (*PakLoadAPI__LoadRpak2Type)(char* filename, void* unknown, int flags, void* callback, void* callback2); -PakLoadAPI__LoadRpak2Type PakLoadAPI__LoadRpak2; - -void* PakLoadAPI__LoadRpak2Hook(char* filename, void* unknown, int flags, void* callback, void* callback2) -{ - spdlog::info("PakLoadAPI__LoadRpak2Hook {}", filename); - - // on dedi, don't load any paks that aren't required - if (strncmp(filename, "common", 6)) - return 0; - - return PakLoadAPI__LoadRpak2(filename, unknown, flags, callback, callback2); -} - -void InitialiseDedicatedRtechGame(HMODULE baseAddress) +ON_DLL_LOAD_DEDI("materialsystem_dx11.dll", DedicatedServerMaterialSystem, (CModule module)) { - baseAddress = GetModuleHandleA("rtech_game.dll"); + AUTOHOOK_DISPATCH() - HookEnabler hook; - // unfortunately this is unstable, seems to freeze when changing maps - // ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xB0F0, &PakLoadAPI__LoadRpakHook, reinterpret_cast<LPVOID*>(&PakLoadAPI__LoadRpak)); - // ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xB170, &PakLoadAPI__LoadRpak2Hook, reinterpret_cast<LPVOID*>(&PakLoadAPI__LoadRpak2)); + // CMaterialSystem::FindMaterial + // make the game always use the error material + module.Offset(0x5F0F1).Patch("E9 34 03 00"); } diff --git a/NorthstarDLL/dedicatedmaterialsystem.h b/NorthstarDLL/dedicatedmaterialsystem.h deleted file mode 100644 index 189001e6..00000000 --- a/NorthstarDLL/dedicatedmaterialsystem.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once -void InitialiseDedicatedMaterialSystem(HMODULE baseAddress); -void InitialiseDedicatedRtechGame(HMODULE baseAddress); diff --git a/NorthstarDLL/demofixes.cpp b/NorthstarDLL/demofixes.cpp new file mode 100644 index 00000000..ab092484 --- /dev/null +++ b/NorthstarDLL/demofixes.cpp @@ -0,0 +1,26 @@ +#include "pch.h" +#include "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 = R2::g_pCVar->FindVar("demo_enabledemos"); + Cvar_demo_enableDemos->m_pszDefaultValue = "1"; + Cvar_demo_enableDemos->SetValue(true); + + ConVar* Cvar_demo_writeLocalFile = R2::g_pCVar->FindVar("demo_writeLocalFile"); + Cvar_demo_writeLocalFile->m_pszDefaultValue = "1"; + Cvar_demo_writeLocalFile->SetValue(true); + + ConVar* Cvar_demo_autoRecord = R2::g_pCVar->FindVar("demo_autoRecord"); + Cvar_demo_autoRecord->m_pszDefaultValue = "0"; + Cvar_demo_autoRecord->SetValue(false); +} diff --git a/NorthstarDLL/dllmain.cpp b/NorthstarDLL/dllmain.cpp index 020ab1cd..8d78ee2c 100644 --- a/NorthstarDLL/dllmain.cpp +++ b/NorthstarDLL/dllmain.cpp @@ -1,62 +1,23 @@ #include "pch.h" -#include "hooks.h" #include "main.h" -#include "squirrel.h" -#include "dedicated.h" -#include "dedicatedmaterialsystem.h" -#include "sourceconsole.h" #include "logging.h" -#include "concommand.h" -#include "modmanager.h" -#include "filesystem.h" -#include "serverauthentication.h" -#include "scriptmodmenu.h" -#include "scriptserverbrowser.h" -#include "keyvalues.h" -#include "masterserver.h" -#include "gameutils.h" -#include "chatcommand.h" -#include "modlocalisation.h" -#include "playlist.h" -#include "miscserverscript.h" -#include "clientauthhooks.h" -#include "latencyflex.h" -#include "scriptbrowserhooks.h" -#include "scriptmainmenupromos.h" -#include "miscclientfixes.h" -#include "miscserverfixes.h" -#include "rpakfilesystem.h" -#include "bansystem.h" +#include "crashhandler.h" #include "memalloc.h" -#include "maxplayers.h" -#include "languagehooks.h" -#include "audio.h" -#include "buildainfile.h" #include "nsprefix.h" -#include "serverchathooks.h" -#include "clientchathooks.h" -#include "localchatwriter.h" -#include "scriptservertoclientstringcommand.h" #include "plugin_abi.h" #include "plugins.h" -#include "debugoverlay.h" -#include "clientvideooverrides.h" -#include "clientruihooks.h" -#include <string.h> #include "version.h" #include "pch.h" -#include "scriptutility.h" #include "rapidjson/document.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "rapidjson/error/en.h" -#include "exploitfixes.h" -#include "scriptjson.h" -typedef void (*initPluginFuncPtr)(void* (*getPluginObject)(PluginObject)); +#include <string.h> +#include <filesystem> -bool initialised = false; +typedef void (*initPluginFuncPtr)(void* (*getPluginObject)(PluginObject)); BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { @@ -72,18 +33,6 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv return TRUE; } -void WaitForDebugger(HMODULE baseAddress) -{ - // earlier waitfordebugger call than is in vanilla, just so we can debug stuff a little easier - if (CommandLine()->CheckParm("-waitfordebugger")) - { - spdlog::info("waiting for debugger..."); - - while (!IsDebuggerPresent()) - Sleep(100); - } -} - void freeLibrary(HMODULE hLib) { if (!FreeLibrary(hLib)) @@ -190,15 +139,13 @@ bool LoadPlugins() bool InitialiseNorthstar() { - if (initialised) - { - // spdlog::warn("Called InitialiseNorthstar more than once!"); // it's actually 100% fine for that to happen + static bool bInitialised = false; + if (bInitialised) return false; - } - initialised = true; + bInitialised = true; - parseConfigurables(); + InitialiseNorthstarPrefix(); InitialiseVersion(); // Fix some users' failure to connect to respawn datacenters @@ -206,6 +153,7 @@ bool InitialiseNorthstar() curl_global_init_mem(CURL_GLOBAL_DEFAULT, _malloc_base, _free_base, _realloc_base, _strdup_base, _calloc_base); + InitialiseCrashHandler(); InitialiseLogging(); InstallInitialHooks(); CreateLogFiles(); @@ -213,94 +161,6 @@ bool InitialiseNorthstar() // Write launcher version to log spdlog::info("NorthstarLauncher version: {}", version); - InitialiseInterfaceCreationHooks(); - - AddDllLoadCallback("tier0.dll", InitialiseTier0GameUtilFunctions); - AddDllLoadCallback("engine.dll", WaitForDebugger); - AddDllLoadCallback("engine.dll", InitialiseEngineGameUtilFunctions); - AddDllLoadCallback("server.dll", InitialiseServerGameUtilFunctions); - - // dedi patches - { - AddDllLoadCallbackForDedicatedServer("tier0.dll", InitialiseDedicatedOrigin); - AddDllLoadCallbackForDedicatedServer("engine.dll", InitialiseDedicated); - AddDllLoadCallbackForDedicatedServer("server.dll", InitialiseDedicatedServerGameDLL); - AddDllLoadCallbackForDedicatedServer("materialsystem_dx11.dll", InitialiseDedicatedMaterialSystem); - // this fucking sucks, but seemingly we somehow load after rtech_game???? unsure how, but because of this we have to apply patches - // here, not on rtech_game load - AddDllLoadCallbackForDedicatedServer("engine.dll", InitialiseDedicatedRtechGame); - } - - AddDllLoadCallback("engine.dll", InitialiseConVars); - AddDllLoadCallback("engine.dll", InitialiseConCommands); - - // client-exclusive patches - { - - AddDllLoadCallbackForClient("tier0.dll", InitialiseTier0LanguageHooks); - AddDllLoadCallbackForClient("client.dll", InitialiseClientSquirrel); - AddDllLoadCallbackForClient("client.dll", InitialiseSourceConsole); - AddDllLoadCallbackForClient("engine.dll", InitialiseChatCommands); - AddDllLoadCallbackForClient("client.dll", InitialiseScriptModMenu); - AddDllLoadCallbackForClient("client.dll", InitialiseScriptServerBrowser); - AddDllLoadCallbackForClient("localize.dll", InitialiseModLocalisation); - AddDllLoadCallbackForClient("engine.dll", InitialiseClientAuthHooks); - AddDllLoadCallbackForClient("client.dll", InitialiseLatencyFleX); - AddDllLoadCallbackForClient("engine.dll", InitialiseScriptExternalBrowserHooks); - AddDllLoadCallbackForClient("client.dll", InitialiseScriptMainMenuPromos); - AddDllLoadCallbackForClient("client.dll", InitialiseMiscClientFixes); - AddDllLoadCallbackForClient("client.dll", InitialiseClientPrintHooks); - AddDllLoadCallbackForClient("client.dll", InitialisePluginCommands); - AddDllLoadCallbackForClient("client.dll", InitialiseClientChatHooks); - AddDllLoadCallbackForClient("client.dll", InitialiseLocalChatWriter); - AddDllLoadCallbackForClient("client.dll", InitialiseScriptServerToClientStringCommands); - AddDllLoadCallbackForClient("engine.dll", InitialiseEngineClientVideoOverrides); - AddDllLoadCallbackForClient("engine.dll", InitialiseEngineClientRUIHooks); - AddDllLoadCallbackForClient("engine.dll", InitialiseDebugOverlay); - AddDllLoadCallbackForClient("client.dll", InitialiseClientSquirrelJson); - AddDllLoadCallbackForClient("client.dll", InitialiseClientSquirrelUtilityFunctions); - // audio hooks - AddDllLoadCallbackForClient("client.dll", InitialiseMilesAudioHooks); - } - - AddDllLoadCallback("engine.dll", InitialiseEngineSpewFuncHooks); - AddDllLoadCallback("server.dll", InitialiseServerSquirrel); - AddDllLoadCallback("engine.dll", InitialiseBanSystem); - AddDllLoadCallback("engine.dll", InitialiseServerAuthentication); - AddDllLoadCallback("engine.dll", InitialiseSharedMasterServer); - AddDllLoadCallback("server.dll", InitialiseMiscServerScriptCommand); - AddDllLoadCallback("server.dll", InitialiseMiscServerFixes); - AddDllLoadCallback("server.dll", InitialiseBuildAINFileHooks); - AddDllLoadCallback("server.dll", InitialiseServerSquirrelUtilityFunctions); - AddDllLoadCallback("server.dll", InitialiseServerSquirrelJson); - - AddDllLoadCallback("engine.dll", InitialisePlaylistHooks); - - AddDllLoadCallback("filesystem_stdio.dll", InitialiseFilesystem); - AddDllLoadCallback("engine.dll", InitialiseEngineRpakFilesystem); - AddDllLoadCallback("engine.dll", InitialiseKeyValues); - - AddDllLoadCallback("engine.dll", InitialiseServerChatHooks_Engine); - AddDllLoadCallback("server.dll", InitialiseServerChatHooks_Server); - - // maxplayers increase - AddDllLoadCallback("engine.dll", InitialiseMaxPlayersOverride_Engine); - AddDllLoadCallback("client.dll", InitialiseMaxPlayersOverride_Client); - AddDllLoadCallback("server.dll", InitialiseMaxPlayersOverride_Server); - - // mod manager after everything else - AddDllLoadCallback("engine.dll", InitialiseModManager); - - { - // activate multi-module exploitfixes callbacks - constexpr const char* EXPLOITFIXES_MULTICALLBACK_MODS[] = {"client.dll", "engine.dll", "server.dll"}; - for (const char* mod : EXPLOITFIXES_MULTICALLBACK_MODS) - AddDllLoadCallback(mod, ExploitFixes::LoadCallback_MultiModule); - - // activate exploit fixes later - AddDllLoadCallback("server.dll", ExploitFixes::LoadCallback_Full); - } - // run callbacks for any libraries that are already loaded by now CallAllPendingDLLLoadCallbacks(); diff --git a/NorthstarDLL/exploitfixes.cpp b/NorthstarDLL/exploitfixes.cpp index aa5adae3..dbe065cc 100644 --- a/NorthstarDLL/exploitfixes.cpp +++ b/NorthstarDLL/exploitfixes.cpp @@ -1,26 +1,37 @@ #include "pch.h" - -#include "exploitfixes.h" -#include "exploitfixes_utf8parser.h" -#include "nsmem.h" #include "cvar.h" -#include "gameutils.h" +#include "limits.h" +#include "dedicated.h" +#include "tier0.h" +#include "r2engine.h" +#include "r2client.h" + +AUTOHOOK_INIT() + +ConVar* Cvar_ns_exploitfixes_log; +ConVar* Cvar_ns_should_log_all_clientcommands; + +ConVar* Cvar_sv_cheats; -ConVar* ns_exploitfixes_log; -#define SHOULD_LOG (ns_exploitfixes_log->m_Value.m_nValue > 0) #define BLOCKED_INFO(s) \ ( \ [=]() -> bool \ { \ - if (SHOULD_LOG) \ + if (Cvar_ns_exploitfixes_log->GetBool()) \ { \ std::stringstream stream; \ - stream << "exploitfixes.cpp: " << BLOCK_PREFIX << s; \ + stream << "ExploitFixes.cpp: " << BLOCK_PREFIX << s; \ spdlog::error(stream.str()); \ } \ return false; \ }()) +// Make sure 3 or less floats are valid +bool ValidateFloats(float a, float b = 0, float c = 0) +{ + return !isnan(a) && !isnan(b) && !isnan(c); +} + struct Float3 { float vals[3]; @@ -32,21 +43,29 @@ struct Float3 val = 0; } }; - -#define BLOCK_NETMSG_FUNC(name, pattern) \ - KHOOK(name, ("engine.dll", pattern), bool, __fastcall, (void* thisptr, void* buffer)) \ - { \ - return false; \ - } - +// block bad netmessages // Servers can literally request a screenshot from any client, yeah no -BLOCK_NETMSG_FUNC(CLC_Screenshot_WriteToBuffer, "48 89 5C 24 ? 57 48 83 EC 20 8B 42 10"); -BLOCK_NETMSG_FUNC(CLC_Screenshot_ReadFromBuffer, "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B DA 48 8B 52 38"); +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 +{ + return false; +} + +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 +{ + return false; +} -// This is unused ingame and a big exploit vector -BLOCK_NETMSG_FUNC(Base_CmdKeyValues_ReadFromBuffer, "40 55 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 5D 70"); +// This is unused ingame and a big client=>server=>client exploit vector +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 +{ + return false; +} -KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10"), bool, __fastcall, (void* pMsg)) +AUTOHOOK(CClient_ProcessSetConVar, engine.dll + 0x75CF0, +bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10 { constexpr int ENTRY_STR_LEN = 260; @@ -69,25 +88,11 @@ KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 }; auto msg = (NET_SetConVar*)pMsg; + bool bIsServerFrame = Tier0::ThreadInServerFrameThread(); - bool areWeServer; + std::string BLOCK_PREFIX = std::string {"NET_SetConVar ("} + (bIsServerFrame ? "server" : "client") + "): Blocked dangerous/invalid msg: "; - { - // Figure out of we are the client or the server - // To do this, we utilize the msg's m_pMessageHandler pointer - // m_pMessageHandler points to a virtual class that handles all net messages - // The first virtual table function of our m_pMessageHandler will differ if it is IServerMessageHandler or IClientMessageHandler - void* msgHandlerVTableFirstFunc = **(void****)(msg->m_pMessageHandler); - static auto engineBaseAddress = (uintptr_t)GetModuleHandleA("engine.dll"); - auto offset = uintptr_t(msgHandlerVTableFirstFunc) - engineBaseAddress; - - constexpr uintptr_t CLIENTSTATE_FIRST_VFUNC_OFFSET = 0x8A15C; - areWeServer = offset != CLIENTSTATE_FIRST_VFUNC_OFFSET; - } - - std::string BLOCK_PREFIX = std::string {"NET_SetConVar ("} + (areWeServer ? "server" : "client") + "): Blocked dangerous/invalid msg: "; - - if (areWeServer) + if (bIsServerFrame) { constexpr int SETCONVAR_SANITY_AMOUNT_LIMIT = 69; if (msg->m_ConVars_count < 1 || msg->m_ConVars_count > SETCONVAR_SANITY_AMOUNT_LIMIT) @@ -101,9 +106,8 @@ KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 auto entry = msg->m_ConVars + i; // Safety check for memory access - if (NSMem::IsMemoryReadable(entry, sizeof(*entry))) + if (MemoryAddress(entry).IsMemoryReadable(sizeof(*entry))) { - // Find null terminators bool nameValid = false, valValid = false; for (int i = 0; i < ENTRY_STR_LEN; i++) @@ -117,7 +121,7 @@ KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 if (!nameValid || !valValid) return BLOCKED_INFO("Missing null terminators"); - auto realVar = g_pCVar->FindVar(entry->name); + auto realVar = R2::g_pCVar->FindVar(entry->name); if (realVar) memcpy( @@ -126,10 +130,10 @@ KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 strlen(realVar->m_ConCommandBase.m_pszName) + 1); // Force name to match case bool isValidFlags = true; - if (areWeServer) + if (bIsServerFrame) { if (realVar) - isValidFlags = ConVar::IsFlagSet(realVar, FCVAR_USERINFO); // ConVar MUST be userinfo var + isValidFlags = realVar->IsFlagSet(FCVAR_USERINFO); // ConVar MUST be userinfo var } else { @@ -155,11 +159,12 @@ KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 } } - return oCClient_ProcessSetConVar(msg); + return CClient_ProcessSetConVar(msg); } -// Purpose: prevent invalid user CMDs -KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __fastcall, (void* thisptr, void* pMsg)) +// prevent invalid user CMDs +AUTOHOOK(CClient_ProcessUsercmds, engine.dll + 0x1040F0, +bool, __fastcall, (void* thisptr, void* pMsg)) // 40 55 56 48 83 EC 58 { struct CLC_Move { @@ -186,22 +191,17 @@ KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __f return BLOCKED_INFO("Invalid m_nNewCommands (" << msg->m_nNewCommands << ")"); } - constexpr int NUMCMD_SANITY_LIMIT = 16; - if ((msg->m_nNewCommands + msg->m_nBackupCommands) > NUMCMD_SANITY_LIMIT) - { - return BLOCKED_INFO("Command count is too high (new: " << msg->m_nNewCommands << ", backup: " << msg->m_nBackupCommands << ")"); - } - if (msg->m_nLength <= 0) return BLOCKED_INFO("Invalid message length (" << msg->m_nLength << ")"); - return oCClient_ProcessUsercmds(thisptr, pMsg); + return CClient_ProcessUsercmds(thisptr, pMsg); } -KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) +AUTOHOOK(ReadUsercmd, server.dll + 0x2603F0, +void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 ? 53 55 56 57 { // Let normal usercmd read happen first, it's safe - oReadUsercmd(buf, pCmd_move, pCmd_from); + 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 @@ -237,7 +237,7 @@ KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall std::string BLOCK_PREFIX = "ReadUsercmd (command_number delta: " + std::to_string(cmd->command_number - fromCmd->command_number) + "): "; - // Fix invalid player angles + // fix invalid player angles cmd->worldViewAngles.MakeValid(); cmd->attackangles.MakeValid(); cmd->localViewAngles.MakeValid(); @@ -249,7 +249,7 @@ KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall // Fix invaid movement vector cmd->move.MakeValid(); - if (cmd->tick_count == 0 || cmd->command_time <= 0) + 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 @@ -260,6 +260,7 @@ KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall 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}; @@ -269,285 +270,174 @@ INVALID_CMD: cmd->meleetarget = 0; } -// basically: by default r2 isn't set as a valve mod, meaning that m_bRestrictServerCommands is false -// this is HORRIBLE for security, because it means servers can run arbitrary concommands on clients -// especially since we have script commands this could theoretically be awful -KHOOK(IsValveMod, ("engine.dll", "48 83 EC 28 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 85 C0 74 63"), bool, __fastcall, ()) +// 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 +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 { - bool result = !CommandLine()->CheckParm("-norestrictservercommands"); - spdlog::info("ExploitFixes: Overriding IsValveMod to {}...", result); - return result; + // somewhat temp, store the modname here, since we don't have a proper ptr in engine to it rn + int iSize = strlen(pModName); + R2::g_pModName = new char[iSize + 1]; + strcpy(R2::g_pModName, pModName); + + return (!strcmp("r2", pModName) || !strcmp("r1", pModName)) + && !Tier0::CommandLine()->CheckParm("-norestrictservercommands"); } -// Fix respawn's crappy UTF8 parser so it doesn't crash -_- -// This also means you can launch multiplayer with "communities_enabled 1" and not crash, you're welcome -KHOOK( - CrashFunc_ParseUTF8, - ("engine.dll", "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"), - bool, - __fastcall, - (INT64 * a1, DWORD* a2, char* strData)) +// ratelimit stringcmds, and prevent remote clients from calling commands that they shouldn't +bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, R2::cmd_source_t commandSource); +AUTOHOOK(CGameClient__ExecuteStringCommand, +engine.dll + 0x1022E0, bool, , (R2::CBaseClient* self, uint32_t unknown, const char* pCommandString)) { + if (Cvar_ns_should_log_all_clientcommands->GetBool()) + spdlog::info("player {} (UID: {}) sent command: \"{}\"", self->m_Name, self->m_UID, pCommandString); - static void* targetRetAddr = NSMem::PatternScan("engine.dll", "84 C0 75 2C 49 8B 16"); - -#ifdef _MSC_VER - if (_ReturnAddress() == targetRetAddr) -#else - if (__builtin_return_address(0) == targetRetAddr) -#endif + if (!g_pServerLimits->CheckStringCommandLimits(self)) { - if (!ExploitFixes_UTF8Parser::CheckValid(a1, a2, strData)) - { - const char* BLOCK_PREFIX = "ParseUTF8 Hook: "; - BLOCKED_INFO("Ignoring potentially-crashing utf8 string"); - return false; - } + R2::CBaseClient__Disconnect(self, 1, "Sent too many stringcmd commands"); + return false; } - return oCrashFunc_ParseUTF8(a1, a2, strData); -} + // 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; -// GetEntByIndex (called by ScriptGetEntByIndex) doesn't check for the index being out of bounds when it's -// above the max entity count. This allows it to be used to crash servers. -typedef void*(__fastcall* GetEntByIndexType)(int idx); -GetEntByIndexType GetEntByIndex; + if (!CCommand__Tokenize(tempCommand, pCommandString, R2::cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC()) + return false; -static void* GetEntByIndexHook(int idx) -{ - if (idx >= 0x4000) - { - spdlog::info("GetEntByIndex {} is out of bounds", idx); - return nullptr; - } - return GetEntByIndex(idx); -} - -// RELOCATED FROM https://github.com/R2Northstar/NorthstarLauncher/commit/25dbf729cfc75107a0fcf0186924b58ecc05214b -// 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. -KHOOK( - LZSS_SafeUncompress, - ("engine.dll", "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 33 ED 41 8B F9"), - uint32_t, - __fastcall, - (void* self, const unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize)) -{ - static constexpr int LZSS_LOOKSHIFT = 4; + ConCommand* command = R2::g_pCVar->FindCommand(tempCommand.Arg(0)); - uint32_t totalBytes = 0; - int getCmdByte = 0, cmdByte = 0; - - struct lzss_header_t + // 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)) { - uint32_t id, actualSize; - }; - - lzss_header_t header = *(lzss_header_t*)pInput; - - if (pInput == NULL || header.id != 'SSZL' || header.actualSize == 0 || header.actualSize > unBufSize) - return 0; + // ensure FCVAR_GAMEDLL concommands without FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS can't be executed by remote clients + if (IsDedicatedServer()) + return false; - pInput += sizeof(lzss_header_t); + if (strcmp(self->m_UID, R2::g_pLocalPlayerUserID)) + return false; + } - for (;;) + // 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()) { - if (!getCmdByte) - cmdByte = *pInput++; - - getCmdByte = (getCmdByte + 1) & 0x07; - - if (cmdByte & 0x01) - { - int position = *pInput++ << LZSS_LOOKSHIFT; - position |= (*pInput >> LZSS_LOOKSHIFT); - position += 1; + constexpr const char* blockedCommands[] = { + "emit", // Sound-playing exploit (likely for Portal 2 coop devs testing splitscreen sound or something) - int count = (*pInput++ & 0x0F) + 1; - if (count == 1) - break; + // These both execute a command for every single entity for some reason, nice one valve + "pre_go_to_hub", + "pre_go_to_calibration", - // Ensure reference chunk exists entirely within our buffer - if (position > totalBytes) - return 0; + "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() + }; - totalBytes += count; - if (totalBytes > unBufSize) - return 0; + int iCmdLength = strlen(tempCommand.Arg(0)); - unsigned char* pSource = pOutput - position; - for (int i = 0; i < count; i++) - *pOutput++ = *pSource++; - } - else + bool bIsBadCommand = false; + for (auto& blockedCommand : blockedCommands) { - totalBytes++; - if (totalBytes > unBufSize) - return 0; + 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 - *pOutput++ = *pInput++; + // this is a command we need to block + return false; + NEXT_COMMAND:; } - cmdByte = cmdByte >> 1; } - if (totalBytes == header.actualSize) - { - return totalBytes; - } - else - { - return 0; - } + return CGameClient__ExecuteStringCommand(self, unknown, pCommandString); } -////////////////////////////////////////////////// - -void DoBytePatches() +// prevent clients from crashing servers through overflowing CNetworkStringTableContainer::WriteBaselines +bool bWasWritingStringTableSuccessful; +AUTOHOOK(CBaseClient__SendServerInfo, +engine.dll + 0x104FB0, void, , (void* self)) { - uintptr_t engineBase = (uintptr_t)GetModuleHandleA("engine.dll"); - uintptr_t serverBase = (uintptr_t)GetModuleHandleA("server.dll"); + bWasWritingStringTableSuccessful = true; + CBaseClient__SendServerInfo(self); + if (!bWasWritingStringTableSuccessful) + R2::CBaseClient__Disconnect( + self, 1, "Overflowed CNetworkStringTableContainer::WriteBaselines, try restarting your client and reconnecting"); +} - // patches to make commands run from client/ui script still work - // note: this is likely preventable in a nicer way? test prolly - NSMem::BytePatch(engineBase + 0x4FB65, "EB 11"); - NSMem::BytePatch(engineBase + 0x4FBAC, "EB 16"); +// 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 +AUTOHOOK(GetEntByIndex, engine.dll + 0x2A8A50, +void*,, (int i)) +{ + const int MAX_ENT_IDX = 0x4000; - // disconnect concommand + if (i >= MAX_ENT_IDX) { - uintptr_t addr = engineBase + 0x5ADA2D; - int val = *(int*)addr | FCVAR_SERVER_CAN_EXECUTE; - NSMem::BytePatch(addr, (BYTE*)&val, sizeof(int)); + spdlog::warn("GetEntByIndex {} is out of bounds (max {})", i, MAX_ENT_IDX); + return nullptr; } - { // Dumb ANTITAMPER patches (they negatively impact performance and security) - - constexpr const char* ANTITAMPER_EXPORTS[] = { - "ANTITAMPER_SPOTCHECK_CODEMARKER", - "ANTITAMPER_TESTVALUE_CODEMARKER", - "ANTITAMPER_TRIGGER_CODEMARKER", - }; - - // Prevent thesefrom actually doing anything - for (auto exportName : ANTITAMPER_EXPORTS) - { - - auto address = (uintptr_t)GetProcAddress(GetModuleHandleA("server.dll"), exportName); - if (!address) - { - spdlog::warn("Failed to find AntiTamper function export \"{}\"", exportName); - } - else - { - // Just return, none of them have any args or are userpurge - NSMem::BytePatch(address, "C3"); - - spdlog::info("Patched AntiTamper function export \"{}\"", exportName); - } - } - } + return GetEntByIndex(i); } -KHOOK( - SpecialClientCommand, - ("server.dll", "48 89 5C 24 ? 48 89 74 24 ? 55 57 41 56 48 8D 6C 24 ? 48 81 EC ? ? ? ? 83 3A 00"), - bool, - __fastcall, - (void* player, CCommand* command)) +ON_DLL_LOAD("engine.dll", EngineExploitFixes, (CModule module)) { + AUTOHOOK_DISPATCH_MODULE(engine.dll) - static ConVar* sv_cheats = g_pCVar->FindVar("sv_cheats"); - - if (sv_cheats->GetBool()) - return oSpecialClientCommand(player, command); // Don't block anything if sv_cheats is on + CCommand__Tokenize = module.Offset(0x418380).As<bool (*)(CCommand&, const char*, R2::cmd_source_t)>(); - // These are mostly from Portal 2 (sigh) - constexpr const char* blockedCommands[] = { - "emit", // Sound-playing exploit (likely for Portal 2 coop devs testing splitscreen sound or something) + // allow client/ui to run clientcommands despite restricting servercommands + module.Offset(0x4FB65).Patch("EB 11"); + module.Offset(0x4FBAC).Patch("EB 16"); - // 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() - }; - - if (command->ArgC() > 0) + // patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails { - std::string cmdStr = command->Arg(0); - for (char& c : cmdStr) - c = tolower(c); + MemoryAddress writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).m_nAddress); - for (const char* blockedCommand : blockedCommands) - { - if (cmdStr.find(blockedCommand) != std::string::npos) - { - // Block this command - spdlog::warn("Blocked exploititive client command \"{}\".", cmdStr); - return true; - } - } - } + MemoryAddress addr = module.Offset(0x234ED2); + addr.Patch("C7 05"); + addr.Offset(2).Patch((BYTE*)&writeAddress, sizeof(writeAddress)); - return oSpecialClientCommand(player, command); -} + addr.Offset(6).Patch("00 00 00 00"); -void SetupKHook(KHook* hook) -{ - if (hook->Setup()) - { - spdlog::debug("KHook::Setup(): Hooked at {}", hook->targetFuncAddr); - } - else - { - spdlog::critical("\tFAILED to initialize all exploit patches."); - - // Force exit - MessageBoxA(0, "FAILED to initialize all exploit patches.", "Northstar", MB_ICONERROR); - exit(0); + addr.Offset(10).NOP(5); } } -void ExploitFixes::LoadCallback_MultiModule(HMODULE baseAddress) +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"); - spdlog::info("ExploitFixes::LoadCallback_MultiModule({}) ...", (void*)baseAddress); + // Dumb ANTITAMPER patches (they negatively impact performance and security) + constexpr const char* ANTITAMPER_EXPORTS[] = { + "ANTITAMPER_SPOTCHECK_CODEMARKER", + "ANTITAMPER_TESTVALUE_CODEMARKER", + "ANTITAMPER_TRIGGER_CODEMARKER", + }; - int hooksEnabled = 0; - for (auto itr = KHook::_allHooks.begin(); itr != KHook::_allHooks.end(); itr++) + // Prevent these from actually doing anything + for (auto exportName : ANTITAMPER_EXPORTS) { - auto curHook = *itr; - if (GetModuleHandleA(curHook->targetFunc.moduleName) == baseAddress) + MemoryAddress exportAddr = module.GetExport(exportName); + if (exportAddr) { - SetupKHook(curHook); - itr = KHook::_allHooks.erase(itr); // Prevent repeated initialization - - hooksEnabled++; - - if (itr == KHook::_allHooks.end()) - break; + // Just return, none of them have any args or are userpurge + exportAddr.Patch("C3"); + spdlog::info("Patched AntiTamper function export \"{}\"", exportName); } } - spdlog::info("\tEnabled {} hooks.", hooksEnabled); -} - -void ExploitFixes::LoadCallback_Full(HMODULE baseAddress) -{ - spdlog::info("ExploitFixes::LoadCallback_Full ..."); - - spdlog::info("\tByte patching..."); - DoBytePatches(); - - for (KHook* hook : KHook::_allHooks) - SetupKHook(hook); - - spdlog::info("\tInitialized " + std::to_string(KHook::_allHooks.size()) + " late exploit-patch hooks."); - KHook::_allHooks.clear(); - - ns_exploitfixes_log = - new ConVar("ns_exploitfixes_log", "1", FCVAR_GAMEDLL, "Whether to log whenever exploitfixes.cpp blocks/corrects something"); + 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"); - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x2a8a50, &GetEntByIndexHook, reinterpret_cast<LPVOID*>(&GetEntByIndex)); + Cvar_sv_cheats = R2::g_pCVar->FindVar("sv_cheats"); } diff --git a/NorthstarDLL/exploitfixes.h b/NorthstarDLL/exploitfixes.h deleted file mode 100644 index d0754d72..00000000 --- a/NorthstarDLL/exploitfixes.h +++ /dev/null @@ -1,12 +0,0 @@ -// KittenPopo's exploit fix hooks, feel free to add more here - -#pragma once -#include "pch.h" - -namespace ExploitFixes -{ - // This callback will be ran muliple times on multiple module loads - void LoadCallback_MultiModule(HMODULE baseAddress); - - void LoadCallback_Full(HMODULE unused); -} // namespace ExploitFixes diff --git a/NorthstarDLL/exploitfixes_lzss.cpp b/NorthstarDLL/exploitfixes_lzss.cpp new file mode 100644 index 00000000..87d2868d --- /dev/null +++ b/NorthstarDLL/exploitfixes_lzss.cpp @@ -0,0 +1,77 @@ +#include "pch.h" + +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. +AUTOHOOK(CLZSS__SafeDecompress, engine.dll + 0x432A10, +unsigned int,, (void* self, const unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize)) +{ + 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/exploitfixes_utf8parser.cpp b/NorthstarDLL/exploitfixes_utf8parser.cpp new file mode 100644 index 00000000..581596a7 --- /dev/null +++ b/NorthstarDLL/exploitfixes_utf8parser.cpp @@ -0,0 +1,196 @@ +#include "pch.h" + +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 (!MemoryAddress(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 +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 +{ + 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 + if ( +#ifdef _MSC_VER + _ReturnAddress() +#else + __builtin_return_address(0) +#endif + == 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").As<INT64(__fastcall*)(DWORD, char*)>(); +} diff --git a/NorthstarDLL/exploitfixes_utf8parser.h b/NorthstarDLL/exploitfixes_utf8parser.h deleted file mode 100644 index 24545ea3..00000000 --- a/NorthstarDLL/exploitfixes_utf8parser.h +++ /dev/null @@ -1,175 +0,0 @@ -// Reimplementation of a exploitable UTF decoding function in titanfall - -#include "NSMem.h" - -#pragma once - -namespace ExploitFixes_UTF8Parser -{ - bool __fastcall CheckValid(INT64* a1, DWORD* a2, char* strData) - { - static auto sub_F1320 = (INT64(__fastcall*)(DWORD a1, char* a2))NSMem::PatternScan("engine.dll", "83 F9 7F 77 08 88 0A"); - - 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 (!NSMem::IsMemoryReadable(v4, 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; - } -} // namespace ExploitFixes_UTF8Parser diff --git a/NorthstarDLL/filesystem.cpp b/NorthstarDLL/filesystem.cpp index c17d813f..9fba160f 100644 --- a/NorthstarDLL/filesystem.cpp +++ b/NorthstarDLL/filesystem.cpp @@ -1,87 +1,67 @@ #include "pch.h" #include "filesystem.h" -#include "hooks.h" -#include "hookutils.h" #include "sourceinterface.h" #include "modmanager.h" #include <iostream> #include <sstream> -// hook forward declares -typedef FileHandle_t (*ReadFileFromVPKType)(VPKData* vpkInfo, __int64* b, char* filename); -ReadFileFromVPKType readFileFromVPK; -FileHandle_t ReadFileFromVPKHook(VPKData* vpkInfo, __int64* b, char* filename); +AUTOHOOK_INIT() -typedef bool (*ReadFromCacheType)(IFileSystem* filesystem, char* path, void* result); -ReadFromCacheType readFromCache; -bool ReadFromCacheHook(IFileSystem* filesystem, char* path, void* result); +using namespace R2; -typedef void (*AddSearchPathType)(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType); -AddSearchPathType addSearchPathOriginal; -void AddSearchPathHook(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType); +bool bReadingOriginalFile = false; +std::string sCurrentModPath; -typedef FileHandle_t (*ReadFileFromFilesystemType)( - IFileSystem* filesystem, const char* pPath, const char* pOptions, int64_t a4, uint32_t a5); -ReadFileFromFilesystemType readFileFromFilesystem; -FileHandle_t ReadFileFromFilesystemHook(IFileSystem* filesystem, const char* pPath, const char* pOptions, int64_t a4, uint32_t a5); +ConVar* Cvar_ns_fs_log_reads; -typedef VPKData* (*MountVPKType)(IFileSystem* fileSystem, const char* vpkPath); -MountVPKType mountVPK; -VPKData* MountVPKHook(IFileSystem* fileSystem, const char* vpkPath); +// use the R2 namespace for game funcs +namespace R2 +{ + SourceInterface<IFileSystem>* g_pFilesystem; -bool readingOriginalFile; -std::string currentModPath; -SourceInterface<IFileSystem>* g_Filesystem; + 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); -void InitialiseFilesystem(HMODULE baseAddress) -{ - g_Filesystem = new SourceInterface<IFileSystem>("filesystem_stdio.dll", "VFileSystem017"); - - // create hooks - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x5CBA0, &ReadFileFromVPKHook, reinterpret_cast<LPVOID*>(&readFileFromVPK)); - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>((*g_Filesystem)->m_vtable->ReadFromCache), - &ReadFromCacheHook, - reinterpret_cast<LPVOID*>(&readFromCache)); - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>((*g_Filesystem)->m_vtable->AddSearchPath), - &AddSearchPathHook, - reinterpret_cast<LPVOID*>(&addSearchPathOriginal)); - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x15F20, &ReadFileFromFilesystemHook, reinterpret_cast<LPVOID*>(&readFileFromFilesystem)); - ENABLER_CREATEHOOK( - hook, reinterpret_cast<void*>((*g_Filesystem)->m_vtable->MountVPK), &MountVPKHook, reinterpret_cast<LPVOID*>(&mountVPK)); -} + 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)); -std::string ReadVPKFile(const char* path) -{ - // read scripts.rson file, todo: check if this can be overwritten - FileHandle_t fileHandle = (*g_Filesystem)->m_vtable2->Open(&(*g_Filesystem)->m_vtable2, path, "rb", "GAME", 0); + (*g_pFilesystem)->m_vtable2->Close(*g_pFilesystem, fileHandle); + + return fileStream.str(); + } - std::stringstream fileStream; - int bytesRead = 0; - char data[4096]; - do + std::string ReadVPKOriginalFile(const char* path) { - bytesRead = (*g_Filesystem)->m_vtable2->Read(&(*g_Filesystem)->m_vtable2, data, (int)std::size(data), fileHandle); - fileStream.write(data, bytesRead); - } while (bytesRead == std::size(data)); + // todo: should probably set search path to be g_pModName here also - (*g_Filesystem)->m_vtable2->Close(*g_Filesystem, fileHandle); + bReadingOriginalFile = true; + std::string ret = ReadVPKFile(path); + bReadingOriginalFile = false; - return fileStream.str(); -} + return ret; + } +} // namespace R2 -std::string ReadVPKOriginalFile(const char* path) +HOOK(AddSearchPathHook, AddSearchPath, +void,, (IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType)) { - readingOriginalFile = true; - std::string ret = ReadVPKFile(path); - readingOriginalFile = false; + AddSearchPath(fileSystem, pPath, pathID, addType); - return ret; + // 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) @@ -90,96 +70,79 @@ void SetNewModSearchPaths(Mod* mod) // 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->ModDirectory) / MOD_OVERRIDE_DIR).string().compare(currentModPath)) + if ((fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().compare(sCurrentModPath)) { - spdlog::info("changing mod search path from {} to {}", currentModPath, mod->ModDirectory.string()); + spdlog::info("changing mod search path from {} to {}", sCurrentModPath, mod->m_ModDirectory.string()); - addSearchPathOriginal( - &*(*g_Filesystem), (fs::absolute(mod->ModDirectory) / MOD_OVERRIDE_DIR).string().c_str(), "GAME", PATH_ADD_TO_HEAD); - currentModPath = (fs::absolute(mod->ModDirectory) / MOD_OVERRIDE_DIR).string(); + 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 - addSearchPathOriginal(&*(*g_Filesystem), fs::absolute(GetCompiledAssetsPath()).string().c_str(), "GAME", PATH_ADD_TO_HEAD); + AddSearchPath(&*(*g_pFilesystem), fs::absolute(GetCompiledAssetsPath()).string().c_str(), "GAME", PATH_ADD_TO_HEAD); } -bool TryReplaceFile(char* path, bool shouldCompile) +bool TryReplaceFile(const char* pPath, bool shouldCompile) { - if (readingOriginalFile) + if (bReadingOriginalFile) return false; if (shouldCompile) - (*g_ModManager).CompileAssetsForFile(path); + 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_ModManager->m_modFiles.find(fs::path(path).lexically_normal().string()); - if (file != g_ModManager->m_modFiles.end()) + auto file = g_pModManager->m_ModFiles.find(g_pModManager->NormaliseModFilePath(fs::path(pPath))); + if (file != g_pModManager->m_ModFiles.end()) { - SetNewModSearchPaths(file->second.owningMod); + SetNewModSearchPaths(file->second.m_pOwningMod); return true; } return false; } -FileHandle_t ReadFileFromVPKHook(VPKData* vpkInfo, __int64* b, char* filename) +// force modded files to be read from mods, not cache +HOOK(ReadFromCacheHook, ReadFromCache, +bool,, (IFileSystem* filesystem, char* pPath, void* result)) { - // move this to a convar at some point when we can read them in native - // spdlog::info("ReadFileFromVPKHook {} {}", filename, vpkInfo->path); - - // there is literally never any reason to compile here, since we'll always compile in ReadFileFromFilesystemHook in the same codepath - // this is called - if (TryReplaceFile(filename, false)) - { - *b = -1; - return b; - } - - return readFileFromVPK(vpkInfo, b, filename); -} - -bool ReadFromCacheHook(IFileSystem* filesystem, char* path, void* result) -{ - // move this to a convar at some point when we can read them in native - // spdlog::info("ReadFromCacheHook {}", path); - - if (TryReplaceFile(path, true)) + if (TryReplaceFile(pPath, true)) return false; - return readFromCache(filesystem, path, result); + return ReadFromCache(filesystem, pPath, result); } -void AddSearchPathHook(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType) +// force modded files to be read from mods, not vpk +AUTOHOOK(ReadFileFromVPK, filesystem_stdio.dll + 0x5CBA0, +FileHandle_t,, (VPKData* vpkInfo, uint64_t* b, char* filename)) { - addSearchPathOriginal(fileSystem, pPath, pathID, addType); - - // make sure current mod paths are at head - if (!strcmp(pathID, "GAME") && currentModPath.compare(pPath) && addType == PATH_ADD_TO_HEAD) + // don't compile here because this is only ever called from OpenEx, which already compiles + if (TryReplaceFile(filename, false)) { - addSearchPathOriginal(fileSystem, currentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD); - addSearchPathOriginal(fileSystem, GetCompiledAssetsPath().string().c_str(), "GAME", PATH_ADD_TO_HEAD); + *b = -1; + return b; } + + return ReadFileFromVPK(vpkInfo, b, filename); } -FileHandle_t ReadFileFromFilesystemHook(IFileSystem* filesystem, const char* pPath, const char* pOptions, int64_t a4, uint32_t a5) +AUTOHOOK(CBaseFileSystem__OpenEx, filesystem_stdio.dll + 0x15F50, +FileHandle_t,, (IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char **ppszResolvedFilename)) { - // this isn't super efficient, but it's necessary, since calling addsearchpath in readfilefromvpk doesn't work, possibly refactor later - // it also might be possible to hook functions that are called later, idk look into callstack for ReadFileFromVPK - if (!readingOriginalFile) - TryReplaceFile((char*)pPath, true); - - return readFileFromFilesystem(filesystem, pPath, pOptions, a4, a5); + TryReplaceFile(pPath, true); + return CBaseFileSystem__OpenEx(filesystem, pPath, pOptions, flags, pPathID, ppszResolvedFilename); } -VPKData* MountVPKHook(IFileSystem* fileSystem, const char* vpkPath) +HOOK(MountVPKHook, MountVPK, +VPKData*,, (IFileSystem* fileSystem, const char* pVpkPath)) { - spdlog::info("MountVPK {}", vpkPath); - VPKData* ret = mountVPK(fileSystem, vpkPath); + spdlog::info("MountVPK {}", pVpkPath); + VPKData* ret = MountVPK(fileSystem, pVpkPath); - for (Mod mod : g_ModManager->m_loadedMods) + for (Mod mod : g_pModManager->m_LoadedMods) { - if (!mod.Enabled) + if (!mod.m_bEnabled) continue; for (ModVPKEntry& vpkEntry : mod.Vpks) @@ -189,13 +152,13 @@ VPKData* MountVPKHook(IFileSystem* fileSystem, const char* vpkPath) { // 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(vpkPath).filename().string()); + 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()); + 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; } @@ -203,3 +166,14 @@ VPKData* MountVPKHook(IFileSystem* fileSystem, const char* vpkPath) return ret; } + +ON_DLL_LOAD("filesystem_stdio.dll", Filesystem, (CModule module)) +{ + AUTOHOOK_DISPATCH() + + R2::g_pFilesystem = new SourceInterface<IFileSystem>("filesystem_stdio.dll", "VFileSystem017"); + + AddSearchPathHook.Dispatch((*g_pFilesystem)->m_vtable->AddSearchPath); + ReadFromCacheHook.Dispatch((*g_pFilesystem)->m_vtable->ReadFromCache); + MountVPKHook.Dispatch((*g_pFilesystem)->m_vtable->MountVPK); +} diff --git a/NorthstarDLL/filesystem.h b/NorthstarDLL/filesystem.h index 6015c6a5..c326b419 100644 --- a/NorthstarDLL/filesystem.h +++ b/NorthstarDLL/filesystem.h @@ -59,7 +59,8 @@ class IFileSystem 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); - void* unknown2[6]; + 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); }; @@ -67,8 +68,11 @@ class IFileSystem VTable2* m_vtable2; }; -std::string ReadVPKFile(const char* path); -std::string ReadVPKOriginalFile(const char* path); +// use the R2 namespace for game funcs +namespace R2 +{ + extern SourceInterface<IFileSystem>* g_pFilesystem; -void InitialiseFilesystem(HMODULE baseAddress); -extern SourceInterface<IFileSystem>* g_Filesystem; + std::string ReadVPKFile(const char* path); + std::string ReadVPKOriginalFile(const char* path); +} // namespace R2 diff --git a/NorthstarDLL/gameutils.cpp b/NorthstarDLL/gameutils.cpp deleted file mode 100644 index 405624ca..00000000 --- a/NorthstarDLL/gameutils.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "pch.h" -#include "convar.h" -#include "gameutils.h" - -// memory -IMemAlloc* g_pMemAllocSingleton; -typedef IMemAlloc* (*CreateGlobalMemAllocType)(); -CreateGlobalMemAllocType CreateGlobalMemAlloc; - -// cmd.h -Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer; -Cbuf_AddTextType Cbuf_AddText; -Cbuf_ExecuteType Cbuf_Execute; - -// hoststate stuff -CHostState* g_pHostState; - -// cengine stuff -CEngine* g_pEngine; -server_state_t* sv_m_State; - -// network stuff -ConVar* Cvar_hostport; - -// playlist stuff -GetCurrentPlaylistType GetCurrentPlaylistName; -SetCurrentPlaylistType SetCurrentPlaylist; -SetPlaylistVarOverrideType SetPlaylistVarOverride; -GetCurrentPlaylistVarType GetCurrentPlaylistVar; - -// server entity stuff -Server_GetEntityByIndexType Server_GetEntityByIndex; - -// auth -char* g_LocalPlayerUserID; -char* g_LocalPlayerOriginToken; - -// misc stuff -ErrorType Error; -CommandLineType CommandLine; -Plat_FloatTimeType Plat_FloatTime; -ThreadInServerFrameThreadType ThreadInServerFrameThread; -GetBaseLocalClientType GetBaseLocalClient; - -void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) -{ - Cbuf_GetCurrentPlayer = (Cbuf_GetCurrentPlayerType)((char*)baseAddress + 0x120630); - Cbuf_AddText = (Cbuf_AddTextType)((char*)baseAddress + 0x1203B0); - Cbuf_Execute = (Cbuf_ExecuteType)((char*)baseAddress + 0x1204B0); - - g_pHostState = (CHostState*)((char*)baseAddress + 0x7CF180); - g_pEngine = *(CEngine**)((char*)baseAddress + 0x7D70C8); - sv_m_State = (server_state_t*)((char*)baseAddress + 0x12A53D48); - - GetCurrentPlaylistName = (GetCurrentPlaylistType)((char*)baseAddress + 0x18C640); - SetCurrentPlaylist = (SetCurrentPlaylistType)((char*)baseAddress + 0x18EB20); - SetPlaylistVarOverride = (SetPlaylistVarOverrideType)((char*)baseAddress + 0x18ED00); - GetCurrentPlaylistVar = (GetCurrentPlaylistVarType)((char*)baseAddress + 0x18C680); - - g_LocalPlayerUserID = (char*)baseAddress + 0x13F8E688; - g_LocalPlayerOriginToken = (char*)baseAddress + 0x13979C80; - - GetBaseLocalClient = (GetBaseLocalClientType)((char*)baseAddress + 0x78200); - - /* NOTE: - g_pCVar->FindVar("convar_name") now works. These are no longer needed. - You can also itterate over every ConVar using CCVarIteratorInternal - dump the pointers to a vector and access them from there. - Example: - std::vector<ConVar*> g_pAllConVars; - for (auto& map : g_pCVar->DumpToMap()) - { - ConVar* pConVar = g_pCVar->FindVar(map.first.c_str()); - if (pConVar) - { - g_pAllConVars.push_back(pConVar); - } - }*/ - - Cvar_hostport = (ConVar*)((char*)baseAddress + 0x13FA6070); -} - -void InitialiseServerGameUtilFunctions(HMODULE baseAddress) -{ - Server_GetEntityByIndex = (Server_GetEntityByIndexType)((char*)baseAddress + 0xFB820); -} - -void InitialiseTier0GameUtilFunctions(HMODULE baseAddress) -{ - if (!baseAddress) - { - spdlog::critical("tier0 base address is null, but it should be already loaded"); - throw "tier0 base address is null, but it should be already loaded"; - } - if (g_pMemAllocSingleton) - return; // seems this function was already called - CreateGlobalMemAlloc = reinterpret_cast<CreateGlobalMemAllocType>(GetProcAddress(baseAddress, "CreateGlobalMemAlloc")); - IMemAlloc** ppMemAllocSingleton = reinterpret_cast<IMemAlloc**>(GetProcAddress(baseAddress, "g_pMemAllocSingleton")); - if (!ppMemAllocSingleton) - { - spdlog::critical("Address of g_pMemAllocSingleton is a null pointer, this should never happen"); - throw "Address of g_pMemAllocSingleton is a null pointer, this should never happen"; - } - if (!*ppMemAllocSingleton) - { - g_pMemAllocSingleton = CreateGlobalMemAlloc(); - *ppMemAllocSingleton = g_pMemAllocSingleton; - spdlog::info("Created new g_pMemAllocSingleton"); - } - else - { - g_pMemAllocSingleton = *ppMemAllocSingleton; - } - - Error = reinterpret_cast<ErrorType>(GetProcAddress(baseAddress, "Error")); - CommandLine = reinterpret_cast<CommandLineType>(GetProcAddress(baseAddress, "CommandLine")); - Plat_FloatTime = reinterpret_cast<Plat_FloatTimeType>(GetProcAddress(baseAddress, "Plat_FloatTime")); - ThreadInServerFrameThread = reinterpret_cast<ThreadInServerFrameThreadType>(GetProcAddress(baseAddress, "ThreadInServerFrameThread")); -} diff --git a/NorthstarDLL/gameutils.h b/NorthstarDLL/gameutils.h deleted file mode 100644 index e86fa38c..00000000 --- a/NorthstarDLL/gameutils.h +++ /dev/null @@ -1,249 +0,0 @@ -#pragma once -#include "convar.h" - -// memory -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; -}; - -extern IMemAlloc* g_pMemAllocSingleton; -typedef IMemAlloc* (*CreateGlobalMemAllocType)(); -extern CreateGlobalMemAllocType CreateGlobalMemAlloc; - -// cmd.h -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_CLIENTCMD_CAN_EXECUTE. - 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; - -// compared to the defs i've seen, this is missing an arg, it could be nTickInterval or source, not sure, guessing it's source -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; - -// commandline stuff -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) {} - virtual void CreateCmdLine(int argc, char** argv) {} - virtual void unknown() {} - virtual const char* GetCmdLine(void) const {} - - virtual const char* CheckParm(const char* psz, const char** ppszValue = 0) const {} - virtual void RemoveParm() const {} - virtual void AppendParm(const char* pszParm, const char* pszValues) {} - - virtual const char* ParmValue(const char* psz, const char* pDefaultVal = 0) const {} - virtual int ParmValue(const char* psz, int nDefaultVal) const {} - virtual float ParmValue(const char* psz, float flDefaultVal) const {} - - virtual int ParmCount() const {} - virtual int FindParm(const char* psz) const {} - virtual const char* GetParm(int nIndex) const {} - virtual void SetParm(int nIndex, char const* pParm) {} - - // virtual const char** GetParms() const {} -}; - -// hoststate stuff -enum 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; - -// cengine stuff -enum EngineQuitState -{ - QUIT_NOTQUITTING = 0, - QUIT_TODESKTOP, - QUIT_RESTART -}; - -enum 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; - -enum server_state_t -{ - ss_dead = 0, // Dead - ss_loading, // Spawning - ss_active, // Running - ss_paused, // Running, but paused -}; - -extern server_state_t* sv_m_State; - -// network stuff -extern ConVar* Cvar_hostport; - -// playlist stuff -typedef const char* (*GetCurrentPlaylistType)(); -extern GetCurrentPlaylistType GetCurrentPlaylistName; - -typedef void (*SetCurrentPlaylistType)(const char* playlistName); -extern SetCurrentPlaylistType SetCurrentPlaylist; - -typedef void (*SetPlaylistVarOverrideType)(const char* varName, const char* value); -extern SetPlaylistVarOverrideType SetPlaylistVarOverride; - -typedef char* (*GetCurrentPlaylistVarType)(const char* varName, bool useOverrides); -extern GetCurrentPlaylistVarType GetCurrentPlaylistVar; - -// server entity stuff -typedef void* (*Server_GetEntityByIndexType)(int index); -extern Server_GetEntityByIndexType Server_GetEntityByIndex; - -// auth -extern char* g_LocalPlayerUserID; -extern char* g_LocalPlayerOriginToken; - -// misc stuff -typedef void (*ErrorType)(const char* fmt, ...); -extern ErrorType Error; - -typedef CCommandLine* (*CommandLineType)(); -extern CommandLineType CommandLine; - -typedef double (*Plat_FloatTimeType)(); -extern Plat_FloatTimeType Plat_FloatTime; - -typedef bool (*ThreadInServerFrameThreadType)(); -extern ThreadInServerFrameThreadType ThreadInServerFrameThread; - -typedef void* (*GetBaseLocalClientType)(); -extern GetBaseLocalClientType GetBaseLocalClient; - -void InitialiseEngineGameUtilFunctions(HMODULE baseAddress); -void InitialiseServerGameUtilFunctions(HMODULE baseAddress); -void InitialiseTier0GameUtilFunctions(HMODULE baseAddress); diff --git a/NorthstarDLL/hooks.cpp b/NorthstarDLL/hooks.cpp index 7f1d3b84..f0ea74e9 100644 --- a/NorthstarDLL/hooks.cpp +++ b/NorthstarDLL/hooks.cpp @@ -1,61 +1,196 @@ #include "pch.h" -#include "hooks.h" -#include "hookutils.h" -#include "sigscanning.h" #include "dedicated.h" +#include <iostream> #include <wchar.h> #include <iostream> #include <vector> #include <fstream> #include <sstream> #include <filesystem> +#include <Psapi.h> -typedef LPSTR (*GetCommandLineAType)(); -LPSTR GetCommandLineAHook(); +AUTOHOOK_INIT() -typedef HMODULE (*LoadLibraryExAType)(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); -HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); +// 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; -typedef HMODULE (*LoadLibraryAType)(LPCSTR lpLibFileName); -HMODULE LoadLibraryAHook(LPCSTR lpLibFileName); + 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]; + } + } + } -typedef HMODULE (*LoadLibraryExWType)(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); -HMODULE LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); + switch (side) + { + case eDllLoadCallbackSide::UNSIDED: + { + AddDllLoadCallback(dllName, callback, uniqueStr, reliesOnArray); + break; + } -typedef HMODULE (*LoadLibraryWType)(LPCWSTR lpLibFileName); -HMODULE LoadLibraryWHook(LPCWSTR lpLibFileName); + case eDllLoadCallbackSide::CLIENT: + { + AddDllLoadCallbackForClient(dllName, callback, uniqueStr, reliesOnArray); + break; + } -GetCommandLineAType GetCommandLineAOriginal; -LoadLibraryExAType LoadLibraryExAOriginal; -LoadLibraryAType LoadLibraryAOriginal; -LoadLibraryExWType LoadLibraryExWOriginal; -LoadLibraryWType LoadLibraryWOriginal; + case eDllLoadCallbackSide::DEDICATED_SERVER: + { + AddDllLoadCallbackForDedicatedServer(dllName, callback, uniqueStr, reliesOnArray); + break; + } + } +} -void InstallInitialHooks() +void __fileAutohook::Dispatch() { - if (MH_Initialize() != MH_OK) - spdlog::error("MH_Initialize (minhook initialization) failed"); + 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; +} + +// 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); +} - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, reinterpret_cast<void*>(&GetCommandLineA), &GetCommandLineAHook, reinterpret_cast<LPVOID*>(&GetCommandLineAOriginal)); - ENABLER_CREATEHOOK( - hook, reinterpret_cast<void*>(&LoadLibraryExA), &LoadLibraryExAHook, reinterpret_cast<LPVOID*>(&LoadLibraryExAOriginal)); - ENABLER_CREATEHOOK(hook, reinterpret_cast<void*>(&LoadLibraryA), &LoadLibraryAHook, reinterpret_cast<LPVOID*>(&LoadLibraryAOriginal)); - ENABLER_CREATEHOOK( - hook, reinterpret_cast<void*>(&LoadLibraryExW), &LoadLibraryExWHook, reinterpret_cast<LPVOID*>(&LoadLibraryExWOriginal)); - ENABLER_CREATEHOOK(hook, reinterpret_cast<void*>(&LoadLibraryW), &LoadLibraryWHook, reinterpret_cast<LPVOID*>(&LoadLibraryWOriginal)); +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); } -LPSTR GetCommandLineAHook() +AUTOHOOK_ABSOLUTEADDR(_GetCommandLineA, GetCommandLineA, +LPSTR, WINAPI, ()) { static char* cmdlineModified; static char* cmdlineOrg; if (cmdlineOrg == nullptr || cmdlineModified == nullptr) { - cmdlineOrg = GetCommandLineAOriginal(); + cmdlineOrg = _GetCommandLineA(); bool isDedi = strstr(cmdlineOrg, "-dedicated"); // well, this one has to be a real argument bool ignoreStartupArgs = strstr(cmdlineOrg, "-nostartupargs"); @@ -111,77 +246,86 @@ LPSTR GetCommandLineAHook() return cmdlineModified; } -// 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; - bool called; -}; - -std::vector<DllLoadCallback*> dllLoadCallbacks; - -void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback) -{ - DllLoadCallback* callbackStruct = new DllLoadCallback; - callbackStruct->dll = dll; - callbackStruct->callback = callback; - callbackStruct->called = false; - - dllLoadCallbacks.push_back(callbackStruct); -} - -void AddDllLoadCallbackForDedicatedServer(std::string dll, DllLoadCallbackFuncType callback) -{ - if (!IsDedicatedServer()) - return; - - DllLoadCallback* callbackStruct = new DllLoadCallback; - callbackStruct->dll = dll; - callbackStruct->callback = callback; - callbackStruct->called = false; - - dllLoadCallbacks.push_back(callbackStruct); -} - -void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callback) -{ - if (IsDedicatedServer()) - return; - - DllLoadCallback* callbackStruct = new DllLoadCallback; - callbackStruct->dll = dll; - callbackStruct->callback = callback; - callbackStruct->called = false; - - dllLoadCallbacks.push_back(callbackStruct); -} - +std::vector<std::string> calledTags; void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress) { - for (auto& callbackStruct : dllLoadCallbacks) + CModule cModule(moduleAddress); + + while (true) { - if (!callbackStruct->called && - strstr(lpLibFileName + (strlen(lpLibFileName) - callbackStruct->dll.length()), callbackStruct->dll.c_str()) != nullptr) + bool bDoneCalling = true; + + for (auto& callbackStruct : GetDllLoadCallbacks()) { - callbackStruct->callback(moduleAddress); - callbackStruct->called = true; + 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) { - for (auto& callbackStruct : dllLoadCallbacks) + CModule cModule(moduleAddress); + + while (true) { - std::wstring wcharStrDll = std::wstring(callbackStruct->dll.begin(), callbackStruct->dll.end()); - const wchar_t* callbackDll = wcharStrDll.c_str(); - if (!callbackStruct->called && wcsstr(lpLibFileName + (wcslen(lpLibFileName) - wcharStrDll.length()), callbackDll) != nullptr) + bool bDoneCalling = true; + + for (auto& callbackStruct : GetDllLoadCallbacks()) { - callbackStruct->callback(moduleAddress); - callbackStruct->called = true; + 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; } } @@ -208,50 +352,55 @@ void CallAllPendingDLLLoadCallbacks() } } -HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) -{ - HMODULE moduleAddress = LoadLibraryExAOriginal(lpLibFileName, hFile, dwFlags); +AUTOHOOK_ABSOLUTEADDR(_LoadLibraryExA, LoadLibraryExA, +HMODULE, WINAPI, (LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)) + { + HMODULE moduleAddress = _LoadLibraryExA(lpLibFileName, hFile, dwFlags); if (moduleAddress) - { CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); - } return moduleAddress; } -HMODULE LoadLibraryAHook(LPCSTR lpLibFileName) + +AUTOHOOK_ABSOLUTEADDR(_LoadLibraryA, LoadLibraryA, +HMODULE, WINAPI, (LPCSTR lpLibFileName)) { - HMODULE moduleAddress = LoadLibraryAOriginal(lpLibFileName); + HMODULE moduleAddress = _LoadLibraryA(lpLibFileName); if (moduleAddress) - { CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); - } return moduleAddress; } -HMODULE LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +AUTOHOOK_ABSOLUTEADDR(_LoadLibraryExW, LoadLibraryExW, +HMODULE, WINAPI, (LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)) { - HMODULE moduleAddress = LoadLibraryExWOriginal(lpLibFileName, hFile, dwFlags); + HMODULE moduleAddress = _LoadLibraryExW(lpLibFileName, hFile, dwFlags); if (moduleAddress) - { CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); - } return moduleAddress; } -HMODULE LoadLibraryWHook(LPCWSTR lpLibFileName) +AUTOHOOK_ABSOLUTEADDR(_LoadLibraryW, LoadLibraryW, +HMODULE, WINAPI, (LPCWSTR lpLibFileName)) { - HMODULE moduleAddress = LoadLibraryWOriginal(lpLibFileName); + HMODULE moduleAddress = _LoadLibraryW(lpLibFileName); if (moduleAddress) - { CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); - } return moduleAddress; } + +void InstallInitialHooks() +{ + if (MH_Initialize() != MH_OK) + spdlog::error("MH_Initialize (minhook initialization) failed"); + + AUTOHOOK_DISPATCH() +} diff --git a/NorthstarDLL/hooks.h b/NorthstarDLL/hooks.h index aca66491..feda40c7 100644 --- a/NorthstarDLL/hooks.h +++ b/NorthstarDLL/hooks.h @@ -1,11 +1,289 @@ #pragma once +#include "memory.h" + #include <string> +#include <iostream> void InstallInitialHooks(); -typedef void (*DllLoadCallbackFuncType)(HMODULE moduleAddress); -void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback); -void AddDllLoadCallbackForDedicatedServer(std::string dll, DllLoadCallbackFuncType callback); -void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callback); +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 __fileAutohook +{ + public: + std::vector<__autohook*> hooks; + + void Dispatch(); + void DispatchForModule(const char* pModuleName); +}; + +// 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: + { + // 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) + break; + + // 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); + + targetAddr = (LPVOID)((uintptr_t)pModuleAddr + iOffset); + break; + } + + case PROCADDRESS: + { + targetAddr = 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(*callingConvention 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(*callingConvention 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(*callingConvention 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(*callingConvention 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(pTarget, pDetour, ppOriginal, __STR(pDetour)) diff --git a/NorthstarDLL/hookutils.cpp b/NorthstarDLL/hookutils.cpp deleted file mode 100644 index dbb45f51..00000000 --- a/NorthstarDLL/hookutils.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "pch.h" -#include "hookutils.h" - -#include <iostream> - -void HookEnabler::CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOriginal, const char* targetName) -{ - // the macro for this uses ppTarget's name as targetName, and this typically starts with & - // targetname is used for debug stuff and debug output is nicer if we don't have this - if (*targetName == '&') - targetName++; - - if (MH_CreateHook(ppTarget, ppDetour, ppOriginal) == MH_OK) - { - HookTarget* target = new HookTarget; - target->targetAddress = ppTarget; - target->targetName = (char*)targetName; - - m_hookTargets.push_back(target); - } - else - { - if (targetName != nullptr) - spdlog::error("MH_CreateHook failed for function {}", targetName); - else - spdlog::error("MH_CreateHook failed for unknown function"); - } -} - -HookEnabler::~HookEnabler() -{ - for (auto& hook : m_hookTargets) - { - if (MH_EnableHook(hook->targetAddress) != MH_OK) - { - if (hook->targetName != nullptr) - spdlog::error("MH_EnableHook failed for function {}", hook->targetName); - else - spdlog::error("MH_EnableHook failed for unknown function"); - } - else - spdlog::info("Enabling hook {}", hook->targetName); - } -} diff --git a/NorthstarDLL/hookutils.h b/NorthstarDLL/hookutils.h deleted file mode 100644 index d0473d69..00000000 --- a/NorthstarDLL/hookutils.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include <vector> - -// Enables all hooks created with the HookEnabler object when it goes out of scope and handles hook errors -class HookEnabler -{ - private: - struct HookTarget - { - char* targetName; - LPVOID targetAddress; - }; - - std::vector<HookTarget*> m_hookTargets; - - public: - void CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOriginal, const char* targetName = nullptr); - ~HookEnabler(); -}; - -// macro to call HookEnabler::CreateHook with the hook's name -#define ENABLER_CREATEHOOK(enabler, ppTarget, ppDetour, ppOriginal) \ - enabler.CreateHook(ppTarget, reinterpret_cast<void*>(ppDetour), ppOriginal, #ppDetour) diff --git a/NorthstarDLL/host.cpp b/NorthstarDLL/host.cpp new file mode 100644 index 00000000..74dad785 --- /dev/null +++ b/NorthstarDLL/host.cpp @@ -0,0 +1,71 @@ +#include "pch.h" +#include "convar.h" +#include "modmanager.h" +#include "printcommand.h" +#include "printmaps.h" +#include "r2engine.h" + +AUTOHOOK_INIT() + +AUTOHOOK(Host_Init, engine.dll + 0x155EA0, +void,, (bool bDedicated)) +{ + spdlog::info("Host_Init()"); + Host_Init(bDedicated); + + // get all mod convars + std::vector<std::string> vModConvarNames; + for (auto& mod : g_pModManager->m_LoadedMods) + for (auto& cvar : mod.ConVars) + vModConvarNames.push_back(cvar->Name); + + // strip hidden and devonly cvar flags + int iNumCvarsAltered = 0; + for (auto& pair : R2::g_pCVar->DumpToMap()) + { + // don't remove from mod cvars + if (std::find(vModConvarNames.begin(), vModConvarNames.end(), pair.second->m_pszName) != vModConvarNames.end()) + continue; + + // 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 servers able to run disconnect on clients + R2::g_pCVar->FindCommand("disconnect")->m_nFlags |= FCVAR_SERVER_CAN_EXECUTE; + + // make clients able to run status and ping + R2::g_pCVar->FindCommand("status")->m_nFlags |= FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS; + R2::g_pCVar->FindCommand("ping")->m_nFlags |= FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS; + + // 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/hoststate.cpp b/NorthstarDLL/hoststate.cpp new file mode 100644 index 00000000..6008a0f2 --- /dev/null +++ b/NorthstarDLL/hoststate.cpp @@ -0,0 +1,104 @@ +#include "pch.h" +#include "hoststate.h" +#include "masterserver.h" +#include "serverauthentication.h" +#include "serverpresence.h" +#include "playlist.h" +#include "tier0.h" +#include "r2engine.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); +} + +AUTOHOOK(CHostState__State_NewGame, engine.dll + 0x16E7D0, +void,, (CHostState* self)) +{ + 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"); + + // don't require authentication on singleplayer startup + g_pServerAuthentication->m_bRequireClientAuth = strncmp(g_pHostState->m_levelName, "sp_", 3); + + 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; +} + +AUTOHOOK(CHostState__State_ChangeLevelMP, engine.dll + 0x16E520, +void,, (CHostState* self)) +{ + 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); +} + +AUTOHOOK(CHostState__State_GameShutdown, engine.dll + 0x16E520, +void,, (CHostState* self)) +{ + spdlog::info("HostState: GameShutdown"); + + g_pServerPresence->DestroyPresence(); + g_pServerAuthentication->StopPlayerAuthServer(); + + CHostState__State_GameShutdown(self); +} + +AUTOHOOK(CHostState__FrameUpdate, engine.dll + 0x16DB00, +void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime)) +{ + CHostState__FrameUpdate(self, flCurrentTime, flFrameTime); + + if (*R2::g_pServerState == R2::server_state_t::ss_active) + { + // update server presence + g_pServerPresence->RunFrame(flCurrentTime); + } +} + +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/hoststate.h b/NorthstarDLL/hoststate.h new file mode 100644 index 00000000..a77385ef --- /dev/null +++ b/NorthstarDLL/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/include/MinHook.x64.lib b/NorthstarDLL/include/MinHook.x64.lib Binary files differindex 213df08f..a346f386 100644 --- a/NorthstarDLL/include/MinHook.x64.lib +++ b/NorthstarDLL/include/MinHook.x64.lib diff --git a/NorthstarDLL/include/libcrypto_static.lib b/NorthstarDLL/include/libcrypto_static.lib Binary files differindex 26c68964..0ddd830c 100644 --- a/NorthstarDLL/include/libcrypto_static.lib +++ b/NorthstarDLL/include/libcrypto_static.lib diff --git a/NorthstarDLL/include/libcurl/lib/libcurl_a.lib b/NorthstarDLL/include/libcurl/lib/libcurl_a.lib Binary files differindex f0fa49f2..32b917c9 100644 --- a/NorthstarDLL/include/libcurl/lib/libcurl_a.lib +++ b/NorthstarDLL/include/libcurl/lib/libcurl_a.lib diff --git a/NorthstarDLL/include/libssl_static.lib b/NorthstarDLL/include/libssl_static.lib Binary files differindex 3411cb57..f7916421 100644 --- a/NorthstarDLL/include/libssl_static.lib +++ b/NorthstarDLL/include/libssl_static.lib diff --git a/NorthstarDLL/keyvalues.cpp b/NorthstarDLL/keyvalues.cpp index 0d829de9..69965e89 100644 --- a/NorthstarDLL/keyvalues.cpp +++ b/NorthstarDLL/keyvalues.cpp @@ -1,45 +1,16 @@ #include "pch.h" -#include "keyvalues.h" #include "modmanager.h" #include "filesystem.h" -#include "hookutils.h" #include <fstream> -// hook forward defs -typedef char (*KeyValues__LoadFromBufferType)( - void* self, const char* resourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7); -KeyValues__LoadFromBufferType KeyValues__LoadFromBuffer; -char KeyValues__LoadFromBufferHook( - void* self, const char* resourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7); - -void InitialiseKeyValues(HMODULE baseAddress) -{ - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x426C30, &KeyValues__LoadFromBufferHook, reinterpret_cast<LPVOID*>(&KeyValues__LoadFromBuffer)); -} - -void* savedFilesystemPtr; - -char KeyValues__LoadFromBufferHook(void* self, const char* resourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7) -{ - // 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 - if (pFileSystem != nullptr) - savedFilesystemPtr = pFileSystem; - if (!pFileSystem && !strcmp(resourceName, "playlists")) - pFileSystem = savedFilesystemPtr; - - return KeyValues__LoadFromBuffer(self, resourceName, pBuffer, pFileSystem, a5, a6, a7); -} +AUTOHOOK_INIT() void ModManager::TryBuildKeyValues(const char* filename) { spdlog::info("Building KeyValues for file {}", filename); - std::string normalisedPath = fs::path(filename).lexically_normal().string(); + std::string normalisedPath = g_pModManager->NormaliseModFilePath(fs::path(filename)); fs::path compiledPath = GetCompiledAssetsPath() / filename; fs::path compiledDir = compiledPath.parent_path(); fs::create_directories(compiledDir); @@ -54,14 +25,14 @@ void ModManager::TryBuildKeyValues(const char* filename) // 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--) + for (int64_t i = m_LoadedMods.size() - 1; i > -1; i--) { - if (!m_loadedMods[i].Enabled) + 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()) + 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" @@ -76,7 +47,7 @@ void ModManager::TryBuildKeyValues(const char* filename) fs::remove(compiledDir / patchFilePath); - fs::copy_file(m_loadedMods[i].ModDirectory / "keyvalues" / filename, compiledDir / patchFilePath); + fs::copy_file(m_LoadedMods[i].m_ModDirectory / "keyvalues" / filename, compiledDir / patchFilePath); } } @@ -86,7 +57,7 @@ void ModManager::TryBuildKeyValues(const char* filename) 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); + std::string originalFile = R2::ReadVPKOriginalFile(filename); if (!originalFile.length()) { @@ -96,7 +67,6 @@ void ModManager::TryBuildKeyValues(const char* filename) char rootName[64]; memset(rootName, 0, sizeof(rootName)); - rootName[63] = '\0'; // iterate until we hit an ascii char that isn't in a # command or comment to get root obj name int i = 0; @@ -127,11 +97,34 @@ void ModManager::TryBuildKeyValues(const char* filename) writeStream.close(); ModOverrideFile overrideFile; - overrideFile.owningMod = nullptr; - overrideFile.path = normalisedPath; + overrideFile.m_pOwningMod = nullptr; + overrideFile.m_Path = normalisedPath; - if (m_modFiles.find(normalisedPath) == m_modFiles.end()) - m_modFiles.insert(std::make_pair(normalisedPath, overrideFile)); + if (m_ModFiles.find(normalisedPath) == m_ModFiles.end()) + m_ModFiles.insert(std::make_pair(normalisedPath, overrideFile)); else - m_modFiles[normalisedPath] = overrideFile; + m_ModFiles[normalisedPath] = overrideFile; +} + +AUTOHOOK(KeyValues__LoadFromBuffer, engine.dll + 0x426C30, +char,, (void* self, const char* resourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7)) +{ + 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(resourceName, "playlists")) + pFileSystem = pSavedFilesystemPtr; + + return KeyValues__LoadFromBuffer(self, resourceName, pBuffer, pFileSystem, a5, a6, a7); +} + +ON_DLL_LOAD("engine.dll", KeyValues, (CModule module)) +{ + AUTOHOOK_DISPATCH() } diff --git a/NorthstarDLL/keyvalues.h b/NorthstarDLL/keyvalues.h deleted file mode 100644 index 8f931e3f..00000000 --- a/NorthstarDLL/keyvalues.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseKeyValues(HMODULE baseAddress); diff --git a/NorthstarDLL/languagehooks.cpp b/NorthstarDLL/languagehooks.cpp index 38435f93..9e5dda86 100644 --- a/NorthstarDLL/languagehooks.cpp +++ b/NorthstarDLL/languagehooks.cpp @@ -1,18 +1,13 @@ #include "pch.h" -#include "languagehooks.h" -#include "gameutils.h" +#include "tier0.h" + #include <filesystem> #include <regex> -namespace fs = std::filesystem; - -typedef char* (*GetGameLanguageType)(); -char* GetGameLanguage(); +AUTOHOOK_INIT() typedef LANGID (*Tier0_DetectDefaultLanguageType)(); -GetGameLanguageType GetGameLanguageOriginal; - bool CheckLangAudioExists(char* lang) { std::string path {"r2\\sound\\general_"}; @@ -52,7 +47,8 @@ std::string GetAnyInstalledAudioLanguage() return "NO LANGUAGE DETECTED"; } -char* GetGameLanguageHook() +AUTOHOOK(GetGameLanguage, tier0.dll + 0xF560, +char*,, ()) { auto tier0Handle = GetModuleHandleA("tier0.dll"); auto Tier0_DetectDefaultLanguageType = GetProcAddress(tier0Handle, "Tier0_DetectDefaultLanguage"); @@ -60,7 +56,7 @@ char* GetGameLanguageHook() bool& canOriginDictateLang = *(bool*)((char*)tier0Handle + 0xA9A90); const char* forcedLanguage; - if (CommandLine()->CheckParm("-language", &forcedLanguage)) + if (Tier0::CommandLine()->CheckParm("-language", &forcedLanguage)) { if (!CheckLangAudioExists((char*)forcedLanguage)) { @@ -79,7 +75,7 @@ char* GetGameLanguageHook() canOriginDictateLang = true; // let it try { - auto lang = GetGameLanguageOriginal(); + auto lang = GetGameLanguage(); if (!CheckLangAudioExists(lang)) { if (strcmp(lang, "russian") != @@ -97,7 +93,7 @@ char* GetGameLanguageHook() 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 = GetGameLanguageOriginal(); + auto lang = GetGameLanguage(); spdlog::info("Detected system language: {}", lang); if (!CheckLangAudioExists(lang)) { @@ -112,8 +108,7 @@ char* GetGameLanguageHook() return lang; } -void InitialiseTier0LanguageHooks(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT("tier0.dll", LanguageHooks, (CModule module)) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xF560, &GetGameLanguageHook, reinterpret_cast<LPVOID*>(&GetGameLanguageOriginal)); + AUTOHOOK_DISPATCH() } diff --git a/NorthstarDLL/languagehooks.h b/NorthstarDLL/languagehooks.h deleted file mode 100644 index 55b591e0..00000000 --- a/NorthstarDLL/languagehooks.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseTier0LanguageHooks(HMODULE baseAddress); diff --git a/NorthstarDLL/latencyflex.cpp b/NorthstarDLL/latencyflex.cpp index bb274dab..7b5a8ebf 100644 --- a/NorthstarDLL/latencyflex.cpp +++ b/NorthstarDLL/latencyflex.cpp @@ -1,76 +1,42 @@ #include "pch.h" -#include "latencyflex.h" -#include "hookutils.h" #include "convar.h" -typedef void (*OnRenderStartType)(); -OnRenderStartType OnRenderStart; +AUTOHOOK_INIT() ConVar* Cvar_r_latencyflex; -HMODULE m_lfxModule {}; -typedef void (*PFN_lfx_WaitAndBeginFrame)(); -PFN_lfx_WaitAndBeginFrame m_lfx_WaitAndBeginFrame {}; +void (*m_winelfx_WaitAndBeginFrame)(); -void OnRenderStartHook() +AUTOHOOK(OnRenderStart, client.dll + 0x1952C0, +void,, ()) { - // Sleep before next frame as needed to reduce latency. - if (Cvar_r_latencyflex->GetInt()) - { - if (m_lfx_WaitAndBeginFrame) - { - m_lfx_WaitAndBeginFrame(); - } - } + if (Cvar_r_latencyflex->GetBool() && m_winelfx_WaitAndBeginFrame) + m_winelfx_WaitAndBeginFrame(); OnRenderStart(); } -void InitialiseLatencyFleX(HMODULE baseAddress) +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/ - const auto lfxModuleName = "latencyflex_layer.dll"; - const auto lfxModuleNameFallback = "latencyflex_wine.dll"; - auto useFallbackEntrypoints = false; - - // Load LatencyFleX library. - m_lfxModule = ::LoadLibraryA(lfxModuleName); - if (m_lfxModule == nullptr && ::GetLastError() == ERROR_MOD_NOT_FOUND) - { - spdlog::info("LFX: Primary LatencyFleX library not found, trying fallback."); - - m_lfxModule = ::LoadLibraryA(lfxModuleNameFallback); - if (m_lfxModule == nullptr) - { - if (::GetLastError() == ERROR_MOD_NOT_FOUND) - { - spdlog::info("LFX: Fallback LatencyFleX library not found."); - } - else - { - spdlog::info("LFX: Error loading fallback LatencyFleX library - Code: {}", ::GetLastError()); - } - - return; - } - - useFallbackEntrypoints = true; - } - else if (m_lfxModule == nullptr) + 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("LFX: Error loading primary LatencyFleX library - Code: {}", ::GetLastError()); + spdlog::info("Unable to load LatencyFleX library, LatencyFleX disabled."); return; } - m_lfx_WaitAndBeginFrame = reinterpret_cast<PFN_lfx_WaitAndBeginFrame>(reinterpret_cast<void*>( - GetProcAddress(m_lfxModule, !useFallbackEntrypoints ? "lfx_WaitAndBeginFrame" : "winelfx_WaitAndBeginFrame"))); - - spdlog::info("LFX: Initialized."); + 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."); - - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1952C0, &OnRenderStartHook, reinterpret_cast<LPVOID*>(&OnRenderStart)); } diff --git a/NorthstarDLL/latencyflex.h b/NorthstarDLL/latencyflex.h deleted file mode 100644 index 663d9ec6..00000000 --- a/NorthstarDLL/latencyflex.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void InitialiseLatencyFleX(HMODULE baseAddress); diff --git a/NorthstarDLL/limits.cpp b/NorthstarDLL/limits.cpp new file mode 100644 index 00000000..f1dc23bd --- /dev/null +++ b/NorthstarDLL/limits.cpp @@ -0,0 +1,187 @@ +#include "pch.h" +#include "limits.h" +#include "hoststate.h" +#include "r2client.h" +#include "r2engine.h" +#include "tier0.h" +#include "serverauthentication.h" + +AUTOHOOK_INIT() + +ServerLimitsManager* g_pServerLimits; + +ConVar* Cvar_net_datablock_enabled; + +void ServerLimitsManager::AddPlayer(R2::CBaseClient* player) +{ + PlayerLimitData limitData; + m_PlayerLimitData.insert(std::make_pair(player, limitData)); +} + +bool ServerLimitsManager::CheckStringCommandLimits(R2::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 (Tier0::Plat_FloatTime() - m_PlayerLimitData[player].lastClientCommandQuotaStart >= 1.0) + { + // reset quota + m_PlayerLimitData[player].lastClientCommandQuotaStart = Tier0::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(R2::CBaseClient* player) +{ + if (Tier0::Plat_FloatTime() - m_PlayerLimitData[player].lastSayTextLimitStart >= 1.0) + { + m_PlayerLimitData[player].lastSayTextLimitStart = Tier0::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; +} + +AUTOHOOK(CNetChan__ProcessMessages, engine.dll + 0x2140A0, +char, __fastcall, (void* self, void* buf)) +{ + enum eNetChanLimitMode + { + NETCHANLIMIT_WARN, + NETCHANLIMIT_KICK + }; + + double startTime = Tier0::Plat_FloatTime(); + char ret = CNetChan__ProcessMessages(self, buf); + + // check processing limits, unless we're in a level transition + if (R2::g_pHostState->m_iCurrentState == R2::HostState_t::HS_RUN && Tier0::ThreadInServerFrameThread()) + { + // player that sent the message + R2::CBaseClient* sender = *(R2::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 += + (Tier0::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(R2::g_pLocalPlayerUserID, sender->m_UID)) + { + R2::CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit"); + return false; + } + } + } + + return ret; +} + +AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800, +bool, , (void* a1, R2::netpacket_t* packet)) +{ + if (packet->adr.type == R2::NA_IP && + (!(packet->data[4] == 'N' && Cvar_net_datablock_enabled->GetBool()) || !Cvar_net_datablock_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 (Tier0::Plat_FloatTime() < sendData->timeoutEnd) + return false; + + if (Tier0::Plat_FloatTime() - sendData->lastQuotaStart >= 1.0) + { + sendData->lastQuotaStart = Tier0::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 = Tier0::Plat_FloatTime() + 60.0; + return false; + } + } + + return ProcessConnectionlessPacket(a1, packet); +} + +ON_DLL_LOAD_RELIESON("engine.dll", ServerLimits, ConVar, (CModule module)) +{ + AUTOHOOK_DISPATCH() + + 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, ""); + + Cvar_net_datablock_enabled = R2::g_pCVar->FindVar("net_datablock_enabled"); +} diff --git a/NorthstarDLL/limits.h b/NorthstarDLL/limits.h new file mode 100644 index 00000000..943e07b6 --- /dev/null +++ b/NorthstarDLL/limits.h @@ -0,0 +1,44 @@ +#pragma once +#include "r2engine.h" +#include "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; +}; + +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; + + std::unordered_map<R2::CBaseClient*, PlayerLimitData> m_PlayerLimitData; + std::vector<UnconnectedPlayerLimitData> m_UnconnectedPlayerLimitData; + + public: + void AddPlayer(R2::CBaseClient* player); + bool CheckStringCommandLimits(R2::CBaseClient* player); + bool CheckChatLimits(R2::CBaseClient* player); +}; + +extern ServerLimitsManager* g_pServerLimits; diff --git a/NorthstarDLL/localchatwriter.cpp b/NorthstarDLL/localchatwriter.cpp index 72a5afa7..2c661feb 100644 --- a/NorthstarDLL/localchatwriter.cpp +++ b/NorthstarDLL/localchatwriter.cpp @@ -37,12 +37,11 @@ class vgui_BaseRichText_vtable void(__fastcall* SetVerticalScrollbar)(vgui_BaseRichText* self, bool state); void(__fastcall* SetMaximumCharCount)(vgui_BaseRichText* self, int maxChars); - void(__fastcall* InsertColorChange)(vgui_BaseRichText* self, vgui_Color col); + 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, vgui_Color URLTextColor, vgui_Color normalTextColor); + 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); @@ -81,25 +80,25 @@ LocalChatWriter::SwatchColor swatchColors[4] = { LocalChatWriter::NetworkNameColor, }; -vgui_Color darkColors[8] = { - vgui_Color {0, 0, 0, 255}, - vgui_Color {205, 49, 49, 255}, - vgui_Color {13, 188, 121, 255}, - vgui_Color {229, 229, 16, 255}, - vgui_Color {36, 114, 200, 255}, - vgui_Color {188, 63, 188, 255}, - vgui_Color {17, 168, 205, 255}, - vgui_Color {229, 229, 229, 255}}; - -vgui_Color lightColors[8] = { - vgui_Color {102, 102, 102, 255}, - vgui_Color {241, 76, 76, 255}, - vgui_Color {35, 209, 139, 255}, - vgui_Color {245, 245, 67, 255}, - vgui_Color {59, 142, 234, 255}, - vgui_Color {214, 112, 214, 255}, - vgui_Color {41, 184, 219, 255}, - vgui_Color {255, 255, 255, 255}}; +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 { @@ -144,7 +143,7 @@ class AnsiEscapeParser LocalChatWriter* m_writer; Next m_next = Next::ControlType; - vgui_Color m_expandedColor {0, 0, 0, 0}; + Color m_expandedColor {0, 0, 0, 0}; Next HandleControlType(unsigned long val) { @@ -190,7 +189,7 @@ class AnsiEscapeParser // Next values are r,g,b if (val == 2) { - m_expandedColor = {0, 0, 0, 255}; + m_expandedColor.SetColor(0, 0, 0, 255); return Next::ForegroundR; } // Next value is 8-bit swatch color @@ -220,12 +219,12 @@ class AnsiEscapeParser unsigned char green = ((code - blue) / 6) % 6; unsigned char red = (code - blue - (green * 6)) / 36; m_writer->InsertColorChange( - vgui_Color {(unsigned char)(red * 51), (unsigned char)(green * 51), (unsigned char)(blue * 51), 255}); + 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(vgui_Color {brightness, brightness, brightness, 255}); + m_writer->InsertColorChange(Color {brightness, brightness, brightness, 255}); } return Next::ControlType; @@ -236,7 +235,7 @@ class AnsiEscapeParser if (val >= UCHAR_MAX) return Next::ControlType; - m_expandedColor.r = (unsigned char)val; + m_expandedColor[0] = (unsigned char)val; return Next::ForegroundG; } @@ -245,7 +244,7 @@ class AnsiEscapeParser if (val >= UCHAR_MAX) return Next::ControlType; - m_expandedColor.g = (unsigned char)val; + m_expandedColor[1] = (unsigned char)val; return Next::ForegroundB; } @@ -254,7 +253,7 @@ class AnsiEscapeParser if (val >= UCHAR_MAX) return Next::ControlType; - m_expandedColor.b = (unsigned char)val; + m_expandedColor[2] = (unsigned char)val; m_writer->InsertColorChange(m_expandedColor); return Next::ControlType; } @@ -280,12 +279,11 @@ void LocalChatWriter::Write(const char* str) 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(writeBuffer, str, copyChars); - writeBuffer[copyChars] = 0; + + strncpy_s(writeBuffer, copyChars + 1, str, copyChars); InsertText(writeBuffer); } @@ -320,6 +318,8 @@ void LocalChatWriter::InsertChar(wchar_t ch) void LocalChatWriter::InsertText(const char* str) { + spdlog::info(str); + WCHAR messageUnicode[288]; ConvertANSIToUnicode(str, -1, messageUnicode, 274); @@ -347,7 +347,7 @@ void LocalChatWriter::InsertText(const wchar_t* str) InsertDefaultFade(); } -void LocalChatWriter::InsertColorChange(vgui_Color color) +void LocalChatWriter::InsertColorChange(Color color) { for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next) { @@ -358,20 +358,24 @@ void LocalChatWriter::InsertColorChange(vgui_Color color) } } -static vgui_Color GetHudSwatchColor(CHudChat* hud, LocalChatWriter::SwatchColor swatchColor) +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 vgui_Color {0, 0, 0, 0}; + + return Color(0, 0, 0, 0); } void LocalChatWriter::InsertSwatchColorChange(SwatchColor swatchColor) @@ -436,12 +440,12 @@ void LocalChatWriter::InsertDefaultFade() } } -void InitialiseLocalChatWriter(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT("client.dll", LocalChatWriter, (CModule module)) { - gGameSettings = (CGameSettings**)((char*)baseAddress + 0x11BAA48); - gChatFadeLength = (CGameFloatVar**)((char*)baseAddress + 0x11BAB78); - gChatFadeSustain = (CGameFloatVar**)((char*)baseAddress + 0x11BAC08); - CHudChat::allHuds = (CHudChat**)((char*)baseAddress + 0x11BA9E8); + gGameSettings = module.Offset(0x11BAA48).As<CGameSettings**>(); + gChatFadeLength = module.Offset(0x11BAB78).As<CGameFloatVar**>(); + gChatFadeSustain = module.Offset(0x11BAC08).As<CGameFloatVar**>(); + CHudChat::allHuds = module.Offset(0x11BA9E8).As<CHudChat**>(); - ConvertANSIToUnicode = (ConvertANSIToUnicodeType)((char*)baseAddress + 0x7339A0); + ConvertANSIToUnicode = module.Offset(0x7339A0).As<ConvertANSIToUnicodeType>(); } diff --git a/NorthstarDLL/localchatwriter.h b/NorthstarDLL/localchatwriter.h index 8048e084..0df0cac8 100644 --- a/NorthstarDLL/localchatwriter.h +++ b/NorthstarDLL/localchatwriter.h @@ -1,13 +1,6 @@ #pragma once #include "pch.h" - -struct vgui_Color -{ - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; -}; +#include "color.h" class vgui_BaseRichText; @@ -18,10 +11,10 @@ class CHudChat char unknown1[720]; - vgui_Color m_sameTeamColor; - vgui_Color m_enemyTeamColor; - vgui_Color m_mainTextColor; - vgui_Color m_networkNameColor; + Color m_sameTeamColor; + Color m_enemyTeamColor; + Color m_mainTextColor; + Color m_networkNameColor; char unknown2[12]; @@ -61,7 +54,7 @@ class LocalChatWriter void InsertChar(wchar_t ch); void InsertText(const char* str); void InsertText(const wchar_t* str); - void InsertColorChange(vgui_Color color); + void InsertColorChange(Color color); void InsertSwatchColorChange(SwatchColor color); private: @@ -70,5 +63,3 @@ class LocalChatWriter const char* ApplyAnsiEscape(const char* escape); void InsertDefaultFade(); }; - -void InitialiseLocalChatWriter(HMODULE baseAddress); diff --git a/NorthstarDLL/logging.cpp b/NorthstarDLL/logging.cpp index 8c7c82ec..e82c2131 100644 --- a/NorthstarDLL/logging.cpp +++ b/NorthstarDLL/logging.cpp @@ -1,252 +1,19 @@ #include "pch.h" #include "logging.h" -#include "sourceconsole.h" -#include "spdlog/sinks/basic_file_sink.h" -#include "hookutils.h" -#include "dedicated.h" #include "convar.h" -#include <iomanip> -#include <sstream> +#include "concommand.h" #include "nsprefix.h" -#include <dbghelp.h> - -// 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 - { - // 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(¤tTime, (GetNorthstarPrefix() + "/logs/nslog%Y-%m-%d %H-%M-%S.txt").c_str()); - spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false)); - spdlog::flush_on(spdlog::level::info); - } -} - -long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) -{ - static bool logged = false; - if (logged) - return EXCEPTION_CONTINUE_SEARCH; - - if (!IsDebuggerPresent()) - { - const DWORD exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode; - if (exceptionCode != EXCEPTION_ACCESS_VIOLATION && exceptionCode != EXCEPTION_ARRAY_BOUNDS_EXCEEDED && - exceptionCode != EXCEPTION_DATATYPE_MISALIGNMENT && exceptionCode != EXCEPTION_FLT_DENORMAL_OPERAND && - exceptionCode != EXCEPTION_FLT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_FLT_INEXACT_RESULT && - exceptionCode != EXCEPTION_FLT_INVALID_OPERATION && exceptionCode != EXCEPTION_FLT_OVERFLOW && - exceptionCode != EXCEPTION_FLT_STACK_CHECK && exceptionCode != EXCEPTION_FLT_UNDERFLOW && - exceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION && exceptionCode != EXCEPTION_IN_PAGE_ERROR && - exceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_INT_OVERFLOW && - exceptionCode != EXCEPTION_INVALID_DISPOSITION && exceptionCode != EXCEPTION_NONCONTINUABLE_EXCEPTION && - exceptionCode != EXCEPTION_PRIV_INSTRUCTION && exceptionCode != EXCEPTION_STACK_OVERFLOW) - return EXCEPTION_CONTINUE_SEARCH; - - std::stringstream exceptionCause; - exceptionCause << "Cause: "; - switch (exceptionCode) - { - case EXCEPTION_ACCESS_VIOLATION: - case EXCEPTION_IN_PAGE_ERROR: - { - exceptionCause << "Access Violation" << std::endl; - - auto exceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0]; - auto exceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1]; - - if (!exceptionInfo0) - exceptionCause << "Attempted to read from: 0x" << (void*)exceptionInfo1; - else if (exceptionInfo0 == 1) - exceptionCause << "Attempted to write to: 0x" << (void*)exceptionInfo1; - else if (exceptionInfo0 == 8) - exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex << exceptionInfo1; - else - exceptionCause << "Unknown access violation at: 0x" << (void*)exceptionInfo1; - - break; - } - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - exceptionCause << "Array bounds exceeded"; - break; - case EXCEPTION_DATATYPE_MISALIGNMENT: - exceptionCause << "Datatype misalignment"; - break; - case EXCEPTION_FLT_DENORMAL_OPERAND: - exceptionCause << "Denormal operand"; - break; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - exceptionCause << "Divide by zero (float)"; - break; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - exceptionCause << "Divide by zero (int)"; - break; - case EXCEPTION_FLT_INEXACT_RESULT: - exceptionCause << "Inexact result"; - break; - case EXCEPTION_FLT_INVALID_OPERATION: - exceptionCause << "Invalid operation"; - break; - case EXCEPTION_FLT_OVERFLOW: - case EXCEPTION_INT_OVERFLOW: - exceptionCause << "Numeric overflow"; - break; - case EXCEPTION_FLT_UNDERFLOW: - exceptionCause << "Numeric underflow"; - break; - case EXCEPTION_FLT_STACK_CHECK: - exceptionCause << "Stack check"; - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - exceptionCause << "Illegal instruction"; - break; - case EXCEPTION_INVALID_DISPOSITION: - exceptionCause << "Invalid disposition"; - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - exceptionCause << "Noncontinuable exception"; - break; - case EXCEPTION_PRIV_INSTRUCTION: - exceptionCause << "Priviledged instruction"; - break; - case EXCEPTION_STACK_OVERFLOW: - exceptionCause << "Stack overflow"; - break; - default: - exceptionCause << "Unknown"; - break; - } - - void* exceptionAddress = exceptionInfo->ExceptionRecord->ExceptionAddress; - - HMODULE crashedModuleHandle; - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(exceptionAddress), &crashedModuleHandle); - - MODULEINFO crashedModuleInfo; - GetModuleInformation(GetCurrentProcess(), crashedModuleHandle, &crashedModuleInfo, sizeof(crashedModuleInfo)); - - char crashedModuleFullName[MAX_PATH]; - GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, crashedModuleFullName, MAX_PATH); - char* crashedModuleName = strrchr(crashedModuleFullName, '\\') + 1; - - DWORD64 crashedModuleOffset = ((DWORD64)exceptionAddress) - ((DWORD64)crashedModuleInfo.lpBaseOfDll); - CONTEXT* exceptionContext = exceptionInfo->ContextRecord; - - spdlog::error("Northstar has crashed! a minidump has been written and exception info is available below:"); - spdlog::error(exceptionCause.str()); - spdlog::error("At: {} + {}", crashedModuleName, (void*)crashedModuleOffset); - - PVOID framesToCapture[62]; - int frames = RtlCaptureStackBackTrace(0, 62, framesToCapture, NULL); - for (int i = 0; i < frames; i++) - { - HMODULE backtraceModuleHandle; - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(framesToCapture[i]), &backtraceModuleHandle); - - char backtraceModuleFullName[MAX_PATH]; - GetModuleFileNameExA(GetCurrentProcess(), backtraceModuleHandle, backtraceModuleFullName, MAX_PATH); - char* backtraceModuleName = strrchr(backtraceModuleFullName, '\\') + 1; - - void* actualAddress = (void*)framesToCapture[i]; - void* relativeAddress = (void*)(uintptr_t(actualAddress) - uintptr_t(backtraceModuleHandle)); - - spdlog::error(" {} + {} ({})", backtraceModuleName, relativeAddress, actualAddress); - } - - spdlog::error("RAX: 0x{0:x}", exceptionContext->Rax); - spdlog::error("RBX: 0x{0:x}", exceptionContext->Rbx); - spdlog::error("RCX: 0x{0:x}", exceptionContext->Rcx); - spdlog::error("RDX: 0x{0:x}", exceptionContext->Rdx); - spdlog::error("RSI: 0x{0:x}", exceptionContext->Rsi); - spdlog::error("RDI: 0x{0:x}", exceptionContext->Rdi); - spdlog::error("RBP: 0x{0:x}", exceptionContext->Rbp); - spdlog::error("RSP: 0x{0:x}", exceptionContext->Rsp); - spdlog::error("R8: 0x{0:x}", exceptionContext->R8); - spdlog::error("R9: 0x{0:x}", exceptionContext->R9); - spdlog::error("R10: 0x{0:x}", exceptionContext->R10); - spdlog::error("R11: 0x{0:x}", exceptionContext->R11); - spdlog::error("R12: 0x{0:x}", exceptionContext->R12); - spdlog::error("R13: 0x{0:x}", exceptionContext->R13); - spdlog::error("R14: 0x{0:x}", exceptionContext->R14); - spdlog::error("R15: 0x{0:x}", exceptionContext->R15); - - time_t time = std::time(nullptr); - tm currentTime = *std::localtime(&time); - std::stringstream stream; - stream << std::put_time(¤tTime, (GetNorthstarPrefix() + "/logs/nsdump%Y-%m-%d %H-%M-%S.dmp").c_str()); - - auto 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 = exceptionInfo; - 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()); - - if (!IsDedicatedServer()) - MessageBoxA( - 0, "Northstar has crashed! Crash info can be found in R2Northstar/logs", "Northstar has crashed!", MB_ICONERROR | MB_OK); - } - - logged = true; - return EXCEPTION_EXECUTE_HANDLER; -} - -HANDLE hExceptionFilter; - -BOOL WINAPI ConsoleHandlerRoutine(DWORD eventCode) -{ - switch (eventCode) - { - case CTRL_CLOSE_EVENT: - // User closed console, shut everything down - spdlog::info("Exiting due to console close..."); - RemoveVectoredExceptionHandler(hExceptionFilter); - exit(EXIT_SUCCESS); - return FALSE; - } - - return TRUE; -} +#include "bitbuf.h" +#include "spdlog/sinks/basic_file_sink.h" -void InitialiseLogging() -{ - hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter); +#include <iomanip> +#include <sstream> - AllocConsole(); - // these two lines are responsible for stuff to not show up in the console sometimes, from talking about it on discord - // apparently they were meant to make logging work when using -northstar, however from testing it seems that it doesnt - // work regardless of these two lines - // freopen("CONOUT$", "w", stdout); - // freopen("CONOUT$", "w", stderr); - spdlog::default_logger()->set_pattern("[%H:%M:%S] [%^%l%$] %v"); - - SetConsoleCtrlHandler(ConsoleHandlerRoutine, true); -} +AUTOHOOK_INIT() ConVar* Cvar_spewlog_enable; -enum SpewType_t +enum class SpewType_t { SPEW_MESSAGE = 0, @@ -258,56 +25,23 @@ enum SpewType_t SPEW_TYPE_COUNT }; -typedef void (*EngineSpewFuncType)(); -EngineSpewFuncType EngineSpewFunc; +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"} +}; -void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, va_list args) +AUTOHOOK(EngineSpewFunc, engine.dll + 0x11CA80, +void,, (void* pEngineServer, SpewType_t type, const char* format, va_list args)) { if (!Cvar_spewlog_enable->GetBool()) return; - const char* typeStr; - switch (type) - { - case SPEW_MESSAGE: - { - typeStr = "SPEW_MESSAGE"; - break; - } - - case SPEW_WARNING: - { - typeStr = "SPEW_WARNING"; - break; - } - - case SPEW_ASSERT: - { - typeStr = "SPEW_ASSERT"; - break; - } - - case SPEW_ERROR: - { - typeStr = "SPEW_ERROR"; - break; - } - - case SPEW_LOG: - { - typeStr = "SPEW_LOG"; - break; - } - - default: - { - typeStr = "SPEW_UNKNOWN"; - break; - } - } - + const char* typeStr = PrintSpewTypes.at(type); char formatted[2048] = {0}; - bool shouldFormat = true; + 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 @@ -354,19 +88,17 @@ void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, default: { - shouldFormat = false; + bShouldFormat = false; break; } } } } - if (shouldFormat) + 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') @@ -375,10 +107,9 @@ void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, spdlog::info("[SERVER {}] {}", typeStr, formatted); } -typedef void (*Status_ConMsg_Type)(const char* text, ...); -Status_ConMsg_Type Status_ConMsg_Original; - -void Status_ConMsg_Hook(const char* text, ...) +// used for printing the output of status +AUTOHOOK(Status_ConMsg, engine.dll + 0x15ABD0, +void,, (const char* text, ...)) { char formatted[2048]; va_list list; @@ -394,10 +125,8 @@ void Status_ConMsg_Hook(const char* text, ...) spdlog::info(formatted); } -typedef bool (*CClientState_ProcessPrint_Type)(__int64 thisptr, __int64 msg); -CClientState_ProcessPrint_Type CClientState_ProcessPrint_Original; - -bool CClientState_ProcessPrint_Hook(__int64 thisptr, __int64 msg) +AUTOHOOK(CClientState_ProcessPrint, engine.dll + 0x1A1530, +bool,, (void* thisptr, uintptr_t msg)) { char* text = *(char**)(msg + 0x20); @@ -409,32 +138,8 @@ bool CClientState_ProcessPrint_Hook(__int64 thisptr, __int64 msg) return true; } -void InitialiseEngineSpewFuncHooks(HMODULE baseAddress) -{ - HookEnabler hook; - - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x11CA80, EngineSpewFuncHook, reinterpret_cast<LPVOID*>(&EngineSpewFunc)); - - // Hook print function that status concmd uses to actually print data - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x15ABD0, Status_ConMsg_Hook, reinterpret_cast<LPVOID*>(&Status_ConMsg_Original)); - - // Hook CClientState::ProcessPrint - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1A1530, - CClientState_ProcessPrint_Hook, - reinterpret_cast<LPVOID*>(&CClientState_ProcessPrint_Original)); - - Cvar_spewlog_enable = new ConVar("spewlog_enable", "1", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged"); -} - -#include "bitbuf.h" - ConVar* Cvar_cl_showtextmsg; -typedef void (*TextMsg_Type)(__int64); -TextMsg_Type TextMsg_Original; - class ICenterPrint { public: @@ -447,11 +152,20 @@ class ICenterPrint virtual void SetTextColor(int r, int g, int b, int a) = 0; }; -ICenterPrint* internalCenterPrint = NULL; +ICenterPrint* pInternalCenterPrint = NULL; -void TextMsgHook(BFRead* msg) +enum class TextMsgPrintType_t { - int msg_dest = msg->ReadByte(); + HUD_PRINTNOTIFY = 1, + HUD_PRINTCONSOLE, + HUD_PRINTTALK, + HUD_PRINTCENTER +}; + +AUTOHOOK(TextMsg, client.dll + 0x198710, +void,, (BFRead* msg)) +{ + TextMsgPrintType_t msg_dest = (TextMsgPrintType_t)msg->ReadByte(); char text[256]; msg->ReadString(text, sizeof(text)); @@ -461,29 +175,74 @@ void TextMsgHook(BFRead* msg) switch (msg_dest) { - case 4: // HUD_PRINTCENTER - internalCenterPrint->Print(text); + case TextMsgPrintType_t::HUD_PRINTCENTER: + pInternalCenterPrint->Print(text); break; + default: spdlog::warn("Unimplemented TextMsg type {}! printing to console", msg_dest); [[fallthrough]]; - case 2: // HUD_PRINTCONSOLE + + 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; } } -void InitialiseClientPrintHooks(HMODULE baseAddress) +AUTOHOOK(ConCommand_echo, engine.dll + 0x123680, +void,, (const CCommand& arg)) +{ + if (arg.ArgC() >= 2) + spdlog::info("[echo] {}", arg.ArgS()); +} + +// 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 + { + // 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(¤tTime, (GetNorthstarPrefix() + "/logs/nslog%Y-%m-%d %H-%M-%S.txt").c_str()); + spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false)); + spdlog::flush_on(spdlog::level::info); + } +} + +void InitialiseLogging() { - HookEnabler hook; + AllocConsole(); + + // Bind stdout to receive console output. + FILE* fp; + freopen_s(&fp, "CONOUT$", "w", stdout); + //_dup2(_fileno(stdout), _fileno(stderr)); - internalCenterPrint = (ICenterPrint*)((char*)baseAddress + 0x216E940); + spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v"); +} + +ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", EngineSpewFuncHooks, ConVar, (CModule module)) +{ + AUTOHOOK_DISPATCH_MODULE(engine.dll) - // "TextMsg" usermessage - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x198710, TextMsgHook, reinterpret_cast<LPVOID*>(&TextMsg_Original)); + Cvar_spewlog_enable = new ConVar("spewlog_enable", "1", 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).As<ICenterPrint*>(); } diff --git a/NorthstarDLL/logging.h b/NorthstarDLL/logging.h index 59537e01..83fd0c12 100644 --- a/NorthstarDLL/logging.h +++ b/NorthstarDLL/logging.h @@ -1,7 +1,4 @@ #pragma once -#include "context.h" void CreateLogFiles(); void InitialiseLogging(); -void InitialiseEngineSpewFuncHooks(HMODULE baseAddress); -void InitialiseClientPrintHooks(HMODULE baseAddress); diff --git a/NorthstarDLL/masterserver.cpp b/NorthstarDLL/masterserver.cpp index d5ef2d9f..a1fb839a 100644 --- a/NorthstarDLL/masterserver.cpp +++ b/NorthstarDLL/masterserver.cpp @@ -1,151 +1,27 @@ #include "pch.h" #include "masterserver.h" #include "concommand.h" -#include "gameutils.h" -#include "hookutils.h" +#include "playlist.h" #include "serverauthentication.h" -#include "gameutils.h" +#include "tier0.h" +#include "r2engine.h" +#include "modmanager.h" +#include "misccommands.h" +#include "version.h" + #include "rapidjson/document.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "rapidjson/error/en.h" -#include "modmanager.h" -#include "misccommands.h" + #include <cstring> #include <regex> -#include "version.h" -#include <chrono> -// NOTE for anyone reading this: we used to use httplib for requests here, but it had issues, so we're moving to curl now for masterserver -// requests so httplib is used exclusively for server stuff now -ConVar* Cvar_ns_masterserver_hostname; -ConVar* Cvar_ns_report_server_to_masterserver; -ConVar* Cvar_ns_report_sp_server_to_masterserver; - -ConVar* Cvar_ns_server_name; -ConVar* Cvar_ns_server_desc; -ConVar* Cvar_ns_server_password; +MasterServerManager* g_pMasterServerManager; +ConVar* Cvar_ns_masterserver_hostname; ConVar* Cvar_ns_curl_log_enable; -// Source ConVar -ConVar* Cvar_hostname; - -MasterServerManager* g_MasterServerManager; - -typedef void (*CHostState__State_NewGameType)(CHostState* hostState); -CHostState__State_NewGameType CHostState__State_NewGame; - -typedef void (*CHostState__State_ChangeLevelMPType)(CHostState* hostState); -CHostState__State_ChangeLevelMPType CHostState__State_ChangeLevelMP; - -typedef void (*CHostState__State_ChangeLevelSPType)(CHostState* hostState); -CHostState__State_ChangeLevelSPType CHostState__State_ChangeLevelSP; - -typedef void (*CHostState__State_GameShutdownType)(CHostState* hostState); -CHostState__State_GameShutdownType CHostState__State_GameShutdown; - -// 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 unescape_unicode(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 UpdateServerInfoFromUnicodeToUTF8() -{ - g_MasterServerManager->ns_auth_srvName = unescape_unicode(Cvar_ns_server_name->GetString()); - g_MasterServerManager->ns_auth_srvDesc = unescape_unicode(Cvar_ns_server_desc->GetString()); -} - -const char* HttplibErrorToString(httplib::Error error) -{ - switch (error) - { - case httplib::Error::Success: - return "httplib::Error::Success"; - case httplib::Error::Unknown: - return "httplib::Error::Unknown"; - case httplib::Error::Connection: - return "httplib::Error::Connection"; - case httplib::Error::BindIPAddress: - return "httplib::Error::BindIPAddress"; - case httplib::Error::Read: - return "httplib::Error::Read"; - case httplib::Error::Write: - return "httplib::Error::Write"; - case httplib::Error::ExceedRedirectCount: - return "httplib::Error::ExceedRedirectCount"; - case httplib::Error::Canceled: - return "httplib::Error::Canceled"; - case httplib::Error::SSLConnection: - return "httplib::Error::SSLConnection"; - case httplib::Error::SSLLoadingCerts: - return "httplib::Error::SSLLoadingCerts"; - case httplib::Error::SSLServerVerification: - return "httplib::Error::SSLServerVerification"; - case httplib::Error::UnsupportedMultipartBoundaryChars: - return "httplib::Error::UnsupportedMultipartBoundaryChars"; - } - - return ""; -} - RemoteServerInfo::RemoteServerInfo( const char* newId, const char* newName, @@ -159,29 +35,25 @@ RemoteServerInfo::RemoteServerInfo( // passworded servers don't have public ips requiresPassword = newRequiresPassword; - strncpy((char*)id, newId, sizeof(id)); - id[sizeof(id) - 1] = 0; - strncpy((char*)name, newName, sizeof(name)); - name[sizeof(name) - 1] = 0; + 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((char*)map, newMap, sizeof(map)); - map[sizeof(map) - 1] = 0; - strncpy((char*)playlist, newPlaylist, sizeof(playlist)); - playlist[sizeof(playlist) - 1] = 0; + strncpy_s((char*)map, sizeof(map), newMap, sizeof(map) - 1); + strncpy_s((char*)playlist, sizeof(playlist), newPlaylist, sizeof(playlist) - 1); playerCount = newPlayerCount; maxPlayers = newMaxPlayers; } -void MasterServerManager::SetCommonHttpClientOptions(CURL* curl) +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); // curl_easy_setopt(curl, CURLOPT_STDERR, stdout); - if (CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work + if (Tier0::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); @@ -191,11 +63,11 @@ void MasterServerManager::SetCommonHttpClientOptions(CURL* curl) void MasterServerManager::ClearServerList() { // this doesn't really do anything lol, probably isn't threadsafe - m_requestingServerList = true; + m_bRequestingServerList = true; - m_remoteServers.clear(); + m_vRemoteServers.clear(); - m_requestingServerList = false; + m_bRequestingServerList = false; } size_t CurlWriteToStringBufferCallback(char* contents, size_t size, size_t nmemb, void* userp) @@ -204,7 +76,7 @@ size_t CurlWriteToStringBufferCallback(char* contents, size_t size, size_t nmemb return size * nmemb; } -void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* originToken) +void MasterServerManager::AuthenticateOriginWithMasterServer(const char* uid, const char* originToken) { if (m_bOriginAuthWithMasterServerInProgress) return; @@ -234,7 +106,7 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or if (result == CURLcode::CURLE_OK) { - m_successfullyConnected = true; + m_bSuccessfullyConnected = true; rapidjson_document originAuthInfo; originAuthInfo.Parse(readBuffer.c_str()); @@ -255,8 +127,10 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or if (originAuthInfo["success"].IsTrue() && originAuthInfo.HasMember("token") && originAuthInfo["token"].IsString()) { - strncpy(m_ownClientAuthToken, originAuthInfo["token"].GetString(), sizeof(m_ownClientAuthToken)); - m_ownClientAuthToken[sizeof(m_ownClientAuthToken) - 1] = 0; + strncpy_s( + m_sOwnClientAuthToken, + sizeof(m_sOwnClientAuthToken), originAuthInfo["token"].GetString(), + sizeof(m_sOwnClientAuthToken) - 1); spdlog::info("Northstar origin authentication completed successfully!"); } else @@ -265,7 +139,7 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or else { spdlog::error("Failed performing northstar origin auth: error {}", curl_easy_strerror(result)); - m_successfullyConnected = false; + m_bSuccessfullyConnected = false; } // we goto this instead of returning so we always hit this @@ -281,18 +155,18 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or void MasterServerManager::RequestServerList() { // do this here so it's instantly set on call for scripts - m_scriptRequestingServerList = true; + 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_requestingServerList) + while (m_bRequestingServerList) Sleep(100); - m_requestingServerList = true; - m_scriptRequestingServerList = true; + m_bRequestingServerList = true; + m_bScriptRequestingServerList = true; spdlog::info("Requesting server list from {}", Cvar_ns_masterserver_hostname->GetString()); @@ -309,7 +183,7 @@ void MasterServerManager::RequestServerList() if (result == CURLcode::CURLE_OK) { - m_successfullyConnected = true; + m_bSuccessfullyConnected = true; rapidjson_document serverInfoJson; serverInfoJson.Parse(readBuffer.c_str()); @@ -365,7 +239,7 @@ void MasterServerManager::RequestServerList() RemoteServerInfo* newServer = nullptr; bool createNewServerInfo = true; - for (RemoteServerInfo& server : m_remoteServers) + for (RemoteServerInfo& server : m_vRemoteServers) { // if server already exists, update info rather than adding to it if (!strncmp((const char*)server.id, id, 32)) @@ -387,7 +261,7 @@ void MasterServerManager::RequestServerList() // server didn't exist if (createNewServerInfo) - newServer = &m_remoteServers.emplace_back( + newServer = &m_vRemoteServers.emplace_back( id, serverObj["name"].GetString(), serverObj["description"].GetString(), @@ -424,20 +298,20 @@ void MasterServerManager::RequestServerList() } std::sort( - m_remoteServers.begin(), - m_remoteServers.end(), + 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_successfullyConnected = false; + m_bSuccessfullyConnected = false; } // we goto this instead of returning so we always hit this REQUEST_END_CLEANUP: - m_requestingServerList = false; - m_scriptRequestingServerList = false; + m_bRequestingServerList = false; + m_bScriptRequestingServerList = false; curl_easy_cleanup(curl); }); @@ -468,7 +342,7 @@ void MasterServerManager::RequestMainMenuPromos() if (result == CURLcode::CURLE_OK) { - m_successfullyConnected = true; + m_bSuccessfullyConnected = true; rapidjson_document mainMenuPromoJson; mainMenuPromoJson.Parse(readBuffer.c_str()); @@ -491,10 +365,6 @@ void MasterServerManager::RequestMainMenuPromos() { spdlog::error("Failed reading masterserver response: got fastify error response"); spdlog::error(readBuffer); - if (mainMenuPromoJson["error"].HasMember("enum")) - s_authfail_reason = std::string(mainMenuPromoJson["error"]["enum"].GetString()); - else - s_authfail_reason = std::string("No error message provided"); goto REQUEST_END_CLEANUP; } @@ -526,29 +396,29 @@ void MasterServerManager::RequestMainMenuPromos() goto REQUEST_END_CLEANUP; } - m_MainMenuPromoData.newInfoTitle1 = mainMenuPromoJson["newInfo"]["Title1"].GetString(); - m_MainMenuPromoData.newInfoTitle2 = mainMenuPromoJson["newInfo"]["Title2"].GetString(); - m_MainMenuPromoData.newInfoTitle3 = mainMenuPromoJson["newInfo"]["Title3"].GetString(); + m_sMainMenuPromoData.newInfoTitle1 = mainMenuPromoJson["newInfo"]["Title1"].GetString(); + m_sMainMenuPromoData.newInfoTitle2 = mainMenuPromoJson["newInfo"]["Title2"].GetString(); + m_sMainMenuPromoData.newInfoTitle3 = mainMenuPromoJson["newInfo"]["Title3"].GetString(); - m_MainMenuPromoData.largeButtonTitle = mainMenuPromoJson["largeButton"]["Title"].GetString(); - m_MainMenuPromoData.largeButtonText = mainMenuPromoJson["largeButton"]["Text"].GetString(); - m_MainMenuPromoData.largeButtonUrl = mainMenuPromoJson["largeButton"]["Url"].GetString(); - m_MainMenuPromoData.largeButtonImageIndex = mainMenuPromoJson["largeButton"]["ImageIndex"].GetInt(); + 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_MainMenuPromoData.smallButton1Title = mainMenuPromoJson["smallButton1"]["Title"].GetString(); - m_MainMenuPromoData.smallButton1Url = mainMenuPromoJson["smallButton1"]["Url"].GetString(); - m_MainMenuPromoData.smallButton1ImageIndex = mainMenuPromoJson["smallButton1"]["ImageIndex"].GetInt(); + m_sMainMenuPromoData.smallButton1Title = mainMenuPromoJson["smallButton1"]["Title"].GetString(); + m_sMainMenuPromoData.smallButton1Url = mainMenuPromoJson["smallButton1"]["Url"].GetString(); + m_sMainMenuPromoData.smallButton1ImageIndex = mainMenuPromoJson["smallButton1"]["ImageIndex"].GetInt(); - m_MainMenuPromoData.smallButton2Title = mainMenuPromoJson["smallButton2"]["Title"].GetString(); - m_MainMenuPromoData.smallButton2Url = mainMenuPromoJson["smallButton2"]["Url"].GetString(); - m_MainMenuPromoData.smallButton2ImageIndex = mainMenuPromoJson["smallButton2"]["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_successfullyConnected = false; + m_bSuccessfullyConnected = false; } REQUEST_END_CLEANUP: @@ -559,15 +429,15 @@ void MasterServerManager::RequestMainMenuPromos() requestThread.detach(); } -void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken) +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_authenticatingWithGameServer) + if (m_bAuthenticatingWithGameServer) return; - m_authenticatingWithGameServer = true; - m_scriptAuthenticatingWithGameServer = true; - m_successfullyAuthenticatedWithGameServer = false; + m_bAuthenticatingWithGameServer = true; + m_bScriptAuthenticatingWithGameServer = true; + m_bSuccessfullyAuthenticatedWithGameServer = false; std::string uidStr(uid); std::string tokenStr(playerToken); @@ -592,7 +462,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken if (result == CURLcode::CURLE_OK) { - m_successfullyConnected = true; + m_bSuccessfullyConnected = true; rapidjson_document authInfoJson; authInfoJson.Parse(readBuffer.c_str()); @@ -615,10 +485,12 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken { spdlog::error("Failed reading masterserver response: got fastify error response"); spdlog::error(readBuffer); + if (authInfoJson["error"].HasMember("enum")) - s_authfail_reason = std::string(authInfoJson["error"]["enum"].GetString()); + m_sAuthFailureReason = authInfoJson["error"]["enum"].GetString(); else - s_authfail_reason = std::string("No error message provided"); + m_sAuthFailureReason = "No error message provided"; + goto REQUEST_END_CLEANUP; } @@ -636,9 +508,8 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken goto REQUEST_END_CLEANUP; } - AuthData newAuthData {}; - strncpy(newAuthData.uid, authInfoJson["id"].GetString(), sizeof(newAuthData.uid)); - newAuthData.uid[sizeof(newAuthData.uid) - 1] = 0; + 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]; @@ -658,28 +529,28 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken newAuthData.pdata[i++] = static_cast<char>(byte.GetUint()); } - std::lock_guard<std::mutex> guard(g_ServerAuthenticationManager->m_authDataMutex); - g_ServerAuthenticationManager->m_authData.clear(); - g_ServerAuthenticationManager->m_authData.insert(std::make_pair(authInfoJson["authToken"].GetString(), newAuthData)); + 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_successfullyAuthenticatedWithGameServer = true; + m_bSuccessfullyAuthenticatedWithGameServer = true; } else { spdlog::error("Failed authenticating with own server: error {}", curl_easy_strerror(result)); - m_successfullyConnected = false; - m_successfullyAuthenticatedWithGameServer = false; - m_scriptAuthenticatingWithGameServer = false; + m_bSuccessfullyConnected = false; + m_bSuccessfullyAuthenticatedWithGameServer = false; + m_bScriptAuthenticatingWithGameServer = false; } REQUEST_END_CLEANUP: - m_authenticatingWithGameServer = false; - m_scriptAuthenticatingWithGameServer = false; + 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); + R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "ns_end_reauth_and_leave_to_lobby", R2::cmd_source_t::kCommandSrcCode); m_bNewgameAfterSelfAuth = false; } @@ -689,15 +560,15 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken requestThread.detach(); } -void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, char* serverId, char* password) +void MasterServerManager::AuthenticateWithServer(const char* uid, const char* playerToken, const char* serverId, const char* password) { // dont wait, just stop if we're trying to do 2 auth requests at once - if (m_authenticatingWithGameServer) + if (m_bAuthenticatingWithGameServer) return; - m_authenticatingWithGameServer = true; - m_scriptAuthenticatingWithGameServer = true; - m_successfullyAuthenticatedWithGameServer = false; + m_bAuthenticatingWithGameServer = true; + m_bScriptAuthenticatingWithGameServer = true; + m_bSuccessfullyAuthenticatedWithGameServer = false; std::string uidStr(uid); std::string tokenStr(playerToken); @@ -708,7 +579,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c [this, uidStr, tokenStr, serverIdStr, passwordStr]() { // esnure that any persistence saving is done, so we know masterserver has newest - while (m_savingPersistentData) + while (m_bSavingPersistentData) Sleep(100); spdlog::info("Attempting authentication with server of id \"{}\"", serverIdStr); @@ -743,7 +614,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c if (result == CURLcode::CURLE_OK) { - m_successfullyConnected = true; + m_bSuccessfullyConnected = true; rapidjson_document connectionInfoJson; connectionInfoJson.Parse(readBuffer.c_str()); @@ -766,10 +637,12 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c { spdlog::error("Failed reading masterserver response: got fastify error response"); spdlog::error(readBuffer); + if (connectionInfoJson["error"].HasMember("enum")) - s_authfail_reason = std::string(connectionInfoJson["error"]["enum"].GetString()); + m_sAuthFailureReason = connectionInfoJson["error"]["enum"].GetString(); else - s_authfail_reason = std::string("No error message provided"); + m_sAuthFailureReason = "No error message provided"; + goto REQUEST_END_CLEANUP; } @@ -791,63 +664,61 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c m_pendingConnectionInfo.ip.S_un.S_addr = inet_addr(connectionInfoJson["ip"].GetString()); m_pendingConnectionInfo.port = (unsigned short)connectionInfoJson["port"].GetUint(); - strncpy(m_pendingConnectionInfo.authToken, connectionInfoJson["authToken"].GetString(), 31); - m_pendingConnectionInfo.authToken[31] = 0; + strncpy_s( + m_pendingConnectionInfo.authToken, + sizeof(m_pendingConnectionInfo.authToken), + connectionInfoJson["authToken"].GetString(), + sizeof(m_pendingConnectionInfo.authToken) - 1); - m_hasPendingConnectionInfo = true; - m_successfullyAuthenticatedWithGameServer = true; + m_bHasPendingConnectionInfo = true; + m_bSuccessfullyAuthenticatedWithGameServer = true; } else { spdlog::error("Failed authenticating with server: error {}", curl_easy_strerror(result)); - m_successfullyConnected = false; - m_successfullyAuthenticatedWithGameServer = false; - m_scriptAuthenticatingWithGameServer = false; + m_bSuccessfullyConnected = false; + m_bSuccessfullyAuthenticatedWithGameServer = false; + m_bScriptAuthenticatingWithGameServer = false; } REQUEST_END_CLEANUP: - m_authenticatingWithGameServer = false; - m_scriptAuthenticatingWithGameServer = false; + m_bAuthenticatingWithGameServer = false; + m_bScriptAuthenticatingWithGameServer = false; curl_easy_cleanup(curl); }); requestThread.detach(); } -void MasterServerManager::AddSelfToServerList( - int port, int authPort, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password) +void MasterServerManager::WritePlayerPersistentData(const char* playerId, const char* pdata, size_t pdataSize) { - if (!Cvar_ns_report_server_to_masterserver->GetBool()) - return; - - if (!Cvar_ns_report_sp_server_to_masterserver->GetBool() && !strncmp(map, "sp_", 3)) + // 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) { - m_bRequireClientAuth = false; + spdlog::warn("attempted to write pdata of size 0!"); return; } - m_bRequireClientAuth = true; - - std::string strName(name); - std::string strDescription(description); - std::string strMap(map); - std::string strPlaylist(playlist); - std::string strPassword(password); + std::string strPlayerId(playerId); + std::string strPdata(pdata, pdataSize); std::thread requestThread( - [this, port, authPort, strName, strDescription, strMap, strPlaylist, maxPlayers, strPassword] + [this, strPlayerId, strPdata, pdataSize] { - // Keep track of attempted connects in case of DUPLICATE_SERVER response - int retry_counter = 0; - - START_REQUEST: - m_ownServerId[0] = 0; - m_ownServerAuthToken[0] = 0; - 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); @@ -855,554 +726,340 @@ void MasterServerManager::AddSelfToServerList( curl_mime* mime = curl_mime_init(curl); curl_mimepart* part = curl_mime_addpart(mime); - curl_mime_data(part, m_ownModInfoJson.c_str(), m_ownModInfoJson.size()); - curl_mime_name(part, "modinfo"); - curl_mime_filename(part, "modinfo.json"); - curl_mime_type(part, "application/json"); + 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); - // format every paramter because computers hate me - { - char* nameEscaped = curl_easy_escape(curl, strName.c_str(), strName.length()); - char* descEscaped = curl_easy_escape(curl, strDescription.c_str(), strDescription.length()); - char* mapEscaped = curl_easy_escape(curl, strMap.c_str(), strMap.length()); - char* playlistEscaped = curl_easy_escape(curl, strPlaylist.c_str(), strPlaylist.length()); - char* passwordEscaped = curl_easy_escape(curl, strPassword.c_str(), strPassword.length()); - - curl_easy_setopt( - curl, - CURLOPT_URL, - fmt::format( - "{}/server/add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}", - Cvar_ns_masterserver_hostname->GetString(), - port, - authPort, - nameEscaped, - descEscaped, - mapEscaped, - playlistEscaped, - maxPlayers, - 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) - { - m_successfullyConnected = true; - - rapidjson_document serverAddedJson; - serverAddedJson.Parse(readBuffer.c_str()); + m_bSuccessfullyConnected = true; + else + m_bSuccessfullyConnected = false; - if (serverAddedJson.HasParseError()) - { - spdlog::error( - "Failed reading masterserver authentication response: encountered parse error \"{}\"", - rapidjson::GetParseError_En(serverAddedJson.GetParseError())); - goto REQUEST_END_CLEANUP; - } + curl_easy_cleanup(curl); - if (!serverAddedJson.IsObject()) - { - spdlog::error("Failed reading masterserver authentication response: root object is not an object"); - goto REQUEST_END_CLEANUP; - } + m_bSavingPersistentData = false; + }); - if (serverAddedJson.HasMember("error")) - { - spdlog::error("Failed reading masterserver response: got fastify error response"); - spdlog::error(readBuffer); + requestThread.detach(); +} - // Check for enum member in JSON - if (serverAddedJson["error"].HasMember("enum")) - { - // Check for DUPLICATE_SERVER error response, stop if we tried 10 times - if (strncmp(serverAddedJson["error"]["enum"].GetString(), "DUPLICATE_SERVER", 17) == 0 && retry_counter < 10) - { +class MasterServerPresenceReporter : public ServerPresenceReporter +{ + const int MAX_REGISTRATION_ATTEMPTS = 5; - spdlog::info("Retrying request in 10 seconds."); - // Incremement retry counter - retry_counter++; + double m_bShouldTryRegisterServer; + int m_nNumRegistrationAttempts; - // Sleep for 10 seconds - std::this_thread::sleep_for(std::chrono::seconds(10)); + void CreatePresence(const ServerPresence* pServerPresence) override + { + m_bShouldTryRegisterServer = true; + m_nNumRegistrationAttempts = 0; + } - // curl cleanup to retry request - curl_easy_cleanup(curl); - curl_mime_free(mime); + void ReportPresence(const ServerPresence* pServerPresence) override + { + // make a copy of presence for multithreading purposes - // go to beginning and retry - goto START_REQUEST; - } - } - goto REQUEST_END_CLEANUP; - } + ServerPresence threadedPresence(pServerPresence); - if (!serverAddedJson["success"].IsTrue()) - { - spdlog::error("Adding server to masterserver failed: \"success\" is not true"); - goto REQUEST_END_CLEANUP; - } - - if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString() || - !serverAddedJson.HasMember("serverAuthToken") || !serverAddedJson["serverAuthToken"].IsString()) + if (!*g_pMasterServerManager->m_sOwnServerId || m_bShouldTryRegisterServer) + { + // add server + std::thread addServerThread( + [this, threadedPresence] { - spdlog::error("Failed reading masterserver response: malformed json object"); - goto REQUEST_END_CLEANUP; - } - - strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId)); - m_ownServerId[sizeof(m_ownServerId) - 1] = 0; + g_pMasterServerManager->m_sOwnServerId[0] = 0; + g_pMasterServerManager->m_sOwnServerAuthToken[0] = 0; - strncpy(m_ownServerAuthToken, serverAddedJson["serverAuthToken"].GetString(), sizeof(m_ownServerAuthToken)); - m_ownServerAuthToken[sizeof(m_ownServerAuthToken) - 1] = 0; + CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); - spdlog::info("Succesfully added server to the master server."); + std::string readBuffer; + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); - // heartbeat thread - // ideally this should actually be done in main thread, rather than on it's own thread, so it'd stop if server freezes - std::thread heartbeatThread( - [this] - { - Sleep(5000); + curl_mime* mime = curl_mime_init(curl); + curl_mimepart* part = curl_mime_addpart(mime); - // defensive check, as m_ownServer could be set to null during the Sleep(5000) above - if (!*m_ownServerId) - return; + curl_mime_data( + part, g_pMasterServerManager->m_sOwnModInfoJson.c_str(), g_pMasterServerManager->m_sOwnModInfoJson.size()); + curl_mime_name(part, "modinfo"); + curl_mime_filename(part, "modinfo.json"); + curl_mime_type(part, "application/json"); - do - { - CURL* curl = curl_easy_init(); - SetCommonHttpClientOptions(curl); + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); - 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); + // format every paramter because computers hate me + { + char* nameEscaped = curl_easy_escape(curl, threadedPresence.m_sServerName.c_str(), NULL); + char* descEscaped = curl_easy_escape(curl, threadedPresence.m_sServerDesc.c_str(), NULL); + char* mapEscaped = curl_easy_escape(curl, threadedPresence.m_MapName, NULL); + char* playlistEscaped = curl_easy_escape(curl, threadedPresence.m_PlaylistName, NULL); + char* passwordEscaped = curl_easy_escape(curl, threadedPresence.m_Password, NULL); + + curl_easy_setopt( + curl, + CURLOPT_URL, + fmt::format( + "{}/server/" + "add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}", + Cvar_ns_masterserver_hostname->GetString(), + threadedPresence.m_iPort, + threadedPresence.m_iAuthPort, + 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); + } - // 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* escapedNameNew = curl_easy_escape(curl, g_MasterServerManager->ns_auth_srvName.c_str(), NULL); - char* escapedDescNew = curl_easy_escape(curl, g_MasterServerManager->ns_auth_srvDesc.c_str(), NULL); - char* escapedMapNew = curl_easy_escape(curl, g_pHostState->m_levelName, NULL); - char* escapedPlaylistNew = curl_easy_escape(curl, GetCurrentPlaylistName(), NULL); - char* escapedPasswordNew = curl_easy_escape(curl, Cvar_ns_server_password->GetString(), NULL); - - int maxPlayers = 6; - char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false); - if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this - maxPlayers = std::stoi(maxPlayersVar); - - curl_easy_setopt( - curl, - CURLOPT_URL, - fmt::format( - "{}/server/" - "update_values?id={}&port={}&authPort={}&name={}&description={}&map={}&playlist={}&playerCount={}&" - "maxPlayers={}&password={}", - Cvar_ns_masterserver_hostname->GetString(), - m_ownServerId, - Cvar_hostport->GetInt(), - Cvar_ns_player_auth_port->GetInt(), - escapedNameNew, - escapedDescNew, - escapedMapNew, - escapedPlaylistNew, - g_ServerAuthenticationManager->m_additionalPlayerData.size(), - maxPlayers, - escapedPasswordNew) - .c_str()); - - curl_free(escapedNameNew); - curl_free(escapedDescNew); - curl_free(escapedMapNew); - curl_free(escapedPlaylistNew); - curl_free(escapedPasswordNew); - } + CURLcode result = curl_easy_perform(curl); - curl_mime* mime = curl_mime_init(curl); - curl_mimepart* part = curl_mime_addpart(mime); + if (result == CURLcode::CURLE_OK) + { + g_pMasterServerManager->m_bSuccessfullyConnected = true; - curl_mime_data(part, m_ownModInfoJson.c_str(), m_ownModInfoJson.size()); - curl_mime_name(part, "modinfo"); - curl_mime_filename(part, "modinfo.json"); - curl_mime_type(part, "application/json"); + rapidjson_document serverAddedJson; + serverAddedJson.Parse(readBuffer.c_str()); - curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + if (serverAddedJson.HasParseError()) + { + spdlog::error( + "Failed reading masterserver authentication response: encountered parse error \"{}\"", + rapidjson::GetParseError_En(serverAddedJson.GetParseError())); + goto REQUEST_END_CLEANUP; + } - CURLcode result = curl_easy_perform(curl); + if (!serverAddedJson.IsObject()) + { + spdlog::error("Failed reading masterserver authentication response: root object is not an object"); + goto REQUEST_END_CLEANUP; + } - // defensive check, as m_ownServerId could be set to null before this request gets processed - if (!*m_ownServerId) - return; + if (serverAddedJson.HasMember("error")) + { + spdlog::error("Failed reading masterserver response: got fastify error response"); + spdlog::error(readBuffer); - if (result == CURLcode::CURLE_OK) + if (serverAddedJson["error"].HasMember("enum") && !strcmp(serverAddedJson["error"]["enum"].GetString(), "DUPLICATE_SERVER")) { - rapidjson_document serverAddedJson; - serverAddedJson.Parse(readBuffer.c_str()); - - if (!serverAddedJson.HasParseError() && serverAddedJson.IsObject()) - { - if (serverAddedJson.HasMember("id") && serverAddedJson["id"].IsString()) - { - strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId)); - m_ownServerId[sizeof(m_ownServerId) - 1] = 0; - } - - if (serverAddedJson.HasMember("serverAuthToken") && serverAddedJson["serverAuthToken"].IsString()) - { - strncpy( - m_ownServerAuthToken, - serverAddedJson["serverAuthToken"].GetString(), - sizeof(m_ownServerAuthToken)); - m_ownServerAuthToken[sizeof(m_ownServerAuthToken) - 1] = 0; - } - } - } - else - spdlog::warn("Heartbeat failed with error {}", curl_easy_strerror(result)); + if (++m_nNumRegistrationAttempts == MAX_REGISTRATION_ATTEMPTS) + m_bShouldTryRegisterServer = false; - curl_easy_cleanup(curl); - Sleep(10000); - } while (*m_ownServerId); - }); + goto REQUEST_END_CLEANUP_RETRY; + } - heartbeatThread.detach(); - } - else - { - spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result)); - m_successfullyConnected = false; - } + goto REQUEST_END_CLEANUP; + } - REQUEST_END_CLEANUP: - curl_easy_cleanup(curl); - curl_mime_free(mime); - }); + if (!serverAddedJson["success"].IsTrue()) + { + spdlog::error("Adding server to masterserver failed: \"success\" is not true"); + goto REQUEST_END_CLEANUP; + } - requestThread.detach(); -} + if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString() || + !serverAddedJson.HasMember("serverAuthToken") || !serverAddedJson["serverAuthToken"].IsString()) + { + spdlog::error("Failed reading masterserver response: malformed json object"); + goto REQUEST_END_CLEANUP; + } -void MasterServerManager::UpdateServerMapAndPlaylist(char* map, char* playlist, int maxPlayers) -{ - // dont call this if we don't have a server id - if (!*m_ownServerId) - return; + strncpy_s( + g_pMasterServerManager->m_sOwnServerId, + sizeof(g_pMasterServerManager->m_sOwnServerId), + serverAddedJson["id"].GetString(), + sizeof(g_pMasterServerManager->m_sOwnServerId) - 1); + + strncpy_s( + g_pMasterServerManager->m_sOwnServerAuthToken, + sizeof(g_pMasterServerManager->m_sOwnServerAuthToken), + serverAddedJson["serverAuthToken"].GetString(), + sizeof(g_pMasterServerManager->m_sOwnServerAuthToken) - 1); + } + else + { + spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result)); + g_pMasterServerManager->m_bSuccessfullyConnected = false; + } - std::string strMap(map); - std::string strPlaylist(playlist); + REQUEST_END_CLEANUP: + m_bShouldTryRegisterServer = false; - std::thread requestThread( - [this, strMap, strPlaylist, maxPlayers] + REQUEST_END_CLEANUP_RETRY: + curl_easy_cleanup(curl); + curl_mime_free(mime); + }); + addServerThread.detach(); + } + else { - 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); - - // escape params - { - char* mapEscaped = curl_easy_escape(curl, strMap.c_str(), strMap.length()); - char* playlistEscaped = curl_easy_escape(curl, strPlaylist.c_str(), strPlaylist.length()); - - curl_easy_setopt( - curl, - CURLOPT_URL, - fmt::format( - "{}/server/update_values?id={}&map={}&playlist={}&maxPlayers={}", - Cvar_ns_masterserver_hostname->GetString(), - m_ownServerId, - mapEscaped, - playlistEscaped, - maxPlayers) - .c_str()); - - curl_free(mapEscaped); - curl_free(playlistEscaped); - } + // update server + std::thread updateServerThread( + [threadedPresence] + { + CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); - CURLcode result = curl_easy_perform(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); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); - if (result == CURLcode::CURLE_OK) - m_successfullyConnected = true; - else - m_successfullyConnected = false; + // 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(), NULL); + char* descEscaped = curl_easy_escape(curl, threadedPresence.m_sServerDesc.c_str(), NULL); + char* mapEscaped = curl_easy_escape(curl, threadedPresence.m_MapName, NULL); + char* playlistEscaped = curl_easy_escape(curl, threadedPresence.m_PlaylistName, NULL); + char* passwordEscaped = curl_easy_escape(curl, threadedPresence.m_Password, NULL); + + curl_easy_setopt( + curl, + CURLOPT_URL, + fmt::format( + "{}/server/" + "update_values?id={}&port={}&authPort={}&name={}&description={}&map={}&playlist={}&playerCount={}&" + "maxPlayers={}&password={}", + Cvar_ns_masterserver_hostname->GetString(), + g_pMasterServerManager->m_sOwnServerId, + threadedPresence.m_iPort, + threadedPresence.m_iAuthPort, + 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_easy_cleanup(curl); - }); + curl_mime* mime = curl_mime_init(curl); + curl_mimepart* part = curl_mime_addpart(mime); - requestThread.detach(); -} + curl_mime_data( + part, g_pMasterServerManager->m_sOwnModInfoJson.c_str(), g_pMasterServerManager->m_sOwnModInfoJson.size()); + curl_mime_name(part, "modinfo"); + curl_mime_filename(part, "modinfo.json"); + curl_mime_type(part, "application/json"); -void MasterServerManager::UpdateServerPlayerCount(int playerCount) -{ - // dont call this if we don't have a server id - if (!*m_ownServerId) - return; + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); - std::thread requestThread( - [this, playerCount] - { - CURL* curl = curl_easy_init(); - SetCommonHttpClientOptions(curl); + CURLcode result = curl_easy_perform(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); - curl_easy_setopt( - curl, - CURLOPT_URL, - fmt::format( - "{}/server/update_values?id={}&playerCount={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId, playerCount) - .c_str()); - - CURLcode result = curl_easy_perform(curl); + if (result == CURLcode::CURLE_OK) + { + rapidjson_document serverAddedJson; + serverAddedJson.Parse(readBuffer.c_str()); - if (result == CURLcode::CURLE_OK) - m_successfullyConnected = true; - else - m_successfullyConnected = false; + if (!serverAddedJson.HasParseError() && serverAddedJson.IsObject()) + { + if (serverAddedJson.HasMember("id") && serverAddedJson["id"].IsString()) + { + strncpy_s( + g_pMasterServerManager->m_sOwnServerId, + sizeof(g_pMasterServerManager->m_sOwnServerId), + serverAddedJson["id"].GetString(), + sizeof(g_pMasterServerManager->m_sOwnServerId) - 1); + } - curl_easy_cleanup(curl); - }); + if (serverAddedJson.HasMember("serverAuthToken") && serverAddedJson["serverAuthToken"].IsString()) + { - requestThread.detach(); -} + strncpy_s( + g_pMasterServerManager->m_sOwnServerAuthToken, + sizeof(g_pMasterServerManager->m_sOwnServerAuthToken), + serverAddedJson["serverAuthToken"].GetString(), + sizeof(g_pMasterServerManager->m_sOwnServerAuthToken) - 1); + } + } + } + else + spdlog::warn("Heartbeat failed with error {}", curl_easy_strerror(result)); -void MasterServerManager::WritePlayerPersistentData(char* playerId, 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_savingPersistentData = true; - if (!pdataSize) - { - spdlog::warn("attempted to write pdata of size 0!"); - return; + curl_easy_cleanup(curl); + }); + updateServerThread.detach(); + } } - 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_ownServerId) - .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_successfullyConnected = true; - else - m_successfullyConnected = false; - - curl_easy_cleanup(curl); - - m_savingPersistentData = false; - }); - - requestThread.detach(); -} - -void MasterServerManager::RemoveSelfFromServerList() -{ - // dont call this if we don't have a server id - if (!*m_ownServerId || !Cvar_ns_report_server_to_masterserver->GetBool()) - return; - - 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(), m_ownServerId).c_str()); - - *m_ownServerId = 0; - CURLcode result = curl_easy_perform(curl); - - if (result == CURLcode::CURLE_OK) - m_successfullyConnected = true; - else - m_successfullyConnected = false; + void DestroyPresence(const ServerPresence* pServerPresence) override + { + // dont call this if we don't have a server id + if (!*g_pMasterServerManager->m_sOwnServerId) + return; - curl_easy_cleanup(curl); - }); + std::thread requestThread( + [this] + { + CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); - requestThread.detach(); -} + 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()); -void ConCommand_ns_fetchservers(const CCommand& args) -{ - g_MasterServerManager->RequestServerList(); -} + *g_pMasterServerManager->m_sOwnServerId = 0; + CURLcode result = curl_easy_perform(curl); -void CHostState__State_NewGameHook(CHostState* hostState) -{ - Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode); - Cbuf_Execute(); + if (result == CURLcode::CURLE_OK) + g_pMasterServerManager->m_bSuccessfullyConnected = true; + else + g_pMasterServerManager->m_bSuccessfullyConnected = false; - // need to do this to ensure we don't go to private match - if (g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame) - SetCurrentPlaylist("tdm"); + curl_easy_cleanup(curl); + }); - // 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)) - { - Cbuf_AddText(Cbuf_GetCurrentPlayer(), "net_data_block_enabled 1", cmd_source_t::kCommandSrcCode); - Cbuf_Execute(); + requestThread.detach(); } +}; - CHostState__State_NewGame(hostState); - - int maxPlayers = 6; - char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false); - if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this - maxPlayers = std::stoi(maxPlayersVar); - - // Copy new server name cvar to source - Cvar_hostname->SetValue(Cvar_ns_server_name->GetString()); - // This calls the function that converts unicode strings from servername and serverdesc to UTF-8 - UpdateServerInfoFromUnicodeToUTF8(); - - g_MasterServerManager->AddSelfToServerList( - Cvar_hostport->GetInt(), - Cvar_ns_player_auth_port->GetInt(), - (char*)Cvar_ns_server_name->GetString(), - (char*)Cvar_ns_server_desc->GetString(), - hostState->m_levelName, - (char*)GetCurrentPlaylistName(), - maxPlayers, - (char*)Cvar_ns_server_password->GetString()); - g_ServerAuthenticationManager->StartPlayerAuthServer(); - g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = false; -} - -void CHostState__State_ChangeLevelMPHook(CHostState* hostState) +void ConCommand_ns_fetchservers(const CCommand& args) { - int maxPlayers = 6; - char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false); - if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this - maxPlayers = std::stoi(maxPlayersVar); - - // 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)) - { - Cbuf_AddText(Cbuf_GetCurrentPlayer(), "net_data_block_enabled 1", cmd_source_t::kCommandSrcCode); - Cbuf_Execute(); - } - - g_MasterServerManager->UpdateServerMapAndPlaylist(hostState->m_levelName, (char*)GetCurrentPlaylistName(), maxPlayers); - CHostState__State_ChangeLevelMP(hostState); + g_pMasterServerManager->RequestServerList(); } -void CHostState__State_ChangeLevelSPHook(CHostState* hostState) -{ - // is this even called? genuinely i don't think so - // from what i can tell, it's not called on mp=>sp change or sp=>sp change - // so idk it's fucked - - int maxPlayers = 6; - char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false); - if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this - maxPlayers = std::stoi(maxPlayersVar); - - g_MasterServerManager->UpdateServerMapAndPlaylist(hostState->m_levelName, (char*)GetCurrentPlaylistName(), maxPlayers); - CHostState__State_ChangeLevelSP(hostState); -} +MasterServerManager::MasterServerManager() : m_pendingConnectionInfo {}, m_sOwnServerId {""}, m_sOwnClientAuthToken {""} {} -void CHostState__State_GameShutdownHook(CHostState* hostState) +ON_DLL_LOAD_RELIESON("engine.dll", MasterServer, (ConCommand, ServerPresence), (CModule module)) { - g_MasterServerManager->RemoveSelfFromServerList(); - g_ServerAuthenticationManager->StopPlayerAuthServer(); + g_pMasterServerManager = new MasterServerManager; - CHostState__State_GameShutdown(hostState); -} + 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"); -MasterServerManager::MasterServerManager() : m_pendingConnectionInfo {}, m_ownServerId {""}, m_ownClientAuthToken {""} {} + RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "Fetch all servers from the masterserver", FCVAR_CLIENTDLL); -void InitialiseSharedMasterServer(HMODULE baseAddress) -{ - Cvar_ns_masterserver_hostname = new ConVar("ns_masterserver_hostname", "127.0.0.1", FCVAR_NONE, ""); - // unfortunately lib doesn't let us specify a port and still have https work - // Cvar_ns_masterserver_port = new ConVar("ns_masterserver_port", "8080", FCVAR_NONE, ""); - - Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, ""); - Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, ""); - Cvar_ns_server_password = new ConVar("ns_server_password", "", FCVAR_GAMEDLL, ""); - Cvar_ns_report_server_to_masterserver = new ConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, ""); - Cvar_ns_report_sp_server_to_masterserver = new ConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, ""); - - Cvar_ns_curl_log_enable = new ConVar("ns_curl_log_enable", "0", FCVAR_NONE, ""); - - Cvar_hostname = *(ConVar**)((char*)baseAddress + 0x1315bae8); - - g_MasterServerManager = new MasterServerManager; - - RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "", FCVAR_CLIENTDLL); - - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x16E7D0, CHostState__State_NewGameHook, reinterpret_cast<LPVOID*>(&CHostState__State_NewGame)); - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x16E520, - CHostState__State_ChangeLevelMPHook, - reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelMP)); - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x16E5D0, - CHostState__State_ChangeLevelSPHook, - reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelSP)); - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x16E640, - CHostState__State_GameShutdownHook, - reinterpret_cast<LPVOID*>(&CHostState__State_GameShutdown)); + MasterServerPresenceReporter* presenceReporter = new MasterServerPresenceReporter; + g_pServerPresence->AddPresenceReporter(presenceReporter); } diff --git a/NorthstarDLL/masterserver.h b/NorthstarDLL/masterserver.h index e4dcb954..5a4e2a91 100644 --- a/NorthstarDLL/masterserver.h +++ b/NorthstarDLL/masterserver.h @@ -1,8 +1,13 @@ #pragma once #include "convar.h" +#include "serverpresence.h" #include <winsock2.h> #include <string> #include <cstring> + +extern ConVar* Cvar_ns_masterserver_hostname; +extern ConVar* Cvar_ns_curl_log_enable; + struct RemoteModInfo { public: @@ -73,63 +78,49 @@ struct MainMenuPromoData class MasterServerManager { private: - bool m_requestingServerList = false; - bool m_authenticatingWithGameServer = false; + bool m_bRequestingServerList = false; + bool m_bAuthenticatingWithGameServer = false; public: - char m_ownServerId[33]; - char m_ownServerAuthToken[33]; - char m_ownClientAuthToken[33]; + char m_sOwnServerId[33]; + char m_sOwnServerAuthToken[33]; + char m_sOwnClientAuthToken[33]; - std::string m_ownModInfoJson; - std::string ns_auth_srvName; // Unicode unescaped version of Cvar_ns_auth_servername for support in cjk characters - std::string ns_auth_srvDesc; // Unicode unescaped version of Cvar_ns_auth_serverdesc for support in cjk characters + std::string m_sOwnModInfoJson; bool m_bOriginAuthWithMasterServerDone = false; bool m_bOriginAuthWithMasterServerInProgress = false; bool m_bRequireClientAuth = false; - bool m_savingPersistentData = false; + bool m_bSavingPersistentData = false; - bool m_scriptRequestingServerList = false; - bool m_successfullyConnected = true; + bool m_bScriptRequestingServerList = false; + bool m_bSuccessfullyConnected = true; bool m_bNewgameAfterSelfAuth = false; - bool m_scriptAuthenticatingWithGameServer = false; - bool m_successfullyAuthenticatedWithGameServer = false; - - std::string s_authfail_reason {}; + bool m_bScriptAuthenticatingWithGameServer = false; + bool m_bSuccessfullyAuthenticatedWithGameServer = false; + std::string m_sAuthFailureReason {}; - bool m_hasPendingConnectionInfo = false; + bool m_bHasPendingConnectionInfo = false; RemoteServerConnectionInfo m_pendingConnectionInfo; - std::vector<RemoteServerInfo> m_remoteServers; + std::vector<RemoteServerInfo> m_vRemoteServers; bool m_bHasMainMenuPromoData = false; - MainMenuPromoData m_MainMenuPromoData; - - private: - void SetCommonHttpClientOptions(CURL* curl); + MainMenuPromoData m_sMainMenuPromoData; public: MasterServerManager(); + void ClearServerList(); void RequestServerList(); void RequestMainMenuPromos(); - void AuthenticateOriginWithMasterServer(char* uid, char* originToken); - void AuthenticateWithOwnServer(char* uid, char* playerToken); - void AuthenticateWithServer(char* uid, char* playerToken, char* serverId, char* password); - void - AddSelfToServerList(int port, int authPort, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password); - void UpdateServerMapAndPlaylist(char* map, char* playlist, int playerCount); - void UpdateServerPlayerCount(int playerCount); - void WritePlayerPersistentData(char* playerId, char* pdata, size_t pdataSize); - void RemoveSelfFromServerList(); + void AuthenticateOriginWithMasterServer(const char* uid, const char* originToken); + void AuthenticateWithOwnServer(const char* uid, const char* playerToken); + void AuthenticateWithServer(const char* uid, const char* playerToken, const char* serverId, const char* password); + void WritePlayerPersistentData(const char* playerId, const char* pdata, size_t pdataSize); }; -std::string unescape_unicode(const std::string& str); -void UpdateServerInfoFromUnicodeToUTF8(); -void InitialiseSharedMasterServer(HMODULE baseAddress); -extern MasterServerManager* g_MasterServerManager; +extern MasterServerManager* g_pMasterServerManager; extern ConVar* Cvar_ns_masterserver_hostname; -extern ConVar* Cvar_ns_server_password; diff --git a/NorthstarDLL/maxplayers.cpp b/NorthstarDLL/maxplayers.cpp index 2d513c71..bc7fce32 100644 --- a/NorthstarDLL/maxplayers.cpp +++ b/NorthstarDLL/maxplayers.cpp @@ -1,6 +1,8 @@ #include "pch.h" +#include "tier0.h" #include "maxplayers.h" -#include "gameutils.h" + +AUTOHOOK_INIT() // never set this to anything below 32 #define NEW_MAX_PLAYERS 64 @@ -45,50 +47,31 @@ 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; -#include "nsmem.h" -template <class T> void ChangeOffset(void* addr, unsigned int offset) -{ - NSMem::BytePatch((uintptr_t)addr, (BYTE*)&offset, sizeof(T)); -} - -/* -typedef bool(*MatchRecvPropsToSendProps_R_Type)(__int64 lookup, __int64 tableNameBroken, __int64 sendTable, __int64 recvTable); -MatchRecvPropsToSendProps_R_Type MatchRecvPropsToSendProps_R_Original; - -bool MatchRecvPropsToSendProps_R_Hook(__int64 lookup, __int64 tableNameBroken, __int64 sendTable, __int64 recvTable) +bool MaxPlayersIncreaseEnabled() { - const char* tableName = *(const char**)(sendTable + 0x118); - - spdlog::info("MatchRecvPropsToSendProps_R table name {}", tableName); - - bool orig = MatchRecvPropsToSendProps_R_Original(lookup, tableNameBroken, sendTable, recvTable); - return orig; + static bool bMaxPlayersIncreaseEnabled = Tier0::CommandLine()->CheckParm("-experimentalmaxplayersincrease"); + return bMaxPlayersIncreaseEnabled; } -typedef bool(*DataTable_SetupReceiveTableFromSendTable_Type)(__int64 sendTable, bool needsDecoder); -DataTable_SetupReceiveTableFromSendTable_Type DataTable_SetupReceiveTableFromSendTable_Original; - -bool DataTable_SetupReceiveTableFromSendTable_Hook(__int64 sendTable, bool needsDecoder) +// should we use R2 for this? not sure +namespace R2 // use R2 namespace for game funcs { - const char* tableName = *(const char**)(sendTable + 0x118); + int GetMaxPlayers() + { + if (MaxPlayersIncreaseEnabled()) + return NEW_MAX_PLAYERS; - spdlog::info("DataTable_SetupReceiveTableFromSendTable table name {}", tableName); - if (!strcmp(tableName, "m_bConnected")) { - char f[64]; - sprintf_s(f, "%p", sendTable); - MessageBoxA(0, f, "DataTable_SetupReceiveTableFromSendTable", 0); + return 32; } +} // namespace R2 - return DataTable_SetupReceiveTableFromSendTable_Original(sendTable, needsDecoder); +template <class T> void ChangeOffset(MemoryAddress addr, unsigned int offset) +{ + addr.Patch((BYTE*)&offset, sizeof(T)); } -*/ -typedef void* (*StringTables_CreateStringTable_Type)( - __int64 thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags); -StringTables_CreateStringTable_Type StringTables_CreateStringTable_Original; - -void* StringTables_CreateStringTable_Hook( - __int64 thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags) +AUTOHOOK(StringTables_CreateStringTable, engine.dll + 0x22E220, +void*,, (void* thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags)) { // Change the amount of entries to account for a bigger player amount if (!strcmp(name, "userinfo")) @@ -100,36 +83,33 @@ void* StringTables_CreateStringTable_Hook( maxentries = maxPlayersPowerOf2; } - return StringTables_CreateStringTable_Original(thisptr, name, maxentries, userdatafixedsize, userdatanetworkbits, flags); + return StringTables_CreateStringTable(thisptr, name, maxentries, userdatafixedsize, userdatanetworkbits, flags); } -bool MaxPlayersIncreaseEnabled() -{ - return CommandLine() && CommandLine()->CheckParm("-experimentalmaxplayersincrease"); -} - -void InitialiseMaxPlayersOverride_Engine(HMODULE baseAddress) +ON_DLL_LOAD("engine.dll", MaxPlayersOverride_Engine, (CModule module)) { if (!MaxPlayersIncreaseEnabled()) return; + AUTOHOOK_DISPATCH_MODULE(engine.dll) + // patch GetPlayerLimits to ignore the boundary limit - ChangeOffset<unsigned char>((char*)baseAddress + 0x116458, 0xEB); // jle => jmp + module.Offset(0x116458).Patch("0xEB"); // jle => jmp // patch ED_Alloc to change nFirstIndex - ChangeOffset<int>((char*)baseAddress + 0x18F46C + 1, NEW_MAX_PLAYERS + 8 + 1); // original: 41 (sv.GetMaxClients() + 1) + 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>((char*)baseAddress + 0x119543 + 2, NEW_MAX_PLAYERS + 8 + 1); // original: 41 (sv.GetMaxClients() + 1) + 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>((char*)baseAddress + 0x11957F + 2, NEW_MAX_PLAYERS); // original: 32 + 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>((char*)baseAddress + 0x119586 + 2, NEW_MAX_PLAYERS + 1); // original: 33 (32 + 1) + ChangeOffset<unsigned char>(module.Offset(0x119586 + 2), NEW_MAX_PLAYERS + 1); // original: 33 (32 + 1) // patch max players somewhere in CClientState - ChangeOffset<unsigned char>((char*)baseAddress + 0x1A162C + 2, NEW_MAX_PLAYERS - 1); // original: 31 (32 - 1) + ChangeOffset<unsigned char>(module.Offset(0x1A162C + 2), NEW_MAX_PLAYERS - 1); // original: 31 (32 - 1) // patch max players in userinfo stringtable creation /*{ @@ -142,22 +122,10 @@ void InitialiseMaxPlayersOverride_Engine(HMODULE baseAddress) // proper fix below // patch max players in userinfo stringtable creation loop - ChangeOffset<unsigned char>((char*)baseAddress + 0x114C48 + 2, NEW_MAX_PLAYERS); // original: 32 + ChangeOffset<unsigned char>(module.Offset(0x114C48 + 2), NEW_MAX_PLAYERS); // original: 32 // do not load prebaked SendTable message list - ChangeOffset<unsigned char>((char*)baseAddress + 0x75859, 0xEB); // jnz -> jmp - - HookEnabler hook; - - // ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x209000, &MatchRecvPropsToSendProps_R_Hook, - // reinterpret_cast<LPVOID*>(&MatchRecvPropsToSendProps_R_Original)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1FACD0, - // &DataTable_SetupReceiveTableFromSendTable_Hook, reinterpret_cast<LPVOID*>(&DataTable_SetupReceiveTableFromSendTable_Original)); - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x22E220, - &StringTables_CreateStringTable_Hook, - reinterpret_cast<LPVOID*>(&StringTables_CreateStringTable_Original)); + module.Offset(0x75859).Patch("EB"); // jnz -> jmp } typedef void (*RunUserCmds_Type)(bool a1, float a2); @@ -167,7 +135,8 @@ HMODULE serverBase = 0; auto RandomIntZeroMax = (__int64(__fastcall*)())0; // lazy rebuild -void RunUserCmds_Hook(bool a1, float a2) +AUTOHOOK(RunUserCmds, server.dll + 0x483D10, +void,, (bool a1, float a2)) { unsigned char v3; // bl int v5; // er14 @@ -308,162 +277,158 @@ void RunUserCmds_Hook(bool a1, float a2) } } -typedef __int64 (*SendPropArray2_Type)(__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn, unsigned char unk1); -SendPropArray2_Type SendPropArray2_Original; - -__int64 __fastcall SendPropArray2_Hook(__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn, unsigned char unk1) +AUTOHOOK(SendPropArray2, server.dll + 0x12B130, +__int64, __fastcall, (__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn, unsigned char unk1)) { // Change the amount of elements to account for a bigger player amount if (!strcmp(name, "\"player_array\"")) elements = NEW_MAX_PLAYERS; - return SendPropArray2_Original(recvProp, elements, flags, name, proxyFn, unk1); + return SendPropArray2(recvProp, elements, flags, name, proxyFn, unk1); } -void InitialiseMaxPlayersOverride_Server(HMODULE baseAddress) +ON_DLL_LOAD("server.dll", MaxPlayersOverride_Server, (CModule module)) { if (!MaxPlayersIncreaseEnabled()) return; + AUTOHOOK_DISPATCH_MODULE(server.dll) + // get required data - serverBase = GetModuleHandleA("server.dll"); + serverBase = (HMODULE)module.m_nAddress; RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax")); // patch max players amount - ChangeOffset<unsigned char>((char*)baseAddress + 0x9A44D + 3, NEW_MAX_PLAYERS); // 0x20 (32) => 0x80 (128) + ChangeOffset<unsigned char>(module.Offset(0x9A44D + 3), NEW_MAX_PLAYERS); // 0x20 (32) => 0x80 (128) // patch SpawnGlobalNonRewinding to change forced edict index - ChangeOffset<unsigned char>((char*)baseAddress + 0x2BC403 + 2, NEW_MAX_PLAYERS + 1); // original: 33 (32 + 1) + 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>((char*)baseAddress + 0x5C560A + 1, CPlayerResource_ModifiedSize); + ChangeOffset<unsigned int>(module.Offset(0x5C560A + 1), CPlayerResource_ModifiedSize); // DT_PlayerResource::m_iPing SendProp - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5059 + 2, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C50A8 + 2, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C50E2 + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xB94598, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB9459C, NEW_MAX_PLAYERS + 1); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB945C0, PlayerResource_Ping_Size); + 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>((char*)baseAddress + 0x5C5110 + 2, CPlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C519C + 2, CPlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C517E + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xB94600, CPlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB94604, NEW_MAX_PLAYERS + 1); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB94628, PlayerResource_Team_Size); + 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>((char*)baseAddress + 0x5C51C0 + 2, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5204 + 2, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C523E + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xB94668, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB9466C, NEW_MAX_PLAYERS + 1); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB94690, PlayerResource_PRHealth_Size); + 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>((char*)baseAddress + 0x5C526C + 2, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C52B4 + 2, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C52EE + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xB946D0, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB946D4, NEW_MAX_PLAYERS + 1); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB946F8, PlayerResource_Connected_Size); + 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>((char*)baseAddress + 0x5C5321 + 2, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5364 + 2, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C539E + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xB94738, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB9473C, NEW_MAX_PLAYERS + 1); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB94760, PlayerResource_Alive_Size); + 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>((char*)baseAddress + 0x5C53CC + 2, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5414 + 2, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C544E + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xB947A0, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB947A4, NEW_MAX_PLAYERS + 1); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB947C8, PlayerResource_BoolStats_Size); + 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>((char*)baseAddress + 0x5C547C + 2, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C54E2 + 2, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C54FE + 4, PlayerResource_KillStats_Length); + 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>((char*)baseAddress + 0xB94808, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB9480C, PlayerResource_KillStats_Length); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB94830, PlayerResource_KillStats_Size); + 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>((char*)baseAddress + 0x5C5528 + 2, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5576 + 2, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5584 + 4, PlayerResource_ScoreStats_Length); + 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>((char*)baseAddress + 0xB94870, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB94874, PlayerResource_ScoreStats_Length); - ChangeOffset<unsigned short>((char*)baseAddress + 0xB94898, PlayerResource_ScoreStats_Size); + 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>((char*)baseAddress + 0x5C66EE + 4, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C672E + 4, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + 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>((char*)baseAddress + 0x5C6394 + 4, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C63DB + 4, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); + 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>((char*)baseAddress + 0x5C63FD + 4, CPlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C6442 + 4, CPlayerResource_OriginalSize + PlayerResource_Team_Start); + 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>((char*)baseAddress + 0x5C645B + 4, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C64A0 + 4, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + 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>((char*)baseAddress + 0x5C64AA + 4, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C64F0 + 4, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + 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>((char*)baseAddress + 0x5C650A + 4, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C654F + 4, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); + 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>((char*)baseAddress + 0x5C6557 + 4, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C65A5 + 4, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + 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>((char*)baseAddress + 0x5C65C2 + 3, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C65E3 + 4, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + 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>((char*)baseAddress + 0x5C6654 + 3, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x5C665B + 3, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - - // GameLoop::RunUserCmds - rebuild - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x483D10, &RunUserCmds_Hook, reinterpret_cast<LPVOID*>(&RunUserCmds_Original)); + ChangeOffset<unsigned int>(module.Offset(0x5C6654 + 3), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); + ChangeOffset<unsigned int>(module.Offset(0x5C665B + 3), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - *(DWORD*)((char*)baseAddress + 0x14E7390) = 0; - auto DT_PlayerResource_Construct = (__int64(__fastcall*)())((char*)baseAddress + 0x5C4FE0); + *module.Offset(0x14E7390).As<DWORD*>() = 0; + auto DT_PlayerResource_Construct = module.Offset(0x5C4FE0).As<__int64(__fastcall*)()>(); DT_PlayerResource_Construct(); constexpr int CTeam_OriginalSize = 3336; @@ -471,191 +436,186 @@ void InitialiseMaxPlayersOverride_Server(HMODULE baseAddress) 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>((char*)baseAddress + 0x23924A + 1, CTeam_ModifiedSize); + ChangeOffset<unsigned int>(module.Offset(0x23924A + 1), CTeam_ModifiedSize); // CTeam::CTeam - increase memset length to clean newly allocated data - ChangeOffset<unsigned int>((char*)baseAddress + 0x2395AE + 2, 256 + CTeam_AddedSize); - - // hook required to change the size of DT_Team::"player_array" - HookEnabler hook2; - ENABLER_CREATEHOOK(hook2, (char*)baseAddress + 0x12B130, &SendPropArray2_Hook, reinterpret_cast<LPVOID*>(&SendPropArray2_Original)); - hook2.~HookEnabler(); // force hook before calling construct function + ChangeOffset<unsigned int>(module.Offset(0x2395AE + 2), 256 + CTeam_AddedSize); - *(DWORD*)((char*)baseAddress + 0xC945A0) = 0; - auto DT_Team_Construct = (__int64(__fastcall*)())((char*)baseAddress + 0x238F50); + *module.Offset(0xC945A0).As<DWORD*>() = 0; + auto DT_Team_Construct = module.Offset(0x238F50).As<__int64(__fastcall*)()>(); DT_Team_Construct(); } -typedef __int64 (*RecvPropArray2_Type)(__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn); -RecvPropArray2_Type RecvPropArray2_Original; - -__int64 __fastcall RecvPropArray2_Hook(__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn) +AUTOHOOK(RecvPropArray2, client.dll + 0x1CEDA0, +__int64, __fastcall, (__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn)) { // Change the amount of elements to account for a bigger player amount if (!strcmp(name, "\"player_array\"")) elements = NEW_MAX_PLAYERS; - return RecvPropArray2_Original(recvProp, elements, flags, name, proxyFn); + return RecvPropArray2(recvProp, elements, flags, name, proxyFn); } -void InitialiseMaxPlayersOverride_Client(HMODULE baseAddress) +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>((char*)baseAddress + 0x164C41 + 1, C_PlayerResource_ModifiedSize); + ChangeOffset<unsigned int>(module.Offset(0x164C41 + 1), C_PlayerResource_ModifiedSize); // C_PlayerResource::C_PlayerResource - change loop end value - ChangeOffset<unsigned char>((char*)baseAddress + 0x1640C4 + 2, NEW_MAX_PLAYERS - 32); + ChangeOffset<unsigned char>(module.Offset(0x1640C4 + 2), NEW_MAX_PLAYERS - 32); // C_PlayerResource::C_PlayerResource - change m_szName address ChangeOffset<unsigned int>( - (char*)baseAddress + 0x1640D0 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // appended to the end of the class + 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>( - (char*)baseAddress + 0x1640D0 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // appended to the end of the class + 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>((char*)baseAddress + 0x1640D0 + 3, 2244 + C_PlayerResource_AddedSize); + ChangeOffset<unsigned int>(module.Offset(0x1640D0 + 3), 2244 + C_PlayerResource_AddedSize); // C_PlayerResource::UpdatePlayerName - change m_szName address - ChangeOffset<unsigned int>((char*)baseAddress + 0x16431F + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + ChangeOffset<unsigned int>(module.Offset(0x16431F + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // C_PlayerResource::GetPlayerName - change m_szName address 1 - ChangeOffset<unsigned int>((char*)baseAddress + 0x1645B1 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + ChangeOffset<unsigned int>(module.Offset(0x1645B1 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // C_PlayerResource::GetPlayerName - change m_szName address 2 - ChangeOffset<unsigned int>((char*)baseAddress + 0x1645C0 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + ChangeOffset<unsigned int>(module.Offset(0x1645C0 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // C_PlayerResource::GetPlayerName - change m_szName address 3 - ChangeOffset<unsigned int>((char*)baseAddress + 0x1645DD + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + 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>((char*)baseAddress + 0x164B71 + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + 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>((char*)baseAddress + 0x164B9B + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + ChangeOffset<unsigned int>(module.Offset(0x164B9B + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 1 - ChangeOffset<unsigned int>((char*)baseAddress + 0x164641 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + ChangeOffset<unsigned int>(module.Offset(0x164641 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 2 - ChangeOffset<unsigned int>((char*)baseAddress + 0x164650 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + ChangeOffset<unsigned int>(module.Offset(0x164650 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 3 - ChangeOffset<unsigned int>((char*)baseAddress + 0x16466D + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + 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>((char*)baseAddress + 0x164BA3 + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + 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>((char*)baseAddress + 0x164BCE + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + 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>((char*)baseAddress + 0x164BE7 + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + ChangeOffset<unsigned int>(module.Offset(0x164BE7 + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // C_PlayerResource::m_szName - ChangeOffset<unsigned int>((char*)baseAddress + 0xc350f8, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc350f8 + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0x163415 + 6, C_PlayerResource_ModifiedSize); + ChangeOffset<unsigned int>(module.Offset(0x163415 + 6), C_PlayerResource_ModifiedSize); // DT_PlayerResource::m_iPing RecvProp - ChangeOffset<unsigned int>((char*)baseAddress + 0x163492 + 2, C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x1634D6 + 2, C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163515 + 5, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xc35170, C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc35170 + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0x163549 + 2, C_PlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x1635C8 + 2, C_PlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x1635AD + 5, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xc351e8, C_PlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc351e8 + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0x1635F9 + 2, C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163625 + 2, C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163675 + 5, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xc35260, C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc35260 + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0x1636A9 + 2, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x1636D5 + 2, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163725 + 5, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xc352d8, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc352d8 + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0x163759 + 2, C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163785 + 2, C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x1637D5 + 5, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xc35350, C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc35350 + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0x163809 + 2, C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163835 + 2, C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163885 + 5, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0xc353c8, C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc353c8 + 4, NEW_MAX_PLAYERS + 1); + 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>((char*)baseAddress + 0x1638B3 + 2, C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x1638E5 + 2, C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163935 + 5, PlayerResource_KillStats_Length); + 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>((char*)baseAddress + 0xc35440, C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc35440 + 4, PlayerResource_KillStats_Length); + 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>((char*)baseAddress + 0x163969 + 2, C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x163995 + 2, C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset<unsigned int>((char*)baseAddress + 0x1639E5 + 5, PlayerResource_ScoreStats_Length); + 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>((char*)baseAddress + 0xc354b8, C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset<unsigned short>((char*)baseAddress + 0xc354b8 + 4, PlayerResource_ScoreStats_Length); + 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>((char*)baseAddress + 0x164599 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned int>(module.Offset(0x164599 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); // C_PlayerResource::GetPlayerName2 (?) - change m_bConnected address - ChangeOffset<unsigned int>((char*)baseAddress + 0x164629 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + 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>((char*)baseAddress + 0x164B13 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + 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>((char*)baseAddress + 0x164860 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + 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>((char*)baseAddress + 0x164834 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned int>(module.Offset(0x164834 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - *(DWORD*)((char*)baseAddress + 0xC35068) = 0; - auto DT_PlayerResource_Construct = (__int64(__fastcall*)())((char*)baseAddress + 0x163400); + *module.Offset(0xC35068).As<DWORD*>() = 0; + auto DT_PlayerResource_Construct = module.Offset(0x163400).As<__int64(__fastcall*)()>(); DT_PlayerResource_Construct(); constexpr int C_Team_OriginalSize = 3200; @@ -663,20 +623,15 @@ void InitialiseMaxPlayersOverride_Client(HMODULE baseAddress) 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>((char*)baseAddress + 0x182321 + 1, C_Team_ModifiedSize); + 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>((char*)baseAddress + 0x1804A2 + 2, 256 + C_Team_AddedSize); + ChangeOffset<unsigned int>(module.Offset(0x1804A2 + 2), 256 + C_Team_AddedSize); // DT_Team size - ChangeOffset<unsigned int>((char*)baseAddress + 0xC3AA0C, C_Team_ModifiedSize); - - // hook required to change the size of DT_Team::"player_array" - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1CEDA0, &RecvPropArray2_Hook, reinterpret_cast<LPVOID*>(&RecvPropArray2_Original)); - hook.~HookEnabler(); // force hook before calling construct function + ChangeOffset<unsigned int>(module.Offset(0xC3AA0C), C_Team_ModifiedSize); - *(DWORD*)((char*)baseAddress + 0xC3AFF8) = 0; - auto DT_Team_Construct = (__int64(__fastcall*)())((char*)baseAddress + 0x17F950); + *module.Offset(0xC3AFF8).As<DWORD*>() = 0; + auto DT_Team_Construct = module.Offset(0x17F950).As<__int64(__fastcall*)()>(); DT_Team_Construct(); } diff --git a/NorthstarDLL/maxplayers.h b/NorthstarDLL/maxplayers.h index 5ce8403a..b251f6a6 100644 --- a/NorthstarDLL/maxplayers.h +++ b/NorthstarDLL/maxplayers.h @@ -1,4 +1,7 @@ #pragma once -void InitialiseMaxPlayersOverride_Engine(HMODULE baseAddress); -void InitialiseMaxPlayersOverride_Server(HMODULE baseAddress); -void InitialiseMaxPlayersOverride_Client(HMODULE baseAddress); + +// should we use R2 for this? not sure +namespace R2 // use R2 namespace for game funcs +{ + int GetMaxPlayers(); +} // namespace R2 diff --git a/NorthstarDLL/memalloc.cpp b/NorthstarDLL/memalloc.cpp index e2240269..8c0fafc8 100644 --- a/NorthstarDLL/memalloc.cpp +++ b/NorthstarDLL/memalloc.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "memalloc.h" -#include "gameutils.h" +#include "tier0.h" + +using namespace Tier0; // TODO: rename to malloc and free after removing statically compiled .libs @@ -8,9 +10,8 @@ extern "C" void* _malloc_base(size_t n) { // allocate into static buffer if g_pMemAllocSingleton isn't initialised if (!g_pMemAllocSingleton) - { - InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); - } + TryCreateGlobalMemAlloc(); + return g_pMemAllocSingleton->m_vtable->Alloc(g_pMemAllocSingleton, n); } @@ -22,19 +23,16 @@ extern "C" void* _malloc_base(size_t n) extern "C" void _free_base(void* p) { if (!g_pMemAllocSingleton) - { - spdlog::warn("Trying to free something before g_pMemAllocSingleton was ready, this should never happen"); - InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); - } + TryCreateGlobalMemAlloc(); + g_pMemAllocSingleton->m_vtable->Free(g_pMemAllocSingleton, p); } extern "C" void* _realloc_base(void* oldPtr, size_t size) { if (!g_pMemAllocSingleton) - { - InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); - } + TryCreateGlobalMemAlloc(); + return g_pMemAllocSingleton->m_vtable->Realloc(g_pMemAllocSingleton, oldPtr, size); } diff --git a/NorthstarDLL/memory.cpp b/NorthstarDLL/memory.cpp new file mode 100644 index 00000000..8b7694bd --- /dev/null +++ b/NorthstarDLL/memory.cpp @@ -0,0 +1,348 @@ +#include "pch.h" +#include "memory.h" + +MemoryAddress::MemoryAddress() : m_nAddress(0) {} +MemoryAddress::MemoryAddress(const uintptr_t nAddress) : m_nAddress(nAddress) {} +MemoryAddress::MemoryAddress(const void* pAddress) : m_nAddress(reinterpret_cast<uintptr_t>(pAddress)) {} + +// operators +MemoryAddress::operator uintptr_t() const +{ + return m_nAddress; +} + +MemoryAddress::operator void*() const +{ + return reinterpret_cast<void*>(m_nAddress); +} + +MemoryAddress::operator bool() const +{ + return m_nAddress != 0; +} + +bool MemoryAddress::operator==(const MemoryAddress& other) const +{ + return m_nAddress == other.m_nAddress; +} + +bool MemoryAddress::operator!=(const MemoryAddress& other) const +{ + return m_nAddress != other.m_nAddress; +} + +bool MemoryAddress::operator==(const uintptr_t& addr) const +{ + return m_nAddress == addr; +} + +bool MemoryAddress::operator!=(const uintptr_t& addr) const +{ + return m_nAddress != addr; +} + +MemoryAddress MemoryAddress::operator+(const MemoryAddress& other) const +{ + return Offset(other.m_nAddress); +} + +MemoryAddress MemoryAddress::operator-(const MemoryAddress& other) const +{ + return MemoryAddress(m_nAddress - other.m_nAddress); +} + +MemoryAddress MemoryAddress::operator+(const uintptr_t& addr) const +{ + return Offset(addr); +} + +MemoryAddress MemoryAddress::operator-(const uintptr_t& addr) const +{ + return MemoryAddress(m_nAddress - addr); +} + +MemoryAddress MemoryAddress::operator*() const +{ + return Deref(); +} + +// traversal +MemoryAddress MemoryAddress::Offset(const uintptr_t nOffset) const +{ + return MemoryAddress(m_nAddress + nOffset); +} + +MemoryAddress MemoryAddress::Deref(const int nNumDerefs) const +{ + uintptr_t ret = m_nAddress; + for (int i = 0; i < nNumDerefs; i++) + ret = *reinterpret_cast<uintptr_t*>(ret); + + return MemoryAddress(ret); +} + +// patching +void MemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize) +{ + if (nSize) + WriteProcessMemory(GetCurrentProcess(), reinterpret_cast<LPVOID>(m_nAddress), pBytes, nSize, NULL); +} + +void MemoryAddress::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(false, "Failed to parse invalid hex string."); + val = -1; + } + + result += (j == 0) ? val * 16 : val; + } + ret.push_back(result); + } + + i++; + } + + return ret; +} + +void MemoryAddress::Patch(const char* pBytes) +{ + std::vector<uint8_t> vBytes = HexBytesToString(pBytes); + Patch(vBytes.data(), vBytes.size()); +} + +void MemoryAddress::NOP(const size_t nSize) +{ + uint8_t* pBytes = new uint8_t[nSize]; + + memset(pBytes, 0x90, nSize); + Patch(pBytes, nSize); + + delete[] pBytes; +} + +bool MemoryAddress::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)) {} + +MemoryAddress CModule::GetExport(const char* pExportName) +{ + return MemoryAddress(reinterpret_cast<uintptr_t>(GetProcAddress(reinterpret_cast<HMODULE>(m_nAddress), pExportName))); +} + +MemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask) +{ + if (!m_ExecutableCode.IsSectionValid()) + return MemoryAddress(); + + 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 MemoryAddress(const_cast<uint8_t*>(pData)); + } + } + else + goto CONTINUE; + } + + return MemoryAddress((&*(const_cast<uint8_t*>(pData)))); + } + } + + CONTINUE:; + } + + return MemoryAddress(); +} + +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(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); +} + +MemoryAddress CModule::FindPattern(const char* pPattern) +{ + const auto pattern = MaskedBytesFromPattern(pPattern); + return FindPattern(pattern.first.data(), pattern.second.c_str()); +} diff --git a/NorthstarDLL/memory.h b/NorthstarDLL/memory.h new file mode 100644 index 00000000..38c76cb3 --- /dev/null +++ b/NorthstarDLL/memory.h @@ -0,0 +1,90 @@ +#pragma once + +class MemoryAddress +{ + public: + uintptr_t m_nAddress; + + public: + MemoryAddress(); + MemoryAddress(const uintptr_t nAddress); + MemoryAddress(const void* pAddress); + + // operators + operator uintptr_t() const; + operator void*() const; + operator bool() const; + + bool operator==(const MemoryAddress& other) const; + bool operator!=(const MemoryAddress& other) const; + bool operator==(const uintptr_t& addr) const; + bool operator!=(const uintptr_t& addr) const; + + MemoryAddress operator+(const MemoryAddress& other) const; + MemoryAddress operator-(const MemoryAddress& other) const; + MemoryAddress operator+(const uintptr_t& other) const; + MemoryAddress operator-(const uintptr_t& other) const; + MemoryAddress operator*() const; + + template <typename T> T As() + { + return reinterpret_cast<T>(m_nAddress); + } + + // traversal + MemoryAddress Offset(const uintptr_t nOffset) const; + MemoryAddress 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 MemoryAddress +{ + 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); + + MemoryAddress GetExport(const char* pExportName); + MemoryAddress FindPattern(const uint8_t* pPattern, const char* pMask); + MemoryAddress FindPattern(const char* pPattern); +}; diff --git a/NorthstarDLL/miscclientfixes.cpp b/NorthstarDLL/miscclientfixes.cpp deleted file mode 100644 index fc57d534..00000000 --- a/NorthstarDLL/miscclientfixes.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "pch.h" -#include "miscclientfixes.h" -#include "hookutils.h" -#include "dedicated.h" - -typedef void* (*CrashingWeaponActivityFuncType)(void* a1); -CrashingWeaponActivityFuncType CrashingWeaponActivityFunc0; -CrashingWeaponActivityFuncType CrashingWeaponActivityFunc1; - -void* CrashingWeaponActivityFunc0Hook(void* a1) -{ - // this return is safe, other functions that use this value seemingly dont care about it being null - if (!a1) - return 0; - - return CrashingWeaponActivityFunc0(a1); -} - -void* CrashingWeaponActivityFunc1Hook(void* a1) -{ - // this return is safe, other functions that use this value seemingly dont care about it being null - if (!a1) - return 0; - - return CrashingWeaponActivityFunc1(a1); -} - -void InitialiseMiscClientFixes(HMODULE baseAddress) -{ - if (IsDedicatedServer()) - return; - - HookEnabler hook; - - // these functions will occasionally pass a null pointer on respawn, unsure what causes this but seems easiest just to return null if - // null, which seems to work fine fucking sucks this has to be fixed like this but unsure what exactly causes this serverside, breaks - // vanilla compatibility to a degree tho will say i have about 0 clue what exactly these functions do, testing this it doesn't even seem - // like they do much of anything i can see tbh - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x5A92D0, &CrashingWeaponActivityFunc0Hook, reinterpret_cast<LPVOID*>(&CrashingWeaponActivityFunc0)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x5A9310, &CrashingWeaponActivityFunc1Hook, reinterpret_cast<LPVOID*>(&CrashingWeaponActivityFunc1)); - - // experimental: allow cl_extrapolate to be enabled without cheats - { - void* ptr = (char*)baseAddress + 0x275F9D9; - *((char*)ptr) = (char)0; - } -} diff --git a/NorthstarDLL/miscclientfixes.h b/NorthstarDLL/miscclientfixes.h deleted file mode 100644 index d2aeea25..00000000 --- a/NorthstarDLL/miscclientfixes.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void InitialiseMiscClientFixes(HMODULE baseAddress); diff --git a/NorthstarDLL/misccommands.cpp b/NorthstarDLL/misccommands.cpp index 42ec15af..572d5620 100644 --- a/NorthstarDLL/misccommands.cpp +++ b/NorthstarDLL/misccommands.cpp @@ -1,60 +1,65 @@ #include "pch.h" #include "misccommands.h" #include "concommand.h" -#include "gameutils.h" +#include "playlist.h" +#include "r2engine.h" +#include "r2client.h" +#include "hoststate.h" #include "masterserver.h" #include "serverauthentication.h" #include "squirrel.h" +void ConCommand_force_newgame(const CCommand& arg) +{ + if (arg.ArgC() < 2) + return; + + R2::g_pHostState->m_iNextState = R2::HostState_t::HS_NEW_GAME; + strncpy(R2::g_pHostState->m_levelName, arg.Arg(1), sizeof(R2::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(R2::g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); +} + +void ConCommand_ns_end_reauth_and_leave_to_lobby(const CCommand& arg) +{ + R2::Cbuf_AddText( + R2::Cbuf_GetCurrentPlayer(), + fmt::format("serverfilter {}", g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first).c_str(), + R2::cmd_source_t::kCommandSrcCode); + R2::Cbuf_Execute(); + + // 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(R2::g_pHostState->m_levelName, "mp_lobby"); + R2::g_pHostState->m_iNextState = R2::HostState_t::HS_NEW_GAME; + } +} + void AddMiscConCommands() { - MAKE_CONCMD( + RegisterConCommand( "force_newgame", + ConCommand_force_newgame, "forces a map load through directly setting g_pHostState->m_iNextState to HS_NEW_GAME", - FCVAR_NONE, - [](const CCommand& arg) - { - if (arg.ArgC() < 2) - return; - - g_pHostState->m_iNextState = HS_NEW_GAME; - strncpy(g_pHostState->m_levelName, arg.Arg(1), sizeof(g_pHostState->m_levelName)); - }); + FCVAR_NONE); - MAKE_CONCMD( + 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, - [](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_MasterServerManager->m_bNewgameAfterSelfAuth = true; - g_MasterServerManager->AuthenticateWithOwnServer(g_LocalPlayerUserID, g_MasterServerManager->m_ownClientAuthToken); - }); + FCVAR_SERVER_CAN_EXECUTE); // this is a concommand because we make a deferred call to it from another thread - MAKE_CONCMD( - "ns_end_reauth_and_leave_to_lobby", - "", - FCVAR_NONE, - [](const CCommand& arg) - { - Cbuf_AddText( - Cbuf_GetCurrentPlayer(), - fmt::format("serverfilter {}", g_ServerAuthenticationManager->m_authData.begin()->first).c_str(), - cmd_source_t::kCommandSrcCode); - Cbuf_Execute(); - - // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this - if (g_ClientSquirrelManager->sqvm) - { - g_ServerAuthenticationManager->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? - SetCurrentPlaylist("tdm"); - strcpy(g_pHostState->m_levelName, "mp_lobby"); - g_pHostState->m_iNextState = HS_NEW_GAME; - } - }); + RegisterConCommand("ns_end_reauth_and_leave_to_lobby", ConCommand_ns_end_reauth_and_leave_to_lobby, "", FCVAR_NONE); } diff --git a/NorthstarDLL/miscserverfixes.cpp b/NorthstarDLL/miscserverfixes.cpp index 728df737..4feca505 100644 --- a/NorthstarDLL/miscserverfixes.cpp +++ b/NorthstarDLL/miscserverfixes.cpp @@ -1,26 +1,7 @@ #include "pch.h" -#include "miscserverfixes.h" -#include "hookutils.h" -#include "nsmem.h" - -void InitialiseMiscServerFixes(HMODULE baseAddress) +ON_DLL_LOAD("server.dll", MiscServerFixes, (CModule module)) { - uintptr_t ba = (uintptr_t)baseAddress; - - // ret at the start of the concommand GenerateObjFile as it can crash servers - { - NSMem::BytePatch(ba + 0x38D920, "C3"); - } - // nop out call to VGUI shutdown since it crashes the game when quitting from the console - { - NSMem::NOP(ba + 0x154A96, 5); - } - - // 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 - { - NSMem::BytePatch(ba + 0x153920, "C3"); - } + module.Offset(0x154A96).NOP(5); } diff --git a/NorthstarDLL/miscserverfixes.h b/NorthstarDLL/miscserverfixes.h deleted file mode 100644 index ea31da6e..00000000 --- a/NorthstarDLL/miscserverfixes.h +++ /dev/null @@ -1 +0,0 @@ -void InitialiseMiscServerFixes(HMODULE baseAddress); diff --git a/NorthstarDLL/miscserverscript.cpp b/NorthstarDLL/miscserverscript.cpp index c371da81..3b30dd23 100644 --- a/NorthstarDLL/miscserverscript.cpp +++ b/NorthstarDLL/miscserverscript.cpp @@ -1,76 +1,64 @@ #include "pch.h" -#include "miscserverscript.h" #include "squirrel.h" #include "masterserver.h" #include "serverauthentication.h" -#include "gameutils.h" #include "dedicated.h" +#include "r2client.h" +#include "r2server.h" -// annoying helper function because i can't figure out getting players or entities from sqvm rn -// wish i didn't have to do it like this, but here we are -void* GetPlayerByIndex(int playerIndex) -{ - const int PLAYER_ARRAY_OFFSET = 0x12A53F90; - const int PLAYER_SIZE = 0x2D728; - - void* playerArrayBase = (char*)GetModuleHandleA("engine.dll") + PLAYER_ARRAY_OFFSET; - void* player = (char*)playerArrayBase + (playerIndex * PLAYER_SIZE); - - return player; -} +#include <filesystem> // void function NSEarlyWritePlayerIndexPersistenceForLeave( int playerIndex ) -SQRESULT SQ_EarlyWritePlayerIndexPersistenceForLeave(void* sqvm) +SQRESULT SQ_EarlyWritePlayerIndexPersistenceForLeave(HSquirrelVM* sqvm) { - int playerIndex = ServerSq_getinteger(sqvm, 1); - void* player = GetPlayerByIndex(playerIndex); + int playerIndex = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 1); + R2::CBaseClient* player = &R2::g_pClientArray[playerIndex]; - if (!g_ServerAuthenticationManager->m_additionalPlayerData.count(player)) + if (!g_pServerAuthentication->m_PlayerAuthenticationData.count(player)) { - ServerSq_pusherror(sqvm, fmt::format("Invalid playerindex {}", playerIndex).c_str()); + g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, fmt::format("Invalid playerindex {}", playerIndex).c_str()); return SQRESULT_ERROR; } - g_ServerAuthenticationManager->m_additionalPlayerData[player].needPersistenceWriteOnLeave = false; - g_ServerAuthenticationManager->WritePersistentData(player); + g_pServerAuthentication->m_PlayerAuthenticationData[player].needPersistenceWriteOnLeave = false; + g_pServerAuthentication->WritePersistentData(player); return SQRESULT_NULL; } // bool function NSIsWritingPlayerPersistence() -SQRESULT SQ_IsWritingPlayerPersistence(void* sqvm) +SQRESULT SQ_IsWritingPlayerPersistence(HSquirrelVM* sqvm) { - ServerSq_pushbool(sqvm, g_MasterServerManager->m_savingPersistentData); + g_pSquirrel<ScriptContext::SERVER>->pushbool(sqvm, g_pMasterServerManager->m_bSavingPersistentData); return SQRESULT_NOTNULL; } // bool function NSIsPlayerIndexLocalPlayer( int playerIndex ) -SQRESULT SQ_IsPlayerIndexLocalPlayer(void* sqvm) +SQRESULT SQ_IsPlayerIndexLocalPlayer(HSquirrelVM* sqvm) { - int playerIndex = ServerSq_getinteger(sqvm, 1); - void* player = GetPlayerByIndex(playerIndex); - if (!g_ServerAuthenticationManager->m_additionalPlayerData.count(player)) + int playerIndex = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 1); + R2::CBaseClient* player = &R2::g_pClientArray[playerIndex]; + if (!g_pServerAuthentication->m_PlayerAuthenticationData.count(player)) { - ServerSq_pusherror(sqvm, fmt::format("Invalid playerindex {}", playerIndex).c_str()); + g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, fmt::format("Invalid playerindex {}", playerIndex).c_str()); return SQRESULT_ERROR; } - ServerSq_pushbool(sqvm, !strcmp(g_LocalPlayerUserID, (char*)player + 0xF500)); + g_pSquirrel<ScriptContext::SERVER>->pushbool(sqvm, !strcmp(R2::g_pLocalPlayerUserID, player->m_UID)); return SQRESULT_NOTNULL; } // bool function NSIsDedicated() - -SQRESULT SQ_IsDedicated(void* sqvm) +SQRESULT SQ_IsDedicated(HSquirrelVM* sqvm) { - ServerSq_pushbool(sqvm, IsDedicatedServer()); + g_pSquirrel<ScriptContext::SERVER>->pushbool(sqvm, IsDedicatedServer()); return SQRESULT_NOTNULL; } -void InitialiseMiscServerScriptCommand(HMODULE baseAddress) +ON_DLL_LOAD_RELIESON("server.dll", MiscServerScriptCommands, ServerSquirrel, (CModule module)) { - g_ServerSquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration( "void", "NSEarlyWritePlayerIndexPersistenceForLeave", "int playerIndex", "", SQ_EarlyWritePlayerIndexPersistenceForLeave); - g_ServerSquirrelManager->AddFuncRegistration("bool", "NSIsWritingPlayerPersistence", "", "", SQ_IsWritingPlayerPersistence); - g_ServerSquirrelManager->AddFuncRegistration("bool", "NSIsPlayerIndexLocalPlayer", "int playerIndex", "", SQ_IsPlayerIndexLocalPlayer); - g_ServerSquirrelManager->AddFuncRegistration("bool", "NSIsDedicated", "", "", SQ_IsDedicated); + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration("bool", "NSIsWritingPlayerPersistence", "", "", SQ_IsWritingPlayerPersistence); + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration("bool", "NSIsPlayerIndexLocalPlayer", "int playerIndex", "", SQ_IsPlayerIndexLocalPlayer); + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration("bool", "NSIsDedicated", "", "", SQ_IsDedicated); } diff --git a/NorthstarDLL/miscserverscript.h b/NorthstarDLL/miscserverscript.h deleted file mode 100644 index d11e7ac5..00000000 --- a/NorthstarDLL/miscserverscript.h +++ /dev/null @@ -1,2 +0,0 @@ -void InitialiseMiscServerScriptCommand(HMODULE baseAddress); -void* GetPlayerByIndex(int playerIndex); diff --git a/NorthstarDLL/modlocalisation.cpp b/NorthstarDLL/modlocalisation.cpp index c8e76d63..14379bb3 100644 --- a/NorthstarDLL/modlocalisation.cpp +++ b/NorthstarDLL/modlocalisation.cpp @@ -1,37 +1,33 @@ #include "pch.h" -#include "modlocalisation.h" -#include "hookutils.h" #include "modmanager.h" -typedef bool (*AddLocalisationFileType)(void* g_pVguiLocalize, const char* path, const char* pathId, char unknown); -AddLocalisationFileType AddLocalisationFile; +AUTOHOOK_INIT() -bool loadModLocalisationFiles = true; - -bool AddLocalisationFileHook(void* g_pVguiLocalize, const char* path, const char* pathId, char unknown) +AUTOHOOK(AddLocalisationFile, localize.dll + 0x6D80, +bool,, (void* pVguiLocalize, const char* path, const char* pathId, char unknown)) { - bool ret = AddLocalisationFile(g_pVguiLocalize, path, pathId, unknown); + static bool bLoadModLocalisationFiles = true; + bool ret = AddLocalisationFile(pVguiLocalize, path, pathId, unknown); if (ret) spdlog::info("Loaded localisation file {} successfully", path); - if (!loadModLocalisationFiles) + if (!bLoadModLocalisationFiles) return ret; - loadModLocalisationFiles = false; + bLoadModLocalisationFiles = false; - for (Mod mod : g_ModManager->m_loadedMods) - if (mod.Enabled) + for (Mod mod : g_pModManager->m_LoadedMods) + if (mod.m_bEnabled) for (std::string& localisationFile : mod.LocalisationFiles) - AddLocalisationFile(g_pVguiLocalize, localisationFile.c_str(), pathId, unknown); + AddLocalisationFile(pVguiLocalize, localisationFile.c_str(), pathId, unknown); - loadModLocalisationFiles = true; + bLoadModLocalisationFiles = true; return ret; } -void InitialiseModLocalisation(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT("localize.dll", Localize, (CModule module)) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x6D80, AddLocalisationFileHook, reinterpret_cast<LPVOID*>(&AddLocalisationFile)); + AUTOHOOK_DISPATCH() } diff --git a/NorthstarDLL/modlocalisation.h b/NorthstarDLL/modlocalisation.h deleted file mode 100644 index 260affad..00000000 --- a/NorthstarDLL/modlocalisation.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseModLocalisation(HMODULE baseAddress); diff --git a/NorthstarDLL/modmanager.cpp b/NorthstarDLL/modmanager.cpp index 868d6cff..c9dfcdbb 100644 --- a/NorthstarDLL/modmanager.cpp +++ b/NorthstarDLL/modmanager.cpp @@ -4,6 +4,10 @@ #include "concommand.h" #include "audio.h" #include "masterserver.h" +#include "filesystem.h" +#include "rpakfilesystem.h" +#include "nsprefix.h" + #include "rapidjson/error/en.h" #include "rapidjson/document.h" #include "rapidjson/ostreamwrapper.h" @@ -13,17 +17,14 @@ #include <string> #include <sstream> #include <vector> -#include "filesystem.h" -#include "rpakfilesystem.h" -#include "nsprefix.h" -ModManager* g_ModManager; +ModManager* g_pModManager; Mod::Mod(fs::path modDir, char* jsonBuf) { - wasReadSuccessfully = false; + m_bWasReadSuccessfully = false; - ModDirectory = modDir; + m_ModDirectory = modDir; rapidjson_document modJson; modJson.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(jsonBuf); @@ -106,11 +107,55 @@ Mod::Mod(fs::path modDir, char* jsonBuf) else convar->HelpString = ""; - // todo: could possibly parse FCVAR names here instead, would be easier + convar->Flags = FCVAR_NONE; + if (convarObj.HasMember("Flags")) - convar->Flags = convarObj["Flags"].GetInt(); - else - convar->Flags = FCVAR_NONE; + { + // 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 + + std::string sFlags = convarObj["Flags"].GetString(); + sFlags += '|'; // add additional | so we register the last flag + std::string sCurrentFlag; + + for (int i = 0; i < sFlags.length(); i++) + { + if (isspace(sFlags[i])) + continue; + + // if we encounter a |, add current string as a flag + if (sFlags[i] == '|') + { + bool bHasFlags = false; + int iCurrentFlags; + + for (auto& flagPair : g_PrintCommandFlags) + { + if (!sCurrentFlag.compare(flagPair.second)) + { + iCurrentFlags = flagPair.first; + bHasFlags = true; + break; + } + } + + if (bHasFlags) + convar->Flags |= iCurrentFlags; + else + spdlog::warn("Mod ConVar {} has unknown flag {}", convar->Name, sCurrentFlag); + + sCurrentFlag = ""; + } + else + sCurrentFlag += sFlags[i]; + } + } + } ConVars.push_back(convar); } @@ -127,7 +172,7 @@ Mod::Mod(fs::path modDir, char* jsonBuf) ModScript script; script.Path = scriptObj["Path"].GetString(); - script.RsonRunOn = scriptObj["RunOn"].GetString(); + script.RunOn = scriptObj["RunOn"].GetString(); if (scriptObj.HasMember("ServerCallback") && scriptObj["ServerCallback"].IsObject()) { @@ -206,7 +251,7 @@ Mod::Mod(fs::path modDir, char* jsonBuf) } } - wasReadSuccessfully = true; + m_bWasReadSuccessfully = true; } ModManager::ModManager() @@ -223,7 +268,7 @@ ModManager::ModManager() void ModManager::LoadMods() { - if (m_hasLoadedMods) + if (m_bHasLoadedMods) UnloadMods(); std::vector<fs::path> modDirs; @@ -232,7 +277,7 @@ void ModManager::LoadMods() fs::remove_all(GetCompiledAssetsPath()); fs::create_directories(GetModFolderPath()); - DependencyConstants.clear(); + m_DependencyConstants.clear(); // read enabled mods cfg std::ifstream enabledModsStream(GetNorthstarPrefix() + "/enabledmods.json"); @@ -244,10 +289,10 @@ void ModManager::LoadMods() enabledModsStringStream << (char)enabledModsStream.get(); enabledModsStream.close(); - m_enabledModsCfg.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>( + m_EnabledModsCfg.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>( enabledModsStringStream.str().c_str()); - m_hasEnabledModsCfg = m_enabledModsCfg.IsObject(); + m_bHasEnabledModsCfg = m_EnabledModsCfg.IsObject(); } // get mod directories @@ -277,41 +322,41 @@ void ModManager::LoadMods() for (auto& pair : mod.DependencyConstants) { - if (DependencyConstants.find(pair.first) != DependencyConstants.end() && DependencyConstants[pair.first] != pair.second) + if (m_DependencyConstants.find(pair.first) != m_DependencyConstants.end() && m_DependencyConstants[pair.first] != pair.second) { spdlog::error("Constant {} in mod {} already exists in another mod.", pair.first, mod.Name); - mod.wasReadSuccessfully = false; + mod.m_bWasReadSuccessfully = false; break; } - if (DependencyConstants.find(pair.first) == DependencyConstants.end()) - DependencyConstants.emplace(pair); + if (m_DependencyConstants.find(pair.first) == m_DependencyConstants.end()) + m_DependencyConstants.emplace(pair); } - if (m_hasEnabledModsCfg && m_enabledModsCfg.HasMember(mod.Name.c_str())) - mod.Enabled = m_enabledModsCfg[mod.Name.c_str()].IsTrue(); + if (m_bHasEnabledModsCfg && m_EnabledModsCfg.HasMember(mod.Name.c_str())) + mod.m_bEnabled = m_EnabledModsCfg[mod.Name.c_str()].IsTrue(); else - mod.Enabled = true; + mod.m_bEnabled = true; - if (mod.wasReadSuccessfully) + if (mod.m_bWasReadSuccessfully) { spdlog::info("Loaded mod {} successfully", mod.Name); - if (mod.Enabled) + if (mod.m_bEnabled) spdlog::info("Mod {} is enabled", mod.Name); else spdlog::info("Mod {} is disabled", mod.Name); - m_loadedMods.push_back(mod); + m_LoadedMods.push_back(mod); } else spdlog::warn("Skipping loading mod file {}", (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; }); + std::sort(m_LoadedMods.begin(), m_LoadedMods.end(), [](Mod& a, Mod& b) { return a.LoadPriority < b.LoadPriority; }); - for (Mod& mod : m_loadedMods) + for (Mod& mod : m_LoadedMods) { - if (!mod.Enabled) + if (!mod.m_bEnabled) continue; // register convars @@ -319,15 +364,15 @@ void ModManager::LoadMods() // 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) - if (!g_pCVar->FindVar(convar->Name.c_str())) // make sure convar isn't registered yet, unsure if necessary but idk what - // behaviour is for defining same convar multiple times + if (!R2::g_pCVar->FindVar(convar->Name.c_str())) // make sure convar isn't registered yet, unsure if necessary but idk what + // behaviour is for defining same convar multiple times new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str()); // read vpk paths - if (fs::exists(mod.ModDirectory / "vpk")) + if (fs::exists(mod.m_ModDirectory / "vpk")) { // read vpk cfg - std::ifstream vpkJsonStream(mod.ModDirectory / "vpk/vpk.json"); + std::ifstream vpkJsonStream(mod.m_ModDirectory / "vpk/vpk.json"); std::stringstream vpkJsonStringStream; bool bUseVPKJson = false; @@ -345,7 +390,7 @@ void ModManager::LoadMods() bUseVPKJson = !dVpkJson.HasParseError() && dVpkJson.IsObject(); } - for (fs::directory_entry file : fs::directory_iterator(mod.ModDirectory / "vpk")) + 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 @@ -364,17 +409,17 @@ void ModManager::LoadMods() dVpkJson["Preload"].HasMember(vpkName) && dVpkJson["Preload"][vpkName].IsTrue()); modVpk.m_sVpkPath = vpkName; - if (m_hasLoadedMods && modVpk.m_bAutoLoad) - (*g_Filesystem)->m_vtable->MountVPK(*g_Filesystem, vpkName.c_str()); + if (m_bHasLoadedMods && modVpk.m_bAutoLoad) + (*R2::g_pFilesystem)->m_vtable->MountVPK(*R2::g_pFilesystem, vpkName.c_str()); } } } // read rpak paths - if (fs::exists(mod.ModDirectory / "paks")) + if (fs::exists(mod.m_ModDirectory / "paks")) { // read rpak cfg - std::ifstream rpakJsonStream(mod.ModDirectory / "paks/rpak.json"); + std::ifstream rpakJsonStream(mod.m_ModDirectory / "paks/rpak.json"); std::stringstream rpakJsonStringStream; bool bUseRpakJson = false; @@ -406,7 +451,7 @@ void ModManager::LoadMods() } } - for (fs::directory_entry file : fs::directory_iterator(mod.ModDirectory / "paks")) + 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") @@ -417,39 +462,38 @@ void ModManager::LoadMods() 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; // not using atm because we need to resolve path to rpak // if (m_hasLoadedMods && modPak.m_bAutoLoad) - // g_PakLoadManager->LoadPakAsync(pakName.c_str()); + // g_pPakLoadManager->LoadPakAsync(pakName.c_str()); } } } // read keyvalues paths - if (fs::exists(mod.ModDirectory / "keyvalues")) + if (fs::exists(mod.m_ModDirectory / "keyvalues")) { - for (fs::directory_entry file : fs::recursive_directory_iterator(mod.ModDirectory / "keyvalues")) + for (fs::directory_entry file : fs::recursive_directory_iterator(mod.m_ModDirectory / "keyvalues")) { if (fs::is_regular_file(file)) { - std::string kvStr = file.path().lexically_relative(mod.ModDirectory / "keyvalues").lexically_normal().string(); + 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.ModDirectory / "mod.pdiff")) + if (fs::exists(mod.m_ModDirectory / "mod.pdiff")) { - std::ifstream pdiffStream(mod.ModDirectory / "mod.pdiff"); + std::ifstream pdiffStream(mod.m_ModDirectory / "mod.pdiff"); if (!pdiffStream.fail()) { @@ -464,17 +508,17 @@ void ModManager::LoadMods() } // read bink video paths - if (fs::exists(mod.ModDirectory / "media")) + if (fs::exists(mod.m_ModDirectory / "media")) { - for (fs::directory_entry file : fs::recursive_directory_iterator(mod.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.ModDirectory / "audio")) + if (fs::exists(mod.m_ModDirectory / "audio")) { - for (fs::directory_entry file : fs::directory_iterator(mod.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") { @@ -489,23 +533,23 @@ void ModManager::LoadMods() } // 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--) + for (int64_t i = m_LoadedMods.size() - 1; i > -1; i--) { - if (!m_loadedMods[i].Enabled) + if (!m_LoadedMods[i].m_bEnabled) continue; - if (fs::exists(m_loadedMods[i].ModDirectory / MOD_OVERRIDE_DIR)) + if (fs::exists(m_LoadedMods[i].m_ModDirectory / MOD_OVERRIDE_DIR)) { - for (fs::directory_entry file : fs::recursive_directory_iterator(m_loadedMods[i].ModDirectory / MOD_OVERRIDE_DIR)) + for (fs::directory_entry file : fs::recursive_directory_iterator(m_LoadedMods[i].m_ModDirectory / MOD_OVERRIDE_DIR)) { - fs::path path = file.path().lexically_relative(m_loadedMods[i].ModDirectory / MOD_OVERRIDE_DIR).lexically_normal(); - - if (file.is_regular_file() && m_modFiles.find(path.string()) == m_modFiles.end()) + 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.owningMod = &m_loadedMods[i]; - modFile.path = path; - m_modFiles.insert(std::make_pair(path.string(), modFile)); + modFile.m_pOwningMod = &m_LoadedMods[i]; + modFile.m_Path = path; + m_ModFiles.insert(std::make_pair(path, modFile)); } } } @@ -517,9 +561,9 @@ void ModManager::LoadMods() modinfoDoc.AddMember("Mods", rapidjson_document::GenericValue(rapidjson::kArrayType), modinfoDoc.GetAllocator()); int currentModIndex = 0; - for (Mod& mod : m_loadedMods) + for (Mod& mod : m_LoadedMods) { - if (!mod.Enabled || (!mod.RequiredOnClient && !mod.Pdiff.size())) + if (!mod.m_bEnabled || (!mod.RequiredOnClient && !mod.Pdiff.size())) continue; modinfoDoc["Mods"].PushBack(rapidjson_document::GenericValue(rapidjson::kObjectType), modinfoDoc.GetAllocator()); @@ -535,27 +579,27 @@ void ModManager::LoadMods() buffer.Clear(); rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); modinfoDoc.Accept(writer); - g_MasterServerManager->m_ownModInfoJson = std::string(buffer.GetString()); + g_pMasterServerManager->m_sOwnModInfoJson = std::string(buffer.GetString()); - m_hasLoadedMods = true; + m_bHasLoadedMods = true; } void ModManager::UnloadMods() { // clean up stuff from mods before we unload - m_modFiles.clear(); + m_ModFiles.clear(); fs::remove_all(GetCompiledAssetsPath()); g_CustomAudioManager.ClearAudioOverrides(); - if (!m_hasEnabledModsCfg) - m_enabledModsCfg.SetObject(); + if (!m_bHasEnabledModsCfg) + m_EnabledModsCfg.SetObject(); - for (Mod& mod : m_loadedMods) + 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.ModDirectory)); + fs::remove(GetCompiledAssetsPath() / fs::path(kvPaths.second).lexically_relative(mod.m_ModDirectory)); mod.KeyValues.clear(); @@ -563,27 +607,39 @@ void ModManager::UnloadMods() // 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( + if (!m_EnabledModsCfg.HasMember(mod.Name.c_str())) + m_EnabledModsCfg.AddMember( rapidjson_document::StringRefType(mod.Name.c_str()), rapidjson_document::GenericValue(false), - m_enabledModsCfg.GetAllocator()); + m_EnabledModsCfg.GetAllocator()); - m_enabledModsCfg[mod.Name.c_str()].SetBool(mod.Enabled); + m_EnabledModsCfg[mod.Name.c_str()].SetBool(mod.m_bEnabled); } std::ofstream writeStream(GetNorthstarPrefix() + "/enabledmods.json"); rapidjson::OStreamWrapper writeStreamWrapper(writeStream); rapidjson::Writer<rapidjson::OStreamWrapper> writer(writeStreamWrapper); - m_enabledModsCfg.Accept(writer); + m_EnabledModsCfg.Accept(writer); // do we need to dealloc individual entries in m_loadedMods? idk, rework - m_loadedMods.clear(); + 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(fs::path(filename).lexically_normal().string()); + size_t fileHash = STR_HASH(NormaliseModFilePath(fs::path(filename))); if (fileHash == m_hScriptsRsonHash) BuildScriptsRson(); @@ -592,9 +648,9 @@ void ModManager::CompileAssetsForFile(const char* filename) 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) + for (Mod& mod : m_LoadedMods) { - if (!mod.Enabled) + if (!mod.m_bEnabled) continue; if (mod.KeyValues.find(fileHash) != mod.KeyValues.end()) @@ -606,16 +662,9 @@ void ModManager::CompileAssetsForFile(const char* filename) } } -void ReloadModsCommand(const CCommand& args) +void ConCommand_reload_mods(const CCommand& args) { - g_ModManager->LoadMods(); -} - -void InitialiseModManager(HMODULE baseAddress) -{ - g_ModManager = new ModManager; - - RegisterConCommand("reload_mods", ReloadModsCommand, "idk", FCVAR_NONE); + g_pModManager->LoadMods(); } fs::path GetModFolderPath() @@ -626,3 +675,10 @@ 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/modmanager.h b/NorthstarDLL/modmanager.h index 83cc0e40..f622c675 100644 --- a/NorthstarDLL/modmanager.h +++ b/NorthstarDLL/modmanager.h @@ -1,14 +1,15 @@ #pragma once #include "convar.h" +#include "memalloc.h" +#include "squirrel.h" + +#include "rapidjson/document.h" #include <string> #include <vector> #include <filesystem> -#include "rapidjson/document.h" -#include "memalloc.h" - -namespace fs = std::filesystem; const std::string MOD_FOLDER_SUFFIX = "/mods"; +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"; @@ -24,9 +25,6 @@ struct ModConVar struct ModScriptCallback { public: - // would've liked to make it possible to hook arbitrary codecallbacks, but couldn't find a function that calls some ui ones - // std::string HookedCodeCallback; - ScriptContext Context; // called before the codecallback is executed @@ -39,7 +37,7 @@ struct ModScript { public: std::string Path; - std::string RsonRunOn; + std::string RunOn; std::vector<ModScriptCallback> Callbacks; }; @@ -64,8 +62,10 @@ class Mod { public: // runtime stuff - fs::path ModDirectory; - bool Enabled = true; + bool m_bEnabled = true; + bool m_bWasReadSuccessfully = false; + fs::path m_ModDirectory; + // bool m_bIsRemote; // mod.json stuff: @@ -100,13 +100,8 @@ class 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 - // iterated over to create squirrel VM constants depending if a mod exists or not. - // this only exists because we cannot access g_ModManager whilst mods are being loaded for the first time for some reason. - std::unordered_map<std::string, std::string> DependencyConstants; - - // other stuff - bool wasReadSuccessfully = false; + std::unordered_map<std::string, std::string> DependencyConstants; public: Mod(fs::path modPath, char* jsonBuf); @@ -115,42 +110,40 @@ class Mod struct ModOverrideFile { public: - Mod* owningMod; - fs::path path; + Mod* m_pOwningMod; + fs::path m_Path; }; class ModManager { private: - bool m_hasLoadedMods = false; - bool m_hasEnabledModsCfg; - rapidjson_document m_enabledModsCfg; + bool m_bHasLoadedMods = false; + bool m_bHasEnabledModsCfg; + rapidjson_document m_EnabledModsCfg; // precalculated hashes size_t m_hScriptsRsonHash; size_t m_hPdefHash; public: - std::vector<Mod> m_loadedMods; - std::unordered_map<std::string, ModOverrideFile> m_modFiles; - // iterated over to create squirrel VM constants depending if a mod exists or not. - // here because constants are global anyways. - std::unordered_map<std::string, std::string> DependencyConstants; + std::vector<Mod> m_LoadedMods; + std::unordered_map<std::string, ModOverrideFile> m_ModFiles; + std::unordered_map<std::string, std::string> m_DependencyConstants; 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 Mods/Compiled/ + // compile asset type stuff, these are done in files under runtime/compiled/ void BuildScriptsRson(); void TryBuildKeyValues(const char* filename); void BuildPdef(); }; -void InitialiseModManager(HMODULE baseAddress); fs::path GetModFolderPath(); fs::path GetCompiledAssetsPath(); -extern ModManager* g_ModManager; +extern ModManager* g_pModManager; diff --git a/NorthstarDLL/nsmem.h b/NorthstarDLL/nsmem.h deleted file mode 100644 index 9b1f9103..00000000 --- a/NorthstarDLL/nsmem.h +++ /dev/null @@ -1,193 +0,0 @@ -#pragma once -#include "pch.h" - -// KittenPopo's memory stuff, made for northstar (because I really can't handle working with northstar's original memory stuff tbh) - -#pragma region Pattern Scanning -namespace NSMem -{ - inline std::vector<int> HexBytesToString(const char* str) - { - std::vector<int> patternNums; - int size = strlen(str); - for (int i = 0; i < size; i++) - { - char c = str[i]; - - // If this is a space character, ignore it - if (c == ' ' || c == '\t') - continue; - - if (c == '?') - { - // Add a wildcard (-1) - patternNums.push_back(-1); - } - else if (i < size - 1) - { - BYTE result = 0; - for (int j = 0; j < 2; j++) - { - int val = 0; - char c = *(str + 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(false); - val = -1; - } - - result += (j == 0) ? val * 16 : val; - } - patternNums.push_back(result); - } - - i++; - } - - return patternNums; - } - - inline void* PatternScan(void* module, const int* pattern, int patternSize, int offset) - { - if (!module) - return NULL; - - auto dosHeader = (PIMAGE_DOS_HEADER)module; - auto ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)module + dosHeader->e_lfanew); - - auto sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage; - - auto scanBytes = (BYTE*)module; - - for (auto i = 0; i < sizeOfImage - patternSize; ++i) - { - bool found = true; - for (auto j = 0; j < patternSize; ++j) - { - if (scanBytes[i + j] != pattern[j] && pattern[j] != -1) - { - found = false; - break; - } - } - - if (found) - { - uintptr_t addressInt = (uintptr_t)(&scanBytes[i]) + offset; - return (uint8_t*)addressInt; - } - } - - return nullptr; - } - - inline void* PatternScan(const char* moduleName, const char* pattern, int offset = 0) - { - std::vector<int> patternNums = HexBytesToString(pattern); - - return PatternScan(GetModuleHandleA(moduleName), &patternNums[0], patternNums.size(), offset); - } - - inline void BytePatch(uintptr_t address, const BYTE* vals, int size) - { - WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, vals, size, NULL); - } - - inline void BytePatch(uintptr_t address, std::initializer_list<BYTE> vals) - { - std::vector<BYTE> bytes = vals; - if (!bytes.empty()) - BytePatch(address, &bytes[0], bytes.size()); - } - - inline void BytePatch(uintptr_t address, const char* bytesStr) - { - std::vector<int> byteInts = HexBytesToString(bytesStr); - std::vector<BYTE> bytes; - for (int v : byteInts) - bytes.push_back(v); - - if (!bytes.empty()) - BytePatch(address, &bytes[0], bytes.size()); - } - - inline void NOP(uintptr_t address, int size) - { - BYTE* buf = (BYTE*)malloc(size); - memset(buf, 0x90, size); - BytePatch(address, buf, size); - free(buf); - } - - inline bool IsMemoryReadable(void* ptr, size_t size) - { - static SYSTEM_INFO sysInfo; - if (!sysInfo.dwPageSize) - GetSystemInfo(&sysInfo); // This should always be 4096 unless ur playing on NES or some shit but whatever - - MEMORY_BASIC_INFORMATION memInfo; - - if (!VirtualQuery(ptr, &memInfo, sizeof(memInfo))) - return false; - - if (memInfo.RegionSize < size) - return false; - - return (memInfo.State & MEM_COMMIT) && !(memInfo.Protect & PAGE_NOACCESS); - } -} // namespace NSMem - -#pragma region KHOOK -struct KHookPatternInfo -{ - const char *moduleName, *pattern; - int offset = 0; - - KHookPatternInfo(const char* moduleName, const char* pattern, int offset = 0) : moduleName(moduleName), pattern(pattern), offset(offset) - { - } -}; - -struct KHook -{ - KHookPatternInfo targetFunc; - void* targetFuncAddr; - void* hookFunc; - void** original; - - static inline std::vector<KHook*> _allHooks; - - KHook(KHookPatternInfo targetFunc, void* hookFunc, void** original) : targetFunc(targetFunc) - { - this->hookFunc = hookFunc; - this->original = original; - _allHooks.push_back(this); - } - - bool Setup() - { - targetFuncAddr = NSMem::PatternScan(targetFunc.moduleName, targetFunc.pattern, targetFunc.offset); - if (!targetFuncAddr) - return false; - - return (MH_CreateHook(targetFuncAddr, hookFunc, original) == MH_OK) && (MH_EnableHook(targetFuncAddr) == MH_OK); - } -}; -#define KHOOK(name, funcPatternInfo, returnType, convention, args) \ - returnType convention hk##name args; \ - auto o##name = (returnType(convention*) args)0; \ - KHook k##name = KHook(KHookPatternInfo funcPatternInfo, reinterpret_cast<void*>(&hk##name), (void**)&o##name); \ - returnType convention hk##name args -#pragma endregion diff --git a/NorthstarDLL/nsprefix.cpp b/NorthstarDLL/nsprefix.cpp index 4c165e05..64a9d1f1 100644 --- a/NorthstarDLL/nsprefix.cpp +++ b/NorthstarDLL/nsprefix.cpp @@ -1,13 +1,13 @@ -#include <string> #include "pch.h" #include "nsprefix.h" +#include <string> std::string GetNorthstarPrefix() { return NORTHSTAR_FOLDER_PREFIX; } -void parseConfigurables() +void InitialiseNorthstarPrefix() { char* clachar = strstr(GetCommandLineA(), "-profile="); if (clachar) diff --git a/NorthstarDLL/nsprefix.h b/NorthstarDLL/nsprefix.h index cc98e15e..9f3087fb 100644 --- a/NorthstarDLL/nsprefix.h +++ b/NorthstarDLL/nsprefix.h @@ -3,5 +3,5 @@ static std::string NORTHSTAR_FOLDER_PREFIX; +void InitialiseNorthstarPrefix(); std::string GetNorthstarPrefix(); -void parseConfigurables(); diff --git a/NorthstarDLL/pch.h b/NorthstarDLL/pch.h index de22b40f..e23d6a9b 100644 --- a/NorthstarDLL/pch.h +++ b/NorthstarDLL/pch.h @@ -8,8 +8,6 @@ #define _WINSOCK_DEPRECATED_NO_WARNINGS // temp because i'm very lazy and want to use inet_addr, remove later #define RAPIDJSON_HAS_STDSTRING 1 -// httplib ssl - // add headers that you want to pre-compile here #include "memalloc.h" @@ -20,11 +18,14 @@ #include <filesystem> #include <sstream> +namespace fs = std::filesystem; + #include "logging.h" -#include "include/MinHook.h" +#include "MinHook.h" #include "spdlog/spdlog.h" #include "libcurl/include/curl/curl.h" -#include "hookutils.h" +#include "hooks.h" +#include "memory.h" template <typename ReturnType, typename... Args> ReturnType CallVFunc(int index, void* thisPtr, Args... args) { diff --git a/NorthstarDLL/pdef.cpp b/NorthstarDLL/pdef.cpp index bc6ec64d..05fba710 100644 --- a/NorthstarDLL/pdef.cpp +++ b/NorthstarDLL/pdef.cpp @@ -1,8 +1,8 @@ #include "pch.h" #include "modmanager.h" #include "filesystem.h" -#include "hookutils.h" #include "pdef.h" + #include <map> #include <sstream> #include <fstream> @@ -14,11 +14,11 @@ void ModManager::BuildPdef() fs::path MOD_PDEF_PATH = fs::path(GetCompiledAssetsPath() / MOD_PDEF_SUFFIX); fs::remove(MOD_PDEF_PATH); - std::string pdef = ReadVPKOriginalFile(VPK_PDEF_PATH); + std::string pdef = R2::ReadVPKOriginalFile(VPK_PDEF_PATH); - for (Mod& mod : m_loadedMods) + for (Mod& mod : m_LoadedMods) { - if (!mod.Enabled || !mod.Pdiff.size()) + if (!mod.m_bEnabled || !mod.Pdiff.size()) continue; // this code probably isn't going to be pretty lol @@ -107,11 +107,11 @@ void ModManager::BuildPdef() writeStream.close(); ModOverrideFile overrideFile; - overrideFile.owningMod = nullptr; - overrideFile.path = VPK_PDEF_PATH; + 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)); + 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; + m_ModFiles[VPK_PDEF_PATH] = overrideFile; } diff --git a/NorthstarDLL/playlist.cpp b/NorthstarDLL/playlist.cpp index e55710c3..7cf9e41e 100644 --- a/NorthstarDLL/playlist.cpp +++ b/NorthstarDLL/playlist.cpp @@ -2,105 +2,118 @@ #include "playlist.h" #include "concommand.h" #include "convar.h" -#include "gameutils.h" -#include "hookutils.h" #include "squirrel.h" +#include "hoststate.h" +#include "serverpresence.h" -typedef char (*Onclc_SetPlaylistVarOverrideType)(void* a1, void* a2); -Onclc_SetPlaylistVarOverrideType Onclc_SetPlaylistVarOverride; +AUTOHOOK_INIT() -typedef int (*GetCurrentGamemodeMaxPlayersType)(); -GetCurrentGamemodeMaxPlayersType GetCurrentGamemodeMaxPlayers; - -// function type defined in gameutils.h -SetPlaylistVarOverrideType SetPlaylistVarOverrideOriginal; -GetCurrentPlaylistVarType GetCurrentPlaylistVarOriginal; +// use the R2 namespace for game funcs +namespace R2 +{ + const char* (*GetCurrentPlaylistName)(); + void (*SetCurrentPlaylist)(const char* pPlaylistName); + void (*SetPlaylistVarOverride)(const char* pVarName, const char* pValue); + const char* (*GetCurrentPlaylistVar)(const char* pVarName, bool bUseOverrides); +} // namespace R2 ConVar* Cvar_ns_use_clc_SetPlaylistVarOverride; -void SetPlaylistCommand(const CCommand& args) +AUTOHOOK(clc_SetPlaylistVarOverride__Process, engine.dll + 0x222180, +char,, (void* a1, void* a2)) { - if (args.ArgC() < 2) - return; + // 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(R2::g_pHostState->m_levelName, "mp_lobby")) + return 1; - SetCurrentPlaylist(args.Arg(1)); + return clc_SetPlaylistVarOverride__Process(a1, a2); } -void SetPlaylistVarOverrideCommand(const CCommand& args) +AUTOHOOK(SetCurrentPlaylist, engine.dll + 0x18EB20, +bool, __fastcall, (const char* pPlaylistName)) { - if (args.ArgC() < 3) + bool bSuccess = SetCurrentPlaylist(pPlaylistName); + + if (bSuccess) + { + spdlog::info("Set playlist to {}", R2::GetCurrentPlaylistName()); + g_pServerPresence->SetPlaylist(R2::GetCurrentPlaylistName()); + } + + return bSuccess; +} + +AUTOHOOK(SetPlaylistVarOverride, engine.dll + 0x18ED00, +void,, (const char* pVarName, const char* pValue)) +{ + if (strlen(pValue) >= 64) return; - for (int i = 1; i < args.ArgC(); i += 2) - SetPlaylistVarOverride(args.Arg(i), args.Arg(i + 1)); + SetPlaylistVarOverride(pVarName, pValue); } -char Onclc_SetPlaylistVarOverrideHook(void* a1, void* a2) +AUTOHOOK(GetCurrentPlaylistVar, engine.dll + 0x18C680, +const char*,, (const char* pVarName, bool bUseOverrides)) { - // the private_match playlist is the only situation where there should be any legitimate sending of this netmessage - // todo: check mp_lobby here too - if (!Cvar_ns_use_clc_SetPlaylistVarOverride->GetBool() || strcmp(GetCurrentPlaylistName(), "private_match")) - return 1; + if (!bUseOverrides && !strcmp(pVarName, "max_players")) + bUseOverrides = true; - return Onclc_SetPlaylistVarOverride(a1, a2); + return GetCurrentPlaylistVar(pVarName, bUseOverrides); } -void SetPlaylistVarOverrideHook(const char* varName, const char* value) +AUTOHOOK(GetCurrentGamemodeMaxPlayers, engine.dll + 0x18C430, +int,, ()) { - if (strlen(value) >= 64) - return; + const char* pMaxPlayers = R2::GetCurrentPlaylistVar("max_players", 0); + if (!pMaxPlayers) + return GetCurrentGamemodeMaxPlayers(); - SetPlaylistVarOverrideOriginal(varName, value); + int iMaxPlayers = atoi(pMaxPlayers); + return iMaxPlayers; } -char* GetCurrentPlaylistVarHook(const char* varName, bool useOverrides) +void ConCommand_playlist(const CCommand& args) { - if (!useOverrides && !strcmp(varName, "max_players")) - useOverrides = true; + if (args.ArgC() < 2) + return; - return GetCurrentPlaylistVarOriginal(varName, useOverrides); + R2::SetCurrentPlaylist(args.Arg(1)); } -int GetCurrentGamemodeMaxPlayersHook() +void ConCommand_setplaylistvaroverride(const CCommand& args) { - char* maxPlayersStr = GetCurrentPlaylistVar("max_players", 0); - if (!maxPlayersStr) - return GetCurrentGamemodeMaxPlayers(); + if (args.ArgC() < 3) + return; - int maxPlayers = atoi(maxPlayersStr); - return maxPlayers; + for (int i = 1; i < args.ArgC(); i += 2) + R2::SetPlaylistVarOverride(args.Arg(i), args.Arg(i + 1)); } -#include "nsmem.h" -void InitialisePlaylistHooks(HMODULE baseAddress) +ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, (ConCommand, ConVar), (CModule module)) { - RegisterConCommand("setplaylist", SetPlaylistCommand, "Sets the current playlist", FCVAR_NONE); - RegisterConCommand("setplaylistvaroverrides", SetPlaylistVarOverrideCommand, "sets a playlist var override", FCVAR_NONE); + AUTOHOOK_DISPATCH() + + R2::GetCurrentPlaylistName = module.Offset(0x18C640).As<const char* (*)()>(); + R2::SetCurrentPlaylist = module.Offset(0x18EB20).As<void (*)(const char*)>(); + R2::SetPlaylistVarOverride = module.Offset(0x18ED00).As<void (*)(const char*, const char*)>(); + R2::GetCurrentPlaylistVar = module.Offset(0x18C680).As<const char* (*)(const char*, bool)>(); + + // 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 + // 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"); - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x222180, &Onclc_SetPlaylistVarOverrideHook, reinterpret_cast<LPVOID*>(&Onclc_SetPlaylistVarOverride)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x18ED00, &SetPlaylistVarOverrideHook, reinterpret_cast<LPVOID*>(&SetPlaylistVarOverrideOriginal)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x18C680, &GetCurrentPlaylistVarHook, reinterpret_cast<LPVOID*>(&GetCurrentPlaylistVarOriginal)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x18C430, &GetCurrentGamemodeMaxPlayersHook, reinterpret_cast<LPVOID*>(&GetCurrentGamemodeMaxPlayers)); - - uintptr_t ba = (uintptr_t)baseAddress; - // 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 - { - NSMem::BytePatch(ba + 0x18ED8D, "C3"); - } + module.Offset(0x18ED8D).Patch("C3"); // patch to allow setplaylistvaroverride to be called before map init on dedicated and private match launched through the game - NSMem::NOP(ba + 0x18ED17, 6); + module.Offset(0x18ED17).NOP(6); } diff --git a/NorthstarDLL/playlist.h b/NorthstarDLL/playlist.h index da7de02d..c77b37d9 100644 --- a/NorthstarDLL/playlist.h +++ b/NorthstarDLL/playlist.h @@ -1,2 +1,10 @@ #pragma once -void InitialisePlaylistHooks(HMODULE baseAddress); + +// use the R2 namespace for game funcs +namespace R2 +{ + extern const char* (*GetCurrentPlaylistName)(); + extern void (*SetCurrentPlaylist)(const char* pPlaylistName); + extern void (*SetPlaylistVarOverride)(const char* pVarName, const char* pValue); + extern const char* (*GetCurrentPlaylistVar)(const char* pVarName, bool bUseOverrides); +} // namespace R2 diff --git a/NorthstarDLL/plugins.cpp b/NorthstarDLL/plugins.cpp index 70535a32..790439d1 100644 --- a/NorthstarDLL/plugins.cpp +++ b/NorthstarDLL/plugins.cpp @@ -1,9 +1,11 @@ #include "pch.h" #include "squirrel.h" #include "plugins.h" -#include <chrono> #include "masterserver.h" #include "convar.h" +#include "serverpresence.h" + +#include <chrono> #include <windows.h> /// <summary> @@ -109,31 +111,31 @@ void initGameState() } // string gamemode, string gamemodeName, string map, string mapName, bool connected, bool loading -SQRESULT SQ_UpdateGameStateUI(void* sqvm) +SQRESULT SQ_UpdateGameStateUI(HSquirrelVM* sqvm) { AcquireSRWLockExclusive(&gameStateLock); - gameState.map = ClientSq_getstring(sqvm, 1); - gameState.mapDisplayName = ClientSq_getstring(sqvm, 2); - gameState.playlist = ClientSq_getstring(sqvm, 3); - gameState.playlistDisplayName = ClientSq_getstring(sqvm, 4); - gameState.connected = ClientSq_getbool(sqvm, 5); - gameState.loading = ClientSq_getbool(sqvm, 6); + gameState.map = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); + gameState.mapDisplayName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 2); + gameState.playlist = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 3); + gameState.playlistDisplayName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 4); + gameState.connected = g_pSquirrel<ScriptContext::UI>->getbool(sqvm, 5); + gameState.loading = g_pSquirrel<ScriptContext::UI>->getbool(sqvm, 6); ReleaseSRWLockExclusive(&gameStateLock); return SQRESULT_NOTNULL; } // int playerCount, int outScore, int secondHighestScore, int highestScore, bool roundBased, int scoreLimit -SQRESULT SQ_UpdateGameStateClient(void* sqvm) +SQRESULT SQ_UpdateGameStateClient(HSquirrelVM* sqvm) { AcquireSRWLockExclusive(&gameStateLock); AcquireSRWLockExclusive(&serverInfoLock); - gameState.players = ClientSq_getinteger(sqvm, 1); - serverInfo.maxPlayers = ClientSq_getinteger(sqvm, 2); - gameState.ourScore = ClientSq_getinteger(sqvm, 3); - gameState.secondHighestScore = ClientSq_getinteger(sqvm, 4); - gameState.highestScore = ClientSq_getinteger(sqvm, 5); - serverInfo.roundBased = ClientSq_getbool(sqvm, 6); - serverInfo.scoreLimit = ClientSq_getbool(sqvm, 7); + gameState.players = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 1); + serverInfo.maxPlayers = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 2); + gameState.ourScore = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 3); + gameState.secondHighestScore = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 4); + gameState.highestScore = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 5); + serverInfo.roundBased = g_pSquirrel<ScriptContext::CLIENT>->getbool(sqvm, 6); + serverInfo.scoreLimit = g_pSquirrel<ScriptContext::CLIENT>->getbool(sqvm, 7); ReleaseSRWLockExclusive(&gameStateLock); ReleaseSRWLockExclusive(&serverInfoLock); return SQRESULT_NOTNULL; @@ -141,59 +143,59 @@ SQRESULT SQ_UpdateGameStateClient(void* sqvm) // string id, string name, string password, int players, int maxPlayers, string map, string mapDisplayName, string playlist, string // playlistDisplayName -SQRESULT SQ_UpdateServerInfo(void* sqvm) +SQRESULT SQ_UpdateServerInfo(HSquirrelVM* sqvm) { AcquireSRWLockExclusive(&gameStateLock); AcquireSRWLockExclusive(&serverInfoLock); - serverInfo.id = ClientSq_getstring(sqvm, 1); - serverInfo.name = ClientSq_getstring(sqvm, 2); - serverInfo.password = ClientSq_getstring(sqvm, 3); - gameState.players = ClientSq_getinteger(sqvm, 4); - serverInfo.maxPlayers = ClientSq_getinteger(sqvm, 5); - gameState.map = ClientSq_getstring(sqvm, 6); - gameState.mapDisplayName = ClientSq_getstring(sqvm, 7); - gameState.playlist = ClientSq_getstring(sqvm, 8); - gameState.playlistDisplayName = ClientSq_getstring(sqvm, 9); + serverInfo.id = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); + serverInfo.name = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 2); + serverInfo.password = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 3); + gameState.players = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 4); + serverInfo.maxPlayers = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 5); + gameState.map = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 6); + gameState.mapDisplayName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 7); + gameState.playlist = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 8); + gameState.playlistDisplayName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 9); ReleaseSRWLockExclusive(&gameStateLock); ReleaseSRWLockExclusive(&serverInfoLock); return SQRESULT_NOTNULL; } // int maxPlayers -SQRESULT SQ_UpdateServerInfoBetweenRounds(void* sqvm) +SQRESULT SQ_UpdateServerInfoBetweenRounds(HSquirrelVM* sqvm) { AcquireSRWLockExclusive(&serverInfoLock); - serverInfo.id = ClientSq_getstring(sqvm, 1); - serverInfo.name = ClientSq_getstring(sqvm, 2); - serverInfo.password = ClientSq_getstring(sqvm, 3); - serverInfo.maxPlayers = ClientSq_getinteger(sqvm, 4); + serverInfo.id = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 1); + serverInfo.name = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 2); + serverInfo.password = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 3); + serverInfo.maxPlayers = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 4); ReleaseSRWLockExclusive(&serverInfoLock); return SQRESULT_NOTNULL; } // float timeInFuture -SQRESULT SQ_UpdateTimeInfo(void* sqvm) +SQRESULT SQ_UpdateTimeInfo(HSquirrelVM* sqvm) { AcquireSRWLockExclusive(&serverInfoLock); - serverInfo.endTime = ceil(ClientSq_getfloat(sqvm, 1)); + serverInfo.endTime = ceil(g_pSquirrel<ScriptContext::CLIENT>->getfloat(sqvm, 1)); ReleaseSRWLockExclusive(&serverInfoLock); return SQRESULT_NOTNULL; } // bool loading -SQRESULT SQ_SetConnected(void* sqvm) +SQRESULT SQ_SetConnected(HSquirrelVM* sqvm) { AcquireSRWLockExclusive(&gameStateLock); - gameState.loading = ClientSq_getbool(sqvm, 1); + gameState.loading = g_pSquirrel<ScriptContext::UI>->getbool(sqvm, 1); ReleaseSRWLockExclusive(&gameStateLock); return SQRESULT_NOTNULL; } -SQRESULT SQ_UpdateListenServer(void* sqvm) +SQRESULT SQ_UpdateListenServer(HSquirrelVM* sqvm) { AcquireSRWLockExclusive(&serverInfoLock); - serverInfo.id = g_MasterServerManager->m_ownServerId; - serverInfo.password = Cvar_ns_server_password->GetString(); + serverInfo.id = g_pMasterServerManager->m_sOwnServerId; + serverInfo.password = ""; // g_pServerPresence->Cvar_ns_server_password->GetString(); todo this fr ReleaseSRWLockExclusive(&serverInfoLock); return SQRESULT_NOTNULL; } @@ -384,26 +386,26 @@ int getPlayerInfoBool(bool* out_ptr, PlayerInfoType var) return n; } -void InitialisePluginCommands(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", PluginCommands, ClientSquirrel, (CModule module)) { // i swear there's a way to make this not have be run in 2 contexts but i can't figure it out // some funcs i need are just not available in UI or CLIENT - if (g_UISquirrelManager && g_ClientSquirrelManager) + if (g_pSquirrel<ScriptContext::UI> && g_pSquirrel<ScriptContext::CLIENT>) { - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "void", "NSUpdateGameStateUI", "string gamemode, string gamemodeName, string map, string mapName, bool connected, bool loading", "", SQ_UpdateGameStateUI); - g_ClientSquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( "void", "NSUpdateGameStateClient", "int playerCount, int maxPlayers, int outScore, int secondHighestScore, int highestScore, bool roundBased, int scoreLimit", "", SQ_UpdateGameStateClient); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "void", "NSUpdateServerInfo", "string id, string name, string password, int players, int maxPlayers, string map, string mapDisplayName, string playlist, " @@ -411,10 +413,10 @@ void InitialisePluginCommands(HMODULE baseAddress) "playlistDisplayName", "", SQ_UpdateServerInfo); - g_ClientSquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( "void", "NSUpdateServerInfoReload", "int maxPlayers", "", SQ_UpdateServerInfoBetweenRounds); - g_ClientSquirrelManager->AddFuncRegistration("void", "NSUpdateTimeInfo", "float timeInFuture", "", SQ_UpdateTimeInfo); - g_UISquirrelManager->AddFuncRegistration("void", "NSSetLoading", "bool loading", "", SQ_SetConnected); - g_UISquirrelManager->AddFuncRegistration("void", "NSUpdateListenServer", "", "", SQ_UpdateListenServer); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration("void", "NSUpdateTimeInfo", "float timeInFuture", "", SQ_UpdateTimeInfo); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSSetLoading", "bool loading", "", SQ_SetConnected); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSUpdateListenServer", "", "", SQ_UpdateListenServer); } } diff --git a/NorthstarDLL/plugins.h b/NorthstarDLL/plugins.h index 84f3073f..953801a2 100644 --- a/NorthstarDLL/plugins.h +++ b/NorthstarDLL/plugins.h @@ -15,5 +15,3 @@ int getPlayerInfoBool(bool* out_ptr, PlayerInfoType var); void initGameState(); void* getPluginObject(PluginObject var); - -void InitialisePluginCommands(HMODULE baseAddress); diff --git a/NorthstarDLL/printcommand.h b/NorthstarDLL/printcommand.h new file mode 100644 index 00000000..6c3ef850 --- /dev/null +++ b/NorthstarDLL/printcommand.h @@ -0,0 +1,6 @@ +#pragma once +#include "concommand.h" + +void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name); +void TryPrintCvarHelpForCommand(const char* pCommand); +void InitialiseCommandPrint(); diff --git a/NorthstarDLL/printcommands.cpp b/NorthstarDLL/printcommands.cpp new file mode 100644 index 00000000..90af1575 --- /dev/null +++ b/NorthstarDLL/printcommands.cpp @@ -0,0 +1,174 @@ +#include "pch.h" +#include "printcommand.h" +#include "convar.h" +#include "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 = R2::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 = R2::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(R2::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]; + + for (auto& map : R2::g_pCVar->DumpToMap()) + { + 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; + } + } + + // print cvars + for (auto& map : R2::g_pCVar->DumpToMap()) + { + if (map.second->m_nFlags & resolvedFlag) + PrintCommandHelpDialogue(map.second, map.second->m_pszName); + } + + delete[] upperFlag; +} + +void InitialiseCommandPrint() +{ + RegisterConCommand("find", ConCommand_find, "Find concommands with the specified string in their name/help text.", FCVAR_NONE); + RegisterConCommand("findflags", ConCommand_findflags, "Find concommands by flags.", FCVAR_NONE); + + // help is already a command, so we need to modify the preexisting command to use our func instead + // and clear the flags also + ConCommand* helpCommand = R2::g_pCVar->FindCommand("help"); + helpCommand->m_nFlags = FCVAR_NONE; + helpCommand->m_pCommandCallback = ConCommand_help; +} diff --git a/NorthstarDLL/printmaps.cpp b/NorthstarDLL/printmaps.cpp new file mode 100644 index 00000000..82ba8d5e --- /dev/null +++ b/NorthstarDLL/printmaps.cpp @@ -0,0 +1,169 @@ +#include "pch.h" +#include "printmaps.h" +#include "convar.h" +#include "concommand.h" +#include "modmanager.h" +#include "tier0.h" +#include "r2engine.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; + +void RefreshMapList() +{ + 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-Z_]+)\\.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 + for (fs::directory_entry file : fs::directory_iterator(fmt::format("{}/maps", R2::g_pModName))) + { + if (file.path().extension() == ".bsp") + { + MapVPKInfo& map = vMapList.emplace_back(); + map.name = file.path().stem().string(); + map.parent = "R2"; + map.source = MapSource_t::GAMEDIR; + } + } +} + +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])) +{ + // don't update our map list often from this func, only refresh every 10 seconds so we avoid constantly reading fs + static double flLastAutocompleteRefresh = -999; + + if (flLastAutocompleteRefresh + 10.0 < Tier0::Plat_FloatTime()) + { + RefreshMapList(); + flLastAutocompleteRefresh = Tier0::Plat_FloatTime(); + } + + // 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; +} + +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); +} + +void InitialiseMapsPrint() +{ + AUTOHOOK_DISPATCH() + + ConCommand* mapsCommand = R2::g_pCVar->FindCommand("maps"); + mapsCommand->m_pCommandCallback = ConCommand_maps; +} diff --git a/NorthstarDLL/printmaps.h b/NorthstarDLL/printmaps.h new file mode 100644 index 00000000..b01761c0 --- /dev/null +++ b/NorthstarDLL/printmaps.h @@ -0,0 +1,2 @@ +#pragma once +void InitialiseMapsPrint(); diff --git a/NorthstarDLL/r2client.cpp b/NorthstarDLL/r2client.cpp new file mode 100644 index 00000000..2e7bd564 --- /dev/null +++ b/NorthstarDLL/r2client.cpp @@ -0,0 +1,20 @@ +#include "pch.h" +#include "r2client.h" + +using namespace R2; + +// use the R2 namespace for game funcs +namespace R2 +{ + char* g_pLocalPlayerUserID; + char* g_pLocalPlayerOriginToken; + GetBaseLocalClientType GetBaseLocalClient; +} // namespace R2 + +ON_DLL_LOAD("engine.dll", R2EngineClient, (CModule module)) +{ + g_pLocalPlayerUserID = module.Offset(0x13F8E688).As<char*>(); + g_pLocalPlayerOriginToken = module.Offset(0x13979C80).As<char*>(); + + GetBaseLocalClient = module.Offset(0x78200).As<GetBaseLocalClientType>(); +} diff --git a/NorthstarDLL/r2client.h b/NorthstarDLL/r2client.h new file mode 100644 index 00000000..64ed6c61 --- /dev/null +++ b/NorthstarDLL/r2client.h @@ -0,0 +1,11 @@ +#pragma once + +// use the R2 namespace for game funcs +namespace R2 +{ + extern char* g_pLocalPlayerUserID; + extern char* g_pLocalPlayerOriginToken; + + typedef void* (*GetBaseLocalClientType)(); + extern GetBaseLocalClientType GetBaseLocalClient; +} // namespace R2 diff --git a/NorthstarDLL/r2engine.cpp b/NorthstarDLL/r2engine.cpp new file mode 100644 index 00000000..4807a174 --- /dev/null +++ b/NorthstarDLL/r2engine.cpp @@ -0,0 +1,35 @@ +#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*>(); // new + + 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/r2engine.h b/NorthstarDLL/r2engine.h new file mode 100644 index 00000000..e5755c54 --- /dev/null +++ b/NorthstarDLL/r2engine.h @@ -0,0 +1,199 @@ +#pragma once + +// 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) + + const int PERSISTENCE_MAX_SIZE = 0xD000; + + // 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 + }; + + #pragma pack(push, 1) + struct CBaseClient // 0x2D728 bytes + { + char pad0[0x16]; + + // +0x16 + char m_Name[64]; + // +0x56 + + char pad1[0x44A]; + + // +0x4A0 + ePersistenceReady m_iPersistenceReady; + // +0x4A1 + + char pad2[0x59]; + + // +0x4FA + char m_PersistenceBuffer[PERSISTENCE_MAX_SIZE]; + + char pad3[0x2006]; + + // +0xF500 + char m_UID[32]; + // +0xF520 + + char pad4[0x1E208]; + }; + #pragma pack(pop) + + 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/r2server.cpp b/NorthstarDLL/r2server.cpp new file mode 100644 index 00000000..50cfa239 --- /dev/null +++ b/NorthstarDLL/r2server.cpp @@ -0,0 +1,17 @@ +#include "pch.h" +#include "r2server.h" + +using namespace R2; + +// use the R2 namespace for game funcs +namespace R2 +{ + CBaseEntity* (*Server_GetEntityByIndex)(int index); + CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex); +} // namespace R2 + +ON_DLL_LOAD("server.dll", R2GameServer, (CModule module)) +{ + Server_GetEntityByIndex = module.Offset(0xFB820).As<CBaseEntity* (*)(int)>(); + UTIL_PlayerByIndex = module.Offset(0x26AA10).As<CBasePlayer*(__fastcall*)(int)>(); +} diff --git a/NorthstarDLL/r2server.h b/NorthstarDLL/r2server.h new file mode 100644 index 00000000..fdfae156 --- /dev/null +++ b/NorthstarDLL/r2server.h @@ -0,0 +1,12 @@ +#pragma once + +// use the R2 namespace for game funcs +namespace R2 +{ + // server entity stuff + class CBaseEntity; + extern CBaseEntity* (*Server_GetEntityByIndex)(int index); + + class CBasePlayer; + extern CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex); +} // namespace R2 diff --git a/NorthstarDLL/rpakfilesystem.cpp b/NorthstarDLL/rpakfilesystem.cpp index 964042b5..d5cff26c 100644 --- a/NorthstarDLL/rpakfilesystem.cpp +++ b/NorthstarDLL/rpakfilesystem.cpp @@ -1,86 +1,97 @@ #include "pch.h" #include "rpakfilesystem.h" -#include "hookutils.h" #include "modmanager.h" #include "dedicated.h" +#include "tier0.h" -typedef void* (*LoadCommonPaksForMapType)(char* map); -LoadCommonPaksForMapType LoadCommonPaksForMap; - -typedef void* (*LoadPakSyncType)(const char* path, void* unknownSingleton, int flags); -typedef int (*LoadPakAsyncType)(const char* path, void* unknownSingleton, int flags, void* callback0, void* callback1); -typedef void* (*UnloadPakType)(int pakHandle, void* callback); -typedef void* (*ReadFullFileFromDiskType)(const char* requestedPath, void* a2); +AUTOHOOK_INIT() // there are more i'm just too lazy to add struct PakLoadFuncs { - void* unk0[2]; - LoadPakSyncType LoadPakSync; - LoadPakAsyncType LoadPakAsync; + void* unk0[3]; + int (*LoadPakAsync)(const char* pPath, void* unknownSingleton, int flags, void* callback0, void* callback1); void* unk1[2]; - UnloadPakType UnloadPak; - void* unk2[17]; - ReadFullFileFromDiskType ReadFullFileFromDisk; + 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; -PakLoadManager* g_PakLoadManager; -void PakLoadManager::LoadPakSync(const char* path) +int PakLoadManager::LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource) { - g_pakLoadApi->LoadPakSync(path, *pUnknownPakLoadSingleton, 0); + int nHandle = g_pakLoadApi->LoadPakAsync(pPath, *pUnknownPakLoadSingleton, 2, nullptr, nullptr); + + // set the load source of the pak we just loaded + GetPakInfo(nHandle)->m_nLoadSource = nLoadSource; + return nHandle; } -void PakLoadManager::LoadPakAsync(const char* path, bool bMarkForUnload) + +void PakLoadManager::UnloadPak(const int nPakHandle) { - int handle = g_pakLoadApi->LoadPakAsync(path, *pUnknownPakLoadSingleton, 2, nullptr, nullptr); + g_pakLoadApi->UnloadPak(nPakHandle, nullptr); +} - if (bMarkForUnload) - m_pakHandlesToUnload.push_back(handle); +void PakLoadManager::UnloadMapPaks() +{ + for (auto& pair : m_vLoadedPaks) + if (pair.second.m_nLoadSource == ePakLoadSource::MAP) + UnloadPak(pair.first); } -void PakLoadManager::UnloadPaks() +LoadedPak* PakLoadManager::TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash) { - for (int pakHandle : m_pakHandlesToUnload) - { - g_pakLoadApi->UnloadPak(pakHandle, nullptr); - // remove pak from loadedPaks and loadedPaksInv - RemoveLoadedPak(pakHandle); - } + 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); +} - m_pakHandlesToUnload.clear(); +void PakLoadManager::RemoveLoadedPak(int nPakHandle) +{ + m_vLoadedPaks.erase(nPakHandle); } -bool PakLoadManager::IsPakLoaded(int32_t pakHandle) +LoadedPak* PakLoadManager::GetPakInfo(const int nPakHandle) { - return loadedPaks.find(pakHandle) != loadedPaks.end(); + return &m_vLoadedPaks.at(nPakHandle); } -bool PakLoadManager::IsPakLoaded(size_t hash) +int PakLoadManager::GetPakHandle(const size_t nPakNameHash) { - return loadedPaksInv.find(hash) != loadedPaksInv.end(); + for (auto& pair : m_vLoadedPaks) + if (pair.second.m_nPakNameHash == nPakNameHash) + return pair.first; + + return -1; } -void PakLoadManager::AddLoadedPak(int32_t pakHandle, size_t hash) +int PakLoadManager::GetPakHandle(const char* pPath) { - loadedPaks[pakHandle] = hash; - loadedPaksInv[hash] = pakHandle; + return GetPakHandle(STR_HASH(pPath)); } -void PakLoadManager::RemoveLoadedPak(int32_t pakHandle) +void* PakLoadManager::LoadFile(const char* path) { - loadedPaksInv.erase(loadedPaks[pakHandle]); - loadedPaks.erase(pakHandle); + 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_ModManager->m_loadedMods.size() - 1; i > -1; i--) + for (int64_t i = g_pModManager->m_LoadedMods.size() - 1; i > -1; i--) { - Mod* mod = &g_ModManager->m_loadedMods[i]; - if (!mod->Enabled) + Mod* mod = &g_pModManager->m_LoadedMods[i]; + if (!mod->m_bEnabled) continue; if (mod->RpakAliases.find(*map) != mod->RpakAliases.end()) @@ -94,50 +105,50 @@ void HandlePakAliases(char** map) void LoadPreloadPaks() { // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { - if (!mod.Enabled) + if (!mod.m_bEnabled) continue; // need to get a relative path of mod to mod folder - fs::path modPakPath("./" / mod.ModDirectory / "paks"); + fs::path modPakPath("./" / mod.m_ModDirectory / "paks"); for (ModRpakEntry& pak : mod.Rpaks) if (pak.m_bAutoLoad) - g_PakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), false); + g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::CONSTANT); } } -void LoadPostloadPaks(char** map) +void LoadPostloadPaks(const char* pPath) { // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { - if (!mod.Enabled) + if (!mod.m_bEnabled) continue; // need to get a relative path of mod to mod folder - fs::path modPakPath("./" / mod.ModDirectory / "paks"); + fs::path modPakPath("./" / mod.m_ModDirectory / "paks"); for (ModRpakEntry& pak : mod.Rpaks) - if (pak.m_sLoadAfterPak == *map) - g_PakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), false); + 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); + bool bHasOriginalPak = fs::exists(fs::path("r2/paks/Win64/") / *pakName); // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { - if (!mod.Enabled) + if (!mod.m_bEnabled) continue; // need to get a relative path of mod to mod folder - fs::path modPakPath("./" / mod.ModDirectory / "paks"); + fs::path modPakPath("./" / mod.m_ModDirectory / "paks"); for (ModRpakEntry& pak : mod.Rpaks) { @@ -156,114 +167,85 @@ void LoadCustomMapPaks(char** pakName, bool* bNeedToFreePakName) true; // we can't free this memory until we're done with the pak, so let whatever's calling this deal with it } else - g_PakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), true); + g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::MAP); } } } } -LoadPakSyncType LoadPakSyncOriginal; -void* LoadPakSyncHook(char* path, void* unknownSingleton, int flags) +HOOK(LoadPakAsyncHook, LoadPakAsync, int, , (char* pPath, void* unknownSingleton, int flags, void* pCallback0, void* pCallback1)) { - HandlePakAliases(&path); - - bool bNeedToFreePakName = false; - - // note: we don't handle loading any preloaded custom paks synchronously since LoadPakSync is never actually called in retail, just load - // them async instead - static bool bShouldLoadPaks = true; - if (bShouldLoadPaks) - { - // disable preloading while we're doing this - bShouldLoadPaks = false; - - LoadPreloadPaks(); - LoadCustomMapPaks(&path, &bNeedToFreePakName); - - bShouldLoadPaks = true; - } - - spdlog::info("LoadPakSync {}", path); - void* ret = LoadPakSyncOriginal(path, unknownSingleton, flags); - - if (bNeedToFreePakName) - delete[] path; - - return ret; -} + HandlePakAliases(&pPath); -LoadPakAsyncType LoadPakAsyncOriginal; -int LoadPakAsyncHook(char* path, void* unknownSingleton, int flags, void* callback0, void* callback1) -{ - size_t hash = STR_HASH(path); - // if the hash is already in the map, dont load the pak, it has already been loaded - if (g_PakLoadManager->IsPakLoaded(hash)) - { + // dont load the pak if it's currently loaded already + size_t nPathHash = STR_HASH(pPath); + if (g_pPakLoadManager->GetPakHandle(nPathHash) != -1) return -1; - } - - HandlePakAliases(&path); 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(&path, &bNeedToFreePakName); + 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 - if (IsDedicatedServer() && strncmp(path, "common", 6)) // dedicated only needs common and common_mp + if (IsDedicatedServer() && (Tier0::CommandLine()->CheckParm("-nopakdedi") || strncmp(&originalPath[0], "common", 6))) // dedicated only needs common and common_mp + { + if (bNeedToFreePakName) + delete[] pPath; + + spdlog::info("Not loading pak {} for dedicated server", originalPath); return -1; + } } - int ret = LoadPakAsyncOriginal(path, unknownSingleton, flags, callback0, callback1); - spdlog::info("LoadPakAsync {} {}", path, ret); - - // add the hash to the map - g_PakLoadManager->AddLoadedPak(ret, hash); + int iPakHandle = LoadPakAsync(pPath, unknownSingleton, flags, pCallback0, pCallback1); + spdlog::info("LoadPakAsync {} {}", pPath, iPakHandle); - LoadPostloadPaks(&path); + // trak the pak + g_pPakLoadManager->TrackLoadedPak(ePakLoadSource::UNTRACKED, iPakHandle, nPathHash); + LoadPostloadPaks(pPath); if (bNeedToFreePakName) - delete[] path; + delete[] pPath; - return ret; + return iPakHandle; } -UnloadPakType UnloadPakOriginal; -void* UnloadPakHook(int pakHandle, void* callback) +HOOK(UnloadPakHook, UnloadPak, +void*,, (int nPakHandle, void* pCallback)) { - if (g_PakLoadManager->IsPakLoaded(pakHandle)) - { - // remove the entry - g_PakLoadManager->RemoveLoadedPak(pakHandle); - } + // stop tracking the pak + g_pPakLoadManager->RemoveLoadedPak(nPakHandle); static bool bShouldUnloadPaks = true; if (bShouldUnloadPaks) { bShouldUnloadPaks = false; - g_PakLoadManager->UnloadPaks(); + g_pPakLoadManager->UnloadMapPaks(); bShouldUnloadPaks = true; } - spdlog::info("UnloadPak {}", pakHandle); - return UnloadPakOriginal(pakHandle, callback); + spdlog::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 and rpak loads -// possibly just async loading all together? -ReadFullFileFromDiskType ReadFullFileFromDiskOriginal; -void* ReadFullFileFromDiskHook(const char* requestedPath, void* a2) +// we hook this exclusively for resolving stbsp paths, but seemingly it's also used for other stuff like vpk, rpak, mprj and starpak loads +HOOK(ReadFileAsyncHook, ReadFileAsync, +void*, , (const char* pPath, void* pCallback)) { - fs::path path(requestedPath); + fs::path path(pPath); char* allocatedNewPath = nullptr; if (path.extension() == ".stbsp") @@ -272,42 +254,34 @@ void* ReadFullFileFromDiskHook(const char* requestedPath, void* a2) spdlog::info("LoadStreamBsp: {}", filename.string()); // resolve modded stbsp path so we can load mod stbsps - auto modFile = g_ModManager->m_modFiles.find(fs::path("maps" / filename).lexically_normal().string()); - if (modFile != g_ModManager->m_modFiles.end()) + auto modFile = g_pModManager->m_ModFiles.find(g_pModManager->NormaliseModFilePath(fs::path("maps" / filename))); + if (modFile != g_pModManager->m_ModFiles.end()) { // need to allocate a new string for this - std::string newPath = (modFile->second.owningMod->ModDirectory / "mod" / modFile->second.path).string(); + std::string newPath = (modFile->second.m_pOwningMod->m_ModDirectory / "mod" / modFile->second.m_Path).string(); allocatedNewPath = new char[newPath.size() + 1]; - strncpy(allocatedNewPath, newPath.c_str(), newPath.size()); - allocatedNewPath[newPath.size()] = '\0'; - requestedPath = allocatedNewPath; + strncpy_s(allocatedNewPath, newPath.size() + 1, newPath.c_str(), newPath.size()); + pPath = allocatedNewPath; } } - void* ret = ReadFullFileFromDiskOriginal(requestedPath, a2); + void* ret = ReadFileAsync(pPath, pCallback); if (allocatedNewPath) delete[] allocatedNewPath; return ret; } -void InitialiseEngineRpakFilesystem(HMODULE baseAddress) +ON_DLL_LOAD("engine.dll", RpakFilesystem, (CModule module)) { - g_PakLoadManager = new PakLoadManager; - - g_pakLoadApi = *(PakLoadFuncs**)((char*)baseAddress + 0x5BED78); - pUnknownPakLoadSingleton = (void**)((char*)baseAddress + 0x7C5E20); - - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, reinterpret_cast<void*>(g_pakLoadApi->LoadPakSync), &LoadPakSyncHook, reinterpret_cast<LPVOID*>(&LoadPakSyncOriginal)); - ENABLER_CREATEHOOK( - hook, reinterpret_cast<void*>(g_pakLoadApi->LoadPakAsync), &LoadPakAsyncHook, reinterpret_cast<LPVOID*>(&LoadPakAsyncOriginal)); - ENABLER_CREATEHOOK( - hook, reinterpret_cast<void*>(g_pakLoadApi->UnloadPak), &UnloadPakHook, reinterpret_cast<LPVOID*>(&UnloadPakOriginal)); - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>(g_pakLoadApi->ReadFullFileFromDisk), - &ReadFullFileFromDiskHook, - reinterpret_cast<LPVOID*>(&ReadFullFileFromDiskOriginal)); + AUTOHOOK_DISPATCH(); + + g_pPakLoadManager = new PakLoadManager; + + g_pakLoadApi = module.Offset(0x5BED78).Deref().As<PakLoadFuncs*>(); + pUnknownPakLoadSingleton = module.Offset(0x7C5E20).As<void**>(); + + LoadPakAsyncHook.Dispatch(g_pakLoadApi->LoadPakAsync); + UnloadPakHook.Dispatch(g_pakLoadApi->UnloadPak); + ReadFileAsyncHook.Dispatch(g_pakLoadApi->ReadFileAsync); } diff --git a/NorthstarDLL/rpakfilesystem.h b/NorthstarDLL/rpakfilesystem.h index af51f6db..3f608dba 100644 --- a/NorthstarDLL/rpakfilesystem.h +++ b/NorthstarDLL/rpakfilesystem.h @@ -1,24 +1,39 @@ #pragma once -void InitialiseEngineRpakFilesystem(HMODULE baseAddress); +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: - void LoadPakSync(const char* path); - void LoadPakAsync(const char* path, bool bMarkForUnload); - void UnloadPaks(); + int LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource); + void UnloadPak(const int nPakHandle); + void UnloadMapPaks(); + void* LoadFile(const char* path); // this is a guess - bool IsPakLoaded(int32_t pakHandle); - bool IsPakLoaded(size_t hash); - void AddLoadedPak(int32_t pakHandle, size_t hash); - void RemoveLoadedPak(int32_t pakHandle); + LoadedPak* TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash); + void RemoveLoadedPak(int nPakHandle); - private: - std::vector<int> m_pakHandlesToUnload; - // these size_t s are the asset path hashed with STR_HASH - std::unordered_map<int32_t, size_t> loadedPaks {}; - std::unordered_map<size_t, int32_t> loadedPaksInv {}; + LoadedPak* GetPakInfo(const int nPakHandle); + + int GetPakHandle(const size_t nPakNameHash); + int GetPakHandle(const char* pPath); }; -extern PakLoadManager* g_PakLoadManager; +extern PakLoadManager* g_pPakLoadManager; diff --git a/NorthstarDLL/runframe.cpp b/NorthstarDLL/runframe.cpp new file mode 100644 index 00000000..0698e430 --- /dev/null +++ b/NorthstarDLL/runframe.cpp @@ -0,0 +1,18 @@ +#include "pch.h" +#include "r2engine.h" +#include "r2server.h" +#include "hoststate.h" +#include "serverpresence.h" + +AUTOHOOK_INIT() + +AUTOHOOK(CEngine__Frame, engine.dll + 0x1C8650, +void, __fastcall, (R2::CEngine* self)) +{ + CEngine__Frame(self); +} + +ON_DLL_LOAD("engine.dll", RunFrame, (CModule module)) +{ + AUTOHOOK_DISPATCH() +} diff --git a/NorthstarDLL/scriptbrowserhooks.cpp b/NorthstarDLL/scriptbrowserhooks.cpp index 22f1101f..7e0144b7 100644 --- a/NorthstarDLL/scriptbrowserhooks.cpp +++ b/NorthstarDLL/scriptbrowserhooks.cpp @@ -1,27 +1,23 @@ #include "pch.h" -#include "scriptbrowserhooks.h" -#include "hookutils.h" -typedef void (*OpenExternalWebBrowserType)(char* url, char flags); -OpenExternalWebBrowserType OpenExternalWebBrowser; +AUTOHOOK_INIT() bool* bIsOriginOverlayEnabled; -void OpenExternalWebBrowserHook(char* url, char flags) +AUTOHOOK(OpenExternalWebBrowser, engine.dll + 0x184E40, +void,, (char* pUrl, char flags)) { bool bIsOriginOverlayEnabledOriginal = *bIsOriginOverlayEnabled; - if (flags & 2 && !strncmp(url, "http", 4)) // custom force external browser flag + 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(url, flags); + OpenExternalWebBrowser(pUrl, flags); *bIsOriginOverlayEnabled = bIsOriginOverlayEnabledOriginal; } -void InitialiseScriptExternalBrowserHooks(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT("engine.dll", ScriptExternalBrowserHooks, (CModule module)) { - bIsOriginOverlayEnabled = (bool*)baseAddress + 0x13978255; + AUTOHOOK_DISPATCH() - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x184E40, &OpenExternalWebBrowserHook, reinterpret_cast<LPVOID*>(&OpenExternalWebBrowser)); + bIsOriginOverlayEnabled = module.Offset(0x13978255).As<bool*>(); } diff --git a/NorthstarDLL/scriptbrowserhooks.h b/NorthstarDLL/scriptbrowserhooks.h deleted file mode 100644 index f303ea93..00000000 --- a/NorthstarDLL/scriptbrowserhooks.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseScriptExternalBrowserHooks(HMODULE baseAddress); diff --git a/NorthstarDLL/scriptdatatables.cpp b/NorthstarDLL/scriptdatatables.cpp new file mode 100644 index 00000000..cd8a6b2a --- /dev/null +++ b/NorthstarDLL/scriptdatatables.cpp @@ -0,0 +1,922 @@ +#include "pch.h" +#include "squirrel.h" +#include "rpakfilesystem.h" +#include "convar.h" +#include "dedicated.h" +#include "filesystem.h" +#include "vector.h" +#include "tier0.h" +#include "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); +std::string DataTableToString(Datatable* datatable); + +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; +} + +template <ScriptContext context> SQRESULT SQ_GetDatatable(HSquirrelVM* sqvm) +{ + 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>->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 ((*R2::g_pFilesystem)->m_vtable2->FileExists(&(*R2::g_pFilesystem)->m_vtable2, sDiskAssetPath.c_str(), "GAME")) + { + std::string sTableCSV = R2::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>->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; + } + } +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableColumnByName(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowCount(HSquirrelVM* sqvm) +{ + 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; + const char* pColumnName = g_pSquirrel<context>->getstring(sqvm, 2); + + g_pSquirrel<context>->pushinteger(sqvm, csv->dataPointers.size()); + return SQRESULT_NOTNULL; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableString(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableAsset(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableInt(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableFloat(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableBool(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableVector(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowMatchingStringValue(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowMatchingAssetValue(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowMatchingFloatValue(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowMatchingIntValue(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowMatchingVectorValue(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowGreaterThanOrEqualToIntValue(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowLessThanOrEqualToIntValue(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowGreaterThanOrEqualToFloatValue(HSquirrelVM* sqvm) +{ + 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; +} + +template <ScriptContext context> SQRESULT SQ_GetDataTableRowLessThanOrEqualToFloatValue(HSquirrelVM* sqvm) +{ + 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((float*)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", R2::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); +} + +template <ScriptContext context> void RegisterDataTableFunctions() +{ + g_pSquirrel<context>->AddFuncOverride("GetDataTable", SQ_GetDatatable<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableColumnByName", SQ_GetDataTableColumnByName<context>); + g_pSquirrel<context>->AddFuncOverride("GetDatatableRowCount", SQ_GetDataTableRowCount<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableString", SQ_GetDataTableString<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableInt", SQ_GetDataTableInt<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableFloat", SQ_GetDataTableFloat<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableBool", SQ_GetDataTableBool<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableAsset", SQ_GetDataTableAsset<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableVector", SQ_GetDataTableVector<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableRowMatchingStringValue", SQ_GetDataTableRowMatchingStringValue<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableRowMatchingAssetValue", SQ_GetDataTableRowMatchingAssetValue<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableRowMatchingFloatValue", SQ_GetDataTableRowMatchingFloatValue<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableRowMatchingIntValue", SQ_GetDataTableRowMatchingIntValue<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableRowMatchingVectorValue", SQ_GetDataTableRowMatchingVectorValue<context>); + g_pSquirrel<context>->AddFuncOverride( + "GetDataTableRowLessThanOrEqualToFloatValue", SQ_GetDataTableRowLessThanOrEqualToFloatValue<context>); + g_pSquirrel<context>->AddFuncOverride( + "GetDataTableRowGreaterThanOrEqualToFloatValue", SQ_GetDataTableRowGreaterThanOrEqualToFloatValue<context>); + g_pSquirrel<context>->AddFuncOverride("GetDataTableRowLessThanOrEqualToIntValue", SQ_GetDataTableRowLessThanOrEqualToIntValue<context>); + g_pSquirrel<context>->AddFuncOverride( + "GetDataTableRowGreaterThanOrEqualToFloatValue", SQ_GetDataTableRowGreaterThanOrEqualToIntValue<context>); +} + +ON_DLL_LOAD_RELIESON("server.dll", ServerScriptDatatables, ServerSquirrel, (CModule module)) +{ + RegisterDataTableFunctions<ScriptContext::SERVER>(); + + SQ_GetDatatableInternal<ScriptContext::SERVER> = module.Offset(0x1250f0).As<Datatable* (*)(HSquirrelVM*)>(); +} + +ON_DLL_LOAD_RELIESON("client.dll", ClientScriptDatatables, ClientSquirrel, (CModule module)) +{ + RegisterDataTableFunctions<ScriptContext::CLIENT>(); + RegisterDataTableFunctions<ScriptContext::UI>(); + + SQ_GetDatatableInternal<ScriptContext::CLIENT> = module.Offset(0x1C9070).As<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() && Tier0::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); +}
\ No newline at end of file diff --git a/NorthstarDLL/scriptjson.cpp b/NorthstarDLL/scriptjson.cpp index bc65a5c1..62074587 100644 --- a/NorthstarDLL/scriptjson.cpp +++ b/NorthstarDLL/scriptjson.cpp @@ -1,502 +1,131 @@ #include "pch.h" -#include "scriptjson.h" +#include "squirrel.h" + +#include "rapidjson/error/en.h" #include "rapidjson/document.h" #include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" -#include "squirrel.h" - #ifdef _MSC_VER #undef GetObject // fuck microsoft developers #endif -void SQ_EncodeJSON_Table( - rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj, - SQTable* sqTable, - rapidjson_document* allocatorDoc); -void SQ_EncodeJSON_Array( - rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj, - SQArray* sqArray, - rapidjson_document* allocatorDoc); -void ServerSq_DecodeJSON_Table( - void* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj); -void ServerSq_DecodeJSON_Array( - void* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* arr); -void ClientSq_DecodeJSON_Table( - void* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj); -void ClientSq_DecodeJSON_Array( - void* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* arr); - -SQRESULT ServerSq_DecodeJSON(void* sqvm) -{ - const char* json = ServerSq_getstring(sqvm, 1); - rapidjson_document doc; - doc.Parse(json); - if (doc.HasParseError()) - { - ServerSq_newTable(sqvm); - return SQRESULT_NOTNULL; - } - ServerSq_newTable(sqvm); - - for (int i = 0; i < doc.MemberCount(); i++) - { - - rapidjson::GenericMember<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>& itr = doc.MemberBegin()[i]; - - switch (itr.value.GetType()) - { - case rapidjson::kObjectType: - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_DecodeJSON_Table( - sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr.value); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kArrayType: - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_DecodeJSON_Array( - sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr.value); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kStringType: - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_pushstring(sqvm, itr.value.GetString(), -1); - - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kTrueType: - if ((long long)itr.name.GetString() == -1) - { - spdlog::info("Neagative String decoding True"); - continue; - } - - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_pushbool(sqvm, true); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kFalseType: - if ((long long)itr.name.GetString() == -1) - { - - continue; - } - - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_pushbool(sqvm, false); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kNumberType: - if (itr.value.IsDouble() || itr.value.IsFloat()) - { - - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_pushfloat(sqvm, itr.value.GetFloat()); - } - else - { - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_pushinteger(sqvm, itr.value.GetInt()); - } - ServerSq_newSlot(sqvm, -3, false); - break; - } - } - return SQRESULT_NOTNULL; -} - -void ServerSq_DecodeJSON_Table( - void* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj) +template <ScriptContext context> void +DecodeJsonArray(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* arr) { - ServerSq_newTable(sqvm); + g_pSquirrel<context>->newarray(sqvm, 0); - for (int i = 0; i < obj->MemberCount(); i++) - { - rapidjson::GenericMember<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>& itr = obj->MemberBegin()[i]; - if (!itr.name.IsString()) - { - spdlog::info("Decoding table with non-string key"); - continue; - } - const char* key = itr.name.GetString(); - switch (itr.value.GetType()) - { - case rapidjson::kObjectType: - ServerSq_pushstring(sqvm, key, -1); - ServerSq_DecodeJSON_Table( - sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr.value); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kArrayType: - ServerSq_pushstring(sqvm, key, -1); - ServerSq_DecodeJSON_Array( - sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr.value); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kStringType: - ServerSq_pushstring(sqvm, key, -1); - ServerSq_pushstring(sqvm, itr.value.GetString(), -1); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kTrueType: - - ServerSq_pushstring(sqvm, key, -1); - ServerSq_pushbool(sqvm, true); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kFalseType: - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_pushbool(sqvm, false); - ServerSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kNumberType: - if (itr.value.IsDouble() || itr.value.IsFloat()) - { - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_pushfloat(sqvm, itr.value.GetFloat()); - } - else - { - ServerSq_pushstring(sqvm, itr.name.GetString(), -1); - ServerSq_pushinteger(sqvm, itr.value.GetInt()); - } - ServerSq_newSlot(sqvm, -3, false); - break; - } - } -} - -void ServerSq_DecodeJSON_Array( - void* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* arr) -{ - int usedType = arr->GetArray().Begin()->GetType(); - bool isFloat = arr->GetArray().Begin()->IsDouble() || arr->GetArray().Begin()->IsFloat(); - ServerSq_newarray(sqvm, 0); for (auto& itr : arr->GetArray()) { switch (itr.GetType()) { case rapidjson::kObjectType: - - if (usedType != itr.GetType()) - continue; - ServerSq_DecodeJSON_Table(sqvm, &itr); - ServerSq_arrayappend(sqvm, -2); + DecodeJsonTable<context>(sqvm, &itr); + g_pSquirrel<context>->arrayappend(sqvm, -2); break; case rapidjson::kArrayType: - - if (usedType != itr.GetType()) - continue; - ServerSq_DecodeJSON_Array(sqvm, &itr); - ServerSq_arrayappend(sqvm, -2); + DecodeJsonArray<context>(sqvm, &itr); + g_pSquirrel<context>->arrayappend(sqvm, -2); break; case rapidjson::kStringType: - if ((long long)itr.GetString() == -1) - { - - continue; - } - if (usedType != itr.GetType()) - continue; - ServerSq_pushstring(sqvm, itr.GetString(), -1); - ServerSq_arrayappend(sqvm, -2); + g_pSquirrel<context>->pushstring(sqvm, itr.GetString(), -1); + g_pSquirrel<context>->arrayappend(sqvm, -2); break; case rapidjson::kTrueType: - if (usedType != rapidjson::kTrueType && usedType != rapidjson::kFalseType) - continue; - ServerSq_pushbool(sqvm, true); - ServerSq_arrayappend(sqvm, -2); - break; case rapidjson::kFalseType: - if (usedType != rapidjson::kTrueType && usedType != rapidjson::kFalseType) - continue; - ServerSq_pushbool(sqvm, false); - ServerSq_arrayappend(sqvm, -2); + g_pSquirrel<context>->pushbool(sqvm, itr.GetBool()); + g_pSquirrel<context>->arrayappend(sqvm, -2); break; case rapidjson::kNumberType: - if (usedType != itr.GetType()) - continue; if (itr.IsDouble() || itr.IsFloat()) - { - - if (!isFloat) - continue; - ServerSq_pushfloat(sqvm, itr.GetFloat()); - } + g_pSquirrel<context>->pushfloat(sqvm, itr.GetFloat()); else - { - if (isFloat) - continue; - ServerSq_pushinteger(sqvm, itr.GetInt()); - } - ServerSq_arrayappend(sqvm, -2); + g_pSquirrel<context>->pushinteger(sqvm, itr.GetInt()); + g_pSquirrel<context>->arrayappend(sqvm, -2); break; } } } -SQRESULT ClientSq_DecodeJSON(void* sqvm) +template <ScriptContext context> void +DecodeJsonTable(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj) { - const char* json = ClientSq_getstring(sqvm, 1); - rapidjson_document doc; - doc.Parse(json); - if (doc.HasParseError()) - { - ClientSq_newTable(sqvm); - return SQRESULT_NOTNULL; - } - ClientSq_newTable(sqvm); + g_pSquirrel<context>->newtable(sqvm); - for (int i = 0; i < doc.MemberCount(); i++) + for (auto itr = obj->MemberBegin(); itr != obj->MemberEnd(); itr++) { - - rapidjson::GenericMember<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>& itr = doc.MemberBegin()[i]; - - switch (itr.value.GetType()) + switch (itr->value.GetType()) { case rapidjson::kObjectType: - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_DecodeJSON_Table( - sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr.value); - ClientSq_newSlot(sqvm, -3, false); + 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: - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_DecodeJSON_Array( - sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr.value); - ClientSq_newSlot(sqvm, -3, false); + 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: - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_pushstring(sqvm, itr.value.GetString(), -1); + g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1); + g_pSquirrel<context>->pushstring(sqvm, itr->value.GetString(), -1); - ClientSq_newSlot(sqvm, -3, false); + g_pSquirrel<context>->newslot(sqvm, -3, false); break; case rapidjson::kTrueType: - if ((long long)itr.name.GetString() == -1) - { - spdlog::info("Neagative String decoding True"); - continue; - } - - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_pushbool(sqvm, true); - ClientSq_newSlot(sqvm, -3, false); - break; case rapidjson::kFalseType: - if ((long long)itr.name.GetString() == -1) - { - - continue; - } - - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_pushbool(sqvm, false); - ClientSq_newSlot(sqvm, -3, false); + 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()) + if (itr->value.IsDouble() || itr->value.IsFloat()) { - - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_pushfloat(sqvm, itr.value.GetFloat()); + g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1); + g_pSquirrel<context>->pushfloat(sqvm, itr->value.GetFloat()); } else { - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_pushinteger(sqvm, itr.value.GetInt()); + g_pSquirrel<context>->pushstring(sqvm, itr->name.GetString(), -1); + g_pSquirrel<context>->pushinteger(sqvm, itr->value.GetInt()); } - ClientSq_newSlot(sqvm, -3, false); + g_pSquirrel<context>->newslot(sqvm, -3, false); break; } } - return SQRESULT_NOTNULL; } -void ClientSq_DecodeJSON_Table( - void* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj) -{ - ClientSq_newTable(sqvm); - - for (int i = 0; i < obj->MemberCount(); i++) - { - rapidjson::GenericMember<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>& itr = obj->MemberBegin()[i]; - if (!itr.name.IsString()) - { - spdlog::info("Decoding table with non-string key"); - continue; - } - const char* key = itr.name.GetString(); - - switch (itr.value.GetType()) - { - case rapidjson::kObjectType: - ClientSq_pushstring(sqvm, key, -1); - ClientSq_DecodeJSON_Table( - sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr.value); - ClientSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kArrayType: - ClientSq_pushstring(sqvm, key, -1); - ClientSq_DecodeJSON_Array( - sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&itr.value); - ClientSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kStringType: - ClientSq_pushstring(sqvm, key, -1); - ClientSq_pushstring(sqvm, itr.value.GetString(), -1); - ClientSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kTrueType: - - ClientSq_pushstring(sqvm, key, -1); - ClientSq_pushbool(sqvm, true); - ClientSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kFalseType: - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_pushbool(sqvm, false); - ClientSq_newSlot(sqvm, -3, false); - break; - case rapidjson::kNumberType: - if (itr.value.IsDouble() || itr.value.IsFloat()) - { - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_pushfloat(sqvm, itr.value.GetFloat()); - } - else - { - ClientSq_pushstring(sqvm, itr.name.GetString(), -1); - ClientSq_pushinteger(sqvm, itr.value.GetInt()); - } - ClientSq_newSlot(sqvm, -3, false); - break; - } - } -} - -void ClientSq_DecodeJSON_Array( - void* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* arr) -{ - int usedType = arr->GetArray().Begin()->GetType(); - bool isFloat = arr->GetArray().Begin()->IsDouble() || arr->GetArray().Begin()->IsFloat(); - ClientSq_newarray(sqvm, 0); - for (auto& itr : arr->GetArray()) - { - switch (itr.GetType()) - { - case rapidjson::kObjectType: - - if (usedType != itr.GetType()) - continue; - ClientSq_DecodeJSON_Table(sqvm, &itr); - ClientSq_arrayappend(sqvm, -2); - break; - case rapidjson::kArrayType: - - if (usedType != itr.GetType()) - continue; - ClientSq_DecodeJSON_Array(sqvm, &itr); - ClientSq_arrayappend(sqvm, -2); - break; - case rapidjson::kStringType: - if ((long long)itr.GetString() == -1) - { - - continue; - } - if (usedType != itr.GetType()) - continue; - ClientSq_pushstring(sqvm, itr.GetString(), -1); - ClientSq_arrayappend(sqvm, -2); - break; - case rapidjson::kTrueType: - if (usedType != rapidjson::kTrueType && usedType != rapidjson::kFalseType) - continue; - ClientSq_pushbool(sqvm, true); - ClientSq_arrayappend(sqvm, -2); - break; - case rapidjson::kFalseType: - if (usedType != rapidjson::kTrueType && usedType != rapidjson::kFalseType) - continue; - ClientSq_pushbool(sqvm, false); - ClientSq_arrayappend(sqvm, -2); - break; - case rapidjson::kNumberType: - if (usedType != itr.GetType()) - continue; - if (itr.IsDouble() || itr.IsFloat()) - { - - if (!isFloat) - continue; - ClientSq_pushfloat(sqvm, itr.GetFloat()); - } - else - { - if (isFloat) - continue; - ClientSq_pushinteger(sqvm, itr.GetInt()); - } - ClientSq_arrayappend(sqvm, -2); - break; - } - } -} - -SQRESULT SQ_EncodeJSON(void* sqvm) -{ - rapidjson_document doc; - doc.SetObject(); - - HSquirrelVM* vm = (HSquirrelVM*)sqvm; - SQTable* table = vm->_stackOfCurrentFunction[1]._VAL.asTable; - SQ_EncodeJSON_Table(&doc, table, &doc); - rapidjson::StringBuffer buffer; - rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); - doc.Accept(writer); - const char* json = buffer.GetString(); - - ServerSq_pushstring(sqvm, json, -1); - return SQRESULT_NOTNULL; -} - -void SQ_EncodeJSON_Table( - rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj, - SQTable* table, - rapidjson_document* allocatorDoc) +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), - allocatorDoc->GetAllocator()); + 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), rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>( (int)node->val._VAL.asInteger), - allocatorDoc->GetAllocator()); + allocator); break; case OT_FLOAT: - obj->AddMember( rapidjson::StringRef(node->key._VAL.asString->_val), rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>(node->val._VAL.asFloat), - allocatorDoc->GetAllocator()); + allocator); break; case OT_BOOL: if (node->val._VAL.asInteger) @@ -504,106 +133,144 @@ void SQ_EncodeJSON_Table( obj->AddMember( rapidjson::StringRef(node->key._VAL.asString->_val), rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>(true), - allocatorDoc->GetAllocator()); + allocator); } else { obj->AddMember( rapidjson::StringRef(node->key._VAL.asString->_val), rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>(false), - allocatorDoc->GetAllocator()); + allocator); } break; case OT_TABLE: - - SQ_EncodeJSON_Table(&newObj, node->val._VAL.asTable, allocatorDoc); - obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), newObj, allocatorDoc->GetAllocator()); + EncodeJSONTable<context>(node->val._VAL.asTable, &newObj, allocator); + obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), newObj, allocator); break; case OT_ARRAY: - - SQ_EncodeJSON_Array(&newArray, node->val._VAL.asArray, allocatorDoc); - obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), newArray, allocatorDoc->GetAllocator()); + EncodeJSONArray<context>(node->val._VAL.asArray, &newArray, allocator); + obj->AddMember(rapidjson::StringRef(node->key._VAL.asString->_val), newArray, allocator); break; default: - spdlog::info("SQ encode Json type {} not supported", sq_getTypeName(node->val._Type)); + spdlog::warn("SQ_EncodeJSON: squirrel type {} not supported", SQTypeNameFromID(node->val._Type)); break; } } } } -void SQ_EncodeJSON_Array( +template <ScriptContext context> void EncodeJSONArray( + SQArray* arr, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj, - SQArray* sqArray, - rapidjson_document* allocatorDoc) + rapidjson::MemoryPoolAllocator<SourceAllocator>& allocator) { - int usedType = sqArray->_values->_Type; - for (int i = 0; i < sqArray->_usedSlots; i++) + for (int i = 0; i < arr->_usedSlots; i++) { - SQObject* node = &sqArray->_values[i]; - if (node->_Type != usedType) - { - const char* typeName = sq_getTypeName(node->_Type); - const char* usedTypeName = sq_getTypeName(usedType); - spdlog::info("SQ encode Json array not same type got %s expected %s", typeName, usedTypeName); - continue; - } + 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), allocatorDoc->GetAllocator()); + obj->PushBack(rapidjson::StringRef(node->_VAL.asString->_val), allocator); break; case OT_INTEGER: obj->PushBack( rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>((int)node->_VAL.asInteger), - allocatorDoc->GetAllocator()); + allocator); break; case OT_FLOAT: obj->PushBack( rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>(node->_VAL.asFloat), - allocatorDoc->GetAllocator()); + allocator); break; case OT_BOOL: if (node->_VAL.asInteger) - { - obj->PushBack(rapidjson::StringRef("true"), allocatorDoc->GetAllocator()); - } + obj->PushBack(rapidjson::StringRef("true"), allocator); else - { - obj->PushBack(rapidjson::StringRef("false"), allocatorDoc->GetAllocator()); - } + obj->PushBack(rapidjson::StringRef("false"), allocator); break; case OT_TABLE: - - SQ_EncodeJSON_Table(&newObj, node->_VAL.asTable, allocatorDoc); - obj->PushBack(newObj, allocatorDoc->GetAllocator()); + EncodeJSONTable<context>(node->_VAL.asTable, &newObj, allocator); + obj->PushBack(newObj, allocator); break; case OT_ARRAY: - - SQ_EncodeJSON_Array(&newArray, node->_VAL.asArray, allocatorDoc); - obj->PushBack(newArray, allocatorDoc->GetAllocator()); + EncodeJSONArray<context>(node->_VAL.asArray, &newArray, allocator); + obj->PushBack(newArray, allocator); break; default: - - spdlog::info("SQ encode Json type {} not supported", sq_getTypeName(node->_Type)); + spdlog::info("SQ encode Json type {} not supported", SQTypeNameFromID(node->_Type)); } } } -void InitialiseServerSquirrelJson(HMODULE baseAddress) +// table function DecodeJSON( string json, bool fatalParseErrors = false ) +template <ScriptContext context> SQRESULT SQ_DecodeJSON(HSquirrelVM* sqvm) { + 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); - g_ServerSquirrelManager->AddFuncRegistration("table", "DecodeJSON", "string json", "", ServerSq_DecodeJSON); - g_ServerSquirrelManager->AddFuncRegistration("string", "EncodeJSON", "table data", "", SQ_EncodeJSON); + 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()); + else + spdlog::warn(sErrorString); + + return SQRESULT_NOTNULL; + } + + DecodeJsonTable<context>(sqvm, (rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>*)&doc); } -void InitialiseClientSquirrelJson(HMODULE baseAddress) +// string function EncodeJSON( table data ) +template <ScriptContext context> SQRESULT SQ_EncodeJSON(HSquirrelVM* sqvm) { - g_ClientSquirrelManager->AddFuncRegistration("table", "DecodeJSON", "string json", "", ClientSq_DecodeJSON); - g_ClientSquirrelManager->AddFuncRegistration("string", "EncodeJSON", "table data", "", SQ_EncodeJSON); + rapidjson_document doc; + doc.SetObject(); - g_UISquirrelManager->AddFuncRegistration("table", "DecodeJSON", "string json", "", ClientSq_DecodeJSON); - g_UISquirrelManager->AddFuncRegistration("string", "EncodeJSON", "table data", "", SQ_EncodeJSON); + // 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; +} + +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientScriptJSON, ClientSquirrel, (CModule module)) +{ + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( + "table", "DecodeJSON", "string json, bool fatalParseErrors = false", "converts a json string to a squirrel table", SQ_DecodeJSON<ScriptContext::CLIENT>); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( + "string", "EncodeJSON", "table data", "converts a squirrel table to a json string", SQ_EncodeJSON<ScriptContext::CLIENT>); + + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( + "table", "DecodeJSON", "string json", "converts a json string to a squirrel table", SQ_DecodeJSON<ScriptContext::UI>); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( + "string", "EncodeJSON", "table data", "converts a squirrel table to a json string", SQ_EncodeJSON<ScriptContext::UI>); } + +ON_DLL_LOAD_RELIESON("server.dll", ServerScriptJSON, ServerSquirrel, (CModule module)) +{ + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration( + "table", "DecodeJSON", "string json", "converts a json string to a squirrel table", SQ_DecodeJSON<ScriptContext::SERVER>); + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration( + "string", "EncodeJSON", "table data", "converts a squirrel table to a json string", SQ_EncodeJSON<ScriptContext::SERVER>); +}
\ No newline at end of file diff --git a/NorthstarDLL/scriptjson.h b/NorthstarDLL/scriptjson.h deleted file mode 100644 index 5ee7400e..00000000 --- a/NorthstarDLL/scriptjson.h +++ /dev/null @@ -1,2 +0,0 @@ -void InitialiseServerSquirrelJson(HMODULE baseAddress); -void InitialiseClientSquirrelJson(HMODULE baseAddress); diff --git a/NorthstarDLL/scriptmainmenupromos.cpp b/NorthstarDLL/scriptmainmenupromos.cpp index eb56a88d..f88cd3e9 100644 --- a/NorthstarDLL/scriptmainmenupromos.cpp +++ b/NorthstarDLL/scriptmainmenupromos.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "scriptmainmenupromos.h" #include "squirrel.h" #include "masterserver.h" @@ -25,102 +24,102 @@ enum eMainMenuPromoDataProperty }; // void function NSRequestCustomMainMenuPromos() -SQRESULT SQ_RequestCustomMainMenuPromos(void* sqvm) +SQRESULT SQ_RequestCustomMainMenuPromos(HSquirrelVM* sqvm) { - g_MasterServerManager->RequestMainMenuPromos(); + g_pMasterServerManager->RequestMainMenuPromos(); return SQRESULT_NULL; } // bool function NSHasCustomMainMenuPromoData() -SQRESULT SQ_HasCustomMainMenuPromoData(void* sqvm) +SQRESULT SQ_HasCustomMainMenuPromoData(HSquirrelVM* sqvm) { - ClientSq_pushbool(sqvm, g_MasterServerManager->m_bHasMainMenuPromoData); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, g_pMasterServerManager->m_bHasMainMenuPromoData); return SQRESULT_NOTNULL; } // var function NSGetCustomMainMenuPromoData( int promoDataKey ) -SQRESULT SQ_GetCustomMainMenuPromoData(void* sqvm) +SQRESULT SQ_GetCustomMainMenuPromoData(HSquirrelVM* sqvm) { - if (!g_MasterServerManager->m_bHasMainMenuPromoData) + if (!g_pMasterServerManager->m_bHasMainMenuPromoData) return SQRESULT_NULL; - switch (ClientSq_getinteger(sqvm, 1)) + switch (g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1)) { case eMainMenuPromoDataProperty::newInfoTitle1: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.newInfoTitle1.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.newInfoTitle1.c_str()); break; } case eMainMenuPromoDataProperty::newInfoTitle2: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.newInfoTitle2.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.newInfoTitle2.c_str()); break; } case eMainMenuPromoDataProperty::newInfoTitle3: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.newInfoTitle3.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.newInfoTitle3.c_str()); break; } case eMainMenuPromoDataProperty::largeButtonTitle: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.largeButtonTitle.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.largeButtonTitle.c_str()); break; } case eMainMenuPromoDataProperty::largeButtonText: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.largeButtonText.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.largeButtonText.c_str()); break; } case eMainMenuPromoDataProperty::largeButtonUrl: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.largeButtonUrl.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.largeButtonUrl.c_str()); break; } case eMainMenuPromoDataProperty::largeButtonImageIndex: { - ClientSq_pushinteger(sqvm, g_MasterServerManager->m_MainMenuPromoData.largeButtonImageIndex); + g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.largeButtonImageIndex); break; } case eMainMenuPromoDataProperty::smallButton1Title: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.smallButton1Title.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton1Title.c_str()); break; } case eMainMenuPromoDataProperty::smallButton1Url: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.smallButton1Url.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton1Url.c_str()); break; } case eMainMenuPromoDataProperty::smallButton1ImageIndex: { - ClientSq_pushinteger(sqvm, g_MasterServerManager->m_MainMenuPromoData.smallButton1ImageIndex); + g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton1ImageIndex); break; } case eMainMenuPromoDataProperty::smallButton2Title: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.smallButton2Title.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton2Title.c_str()); break; } case eMainMenuPromoDataProperty::smallButton2Url: { - ClientSq_pushstring(sqvm, g_MasterServerManager->m_MainMenuPromoData.smallButton2Url.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton2Url.c_str()); break; } case eMainMenuPromoDataProperty::smallButton2ImageIndex: { - ClientSq_pushinteger(sqvm, g_MasterServerManager->m_MainMenuPromoData.smallButton2ImageIndex); + g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_sMainMenuPromoData.smallButton2ImageIndex); break; } } @@ -128,9 +127,9 @@ SQRESULT SQ_GetCustomMainMenuPromoData(void* sqvm) return SQRESULT_NOTNULL; } -void InitialiseScriptMainMenuPromos(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ScriptMainMenuPromos, ClientSquirrel, (CModule module)) { - g_UISquirrelManager->AddFuncRegistration("void", "NSRequestCustomMainMenuPromos", "", "", SQ_RequestCustomMainMenuPromos); - g_UISquirrelManager->AddFuncRegistration("bool", "NSHasCustomMainMenuPromoData", "", "", SQ_HasCustomMainMenuPromoData); - g_UISquirrelManager->AddFuncRegistration("var", "NSGetCustomMainMenuPromoData", "int promoDataKey", "", SQ_GetCustomMainMenuPromoData); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSRequestCustomMainMenuPromos", "", "", SQ_RequestCustomMainMenuPromos); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("bool", "NSHasCustomMainMenuPromoData", "", "", SQ_HasCustomMainMenuPromoData); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("var", "NSGetCustomMainMenuPromoData", "int promoDataKey", "", SQ_GetCustomMainMenuPromoData); } diff --git a/NorthstarDLL/scriptmainmenupromos.h b/NorthstarDLL/scriptmainmenupromos.h deleted file mode 100644 index f0aa6332..00000000 --- a/NorthstarDLL/scriptmainmenupromos.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseScriptMainMenuPromos(HMODULE baseAddress); diff --git a/NorthstarDLL/scriptmodmenu.cpp b/NorthstarDLL/scriptmodmenu.cpp index 767ede91..d2f35285 100644 --- a/NorthstarDLL/scriptmodmenu.cpp +++ b/NorthstarDLL/scriptmodmenu.cpp @@ -1,33 +1,32 @@ #include "pch.h" -#include "scriptmodmenu.h" #include "modmanager.h" #include "squirrel.h" // array<string> function NSGetModNames() -SQRESULT SQ_GetModNames(void* sqvm) +SQRESULT SQ_GetModNames(HSquirrelVM* sqvm) { - ClientSq_newarray(sqvm, 0); + g_pSquirrel<ScriptContext::UI>->newarray(sqvm, 0); - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { - ClientSq_pushstring(sqvm, mod.Name.c_str(), -1); - ClientSq_arrayappend(sqvm, -2); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, mod.Name.c_str()); + g_pSquirrel<ScriptContext::UI>->arrayappend(sqvm, -2); } return SQRESULT_NOTNULL; } // bool function NSIsModEnabled(string modName) -SQRESULT SQ_IsModEnabled(void* sqvm) +SQRESULT SQ_IsModEnabled(HSquirrelVM* sqvm) { - const SQChar* modName = ClientSq_getstring(sqvm, 1); + const SQChar* modName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { if (!mod.Name.compare(modName)) { - ClientSq_pushbool(sqvm, mod.Enabled); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, mod.m_bEnabled); return SQRESULT_NOTNULL; } } @@ -36,17 +35,17 @@ SQRESULT SQ_IsModEnabled(void* sqvm) } // void function NSSetModEnabled(string modName, bool enabled) -SQRESULT SQ_SetModEnabled(void* sqvm) +SQRESULT SQ_SetModEnabled(HSquirrelVM* sqvm) { - const SQChar* modName = ClientSq_getstring(sqvm, 1); - const SQBool enabled = ClientSq_getbool(sqvm, 2); + const SQChar* modName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); + const SQBool enabled = g_pSquirrel<ScriptContext::UI>->getbool(sqvm, 2); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { if (!mod.Name.compare(modName)) { - mod.Enabled = enabled; + mod.m_bEnabled = enabled; return SQRESULT_NULL; } } @@ -55,16 +54,16 @@ SQRESULT SQ_SetModEnabled(void* sqvm) } // string function NSGetModDescriptionByModName(string modName) -SQRESULT SQ_GetModDescription(void* sqvm) +SQRESULT SQ_GetModDescription(HSquirrelVM* sqvm) { - const SQChar* modName = ClientSq_getstring(sqvm, 1); + const SQChar* modName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { if (!mod.Name.compare(modName)) { - ClientSq_pushstring(sqvm, mod.Description.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, mod.Description.c_str()); return SQRESULT_NOTNULL; } } @@ -73,16 +72,16 @@ SQRESULT SQ_GetModDescription(void* sqvm) } // string function NSGetModVersionByModName(string modName) -SQRESULT SQ_GetModVersion(void* sqvm) +SQRESULT SQ_GetModVersion(HSquirrelVM* sqvm) { - const SQChar* modName = ClientSq_getstring(sqvm, 1); + const SQChar* modName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { if (!mod.Name.compare(modName)) { - ClientSq_pushstring(sqvm, mod.Version.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, mod.Version.c_str()); return SQRESULT_NOTNULL; } } @@ -91,16 +90,16 @@ SQRESULT SQ_GetModVersion(void* sqvm) } // string function NSGetModDownloadLinkByModName(string modName) -SQRESULT SQ_GetModDownloadLink(void* sqvm) +SQRESULT SQ_GetModDownloadLink(HSquirrelVM* sqvm) { - const SQChar* modName = ClientSq_getstring(sqvm, 1); + const SQChar* modName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { if (!mod.Name.compare(modName)) { - ClientSq_pushstring(sqvm, mod.DownloadLink.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, mod.DownloadLink.c_str()); return SQRESULT_NOTNULL; } } @@ -109,16 +108,16 @@ SQRESULT SQ_GetModDownloadLink(void* sqvm) } // int function NSGetModLoadPriority(string modName) -SQRESULT SQ_GetModLoadPriority(void* sqvm) +SQRESULT SQ_GetModLoadPriority(HSquirrelVM* sqvm) { - const SQChar* modName = ClientSq_getstring(sqvm, 1); + const SQChar* modName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { if (!mod.Name.compare(modName)) { - ClientSq_pushinteger(sqvm, mod.LoadPriority); + g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, mod.LoadPriority); return SQRESULT_NOTNULL; } } @@ -127,16 +126,16 @@ SQRESULT SQ_GetModLoadPriority(void* sqvm) } // bool function NSIsModRequiredOnClient(string modName) -SQRESULT SQ_IsModRequiredOnClient(void* sqvm) +SQRESULT SQ_IsModRequiredOnClient(HSquirrelVM* sqvm) { - const SQChar* modName = ClientSq_getstring(sqvm, 1); + const SQChar* modName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { if (!mod.Name.compare(modName)) { - ClientSq_pushbool(sqvm, mod.RequiredOnClient); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, mod.RequiredOnClient); return SQRESULT_NOTNULL; } } @@ -145,20 +144,20 @@ SQRESULT SQ_IsModRequiredOnClient(void* sqvm) } // array<string> function NSGetModConvarsByModName(string modName) -SQRESULT SQ_GetModConvars(void* sqvm) +SQRESULT SQ_GetModConvars(HSquirrelVM* sqvm) { - const SQChar* modName = ClientSq_getstring(sqvm, 1); - ClientSq_newarray(sqvm, 0); + const SQChar* modName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); + g_pSquirrel<ScriptContext::UI>->newarray(sqvm, 0); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_ModManager->m_loadedMods) + for (Mod& mod : g_pModManager->m_LoadedMods) { if (!mod.Name.compare(modName)) { for (ModConVar* cvar : mod.ConVars) { - ClientSq_pushstring(sqvm, cvar->Name.c_str(), -1); - ClientSq_arrayappend(sqvm, -2); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, cvar->Name.c_str()); + g_pSquirrel<ScriptContext::UI>->arrayappend(sqvm, -2); } return SQRESULT_NOTNULL; @@ -169,35 +168,36 @@ SQRESULT SQ_GetModConvars(void* sqvm) } // void function NSReloadMods() -SQRESULT SQ_ReloadMods(void* sqvm) +SQRESULT SQ_ReloadMods(HSquirrelVM* sqvm) { - g_ModManager->LoadMods(); + g_pModManager->LoadMods(); return SQRESULT_NULL; } -void InitialiseScriptModMenu(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ScriptModMenu, ClientSquirrel, (CModule module)) { - g_UISquirrelManager->AddFuncRegistration("array<string>", "NSGetModNames", "", "Returns the names of all loaded mods", SQ_GetModNames); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( + "array<string>", "NSGetModNames", "", "Returns the names of all loaded mods", SQ_GetModNames); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "bool", "NSIsModEnabled", "string modName", "Returns whether a given mod is enabled", SQ_IsModEnabled); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "void", "NSSetModEnabled", "string modName, bool enabled", "Sets whether a given mod is enabled", SQ_SetModEnabled); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "string", "NSGetModDescriptionByModName", "string modName", "Returns a given mod's description", SQ_GetModDescription); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "string", "NSGetModVersionByModName", "string modName", "Returns a given mod's version", SQ_GetModVersion); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "string", "NSGetModDownloadLinkByModName", "string modName", "Returns a given mod's download link", SQ_GetModDownloadLink); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "bool", "NSIsModRequiredOnClient", "string modName", "Returns whether a given mod is required on connecting clients", SQ_IsModRequiredOnClient); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "int", "NSGetModLoadPriority", "string modName", "Returns a given mod's load priority", SQ_GetModLoadPriority); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "array<string>", "NSGetModConvarsByModName", "string modName", "Returns the names of all a given mod's cvars", SQ_GetModConvars); - g_UISquirrelManager->AddFuncRegistration("void", "NSReloadMods", "", "Reloads mods", SQ_ReloadMods); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSReloadMods", "", "Reloads mods", SQ_ReloadMods); } diff --git a/NorthstarDLL/scriptmodmenu.h b/NorthstarDLL/scriptmodmenu.h deleted file mode 100644 index e7f71b11..00000000 --- a/NorthstarDLL/scriptmodmenu.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseScriptModMenu(HMODULE baseAddress); diff --git a/NorthstarDLL/scriptserverbrowser.cpp b/NorthstarDLL/scriptserverbrowser.cpp index aa5b0e17..ba5b17e2 100644 --- a/NorthstarDLL/scriptserverbrowser.cpp +++ b/NorthstarDLL/scriptserverbrowser.cpp @@ -1,383 +1,378 @@ #include "pch.h" -#include "scriptserverbrowser.h" #include "squirrel.h" #include "masterserver.h" -#include "gameutils.h" #include "serverauthentication.h" +#include "r2engine.h" +#include "r2client.h" // functions for viewing server browser // bool function NSIsMasterServerAuthenticated() -SQRESULT SQ_IsMasterServerAuthenticated(void* sqvm) +SQRESULT SQ_IsMasterServerAuthenticated(HSquirrelVM* sqvm) { - ClientSq_pushbool(sqvm, g_MasterServerManager->m_bOriginAuthWithMasterServerDone); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, g_pMasterServerManager->m_bOriginAuthWithMasterServerDone); return SQRESULT_NOTNULL; } // void function NSRequestServerList() -SQRESULT SQ_RequestServerList(void* sqvm) +SQRESULT SQ_RequestServerList(HSquirrelVM* sqvm) { - g_MasterServerManager->RequestServerList(); + g_pMasterServerManager->RequestServerList(); return SQRESULT_NULL; } // bool function NSIsRequestingServerList() -SQRESULT SQ_IsRequestingServerList(void* sqvm) +SQRESULT SQ_IsRequestingServerList(HSquirrelVM* sqvm) { - ClientSq_pushbool(sqvm, g_MasterServerManager->m_scriptRequestingServerList); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, g_pMasterServerManager->m_bScriptRequestingServerList); return SQRESULT_NOTNULL; } // bool function NSMasterServerConnectionSuccessful() -SQRESULT SQ_MasterServerConnectionSuccessful(void* sqvm) +SQRESULT SQ_MasterServerConnectionSuccessful(HSquirrelVM* sqvm) { - ClientSq_pushbool(sqvm, g_MasterServerManager->m_successfullyConnected); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, g_pMasterServerManager->m_bSuccessfullyConnected); return SQRESULT_NOTNULL; } // int function NSGetServerCount() -SQRESULT SQ_GetServerCount(void* sqvm) +SQRESULT SQ_GetServerCount(HSquirrelVM* sqvm) { - ClientSq_pushinteger(sqvm, g_MasterServerManager->m_remoteServers.size()); + g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_vRemoteServers.size()); return SQRESULT_NOTNULL; } // string function NSGetServerName( int serverIndex ) -SQRESULT SQ_GetServerName(void* sqvm) +SQRESULT SQ_GetServerName(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get name of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushstring(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].name, -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].name); return SQRESULT_NOTNULL; } // string function NSGetServerDescription( int serverIndex ) -SQRESULT SQ_GetServerDescription(void* sqvm) +SQRESULT SQ_GetServerDescription(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get description of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushstring(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].description.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].description.c_str()); return SQRESULT_NOTNULL; } // string function NSGetServerMap( int serverIndex ) -SQInteger SQ_GetServerMap(void* sqvm) +SQRESULT SQ_GetServerMap(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get map of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushstring(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].map, -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].map); return SQRESULT_NOTNULL; } // string function NSGetServerPlaylist( int serverIndex ) -SQRESULT SQ_GetServerPlaylist(void* sqvm) +SQRESULT SQ_GetServerPlaylist(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get playlist of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushstring(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].playlist, -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].playlist); return SQRESULT_NOTNULL; } // int function NSGetServerPlayerCount( int serverIndex ) -SQRESULT SQ_GetServerPlayerCount(void* sqvm) +SQRESULT SQ_GetServerPlayerCount(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get playercount of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushinteger(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].playerCount); + g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].playerCount); return SQRESULT_NOTNULL; } // int function NSGetServerMaxPlayerCount( int serverIndex ) -SQRESULT SQ_GetServerMaxPlayerCount(void* sqvm) +SQRESULT SQ_GetServerMaxPlayerCount(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get max playercount of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushinteger(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].maxPlayers); + g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].maxPlayers); return SQRESULT_NOTNULL; } // string function NSGetServerID( int serverIndex ) -SQRESULT SQ_GetServerID(void* sqvm) +SQRESULT SQ_GetServerID(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get id of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushstring(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].id, -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].id); return SQRESULT_NOTNULL; } // bool function NSServerRequiresPassword( int serverIndex ) -SQRESULT SQ_ServerRequiresPassword(void* sqvm) +SQRESULT SQ_ServerRequiresPassword(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get hasPassword of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushbool(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].requiresPassword); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].requiresPassword); return SQRESULT_NOTNULL; } // int function NSGetServerRequiredModsCount( int serverIndex ) -SQRESULT SQ_GetServerRequiredModsCount(void* sqvm) +SQRESULT SQ_GetServerRequiredModsCount(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get required mods count of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushinteger(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].requiredMods.size()); + g_pSquirrel<ScriptContext::UI>->pushinteger(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].requiredMods.size()); return SQRESULT_NOTNULL; } // string function NSGetServerRequiredModName( int serverIndex, int modIndex ) -SQRESULT SQ_GetServerRequiredModName(void* sqvm) +SQRESULT SQ_GetServerRequiredModName(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); - SQInteger modIndex = ClientSq_getinteger(sqvm, 2); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); + SQInteger modIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 2); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get hasPassword of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - if (modIndex >= g_MasterServerManager->m_remoteServers[serverIndex].requiredMods.size()) + if (modIndex >= g_pMasterServerManager->m_vRemoteServers[serverIndex].requiredMods.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get required mod name of mod index {} when only {} mod are available", modIndex, - g_MasterServerManager->m_remoteServers[serverIndex].requiredMods.size()) + g_pMasterServerManager->m_vRemoteServers[serverIndex].requiredMods.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushstring(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].requiredMods[modIndex].Name.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].requiredMods[modIndex].Name.c_str()); return SQRESULT_NOTNULL; } // string function NSGetServerRequiredModVersion( int serverIndex, int modIndex ) -SQRESULT SQ_GetServerRequiredModVersion(void* sqvm) +SQRESULT SQ_GetServerRequiredModVersion(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); - SQInteger modIndex = ClientSq_getinteger(sqvm, 2); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); + SQInteger modIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 2); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get required mod version of server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + g_pMasterServerManager->m_vRemoteServers.size()) .c_str()); return SQRESULT_ERROR; } - if (modIndex >= g_MasterServerManager->m_remoteServers[serverIndex].requiredMods.size()) + if (modIndex >= g_pMasterServerManager->m_vRemoteServers[serverIndex].requiredMods.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to get required mod version of mod index {} when only {} mod are available", modIndex, - g_MasterServerManager->m_remoteServers[serverIndex].requiredMods.size()) + g_pMasterServerManager->m_vRemoteServers[serverIndex].requiredMods.size()) .c_str()); return SQRESULT_ERROR; } - ClientSq_pushstring(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].requiredMods[modIndex].Version.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_vRemoteServers[serverIndex].requiredMods[modIndex].Version.c_str()); return SQRESULT_NOTNULL; } // void function NSClearRecievedServerList() -SQRESULT SQ_ClearRecievedServerList(void* sqvm) +SQRESULT SQ_ClearRecievedServerList(HSquirrelVM* sqvm) { - g_MasterServerManager->ClearServerList(); + g_pMasterServerManager->ClearServerList(); return SQRESULT_NULL; } // functions for authenticating with servers // void function NSTryAuthWithServer( int serverIndex, string password = "" ) -SQRESULT SQ_TryAuthWithServer(void* sqvm) +SQRESULT SQ_TryAuthWithServer(HSquirrelVM* sqvm) { - SQInteger serverIndex = ClientSq_getinteger(sqvm, 1); - const SQChar* password = ClientSq_getstring(sqvm, 2); + SQInteger serverIndex = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 1); + const SQChar* password = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 2); - if (serverIndex >= g_MasterServerManager->m_remoteServers.size()) + if (serverIndex >= g_pMasterServerManager->m_vRemoteServers.size()) { - ClientSq_pusherror( + g_pSquirrel<ScriptContext::UI>->raiseerror( sqvm, fmt::format( "Tried to auth with server index {} when only {} servers are available", serverIndex, - g_MasterServerManager->m_remoteServers.size()) + 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_ServerAuthenticationManager->m_additionalPlayerData) - g_ServerAuthenticationManager->WritePersistentData(pair.first); + for (auto& pair : g_pServerAuthentication->m_PlayerAuthenticationData) + g_pServerAuthentication->WritePersistentData(pair.first); // do auth - g_MasterServerManager->AuthenticateWithServer( - g_LocalPlayerUserID, - g_MasterServerManager->m_ownClientAuthToken, - g_MasterServerManager->m_remoteServers[serverIndex].id, + g_pMasterServerManager->AuthenticateWithServer( + R2::g_pLocalPlayerUserID, + g_pMasterServerManager->m_sOwnClientAuthToken, + g_pMasterServerManager->m_vRemoteServers[serverIndex].id, (char*)password); return SQRESULT_NULL; } // bool function NSIsAuthenticatingWithServer() -SQRESULT SQ_IsAuthComplete(void* sqvm) +SQRESULT SQ_IsAuthComplete(HSquirrelVM* sqvm) { - ClientSq_pushbool(sqvm, g_MasterServerManager->m_scriptAuthenticatingWithGameServer); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, g_pMasterServerManager->m_bScriptAuthenticatingWithGameServer); return SQRESULT_NOTNULL; } // bool function NSWasAuthSuccessful() -SQRESULT SQ_WasAuthSuccessful(void* sqvm) +SQRESULT SQ_WasAuthSuccessful(HSquirrelVM* sqvm) { - ClientSq_pushbool(sqvm, g_MasterServerManager->m_successfullyAuthenticatedWithGameServer); - return SQRESULT_NOTNULL; -} - -// bool function NSWasAuthSuccessful() -SQRESULT SQ_GetAuthFailReason(void* sqvm) -{ - ClientSq_pushstring(sqvm, g_MasterServerManager->s_authfail_reason.c_str(), -1); + g_pSquirrel<ScriptContext::UI>->pushbool(sqvm, g_pMasterServerManager->m_bSuccessfullyAuthenticatedWithGameServer); return SQRESULT_NOTNULL; } // void function NSConnectToAuthedServer() -SQRESULT SQ_ConnectToAuthedServer(void* sqvm) +SQRESULT SQ_ConnectToAuthedServer(HSquirrelVM* sqvm) { - if (!g_MasterServerManager->m_hasPendingConnectionInfo) + if (!g_pMasterServerManager->m_bHasPendingConnectionInfo) { - ClientSq_pusherror(sqvm, fmt::format("Tried to connect to authed server before any pending connection info was available").c_str()); + g_pSquirrel<ScriptContext::UI>->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_MasterServerManager->m_pendingConnectionInfo; + 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 - Cbuf_AddText(Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", info.authToken).c_str(), cmd_source_t::kCommandSrcCode); - Cbuf_AddText( - Cbuf_GetCurrentPlayer(), + R2::Cbuf_AddText( + R2::Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", info.authToken).c_str(), R2::cmd_source_t::kCommandSrcCode); + R2::Cbuf_AddText( + R2::Cbuf_GetCurrentPlayer(), fmt::format( "connect {}.{}.{}.{}:{}", info.ip.S_un.S_un_b.s_b1, @@ -386,65 +381,72 @@ SQRESULT SQ_ConnectToAuthedServer(void* sqvm) info.ip.S_un.S_un_b.s_b4, info.port) .c_str(), - cmd_source_t::kCommandSrcCode); + R2::cmd_source_t::kCommandSrcCode); - g_MasterServerManager->m_hasPendingConnectionInfo = false; + g_pMasterServerManager->m_bHasPendingConnectionInfo = false; return SQRESULT_NULL; } // void function NSTryAuthWithLocalServer() -SQRESULT SQ_TryAuthWithLocalServer(void* sqvm) +SQRESULT SQ_TryAuthWithLocalServer(HSquirrelVM* sqvm) { // do auth request - g_MasterServerManager->AuthenticateWithOwnServer(g_LocalPlayerUserID, g_MasterServerManager->m_ownClientAuthToken); + g_pMasterServerManager->AuthenticateWithOwnServer(R2::g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); return SQRESULT_NULL; } // void function NSCompleteAuthWithLocalServer() -SQRESULT SQ_CompleteAuthWithLocalServer(void* sqvm) +SQRESULT SQ_CompleteAuthWithLocalServer(HSquirrelVM* sqvm) { // literally just set serverfilter // note: this assumes we have no authdata other than our own - Cbuf_AddText( - Cbuf_GetCurrentPlayer(), - fmt::format("serverfilter {}", g_ServerAuthenticationManager->m_authData.begin()->first).c_str(), - cmd_source_t::kCommandSrcCode); + R2::Cbuf_AddText( + R2::Cbuf_GetCurrentPlayer(), + fmt::format("serverfilter {}", g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first).c_str(), + R2::cmd_source_t::kCommandSrcCode); return SQRESULT_NULL; } -void InitialiseScriptServerBrowser(HMODULE baseAddress) +// string function NSGetAuthFailReason() +SQRESULT SQ_GetAuthFailReason(HSquirrelVM* sqvm) +{ + g_pSquirrel<ScriptContext::UI>->pushstring(sqvm, g_pMasterServerManager->m_sAuthFailureReason.c_str(), -1); + return SQRESULT_NOTNULL; +} + +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ScriptServerBrowser, ClientSquirrel, (CModule module)) { - g_UISquirrelManager->AddFuncRegistration("bool", "NSIsMasterServerAuthenticated", "", "", SQ_IsMasterServerAuthenticated); - g_UISquirrelManager->AddFuncRegistration("void", "NSRequestServerList", "", "", SQ_RequestServerList); - g_UISquirrelManager->AddFuncRegistration("bool", "NSIsRequestingServerList", "", "", SQ_IsRequestingServerList); - g_UISquirrelManager->AddFuncRegistration("bool", "NSMasterServerConnectionSuccessful", "", "", SQ_MasterServerConnectionSuccessful); - g_UISquirrelManager->AddFuncRegistration("int", "NSGetServerCount", "", "", SQ_GetServerCount); - g_UISquirrelManager->AddFuncRegistration("void", "NSClearRecievedServerList", "", "", SQ_ClearRecievedServerList); - - g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerName", "int serverIndex", "", SQ_GetServerName); - g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerDescription", "int serverIndex", "", SQ_GetServerDescription); - g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerMap", "int serverIndex", "", SQ_GetServerMap); - g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerPlaylist", "int serverIndex", "", SQ_GetServerPlaylist); - g_UISquirrelManager->AddFuncRegistration("int", "NSGetServerPlayerCount", "int serverIndex", "", SQ_GetServerPlayerCount); - g_UISquirrelManager->AddFuncRegistration("int", "NSGetServerMaxPlayerCount", "int serverIndex", "", SQ_GetServerMaxPlayerCount); - g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerID", "int serverIndex", "", SQ_GetServerID); - g_UISquirrelManager->AddFuncRegistration("bool", "NSServerRequiresPassword", "int serverIndex", "", SQ_ServerRequiresPassword); - g_UISquirrelManager->AddFuncRegistration("int", "NSGetServerRequiredModsCount", "int serverIndex", "", SQ_GetServerRequiredModsCount); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("bool", "NSIsMasterServerAuthenticated", "", "", SQ_IsMasterServerAuthenticated); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSRequestServerList", "", "", SQ_RequestServerList); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("bool", "NSIsRequestingServerList", "", "", SQ_IsRequestingServerList); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("bool", "NSMasterServerConnectionSuccessful", "", "", SQ_MasterServerConnectionSuccessful); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("int", "NSGetServerCount", "", "", SQ_GetServerCount); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSClearRecievedServerList", "", "", SQ_ClearRecievedServerList); + + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("string", "NSGetServerName", "int serverIndex", "", SQ_GetServerName); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("string", "NSGetServerDescription", "int serverIndex", "", SQ_GetServerDescription); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("string", "NSGetServerMap", "int serverIndex", "", SQ_GetServerMap); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("string", "NSGetServerPlaylist", "int serverIndex", "", SQ_GetServerPlaylist); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("int", "NSGetServerPlayerCount", "int serverIndex", "", SQ_GetServerPlayerCount); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("int", "NSGetServerMaxPlayerCount", "int serverIndex", "", SQ_GetServerMaxPlayerCount); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("string", "NSGetServerID", "int serverIndex", "", SQ_GetServerID); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("bool", "NSServerRequiresPassword", "int serverIndex", "", SQ_ServerRequiresPassword); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("int", "NSGetServerRequiredModsCount", "int serverIndex", "", SQ_GetServerRequiredModsCount); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "string", "NSGetServerRequiredModName", "int serverIndex, int modIndex", "", SQ_GetServerRequiredModName); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "string", "NSGetServerRequiredModVersion", "int serverIndex, int modIndex", "", SQ_GetServerRequiredModVersion); - g_UISquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( "void", "NSTryAuthWithServer", "int serverIndex, string password = \"\"", "", SQ_TryAuthWithServer); - g_UISquirrelManager->AddFuncRegistration("bool", "NSIsAuthenticatingWithServer", "", "", SQ_IsAuthComplete); - g_UISquirrelManager->AddFuncRegistration("bool", "NSWasAuthSuccessful", "", "", SQ_WasAuthSuccessful); - g_UISquirrelManager->AddFuncRegistration("void", "NSConnectToAuthedServer", "", "", SQ_ConnectToAuthedServer); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("bool", "NSIsAuthenticatingWithServer", "", "", SQ_IsAuthComplete); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("bool", "NSWasAuthSuccessful", "", "", SQ_WasAuthSuccessful); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSConnectToAuthedServer", "", "", SQ_ConnectToAuthedServer); - g_UISquirrelManager->AddFuncRegistration("void", "NSTryAuthWithLocalServer", "", "", SQ_TryAuthWithLocalServer); - g_UISquirrelManager->AddFuncRegistration("void", "NSCompleteAuthWithLocalServer", "", "", SQ_CompleteAuthWithLocalServer); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSTryAuthWithLocalServer", "", "", SQ_TryAuthWithLocalServer); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSCompleteAuthWithLocalServer", "", "", SQ_CompleteAuthWithLocalServer); - g_UISquirrelManager->AddFuncRegistration("string", "NSGetAuthFailReason", "", "", SQ_GetAuthFailReason); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("string", "NSGetAuthFailReason", "", "", SQ_GetAuthFailReason); } diff --git a/NorthstarDLL/scriptserverbrowser.h b/NorthstarDLL/scriptserverbrowser.h deleted file mode 100644 index 3a88a019..00000000 --- a/NorthstarDLL/scriptserverbrowser.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include <minwindef.h> - -void InitialiseScriptServerBrowser(HMODULE baseAddress); diff --git a/NorthstarDLL/scriptservertoclientstringcommand.cpp b/NorthstarDLL/scriptservertoclientstringcommand.cpp index 084db1e4..b3a405fc 100644 --- a/NorthstarDLL/scriptservertoclientstringcommand.cpp +++ b/NorthstarDLL/scriptservertoclientstringcommand.cpp @@ -1,20 +1,19 @@ #include "pch.h" -#include "scriptservertoclientstringcommand.h" #include "squirrel.h" #include "convar.h" #include "concommand.h" void ConCommand_ns_script_servertoclientstringcommand(const CCommand& arg) { - if (g_ClientSquirrelManager->sqvm && - g_ClientSquirrelManager->setupfunc("NSClientCodeCallback_RecievedServerToClientStringCommand") != SQRESULT_ERROR) + if (g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM && + g_pSquirrel<ScriptContext::CLIENT>->setupfunc("NSClientCodeCallback_RecievedServerToClientStringCommand") != SQRESULT_ERROR) { - g_ClientSquirrelManager->pusharg(arg.ArgS()); - g_ClientSquirrelManager->call(1); // todo: doesn't throw or log errors from within this, probably not great behaviour + g_pSquirrel<ScriptContext::CLIENT>->pushstring(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, arg.ArgS()); + g_pSquirrel<ScriptContext::CLIENT>->call(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, 1); // todo: doesn't throw or log errors from within this, probably not great behaviour } } -void InitialiseScriptServerToClientStringCommands(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ScriptServerToClientStringCommand, ClientSquirrel, (CModule module)) { RegisterConCommand( "ns_script_servertoclientstringcommand", diff --git a/NorthstarDLL/scriptservertoclientstringcommand.h b/NorthstarDLL/scriptservertoclientstringcommand.h deleted file mode 100644 index 1970c1e6..00000000 --- a/NorthstarDLL/scriptservertoclientstringcommand.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InitialiseScriptServerToClientStringCommands(HMODULE baseAddress); diff --git a/NorthstarDLL/scriptsrson.cpp b/NorthstarDLL/scriptsrson.cpp index 2346e7e1..1e0ded27 100644 --- a/NorthstarDLL/scriptsrson.cpp +++ b/NorthstarDLL/scriptsrson.cpp @@ -12,15 +12,12 @@ void ModManager::BuildScriptsRson() fs::path MOD_SCRIPTS_RSON_PATH = fs::path(GetCompiledAssetsPath() / MOD_SCRIPTS_RSON_SUFFIX); fs::remove(MOD_SCRIPTS_RSON_PATH); - // not really important since it doesn't affect actual functionality at all, but the rson we output is really weird - // has a shitload of newlines added, even in places where we don't modify it at all - - std::string scriptsRson = ReadVPKOriginalFile(VPK_SCRIPTS_RSON_PATH); + std::string scriptsRson = R2::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) + for (Mod& mod : m_LoadedMods) { - if (!mod.Enabled) + if (!mod.m_bEnabled) continue; // this isn't needed at all, just nice to have imo @@ -38,7 +35,7 @@ void ModManager::BuildScriptsRson() ]*/ scriptsRson += "When: \""; - scriptsRson += script.RsonRunOn; + scriptsRson += script.RunOn; scriptsRson += "\"\n"; scriptsRson += "Scripts:\n[\n\t"; @@ -54,13 +51,13 @@ void ModManager::BuildScriptsRson() writeStream.close(); ModOverrideFile overrideFile; - overrideFile.owningMod = nullptr; - overrideFile.path = VPK_SCRIPTS_RSON_PATH; + 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)); + 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; + 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/scriptutility.cpp b/NorthstarDLL/scriptutility.cpp index f781a51d..7a1936cf 100644 --- a/NorthstarDLL/scriptutility.cpp +++ b/NorthstarDLL/scriptutility.cpp @@ -1,29 +1,23 @@ #include "pch.h" -#include "scriptutility.h" #include "squirrel.h" -template <ScriptContext context> SQRESULT SQ_StringToAsset(void* sqvm) +// asset function StringToAsset( string assetName ) +template <ScriptContext context> SQRESULT SQ_StringToAsset(HSquirrelVM* sqvm) { - if (context == ScriptContext::SERVER) - { - const char* asset = ServerSq_getstring(sqvm, 1); - ServerSq_pushAsset(sqvm, asset, -1); - } - else - { - const char* asset = ClientSq_getstring(sqvm, 1); - ClientSq_pushAsset(sqvm, asset, -1); - } + g_pSquirrel<context>->pushasset(sqvm, g_pSquirrel<context>->getstring(sqvm, 1), -1); return SQRESULT_NOTNULL; } -void InitialiseClientSquirrelUtilityFunctions(HMODULE baseAddress) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientSharedScriptUtility, ClientSquirrel, (CModule module)) { - g_ClientSquirrelManager->AddFuncRegistration("asset", "StringToAsset", "string assetName", "", SQ_StringToAsset<ScriptContext::CLIENT>); - g_UISquirrelManager->AddFuncRegistration("asset", "StringToAsset", "string assetName", "", SQ_StringToAsset<ScriptContext::UI>); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( + "asset", "StringToAsset", "string assetName", "converts a given string to an asset", SQ_StringToAsset<ScriptContext::CLIENT>); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( + "asset", "StringToAsset", "string assetName", "converts a given string to an asset", SQ_StringToAsset<ScriptContext::UI>); } -void InitialiseServerSquirrelUtilityFunctions(HMODULE baseAddress) +ON_DLL_LOAD_RELIESON("server.dll", ServerSharedScriptUtility, ServerSquirrel, (CModule module)) { - g_ServerSquirrelManager->AddFuncRegistration("asset", "StringToAsset", "string assetName", "", SQ_StringToAsset<ScriptContext::SERVER>); -} + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration( + "asset", "StringToAsset", "string assetName", "converts a given string to an asset", SQ_StringToAsset<ScriptContext::SERVER>); +}
\ No newline at end of file diff --git a/NorthstarDLL/scriptutility.h b/NorthstarDLL/scriptutility.h deleted file mode 100644 index 5fdd96c7..00000000 --- a/NorthstarDLL/scriptutility.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -void InitialiseClientSquirrelUtilityFunctions(HMODULE baseAddress); -void InitialiseServerSquirrelUtilityFunctions(HMODULE baseAddress); diff --git a/NorthstarDLL/serverauthentication.cpp b/NorthstarDLL/serverauthentication.cpp index 9fc41372..366a391a 100644 --- a/NorthstarDLL/serverauthentication.cpp +++ b/NorthstarDLL/serverauthentication.cpp @@ -1,99 +1,44 @@ #include "pch.h" #include "serverauthentication.h" +#include "limits.h" #include "cvar.h" #include "convar.h" -#include "hookutils.h" #include "masterserver.h" -#include "httplib.h" -#include "gameutils.h" +#include "serverpresence.h" +#include "hoststate.h" +#include "maxplayers.h" #include "bansystem.h" -#include "miscserverscript.h" #include "concommand.h" #include "dedicated.h" -#include <fstream> -#include <filesystem> -#include <thread> #include "nsprefix.h" -#include "nsmem.h" - -const char* AUTHSERVER_VERIFY_STRING = "I am a northstar server!"; +#include "tier0.h" +#include "r2engine.h" +#include "r2client.h" +#include "r2server.h" -// This convar defines whether to log all client commands -ConVar* Cvar_ns_should_log_all_clientcommands; - -// hook types - -typedef void* (*CBaseServer__ConnectClientType)( - void* server, - void* a2, - void* a3, - uint32_t a4, - uint32_t a5, - int32_t a6, - void* a7, - void* a8, - char* serverFilter, - void* a10, - char a11, - void* a12, - char a13, - char a14, - int64_t uid, - uint32_t a16, - uint32_t a17); -CBaseServer__ConnectClientType CBaseServer__ConnectClient; - -typedef bool (*CBaseClient__ConnectType)( - void* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, void* a7); -CBaseClient__ConnectType CBaseClient__Connect; - -typedef void (*CBaseClient__ActivatePlayerType)(void* self); -CBaseClient__ActivatePlayerType CBaseClient__ActivatePlayer; - -CBaseClient__DisconnectType CBaseClient__Disconnect; - -typedef char (*CGameClient__ExecuteStringCommandType)(void* self, uint32_t unknown, const char* pCommandString); -CGameClient__ExecuteStringCommandType CGameClient__ExecuteStringCommand; - -typedef char (*__fastcall CNetChan___ProcessMessagesType)(void* self, void* buf); -CNetChan___ProcessMessagesType CNetChan___ProcessMessages; - -typedef char (*CBaseClient__SendServerInfoType)(void* self); -CBaseClient__SendServerInfoType CBaseClient__SendServerInfo; +#include "httplib.h" -typedef bool (*ProcessConnectionlessPacketType)(void* a1, netpacket_t* packet); -ProcessConnectionlessPacketType ProcessConnectionlessPacket; +#include <fstream> +#include <filesystem> +#include <thread> -typedef void (*CServerGameDLL__OnReceivedSayTextMessageType)(void* self, unsigned int senderClientIndex, const char* message, char unknown); -CServerGameDLL__OnReceivedSayTextMessageType CServerGameDLL__OnReceivedSayTextMessage; +AUTOHOOK_INIT() -typedef void (*ConCommand__DispatchType)(ConCommand* command, const CCommand& args, void* a3); -ConCommand__DispatchType ConCommand__Dispatch; +const char* AUTHSERVER_VERIFY_STRING = "I am a northstar server!"; // global vars -ServerAuthenticationManager* g_ServerAuthenticationManager; - -ConVar* Cvar_ns_player_auth_port; -ConVar* Cvar_ns_erase_auth_info; -ConVar* CVar_ns_auth_allow_insecure; -ConVar* CVar_ns_auth_allow_insecure_write; -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_net_datablock_enabled; +ServerAuthenticationManager* g_pServerAuthentication; void ServerAuthenticationManager::StartPlayerAuthServer() { - if (m_runningPlayerAuthThread) + if (m_bRunningPlayerAuthThread) { - spdlog::warn("ServerAuthenticationManager::StartPlayerAuthServer was called while m_runningPlayerAuthThread is true"); + spdlog::warn("ServerAuthenticationManager::StartPlayerAuthServer was called while m_bRunningPlayerAuthThread is true"); return; } - m_runningPlayerAuthThread = true; + g_pServerPresence->SetAuthPort(Cvar_ns_player_auth_port->GetInt()); // set auth port for presence + m_bRunningPlayerAuthThread = true; // listen is a blocking call so thread this std::thread serverThread( @@ -101,61 +46,44 @@ void ServerAuthenticationManager::StartPlayerAuthServer() { // this is just a super basic way to verify that servers have ports open, masterserver will try to read this before ensuring // server is legit - m_playerAuthServer.Get( + m_PlayerAuthServer.Get( "/verify", [](const httplib::Request& request, httplib::Response& response) { response.set_content(AUTHSERVER_VERIFY_STRING, "text/plain"); }); - m_playerAuthServer.Post( + m_PlayerAuthServer.Post( "/authenticate_incoming_player", [this](const httplib::Request& request, httplib::Response& response) { - // can't just do request.remote_addr == Cvar_ns_masterserver_hostname->GetString() because the cvar can be a url, gotta - // resolve an ip from it for comparisons - // unsigned long remoteAddr = inet_addr(request.remote_addr.c_str()); - // - // char* addrPtr = Cvar_ns_masterserver_hostname->GetString(); - // char* typeStart = strstr(addrPtr, "://"); - // if (typeStart) - // addrPtr = typeStart + 3; - // hostent* resolvedRemoteAddr = gethostbyname((const char*)addrPtr); - - if (!request.has_param("id") || !request.has_param("authToken") || request.body.size() >= 65335 || + if (!request.has_param("id") || !request.has_param("authToken") || request.body.size() >= R2::PERSISTENCE_MAX_SIZE || !request.has_param("serverAuthToken") || strcmp( - g_MasterServerManager->m_ownServerAuthToken, + g_pMasterServerManager->m_sOwnServerAuthToken, request.get_param_value("serverAuthToken") - .c_str())) // || !resolvedRemoteAddr || ((in_addr**)resolvedRemoteAddr->h_addr_list)[0]->S_un.S_addr != - // remoteAddr) + .c_str())) { response.set_content("{\"success\":false}", "application/json"); return; } - // Log playername and UID from request - spdlog::info( - "Player \"{}\" with UID \"{}\" requested to join", - request.get_param_value("username").c_str(), - request.get_param_value("id").c_str()); - - AuthData newAuthData {}; - strncpy(newAuthData.uid, request.get_param_value("id").c_str(), sizeof(newAuthData.uid)); - newAuthData.uid[sizeof(newAuthData.uid) - 1] = 0; - - strncpy(newAuthData.username, request.get_param_value("username").c_str(), sizeof(newAuthData.username)); - newAuthData.username[sizeof(newAuthData.username) - 1] = 0; + RemoteAuthData newAuthData {}; + strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), request.get_param_value("id").c_str(), sizeof(newAuthData.uid) - 1); + strncpy_s( + newAuthData.username, + sizeof(newAuthData.username), request.get_param_value("username").c_str(), + sizeof(newAuthData.username) - 1); newAuthData.pdataSize = request.body.size(); newAuthData.pdata = new char[newAuthData.pdataSize]; memcpy(newAuthData.pdata, request.body.c_str(), newAuthData.pdataSize); - std::lock_guard<std::mutex> guard(m_authDataMutex); - m_authData.insert(std::make_pair(request.get_param_value("authToken"), newAuthData)); + std::lock_guard<std::mutex> guard(m_AuthDataMutex); + m_RemoteAuthenticationData.insert(std::make_pair(request.get_param_value("authToken"), newAuthData)); response.set_content("{\"success\":true}", "application/json"); }); - m_playerAuthServer.listen("0.0.0.0", Cvar_ns_player_auth_port->GetInt()); + m_PlayerAuthServer.listen("0.0.0.0", Cvar_ns_player_auth_port->GetInt()); }); serverThread.detach(); @@ -163,144 +91,125 @@ void ServerAuthenticationManager::StartPlayerAuthServer() void ServerAuthenticationManager::StopPlayerAuthServer() { - if (!m_runningPlayerAuthThread) + if (!m_bRunningPlayerAuthThread) { - spdlog::warn("ServerAuthenticationManager::StopPlayerAuthServer was called while m_runningPlayerAuthThread is false"); + spdlog::warn("ServerAuthenticationManager::StopPlayerAuthServer was called while m_bRunningPlayerAuthThread is false"); return; } - m_runningPlayerAuthThread = false; - m_playerAuthServer.stop(); + m_bRunningPlayerAuthThread = false; + m_PlayerAuthServer.stop(); } -char* ServerAuthenticationManager::VerifyPlayerName(void* player, char* authToken, char* name) +void ServerAuthenticationManager::AddPlayerData(R2::CBaseClient* player, const char* pToken) { - std::lock_guard<std::mutex> guard(m_authDataMutex); + PlayerAuthenticationData additionalData; + additionalData.pdataSize = m_RemoteAuthenticationData[pToken].pdataSize; + additionalData.usingLocalPdata = player->m_iPersistenceReady == R2::ePersistenceReady::READY_INSECURE; - if (!m_authData.empty() && m_authData.count(std::string(authToken))) + m_PlayerAuthenticationData.insert(std::make_pair(player, additionalData)); +} + +void ServerAuthenticationManager::VerifyPlayerName(R2::CBaseClient* player, char* authToken, char* name) +{ + std::lock_guard<std::mutex> guard(m_AuthDataMutex); + + if (!m_RemoteAuthenticationData.empty() && m_RemoteAuthenticationData.count(std::string(authToken))) { - AuthData authData = m_authData[authToken]; + RemoteAuthData authData = m_RemoteAuthenticationData[authToken]; bool nameAccepted = (!*authData.username || !strcmp(name, authData.username)); - if (!nameAccepted && g_MasterServerManager->m_bRequireClientAuth && !CVar_ns_auth_allow_insecure->GetInt()) + if (!nameAccepted && g_pMasterServerManager->m_bRequireClientAuth && !CVar_ns_auth_allow_insecure->GetInt()) { // limit name length to 64 characters just in case something changes, this technically shouldn't be needed given the master // server gets usernames from origin but we have it just in case - strncpy(name, authData.username, 64); - name[63] = 0; + strncpy_s(name, 64, authData.username, 63); } } - return name; } -bool ServerAuthenticationManager::AuthenticatePlayer(void* player, int64_t uid, char* authToken) +bool ServerAuthenticationManager::CheckDuplicateAccounts(R2::CBaseClient* player) +{ + if (m_bAllowDuplicateAccounts) + return true; + + bool bHasUidPlayer = false; + for (int i = 0; i < R2::GetMaxPlayers(); i++) + if (&R2::g_pClientArray[i] != player && !strcmp(R2::g_pClientArray[i].m_UID, player->m_UID)) + return false; + + return true; +} + +bool ServerAuthenticationManager::AuthenticatePlayer(R2::CBaseClient* player, uint64_t uid, char* authToken) { std::string strUid = std::to_string(uid); - std::lock_guard<std::mutex> guard(m_authDataMutex); + std::lock_guard<std::mutex> guard(m_AuthDataMutex); + + // copy uuid + strcpy(player->m_UID, strUid.c_str()); bool authFail = true; - if (!m_authData.empty() && m_authData.count(std::string(authToken))) + if (!m_RemoteAuthenticationData.empty() && m_RemoteAuthenticationData.count(std::string(authToken))) { - // use stored auth data - AuthData authData = m_authData[authToken]; + if (!CheckDuplicateAccounts(player)) + return false; - // Log playnername and UID from request - spdlog::info("Comparing connecting UID \"{}\" against stored UID from ms auth request \"{}\"", strUid.c_str(), authData.uid); + // use stored auth data + RemoteAuthData authData = m_RemoteAuthenticationData[authToken]; if (!strcmp(strUid.c_str(), authData.uid)) // connecting client's uid is the same as auth's uid { - authFail = false; - // uuid - strcpy((char*)player + 0xF500, strUid.c_str()); - - // reset from disk if we're doing that - if (m_bForceReadLocalPlayerPersistenceFromDisk && !strcmp(authData.uid, g_LocalPlayerUserID)) - { - std::fstream pdataStream(GetNorthstarPrefix() + "/placeholder_playerdata.pdata", std::ios_base::in); - - if (!pdataStream.fail()) - { - // get file length - pdataStream.seekg(0, pdataStream.end); - auto length = pdataStream.tellg(); - pdataStream.seekg(0, pdataStream.beg); - - // copy pdata into buffer - pdataStream.read((char*)player + 0x4FA, length); - } - else // fallback to remote pdata if no local default - memcpy((char*)player + 0x4FA, authData.pdata, authData.pdataSize); - } - else + // if we're resetting let script handle the reset + if (!m_bForceResetLocalPlayerPersistence || strcmp(authData.uid, R2::g_pLocalPlayerUserID)) { // copy pdata into buffer - memcpy((char*)player + 0x4FA, authData.pdata, authData.pdataSize); + memcpy(player->m_PersistenceBuffer, authData.pdata, authData.pdataSize); } - // set persistent data as ready, we use 0x4 internally to mark the client as using remote persistence - *((char*)player + 0x4a0) = (char)0x4; + // set persistent data as ready + player->m_iPersistenceReady = R2::ePersistenceReady::READY_REMOTE; + authFail = false; } } if (authFail) { - // set persistent data as ready, we use 0x3 internally to mark the client as using local persistence - *((char*)player + 0x4a0) = (char)0x3; - - // no auth data and insecure connections aren't allowed, so dc the client - if (!CVar_ns_auth_allow_insecure->GetBool() && strncmp(GetCurrentPlaylistName(), "solo", 5) != 0) + if (CVar_ns_auth_allow_insecure->GetBool()) + { + // set persistent data as ready + // note: actual placeholder persistent data is populated in script with InitPersistentData() + player->m_iPersistenceReady = R2::ePersistenceReady::READY_INSECURE; + return true; + } + else return false; - - // insecure connections are allowed, try reading from disk - // uuid - strcpy((char*)player + 0xF500, strUid.c_str()); - - // try reading pdata file for player - std::string pdataPath = GetNorthstarPrefix() + "/playerdata_"; - pdataPath += strUid; - pdataPath += ".pdata"; - - std::fstream pdataStream(pdataPath, std::ios_base::in); - if (pdataStream.fail()) // file doesn't exist, use placeholder - pdataStream = std::fstream(GetNorthstarPrefix() + "/placeholder_playerdata.pdata", std::ios_base::in); - - // get file length - pdataStream.seekg(0, pdataStream.end); - auto length = pdataStream.tellg(); - pdataStream.seekg(0, pdataStream.beg); - - // copy pdata into buffer - pdataStream.read((char*)player + 0x4FA, length); - - pdataStream.close(); } return true; // auth successful, client stays on } -bool ServerAuthenticationManager::RemovePlayerAuthData(void* player) +bool ServerAuthenticationManager::RemovePlayerAuthData(R2::CBaseClient* player) { - if (!Cvar_ns_erase_auth_info->GetBool()) + 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((char*)player + 0xF500, g_LocalPlayerUserID)) + if (m_bNeedLocalAuthForNewgame && !strcmp(player->m_UID, R2::g_pLocalPlayerUserID)) return false; // we don't have our auth token at this point, so lookup authdata by uid - for (auto& auth : m_authData) + for (auto& auth : m_RemoteAuthenticationData) { - if (!strcmp((char*)player + 0xF500, auth.second.uid)) + if (!strcmp(player->m_UID, auth.second.uid)) { - // Log UID - spdlog::info("Erasing auth data from 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); + std::lock_guard<std::mutex> guard(m_AuthDataMutex); delete[] auth.second.pdata; - m_authData.erase(auth.first); + m_RemoteAuthenticationData.erase(auth.first); return true; } } @@ -308,13 +217,12 @@ bool ServerAuthenticationManager::RemovePlayerAuthData(void* player) return false; } -void ServerAuthenticationManager::WritePersistentData(void* player) +void ServerAuthenticationManager::WritePersistentData(R2::CBaseClient* player) { - // we use 0x4 internally to mark clients as using remote persistence - if (*((char*)player + 0x4A0) == (char)0x4) + if (player->m_iPersistenceReady == R2::ePersistenceReady::READY_REMOTE) { - g_MasterServerManager->WritePlayerPersistentData( - (char*)player + 0xF500, (char*)player + 0x4FA, m_additionalPlayerData[player].pdataSize); + g_pMasterServerManager->WritePlayerPersistentData( + player->m_UID, (const char*)player->m_PersistenceBuffer, m_PlayerAuthenticationData[player].pdataSize); } else if (CVar_ns_auth_allow_insecure_write->GetBool()) { @@ -322,29 +230,15 @@ void ServerAuthenticationManager::WritePersistentData(void* player) } } -bool ServerAuthenticationManager::CheckPlayerChatRatelimit(void* player) -{ - if (Plat_FloatTime() - m_additionalPlayerData[player].lastSayTextLimitStart >= 1.0) - { - m_additionalPlayerData[player].lastSayTextLimitStart = Plat_FloatTime(); - m_additionalPlayerData[player].sayTextLimitCount = 0; - } - - if (m_additionalPlayerData[player].sayTextLimitCount >= Cvar_sv_max_chat_messages_per_sec->GetInt()) - return false; - - m_additionalPlayerData[player].sayTextLimitCount++; - return true; -} - // 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 cbaseclient::connect -char* nextPlayerToken; -uint64_t nextPlayerUid; +// 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; -void* CBaseServer__ConnectClientHook( +AUTOHOOK(CBaseServer__ConnectClient, engine.dll + 0x114430, +void*,, ( void* server, void* a2, void* a3, @@ -361,395 +255,135 @@ void* CBaseServer__ConnectClientHook( char a14, int64_t uid, uint32_t a16, - uint32_t a17) + uint32_t a17)) { // auth tokens are sent with serverfilter, can't be accessed from player struct to my knowledge, so have to do this here - nextPlayerToken = serverFilter; - nextPlayerUid = uid; - - // Random UID log - spdlog::info("CBaseServer__ConnectClientHook says UID \"{}\"", uid); + pNextPlayerToken = serverFilter; + iNextPlayerUid = uid; return CBaseServer__ConnectClient(server, a2, a3, a4, a5, a6, a7, a8, serverFilter, a10, a11, a12, a13, a14, uid, a16, a17); } -bool CBaseClient__ConnectHook(void* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, void* a7) +AUTOHOOK(CBaseClient__Connect, engine.dll + 0x101740, +bool,, (R2::CBaseClient* self, char* name, void* netchan_ptr_arg, char b_fake_player_arg, void* a5, char* Buffer, void* a7)) { // try changing name before all else - name = g_ServerAuthenticationManager->VerifyPlayerName(self, nextPlayerToken, name); + g_pServerAuthentication->VerifyPlayerName(self, pNextPlayerToken, name); // try to auth player, dc if it fails - // we connect irregardless of auth, because returning bad from this function can fuck client state p bad + // we connect regardless of auth, because returning bad from this function can fuck client state p bad bool ret = CBaseClient__Connect(self, name, netchan_ptr_arg, b_fake_player_arg, a5, Buffer, a7); - - // Another UID log - spdlog::info("CBaseClient__ConnectHook says UID \"{}\"", nextPlayerUid); - if (!ret) return ret; - if (!g_ServerBanSystem->IsUIDAllowed(nextPlayerUid)) + if (!g_pBanSystem->IsUIDAllowed(iNextPlayerUid)) { - CBaseClient__Disconnect(self, 1, "Banned from server"); + R2::CBaseClient__Disconnect(self, 1, "Banned from server"); return ret; } if (strlen(name) >= 64) // fix for name overflow bug - CBaseClient__Disconnect(self, 1, "Invalid name"); - else if ( - !g_ServerAuthenticationManager->AuthenticatePlayer(self, nextPlayerUid, nextPlayerToken) && - g_MasterServerManager->m_bRequireClientAuth) - CBaseClient__Disconnect(self, 1, "Authentication Failed"); - - if (!g_ServerAuthenticationManager->m_additionalPlayerData.count(self)) - { - AdditionalPlayerData additionalData; - additionalData.pdataSize = g_ServerAuthenticationManager->m_authData[nextPlayerToken].pdataSize; - additionalData.usingLocalPdata = *((char*)self + 0x4a0) == (char)0x3; + R2::CBaseClient__Disconnect(self, 1, "Invalid name"); + else if (!g_pServerAuthentication->AuthenticatePlayer(self, iNextPlayerUid, pNextPlayerToken) && + g_pServerAuthentication->m_bRequireClientAuth) + R2::CBaseClient__Disconnect(self, 1, "Authentication Failed"); - g_ServerAuthenticationManager->m_additionalPlayerData.insert(std::make_pair(self, additionalData)); - - g_ServerAuthenticationManager->m_additionalPlayerData[self].uid = nextPlayerUid; - } + g_pServerAuthentication->AddPlayerData(self, pNextPlayerToken); + g_pServerLimits->AddPlayer(self); return ret; } -void CBaseClient__ActivatePlayerHook(void* self) +AUTOHOOK(CBaseClient__ActivatePlayer, engine.dll + 0x100F80, +void,, (R2::CBaseClient* self)) { - bool uidMatches = false; - if (g_ServerAuthenticationManager->m_additionalPlayerData.count(self)) - { - std::string strUid = std::to_string(g_ServerAuthenticationManager->m_additionalPlayerData[self].uid); - if (!strcmp(strUid.c_str(), (char*)self + 0xF500)) // connecting client's uid is the same as auth's uid - { - uidMatches = true; - } - } - if (!uidMatches) - { - CBaseClient__Disconnect(self, 1, "Authentication Failed"); - return; - } - // 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 (*((char*)self + 0x4A0) >= (char)0x3 && !g_ServerAuthenticationManager->RemovePlayerAuthData(self)) + if (self->m_iPersistenceReady >= R2::ePersistenceReady::READY && !g_pServerAuthentication->RemovePlayerAuthData(self)) { - g_ServerAuthenticationManager->m_bForceReadLocalPlayerPersistenceFromDisk = false; - g_ServerAuthenticationManager->WritePersistentData(self); - g_MasterServerManager->UpdateServerPlayerCount(g_ServerAuthenticationManager->m_additionalPlayerData.size()); + g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = false; + g_pServerAuthentication->WritePersistentData(self); + g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size()); } - // Log UID - spdlog::info("In CBaseClient__ActivatePlayerHook, activating UID \"{}\"", (char*)self + 0xF500); CBaseClient__ActivatePlayer(self); } -void CBaseClient__DisconnectHook(void* self, uint32_t unknownButAlways1, const char* reason, ...) +AUTOHOOK(_CBaseClient__Disconnect, engine.dll + 0x1012C0, +void,, (R2::CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...)) { // have to manually format message because can't pass varargs to original func char buf[1024]; va_list va; - va_start(va, reason); - vsprintf(buf, reason, 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(reason, "Connection closing")) + if (strcmp(pReason, "Connection closing")) { - spdlog::info("Player {} disconnected: \"{}\"", (char*)self + 0x16, buf); + spdlog::info("Player {} disconnected: \"{}\"", self->m_Name, buf); // dcing, write persistent data - if (g_ServerAuthenticationManager->m_additionalPlayerData[self].needPersistenceWriteOnLeave) - g_ServerAuthenticationManager->WritePersistentData(self); - g_ServerAuthenticationManager->RemovePlayerAuthData(self); // won't do anything 99% of the time, but just in case + if (g_pServerAuthentication->m_PlayerAuthenticationData[self].needPersistenceWriteOnLeave) + g_pServerAuthentication->WritePersistentData(self); + g_pServerAuthentication->RemovePlayerAuthData(self); // won't do anything 99% of the time, but just in case } - if (g_ServerAuthenticationManager->m_additionalPlayerData.count(self)) + if (g_pServerAuthentication->m_PlayerAuthenticationData.count(self)) { - g_ServerAuthenticationManager->m_additionalPlayerData.erase(self); - g_MasterServerManager->UpdateServerPlayerCount(g_ServerAuthenticationManager->m_additionalPlayerData.size()); + g_pServerAuthentication->m_PlayerAuthenticationData.erase(self); + g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size()); } - CBaseClient__Disconnect(self, unknownButAlways1, buf); + _CBaseClient__Disconnect(self, unknownButAlways1, buf); } -// maybe this should be done outside of auth code, but effort to refactor rn and it sorta fits -typedef bool (*CCommand__TokenizeType)(CCommand& self, const char* pCommandString, cmd_source_t commandSource); -CCommand__TokenizeType CCommand__Tokenize; - -char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const char* pCommandString) +void ConCommand_ns_resetpersistence(const CCommand& args) { - // Only log clientcommands if the convar `ns_should_log_all_clientcommands` equals 1 - if (Cvar_ns_should_log_all_clientcommands->GetBool()) - { - spdlog::info("{} (UID: {}) executed command: \"{}\"", (char*)self + 0x16, (char*)self + 0xF500, pCommandString); - } - - if (CVar_sv_quota_stringcmdspersecond->GetInt() != -1) - { - // note: this isn't super perfect, legit clients can trigger it in lobby, mostly good enough tho imo - // https://github.com/perilouswithadollarsign/cstrike15_src/blob/f82112a2388b841d72cb62ca48ab1846dfcc11c8/engine/sv_client.cpp#L1513 - if (Plat_FloatTime() - g_ServerAuthenticationManager->m_additionalPlayerData[self].lastClientCommandQuotaStart >= 1.0) - { - // reset quota - g_ServerAuthenticationManager->m_additionalPlayerData[self].lastClientCommandQuotaStart = Plat_FloatTime(); - g_ServerAuthenticationManager->m_additionalPlayerData[self].numClientCommandsInQuota = 0; - } - - g_ServerAuthenticationManager->m_additionalPlayerData[self].numClientCommandsInQuota++; - if (g_ServerAuthenticationManager->m_additionalPlayerData[self].numClientCommandsInQuota > - CVar_sv_quota_stringcmdspersecond->GetInt()) - { - // too many stringcmds, dc player - CBaseClient__Disconnect(self, 1, "Sent too many stringcmd commands"); - return false; - } - } - - // verify the command we're trying to execute is FCVAR_CLIENTCMD_CAN_EXECUTE, 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_CLIENTCMD_CAN_EXECUTE)) - { - // ensure FCVAR_GAMEDLL concommands without FCVAR_CLIENTCMD_CAN_EXECUTE can't be executed by remote clients - if (IsDedicatedServer()) - return false; - - if (strcmp((char*)self + 0xF500, g_LocalPlayerUserID)) - return false; - } - - // todo later, basically just limit to CVar_sv_quota_stringcmdspersecond->GetInt() stringcmds per client per second - return CGameClient__ExecuteStringCommand(self, unknown, pCommandString); -} - -char __fastcall CNetChan___ProcessMessagesHook(void* self, void* buf) -{ - 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 - void* sender = *(void**)((char*)self + 368); - - // if no sender, return - // relatively certain this is fine? - if (!sender || !g_ServerAuthenticationManager->m_additionalPlayerData.count(sender)) - return ret; - - // reset every second - if (startTime - g_ServerAuthenticationManager->m_additionalPlayerData[sender].lastNetChanProcessingLimitStart >= 1.0 || - g_ServerAuthenticationManager->m_additionalPlayerData[sender].lastNetChanProcessingLimitStart == -1.0) - { - g_ServerAuthenticationManager->m_additionalPlayerData[sender].lastNetChanProcessingLimitStart = startTime; - g_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime = 0.0; - } - g_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime += - (Plat_FloatTime() * 1000) - (startTime * 1000); - - if (g_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime >= - 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_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime, - Cvar_net_chan_limit_msec_per_sec->GetInt()); - - // nonzero = kick, 0 = warn, but never kick local player - if (Cvar_net_chan_limit_mode->GetInt() && strcmp(g_LocalPlayerUserID, (char*)sender + 0xF500)) - { - CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit"); - return false; - } - } - } - - return ret; -} - -bool bWasWritingStringTableSuccessful; - -void CBaseClient__SendServerInfoHook(void* self) -{ - bWasWritingStringTableSuccessful = true; - CBaseClient__SendServerInfo(self); - if (!bWasWritingStringTableSuccessful) - CBaseClient__Disconnect( - self, 1, "Overflowed CNetworkStringTableContainer::WriteBaselines, try restarting your client and reconnecting"); -} - -bool ProcessConnectionlessPacketHook(void* a1, netpacket_t* packet) -{ - if (packet->adr.type == NA_IP && - (!(packet->data[4] == 'N' && Cvar_net_datablock_enabled->GetBool()) || !Cvar_net_datablock_enabled->GetBool())) - { - // bad lookup: optimise later tm - UnconnectedPlayerSendData* sendData = nullptr; - for (UnconnectedPlayerSendData& foundSendData : g_ServerAuthenticationManager->m_unconnectedPlayerSendData) - { - if (!memcmp(packet->adr.ip, foundSendData.ip, 16)) - { - sendData = &foundSendData; - break; - } - } - - if (!sendData) - { - sendData = &g_ServerAuthenticationManager->m_unconnectedPlayerSendData.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 >= Cvar_sv_querylimit_per_sec->GetInt()) - { - spdlog::warn( - "Client went over connectionless ratelimit of {} per sec with packet of type {}", - Cvar_sv_querylimit_per_sec->GetInt(), - packet->data[4]); - - // timeout for a minute - sendData->timeoutEnd = Plat_FloatTime() + 60.0; - return false; - } - } - - return ProcessConnectionlessPacket(a1, packet); -} - -void ResetPdataCommand(const CCommand& args) -{ - if (*sv_m_State == server_state_t::ss_active) + if (*R2::g_pServerState == R2::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_ServerAuthenticationManager->m_bForceReadLocalPlayerPersistenceFromDisk = true; + g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = true; } -void InitialiseServerAuthentication(HMODULE baseAddress) +ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, (ConCommand, ConVar), (CModule module)) { - g_ServerAuthenticationManager = new ServerAuthenticationManager; + AUTOHOOK_DISPATCH() - Cvar_ns_erase_auth_info = + g_pServerAuthentication = new ServerAuthenticationManager; + + g_pServerAuthentication->Cvar_ns_player_auth_port = new ConVar("ns_player_auth_port", "8081", FCVAR_GAMEDLL, ""); + 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"); - CVar_ns_auth_allow_insecure = + 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"); - CVar_ns_auth_allow_insecure_write = new ConVar( + 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"); - // literally just stolen from a fix valve used in csgo - 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"); - // https://blog.counter-strike.net/index.php/2019/07/24922/ but different because idk how to check what current tick number is - Cvar_net_chan_limit_mode = - new ConVar("net_chan_limit_mode", "0", FCVAR_GAMEDLL, "The mode for netchan processing limits: 0 = log, 1 = kick"); - 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"); - Cvar_ns_should_log_all_clientcommands = - new ConVar("ns_should_log_all_clientcommands", "0", FCVAR_NONE, "Whether to log all clientcommands"); - Cvar_ns_player_auth_port = new ConVar("ns_player_auth_port", "8081", FCVAR_GAMEDLL, ""); - Cvar_sv_querylimit_per_sec = new ConVar("sv_querylimit_per_sec", "15", FCVAR_GAMEDLL, ""); - Cvar_sv_max_chat_messages_per_sec = new ConVar("sv_max_chat_messages_per_sec", "5", FCVAR_GAMEDLL, ""); - - Cvar_net_datablock_enabled = g_pCVar->FindVar("net_datablock_enabled"); - - RegisterConCommand("ns_resetpersistence", ResetPdataCommand, "resets your pdata when you next enter the lobby", FCVAR_NONE); - - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x114430, &CBaseServer__ConnectClientHook, reinterpret_cast<LPVOID*>(&CBaseServer__ConnectClient)); - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x101740, &CBaseClient__ConnectHook, reinterpret_cast<LPVOID*>(&CBaseClient__Connect)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x100F80, &CBaseClient__ActivatePlayerHook, reinterpret_cast<LPVOID*>(&CBaseClient__ActivatePlayer)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x1012C0, &CBaseClient__DisconnectHook, reinterpret_cast<LPVOID*>(&CBaseClient__Disconnect)); - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1022E0, - &CGameClient__ExecuteStringCommandHook, - reinterpret_cast<LPVOID*>(&CGameClient__ExecuteStringCommand)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x2140A0, &CNetChan___ProcessMessagesHook, reinterpret_cast<LPVOID*>(&CNetChan___ProcessMessages)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x104FB0, &CBaseClient__SendServerInfoHook, reinterpret_cast<LPVOID*>(&CBaseClient__SendServerInfo)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x117800, &ProcessConnectionlessPacketHook, reinterpret_cast<LPVOID*>(&ProcessConnectionlessPacket)); - - CCommand__Tokenize = (CCommand__TokenizeType)((char*)baseAddress + 0x418380); - - uintptr_t ba = (uintptr_t)baseAddress; + + 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 - { - NSMem::BytePatch( - ba + 0x114655, - "EB" // jz => jmp - ); - } + module.Offset(0x114655).Patch("EB"); // patch to disable fairfight marking players as cheaters and kicking them - { - NSMem::BytePatch( - ba + 0x101012, - "E9 90 00" // jz => jmp - ); - } + module.Offset(0x101012).Patch("E9 90 00"); - // patch to allow same of multiple account - if (CommandLine()->CheckParm("-allowdupeaccounts")) + if (Tier0::CommandLine()->CheckParm("-allowdupeaccounts")) { - NSMem::BytePatch( - ba + 0x114510, - "EB" // jz => jmp - ); - } - - // patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails - { - uintptr_t writeAddress = (uintptr_t)(&bWasWritingStringTableSuccessful - (ba + 0x234EDC)); - - auto addr = ba + 0x234ED2; - NSMem::BytePatch(addr, "C7 05"); - NSMem::BytePatch(addr + 2, (BYTE*)&writeAddress, sizeof(writeAddress)); - - NSMem::BytePatch(addr + 6, "00 00 00 00"); + // patch to allow same of multiple account + module.Offset(0x114510).Patch("EB"); - NSMem::NOP(addr + 10, 5); + g_pServerAuthentication->m_bAllowDuplicateAccounts = true; } } diff --git a/NorthstarDLL/serverauthentication.h b/NorthstarDLL/serverauthentication.h index e79577e6..b010e6b1 100644 --- a/NorthstarDLL/serverauthentication.h +++ b/NorthstarDLL/serverauthentication.h @@ -1,10 +1,11 @@ #pragma once #include "convar.h" #include "httplib.h" +#include "r2engine.h" #include <unordered_map> #include <string> -struct AuthData +struct RemoteAuthData { char uid[33]; char username[64]; @@ -14,99 +15,42 @@ struct AuthData size_t pdataSize; }; -struct AdditionalPlayerData +struct PlayerAuthenticationData { bool usingLocalPdata; size_t pdataSize; bool needPersistenceWriteOnLeave = true; - - double lastClientCommandQuotaStart = -1.0; - int numClientCommandsInQuota = 0; - - double lastNetChanProcessingLimitStart = -1.0; - double netChanProcessingLimitTime = 0.0; - - double lastSayTextLimitStart = -1.0; - int sayTextLimitCount = 0; - - uint64_t uid; -}; - -#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) - -struct UnconnectedPlayerSendData -{ - char ip[16]; - double lastQuotaStart = 0.0; - int packetCount = 0; - double timeoutEnd = -1.0; }; class ServerAuthenticationManager { private: - httplib::Server m_playerAuthServer; + httplib::Server m_PlayerAuthServer; public: - std::mutex m_authDataMutex; - std::unordered_map<std::string, AuthData> m_authData; - std::unordered_map<void*, AdditionalPlayerData> m_additionalPlayerData; - std::vector<UnconnectedPlayerSendData> m_unconnectedPlayerSendData; - bool m_runningPlayerAuthThread = false; + ConVar* Cvar_ns_player_auth_port; + 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<R2::CBaseClient*, PlayerAuthenticationData> m_PlayerAuthenticationData; + bool m_bRequireClientAuth = true; + bool m_bAllowDuplicateAccounts = false; + bool m_bRunningPlayerAuthThread = false; bool m_bNeedLocalAuthForNewgame = false; - bool m_bForceReadLocalPlayerPersistenceFromDisk = false; + bool m_bForceResetLocalPlayerPersistence = false; public: void StartPlayerAuthServer(); void StopPlayerAuthServer(); - bool AuthenticatePlayer(void* player, int64_t uid, char* authToken); - char* VerifyPlayerName(void* player, char* authToken, char* name); - bool RemovePlayerAuthData(void* player); - void WritePersistentData(void* player); - bool CheckPlayerChatRatelimit(void* player); + void AddPlayerData(R2::CBaseClient* player, const char* pToken); + bool CheckDuplicateAccounts(R2::CBaseClient* player); + bool AuthenticatePlayer(R2::CBaseClient* player, uint64_t uid, char* authToken); + void VerifyPlayerName(R2::CBaseClient* player, char* authToken, char* name); + bool RemovePlayerAuthData(R2::CBaseClient* player); + void WritePersistentData(R2::CBaseClient* player); }; -typedef void (*CBaseClient__DisconnectType)(void* self, uint32_t unknownButAlways1, const char* reason, ...); -extern CBaseClient__DisconnectType CBaseClient__Disconnect; - -void InitialiseServerAuthentication(HMODULE baseAddress); - -extern ServerAuthenticationManager* g_ServerAuthenticationManager; -extern ConVar* Cvar_ns_player_auth_port; +extern ServerAuthenticationManager* g_pServerAuthentication; diff --git a/NorthstarDLL/serverchathooks.cpp b/NorthstarDLL/serverchathooks.cpp index 2f5be5c1..bc9098dc 100644 --- a/NorthstarDLL/serverchathooks.cpp +++ b/NorthstarDLL/serverchathooks.cpp @@ -1,14 +1,16 @@ #include "pch.h" #include "serverchathooks.h" +#include "limits.h" +#include "squirrel.h" +#include "r2server.h" + #include <rapidjson/document.h> #include <rapidjson/stringbuffer.h> #include <rapidjson/writer.h> -#include "serverauthentication.h" -#include "squirrel.h" -#include "miscserverscript.h" + +AUTOHOOK_INIT() class CServerGameDLL; -class CBasePlayer; class CRecipientFilter { @@ -17,79 +19,53 @@ class CRecipientFilter CServerGameDLL* g_pServerGameDLL; -typedef void(__fastcall* CServerGameDLL__OnReceivedSayTextMessageType)( +void(__fastcall* CServerGameDLL__OnReceivedSayTextMessage)( CServerGameDLL* self, unsigned int senderPlayerId, const char* text, int channelId); -CServerGameDLL__OnReceivedSayTextMessageType CServerGameDLL__OnReceivedSayTextMessage; -CServerGameDLL__OnReceivedSayTextMessageType CServerGameDLL__OnReceivedSayTextMessageHookBase; - -typedef CBasePlayer*(__fastcall* UTIL_PlayerByIndexType)(int playerIndex); -UTIL_PlayerByIndexType UTIL_PlayerByIndex; - -typedef void(__fastcall* CRecipientFilter__ConstructType)(CRecipientFilter* self); -CRecipientFilter__ConstructType CRecipientFilter__Construct; - -typedef void(__fastcall* CRecipientFilter__DestructType)(CRecipientFilter* self); -CRecipientFilter__DestructType CRecipientFilter__Destruct; - -typedef void(__fastcall* CRecipientFilter__AddAllPlayersType)(CRecipientFilter* self); -CRecipientFilter__AddAllPlayersType CRecipientFilter__AddAllPlayers; - -typedef void(__fastcall* CRecipientFilter__AddRecipientType)(CRecipientFilter* self, const CBasePlayer* player); -CRecipientFilter__AddRecipientType CRecipientFilter__AddRecipient; -typedef void(__fastcall* CRecipientFilter__MakeReliableType)(CRecipientFilter* self); -CRecipientFilter__MakeReliableType CRecipientFilter__MakeReliable; +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 R2::CBasePlayer* player); +void(__fastcall* CRecipientFilter__MakeReliable)(CRecipientFilter* self); -typedef void(__fastcall* UserMessageBeginType)(CRecipientFilter* filter, const char* messagename); -UserMessageBeginType UserMessageBegin; +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); -typedef void(__fastcall* MessageEndType)(); -MessageEndType MessageEnd; +bool bShouldCallSayTextHook = false; -typedef void(__fastcall* MessageWriteByteType)(int iValue); -MessageWriteByteType MessageWriteByte; - -typedef void(__fastcall* MessageWriteStringType)(const char* sz); -MessageWriteStringType MessageWriteString; - -typedef void(__fastcall* MessageWriteBoolType)(bool bValue); -MessageWriteBoolType MessageWriteBool; - -bool isSkippingHook = false; - -static void CServerGameDLL__OnReceivedSayTextMessageHook(CServerGameDLL* self, unsigned int senderPlayerId, const char* text, bool isTeam) +AUTOHOOK(_CServerGameDLL__OnReceivedSayTextMessage, server.dll + 0x1595C0, +void,, (CServerGameDLL* self, unsigned int senderPlayerId, const char* text, bool isTeam)) { // 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 (isSkippingHook) + if (bShouldCallSayTextHook) { - isSkippingHook = false; - CServerGameDLL__OnReceivedSayTextMessageHookBase(self, senderPlayerId, text, isTeam); + bShouldCallSayTextHook = false; + _CServerGameDLL__OnReceivedSayTextMessage(self, senderPlayerId, text, isTeam); return; } - void* sender = GetPlayerByIndex(senderPlayerId - 1); - // check chat ratelimits - if (!g_ServerAuthenticationManager->CheckPlayerChatRatelimit(sender)) - { + if (!g_pServerLimits->CheckChatLimits(&R2::g_pClientArray[senderPlayerId - 1])) return; - } - if (g_ServerSquirrelManager->setupfunc("CServerGameDLL_ProcessMessageStartThread") != SQRESULT_ERROR) + if (g_pSquirrel<ScriptContext::SERVER>->setupfunc("CServerGameDLL_ProcessMessageStartThread") != SQRESULT_ERROR) { - g_ServerSquirrelManager->pusharg((int)senderPlayerId - 1); - g_ServerSquirrelManager->pusharg(text); - g_ServerSquirrelManager->pusharg(isTeam); - g_ServerSquirrelManager->call(3); + g_pSquirrel<ScriptContext::SERVER>->pushinteger(g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm, (int)senderPlayerId - 1); + g_pSquirrel<ScriptContext::SERVER>->pushstring(g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm, text); + g_pSquirrel<ScriptContext::SERVER>->pushbool(g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm, isTeam); + g_pSquirrel<ScriptContext::SERVER>->call(g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm, 3); } else - CServerGameDLL__OnReceivedSayTextMessageHookBase(self, senderPlayerId, text, isTeam); + _CServerGameDLL__OnReceivedSayTextMessage(self, senderPlayerId, text, isTeam); } void ChatSendMessage(unsigned int playerIndex, const char* text, bool isteam) { - isSkippingHook = true; + bShouldCallSayTextHook = true; CServerGameDLL__OnReceivedSayTextMessage( g_pServerGameDLL, // Ensure the first bit isn't set, since this indicates a custom message @@ -100,10 +76,10 @@ void ChatSendMessage(unsigned int playerIndex, const char* text, bool isteam) void ChatBroadcastMessage(int fromPlayerIndex, int toPlayerIndex, const char* text, bool isTeam, bool isDead, CustomMessageType messageType) { - CBasePlayer* toPlayer = NULL; + R2::CBasePlayer* toPlayer = NULL; if (toPlayerIndex >= 0) { - toPlayer = UTIL_PlayerByIndex(toPlayerIndex + 1); + toPlayer = R2::UTIL_PlayerByIndex(toPlayerIndex + 1); if (toPlayer == NULL) return; } @@ -111,8 +87,7 @@ void ChatBroadcastMessage(int fromPlayerIndex, int toPlayerIndex, const char* te // Build a new string where the first byte is the message type char sendText[256]; sendText[0] = (char)messageType; - strncpy(sendText + 1, text, 255); - sendText[255] = 0; + 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); @@ -139,29 +114,31 @@ void ChatBroadcastMessage(int fromPlayerIndex, int toPlayerIndex, const char* te CRecipientFilter__Destruct(&filter); } -SQRESULT SQ_SendMessage(void* sqvm) +// void function NSSendMessage( int playerIndex, string text, bool isTeam ) +SQRESULT SQ_SendMessage(HSquirrelVM* sqvm) { - int playerIndex = ServerSq_getinteger(sqvm, 1); - const char* text = ServerSq_getstring(sqvm, 2); - bool isTeam = ServerSq_getbool(sqvm, 3); + 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; } -SQRESULT SQ_BroadcastMessage(void* sqvm) +// void function NSBroadcastMessage( int fromPlayerIndex, int toPlayerIndex, string text, bool isTeam, bool isDead, int messageType ) +SQRESULT SQ_BroadcastMessage(HSquirrelVM* sqvm) { - int fromPlayerIndex = ServerSq_getinteger(sqvm, 1); - int toPlayerIndex = ServerSq_getinteger(sqvm, 2); - const char* text = ServerSq_getstring(sqvm, 3); - bool isTeam = ServerSq_getbool(sqvm, 4); - bool isDead = ServerSq_getbool(sqvm, 5); - int messageType = ServerSq_getinteger(sqvm, 6); + 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) { - ServerSq_pusherror(sqvm, fmt::format("Invalid message type {}", messageType).c_str()); + g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, fmt::format("Invalid message type {}", messageType).c_str()); return SQRESULT_ERROR; } @@ -170,37 +147,31 @@ SQRESULT SQ_BroadcastMessage(void* sqvm) return SQRESULT_NULL; } -void InitialiseServerChatHooks_Engine(HMODULE baseAddress) +ON_DLL_LOAD("engine.dll", EngineServerChatHooks, (CModule module)) { - g_pServerGameDLL = (CServerGameDLL*)((char*)baseAddress + 0x13F0AA98); + g_pServerGameDLL = module.Offset(0x13F0AA98).As<CServerGameDLL*>(); } -void InitialiseServerChatHooks_Server(HMODULE baseAddress) +ON_DLL_LOAD_RELIESON("server.dll", ServerChatHooks, ServerSquirrel, (CModule module)) { - CServerGameDLL__OnReceivedSayTextMessage = (CServerGameDLL__OnReceivedSayTextMessageType)((char*)baseAddress + 0x1595C0); - UTIL_PlayerByIndex = (UTIL_PlayerByIndexType)((char*)baseAddress + 0x26AA10); - CRecipientFilter__Construct = (CRecipientFilter__ConstructType)((char*)baseAddress + 0x1E9440); - CRecipientFilter__Destruct = (CRecipientFilter__DestructType)((char*)baseAddress + 0x1E9700); - CRecipientFilter__AddAllPlayers = (CRecipientFilter__AddAllPlayersType)((char*)baseAddress + 0x1E9940); - CRecipientFilter__AddRecipient = (CRecipientFilter__AddRecipientType)((char*)baseAddress + 0x1E9b30); - CRecipientFilter__MakeReliable = (CRecipientFilter__MakeReliableType)((char*)baseAddress + 0x1EA4E0); - - UserMessageBegin = (UserMessageBeginType)((char*)baseAddress + 0x15C520); - MessageEnd = (MessageEndType)((char*)baseAddress + 0x158880); - MessageWriteByte = (MessageWriteByteType)((char*)baseAddress + 0x158A90); - MessageWriteString = (MessageWriteStringType)((char*)baseAddress + 0x158D00); - MessageWriteBool = (MessageWriteBoolType)((char*)baseAddress + 0x158A00); - - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>(CServerGameDLL__OnReceivedSayTextMessage), - &CServerGameDLL__OnReceivedSayTextMessageHook, - reinterpret_cast<LPVOID*>(&CServerGameDLL__OnReceivedSayTextMessageHookBase)); + AUTOHOOK_DISPATCH_MODULE(server.dll) + + CServerGameDLL__OnReceivedSayTextMessage = module.Offset(0x1595C0).As<void(__fastcall*)(CServerGameDLL*, unsigned int, const char*, int)>(); + CRecipientFilter__Construct = module.Offset(0x1E9440).As<void(__fastcall*)(CRecipientFilter*)>(); + CRecipientFilter__Destruct = module.Offset(0x1E9700).As<void(__fastcall*)(CRecipientFilter*)>(); + CRecipientFilter__AddAllPlayers = module.Offset(0x1E9940).As<void(__fastcall*)(CRecipientFilter*)>(); + CRecipientFilter__AddRecipient = module.Offset(0x1E9B30).As<void(__fastcall*)(CRecipientFilter*, const R2::CBasePlayer*)>(); + CRecipientFilter__MakeReliable = module.Offset(0x1EA4E0).As<void(__fastcall*)(CRecipientFilter*)>(); + + UserMessageBegin = module.Offset(0x15C520).As<void(__fastcall*)(CRecipientFilter*, const char*)>(); + MessageEnd = module.Offset(0x158880).As<void(__fastcall*)()>(); + MessageWriteByte = module.Offset(0x158A90).As<void(__fastcall*)(int)>(); + MessageWriteString = module.Offset(0x158D00).As<void(__fastcall*)(const char*)>(); + MessageWriteBool = module.Offset(0x158A00).As<void(__fastcall*)(bool)>(); // Chat sending functions - g_ServerSquirrelManager->AddFuncRegistration("void", "NSSendMessage", "int playerIndex, string text, bool isTeam", "", SQ_SendMessage); - g_ServerSquirrelManager->AddFuncRegistration( + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration("void", "NSSendMessage", "int playerIndex, string text, bool isTeam", "", SQ_SendMessage); + g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration( "void", "NSBroadcastMessage", "int fromPlayerIndex, int toPlayerIndex, string text, bool isTeam, bool isDead, int messageType", diff --git a/NorthstarDLL/serverchathooks.h b/NorthstarDLL/serverchathooks.h index f3425ae6..1d8a806a 100644 --- a/NorthstarDLL/serverchathooks.h +++ b/NorthstarDLL/serverchathooks.h @@ -23,7 +23,3 @@ void ChatSendMessage(unsigned int playerIndex, const char* text, bool isteam); // messageType: send a specific message type void ChatBroadcastMessage( int fromPlayerIndex, int toPlayerIndex, const char* text, bool isTeam, bool isDead, CustomMessageType messageType); - -void InitialiseServerChatHooks_Engine(HMODULE baseAddress); - -void InitialiseServerChatHooks_Server(HMODULE baseAddress); diff --git a/NorthstarDLL/serverpresence.cpp b/NorthstarDLL/serverpresence.cpp new file mode 100644 index 00000000..9338cd9c --- /dev/null +++ b/NorthstarDLL/serverpresence.cpp @@ -0,0 +1,227 @@ +#include "pch.h" +#include "serverpresence.h" +#include "playlist.h" +#include "tier0.h" +#include "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; +} + +ServerPresenceManager::ServerPresenceManager() +{ + // 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 description", 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 name", 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"); +} + +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/auth port, map/playlist name, and playercount/maxplayers + m_ServerPresence.m_iPort = 0; + m_ServerPresence.m_iAuthPort = 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; + + // 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::SetAuthPort(const int iAuthPort) +{ + // update authport + m_ServerPresence.m_iAuthPort = iAuthPort; +} + +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 = new ServerPresenceManager; + + Cvar_hostname = module.Offset(0x1315BAE8).Deref().As<ConVar*>(); +} diff --git a/NorthstarDLL/serverpresence.h b/NorthstarDLL/serverpresence.h new file mode 100644 index 00000000..f27c9393 --- /dev/null +++ b/NorthstarDLL/serverpresence.h @@ -0,0 +1,91 @@ +#pragma once +#include "convar.h" + +struct ServerPresence +{ + int m_iPort; + int m_iAuthPort; + + 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_iAuthPort = obj->m_iAuthPort; + + 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) {} +}; + +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: + ServerPresenceManager(); + + void AddPresenceReporter(ServerPresenceReporter* reporter); + + void CreatePresence(); + void DestroyPresence(); + void RunFrame(double flCurrentTime); + + void SetPort(const int iPort); + void SetAuthPort(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/sourceconsole.cpp b/NorthstarDLL/sourceconsole.cpp index 2e816485..718b6c3c 100644 --- a/NorthstarDLL/sourceconsole.cpp +++ b/NorthstarDLL/sourceconsole.cpp @@ -3,78 +3,70 @@ #include "sourceconsole.h" #include "sourceinterface.h" #include "concommand.h" -#include "hookutils.h" +#include "printcommand.h" -SourceInterface<CGameConsole>* g_SourceGameConsole; +SourceInterface<CGameConsole>* g_pSourceGameConsole; void ConCommand_toggleconsole(const CCommand& arg) { - if ((*g_SourceGameConsole)->IsConsoleVisible()) - (*g_SourceGameConsole)->Hide(); + if ((*g_pSourceGameConsole)->IsConsoleVisible()) + (*g_pSourceGameConsole)->Hide(); else - (*g_SourceGameConsole)->Activate(); + (*g_pSourceGameConsole)->Activate(); } -typedef void (*OnCommandSubmittedType)(CConsoleDialog* consoleDialog, const char* pCommand); -OnCommandSubmittedType onCommandSubmittedOriginal; -void OnCommandSubmittedHook(CConsoleDialog* consoleDialog, const char* pCommand) +void ConCommand_showconsole(const CCommand& arg) +{ + (*g_pSourceGameConsole)->Activate(); +} + +void ConCommand_hideconsole(const CCommand& arg) +{ + (*g_pSourceGameConsole)->Hide(); +} + +void SourceConsoleSink::sink_it_(const spdlog::details::log_msg& msg) +{ + if (!(*g_pSourceGameConsole)->m_bInitialized) + return; + + spdlog::memory_buf_t formatted; + spdlog::sinks::base_sink<std::mutex>::formatter_->format(msg, formatted); + (*g_pSourceGameConsole) + ->m_pConsole->m_pConsolePanel->ColorPrint(m_LogColours[msg.level], fmt::to_string(formatted).c_str()); +} + +void SourceConsoleSink::flush_() {} + +HOOK(OnCommandSubmittedHook, OnCommandSubmitted, +void,, (CConsoleDialog* consoleDialog, const char* pCommand)) { consoleDialog->m_pConsolePanel->Print("] "); consoleDialog->m_pConsolePanel->Print(pCommand); consoleDialog->m_pConsolePanel->Print("\n"); - // todo: call the help command in the future + TryPrintCvarHelpForCommand(pCommand); - onCommandSubmittedOriginal(consoleDialog, pCommand); + OnCommandSubmitted(consoleDialog, pCommand); } // called from sourceinterface.cpp in client createinterface hooks, on GameClientExports001 void InitialiseConsoleOnInterfaceCreation() { - (*g_SourceGameConsole)->Initialize(); + (*g_pSourceGameConsole)->Initialize(); + // hook OnCommandSubmitted so we print inputted commands + OnCommandSubmittedHook.Dispatch((*g_pSourceGameConsole)->m_pConsole->m_vtable->OnCommandSubmitted); auto consoleLogger = std::make_shared<SourceConsoleSink>(); consoleLogger->set_pattern("[%l] %v"); - spdlog::default_logger()->sinks().push_back(consoleLogger); - - // hook OnCommandSubmitted so we print inputted commands - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - (void*)((*g_SourceGameConsole)->m_pConsole->m_vtable->OnCommandSubmitted), - &OnCommandSubmittedHook, - reinterpret_cast<LPVOID*>(&onCommandSubmittedOriginal)); -} - -void InitialiseSourceConsole(HMODULE baseAddress) -{ - g_SourceGameConsole = new SourceInterface<CGameConsole>("client.dll", "GameConsole004"); - RegisterConCommand("toggleconsole", ConCommand_toggleconsole, "toggles the console", FCVAR_DONTRECORD); -} - -// logging stuff - -SourceConsoleSink::SourceConsoleSink() -{ - logColours.emplace(spdlog::level::trace, SourceColor(0, 255, 255, 255)); - logColours.emplace(spdlog::level::debug, SourceColor(0, 255, 255, 255)); - logColours.emplace(spdlog::level::info, SourceColor(255, 255, 255, 255)); - logColours.emplace(spdlog::level::warn, SourceColor(255, 255, 0, 255)); - logColours.emplace(spdlog::level::err, SourceColor(255, 0, 0, 255)); - logColours.emplace(spdlog::level::critical, SourceColor(255, 0, 0, 255)); - logColours.emplace(spdlog::level::off, SourceColor(0, 0, 0, 0)); } -void SourceConsoleSink::sink_it_(const spdlog::details::log_msg& msg) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", SourceConsole, ConCommand, (CModule module)) { - if (!(*g_SourceGameConsole)->m_bInitialized) - return; + g_pSourceGameConsole = new SourceInterface<CGameConsole>("client.dll", "GameConsole004"); - spdlog::memory_buf_t formatted; - spdlog::sinks::base_sink<std::mutex>::formatter_->format(msg, formatted); - (*g_SourceGameConsole) - ->m_pConsole->m_pConsolePanel->ColorPrint(logColours[msg.level], fmt::to_string(formatted).c_str()); // todo needs colour support + 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); } - -void SourceConsoleSink::flush_() {} diff --git a/NorthstarDLL/sourceconsole.h b/NorthstarDLL/sourceconsole.h index 1dee136a..9f6c2bf8 100644 --- a/NorthstarDLL/sourceconsole.h +++ b/NorthstarDLL/sourceconsole.h @@ -86,21 +86,25 @@ class CGameConsole CConsoleDialog* m_pConsole; }; -extern SourceInterface<CGameConsole>* g_SourceGameConsole; +extern SourceInterface<CGameConsole>* g_pSourceGameConsole; // spdlog logger class SourceConsoleSink : public spdlog::sinks::base_sink<std::mutex> { private: - std::map<spdlog::level::level_enum, SourceColor> logColours; - - public: - SourceConsoleSink(); + std::map<spdlog::level::level_enum, SourceColor> m_LogColours = { + {spdlog::level::trace, SourceColor(0, 255, 255, 255)}, + {spdlog::level::debug, SourceColor(0, 255, 255, 255)}, + {spdlog::level::info, SourceColor(255, 255, 255, 255)}, + {spdlog::level::warn, SourceColor(255, 255, 0, 255)}, + {spdlog::level::err, SourceColor(255, 0, 0, 255)}, + {spdlog::level::critical, SourceColor(255, 0, 0, 255)}, + {spdlog::level::off, SourceColor(0, 0, 0, 0)} + }; protected: void sink_it_(const spdlog::details::log_msg& msg) override; void flush_() override; }; -void InitialiseSourceConsole(HMODULE baseAddress); void InitialiseConsoleOnInterfaceCreation(); diff --git a/NorthstarDLL/sourceinterface.cpp b/NorthstarDLL/sourceinterface.cpp index 56020e5e..2f1df4ba 100644 --- a/NorthstarDLL/sourceinterface.cpp +++ b/NorthstarDLL/sourceinterface.cpp @@ -1,82 +1,52 @@ #include "pch.h" #include "sourceinterface.h" -#include "hooks.h" -#include "hookutils.h" - #include "sourceconsole.h" -#include "context.h" -#include "convar.h" -#include <iostream> + +AUTOHOOK_INIT() // really wanted to do a modular callback system here but honestly couldn't be bothered so hardcoding stuff for now: todo later -CreateInterfaceFn clientCreateInterfaceOriginal; -void* ClientCreateInterfaceHook(const char* pName, int* pReturnCode) +AUTOHOOK_PROCADDRESS(ClientCreateInterface, client.dll, CreateInterface, +void*,, (const char* pName, const int* pReturnCode)) { - void* ret = clientCreateInterfaceOriginal(pName, pReturnCode); - + void* ret = ClientCreateInterface(pName, pReturnCode); spdlog::info("CreateInterface CLIENT {}", pName); + if (!strcmp(pName, "GameClientExports001")) InitialiseConsoleOnInterfaceCreation(); return ret; } -CreateInterfaceFn serverCreateInterfaceOriginal; -void* ServerCreateInterfaceHook(const char* pName, int* pReturnCode) +AUTOHOOK_PROCADDRESS(ServerCreateInterface, server.dll, CreateInterface, +void*,, (const char* pName, const int* pReturnCode)) { - void* ret = serverCreateInterfaceOriginal(pName, pReturnCode); - - std::cout << "CreateInterface SERVER " << pName << std::endl; + void* ret = ServerCreateInterface(pName, pReturnCode); + spdlog::info("CreateInterface SERVER {}", pName); return ret; } -CreateInterfaceFn engineCreateInterfaceOriginal; -void* EngineCreateInterfaceHook(const char* pName, int* pReturnCode) +AUTOHOOK_PROCADDRESS(EngineCreateInterface, engine.dll, CreateInterface, +void*,, (const char* pName, const int* pReturnCode)) { - void* ret = engineCreateInterfaceOriginal(pName, pReturnCode); - - std::cout << "CreateInterface ENGINE " << pName << std::endl; + void* ret = EngineCreateInterface(pName, pReturnCode); + spdlog::info("CreateInterface ENGINE {}", pName); return ret; } -void HookClientCreateInterface(HMODULE baseAddress) +ON_DLL_LOAD("client.dll", ClientInterface, (CModule module)) { - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>(GetProcAddress(baseAddress, "CreateInterface")), - &ClientCreateInterfaceHook, - reinterpret_cast<LPVOID*>(&clientCreateInterfaceOriginal)); + AUTOHOOK_DISPATCH_MODULE(client.dll) } -void HookServerCreateInterface(HMODULE baseAddress) +ON_DLL_LOAD("server.dll", ServerInterface, (CModule module)) { - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>(GetProcAddress(baseAddress, "CreateInterface")), - &ServerCreateInterfaceHook, - reinterpret_cast<LPVOID*>(&serverCreateInterfaceOriginal)); + AUTOHOOK_DISPATCH_MODULE(server.dll) } -void HookEngineCreateInterface(HMODULE baseAddress) -{ - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>(GetProcAddress(baseAddress, "CreateInterface")), - &EngineCreateInterfaceHook, - reinterpret_cast<LPVOID*>(&engineCreateInterfaceOriginal)); -} - -void InitialiseInterfaceCreationHooks() -{ - AddDllLoadCallback("client.dll", HookClientCreateInterface); - - // not used atm - // AddDllLoadCallback("server.dll", HookServerCreateInterface); - // AddDllLoadCallback("engine.dll", HookEngineCreateInterface); +ON_DLL_LOAD("engine.dll", EngineInterface, (CModule module)) +{ + AUTOHOOK_DISPATCH_MODULE(engine.dll) } diff --git a/NorthstarDLL/sourceinterface.h b/NorthstarDLL/sourceinterface.h index 238ebec8..474e961b 100644 --- a/NorthstarDLL/sourceinterface.h +++ b/NorthstarDLL/sourceinterface.h @@ -29,6 +29,3 @@ template <typename T> class SourceInterface return m_interface; } }; - -// functions for interface creation callbacks -void InitialiseInterfaceCreationHooks(); diff --git a/NorthstarDLL/squirrel.cpp b/NorthstarDLL/squirrel.cpp index 2fa957fb..4bd4bca8 100644 --- a/NorthstarDLL/squirrel.cpp +++ b/NorthstarDLL/squirrel.cpp @@ -1,271 +1,224 @@ #include "pch.h" #include "squirrel.h" -#include "hooks.h" -#include "hookutils.h" -#include "sigscanning.h" #include "concommand.h" #include "modmanager.h" -#include <iostream> -#include "gameutils.h" +#include "dedicated.h" +#include "r2engine.h" +#include "tier0.h" -// hook forward declarations -typedef SQInteger (*SQPrintType)(void* sqvm, char* fmt, ...); -SQPrintType ClientSQPrint; -SQPrintType UISQPrint; -SQPrintType ServerSQPrint; -template <ScriptContext context> SQInteger SQPrintHook(void* sqvm, char* fmt, ...); +AUTOHOOK_INIT() -typedef void* (*CreateNewVMType)(void* a1, ScriptContext contextArg); -CreateNewVMType ClientCreateNewVM; // only need a client one since ui doesn't have its own func for this -CreateNewVMType ServerCreateNewVM; -template <ScriptContext context> void* CreateNewVMHook(void* a1, ScriptContext contextArg); - -typedef void (*DestroyVMType)(void* a1, void* sqvm); -DestroyVMType ClientDestroyVM; // only need a client one since ui doesn't have its own func for this -DestroyVMType ServerDestroyVM; -template <ScriptContext context> void DestroyVMHook(void* a1, void* sqvm); +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"; + } +} -typedef void (*ScriptCompileError)(void* sqvm, const char* error, const char* file, int line, int column); -ScriptCompileError ClientSQCompileError; // only need a client one since ui doesn't have its own func for this -ScriptCompileError ServerSQCompileError; -template <ScriptContext context> void ScriptCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column); +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 +} -typedef char (*CallScriptInitCallbackType)(void* sqvm, const char* callback); -CallScriptInitCallbackType ClientCallScriptInitCallback; -CallScriptInitCallbackType ServerCallScriptInitCallback; -template <ScriptContext context> char CallScriptInitCallbackHook(void* sqvm, const char* callback); +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 ""; +} -RegisterSquirrelFuncType ClientRegisterSquirrelFunc; -RegisterSquirrelFuncType ServerRegisterSquirrelFunc; -template <ScriptContext context> int64_t RegisterSquirrelFuncHook(void* sqvm, SQFuncRegistration* funcReg, char unknown); +// 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>; -// core sqvm funcs -sq_compilebufferType ClientSq_compilebuffer; -sq_compilebufferType ServerSq_compilebuffer; +template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquirrelVM* newSqvm) +{ + m_pSQVM = newSqvm; -sq_pushroottableType ClientSq_pushroottable; -sq_pushroottableType ServerSq_pushroottable; + for (SQFuncRegistration* funcReg : m_funcRegistrations) + { + spdlog::info("Registering {} function {}", GetContextName(context), funcReg->squirrelFuncName); + RegisterSquirrelFunc(m_pSQVM, funcReg, 1); + } -sq_callType ClientSq_call; -sq_callType ServerSq_call; + for (auto& pair : g_pModManager->m_DependencyConstants) + { + bool bWasFound = false; + for (Mod& dependency : g_pModManager->m_LoadedMods) + { + if (!dependency.m_bEnabled) + continue; -// sq stack array funcs -sq_newarrayType ClientSq_newarray; -sq_newarrayType ServerSq_newarray; + if (dependency.Name == pair.second) + { + bWasFound = true; + break; + } + } -sq_arrayappendType ClientSq_arrayappend; -sq_arrayappendType ServerSq_arrayappend; + defconst(m_pSQVM, pair.first.c_str(), bWasFound); + } +} -// sq stack push funcs -sq_pushstringType ClientSq_pushstring; -sq_pushstringType ServerSq_pushstring; +template <ScriptContext context> void SquirrelManager<context>::VMDestroyed() +{ + m_pSQVM = nullptr; +} -sq_pushintegerType ClientSq_pushinteger; -sq_pushintegerType ServerSq_pushinteger; +template <ScriptContext context> void SquirrelManager<context>::ExecuteCode(const char* pCode) +{ + if (!m_pSQVM) + { + spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context)); + return; + } -sq_pushfloatType ClientSq_pushfloat; -sq_pushfloatType ServerSq_pushfloat; + spdlog::info("Executing {} script code {} ", GetContextName(context), pCode); -sq_pushboolType ClientSq_pushbool; -sq_pushboolType ServerSq_pushbool; + std::string strCode(pCode); + CompileBufferState bufferState = CompileBufferState(strCode); -sq_pusherrorType ClientSq_pusherror; -sq_pusherrorType ServerSq_pusherror; + SQRESULT compileResult = compilebuffer(&bufferState, "console"); + spdlog::info("sq_compilebuffer returned {}", PrintSQRESULT.at(compileResult)); -sq_defconst ClientSq_defconst; -sq_defconst ServerSq_defconst; + if (compileResult != SQRESULT_ERROR) + { + pushroottable(m_pSQVM->sqvm); + SQRESULT callResult = call(m_pSQVM->sqvm, 0); + spdlog::info("sq_call returned {}", PrintSQRESULT.at(callResult)); + } +} -sq_pushAssetType ClientSq_pushAsset; -sq_pushAssetType ServerSq_pushAsset; +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; -// sq stack get funcs -sq_getstringType ClientSq_getstring; -sq_getstringType ServerSq_getstring; + reg->squirrelFuncName = new char[name.size() + 1]; + strcpy((char*)reg->squirrelFuncName, name.c_str()); + reg->cppFuncName = reg->squirrelFuncName; -sq_getintegerType ClientSq_getinteger; -sq_getintegerType ServerSq_getinteger; + reg->helpText = new char[helpText.size() + 1]; + strcpy((char*)reg->helpText, helpText.c_str()); -sq_getfloatType ClientSq_getfloat; -sq_getfloatType ServerSq_getfloat; + reg->returnTypeString = new char[returnType.size() + 1]; + strcpy((char*)reg->returnTypeString, returnType.c_str()); + reg->returnType = SQReturnTypeFromString(returnType.c_str()); -sq_getboolType ClientSq_getbool; -sq_getboolType ServerSq_getbool; + reg->argTypes = new char[argTypes.size() + 1]; + strcpy((char*)reg->argTypes, argTypes.c_str()); -sq_getType ClientSq_sq_get; -sq_getType ServerSq_sq_get; + reg->funcPtr = func; -sq_newSlotType ServerSq_newSlot; -sq_newSlotType ClientSq_newSlot; + m_funcRegistrations.push_back(reg); +} -sq_newTableType ServerSq_newTable; -sq_newTableType ClientSq_newTable; +template <ScriptContext context> SQRESULT SquirrelManager<context>::setupfunc(const SQChar* funcname) +{ + pushroottable(m_pSQVM->sqvm); + pushstring(m_pSQVM->sqvm, funcname, -1); -template <ScriptContext context> void ExecuteCodeCommand(const CCommand& args); + SQRESULT result = get(m_pSQVM->sqvm, -2); + if (result != SQRESULT_ERROR) + pushroottable(m_pSQVM->sqvm); -// inits -SquirrelManager<ScriptContext::CLIENT>* g_ClientSquirrelManager; -SquirrelManager<ScriptContext::SERVER>* g_ServerSquirrelManager; -SquirrelManager<ScriptContext::UI>* g_UISquirrelManager; + return result; +} -SQInteger NSTestFunc(void* sqvm) +template <ScriptContext context> void SquirrelManager<context>::AddFuncOverride(std::string name, SQFunction func) { - return 1; + m_funcOverrides[name] = func; } -void InitialiseClientSquirrel(HMODULE baseAddress) +// hooks +bool IsUIVM(ScriptContext context, HSquirrelVM* pSqvm) { - HookEnabler hook; - - // client inits - g_ClientSquirrelManager = new SquirrelManager<ScriptContext::CLIENT>(); - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x12B00, - &SQPrintHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientSQPrint)); // client print function - RegisterConCommand( - "script_client", ExecuteCodeCommand<ScriptContext::CLIENT>, "Executes script code on the client vm", FCVAR_CLIENTDLL); - - // ui inits - g_UISquirrelManager = new SquirrelManager<ScriptContext::UI>(); - - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x12BA0, &SQPrintHook<ScriptContext::UI>, reinterpret_cast<LPVOID*>(&UISQPrint)); // ui print function - RegisterConCommand("script_ui", ExecuteCodeCommand<ScriptContext::UI>, "Executes script code on the ui vm", FCVAR_CLIENTDLL); - - // inits for both client and ui, since they share some functions - ClientSq_compilebuffer = (sq_compilebufferType)((char*)baseAddress + 0x3110); - ClientSq_pushroottable = (sq_pushroottableType)((char*)baseAddress + 0x5860); - ClientSq_call = (sq_callType)((char*)baseAddress + 0x8650); - ClientRegisterSquirrelFunc = (RegisterSquirrelFuncType)((char*)baseAddress + 0x108E0); - - ClientSq_newarray = (sq_newarrayType)((char*)baseAddress + 0x39F0); - ClientSq_arrayappend = (sq_arrayappendType)((char*)baseAddress + 0x3C70); - - ClientSq_pushstring = (sq_pushstringType)((char*)baseAddress + 0x3440); - ClientSq_pushinteger = (sq_pushintegerType)((char*)baseAddress + 0x36A0); - ClientSq_pushfloat = (sq_pushfloatType)((char*)baseAddress + 0x3800); - ClientSq_pushbool = (sq_pushboolType)((char*)baseAddress + 0x3710); - ClientSq_pusherror = (sq_pusherrorType)((char*)baseAddress + 0x8470); - ClientSq_pushAsset = (sq_pushAssetType)((char*)baseAddress + 0x3560); - - ClientSq_getstring = (sq_getstringType)((char*)baseAddress + 0x60C0); - ClientSq_getinteger = (sq_getintegerType)((char*)baseAddress + 0x60E0); - ClientSq_getfloat = (sq_getfloatType)((char*)baseAddress + 0x6100); - ClientSq_getbool = (sq_getboolType)((char*)baseAddress + 0x6130); - - ClientSq_sq_get = (sq_getType)((char*)baseAddress + 0x7C30); - - ClientSq_defconst = (sq_defconst)((char*)baseAddress + 0x12120); - - // Table functions - ClientSq_newTable = (sq_newTableType)((char*)baseAddress + 0x3960); - ClientSq_newSlot = (sq_newSlotType)((char*)baseAddress + 0x70B0); - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x26130, - &CreateNewVMHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientCreateNewVM)); // client createnewvm function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x26E70, - &DestroyVMHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientDestroyVM)); // client destroyvm function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x79A50, - &ScriptCompileErrorHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientSQCompileError)); // client compileerror function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x10190, - &CallScriptInitCallbackHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientCallScriptInitCallback)); // client callscriptinitcallback function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x108E0, - &RegisterSquirrelFuncHook<ScriptContext::CLIENT>, - reinterpret_cast<LPVOID*>(&ClientRegisterSquirrelFunc)); // client registersquirrelfunc function + return context != ScriptContext::SERVER && g_pSquirrel<ScriptContext::UI>->m_pSQVM && + g_pSquirrel<ScriptContext::UI>->m_pSQVM->sqvm == pSqvm; } -void InitialiseServerSquirrel(HMODULE baseAddress) +template <ScriptContext context> void* (*sq_compiler_create)(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError); +template <ScriptContext context> void* sq_compiler_createHook(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError) { - g_ServerSquirrelManager = new SquirrelManager<ScriptContext::SERVER>(); - - HookEnabler hook; - - ServerSq_compilebuffer = (sq_compilebufferType)((char*)baseAddress + 0x3110); - ServerSq_pushroottable = (sq_pushroottableType)((char*)baseAddress + 0x5840); - ServerSq_call = (sq_callType)((char*)baseAddress + 0x8620); - ServerRegisterSquirrelFunc = (RegisterSquirrelFuncType)((char*)baseAddress + 0x1DD10); - - ServerSq_newarray = (sq_newarrayType)((char*)baseAddress + 0x39F0); - ServerSq_arrayappend = (sq_arrayappendType)((char*)baseAddress + 0x3C70); - - ServerSq_pushstring = (sq_pushstringType)((char*)baseAddress + 0x3440); - ServerSq_pushinteger = (sq_pushintegerType)((char*)baseAddress + 0x36A0); - ServerSq_pushfloat = (sq_pushfloatType)((char*)baseAddress + 0x3800); - ServerSq_pushbool = (sq_pushboolType)((char*)baseAddress + 0x3710); - ServerSq_pusherror = (sq_pusherrorType)((char*)baseAddress + 0x8440); - ServerSq_pushAsset = (sq_pushAssetType)((char*)baseAddress + 0x3560); - - ServerSq_getstring = (sq_getstringType)((char*)baseAddress + 0x60A0); - ServerSq_getinteger = (sq_getintegerType)((char*)baseAddress + 0x60C0); - ServerSq_getfloat = (sq_getfloatType)((char*)baseAddress + 0x60E0); - ServerSq_getbool = (sq_getboolType)((char*)baseAddress + 0x6110); - - ServerSq_sq_get = (sq_getType)((char*)baseAddress + 0x7C00); - - ServerSq_defconst = (sq_defconst)((char*)baseAddress + 0x1F550); - - ServerSq_newSlot = (sq_newSlotType)((char*)baseAddress + 0x7080); - ServerSq_newTable = (sq_newTableType)((char*)baseAddress + 0x3960); - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1FE90, - &SQPrintHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerSQPrint)); // server print function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x260E0, - &CreateNewVMHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerCreateNewVM)); // server createnewvm function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x26E20, - &DestroyVMHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerDestroyVM)); // server destroyvm function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x799E0, - &ScriptCompileErrorHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerSQCompileError)); // server compileerror function - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1D5C0, - &CallScriptInitCallbackHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerCallScriptInitCallback)); // server callscriptinitcallback function - - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1DD10, - &RegisterSquirrelFuncHook<ScriptContext::SERVER>, - reinterpret_cast<LPVOID*>(&ServerRegisterSquirrelFunc)); // server registersquirrelfunc function - - // cheat and clientcmd_can_execute 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", - ExecuteCodeCommand<ScriptContext::SERVER>, - "Executes script code on the server vm", - FCVAR_GAMEDLL | FCVAR_CLIENTCMD_CAN_EXECUTE | FCVAR_CHEAT); + // 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); } -// hooks -template <ScriptContext context> SQInteger SQPrintHook(void* sqvm, char* fmt, ...) +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); @@ -285,189 +238,157 @@ template <ScriptContext context> SQInteger SQPrintHook(void* sqvm, char* fmt, .. return 0; } -template <ScriptContext context> void* CreateNewVMHook(void* a1, ScriptContext realContext) +template <ScriptContext context> CSquirrelVM* (*CreateNewVM)(void* a1, ScriptContext realContext); +template <ScriptContext context> CSquirrelVM* CreateNewVMHook(void* a1, ScriptContext realContext) { - void* sqvm; - - if (context == ScriptContext::CLIENT) - { - sqvm = ClientCreateNewVM(a1, realContext); - - if (realContext == ScriptContext::UI) - g_UISquirrelManager->VMCreated(sqvm); - else - g_ClientSquirrelManager->VMCreated(sqvm); - } - else if (context == ScriptContext::SERVER) - { - sqvm = ServerCreateNewVM(a1, context); - g_ServerSquirrelManager->VMCreated(sqvm); - } + 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), sqvm); + spdlog::info("CreateNewVM {} {}", GetContextName(realContext), (void*)sqvm); return sqvm; } -template <ScriptContext context> void DestroyVMHook(void* a1, void* sqvm) +template <ScriptContext context> void (*DestroyVM)(void* a1, HSquirrelVM* sqvm); +template <ScriptContext context> void DestroyVMHook(void* a1, HSquirrelVM* sqvm) { ScriptContext realContext = context; // ui and client use the same function so we use this for prints - - if (context == ScriptContext::CLIENT) + if (IsUIVM(context, sqvm)) { - if (g_ClientSquirrelManager->sqvm == sqvm) - g_ClientSquirrelManager->VMDestroyed(); - else if (g_UISquirrelManager->sqvm == sqvm) - { - g_UISquirrelManager->VMDestroyed(); - realContext = ScriptContext::UI; - } - - ClientDestroyVM(a1, sqvm); - } - else if (context == ScriptContext::SERVER) - { - g_ServerSquirrelManager->VMDestroyed(); - ServerDestroyVM(a1, sqvm); + realContext = ScriptContext::UI; + g_pSquirrel<ScriptContext::UI>->VMDestroyed(); } + else + DestroyVM<context>(a1, sqvm); - spdlog::info("DestroyVM {} {}", GetContextName(realContext), sqvm); + spdlog::info("DestroyVM {} {}", GetContextName(realContext), (void*)sqvm); } -template <ScriptContext context> void ScriptCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column) +template <ScriptContext context> void (*SQCompileError)(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column); +template <ScriptContext context> void 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 (context == ScriptContext::CLIENT && sqvm == g_UISquirrelManager->sqvm) + if (IsUIVM(context, sqvm)) + { realContext = ScriptContext::UI; + bIsFatalError = g_pSquirrel<ScriptContext::UI>->m_bFatalCompilationErrors; + } spdlog::error("{} SCRIPT COMPILE ERROR {}", GetContextName(realContext), error); spdlog::error("{} line [{}] column [{}]", file, line, column); - // dont call the original since it kills game - // in the future it'd be nice to do an actual error with UICodeCallback_ErrorDialog here, but only if we're compiling level scripts - // compilestring and stuff shouldn't tho - // though, that also has potential to be REALLY bad if we're compiling ui scripts lol + // 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()) + abort(); + else + { + R2::Cbuf_AddText( + R2::Cbuf_GetCurrentPlayer(), + fmt::format("disconnect \"Encountered {} script compilation error, see console for details.\"", GetContextName(realContext)) + .c_str(), + R2::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) + R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "showconsole", R2::cmd_source_t::kCommandSrcCode); + } + } + + // dont call the original function since it kills game lol } -template <ScriptContext context> char CallScriptInitCallbackHook(void* sqvm, const char* callback) +template <ScriptContext context> int64_t(*RegisterSquirrelFunction)(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown); +template <ScriptContext context> int64_t RegisterSquirrelFunctionHook(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown) { - char ret; - - if (context == ScriptContext::CLIENT) + if (IsUIVM(context, sqvm->sqvm)) { - ScriptContext realContext = context; // ui and client use the same function so we use this for prints - bool shouldCallCustomCallbacks = false; - - // since we don't hook arbitrary callbacks yet, make sure we're only doing callbacks on inits - if (!strcmp(callback, "UICodeCallback_UIInit")) + if (g_pSquirrel<ScriptContext::UI>->m_funcOverrides.count(funcReg->squirrelFuncName)) { - realContext = ScriptContext::UI; - shouldCallCustomCallbacks = true; + 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)); } - else if (!strcmp(callback, "ClientCodeCallback_MapSpawn")) - shouldCallCustomCallbacks = true; - // run before callbacks - // todo: we need to verify if RunOn is valid for current state before calling callbacks - if (shouldCallCustomCallbacks) - { - for (Mod mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled) - continue; + return g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc(sqvm, funcReg, unknown); + } - 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); - ClientCallScriptInitCallback(sqvm, modCallback.BeforeCallback.c_str()); - } - } - } - } - } + 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)); + } - spdlog::info("{} CodeCallback {} called", GetContextName(realContext), callback); - if (!shouldCallCustomCallbacks) - spdlog::info("Not executing custom callbacks for CodeCallback {}", callback); - ret = ClientCallScriptInitCallback(sqvm, callback); + return g_pSquirrel<context>->RegisterSquirrelFunc(sqvm, funcReg, unknown); +} - // run after callbacks - if (shouldCallCustomCallbacks) - { - for (Mod mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled) - continue; +template <ScriptContext context> bool (*CallScriptInitCallback)(void* sqvm, const char* callback); +template <ScriptContext context> bool CallScriptInitCallbackHook(void* sqvm, const char* callback) +{ + ScriptContext realContext = context; + bool bShouldCallCustomCallbacks = true; - 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); - ClientCallScriptInitCallback(sqvm, modCallback.AfterCallback.c_str()); - } - } - } - } - } + 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) - { - // since we don't hook arbitrary callbacks yet, make sure we're only doing callbacks on inits - bool shouldCallCustomCallbacks = !strcmp(callback, "CodeCallback_MapSpawn"); + bShouldCallCustomCallbacks = !strcmp(callback, "CodeCallback_MapSpawn"); - // run before callbacks - // todo: we need to verify if RunOn is valid for current state before calling callbacks - if (shouldCallCustomCallbacks) + if (bShouldCallCustomCallbacks) + { + for (Mod mod : g_pModManager->m_LoadedMods) { - for (Mod mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled) - continue; + if (!mod.m_bEnabled) + continue; - for (ModScript script : mod.Scripts) + for (ModScript script : mod.Scripts) + { + for (ModScriptCallback modCallback : script.Callbacks) { - for (ModScriptCallback modCallback : script.Callbacks) + if (modCallback.Context == realContext && modCallback.BeforeCallback.length()) { - if (modCallback.Context == ScriptContext::SERVER && modCallback.BeforeCallback.length()) - { - spdlog::info("Running custom {} script callback \"{}\"", GetContextName(context), modCallback.BeforeCallback); - ServerCallScriptInitCallback(sqvm, modCallback.BeforeCallback.c_str()); - } + spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.BeforeCallback); + CallScriptInitCallback<context>(sqvm, modCallback.BeforeCallback.c_str()); } } } } + } - spdlog::info("{} CodeCallback {} called", GetContextName(context), callback); - if (!shouldCallCustomCallbacks) - spdlog::info("Not executing custom callbacks for CodeCallback {}", callback); - ret = ServerCallScriptInitCallback(sqvm, callback); + 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 (shouldCallCustomCallbacks) + // run after callbacks + if (bShouldCallCustomCallbacks) + { + for (Mod mod : g_pModManager->m_LoadedMods) { - for (Mod mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled) - continue; + if (!mod.m_bEnabled) + continue; - for (ModScript script : mod.Scripts) + for (ModScript script : mod.Scripts) + { + for (ModScriptCallback modCallback : script.Callbacks) { - for (ModScriptCallback modCallback : script.Callbacks) + if (modCallback.Context == realContext && modCallback.AfterCallback.length()) { - if (modCallback.Context == ScriptContext::SERVER && modCallback.AfterCallback.length()) - { - spdlog::info("Running custom {} script callback \"{}\"", GetContextName(context), modCallback.AfterCallback); - ServerCallScriptInitCallback(sqvm, modCallback.AfterCallback.c_str()); - } + spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.AfterCallback); + CallScriptInitCallback<context>(sqvm, modCallback.AfterCallback.c_str()); } } } @@ -477,113 +398,194 @@ template <ScriptContext context> char CallScriptInitCallbackHook(void* sqvm, con return ret; } -template <ScriptContext context> void ExecuteCodeCommand(const CCommand& args) +template <ScriptContext context> void ConCommand_script(const CCommand& args) { - if (context == ScriptContext::CLIENT) - g_ClientSquirrelManager->ExecuteCode(args.ArgS()); - else if (context == ScriptContext::UI) - g_UISquirrelManager->ExecuteCode(args.ArgS()); - else if (context == ScriptContext::SERVER) - g_ServerSquirrelManager->ExecuteCode(args.ArgS()); + g_pSquirrel<context>->ExecuteCode(args.ArgS()); } -SQRESULT SQ_DevFuncStub(void* sqvm) +// literal class type that wraps a constant expression string +template <size_t N> struct TemplateStringLiteral { - spdlog::warn("Blocked execution of squirrel developer function for security reasons. To re-enable them use start parameter " - "-allowSquirrelDevFunctions."); - return SQRESULT_NULL; -} + constexpr TemplateStringLiteral(const char (&str)[N]) + { + std::copy_n(str, N, value); + } -template <ScriptContext context> int64_t RegisterSquirrelFuncHook(void* sqvm, SQFuncRegistration* funcReg, char unknown) + char value[N]; +}; + +template <ScriptContext context, TemplateStringLiteral funcName> SQRESULT SQ_StubbedFunc(HSquirrelVM* sqvm) { - static std::set<std::string> allowedDevFunctions = { - "Dev_CommandLineHasParm", - "Dev_CommandLineParmValue", - "Dev_CommandLineRemoveParm", - }; - - if ((funcReg->devLevel == 1) && (!CommandLine()->CheckParm("-allowSquirrelDevFunctions")) && - (!allowedDevFunctions.count(funcReg->squirrelFuncName))) - funcReg->funcPtr = reinterpret_cast<void*>(SQ_DevFuncStub); - - if (context == ScriptContext::SERVER) - return ServerRegisterSquirrelFunc(sqvm, funcReg, unknown); - else - return ClientRegisterSquirrelFunc(sqvm, funcReg, unknown); + spdlog::info("Blocking call to stubbed function {} in {}", funcName.value, GetContextName(context)); + return SQRESULT_NULL; } -const char* sq_getTypeName(int type) +template <ScriptContext context> void StubUnsafeSQFuncs() { - switch (type) + if (!Tier0::CommandLine()->CheckParm("-allowunsafesqfuncs")) { - 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 0x8000040: - 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 0xA000020: - return "table"; - case 0xA008000: - return "instance"; - case 0xA400000: - return "entity"; + 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">); } - return ""; } -SQReturnTypeEnum GetReturnTypeEnumFromString(const char* returnTypeString) +ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) { + AUTOHOOK_DISPATCH_MODULE(client.dll) + + g_pSquirrel<ScriptContext::CLIENT> = new SquirrelManager<ScriptContext::CLIENT>; + g_pSquirrel<ScriptContext::UI> = new SquirrelManager<ScriptContext::UI>; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst = module.Offset(0x12120).As<sq_defconstType>(); + g_pSquirrel<ScriptContext::UI>->__sq_defconst = g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_compilebuffer = module.Offset(0x3110).As<sq_compilebufferType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushroottable = module.Offset(0x5860).As<sq_pushroottableType>(); + 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::CLIENT>->__sq_call = module.Offset(0x8650).As<sq_callType>(); + g_pSquirrel<ScriptContext::UI>->__sq_call = g_pSquirrel<ScriptContext::CLIENT>->__sq_call; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_newarray = module.Offset(0x39F0).As<sq_newarrayType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_arrayappend = module.Offset(0x3C70).As<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).As<sq_newtableType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot = module.Offset(0x70B0).As<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).As<sq_pushstringType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger = module.Offset(0x36A0).As<sq_pushintegerType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat = module.Offset(0x3800).As<sq_pushfloatType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool = module.Offset(0x3710).As<sq_pushboolType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset = module.Offset(0x3560).As<sq_pushassetType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushvector = module.Offset(0x3780).As<sq_pushvectorType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror = module.Offset(0x8470).As<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_raiseerror = g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror; + + g_pSquirrel<ScriptContext::CLIENT>->__sq_getstring = module.Offset(0x60C0).As<sq_getstringType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getinteger = module.Offset(0x60E0).As<sq_getintegerType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getfloat = module.Offset(0x6100).As<sq_getfloatType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getbool = module.Offset(0x6130).As<sq_getboolType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_get = module.Offset(0x7C30).As<sq_getType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getasset = module.Offset(0x6010).As<sq_getassetType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getuserdata = module.Offset(0x63D0).As<sq_getuserdataType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_getvector = module.Offset(0x6140).As<sq_getvectorType>(); + 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::CLIENT>->__sq_createuserdata = module.Offset(0x38D0).As<sq_createuserdataType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_setuserdatatypeid = module.Offset(0x6490).As<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; + + MAKEHOOK( + module.Offset(0x108E0), + &RegisterSquirrelFunctionHook<ScriptContext::CLIENT>, + &g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc); + g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc = g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc; + + // 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>); - static std::map<std::string, SQReturnTypeEnum> sqEnumStrMap = { - {"bool", SqReturnBoolean}, - {"float", SqReturnFloat}, - {"vector", SqReturnVector}, - {"int", SqReturnInteger}, - {"entity", SqReturnEntity}, - {"string", SqReturnString}, - {"array", SqReturnArrays}, - {"asset", SqReturnAsset}, - {"table", SqReturnTable}}; - - if (sqEnumStrMap.count(returnTypeString)) - { - return sqEnumStrMap[returnTypeString]; - } - else - { - return SqReturnDefault; // previous default value - } + 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>(); +} + +ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) +{ + AUTOHOOK_DISPATCH_MODULE(server.dll) + + g_pSquirrel<ScriptContext::SERVER> = new SquirrelManager<ScriptContext::SERVER>; + + g_pSquirrel<ScriptContext::SERVER>->__sq_defconst = module.Offset(0x1F550).As<sq_defconstType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_compilebuffer = module.Offset(0x3110).As<sq_compilebufferType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushroottable = module.Offset(0x5840).As<sq_pushroottableType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_call = module.Offset(0x8620).As<sq_callType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_newarray = module.Offset(0x39F0).As<sq_newarrayType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_arrayappend = module.Offset(0x3C70).As<sq_arrayappendType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_newtable = module.Offset(0x3960).As<sq_newtableType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_newslot = module.Offset(0x7080).As<sq_newslotType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_pushstring = module.Offset(0x3440).As<sq_pushstringType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushinteger = module.Offset(0x36A0).As<sq_pushintegerType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushfloat = module.Offset(0x3800).As<sq_pushfloatType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushbool = module.Offset(0x3710).As<sq_pushboolType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushasset = module.Offset(0x3560).As<sq_pushassetType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushvector = module.Offset(0x3780).As<sq_pushvectorType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_raiseerror = module.Offset(0x8440).As<sq_raiseerrorType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_getstring = module.Offset(0x60A0).As<sq_getstringType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getinteger = module.Offset(0x60C0).As<sq_getintegerType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getfloat = module.Offset(0x60E0).As<sq_getfloatType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getbool = module.Offset(0x6110).As<sq_getboolType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getasset = module.Offset(0x5FF0).As<sq_getassetType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getuserdata = module.Offset(0x63B0).As<sq_getuserdataType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_getvector = module.Offset(0x6120).As<sq_getvectorType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_get = module.Offset(0x7C00).As<sq_getType>(); + + g_pSquirrel<ScriptContext::SERVER>->__sq_createuserdata = module.Offset(0x38D0).As<sq_createuserdataType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_setuserdatatypeid = module.Offset(0x6470).As<sq_setuserdatatypeidType>(); + + 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>); + + // 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>(); } diff --git a/NorthstarDLL/squirrel.h b/NorthstarDLL/squirrel.h index e465ddc6..2fa50ba6 100644 --- a/NorthstarDLL/squirrel.h +++ b/NorthstarDLL/squirrel.h @@ -1,39 +1,43 @@ -#include <../modmanager.h> #pragma once -void InitialiseClientSquirrel(HMODULE baseAddress); -void InitialiseServerSquirrel(HMODULE baseAddress); +#include "squirreldatatypes.h" +#include "vector.h" // stolen from ttf2sdk: sqvm types typedef float SQFloat; typedef long SQInteger; typedef unsigned long SQUnsignedInteger; typedef char SQChar; - typedef SQUnsignedInteger SQBool; -typedef SQInteger SQRESULT; -const SQRESULT SQRESULT_ERROR = -1; -const SQRESULT SQRESULT_NULL = 0; -const SQRESULT SQRESULT_NOTNULL = 1; +enum SQRESULT : SQInteger +{ + SQRESULT_ERROR = -1, + SQRESULT_NULL = 0, + SQRESULT_NOTNULL = 1, +}; -typedef SQInteger (*SQFunction)(void* sqvm); +typedef SQRESULT (*SQFunction)(HSquirrelVM* sqvm); -enum SQReturnTypeEnum +enum class eSQReturnType { - SqReturnFloat = 0x1, - SqReturnVector = 0x3, - SqReturnInteger = 0x5, - SqReturnBoolean = 0x6, - SqReturnEntity = 0xD, - SqReturnString = 0x21, - SqReturnDefault = 0x20, - SqReturnArrays = 0x25, - SqReturnAsset = 0x28, - SqReturnTable = 0x26, + Float = 0x1, + Vector = 0x3, + Integer = 0x5, + Boolean = 0x6, + Entity = 0xD, + String = 0x21, + Default = 0x20, + Arrays = 0x25, + Asset = 0x28, + Table = 0x26, }; -const char* sq_getTypeName(int type); +const std::map<SQRESULT, const char*> PrintSQRESULT = { + {SQRESULT_ERROR, "SQRESULT_ERROR"}, + {SQRESULT_NULL, "SQRESULT_NULL"}, + {SQRESULT_NOTNULL, "SQRESULT_NOTNULL"} +}; struct CompileBufferState { @@ -49,6 +53,7 @@ struct CompileBufferState } }; + struct SQFuncRegistration { const char* squirrelFuncName; @@ -56,758 +61,262 @@ struct SQFuncRegistration const char* helpText; const char* returnTypeString; const char* argTypes; - __int32 unknown1; - __int32 devLevel; + uint32_t unknown1; + uint32_t devLevel; const char* shortNameMaybe; - __int32 unknown2; - SQReturnTypeEnum returnTypeEnum; - __int32* externalBufferPointer; - __int64 externalBufferSize; - __int64 unknown3; - __int64 unknown4; - void* funcPtr; + 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->returnTypeEnum = SqReturnDefault; + this->returnType = eSQReturnType::Default; } }; -SQReturnTypeEnum GetReturnTypeEnumFromString(const char* returnTypeString); - -struct CallInfo; -struct SQTable; -struct SQString; -struct SQFunctionProto; -struct SQClosure; -struct SQSharedState; -struct StringTable; -struct SQStructInstance; -struct SQStructDef; -struct SQNativeClosure; -struct SQArray; -struct SQInstruction; - -/* 127 */ -enum SQObjectType : __int32 -{ - _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 alignas(8) SQObjectValue -{ - SQString* asString; - SQTable* asTable; - SQClosure* asClosure; - SQFunctionProto* asFuncProto; - SQStructDef* asStructDef; - __int64 asInteger; - SQStructInstance* asStructInstance; - float asFloat; - SQNativeClosure* asNativeClosure; - SQArray* asArray; -}; - -/* 128 */ -struct alignas(8) SQObject -{ - SQObjectType _Type; - __int32 _structOffset; - SQObjectValue _VAL; -}; - -struct tableNode -{ - SQObject val; - SQObject key; - tableNode* next; -}; - -/* 138 */ -struct alignas(8) SQString -{ - __int64* vftable; - __int32 uiRef; - __int32 uiRef1; - SQString* _next_maybe; - SQSharedState* sharedState; - __int32 length; - uint8_t gap_24[4]; - char _hash[8]; - char _val[1]; -}; - -/* 137 */ -struct alignas(8) SQTable -{ - __int64* vftable; - uint8_t gap_08[4]; - __int32 uiRef; - uint8_t gap_10[8]; - void* pointer_18; - void* pointer_20; - void* _sharedState; - __int64 field_30; - tableNode* _nodes; - __int32 _numOfNodes; - __int32 size; - __int32 field_48; - __int32 _usedNodes; - uint8_t _gap_50[20]; - __int32 field_64; - uint8_t _gap_68[80]; -}; - -/* 140 */ -struct alignas(8) SQClosure -{ - void* vftable; - uint8_t gap_08[4]; - __int32 uiRef; - void* pointer_10; - void* pointer_18; - void* pointer_20; - void* sharedState; - SQObject obj_30; - SQObject _function; - SQObject* _outervalues; - uint8_t gap_58[8]; - uint8_t gap_60[96]; - SQObject* objectPointer_C0; -}; - -/* 139 */ -struct alignas(8) SQFunctionProto -{ - void* vftable; - uint8_t gap_08[4]; - __int32 uiRef; - uint8_t gap_10[8]; - void* pointer_18; - void* pointer_20; - void* sharedState; - void* pointer_30; - SQObject fileName; - SQObject funcName; - SQObject obj_58; - uint8_t gap_68[64]; - __int32 nParameters; - uint8_t gap_AC[60]; - __int32 nDefaultParams; - uint8_t gap_EC[200]; -}; - -/* 152 */ -struct SQStructDef -{ - uint8_t gap_0[56]; - SQString* name; - uint8_t gap_[300]; -}; - -/* 150 */ -struct SQStructInstance -{ - void* vftable; - uint8_t gap_8[16]; - void* pointer_18; - uint8_t gap_20[8]; - SQSharedState* _sharedState; - uint8_t gap_30[8]; - SQObject data[1]; -}; - -/* 157 */ -struct alignas(8) SQNativeClosure -{ - void* vftable; - uint8_t gap_08[4]; - __int32 uiRef; - uint8_t gap_10[88]; - SQString* _name; - uint8_t gap_0[300]; -}; - -/* 148 */ -struct SQSharedState -{ - uint8_t gap_0[72]; - StringTable* _stringtable; - uint8_t gap_50[30000]; -}; - -/* 149 */ -struct StringTable -{ - uint8_t gap_0[12]; - int _numofslots; - uint8_t gap_10[200]; -}; - -/* 129 */ -struct alignas(8) HSquirrelVM +enum class ScriptContext : int { - void* vftable; - __int32 uiRef; - uint8_t gap_8[12]; - void* _toString; - void* _roottable_pointer; - void* pointer_28; - CallInfo* ci; - CallInfo* _callsstack; - __int32 _callsstacksize; - __int32 _stackbase; - SQObject* _stackOfCurrentFunction; - SQSharedState* sharedState; - void* pointer_58; - void* pointer_60; - __int32 _top; - SQObject* _stack; - uint8_t gap_78[8]; - SQObject* _vargsstack; - uint8_t gap_88[8]; - SQObject temp_reg; - uint8_t gapA0[8]; - void* pointer_A8; - uint8_t gap_B0[8]; - SQObject _roottable_object; - SQObject _lasterror; - SQObject _errorHandler; - __int64 field_E8; - __int32 traps; - uint8_t gap_F4[12]; - __int32 _nnativecalls; - __int32 _suspended; - __int32 _suspended_root; - __int32 _callstacksize; - __int32 _suspended_target; - __int32 field_114; - __int32 _suspend_varargs; - SQObject* _object_pointer_120; + SERVER, + CLIENT, + UI, }; -/* 136 */ -struct alignas(8) CallInfo -{ - SQInstruction* ip; - SQObject* _literals; - SQObject obj10; - SQObject closure; - __int32 _etraps[4]; - __int32 _root; - short _vargs_size; - short _vargs_base; -}; - -/* 135 */ -enum SQOpcode : int -{ - _OP_LOAD = 0x0, - _OP_LOADCOPY = 0x1, - _OP_LOADINT = 0x2, - _OP_LOADFLOAT = 0x3, - _OP_DLOAD = 0x4, - _OP_TAILCALL = 0x5, - _OP_CALL = 0x6, - _OP_PREPCALL = 0x7, - _OP_PREPCALLK = 0x8, - _OP_GETK = 0x9, - _OP_MOVE = 0xA, - _OP_NEWSLOT = 0xB, - _OP_DELETE = 0xC, - _OP_SET = 0xD, - _OP_GET = 0xE, - _OP_EQ = 0xF, - _OP_NE = 0x10, - _OP_ARITH = 0x11, - _OP_BITW = 0x12, - _OP_RETURN = 0x13, - _OP_LOADNULLS = 0x14, - _OP_LOADROOTTABLE = 0x15, - _OP_LOADBOOL = 0x16, - _OP_DMOVE = 0x17, - _OP_JMP = 0x18, - _OP_JNZ = 0x19, - _OP_JZ = 0x1A, - _OP_LOADFREEVAR = 0x1B, - _OP_VARGC = 0x1C, - _OP_GETVARGV = 0x1D, - _OP_NEWTABLE = 0x1E, - _OP_NEWARRAY = 0x1F, - _OP_APPENDARRAY = 0x20, - _OP_GETPARENT = 0x21, - _OP_COMPOUND_ARITH = 0x22, - _OP_COMPOUND_ARITH_LOCAL = 0x23, - _OP_INCREMENT_PREFIX = 0x24, - _OP_INCREMENT_PREFIX_LOCAL = 0x25, - _OP_INCREMENT_PREFIX_STRUCTFIELD = 0x26, - _OP_INCREMENT_POSTFIX = 0x27, - _OP_INCREMENT_POSTFIX_LOCAL = 0x28, - _OP_INCREMENT_POSTFIX_STRUCTFIELD = 0x29, - _OP_CMP = 0x2A, - _OP_EXISTS = 0x2B, - _OP_INSTANCEOF = 0x2C, - _OP_NEG = 0x2D, - _OP_NOT = 0x2E, - _OP_BWNOT = 0x2F, - _OP_CLOSURE = 0x30, - _OP_FOREACH = 0x31, - _OP_FOREACH_STATICARRAY_START = 0x32, - _OP_FOREACH_STATICARRAY_NEXT = 0x33, - _OP_FOREACH_STATICARRAY_NESTEDSTRUCT_START = 0x34, - _OP_FOREACH_STATICARRAY_NESTEDSTRUCT_NEXT = 0x35, - _OP_DELEGATE = 0x36, - _OP_CLONE = 0x37, - _OP_TYPEOF = 0x38, - _OP_PUSHTRAP = 0x39, - _OP_POPTRAP = 0x3A, - _OP_THROW = 0x3B, - _OP_CLASS = 0x3C, - _OP_NEWSLOTA = 0x3D, - _OP_EQ_LITERAL = 0x3E, - _OP_NE_LITERAL = 0x3F, - _OP_FOREACH_SETUP = 0x40, - _OP_ASSERT_FAILED = 0x41, - _OP_ADD = 0x42, - _OP_SUB = 0x43, - _OP_MUL = 0x44, - _OP_DIV = 0x45, - _OP_MOD = 0x46, - _OP_PREPCALLK_CALL = 0x47, - _OP_PREPCALLK_MOVE_CALL = 0x48, - _OP_PREPCALLK_LOADINT_CALL = 0x49, - _OP_CMP_JZ = 0x4A, - _OP_INCREMENT_LOCAL_DISCARD_JMP = 0x4B, - _OP_JZ_RETURN = 0x4C, - _OP_JZ_LOADBOOL_RETURN = 0x4D, - _OP_NEWVECTOR = 0x4E, - _OP_ZEROVECTOR = 0x4F, - _OP_GET_VECTOR_COMPONENT = 0x50, - _OP_SET_VECTOR_COMPONENT = 0x51, - _OP_VECTOR_COMPONENT_MINUSEQ = 0x52, - _OP_VECTOR_COMPONENT_PLUSEQ = 0x53, - _OP_VECTOR_COMPONENT_MULEQ = 0x54, - _OP_VECTOR_COMPONENT_DIVEQ = 0x55, - _OP_VECTOR_NORMALIZE = 0x56, - _OP_VECTOR_NORMALIZE_IN_PLACE = 0x57, - _OP_VECTOR_DOT_PRODUCT = 0x58, - _OP_VECTOR_DOT_PRODUCT2D = 0x59, - _OP_VECTOR_CROSS_PRODUCT = 0x5A, - _OP_VECTOR_CROSS_PRODUCT2D = 0x5B, - _OP_VECTOR_LENGTH = 0x5C, - _OP_VECTOR_LENGTHSQR = 0x5D, - _OP_VECTOR_LENGTH2D = 0x5E, - _OP_VECTOR_LENGTH2DSQR = 0x5F, - _OP_VECTOR_DISTANCE = 0x60, - _OP_VECTOR_DISTANCESQR = 0x61, - _OP_VECTOR_DISTANCE2D = 0x62, - _OP_VECTOR_DISTANCE2DSQR = 0x63, - _OP_INCREMENT_LOCAL_DISCARD = 0x64, - _OP_FASTCALL = 0x65, - _OP_FASTCALL_NATIVE = 0x66, - _OP_FASTCALL_NATIVE_ARGTYPECHECK = 0x67, - _OP_FASTCALL_ENV = 0x68, - _OP_FASTCALL_NATIVE_ENV = 0x69, - _OP_FASTCALL_NATIVE_ENV_ARGTYPECHECK = 0x6A, - _OP_LOADGLOBALARRAY = 0x6B, - _OP_GETGLOBAL = 0x6C, - _OP_SETGLOBAL = 0x6D, - _OP_COMPOUND_ARITH_GLOBAL = 0x6E, - _OP_GETSTRUCTFIELD = 0x6F, - _OP_SETSTRUCTFIELD = 0x70, - _OP_COMPOUND_ARITH_STRUCTFIELD = 0x71, - _OP_NEWSTRUCT = 0x72, - _OP_GETSUBSTRUCT = 0x73, - _OP_GETSUBSTRUCT_DYNAMIC = 0x74, - _OP_TYPECAST = 0x75, - _OP_TYPECHECK = 0x76, - _OP_TYPECHECK_ORNULL = 0x77, - _OP_TYPECHECK_NOTNULL = 0x78, - _OP_CHECK_ENTITY_CLASS = 0x79, - _OP_UNREACHABLE = 0x7A, - _OP_ARRAY_RESIZE = 0x7B, -}; - -/* 141 */ -struct alignas(8) SQStackInfos -{ - char* _name; - char* _sourceName; - __int32 _line; -}; - -/* 151 */ -struct alignas(4) SQInstruction -{ - int op; - int arg1; - int output; - __int16 arg2; - __int16 arg3; -}; - -/* 154 */ -struct SQLexer -{ - uint8_t gap_0[112]; -}; - -/* 153 */ -struct SQCompiler -{ - uint8_t gap_0[4]; - __int32 _token; - uint8_t gap_8[8]; - SQObject object_10; - SQLexer lexer; - uint8_t gap_1[768]; -}; - -/* 155 */ -struct CSquirrelVM -{ - uint8_t gap_0[8]; - HSquirrelVM* sqvm; -}; - -struct SQVector -{ - SQObjectType _Type; - float x; - float y; - float z; -}; - -struct SQArray -{ - void* vftable; - __int32 uiRef; - uint8_t gap_24[36]; - SQObject* _values; - __int32 _usedSlots; - __int32 _allocated; -}; - -#define INCREMENT_REFERENCECOUNT(val) \ - if ((val->_Type & SQOBJECT_REF_COUNTED) != 0) \ - ++val->_VAL.asString->uiRef; - -#define DECREMENT_REFERENCECOUNT(val) \ - if ((val->_Type & SQOBJECT_REF_COUNTED) != 0) \ - { \ - if (val->_VAL.asString->uiRef-- == 1) \ - { \ - spdlog::info("Deleted SQObject of type {} with address {:X}", sq_getTypeName(val->_Type), val->_VAL.asInteger); \ - (*(void(__fastcall**)(SQString*))(&val->_VAL.asString->vftable[1]))(val->_VAL.asString); \ - } \ - } +const char* GetContextName(ScriptContext context); +eSQReturnType SQReturnTypeFromString(const char* pReturnType); +const char* SQTypeNameFromID(const int iTypeId); // core sqvm funcs -typedef SQRESULT (*sq_compilebufferType)(void* sqvm, CompileBufferState* compileBuffer, const char* file, int a1, ScriptContext a2); -extern sq_compilebufferType ClientSq_compilebuffer; -extern sq_compilebufferType ServerSq_compilebuffer; - -typedef void (*sq_pushroottableType)(void* sqvm); -extern sq_pushroottableType ClientSq_pushroottable; -extern sq_pushroottableType ServerSq_pushroottable; - -typedef SQRESULT (*sq_callType)(void* sqvm, SQInteger s1, SQBool a2, SQBool a3); -extern sq_callType ClientSq_call; -extern sq_callType ServerSq_call; +typedef int64_t (*RegisterSquirrelFuncType)(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown); +typedef void (*sq_defconstType)(CSquirrelVM* sqvm, const SQChar* name, int value); -typedef int64_t (*RegisterSquirrelFuncType)(void* sqvm, SQFuncRegistration* funcReg, char unknown); -extern RegisterSquirrelFuncType ClientRegisterSquirrelFunc; -extern RegisterSquirrelFuncType ServerRegisterSquirrelFunc; +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); // sq stack array funcs -typedef void (*sq_newarrayType)(void* sqvm, SQInteger stackpos); -extern sq_newarrayType ClientSq_newarray; -extern sq_newarrayType ServerSq_newarray; +typedef void (*sq_newarrayType)(HSquirrelVM* sqvm, SQInteger iStackpos); +typedef SQRESULT (*sq_arrayappendType)(HSquirrelVM* sqvm, SQInteger iStackpos); -typedef SQRESULT (*sq_arrayappendType)(void* sqvm, SQInteger stackpos); -extern sq_arrayappendType ClientSq_arrayappend; -extern sq_arrayappendType ServerSq_arrayappend; +// 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_pushstringType)(void* sqvm, const SQChar* str, SQInteger stackpos); -extern sq_pushstringType ClientSq_pushstring; -extern sq_pushstringType ServerSq_pushstring; +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); -// weird how these don't take a stackpos arg? -typedef void (*sq_pushintegerType)(void* sqvm, SQInteger i); -extern sq_pushintegerType ClientSq_pushinteger; -extern sq_pushintegerType ServerSq_pushinteger; -typedef void (*sq_pushfloatType)(void* sqvm, SQFloat f); -extern sq_pushfloatType ClientSq_pushfloat; -extern sq_pushfloatType ServerSq_pushfloat; - -typedef void (*sq_pushboolType)(void* sqvm, SQBool b); -extern sq_pushboolType ClientSq_pushbool; -extern sq_pushboolType ServerSq_pushbool; - -typedef SQInteger (*sq_pusherrorType)(void* sqvm, const SQChar* error); -extern sq_pusherrorType ClientSq_pusherror; -extern sq_pusherrorType ServerSq_pusherror; +// 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); + +// sq stack userpointer funcs +typedef void* (*sq_createuserdataType)(HSquirrelVM* sqvm, SQInteger iSize); +typedef SQRESULT (*sq_setuserdatatypeidType)(HSquirrelVM* sqvm, SQInteger iStackpos, uint64_t iTypeId); -typedef void (*sq_defconst)(void* sqvm, const SQChar* name, int value); -extern sq_defconst ClientSq_defconst; -extern sq_defconst ServerSq_defconst; +template <ScriptContext context> class SquirrelManager +{ + private: + std::vector<SQFuncRegistration*> m_funcRegistrations; -typedef SQRESULT (*sq_pushAssetType)(void* sqvm, const SQChar* assetName, SQInteger nameLength); -extern sq_pushAssetType ServerSq_pushAsset; -extern sq_pushAssetType ClientSq_pushAsset; + public: + CSquirrelVM* m_pSQVM; + std::map<std::string, SQFunction> m_funcOverrides = {}; + std::map<std::string, SQFunction> m_funcOriginals = {}; + + bool m_bFatalCompilationErrors = false; + + #pragma region SQVM funcs + RegisterSquirrelFuncType RegisterSquirrelFunc; + sq_defconstType __sq_defconst; + + sq_compilebufferType __sq_compilebuffer; + sq_callType __sq_call; + sq_raiseerrorType __sq_raiseerror; + + 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_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_createuserdataType __sq_createuserdata; + sq_setuserdatatypeidType __sq_setuserdatatypeid; +#pragma endregion -// sq stack get funcs -typedef const SQChar* (*sq_getstringType)(void* sqvm, SQInteger stackpos); -extern sq_getstringType ClientSq_getstring; -extern sq_getstringType ServerSq_getstring; + public: + SquirrelManager() : m_pSQVM(nullptr) {} -typedef SQInteger (*sq_getintegerType)(void* sqvm, SQInteger stackpos); -extern sq_getintegerType ClientSq_getinteger; -extern sq_getintegerType ServerSq_getinteger; + 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); -typedef SQFloat (*sq_getfloatType)(void*, SQInteger stackpos); -extern sq_getfloatType ClientSq_getfloat; -extern sq_getfloatType ServerSq_getfloat; + #pragma region SQVM func wrappers + inline void defconst(CSquirrelVM* sqvm, const SQChar* pName, int nValue) + { + __sq_defconst(sqvm, pName, nValue); + } -typedef SQBool (*sq_getboolType)(void*, SQInteger stackpos); -extern sq_getboolType ClientSq_getbool; -extern sq_getboolType ServerSq_getbool; + inline SQRESULT + compilebuffer(CompileBufferState* bufferState, const SQChar* bufferName = "unnamedbuffer", const SQBool bShouldThrowError = false) + { + return __sq_compilebuffer(m_pSQVM->sqvm, bufferState, bufferName, -1, bShouldThrowError); + } -typedef SQRESULT (*sq_getType)(void* sqvm, SQInteger idx); -extern sq_getType ServerSq_sq_get; -extern sq_getType ClientSq_sq_get; + inline SQRESULT call(HSquirrelVM* sqvm, const SQInteger args) + { + return __sq_call(sqvm, args + 1, false, false); + } -// sq table functions -typedef SQRESULT (*sq_newTableType)(void* sqvm); -extern sq_newTableType ServerSq_newTable; -extern sq_newTableType ClientSq_newTable; + inline SQInteger raiseerror(HSquirrelVM* sqvm, const const SQChar* sError) + { + return __sq_raiseerror(sqvm, sError); + } -typedef SQRESULT (*sq_newSlotType)(void* sqvm, int idx, bool bStatic); -extern sq_newSlotType ServerSq_newSlot; -extern sq_newSlotType ClientSq_newSlot; + inline void newarray(HSquirrelVM* sqvm, const SQInteger stackpos = 0) + { + __sq_newarray(sqvm, stackpos); + } -template <ScriptContext context> class SquirrelManager -{ - private: - std::vector<SQFuncRegistration*> m_funcRegistrations; + inline SQRESULT arrayappend(HSquirrelVM* sqvm, const SQInteger stackpos) + { + return __sq_arrayappend(sqvm, stackpos); + } - public: - void* sqvm; - void* sqvm2; + inline SQRESULT newtable(HSquirrelVM* sqvm) + { + return __sq_newtable(sqvm); + } - public: - SquirrelManager() : sqvm(nullptr) {} + inline SQRESULT newslot(HSquirrelVM* sqvm, SQInteger idx, SQBool bStatic) + { + return __sq_newslot(sqvm, idx, bStatic); + } - void VMCreated(void* newSqvm) + inline void pushroottable(HSquirrelVM* sqvm) { - sqvm = newSqvm; - sqvm2 = *((void**)((char*)sqvm + 8)); // honestly not 100% sure on what this is, but alot of functions take it - - for (SQFuncRegistration* funcReg : m_funcRegistrations) - { - spdlog::info("Registering {} function {}", GetContextName(context), funcReg->squirrelFuncName); - - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - ClientRegisterSquirrelFunc(sqvm, funcReg, 1); - else - ServerRegisterSquirrelFunc(sqvm, funcReg, 1); - } - for (auto& pair : g_ModManager->DependencyConstants) - { - bool wasFound = false; - for (Mod& dependency : g_ModManager->m_loadedMods) - { - if (dependency.Name == pair.second) - { - wasFound = dependency.Enabled; - break; - } - } - if (context == ScriptContext::SERVER) - ServerSq_defconst(sqvm, pair.first.c_str(), wasFound); - else - ClientSq_defconst(sqvm, pair.first.c_str(), wasFound); - } + __sq_pushroottable(sqvm); } - void VMDestroyed() + inline void pushstring(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1) { - sqvm = nullptr; + __sq_pushstring(sqvm, sVal, length); } - void ExecuteCode(const char* code) + inline void pushinteger(HSquirrelVM* sqvm, const SQInteger iVal) { - // ttf2sdk checks ThreadIsInMainThread here, might be good to do that? doesn't seem like an issue rn tho - - if (!sqvm) - { - spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context)); - return; - } - - spdlog::info("Executing {} script code {} ", GetContextName(context), code); - - std::string strCode(code); - CompileBufferState bufferState = CompileBufferState(strCode); - - SQRESULT compileResult; - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - compileResult = ClientSq_compilebuffer(sqvm2, &bufferState, "console", -1, context); - else if (context == ScriptContext::SERVER) - compileResult = ServerSq_compilebuffer(sqvm2, &bufferState, "console", -1, context); - - spdlog::info("sq_compilebuffer returned {}", compileResult); - if (compileResult >= 0) - { - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - { - ClientSq_pushroottable(sqvm2); - SQRESULT callResult = ClientSq_call(sqvm2, 1, false, false); - spdlog::info("sq_call returned {}", callResult); - } - else if (context == ScriptContext::SERVER) - { - ServerSq_pushroottable(sqvm2); - SQRESULT callResult = ServerSq_call(sqvm2, 1, false, false); - spdlog::info("sq_call returned {}", callResult); - } - } + __sq_pushinteger(sqvm, iVal); } - int setupfunc(const char* funcname) + inline void pushfloat(HSquirrelVM* sqvm, const SQFloat flVal) { - int result = -2; - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - { - ClientSq_pushroottable(sqvm2); - ClientSq_pushstring(sqvm2, funcname, -1); - result = ClientSq_sq_get(sqvm2, -2); - if (result != SQRESULT_ERROR) - { - ClientSq_pushroottable(sqvm2); - } - } - else if (context == ScriptContext::SERVER) - { - ServerSq_pushroottable(sqvm2); - ServerSq_pushstring(sqvm2, funcname, -1); - result = ServerSq_sq_get(sqvm2, -2); - if (result != SQRESULT_ERROR) - { - ServerSq_pushroottable(sqvm2); - } - } - return result; + __sq_pushfloat(sqvm, flVal); } - void pusharg(int arg) + inline void pushbool(HSquirrelVM* sqvm, const SQBool bVal) { - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - ClientSq_pushinteger(sqvm2, arg); - else if (context == ScriptContext::SERVER) - ServerSq_pushinteger(sqvm2, arg); + __sq_pushbool(sqvm, bVal); } - void pusharg(const char* arg) + + inline void pushasset(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1) { - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - ClientSq_pushstring(sqvm2, arg, -1); - else if (context == ScriptContext::SERVER) - ServerSq_pushstring(sqvm2, arg, -1); + __sq_pushasset(sqvm, sVal, length); } - void pusharg(float arg) + + inline void pushvector(HSquirrelVM* sqvm, const Vector3 pVal) { - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - ClientSq_pushfloat(sqvm2, arg); - else if (context == ScriptContext::SERVER) - ServerSq_pushfloat(sqvm2, arg); + __sq_pushvector(sqvm, *(float**)&pVal); } - void pusharg(bool arg) + + inline const SQChar* getstring(HSquirrelVM* sqvm, const SQInteger stackpos) { - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - ClientSq_pushbool(sqvm2, arg); - else if (context == ScriptContext::SERVER) - ServerSq_pushbool(sqvm2, arg); + return __sq_getstring(sqvm, stackpos); } - int call(int args) + inline SQInteger getinteger(HSquirrelVM* sqvm, const SQInteger stackpos) { - int result = -2; - if (context == ScriptContext::CLIENT || context == ScriptContext::UI) - result = ClientSq_call(sqvm2, args + 1, false, false); - else if (context == ScriptContext::SERVER) - result = ServerSq_call(sqvm2, args + 1, false, false); + return __sq_getinteger(sqvm, stackpos); + } - return result; + inline SQFloat getfloat(HSquirrelVM* sqvm, const SQInteger stackpos) + { + return __sq_getfloat(sqvm, stackpos); } - void AddFuncRegistration(std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func) + inline SQBool getbool(HSquirrelVM* sqvm, const SQInteger stackpos) { - SQFuncRegistration* reg = new SQFuncRegistration; + return __sq_getbool(sqvm, stackpos); + } - reg->squirrelFuncName = new char[name.size() + 1]; - strcpy((char*)reg->squirrelFuncName, name.c_str()); - reg->cppFuncName = reg->squirrelFuncName; + inline SQRESULT get(HSquirrelVM* sqvm, const SQInteger stackpos) + { + return __sq_get(sqvm, stackpos); + } - reg->helpText = new char[helpText.size() + 1]; - strcpy((char*)reg->helpText, helpText.c_str()); + inline Vector3 getvector(HSquirrelVM* sqvm, const SQInteger stackpos) + { + float* pRet = __sq_getvector(sqvm, stackpos); + return *(Vector3*)&pRet; + } - reg->returnTypeString = new char[returnType.size() + 1]; - strcpy((char*)reg->returnTypeString, returnType.c_str()); - reg->returnTypeEnum = GetReturnTypeEnumFromString(returnType.c_str()); + inline SQRESULT getasset(HSquirrelVM* sqvm, const SQInteger stackpos, const char** result) + { + return __sq_getasset(sqvm, stackpos, result); + } - reg->argTypes = new char[argTypes.size() + 1]; - strcpy((char*)reg->argTypes, argTypes.c_str()); + 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 + } - reg->funcPtr = reinterpret_cast<void*>(func); + template <typename T> inline T* createuserdata(HSquirrelVM* sqvm, SQInteger size) + { + void* ret = __sq_createuserdata(sqvm, size); + memset(ret, 0, size); + return (T*)ret; + } - m_funcRegistrations.push_back(reg); + inline SQRESULT setuserdatatypeid(HSquirrelVM* sqvm, const SQInteger stackpos, uint64_t typeId) + { + return __sq_setuserdatatypeid(sqvm, stackpos, typeId); } +#pragma endregion }; -extern SquirrelManager<ScriptContext::CLIENT>* g_ClientSquirrelManager; -extern SquirrelManager<ScriptContext::SERVER>* g_ServerSquirrelManager; -extern SquirrelManager<ScriptContext::UI>* g_UISquirrelManager; +template <ScriptContext context> SquirrelManager<context>* g_pSquirrel; diff --git a/NorthstarDLL/squirreldatatypes.h b/NorthstarDLL/squirreldatatypes.h new file mode 100644 index 00000000..818ce2a4 --- /dev/null +++ b/NorthstarDLL/squirreldatatypes.h @@ -0,0 +1,484 @@ +#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; + +typedef void (*releasehookType)(void* val, int size); + +/* 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; +}; + +/* 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 _callsstacksize; + 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 _callstacksize; + int _suspended_target; + int trapAmount; + int _suspend_varargs; + int unknown_field_11C; + SQObject object_120; +}; + +/* 150 */ +struct SQStructInstance +{ + void* vftable; + unsigned char gap_8[16]; + void* pointer_18; + unsigned char gap_20[8]; + SQSharedState* _sharedState; + unsigned char gap[8]; + SQObject data[20]; +}; + +/* 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; + unsigned char gap_43E8[8]; + 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 +{ + unsigned char gap_0[8]; + HSquirrelVM* sqvm; + unsigned char gap_10[44]; + int loadEnumFromFileMaybe; + unsigned char gap_40[200]; +}; + +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/tier0.cpp b/NorthstarDLL/tier0.cpp new file mode 100644 index 00000000..61ad7783 --- /dev/null +++ b/NorthstarDLL/tier0.cpp @@ -0,0 +1,37 @@ +#include "pch.h" +#include "tier0.h" + +// use the Tier0 namespace for tier0 funcs +namespace Tier0 +{ + IMemAlloc* g_pMemAllocSingleton; + + ErrorType Error; + CommandLineType CommandLine; + Plat_FloatTimeType Plat_FloatTime; + ThreadInServerFrameThreadType ThreadInServerFrameThread; +} // namespace Tier0 + +typedef Tier0::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")); + Tier0::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 + Tier0::Error = module.GetExport("Error").As<Tier0::ErrorType>(); + Tier0::CommandLine = module.GetExport("CommandLine").As<Tier0::CommandLineType>(); + Tier0::Plat_FloatTime = module.GetExport("Plat_FloatTime").As<Tier0::Plat_FloatTimeType>(); + Tier0::ThreadInServerFrameThread = module.GetExport("ThreadInServerFrameThread").As<Tier0::ThreadInServerFrameThreadType>(); +} diff --git a/NorthstarDLL/tier0.h b/NorthstarDLL/tier0.h new file mode 100644 index 00000000..92a63027 --- /dev/null +++ b/NorthstarDLL/tier0.h @@ -0,0 +1,68 @@ +#pragma once +namespace Tier0 +{ + 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) {} + virtual void CreateCmdLine(int argc, char** argv) {} + virtual void unknown() {} + virtual const char* GetCmdLine(void) const {} + + virtual const char* CheckParm(const char* psz, const char** ppszValue = 0) const {} + virtual void RemoveParm() const {} + virtual void AppendParm(const char* pszParm, const char* pszValues) {} + + virtual const char* ParmValue(const char* psz, const char* pDefaultVal = 0) const {} + virtual int ParmValue(const char* psz, int nDefaultVal) const {} + virtual float ParmValue(const char* psz, float flDefaultVal) const {} + + virtual int ParmCount() const {} + virtual int FindParm(const char* psz) const {} + virtual const char* GetParm(int nIndex) const {} + virtual void SetParm(int nIndex, char const* pParm) {} + + // virtual const char** GetParms() const {} + }; + + extern IMemAlloc* g_pMemAllocSingleton; + + typedef void (*ErrorType)(const char* fmt, ...); + extern ErrorType Error; + + typedef CCommandLine* (*CommandLineType)(); + extern CommandLineType CommandLine; + + typedef double (*Plat_FloatTimeType)(); + extern Plat_FloatTimeType Plat_FloatTime; + + typedef bool (*ThreadInServerFrameThreadType)(); + extern ThreadInServerFrameThreadType ThreadInServerFrameThread; +} // namespace Tier0 + +void TryCreateGlobalMemAlloc(); diff --git a/NorthstarDLL/vector.h b/NorthstarDLL/vector.h new file mode 100644 index 00000000..a5d68003 --- /dev/null +++ b/NorthstarDLL/vector.h @@ -0,0 +1,44 @@ +#pragma once
+
+union Vector3
+{
+ struct
+ {
+ float x;
+ float y;
+ float z;
+ };
+
+ float raw[3];
+
+ Vector3() : x(0), y(0), z(0) {}
+ Vector3(float* pRawFloats) // assumes float[3] => vector
+ {
+ memcpy(raw, pRawFloats, sizeof(this));
+ }
+
+ // 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;
+ }
+};
\ No newline at end of file diff --git a/NorthstarLauncher/NorthstarLauncher.vcxproj b/NorthstarLauncher/NorthstarLauncher.vcxproj index 33930f14..1c3ebe37 100644 --- a/NorthstarLauncher/NorthstarLauncher.vcxproj +++ b/NorthstarLauncher/NorthstarLauncher.vcxproj @@ -14,7 +14,7 @@ <VCProjectVersion>16.0</VCProjectVersion> <Keyword>Win32Proj</Keyword> <ProjectGuid>{0ea82cb0-53fe-4d4c-96df-47fa970513d0}</ProjectGuid> - <RootNamespace>NorthstarLauncher</RootNamespace> + <RootNamespace>LauncherInjector</RootNamespace> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> <ProjectName>NorthstarLauncher</ProjectName> </PropertyGroup> @@ -65,6 +65,9 @@ <AdditionalDependencies>shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> <StackReserveSize>8000000</StackReserveSize> </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(SolutionDir)..\..\"</Command> + </PostBuildEvent> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> @@ -85,6 +88,9 @@ <AdditionalDependencies>shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> <StackReserveSize>8000000</StackReserveSize> </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(SolutionDir)..\..\"</Command> + </PostBuildEvent> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="main.cpp" /> diff --git a/NorthstarLauncher/NorthstarLauncher.vcxproj.filters b/NorthstarLauncher/NorthstarLauncher.vcxproj.filters index 9cdc5aab..76b907bf 100644 --- a/NorthstarLauncher/NorthstarLauncher.vcxproj.filters +++ b/NorthstarLauncher/NorthstarLauncher.vcxproj.filters @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> diff --git a/NorthstarLauncher/main.cpp b/NorthstarLauncher/main.cpp index b097b2f8..078cbc4a 100644 --- a/NorthstarLauncher/main.cpp +++ b/NorthstarLauncher/main.cpp @@ -7,6 +7,11 @@ #include <shlwapi.h> #include <iostream> +#pragma comment(lib, "Ws2_32.lib") + +#include <winsock2.h> +#include <WS2tcpip.h> + namespace fs = std::filesystem; extern "C" @@ -125,6 +130,41 @@ void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* MessageBoxA(GetForegroundWindow(), text, "Northstar Launcher Error", 0); } +void AwaitOriginStartup() +{ + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); + SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (sock != INVALID_SOCKET) + { + const int LSX_PORT = 3216; + + sockaddr_in lsxAddr; + lsxAddr.sin_family = AF_INET; + inet_pton(AF_INET, "127.0.0.1", &(lsxAddr.sin_addr)); + lsxAddr.sin_port = htons(LSX_PORT); + + std::cout << "LSX: connect()" << std::endl; + connect(sock, (struct sockaddr*)&lsxAddr, sizeof(lsxAddr)); + + char buf[4096]; + memset(buf, 0, sizeof(buf)); + + do + { + recv(sock, buf, 4096, 0); + std::cout << buf << std::endl; + + // honestly really shit, this isn't needed for origin due to being able to check OriginClientService + // but for ea desktop we don't have anything like this, so atm we just have to wait to ensure that we start after logging in + Sleep(8000); + } while (!strstr(buf, "<LSX>")); // ensure we're actually getting data from lsx + } + + WSACleanup(); // cleanup sockets and such so game can contact lsx itself +} + void EnsureOriginStarted() { if (GetProcessByName(L"Origin.exe") || GetProcessByName(L"EADesktop.exe")) @@ -170,9 +210,14 @@ void EnsureOriginStarted() std::cout << "[*] Waiting for Origin..." << std::endl; - // wait for origin to be ready, this process is created when origin is ready enough to launch game without any errors - while (!GetProcessByName(L"OriginClientService.exe") && !GetProcessByName(L"EADesktop.exe")) - Sleep(200); + // wait for origin process to boot + do + { + Sleep(500); + } while (!GetProcessByName(L"OriginClientService.exe") && !GetProcessByName(L"EADesktop.exe")); + + // wait for origin to be ready to start + AwaitOriginStartup(); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); diff --git a/NorthstarLauncher/ns_icon.ico b/NorthstarLauncher/ns_icon.ico Binary files differindex fc9ad166..76f4af69 100644 --- a/NorthstarLauncher/ns_icon.ico +++ b/NorthstarLauncher/ns_icon.ico diff --git a/R2Northstar.sln b/R2Northstar.sln index 341c66dd..b66b0dcd 100644 --- a/R2Northstar.sln +++ b/R2Northstar.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32014.148 @@ -9,4 +9,4 @@ Check [BUILD.md](https://github.com/R2Northstar/NorthstarLauncher/blob/main/BUIL ## Format -This project uses [clang-format](https://clang.llvm.org/docs/ClangFormat.html), make sure you run `clang-format -i --style=file NorthstarLauncher/*.cpp NorthstarLauncher/*.h NorthstarDLL/*.cpp NorthstarDLL/*.h` when opening a Pull Request. Check the tool's website for instructions on how to integrate it with your IDE.
\ No newline at end of file +This project uses [clang-format](https://clang.llvm.org/docs/ClangFormat.html), make sure you run `clang-format -i --style=file NorthstarLauncher/*.cpp NorthstarLauncher/*.h NorthstarDLL/*.cpp NorthstarDLL/*.h` when opening a Pull Request. Check the tool's website for instructions on how to integrate it with your IDE. diff --git a/loader_launcher_proxy/Memory.cpp b/loader_launcher_proxy/Memory.cpp index 344fa834..200246eb 100644 --- a/loader_launcher_proxy/Memory.cpp +++ b/loader_launcher_proxy/Memory.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "Memory.h" extern HMODULE hTier0Module; IMemAlloc** g_ppMemAllocSingleton; @@ -78,12 +77,6 @@ void* realloc(void* old_ptr, size_t size) return nullptr; } -void* operator new(size_t n) -{ - return malloc(n); -} +void* operator new(size_t n) { return malloc(n); } -void operator delete(void* p) noexcept -{ - return free(p); -} +void operator delete(void* p) { return free(p); } diff --git a/loader_launcher_proxy/Memory.h b/loader_launcher_proxy/Memory.h index 824d9d0f..c983966c 100644 --- a/loader_launcher_proxy/Memory.h +++ b/loader_launcher_proxy/Memory.h @@ -2,23 +2,23 @@ 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); - }; +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; + VTable* m_vtable; }; diff --git a/loader_launcher_proxy/dllmain.cpp b/loader_launcher_proxy/dllmain.cpp index a382bc97..a86dddea 100644 --- a/loader_launcher_proxy/dllmain.cpp +++ b/loader_launcher_proxy/dllmain.cpp @@ -16,33 +16,31 @@ using CreateInterfaceFn = void* (*)(const char* pName, int* pReturnCode); // does not seem to ever be used extern "C" __declspec(dllexport) void* __fastcall CreateInterface(const char* pName, int* pReturnCode) { - // AppSystemCreateInterfaceFn(pName, pReturnCode); - printf("external CreateInterface: name: %s\n", pName); + //AppSystemCreateInterfaceFn(pName, pReturnCode); + printf("external CreateInterface: name: %s\n", pName); - static CreateInterfaceFn launcher_CreateInterface = (CreateInterfaceFn)GetProcAddress(hLauncherModule, "CreateInterface"); - auto res = launcher_CreateInterface(pName, pReturnCode); + static CreateInterfaceFn launcher_CreateInterface = (CreateInterfaceFn)GetProcAddress(hLauncherModule, "CreateInterface"); + auto res = launcher_CreateInterface(pName, pReturnCode); - printf("external CreateInterface: return code: %p\n", res); - return res; + printf("external CreateInterface: return code: %p\n", res); + return res; } bool GetExePathWide(wchar_t* dest, DWORD destSize) { - if (!dest) - return NULL; - if (destSize < MAX_PATH) - return NULL; + if (!dest) return NULL; + if (destSize < MAX_PATH) return NULL; - DWORD length = GetModuleFileNameW(NULL, dest, destSize); - return length && PathRemoveFileSpecW(dest); + DWORD length = GetModuleFileNameW(NULL, dest, destSize); + return length && PathRemoveFileSpecW(dest); } FARPROC GetLauncherMain() { - static FARPROC Launcher_LauncherMain; - if (!Launcher_LauncherMain) - Launcher_LauncherMain = GetProcAddress(hLauncherModule, "LauncherMain"); - return Launcher_LauncherMain; + static FARPROC Launcher_LauncherMain; + if (!Launcher_LauncherMain) + Launcher_LauncherMain = GetProcAddress(hLauncherModule, "LauncherMain"); + return Launcher_LauncherMain; } void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* location) @@ -50,6 +48,7 @@ void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* char text[4096]; std::string message = std::system_category().message(dwMessageId); sprintf_s(text, "Failed to load the %ls at \"%ls\" (%lu):\n\n%hs", libName, location, dwMessageId, message.c_str()); + if (dwMessageId == 126 && std::filesystem::exists(location)) { sprintf_s( @@ -58,20 +57,24 @@ void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* "found.", text); } + MessageBoxA(GetForegroundWindow(), text, "Northstar Launcher Proxy Error", 0); } -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +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; + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; } wchar_t exePath[4096]; @@ -79,96 +82,86 @@ wchar_t dllPath[4096]; bool ShouldLoadNorthstar() { - bool loadNorthstar = !strstr(GetCommandLineA(), "-vanilla"); - - if (!loadNorthstar) - return loadNorthstar; - - auto runNorthstarFile = std::ifstream("run_northstar.txt"); - if (runNorthstarFile) - { - std::stringstream runNorthstarFileBuffer; - runNorthstarFileBuffer << runNorthstarFile.rdbuf(); - runNorthstarFile.close(); - if (runNorthstarFileBuffer.str().starts_with("0")) - loadNorthstar = false; - } - return loadNorthstar; + bool loadNorthstar = !strstr(GetCommandLineA(), "-vanilla"); + + if (!loadNorthstar) + return loadNorthstar; + + auto runNorthstarFile = std::ifstream("run_northstar.txt"); + if (runNorthstarFile) + { + std::stringstream runNorthstarFileBuffer; + runNorthstarFileBuffer << runNorthstarFile.rdbuf(); + runNorthstarFile.close(); + if (runNorthstarFileBuffer.str().starts_with("0")) + loadNorthstar = false; + } + return loadNorthstar; } bool LoadNorthstar() { - FARPROC Hook_Init = nullptr; - { - swprintf_s(dllPath, L"%s\\Northstar.dll", exePath); - hHookModule = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); - if (hHookModule) - Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); - if (!hHookModule || Hook_Init == nullptr) - { - LibraryLoadError(GetLastError(), L"Northstar.dll", dllPath); - return false; - } - } - - ((bool (*)())Hook_Init)(); - return true; + FARPROC Hook_Init = nullptr; + { + swprintf_s(dllPath, L"%s\\Northstar.dll", exePath); + hHookModule = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (hHookModule) Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); + if (!hHookModule || Hook_Init == nullptr) + { + LibraryLoadError(GetLastError(), L"Northstar.dll", dllPath); + return false; + } + } + + ((bool (*)()) Hook_Init)(); + return true; } extern "C" __declspec(dllexport) int LauncherMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { - { - if (!GetExePathWide(exePath, 4096)) - { - MessageBoxA( - GetForegroundWindow(), - "Failed getting game directory.\nThe game cannot continue and has to exit.", - "Northstar Launcher Proxy Error", - 0); - return 1; - } - - bool loadNorthstar = ShouldLoadNorthstar(); - - if (loadNorthstar) - { - swprintf_s(dllPath, L"%s\\bin\\x64_retail\\tier0.dll", exePath); - hTier0Module = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!hTier0Module) - { - LibraryLoadError(GetLastError(), L"tier0.dll", dllPath); - return 1; - } - - if (!LoadNorthstar()) - return 1; - } - // else printf("\n\n WILL !!!NOT!!! LOAD NORTHSTAR\n\n"); - - swprintf_s(dllPath, L"%s\\bin\\x64_retail\\launcher.org.dll", exePath); - hLauncherModule = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!hLauncherModule) - { - LibraryLoadError(GetLastError(), L"launcher.org.dll", dllPath); - return 1; - } - } - - auto LauncherMain = GetLauncherMain(); - if (!LauncherMain) - MessageBoxA( - GetForegroundWindow(), - "Failed loading launcher.org.dll.\nThe game cannot continue and has to exit.", - "Northstar Launcher Proxy Error", - 0); - // auto result = ((__int64(__fastcall*)())LauncherMain)(); - // auto result = ((signed __int64(__fastcall*)(__int64))LauncherMain)(0i64); - return ((int(__fastcall*)(HINSTANCE, HINSTANCE, LPSTR, int))LauncherMain)(hInstance, hPrevInstance, lpCmdLine, nCmdShow); + { + if (!GetExePathWide(exePath, 4096)) + { + MessageBoxA(GetForegroundWindow(), "Failed getting game directory.\nThe game cannot continue and has to exit.", "Northstar Launcher Proxy Error", 0); + return 1; + } + + bool loadNorthstar = ShouldLoadNorthstar(); + + if (loadNorthstar) + { + swprintf_s(dllPath, L"%s\\bin\\x64_retail\\tier0.dll", exePath); + hTier0Module = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hTier0Module) + { + LibraryLoadError(GetLastError(), L"tier0.dll", dllPath); + return 1; + } + + if (!LoadNorthstar()) + return 1; + } + //else printf("\n\n WILL !!!NOT!!! LOAD NORTHSTAR\n\n"); + + swprintf_s(dllPath, L"%s\\bin\\x64_retail\\launcher.org.dll", exePath); + hLauncherModule = LoadLibraryExW(dllPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hLauncherModule) + { + LibraryLoadError(GetLastError(), L"launcher.org.dll", dllPath); + return 1; + } + } + + auto LauncherMain = GetLauncherMain(); + if (!LauncherMain) + MessageBoxA(GetForegroundWindow(), "Failed loading launcher.org.dll.\nThe game cannot continue and has to exit.", "Northstar Launcher Proxy Error", 0); + //auto result = ((__int64(__fastcall*)())LauncherMain)(); + //auto result = ((signed __int64(__fastcall*)(__int64))LauncherMain)(0i64); + return ((int(__fastcall*)(HINSTANCE, HINSTANCE, LPSTR, int))LauncherMain)(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } // doubt that will help us here (in launcher.dll) though -extern "C" -{ - __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; - __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +extern "C" { + __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } diff --git a/loader_launcher_proxy/framework.h b/loader_launcher_proxy/framework.h index 2fd581a2..d2a898df 100644 --- a/loader_launcher_proxy/framework.h +++ b/loader_launcher_proxy/framework.h @@ -1,6 +1,6 @@ #pragma once -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from windows.headers +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #define WIN32_EXTRA_LEAN #define VC_EXTRALEAN // Windows Header Files diff --git a/loader_launcher_proxy/loader_launcher_proxy.vcxproj.filters b/loader_launcher_proxy/loader_launcher_proxy.vcxproj.filters index f64932cd..4bf52578 100644 --- a/loader_launcher_proxy/loader_launcher_proxy.vcxproj.filters +++ b/loader_launcher_proxy/loader_launcher_proxy.vcxproj.filters @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> diff --git a/loader_launcher_proxy/pch.h b/loader_launcher_proxy/pch.h index cc757129..885d5d62 100644 --- a/loader_launcher_proxy/pch.h +++ b/loader_launcher_proxy/pch.h @@ -10,4 +10,4 @@ // add headers that you want to pre-compile here #include "framework.h" -#endif // PCH_H +#endif //PCH_H diff --git a/loader_wsock32_proxy/dllmain.cpp b/loader_wsock32_proxy/dllmain.cpp index 7feb278d..e09cd183 100644 --- a/loader_wsock32_proxy/dllmain.cpp +++ b/loader_wsock32_proxy/dllmain.cpp @@ -10,10 +10,8 @@ HINSTANCE hL = 0; bool GetExePathWide(wchar_t* dest, DWORD destSize) { - if (!dest) - return NULL; - if (destSize < MAX_PATH) - return NULL; + if (!dest) return NULL; + if (destSize < MAX_PATH) return NULL; DWORD length = GetModuleFileNameW(NULL, dest, destSize); return length && PathRemoveFileSpecW(dest); @@ -31,11 +29,7 @@ BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) if (!GetExePathWide(exePath, 4096)) { - MessageBoxA( - GetForegroundWindow(), - "Failed getting game directory.\nThe game cannot continue and has to exit.", - "Northstar Wsock32 Proxy Error", - 0); + MessageBoxA(GetForegroundWindow(), "Failed getting game directory.\nThe game cannot continue and has to exit.", "Northstar Wsock32 Proxy Error", 0); return true; } @@ -65,14 +59,7 @@ BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) { if (!std::filesystem::exists(temp_dir)) { - swprintf_s( - buffer2, - L"Failed copying wsock32.dll from system32 to \"%s\"\n\n%S\n\nFurthermore, we failed copying wsock32.dll into " - L"temporary directory at \"%s\"\n\n%S", - buffer1, - e1.what(), - temp_dir.c_str(), - e2.what()); + swprintf_s(buffer2, L"Failed copying wsock32.dll from system32 to \"%s\"\n\n%S\n\nFurthermore, we failed copying wsock32.dll into temporary directory at \"%s\"\n\n%S", buffer1, e1.what(), temp_dir.c_str(), e2.what()); MessageBoxW(GetForegroundWindow(), buffer2, L"Northstar Wsock32 Proxy Error", 0); return false; } @@ -81,8 +68,7 @@ BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) } } hL = LoadLibraryExW(buffer1, 0, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!hL) - { + if (!hL) { LibraryLoadError(GetLastError(), L"wsock32.org.dll", buffer1); return false; } @@ -118,62 +104,73 @@ extern "C" FARPROC PA = NULL; int RunASM(); - void PROXY_EnumProtocolsA() + void PROXY_EnumProtocolsA() { PA = p[1]; RunASM(); } - void PROXY_EnumProtocolsW() + + void PROXY_EnumProtocolsW() { PA = p[2]; RunASM(); } - void PROXY_GetAddressByNameA() + + void PROXY_GetAddressByNameA() { PA = p[4]; RunASM(); } - void PROXY_GetAddressByNameW() + + void PROXY_GetAddressByNameW() { PA = p[5]; RunASM(); } - void PROXY_WEP() + + void PROXY_WEP() { PA = p[17]; RunASM(); } - void PROXY_WSARecvEx() + + void PROXY_WSARecvEx() { PA = p[30]; RunASM(); } - void PROXY___WSAFDIsSet() + + void PROXY___WSAFDIsSet() { PA = p[36]; RunASM(); } - void PROXY_getnetbyname() + + void PROXY_getnetbyname() { PA = p[45]; RunASM(); } - void PROXY_getsockopt() + + void PROXY_getsockopt() { PA = p[52]; RunASM(); } - void PROXY_inet_network() + + void PROXY_inet_network() { PA = p[56]; RunASM(); } - void PROXY_s_perror() + + void PROXY_s_perror() { PA = p[67]; RunASM(); } - void PROXY_setsockopt() + + void PROXY_setsockopt() { PA = p[72]; RunASM(); diff --git a/loader_wsock32_proxy/hookutils.cpp b/loader_wsock32_proxy/hookutils.cpp deleted file mode 100644 index f359e6ec..00000000 --- a/loader_wsock32_proxy/hookutils.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include <cstdio> - -#include "pch.h" -#include "../NorthstarDLL/hookutils.h" - -#define HU_ERROR(...) \ - { \ - char err[2048]; \ - snprintf(err, sizeof(err), __VA_ARGS__); \ - MessageBoxA(GetForegroundWindow(), err, "Northstar Wsock32 Proxy Error", 0); \ - } - -void HookEnabler::CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOriginal, const char* targetName) -{ - // the macro for this uses ppTarget's name as targetName, and this typically starts with & - // targetname is used for debug stuff and debug output is nicer if we don't have this - if (*targetName == '&') - targetName++; - - if (MH_CreateHook(ppTarget, ppDetour, ppOriginal) == MH_OK) - { - HookTarget* target = new HookTarget; - target->targetAddress = ppTarget; - target->targetName = (char*)targetName; - - m_hookTargets.push_back(target); - } - else - { - if (targetName != nullptr) - { - HU_ERROR("MH_CreateHook failed for function %s", targetName); - } - else - { - HU_ERROR("MH_CreateHook failed for unknown function"); - } - } -} - -HookEnabler::~HookEnabler() -{ - for (auto& hook : m_hookTargets) - { - if (MH_EnableHook(hook->targetAddress) != MH_OK) - { - if (hook->targetName != nullptr) - { - HU_ERROR("MH_EnableHook failed for function %s", hook->targetName); - } - else - { - HU_ERROR("MH_EnableHook failed for unknown function"); - } - } - else - { - // HU_ERROR("Enabling hook %s", hook->targetName); - } - } -} diff --git a/loader_wsock32_proxy/include/MinHook.h b/loader_wsock32_proxy/include/MinHook.h new file mode 100644 index 00000000..15c0a875 --- /dev/null +++ b/loader_wsock32_proxy/include/MinHook.h @@ -0,0 +1,186 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) + #error MinHook supports only x86 and x64 systems. +#endif + +#include <windows.h> + +// MinHook Error Codes. +typedef enum MH_STATUS +{ + // Unknown error. Should not be returned. + MH_UNKNOWN = -1, + + // Successful. + MH_OK = 0, + + // MinHook is already initialized. + MH_ERROR_ALREADY_INITIALIZED, + + // MinHook is not initialized yet, or already uninitialized. + MH_ERROR_NOT_INITIALIZED, + + // The hook for the specified target function is already created. + MH_ERROR_ALREADY_CREATED, + + // The hook for the specified target function is not created yet. + MH_ERROR_NOT_CREATED, + + // The hook for the specified target function is already enabled. + MH_ERROR_ENABLED, + + // The hook for the specified target function is not enabled yet, or already + // disabled. + MH_ERROR_DISABLED, + + // The specified pointer is invalid. It points the address of non-allocated + // and/or non-executable region. + MH_ERROR_NOT_EXECUTABLE, + + // The specified target function cannot be hooked. + MH_ERROR_UNSUPPORTED_FUNCTION, + + // Failed to allocate memory. + MH_ERROR_MEMORY_ALLOC, + + // Failed to change the memory protection. + MH_ERROR_MEMORY_PROTECT, + + // The specified module is not loaded. + MH_ERROR_MODULE_NOT_FOUND, + + // The specified function is not found. + MH_ERROR_FUNCTION_NOT_FOUND +} +MH_STATUS; + +// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, +// MH_QueueEnableHook or MH_QueueDisableHook. +#define MH_ALL_HOOKS NULL + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize the MinHook library. You must call this function EXACTLY ONCE + // at the beginning of your program. + MH_STATUS WINAPI MH_Initialize(VOID); + + // Uninitialize the MinHook library. You must call this function EXACTLY + // ONCE at the end of your program. + MH_STATUS WINAPI MH_Uninitialize(VOID); + + // Creates a Hook for the specified target function, in disabled state. + // Parameters: + // pTarget [in] A pointer to the target function, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + // ppTarget [out] A pointer to the target function, which will be used + // with other functions. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); + + // Removes an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); + + // Enables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // enabled in one go. + MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); + + // Disables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // disabled in one go. + MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); + + // Queues to enable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be enabled. + MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); + + // Queues to disable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be disabled. + MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); + + // Applies all queued changes in one go. + MH_STATUS WINAPI MH_ApplyQueued(VOID); + + // Translates the MH_STATUS to its name as a string. + const char * WINAPI MH_StatusToString(MH_STATUS status); + +#ifdef __cplusplus +} +#endif + diff --git a/loader_wsock32_proxy/include/MinHook.x64.lib b/loader_wsock32_proxy/include/MinHook.x64.lib Binary files differnew file mode 100644 index 00000000..a346f386 --- /dev/null +++ b/loader_wsock32_proxy/include/MinHook.x64.lib diff --git a/loader_wsock32_proxy/loader.cpp b/loader_wsock32_proxy/loader.cpp index 9642df45..34a4b546 100644 --- a/loader_wsock32_proxy/loader.cpp +++ b/loader_wsock32_proxy/loader.cpp @@ -1,6 +1,6 @@ #include "pch.h" #include "loader.h" -#include "../NorthstarDLL/hookutils.h" +#include "include/MinHook.h" #include <string> #include <system_error> #include <sstream> @@ -14,11 +14,7 @@ void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* sprintf_s(text, "Failed to load the %ls at \"%ls\" (%lu):\n\n%hs", libName, location, dwMessageId, message.c_str()); if (dwMessageId == 126 && std::filesystem::exists(location)) { - sprintf_s( - text, - "%s\n\nThe file at the specified location DOES exist, so this error indicates that one of its *dependencies* failed to be " - "found.", - text); + sprintf_s(text, "%s\n\nThe file at the specified location DOES exist, so this error indicates that one of its *dependencies* failed to be found.", text); } MessageBoxA(GetForegroundWindow(), text, "Northstar Wsock32 Proxy Error", 0); } @@ -36,7 +32,7 @@ bool ShouldLoadNorthstar() std::stringstream runNorthstarFileBuffer; runNorthstarFileBuffer << runNorthstarFile.rdbuf(); runNorthstarFile.close(); - if (!runNorthstarFileBuffer.str().starts_with("0")) + if (!runNorthstarFileBuffer.str()._Starts_with("0")) loadNorthstar = true; } return loadNorthstar; @@ -48,8 +44,7 @@ bool LoadNorthstar() { swprintf_s(buffer1, L"%s\\Northstar.dll", exePath); auto hHookModule = LoadLibraryExW(buffer1, 0, LOAD_WITH_ALTERED_SEARCH_PATH); - if (hHookModule) - Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); + if (hHookModule) Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); if (!hHookModule || Hook_Init == nullptr) { LibraryLoadError(GetLastError(), L"Northstar.dll", buffer1); @@ -57,11 +52,11 @@ bool LoadNorthstar() } } - ((bool (*)())Hook_Init)(); + ((bool (*)()) Hook_Init)(); return true; } -typedef int (*LauncherMainType)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); +typedef int(*LauncherMainType)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); LauncherMainType LauncherMainOriginal; int LauncherMainHook(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) @@ -78,28 +73,21 @@ bool ProvisionNorthstar() if (MH_Initialize() != MH_OK) { - MessageBoxA( - GetForegroundWindow(), "MH_Initialize failed\nThe game cannot continue and has to exit.", "Northstar Wsock32 Proxy Error", 0); + MessageBoxA(GetForegroundWindow(), "MH_Initialize failed\nThe game cannot continue and has to exit.", "Northstar Wsock32 Proxy Error", 0); return false; } auto launcherHandle = GetModuleHandleA("launcher.dll"); if (!launcherHandle) { - MessageBoxA( - GetForegroundWindow(), - "Launcher isn't loaded yet.\nThe game cannot continue and has to exit.", - "Northstar Wsock32 Proxy Error", - 0); + MessageBoxA(GetForegroundWindow(), "Launcher isn't loaded yet.\nThe game cannot continue and has to exit.", "Northstar Wsock32 Proxy Error", 0); return false; } - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - reinterpret_cast<void*>(GetProcAddress(launcherHandle, "LauncherMain")), - &LauncherMainHook, - reinterpret_cast<LPVOID*>(&LauncherMainOriginal)); + LPVOID pTarget = GetProcAddress(launcherHandle, "LauncherMain"); + if (MH_CreateHook(pTarget, &LauncherMainHook, reinterpret_cast<LPVOID*>(&LauncherMainOriginal)) != MH_OK || MH_EnableHook(pTarget) != MH_OK) + MessageBoxA( + GetForegroundWindow(), "Hook creation failed for function LauncherMain.", "Northstar Wsock32 Proxy Error", 0); return true; } diff --git a/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj b/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj index 3e72fb78..d65a8f12 100644 --- a/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj +++ b/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj @@ -61,14 +61,14 @@ <PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <LanguageStandard>stdcpp20</LanguageStandard> - <AdditionalIncludeDirectories>..\NorthstarDLL\</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\NorthstarDedicatedTest\</AdditionalIncludeDirectories> </ClCompile> <Link> <SubSystem>Windows</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> <ModuleDefinitionFile>wsock32.def</ModuleDefinitionFile> - <AdditionalDependencies>..\NorthstarDLL\include\MinHook.x64.lib;mswsock.lib;ws2_32.lib;Shlwapi.lib;imagehlp.lib;dbghelp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>.\include\MinHook.x64.lib;mswsock.lib;ws2_32.lib;Shlwapi.lib;imagehlp.lib;dbghelp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> @@ -81,7 +81,7 @@ <ConformanceMode>true</ConformanceMode> <PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> - <AdditionalIncludeDirectories>..\NorthstarDLL\</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\NorthstarDedicatedTest\</AdditionalIncludeDirectories> <LanguageStandard>stdcpp20</LanguageStandard> </ClCompile> <Link> @@ -91,16 +91,16 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> <ModuleDefinitionFile>wsock32.def</ModuleDefinitionFile> - <AdditionalDependencies>..\NorthstarDLL\include\MinHook.x64.lib;mswsock.lib;ws2_32.lib;Shlwapi.lib;imagehlp.lib;dbghelp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;wsock32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>.\include\MinHook.x64.lib;mswsock.lib;ws2_32.lib;Shlwapi.lib;imagehlp.lib;dbghelp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;wsock32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> + <ClInclude Include="include\MinHook.h" /> <ClInclude Include="loader.h" /> <ClInclude Include="pch.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="dllmain.cpp" /> - <ClCompile Include="hookutils.cpp" /> <ClCompile Include="loader.cpp" /> <ClCompile Include="pch.cpp"> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> diff --git a/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj.filters b/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj.filters index 6d131e5b..d3c022de 100644 --- a/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj.filters +++ b/loader_wsock32_proxy/loader_wsock32_proxy.vcxproj.filters @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> @@ -13,6 +13,9 @@ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> </Filter> + <Filter Include="Header Files\include"> + <UniqueIdentifier>{1b1575f7-3332-478b-9e85-9c0978249a70}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClInclude Include="pch.h"> @@ -21,6 +24,9 @@ <ClInclude Include="loader.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="include\MinHook.h"> + <Filter>Header Files\include</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="dllmain.cpp"> @@ -32,9 +38,6 @@ <ClCompile Include="loader.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="hookutils.cpp"> - <Filter>Source Files</Filter> - </ClCompile> </ItemGroup> <ItemGroup> <MASM Include="wsock32.asm"> @@ -46,4 +49,4 @@ <Filter>Source Files</Filter> </None> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/loader_wsock32_proxy/pch.h b/loader_wsock32_proxy/pch.h index 6e8873a1..0103ff59 100644 --- a/loader_wsock32_proxy/pch.h +++ b/loader_wsock32_proxy/pch.h @@ -7,10 +7,10 @@ #ifndef PCH_H #define PCH_H -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files #include <windows.h> #include "include/MinHook.h" -#endif // PCH_H +#endif //PCH_H |