commit
2bb1fcdcf9
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@ -11,7 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build binaries
|
name: Build binaries
|
||||||
runs-on: windows-latest
|
runs-on: windows-2022
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
configuration:
|
configuration:
|
||||||
@ -35,11 +35,10 @@ jobs:
|
|||||||
lfs: false
|
lfs: false
|
||||||
|
|
||||||
- name: Add msbuild to PATH
|
- name: Add msbuild to PATH
|
||||||
uses: microsoft/setup-msbuild@v1.0.2
|
uses: microsoft/setup-msbuild@v1.1
|
||||||
|
|
||||||
- name: Generate project files
|
- name: Generate project files
|
||||||
#run: tools/premake5 vs2019 --ci-build
|
run: tools/premake5 vs2022
|
||||||
run: tools/premake5 vs2019
|
|
||||||
|
|
||||||
- name: Set up problem matching
|
- name: Set up problem matching
|
||||||
uses: ammaraskar/msvc-problem-matcher@master
|
uses: ammaraskar/msvc-problem-matcher@master
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
[![Build](https://github.com/momo5502/open-iw5/workflows/Build/badge.svg)](https://github.com/momo5502/open-iw5/actions)
|
||||||
![issues](https://img.shields.io/github/issues/momo5502/open-iw5.svg)
|
![issues](https://img.shields.io/github/issues/momo5502/open-iw5.svg)
|
||||||
![license](https://img.shields.io/github/license/momo5502/open-iw5.svg)
|
![license](https://img.shields.io/github/license/momo5502/open-iw5.svg)
|
||||||
![forks](https://img.shields.io/github/forks/momo5502/open-iw5.svg)
|
![forks](https://img.shields.io/github/forks/momo5502/open-iw5.svg)
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
@echo off
|
@echo off
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
tools\premake5 %* vs2019
|
tools\premake5 %* vs2022
|
||||||
|
48
premake5.lua
48
premake5.lua
@ -42,15 +42,14 @@ workspace "open-iw5"
|
|||||||
objdir "%{wks.location}/obj"
|
objdir "%{wks.location}/obj"
|
||||||
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
|
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
|
||||||
|
|
||||||
configurations {
|
configurations { "Debug", "Release" }
|
||||||
"Debug",
|
|
||||||
"Release",
|
|
||||||
}
|
|
||||||
|
|
||||||
architecture "x32"
|
language "C++"
|
||||||
platforms "x32"
|
cppdialect "C++20"
|
||||||
|
|
||||||
|
architecture "x86"
|
||||||
|
platforms "Win32"
|
||||||
|
|
||||||
buildoptions "/std:c++latest"
|
|
||||||
systemversion "latest"
|
systemversion "latest"
|
||||||
symbols "On"
|
symbols "On"
|
||||||
staticruntime "On"
|
staticruntime "On"
|
||||||
@ -65,33 +64,21 @@ workspace "open-iw5"
|
|||||||
"No64BitChecks"
|
"No64BitChecks"
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration "windows"
|
filter "platforms:Win*"
|
||||||
defines {
|
defines { "_WINDOWS", "WIN32" }
|
||||||
"_WINDOWS",
|
filter {}
|
||||||
"WIN32",
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration "Release"
|
filter "configurations:Release"
|
||||||
optimize "Full"
|
optimize "Full"
|
||||||
buildoptions "/Os"
|
buildoptions { "/Os" }
|
||||||
|
defines { "NDEBUG" }
|
||||||
|
flags { "FatalCompileWarnings" }
|
||||||
|
filter {}
|
||||||
|
|
||||||
defines {
|
filter "configurations:Debug"
|
||||||
"NDEBUG",
|
|
||||||
}
|
|
||||||
|
|
||||||
flags {
|
|
||||||
"FatalCompileWarnings",
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration "Debug"
|
|
||||||
optimize "Debug"
|
optimize "Debug"
|
||||||
|
defines { "DEBUG", "_DEBUG" }
|
||||||
defines {
|
filter {}
|
||||||
"DEBUG",
|
|
||||||
"_DEBUG",
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration {}
|
|
||||||
|
|
||||||
project "open-iw5"
|
project "open-iw5"
|
||||||
kind "ConsoleApp"
|
kind "ConsoleApp"
|
||||||
@ -103,7 +90,6 @@ workspace "open-iw5"
|
|||||||
linkoptions "/IGNORE:4254 /DYNAMICBASE:NO /SAFESEH:NO /LARGEADDRESSAWARE"
|
linkoptions "/IGNORE:4254 /DYNAMICBASE:NO /SAFESEH:NO /LARGEADDRESSAWARE"
|
||||||
linkoptions "/LAST:.main"
|
linkoptions "/LAST:.main"
|
||||||
|
|
||||||
|
|
||||||
files {
|
files {
|
||||||
"./src/**.rc",
|
"./src/**.rc",
|
||||||
"./src/**.hpp",
|
"./src/**.hpp",
|
||||||
|
@ -23,15 +23,34 @@ namespace game
|
|||||||
|
|
||||||
SL_GetStringOfSize_t SL_GetStringOfSize;
|
SL_GetStringOfSize_t SL_GetStringOfSize;
|
||||||
|
|
||||||
|
Scr_AddEntityNum_t Scr_AddEntityNum;
|
||||||
|
|
||||||
|
Scr_Notify_t Scr_Notify;
|
||||||
|
|
||||||
Sys_ShowConsole_t Sys_ShowConsole;
|
Sys_ShowConsole_t Sys_ShowConsole;
|
||||||
|
|
||||||
VM_Notify_t VM_Notify;
|
VM_Notify_t VM_Notify;
|
||||||
|
|
||||||
|
BG_NetDataChecksum_t BG_NetDataChecksum;
|
||||||
|
|
||||||
|
LiveStorage_GetPersistentDataDefVersion_t LiveStorage_GetPersistentDataDefVersion;
|
||||||
|
|
||||||
|
LiveStorage_GetPersistentDataDefFormatChecksum_t LiveStorage_GetPersistentDataDefFormatChecksum;
|
||||||
|
|
||||||
|
SV_DirectConnect_t SV_DirectConnect;
|
||||||
|
|
||||||
|
SV_ClientEnterWorld_t SV_ClientEnterWorld;
|
||||||
|
|
||||||
|
SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString;
|
||||||
|
|
||||||
|
SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString;
|
||||||
|
|
||||||
|
XUIDToString_t XUIDToString;
|
||||||
|
|
||||||
decltype(longjmp)* _longjmp;
|
decltype(longjmp)* _longjmp;
|
||||||
|
|
||||||
int* cmd_args;
|
CmdArgs* sv_cmd_args;
|
||||||
int* cmd_argc;
|
CmdArgs* cmd_args;
|
||||||
const char*** cmd_argv;
|
|
||||||
|
|
||||||
short* scrVarGlob;
|
short* scrVarGlob;
|
||||||
char** scrMemTreePub;
|
char** scrMemTreePub;
|
||||||
@ -49,6 +68,18 @@ namespace game
|
|||||||
|
|
||||||
scr_classStruct_t* g_classMap;
|
scr_classStruct_t* g_classMap;
|
||||||
|
|
||||||
|
int* svs_clientCount;
|
||||||
|
|
||||||
|
namespace mp
|
||||||
|
{
|
||||||
|
client_t* svs_clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace dedi
|
||||||
|
{
|
||||||
|
client_t* svs_clients;
|
||||||
|
}
|
||||||
|
|
||||||
void AddRefToValue(VariableValue* value)
|
void AddRefToValue(VariableValue* value)
|
||||||
{
|
{
|
||||||
if (value->type == SCRIPT_OBJECT)
|
if (value->type == SCRIPT_OBJECT)
|
||||||
@ -154,6 +185,31 @@ namespace game
|
|||||||
return scrMemTreeGlob + 12 * size_t(MT_AllocIndex(numBytes, type));
|
return scrMemTreeGlob + 12 * size_t(MT_AllocIndex(numBytes, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__declspec(naked) dvar_t* dvar_find_malleable_var(const char* dvarName)
|
||||||
|
{
|
||||||
|
static DWORD func = 0x531320;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov edi, dvarName
|
||||||
|
call func
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dvar_t* Dvar_FindVar(const char* dvarName)
|
||||||
|
{
|
||||||
|
if (is_dedi())
|
||||||
|
{
|
||||||
|
return dvar_find_malleable_var(dvarName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return reinterpret_cast<dvar_t*(*)(const char*)>
|
||||||
|
(SELECT_VALUE(0x539550, 0x5BDCC0, 0x0))(dvarName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const float* Scr_AllocVector(const float* v)
|
const float* Scr_AllocVector(const float* v)
|
||||||
{
|
{
|
||||||
const auto mem = static_cast<DWORD*>(MT_Alloc(16, 2));
|
const auto mem = static_cast<DWORD*>(MT_Alloc(16, 2));
|
||||||
@ -280,6 +336,31 @@ namespace game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void scr_add_string_dedi(const char* value)
|
||||||
|
{
|
||||||
|
static DWORD func = 0x4F1010;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov edi, value
|
||||||
|
call func
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scr_AddString(const char* value)
|
||||||
|
{
|
||||||
|
if (is_dedi())
|
||||||
|
{
|
||||||
|
scr_add_string_dedi(value);
|
||||||
|
}
|
||||||
|
else if (is_mp())
|
||||||
|
{
|
||||||
|
reinterpret_cast<void(*)(const char*)>
|
||||||
|
(0x56AC00)(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char* SL_ConvertToString(const unsigned int stringValue)
|
const char* SL_ConvertToString(const unsigned int stringValue)
|
||||||
{
|
{
|
||||||
if (!stringValue) return nullptr;
|
if (!stringValue) return nullptr;
|
||||||
@ -292,6 +373,85 @@ namespace game
|
|||||||
{
|
{
|
||||||
return SL_GetStringOfSize(str, user, strlen(str) + 1, 7);
|
return SL_GetStringOfSize(str, user, strlen(str) + 1, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void sv_send_client_game_state_mp(mp::client_t* /*client*/)
|
||||||
|
{
|
||||||
|
static DWORD func = 0x570FC0;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
pushad
|
||||||
|
|
||||||
|
mov esi, [esp + 0x20 + 0x4]
|
||||||
|
call func
|
||||||
|
|
||||||
|
popad
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SV_SendClientGameState(mp::client_t* client)
|
||||||
|
{
|
||||||
|
if (is_mp())
|
||||||
|
{
|
||||||
|
sv_send_client_game_state_mp(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SV_IsTestClient(int clientNum)
|
||||||
|
{
|
||||||
|
assert(clientNum < *svs_clientCount);
|
||||||
|
|
||||||
|
if (is_dedi())
|
||||||
|
{
|
||||||
|
return dedi::svs_clients[clientNum].bIsTestClient;
|
||||||
|
}
|
||||||
|
else if (is_mp())
|
||||||
|
{
|
||||||
|
return mp::svs_clients[clientNum].bIsTestClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SV_DropClient(mp::client_t* drop, const char* reason, bool tellThem)
|
||||||
|
{
|
||||||
|
if (is_mp())
|
||||||
|
{
|
||||||
|
reinterpret_cast<void(*)(mp::client_t*, const char*, bool)>
|
||||||
|
(0x570980)(drop, reason, tellThem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sv_drop_all_bots_mp()
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < *svs_clientCount; i++)
|
||||||
|
{
|
||||||
|
if (mp::svs_clients[i].header.state != CS_FREE
|
||||||
|
&& mp::svs_clients[i].header.netchan.remoteAddress.type == NA_BOT)
|
||||||
|
{
|
||||||
|
SV_DropClient(&mp::svs_clients[i], "EXE_TIMEDOUT", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SV_DropAllBots()
|
||||||
|
{
|
||||||
|
if (is_mp())
|
||||||
|
{
|
||||||
|
sv_drop_all_bots_mp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetProtocolVersion()
|
||||||
|
{
|
||||||
|
return 0x507C;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetAdr_SetType(netadr_s* addr, netadrtype_t type)
|
||||||
|
{
|
||||||
|
addr->type = type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
launcher::mode mode = launcher::mode::none;
|
launcher::mode mode = launcher::mode::none;
|
||||||
@ -344,15 +504,36 @@ namespace game
|
|||||||
|
|
||||||
native::SL_GetStringOfSize = native::SL_GetStringOfSize_t(SELECT_VALUE(0x4E13F0, 0x564650, 0x4E7490));
|
native::SL_GetStringOfSize = native::SL_GetStringOfSize_t(SELECT_VALUE(0x4E13F0, 0x564650, 0x4E7490));
|
||||||
|
|
||||||
|
native::Scr_AddEntityNum = native::Scr_AddEntityNum_t(SELECT_VALUE(0x0, 0x56ABC0, 0x4EA2F0));
|
||||||
|
|
||||||
|
native::Scr_Notify = native::Scr_Notify_t(SELECT_VALUE(0x0, 0x52B190, 0x0));
|
||||||
|
|
||||||
native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0));
|
native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0));
|
||||||
|
|
||||||
native::VM_Notify = native::VM_Notify_t(SELECT_VALUE(0x610200, 0x569720, 0x4EF450));
|
native::VM_Notify = native::VM_Notify_t(SELECT_VALUE(0x610200, 0x569720, 0x4EF450));
|
||||||
|
|
||||||
|
native::BG_NetDataChecksum = native::BG_NetDataChecksum_t(SELECT_VALUE(0x0, 0x41BB20, 0x0));
|
||||||
|
|
||||||
|
native::LiveStorage_GetPersistentDataDefVersion = native::LiveStorage_GetPersistentDataDefVersion_t(
|
||||||
|
SELECT_VALUE(0x0, 0x548D60, 0x4D0390));
|
||||||
|
|
||||||
|
native::LiveStorage_GetPersistentDataDefFormatChecksum = native::LiveStorage_GetPersistentDataDefFormatChecksum_t(
|
||||||
|
SELECT_VALUE(0x0, 0x548D80, 0x4D03D0));
|
||||||
|
|
||||||
|
native::SV_DirectConnect = native::SV_DirectConnect_t(SELECT_VALUE(0x0, 0x572750, 0x4F74C0));
|
||||||
|
|
||||||
|
native::SV_ClientEnterWorld = native::SV_ClientEnterWorld_t(SELECT_VALUE(0x0, 0x571100, 0x0));
|
||||||
|
|
||||||
|
native::SV_Cmd_TokenizeString = native::SV_Cmd_TokenizeString_t(SELECT_VALUE(0x0, 0x545D40, 0x0));
|
||||||
|
|
||||||
|
native::SV_Cmd_EndTokenizedString = native::SV_Cmd_EndTokenizedString_t(SELECT_VALUE(0x0, 0x545D70, 0x0));
|
||||||
|
|
||||||
|
native::XUIDToString = native::XUIDToString_t(SELECT_VALUE(0x4FAA30, 0x55CC20, 0x0));
|
||||||
|
|
||||||
native::_longjmp = reinterpret_cast<decltype(longjmp)*>(SELECT_VALUE(0x73AC20, 0x7363BC, 0x655558));
|
native::_longjmp = reinterpret_cast<decltype(longjmp)*>(SELECT_VALUE(0x73AC20, 0x7363BC, 0x655558));
|
||||||
|
|
||||||
native::cmd_args = reinterpret_cast<int*>(SELECT_VALUE(0x1750750, 0x1C978D0, 0x1B455F8));
|
native::sv_cmd_args = reinterpret_cast<native::CmdArgs*>(SELECT_VALUE(0x0, 0x1CAA998, 0x1B5E7D8));
|
||||||
native::cmd_argc = reinterpret_cast<int*>(SELECT_VALUE(0x1750794, 0x1C97914, 0x1B4563C));
|
native::cmd_args = reinterpret_cast<native::CmdArgs*>(SELECT_VALUE(0x1750750, 0x1C978D0, 0x1B455F8));
|
||||||
native::cmd_argv = reinterpret_cast<const char***>(SELECT_VALUE(0x17507B4, 0x1C97934, 0x1B4565C));
|
|
||||||
|
|
||||||
native::scrVarGlob = reinterpret_cast<short*>(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800));
|
native::scrVarGlob = reinterpret_cast<short*>(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800));
|
||||||
native::scrMemTreePub = reinterpret_cast<char**>(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4));
|
native::scrMemTreePub = reinterpret_cast<char**>(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4));
|
||||||
@ -371,6 +552,11 @@ namespace game
|
|||||||
|
|
||||||
native::g_classMap = reinterpret_cast<native::scr_classStruct_t*>(SELECT_VALUE(0x92D140, 0x8B4300, 0x7C0408));
|
native::g_classMap = reinterpret_cast<native::scr_classStruct_t*>(SELECT_VALUE(0x92D140, 0x8B4300, 0x7C0408));
|
||||||
|
|
||||||
|
native::svs_clientCount = reinterpret_cast<int*>(SELECT_VALUE(0x0, 0x4B5CF8C, 0x4A12E8C));
|
||||||
|
|
||||||
native::levelEntityId = reinterpret_cast<unsigned int*>(SELECT_VALUE(0x1BCBCA4, 0x208E1A4, 0x1CD873C));
|
native::levelEntityId = reinterpret_cast<unsigned int*>(SELECT_VALUE(0x1BCBCA4, 0x208E1A4, 0x1CD873C));
|
||||||
|
|
||||||
|
native::mp::svs_clients = reinterpret_cast<native::mp::client_t*>(0x4B5CF90);
|
||||||
|
native::dedi::svs_clients = reinterpret_cast<native::dedi::client_t*>(0x4A12E90);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,17 +38,46 @@ namespace game
|
|||||||
typedef unsigned int (*SL_GetStringOfSize_t)(const char* str, unsigned int user, unsigned int len, int type);
|
typedef unsigned int (*SL_GetStringOfSize_t)(const char* str, unsigned int user, unsigned int len, int type);
|
||||||
extern SL_GetStringOfSize_t SL_GetStringOfSize;
|
extern SL_GetStringOfSize_t SL_GetStringOfSize;
|
||||||
|
|
||||||
|
typedef void (*Scr_AddEntityNum_t)(int entnum, unsigned int classnum);
|
||||||
|
extern Scr_AddEntityNum_t Scr_AddEntityNum;
|
||||||
|
|
||||||
|
typedef void (*Scr_Notify_t)(gentity_s* ent, unsigned __int16 stringValue, unsigned int paramcount);
|
||||||
|
extern Scr_Notify_t Scr_Notify;
|
||||||
|
|
||||||
typedef void (*Sys_ShowConsole_t)();
|
typedef void (*Sys_ShowConsole_t)();
|
||||||
extern Sys_ShowConsole_t Sys_ShowConsole;
|
extern Sys_ShowConsole_t Sys_ShowConsole;
|
||||||
|
|
||||||
typedef void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top);
|
typedef void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top);
|
||||||
extern VM_Notify_t VM_Notify;
|
extern VM_Notify_t VM_Notify;
|
||||||
|
|
||||||
|
typedef unsigned int (*BG_NetDataChecksum_t)();
|
||||||
|
extern BG_NetDataChecksum_t BG_NetDataChecksum;
|
||||||
|
|
||||||
|
typedef int (*LiveStorage_GetPersistentDataDefVersion_t)();
|
||||||
|
extern LiveStorage_GetPersistentDataDefVersion_t LiveStorage_GetPersistentDataDefVersion;
|
||||||
|
|
||||||
|
typedef unsigned int (*LiveStorage_GetPersistentDataDefFormatChecksum_t)();
|
||||||
|
extern LiveStorage_GetPersistentDataDefFormatChecksum_t LiveStorage_GetPersistentDataDefFormatChecksum;
|
||||||
|
|
||||||
|
typedef void (*SV_DirectConnect_t)(netadr_s from);
|
||||||
|
extern SV_DirectConnect_t SV_DirectConnect;
|
||||||
|
|
||||||
|
typedef void (*SV_ClientEnterWorld_t)(mp::client_t* client, usercmd_s* cmd);
|
||||||
|
extern SV_ClientEnterWorld_t SV_ClientEnterWorld;
|
||||||
|
|
||||||
|
typedef void (*SV_Cmd_TokenizeString_t)(const char* text_in);
|
||||||
|
extern SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString;
|
||||||
|
|
||||||
|
typedef void (*SV_Cmd_EndTokenizedString_t)();
|
||||||
|
extern SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString;
|
||||||
|
|
||||||
|
typedef void (*XUIDToString_t)(const unsigned __int64* xuid, char* str);
|
||||||
|
extern XUIDToString_t XUIDToString;
|
||||||
|
|
||||||
extern decltype(longjmp)* _longjmp;
|
extern decltype(longjmp)* _longjmp;
|
||||||
|
|
||||||
extern int* cmd_args;
|
extern CmdArgs* sv_cmd_args;
|
||||||
extern int* cmd_argc;
|
extern CmdArgs* cmd_args;
|
||||||
extern const char*** cmd_argv;
|
|
||||||
|
|
||||||
extern short* scrVarGlob;
|
extern short* scrVarGlob;
|
||||||
extern char** scrMemTreePub;
|
extern char** scrMemTreePub;
|
||||||
@ -66,6 +95,18 @@ namespace game
|
|||||||
|
|
||||||
extern scr_classStruct_t* g_classMap;
|
extern scr_classStruct_t* g_classMap;
|
||||||
|
|
||||||
|
extern int* svs_clientCount;
|
||||||
|
|
||||||
|
namespace mp
|
||||||
|
{
|
||||||
|
extern client_t* svs_clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace dedi
|
||||||
|
{
|
||||||
|
extern client_t* svs_clients;
|
||||||
|
}
|
||||||
|
|
||||||
void AddRefToValue(VariableValue* value);
|
void AddRefToValue(VariableValue* value);
|
||||||
|
|
||||||
void Conbuf_AppendText(const char* message);
|
void Conbuf_AppendText(const char* message);
|
||||||
@ -76,15 +117,27 @@ namespace game
|
|||||||
|
|
||||||
void* MT_Alloc(int numBytes, int type);
|
void* MT_Alloc(int numBytes, int type);
|
||||||
|
|
||||||
|
dvar_t* Dvar_FindVar(const char* dvarName);
|
||||||
|
|
||||||
const float* Scr_AllocVector(const float* v);
|
const float* Scr_AllocVector(const float* v);
|
||||||
void Scr_ClearOutParams();
|
void Scr_ClearOutParams();
|
||||||
scr_entref_t Scr_GetEntityIdRef(unsigned int id);
|
scr_entref_t Scr_GetEntityIdRef(unsigned int id);
|
||||||
scr_call_t Scr_GetFunc(unsigned int index);
|
scr_call_t Scr_GetFunc(unsigned int index);
|
||||||
void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount);
|
void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount);
|
||||||
int Scr_SetObjectField(unsigned int classnum, int entnum, int offset);
|
int Scr_SetObjectField(unsigned int classnum, int entnum, int offset);
|
||||||
|
void Scr_AddString(const char* value);
|
||||||
|
|
||||||
const char* SL_ConvertToString(unsigned int stringValue);
|
const char* SL_ConvertToString(unsigned int stringValue);
|
||||||
unsigned int SL_GetString(const char* str, unsigned int user);
|
unsigned int SL_GetString(const char* str, unsigned int user);
|
||||||
|
|
||||||
|
void SV_SendClientGameState(mp::client_t* client);
|
||||||
|
int SV_IsTestClient(int clientNum);
|
||||||
|
void SV_DropClient(mp::client_t* drop, const char* reason, bool tellThem);
|
||||||
|
void SV_DropAllBots();
|
||||||
|
|
||||||
|
int GetProtocolVersion();
|
||||||
|
|
||||||
|
void NetAdr_SetType(netadr_s* addr, netadrtype_t type);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_mp();
|
bool is_mp();
|
||||||
|
@ -379,6 +379,15 @@ namespace game
|
|||||||
int flags;
|
int flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CmdArgs
|
||||||
|
{
|
||||||
|
int nesting;
|
||||||
|
int localClientNum[8];
|
||||||
|
int controllerIndex[8];
|
||||||
|
int argc[8];
|
||||||
|
const char** argv[8];
|
||||||
|
};
|
||||||
|
|
||||||
struct msg_t
|
struct msg_t
|
||||||
{
|
{
|
||||||
int overflowed;
|
int overflowed;
|
||||||
@ -546,5 +555,198 @@ namespace game
|
|||||||
bool (__cdecl *domainFunc)(dvar_t*, DvarValue);
|
bool (__cdecl *domainFunc)(dvar_t*, DvarValue);
|
||||||
dvar_t* hashNext;
|
dvar_t* hashNext;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct usercmd_s
|
||||||
|
{
|
||||||
|
int serverTime;
|
||||||
|
int buttons;
|
||||||
|
int angles[3];
|
||||||
|
unsigned int weapon;
|
||||||
|
unsigned int offHand;
|
||||||
|
char forwardmove;
|
||||||
|
char rightmove;
|
||||||
|
unsigned __int16 airburstMarkDistance;
|
||||||
|
unsigned __int16 meleeChargeEnt;
|
||||||
|
unsigned char meleeChargeDist;
|
||||||
|
char selectedLoc[2];
|
||||||
|
unsigned char selectedLocAngle;
|
||||||
|
char remoteControlAngles[2];
|
||||||
|
char remoteControlMove[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(usercmd_s) == 0x2C);
|
||||||
|
|
||||||
|
struct playerState_s
|
||||||
|
{
|
||||||
|
unsigned char __pad0[0x4EC];
|
||||||
|
unsigned int perks[0x2];
|
||||||
|
unsigned int perkSlots[0x9];
|
||||||
|
unsigned char __pad1[0x2DE8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gclient_s
|
||||||
|
{
|
||||||
|
playerState_s ps;
|
||||||
|
unsigned char __pad0[0x2CC];
|
||||||
|
int flags;
|
||||||
|
unsigned char __pad1[0x3B0];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(gclient_s) == 0x3980);
|
||||||
|
|
||||||
|
struct entityState_s
|
||||||
|
{
|
||||||
|
int number;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EntHandle
|
||||||
|
{
|
||||||
|
unsigned __int16 number;
|
||||||
|
unsigned __int16 infoIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gentity_s
|
||||||
|
{
|
||||||
|
entityState_s s;
|
||||||
|
unsigned char __pad0[0x154];
|
||||||
|
gclient_s* client; // 0x158
|
||||||
|
unsigned char __pad1[0x28];
|
||||||
|
int flags;
|
||||||
|
int eventTime;
|
||||||
|
int clipmask;
|
||||||
|
int processedFrame;
|
||||||
|
EntHandle parent;
|
||||||
|
int nextthink;
|
||||||
|
int health;
|
||||||
|
int maxHealth;
|
||||||
|
int damage;
|
||||||
|
int count;
|
||||||
|
unsigned char __pad2[0xc8];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(gentity_s) == 0x274);
|
||||||
|
|
||||||
|
enum clientState_t
|
||||||
|
{
|
||||||
|
CS_FREE = 0,
|
||||||
|
CS_ZOMBIE = 1,
|
||||||
|
CS_RECONNECTING = 2,
|
||||||
|
CS_CONNECTED = 3,
|
||||||
|
CS_CLIENTLOADING = 4,
|
||||||
|
CS_ACTIVE = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum netsrc_t
|
||||||
|
{
|
||||||
|
NS_CLIENT1 = 0x0,
|
||||||
|
NS_CLIENT2 = 0x1,
|
||||||
|
NS_CLIENT3 = 0x2,
|
||||||
|
NS_CLIENT4 = 0x3,
|
||||||
|
NS_MAXCLIENTS = 0x4,
|
||||||
|
NS_SERVER = 0x4,
|
||||||
|
NS_PACKET = 0x5,
|
||||||
|
NS_INVALID_NETSRC = 0x6,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum netadrtype_t
|
||||||
|
{
|
||||||
|
NA_BOT = 0x0,
|
||||||
|
NA_BAD = 0x1,
|
||||||
|
NA_LOOPBACK = 0x2,
|
||||||
|
NA_BROADCAST = 0x3,
|
||||||
|
NA_IP = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netadr_s
|
||||||
|
{
|
||||||
|
netadrtype_t type;
|
||||||
|
unsigned char ip[4];
|
||||||
|
unsigned __int16 port;
|
||||||
|
unsigned char ipx[10];
|
||||||
|
unsigned int addrHandleIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(netadr_s) == 24);
|
||||||
|
|
||||||
|
struct netProfileInfo_t // Unused
|
||||||
|
{
|
||||||
|
unsigned char __pad0[0x5E0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct netchan_t
|
||||||
|
{
|
||||||
|
int outgoingSequence;
|
||||||
|
netsrc_t sock;
|
||||||
|
int dropped;
|
||||||
|
int incomingSequence;
|
||||||
|
netadr_s remoteAddress;
|
||||||
|
int qport;
|
||||||
|
int fragmentSequence;
|
||||||
|
int fragmentLength;
|
||||||
|
unsigned char* fragmentBuffer;
|
||||||
|
int fragmentBufferSize;
|
||||||
|
int unsentFragments;
|
||||||
|
int unsentFragmentStart;
|
||||||
|
int unsentLength;
|
||||||
|
unsigned char* unsentBuffer;
|
||||||
|
int unsentBufferSize;
|
||||||
|
netProfileInfo_t prof;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(netchan_t) == 0x630);
|
||||||
|
|
||||||
|
struct clientHeader_t
|
||||||
|
{
|
||||||
|
clientState_t state;
|
||||||
|
int sendAsActive;
|
||||||
|
int deltaMessage;
|
||||||
|
int rateDealyed;
|
||||||
|
int hasAckedBaselineData;
|
||||||
|
int hugeSnapshotSent;
|
||||||
|
netchan_t netchan;
|
||||||
|
float predictedOrigin[3];
|
||||||
|
int predictedOriginServerTime;
|
||||||
|
int migrationState;
|
||||||
|
float predictedVehicleOrigin[3];
|
||||||
|
int predictedVehicleServerTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(clientHeader_t) == 0x66C);
|
||||||
|
|
||||||
|
namespace mp
|
||||||
|
{
|
||||||
|
struct client_t
|
||||||
|
{
|
||||||
|
clientHeader_t header;
|
||||||
|
const char* dropReason; // 0x66C
|
||||||
|
char userinfo[1024]; // 0x670
|
||||||
|
unsigned char __pad0[0x209B8];
|
||||||
|
gentity_s* gentity; // 0x21428
|
||||||
|
unsigned char __pad1[0x20886];
|
||||||
|
unsigned __int16 scriptId; // 0x41CB2
|
||||||
|
int bIsTestClient; // 0x41CB4
|
||||||
|
int serverId; // 0x41CB8
|
||||||
|
unsigned char __pad2[0x369DC];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(mp::client_t) == 0x78698);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace dedi
|
||||||
|
{
|
||||||
|
struct client_t
|
||||||
|
{
|
||||||
|
clientHeader_t header;
|
||||||
|
const char* dropReason;
|
||||||
|
char userinfo[1024];
|
||||||
|
unsigned char __pad0[0x4123E];
|
||||||
|
unsigned __int16 scriptId;
|
||||||
|
int bIsTestClient; // 0x41CB0
|
||||||
|
int serverId;
|
||||||
|
unsigned char __pad1[0x369D8];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(dedi::client_t) == 0x78690);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,8 @@ int main()
|
|||||||
FARPROC entry_point;
|
FARPROC entry_point;
|
||||||
enable_dpi_awareness();
|
enable_dpi_awareness();
|
||||||
|
|
||||||
|
std::srand(uint32_t(time(nullptr)));
|
||||||
|
|
||||||
{
|
{
|
||||||
auto premature_shutdown = true;
|
auto premature_shutdown = true;
|
||||||
const auto _ = gsl::finally([&premature_shutdown]()
|
const auto _ = gsl::finally([&premature_shutdown]()
|
||||||
|
@ -31,21 +31,21 @@ void command::pre_destroy()
|
|||||||
|
|
||||||
void command::dispatcher()
|
void command::dispatcher()
|
||||||
{
|
{
|
||||||
const auto cmd_index = *game::native::cmd_args;
|
const auto cmd_index = game::native::cmd_args->nesting;
|
||||||
const auto arg_count = game::native::cmd_argc[cmd_index];
|
const auto arg_count = game::native::cmd_args->argc[cmd_index];
|
||||||
|
|
||||||
if (arg_count < 1) return;
|
if (arg_count < 1) return;
|
||||||
|
|
||||||
const auto command = utils::string::to_lower(game::native::cmd_argv[cmd_index][0]);
|
const auto command = utils::string::to_lower(game::native::cmd_args->argv[cmd_index][0]);
|
||||||
const auto handler = callbacks_.find(command);
|
const auto handler = callbacks_.find(command);
|
||||||
if (handler == callbacks_.end()) return;
|
if (handler == callbacks_.end()) return;
|
||||||
|
|
||||||
std::vector<std::string> arguments;
|
std::vector<std::string> arguments;
|
||||||
arguments.reserve(arg_count);
|
arguments.reserve(arg_count);
|
||||||
|
|
||||||
for (auto i = 0; i < game::native::cmd_argc[cmd_index]; ++i)
|
for (auto i = 0; i < game::native::cmd_args->argc[cmd_index]; ++i)
|
||||||
{
|
{
|
||||||
arguments.emplace_back(game::native::cmd_argv[cmd_index][i]);
|
arguments.emplace_back(game::native::cmd_args->argv[cmd_index][i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
handler->second(arguments);
|
handler->second(arguments);
|
||||||
|
183
src/module/test_clients.cpp
Normal file
183
src/module/test_clients.cpp
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
|
||||||
|
#include "test_clients.hpp"
|
||||||
|
#include "command.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include "utils/hook.hpp"
|
||||||
|
|
||||||
|
bool test_clients::can_add()
|
||||||
|
{
|
||||||
|
auto i = 0;
|
||||||
|
while (i < *game::native::svs_clientCount)
|
||||||
|
{
|
||||||
|
if (game::native::mp::svs_clients[i].header.state == game::native::clientState_t::CS_FREE)
|
||||||
|
{
|
||||||
|
// Free slot was found
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i < *game::native::svs_clientCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::native::gentity_s* test_clients::sv_add_test_client()
|
||||||
|
{
|
||||||
|
if (!test_clients::can_add())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(game::native::Dvar_FindVar("sv_running")->current.enabled);
|
||||||
|
assert(game::native::Dvar_FindVar("sv_maxclients")->current.integer <= *game::native::svs_clientCount);
|
||||||
|
|
||||||
|
static auto bot_port = 0;
|
||||||
|
char user_info[1024] = {0};
|
||||||
|
char xuid[32] = {0};
|
||||||
|
|
||||||
|
const std::uint64_t random_xuid = std::rand();
|
||||||
|
game::native::XUIDToString(&random_xuid, xuid);
|
||||||
|
|
||||||
|
// Most basic string the game will accept.
|
||||||
|
_snprintf_s(user_info, _TRUNCATE,
|
||||||
|
"connect bot%d \"\\invited\\0\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\bot%d\\xuid\\%s\\xnaddr\\%s\\natType\\2\\protocol\\%d\\checksum\\%u\\statver\\%d %u\\qport\\%d\"",
|
||||||
|
bot_port, bot_port, xuid, "", game::native::GetProtocolVersion(), game::native::BG_NetDataChecksum(),
|
||||||
|
game::native::LiveStorage_GetPersistentDataDefVersion(), game::native::LiveStorage_GetPersistentDataDefFormatChecksum(), bot_port);
|
||||||
|
|
||||||
|
game::native::netadr_s adr;
|
||||||
|
std::memset(&adr, 0, sizeof(game::native::netadr_s));
|
||||||
|
|
||||||
|
game::native::SV_Cmd_TokenizeString(user_info);
|
||||||
|
|
||||||
|
adr.port = static_cast<std::uint16_t>(bot_port++);
|
||||||
|
game::native::NetAdr_SetType(&adr, game::native::netadrtype_t::NA_BOT);
|
||||||
|
|
||||||
|
game::native::SV_DirectConnect(adr);
|
||||||
|
|
||||||
|
game::native::SV_Cmd_EndTokenizedString();
|
||||||
|
|
||||||
|
// Find the bot
|
||||||
|
auto idx = 1;
|
||||||
|
for (idx = 0; idx < *game::native::svs_clientCount; idx++)
|
||||||
|
{
|
||||||
|
if (game::native::mp::svs_clients[idx].header.state == game::native::clientState_t::CS_FREE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (game::native::mp::svs_clients[idx].header.netchan.remoteAddress.type == adr.type
|
||||||
|
&& game::native::mp::svs_clients[idx].header.netchan.remoteAddress.port == adr.port)
|
||||||
|
break; // Found them
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx == *game::native::svs_clientCount)
|
||||||
|
{
|
||||||
|
// Failed to add test client
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto client = &game::native::mp::svs_clients[idx];
|
||||||
|
client->bIsTestClient = 1;
|
||||||
|
|
||||||
|
game::native::SV_SendClientGameState(client);
|
||||||
|
|
||||||
|
game::native::usercmd_s cmd;
|
||||||
|
std::memset(&cmd, 0, sizeof(game::native::usercmd_s));
|
||||||
|
|
||||||
|
game::native::SV_ClientEnterWorld(client, &cmd);
|
||||||
|
|
||||||
|
assert(client->gentity != nullptr);
|
||||||
|
|
||||||
|
return client->gentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_clients::spawn()
|
||||||
|
{
|
||||||
|
auto* ent = test_clients::sv_add_test_client();
|
||||||
|
|
||||||
|
if (ent == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
game::native::Scr_AddEntityNum(ent->s.number, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_clients::scr_shutdown_system_mp_stub(unsigned char sys)
|
||||||
|
{
|
||||||
|
game::native::SV_DropAllBots();
|
||||||
|
reinterpret_cast<void (*)(unsigned char)>(0x569E30)(sys);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void test_clients::reset_reliable_mp()
|
||||||
|
{
|
||||||
|
// client_t ptr is in esi
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
cmp [esi + 0x41CB4], 1 // bIsTestClient
|
||||||
|
jnz resume
|
||||||
|
|
||||||
|
mov eax, [esi + 0x20E70] // Move reliableSequence to eax
|
||||||
|
mov [esi + 0x20E74], eax // Move eax to reliableAcknowledge
|
||||||
|
|
||||||
|
resume:
|
||||||
|
push 0x5CE740 // Sys_Milliseconds
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_clients::check_timeouts(const game::native::mp::client_t* cl)
|
||||||
|
{
|
||||||
|
return (!cl->bIsTestClient || cl->header.state == game::native::clientState_t::CS_ZOMBIE)
|
||||||
|
&& cl->header.netchan.remoteAddress.type != game::native::netadrtype_t::NA_LOOPBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void test_clients::check_timeouts_stub_mp()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
push eax
|
||||||
|
pushad
|
||||||
|
|
||||||
|
lea esi, [ebx - 0x2146C]
|
||||||
|
push esi
|
||||||
|
call test_clients::check_timeouts
|
||||||
|
add esp, 4
|
||||||
|
|
||||||
|
mov [esp + 0x20], eax
|
||||||
|
popad
|
||||||
|
pop eax
|
||||||
|
|
||||||
|
test al, al
|
||||||
|
|
||||||
|
push 0x576DD3
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_clients::post_load()
|
||||||
|
{
|
||||||
|
if (game::is_mp()) this->patch_mp();
|
||||||
|
else return; // No sp/dedi bots for now :(
|
||||||
|
|
||||||
|
command::add("spawnBot", []([[maybe_unused]] const std::vector<std::string>& params)
|
||||||
|
{
|
||||||
|
// Because I am unable to expand the scheduler at the moment
|
||||||
|
// we only get one bot at the time
|
||||||
|
test_clients::spawn();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_clients::patch_mp()
|
||||||
|
{
|
||||||
|
utils::hook::nop(0x639803, 5); // LiveSteamServer_RunFrame
|
||||||
|
utils::hook::nop(0x5CD65A, 5); // Do not crash for silly fatal error
|
||||||
|
|
||||||
|
utils::hook::set<BYTE>(0x572879, 0xEB); // Skip checks in SV_DirectConnect
|
||||||
|
utils::hook::set<BYTE>(0x5728D4, 0xEB);
|
||||||
|
utils::hook::set<BYTE>(0x57293D, 0xEB);
|
||||||
|
|
||||||
|
utils::hook(0x50C147, &test_clients::scr_shutdown_system_mp_stub, HOOK_CALL).install()->quick(); // G_ShutdownGame
|
||||||
|
utils::hook(0x57BBF9, &test_clients::reset_reliable_mp, HOOK_CALL).install()->quick(); // SV_SendMessageToClient
|
||||||
|
utils::hook(0x576DCC, &test_clients::check_timeouts_stub_mp, HOOK_JUMP).install()->quick(); // SV_CheckTimeouts
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_MODULE(test_clients);
|
25
src/module/test_clients.hpp
Normal file
25
src/module/test_clients.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "loader/module_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/structs.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
class test_clients final : public module
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_load() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void patch_mp();
|
||||||
|
|
||||||
|
static bool can_add();
|
||||||
|
static game::native::gentity_s* sv_add_test_client();
|
||||||
|
static void spawn();
|
||||||
|
|
||||||
|
static void scr_shutdown_system_mp_stub(unsigned char sys);
|
||||||
|
|
||||||
|
static void reset_reliable_mp();
|
||||||
|
|
||||||
|
static bool check_timeouts(const game::native::mp::client_t* cl);
|
||||||
|
static void check_timeouts_stub_mp();
|
||||||
|
};
|
@ -66,7 +66,9 @@
|
|||||||
#pragma comment(lib, "ws2_32.lib")
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
||||||
#pragma warning(disable: 4100)
|
#pragma warning(disable: 4100)
|
||||||
|
#pragma warning(disable: 26812)
|
||||||
|
|
||||||
#include "resource.hpp"
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ namespace utils::flags
|
|||||||
if (!parsed)
|
if (!parsed)
|
||||||
{
|
{
|
||||||
parse_flags(enabled_flags);
|
parse_flags(enabled_flags);
|
||||||
|
parsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& entry : enabled_flags)
|
for (const auto& entry : enabled_flags)
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user