2022-02-27 07:53:44 -05:00
|
|
|
#include <STDInclude.hpp>
|
2023-01-03 07:16:44 -05:00
|
|
|
#include <Utils/InfoString.hpp>
|
|
|
|
|
2022-12-26 07:07:24 -05:00
|
|
|
#include "CardTitles.hpp"
|
|
|
|
#include "ClanTags.hpp"
|
2023-01-27 18:05:26 -05:00
|
|
|
#include "Party.hpp"
|
2022-12-26 07:07:24 -05:00
|
|
|
#include "ServerCommands.hpp"
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
2017-02-17 06:26:07 -05:00
|
|
|
SteamID Dedicated::PlayerGuids[18][2];
|
2022-05-03 19:03:11 -04:00
|
|
|
|
|
|
|
Dvar::Var Dedicated::SVLanOnly;
|
2022-12-31 09:03:33 -05:00
|
|
|
Dvar::Var Dedicated::SVMOTD;
|
2022-06-04 16:16:55 -04:00
|
|
|
Dvar::Var Dedicated::COMLogFilter;
|
2017-01-29 15:49:48 -05:00
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
bool Dedicated::IsEnabled()
|
|
|
|
{
|
2017-06-13 09:35:12 -04:00
|
|
|
static std::optional<bool> flag;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-06-13 09:35:12 -04:00
|
|
|
if (!flag.has_value())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-06-13 09:35:12 -04:00
|
|
|
flag.emplace(Flags::HasFlag("dedicated"));
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2017-06-13 09:35:12 -04:00
|
|
|
return flag.value();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2022-11-15 17:18:00 -05:00
|
|
|
bool Dedicated::IsRunning()
|
|
|
|
{
|
|
|
|
assert(*Game::com_sv_running);
|
|
|
|
return *Game::com_sv_running && (*Game::com_sv_running)->current.enabled;
|
|
|
|
}
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
void Dedicated::InitDedicatedServer()
|
|
|
|
{
|
|
|
|
static const char* fastfiles[7] =
|
|
|
|
{
|
|
|
|
"code_post_gfx_mp",
|
|
|
|
"localized_code_post_gfx_mp",
|
|
|
|
"ui_mp",
|
|
|
|
"localized_ui_mp",
|
|
|
|
"common_mp",
|
|
|
|
"localized_common_mp",
|
|
|
|
"patch_mp"
|
|
|
|
};
|
|
|
|
|
|
|
|
std::memcpy(reinterpret_cast<void*>(0x66E1CB0), &fastfiles, sizeof(fastfiles));
|
|
|
|
Game::R_LoadGraphicsAssets();
|
|
|
|
|
2022-06-04 16:16:55 -04:00
|
|
|
if (COMLogFilter.get<bool>())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-06-01 16:23:26 -04:00
|
|
|
Utils::Hook::Nop(0x647466, 5); // 'dvar set' lines
|
|
|
|
Utils::Hook::Nop(0x5DF4F2, 5); // 'sending splash open' lines
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Utils::Hook::Call<void()>(0x4F84C0)();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dedicated::PostInitialization()
|
|
|
|
{
|
|
|
|
Command::Execute("exec autoexec.cfg");
|
|
|
|
Command::Execute("onlinegame 1");
|
|
|
|
Command::Execute("exec default_xboxlive.cfg");
|
|
|
|
Command::Execute("xblive_rankedmatch 1");
|
|
|
|
Command::Execute("xblive_privatematch 1");
|
|
|
|
Command::Execute("xblive_privateserver 0");
|
|
|
|
Command::Execute("xstartprivatematch");
|
|
|
|
//Command::Execute("xstartlobby");
|
|
|
|
Command::Execute("sv_network_fps 1000");
|
2017-06-29 17:03:57 -04:00
|
|
|
Command::Execute("cl_maxpackets 125");
|
|
|
|
Command::Execute("snaps 30");
|
2017-01-16 11:42:50 -05:00
|
|
|
Command::Execute("com_maxfps 125");
|
|
|
|
|
2022-09-24 13:04:37 -04:00
|
|
|
Game::Com_AddStartupCommands();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void Dedicated::PostInitializationStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
2017-02-01 07:44:25 -05:00
|
|
|
pushad
|
2022-12-31 09:03:33 -05:00
|
|
|
call PostInitialization
|
2017-02-01 07:44:25 -05:00
|
|
|
popad
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Start Com_EvenLoop
|
|
|
|
mov eax, 43D140h
|
|
|
|
jmp eax
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-30 07:43:24 -05:00
|
|
|
void Dedicated::Com_ClampMsec(const int msec)
|
|
|
|
{
|
|
|
|
if (msec > 500 && msec < 500000)
|
|
|
|
{
|
|
|
|
Game::Com_PrintWarning(Game::CON_CHANNEL_SYSTEM, "Hitch warning: %i msec frame time\n", msec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void Dedicated::Com_ClampMsec_Stub()
|
|
|
|
{
|
|
|
|
using namespace Game;
|
|
|
|
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
pushad
|
|
|
|
|
|
|
|
push ecx
|
|
|
|
call Com_ClampMsec
|
|
|
|
add esp, 0x4
|
|
|
|
|
|
|
|
popad
|
|
|
|
|
|
|
|
// Game's code
|
|
|
|
mov edx, dword ptr com_sv_running
|
|
|
|
|
|
|
|
push 0x47DDB8
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-29 15:49:48 -05:00
|
|
|
void Dedicated::TransmitGuids()
|
|
|
|
{
|
|
|
|
std::string list = Utils::String::VA("%c", 20);
|
|
|
|
|
2022-08-20 06:09:41 -04:00
|
|
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
2017-01-29 15:49:48 -05:00
|
|
|
{
|
2022-08-20 06:09:41 -04:00
|
|
|
if (Game::svs_clients[i].header.state >= 3)
|
2017-01-29 15:49:48 -05:00
|
|
|
{
|
2021-09-08 05:19:30 -04:00
|
|
|
list.append(Utils::String::VA(" %llX", Game::svs_clients[i].steamID));
|
2017-02-17 06:26:07 -05:00
|
|
|
|
2022-07-16 17:24:26 -04:00
|
|
|
Utils::InfoString info(Game::svs_clients[i].userinfo);
|
2022-12-31 09:03:33 -05:00
|
|
|
list.append(Utils::String::VA(" %llX", std::strtoull(info.get("realsteamId").data(), nullptr, 16)));
|
2017-01-29 15:49:48 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-17 06:26:07 -05:00
|
|
|
list.append(" 0 0");
|
2017-01-29 15:49:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-31 11:57:44 -04:00
|
|
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, list.data());
|
2017-01-29 15:49:48 -05:00
|
|
|
}
|
|
|
|
|
2021-11-16 11:56:13 -05:00
|
|
|
void Dedicated::TimeWrapStub(Game::errorParm_t code, const char* message)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-06-13 13:59:32 -04:00
|
|
|
Scheduler::Once([]
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-08-10 17:03:26 -04:00
|
|
|
std::string mapname = (*Game::sv_mapname)->current.string;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2023-01-27 18:05:26 -05:00
|
|
|
if (!Party::IsEnabled()) // Time wrapping should not occur in party servers, but yeah...
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
if (mapname.empty()) mapname = "mp_rust";
|
2022-12-11 12:54:24 -05:00
|
|
|
Command::Execute(std::format("map {}", mapname), true);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2022-06-13 13:59:32 -04:00
|
|
|
}, Scheduler::Pipeline::SERVER);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
Game::Com_Error(code, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dedicated::Heartbeat()
|
2022-05-03 19:26:53 -04:00
|
|
|
{
|
|
|
|
// Do not send a heartbeat if sv_lanOnly is set to true
|
2022-12-31 09:03:33 -05:00
|
|
|
if (SVLanOnly.get<bool>())
|
2022-05-03 19:26:53 -04:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-13 15:33:26 -05:00
|
|
|
const auto masterPort = (*Game::com_masterPort)->current.integer;
|
|
|
|
const auto* masterServerName = (*Game::com_masterServerName)->current.string;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort));
|
|
|
|
|
2022-06-12 17:07:53 -04:00
|
|
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Sending heartbeat to master: {}:{}\n", masterServerName, masterPort);
|
2017-01-16 11:42:50 -05:00
|
|
|
Network::SendCommand(master, "heartbeat", "IW4");
|
|
|
|
}
|
|
|
|
|
|
|
|
Dedicated::Dedicated()
|
|
|
|
{
|
2022-12-31 09:03:33 -05:00
|
|
|
COMLogFilter = Dvar::Register<bool>("com_logFilter", true,
|
2022-07-02 13:52:57 -04:00
|
|
|
Game::DVAR_LATCH, "Removes ~95% of unneeded lines from the log");
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-31 09:03:33 -05:00
|
|
|
if (IsEnabled() || ZoneBuilder::IsEnabled())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-01-22 14:12:36 -05:00
|
|
|
// Make sure all callbacks are handled
|
2022-06-16 10:15:26 -04:00
|
|
|
Scheduler::Loop(Steam::SteamAPI_RunCallbacks, Scheduler::Pipeline::SERVER);
|
2017-01-22 14:12:36 -05:00
|
|
|
|
2022-12-31 09:03:33 -05:00
|
|
|
SVLanOnly = Dvar::Register<bool>("sv_lanOnly", false, Game::DVAR_NONE, "Don't act as node");
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-31 09:03:33 -05:00
|
|
|
Utils::Hook(0x60BE98, InitDedicatedServer, HOOK_CALL).install()->quick();
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
Utils::Hook::Set<BYTE>(0x683370, 0xC3); // steam sometimes doesn't like the server
|
|
|
|
|
|
|
|
Utils::Hook::Set<BYTE>(0x5B4FF0, 0xC3); // self-registration on party
|
|
|
|
Utils::Hook::Set<BYTE>(0x426130, 0xC3); // other party stuff?
|
|
|
|
|
|
|
|
Utils::Hook::Set<BYTE>(0x4D7030, 0xC3); // upnp stuff
|
|
|
|
|
|
|
|
Utils::Hook::Set<BYTE>(0x4B0FC3, 0x04); // make CL_Frame do client packets, even for game state 9
|
|
|
|
Utils::Hook::Set<BYTE>(0x4F5090, 0xC3); // init sound system (1)
|
|
|
|
Utils::Hook::Set<BYTE>(0x507B80, 0xC3); // start render thread
|
|
|
|
Utils::Hook::Set<BYTE>(0x4F84C0, 0xC3); // R_Init caller
|
|
|
|
Utils::Hook::Set<BYTE>(0x46A630, 0xC3); // init sound system (2)
|
|
|
|
Utils::Hook::Set<BYTE>(0x41FDE0, 0xC3); // Com_Frame audio processor?
|
|
|
|
Utils::Hook::Set<BYTE>(0x41B9F0, 0xC3); // called from Com_Frame, seems to do renderer stuff
|
|
|
|
Utils::Hook::Set<BYTE>(0x41D010, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly
|
|
|
|
Utils::Hook::Set<BYTE>(0x62B6C0, 0xC3); // UI expression 'DebugPrint', mainly to prevent some console spam
|
|
|
|
|
|
|
|
Utils::Hook::Set<BYTE>(0x468960, 0xC3); // some mixer-related function called on shutdown
|
|
|
|
Utils::Hook::Set<BYTE>(0x60AD90, 0); // masterServerName flags
|
|
|
|
|
|
|
|
Utils::Hook::Nop(0x4DCEC9, 2); // some check preventing proper game functioning
|
|
|
|
Utils::Hook::Nop(0x507C79, 6); // another similar bsp check
|
2022-02-03 16:09:17 -05:00
|
|
|
Utils::Hook::Nop(0x414E4D, 6); // cl->messageAcknowledge > cl->gamestateMessageNum check in SV_ExecuteClientMessage
|
2017-01-16 11:42:50 -05:00
|
|
|
Utils::Hook::Nop(0x4DCEE9, 5); // some deinit renderer function
|
|
|
|
Utils::Hook::Nop(0x59A896, 5); // warning message on a removed subsystem
|
|
|
|
Utils::Hook::Nop(0x4B4EEF, 5); // same as above
|
|
|
|
Utils::Hook::Nop(0x64CF77, 5); // function detecting video card, causes Direct3DCreate9 to be called
|
|
|
|
Utils::Hook::Nop(0x60BC52, 0x15); // recommended settings check
|
|
|
|
|
|
|
|
Utils::Hook::Nop(0x45148B, 5); // Disable splash screen
|
|
|
|
|
2017-02-26 15:49:12 -05:00
|
|
|
// do not trigger host migration, even if the server is a 'bad host'
|
|
|
|
Utils::Hook::Set<BYTE>(0x626AA8, 0xEB);
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
// isHost script call return 0
|
|
|
|
Utils::Hook::Set<DWORD>(0x5DEC04, 0);
|
|
|
|
|
|
|
|
// r_loadForRenderer default to 0
|
|
|
|
Utils::Hook::Set<BYTE>(0x519DDF, 0);
|
|
|
|
|
|
|
|
// disable cheat protection on onlinegame
|
|
|
|
Utils::Hook::Set<BYTE>(0x404CF7, 0x80);
|
|
|
|
|
|
|
|
// some d3d9 call on error
|
|
|
|
Utils::Hook::Set<BYTE>(0x508470, 0xC3);
|
|
|
|
|
|
|
|
// stop saving a config_mp.cfg
|
|
|
|
Utils::Hook::Set<BYTE>(0x60B240, 0xC3);
|
|
|
|
|
|
|
|
// don't load the config
|
|
|
|
Utils::Hook::Set<BYTE>(0x4B4D19, 0xEB);
|
|
|
|
|
|
|
|
// Intercept time wrapping
|
2022-12-31 09:03:33 -05:00
|
|
|
Utils::Hook(0x62737D, TimeWrapStub, HOOK_CALL).install()->quick();
|
2017-01-16 11:42:50 -05:00
|
|
|
//Utils::Hook::Set<DWORD>(0x62735C, 50'000); // Time wrap after 50 seconds (for testing - i don't want to wait 3 weeks)
|
|
|
|
|
2017-06-08 09:54:00 -04:00
|
|
|
if (!ZoneBuilder::IsEnabled())
|
2017-06-07 15:08:04 -04:00
|
|
|
{
|
2023-01-27 18:05:26 -05:00
|
|
|
Events::OnDvarInit([]
|
2022-06-16 10:15:26 -04:00
|
|
|
{
|
2022-12-31 09:03:33 -05:00
|
|
|
SVMOTD = Dvar::Register<const char*>("sv_motd", "", Game::DVAR_NONE, "A custom message of the day for servers");
|
2023-01-27 18:05:26 -05:00
|
|
|
});
|
2022-06-16 10:15:26 -04:00
|
|
|
|
2017-06-08 09:54:00 -04:00
|
|
|
// Post initialization point
|
2022-12-31 09:03:33 -05:00
|
|
|
Utils::Hook(0x60BFBF, PostInitializationStub, HOOK_JUMP).install()->quick();
|
2017-05-30 18:27:08 -04:00
|
|
|
|
2023-01-30 07:43:24 -05:00
|
|
|
Utils::Hook(0x47DDB2, Com_ClampMsec_Stub, HOOK_JUMP).install()->quick(); // Com_Frame_Try_Block_Function
|
|
|
|
Utils::Hook::Nop(0x47DDB2 + 5, 1);
|
|
|
|
|
2017-06-08 09:54:00 -04:00
|
|
|
// Transmit custom data
|
2022-05-05 10:03:14 -04:00
|
|
|
Scheduler::Loop([]
|
2017-06-07 15:27:09 -04:00
|
|
|
{
|
2022-05-05 10:03:14 -04:00
|
|
|
CardTitles::SendCustomTitlesToClients();
|
2022-07-16 18:15:15 -04:00
|
|
|
ClanTags::SendClanTagsToClients();
|
2022-05-05 10:03:14 -04:00
|
|
|
}, Scheduler::Pipeline::SERVER, 10s);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-06-08 09:54:00 -04:00
|
|
|
// Heartbeats
|
2022-12-31 09:03:33 -05:00
|
|
|
Scheduler::Once(Heartbeat, Scheduler::Pipeline::SERVER);
|
|
|
|
Scheduler::Loop(Heartbeat, Scheduler::Pipeline::SERVER, 2min);
|
2017-06-08 09:54:00 -04:00
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2017-01-29 15:49:48 -05:00
|
|
|
else
|
|
|
|
{
|
2022-12-31 09:03:33 -05:00
|
|
|
for (int i = 0; i < ARRAYSIZE(PlayerGuids); ++i)
|
2017-01-29 15:49:48 -05:00
|
|
|
{
|
2022-12-31 09:03:33 -05:00
|
|
|
PlayerGuids[i][0].bits = 0;
|
|
|
|
PlayerGuids[i][1].bits = 0;
|
2017-01-29 15:49:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Intercept server commands
|
2017-05-31 04:42:43 -04:00
|
|
|
ServerCommands::OnCommand(20, [](Command::Params* params)
|
|
|
|
{
|
2017-05-30 19:51:06 -04:00
|
|
|
for (int client = 0; client < 18; client++)
|
|
|
|
{
|
2022-12-31 09:03:33 -05:00
|
|
|
PlayerGuids[client][0].bits = std::strtoull(params->get(2 * client + 1), nullptr, 16);
|
|
|
|
PlayerGuids[client][1].bits = std::strtoull(params->get(2 * client + 2), nullptr, 16);
|
2017-05-30 19:51:06 -04:00
|
|
|
|
2022-12-31 09:03:33 -05:00
|
|
|
if (Steam::Proxy::SteamFriends && PlayerGuids[client][1].bits != 0)
|
2017-05-30 19:51:06 -04:00
|
|
|
{
|
2022-12-31 09:03:33 -05:00
|
|
|
Steam::Proxy::SteamFriends->SetPlayedWith(PlayerGuids[client][1]);
|
2017-05-30 19:51:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
2017-01-29 15:49:48 -05:00
|
|
|
}
|
|
|
|
|
2022-05-05 10:03:14 -04:00
|
|
|
Scheduler::Loop([]
|
2017-01-29 15:49:48 -05:00
|
|
|
{
|
2022-12-31 09:03:33 -05:00
|
|
|
if (IsRunning())
|
2017-01-29 15:49:48 -05:00
|
|
|
{
|
2022-12-31 09:03:33 -05:00
|
|
|
TransmitGuids();
|
2017-01-29 15:49:48 -05:00
|
|
|
}
|
2022-05-05 10:03:14 -04:00
|
|
|
}, Scheduler::Pipeline::SERVER, 15s);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|