Merge remote-tracking branch 'origin/develop' into scheduler
This commit is contained in:
commit
0ebc3a8224
@ -19,7 +19,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
|
|||||||
|
|
||||||
- Knife charge is fixed for controller players (#259)
|
- Knife charge is fixed for controller players (#259)
|
||||||
- Fixed internet, local and favorites filters (#260)
|
- Fixed internet, local and favorites filters (#260)
|
||||||
- `sv_lanOnly` Dvar now prevents the server from sending heatbeats to master if set to true (#246)
|
- `sv_lanOnly` Dvar now prevents the server from sending heartbeats to master if set to true (#246)
|
||||||
|
|
||||||
### Known issue
|
### Known issue
|
||||||
|
|
||||||
|
1
deps/premake/json11.lua
vendored
1
deps/premake/json11.lua
vendored
@ -15,6 +15,7 @@ end
|
|||||||
function json11.project()
|
function json11.project()
|
||||||
project "json11"
|
project "json11"
|
||||||
language "C++"
|
language "C++"
|
||||||
|
cppdialect "C++11"
|
||||||
|
|
||||||
files
|
files
|
||||||
{
|
{
|
||||||
|
@ -109,6 +109,7 @@ namespace Components
|
|||||||
Loader::Register(new RawMouse());
|
Loader::Register(new RawMouse());
|
||||||
Loader::Register(new Bullet());
|
Loader::Register(new Bullet());
|
||||||
Loader::Register(new Ceg());
|
Loader::Register(new Ceg());
|
||||||
|
Loader::Register(new UserInfo());
|
||||||
|
|
||||||
Loader::Pregame = false;
|
Loader::Pregame = false;
|
||||||
}
|
}
|
||||||
|
@ -140,3 +140,4 @@ namespace Components
|
|||||||
#include "Modules/RawMouse.hpp"
|
#include "Modules/RawMouse.hpp"
|
||||||
#include "Modules/Bullet.hpp"
|
#include "Modules/Bullet.hpp"
|
||||||
#include "Modules/Ceg.hpp"
|
#include "Modules/Ceg.hpp"
|
||||||
|
#include "Modules/UserInfo.hpp"
|
||||||
|
@ -313,10 +313,13 @@ namespace Components
|
|||||||
* Should be called when a client drops from the server
|
* Should be called when a client drops from the server
|
||||||
* but not "between levels" (Quake-III-Arena)
|
* but not "between levels" (Quake-III-Arena)
|
||||||
*/
|
*/
|
||||||
void Bots::ClientDisconnect_Hk(int clientNum)
|
void Bots::ClientDisconnect_Hk(const int clientNum)
|
||||||
{
|
{
|
||||||
g_botai[clientNum].active = false;
|
g_botai[clientNum].active = false;
|
||||||
|
|
||||||
|
// Clear the overrides for UserInfo
|
||||||
|
UserInfo::ClearClientOverrides(clientNum);
|
||||||
|
|
||||||
// Call original function
|
// Call original function
|
||||||
Utils::Hook::Call<void(int)>(0x4AA430)(clientNum);
|
Utils::Hook::Call<void(int)>(0x4AA430)(clientNum);
|
||||||
}
|
}
|
||||||
|
@ -173,8 +173,8 @@ namespace Components
|
|||||||
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).c_str(), playerTitle));
|
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).c_str(), playerTitle));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data());
|
const auto* command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data());
|
||||||
Game::SV_GameSendServerCommand(-1, 0, command.data());
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CardTitles::ParseCustomTitles(const char* msg)
|
void CardTitles::ParseCustomTitles(const char* msg)
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
Game::dvar_t** Chat::cg_chatHeight = reinterpret_cast<Game::dvar_t**>(0x7ED398);
|
|
||||||
Dvar::Var Chat::cg_chatWidth;
|
Dvar::Var Chat::cg_chatWidth;
|
||||||
|
Dvar::Var Chat::sv_disableChat;
|
||||||
|
|
||||||
|
Game::dvar_t** Chat::cg_chatHeight = reinterpret_cast<Game::dvar_t**>(0x7ED398);
|
||||||
Game::dvar_t** Chat::cg_chatTime = reinterpret_cast<Game::dvar_t**>(0x9F5DE8);
|
Game::dvar_t** Chat::cg_chatTime = reinterpret_cast<Game::dvar_t**>(0x9F5DE8);
|
||||||
|
|
||||||
bool Chat::SendChat;
|
bool Chat::SendChat;
|
||||||
@ -27,7 +29,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
Chat::SendChat = false;
|
Chat::SendChat = false;
|
||||||
Game::SV_GameSendServerCommand(player->s.number, 0,
|
Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"You are muted\"", 0x65));
|
Utils::String::VA("%c \"You are muted\"", 0x65));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +39,13 @@ namespace Components
|
|||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Chat::sv_disableChat.get<bool>())
|
||||||
|
{
|
||||||
|
Chat::SendChat = false;
|
||||||
|
Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE,
|
||||||
|
Utils::String::VA("%c \"Chat is disabled\"", 0x65));
|
||||||
|
}
|
||||||
|
|
||||||
TextRenderer::StripMaterialTextIcons(text, text, strlen(text) + 1);
|
TextRenderer::StripMaterialTextIcons(text, text, strlen(text) + 1);
|
||||||
|
|
||||||
Game::Scr_AddEntity(player);
|
Game::Scr_AddEntity(player);
|
||||||
@ -223,14 +232,14 @@ namespace Components
|
|||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
Logger::Print("%s was muted\n", client->name);
|
Logger::Print("%s was muted\n", client->name);
|
||||||
Game::SV_GameSendServerCommand(client->gentity->s.number, 0,
|
Game::SV_GameSendServerCommand(client->gentity->s.number, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"You were muted\"", 0x65));
|
Utils::String::VA("%c \"You were muted\"", 0x65));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
Logger::Print("%s is already muted\n", client->name);
|
Logger::Print("%s is already muted\n", client->name);
|
||||||
Game::SV_GameSendServerCommand(-1, 0,
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"%s is already muted\"", 0x65, client->name));
|
Utils::String::VA("%c \"%s is already muted\"", 0x65, client->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +248,7 @@ namespace Components
|
|||||||
Chat::UnmuteInternal(client->steamID);
|
Chat::UnmuteInternal(client->steamID);
|
||||||
|
|
||||||
Logger::Print("%s was unmuted\n", client->name);
|
Logger::Print("%s was unmuted\n", client->name);
|
||||||
Game::SV_GameSendServerCommand(client->gentity->s.number, 0,
|
Game::SV_GameSendServerCommand(client->gentity->s.number, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"You were unmuted\"", 0x65));
|
Utils::String::VA("%c \"You were unmuted\"", 0x65));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,6 +325,7 @@ namespace Components
|
|||||||
Chat::Chat()
|
Chat::Chat()
|
||||||
{
|
{
|
||||||
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
|
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
|
||||||
|
Chat::sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients");
|
||||||
Scheduler::Once(Chat::AddChatCommands, Scheduler::Pipeline::MAIN);
|
Scheduler::Once(Chat::AddChatCommands, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
// Intercept chat sending
|
// Intercept chat sending
|
||||||
|
@ -9,8 +9,11 @@ namespace Components
|
|||||||
Chat();
|
Chat();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Game::dvar_t** cg_chatHeight;
|
|
||||||
static Dvar::Var cg_chatWidth;
|
static Dvar::Var cg_chatWidth;
|
||||||
|
static Dvar::Var sv_disableChat;
|
||||||
|
|
||||||
|
// Game dvars
|
||||||
|
static Game::dvar_t** cg_chatHeight;
|
||||||
static Game::dvar_t** cg_chatTime;
|
static Game::dvar_t** cg_chatTime;
|
||||||
|
|
||||||
static bool SendChat;
|
static bool SendChat;
|
||||||
|
@ -32,7 +32,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string command = Utils::String::VA("%c clantags \"%s\"", 22, list.data());
|
std::string command = Utils::String::VA("%c clantags \"%s\"", 22, list.data());
|
||||||
Game::SV_GameSendServerCommand(-1, 0, command.data());
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* ClanTags::GetUserClantag(std::uint32_t /*clientnum*/, const char* playername)
|
const char* ClanTags::GetUserClantag(std::uint32_t /*clientnum*/, const char* playername)
|
||||||
|
@ -11,14 +11,14 @@ namespace Components
|
|||||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
if (!Dvar::Var("sv_cheats").get<bool>())
|
||||||
{
|
{
|
||||||
Logger::Print("CheatsOk: cheats are disabled!\n");
|
Logger::Print("CheatsOk: cheats are disabled!\n");
|
||||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65));
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ent->health < 1)
|
if (ent->health < 1)
|
||||||
{
|
{
|
||||||
Logger::Print("CheatsOk: entity %i must be alive to use this command!\n", entNum);
|
Logger::Print("CheatsOk: entity %i must be alive to use this command!\n", entNum);
|
||||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65));
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Print("Noclip toggled for entity %i\n", entNum);
|
Logger::Print("Noclip toggled for entity %i\n", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Print("UFO toggled for entity %i\n", entNum);
|
Logger::Print("UFO toggled for entity %i\n", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Print("God toggled for entity %i\n", entNum);
|
Logger::Print("God toggled for entity %i\n", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Print("Demigod toggled for entity %i\n", entNum);
|
Logger::Print("Demigod toggled for entity %i\n", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
(ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
(ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Print("Notarget toggled for entity %i\n", entNum);
|
Logger::Print("Notarget toggled for entity %i\n", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ namespace Components
|
|||||||
|
|
||||||
if (params->size() < 4 || params->size() > 6)
|
if (params->size() < 4 || params->size() > 6)
|
||||||
{
|
{
|
||||||
Game::SV_GameSendServerCommand(ent->s.number, 0,
|
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65));
|
Utils::String::VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -208,7 +208,7 @@ namespace Components
|
|||||||
strncpy_s(ent->client->visionName[visMode],
|
strncpy_s(ent->client->visionName[visMode],
|
||||||
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(ent->s.number, 1,
|
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE,
|
||||||
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ namespace Components
|
|||||||
strncpy_s(ent->client->visionName[visMode],
|
strncpy_s(ent->client->visionName[visMode],
|
||||||
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(ent->s.number, 1,
|
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE,
|
||||||
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -262,6 +262,82 @@ namespace Components
|
|||||||
Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::hitLocation_t::HITLOC_NONE, 0);
|
Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::hitLocation_t::HITLOC_NONE, 0);
|
||||||
}, Scheduler::Pipeline::SERVER);
|
}, Scheduler::Pipeline::SERVER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ClientCommand::Add("give", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||||
|
{
|
||||||
|
if (!ClientCommand::CheatsOk(ent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (params->size() < 2)
|
||||||
|
{
|
||||||
|
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE,
|
||||||
|
Utils::String::VA("%c \"GAME_USAGE\x15: give weapon\n\"", 0x65));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::level->initializing = 1;
|
||||||
|
const auto* weaponName = params->get(1);
|
||||||
|
const auto weaponIndex = Game::G_GetWeaponIndexForName(weaponName);
|
||||||
|
|
||||||
|
if (weaponIndex == 0)
|
||||||
|
{
|
||||||
|
Game::level->initializing = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game::BG_GetWeaponDef(weaponIndex)->inventoryType == Game::weapInventoryType_t::WEAPINVENTORY_ALTMODE)
|
||||||
|
{
|
||||||
|
Game::Com_PrintError(Game::CON_CHANNEL_ERROR,
|
||||||
|
"You can't directly spawn the altfire weapon '%s'. Spawn a weapon that has this altmode instead.\n", weaponName);
|
||||||
|
Game::level->initializing = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* weapEnt = Game::G_Spawn();
|
||||||
|
Utils::VectorCopy(weapEnt->r.currentOrigin, ent->r.currentOrigin);
|
||||||
|
Game::G_GetItemClassname(static_cast<int>(weaponIndex), weapEnt);
|
||||||
|
Game::G_SpawnItem(weapEnt, static_cast<int>(weaponIndex));
|
||||||
|
|
||||||
|
weapEnt->active = 1;
|
||||||
|
const auto offHandClass = Game::BG_GetWeaponDef(weaponIndex)->offhandClass;
|
||||||
|
if (offHandClass != Game::OFFHAND_CLASS_NONE)
|
||||||
|
{
|
||||||
|
auto* client = ent->client;
|
||||||
|
if ((client->ps.weapCommon.offhandPrimary != offHandClass) && (client->ps.weapCommon.offhandSecondary != offHandClass))
|
||||||
|
{
|
||||||
|
switch (offHandClass)
|
||||||
|
{
|
||||||
|
case Game::OFFHAND_CLASS_FRAG_GRENADE:
|
||||||
|
case Game::OFFHAND_CLASS_THROWINGKNIFE:
|
||||||
|
case Game::OFFHAND_CLASS_OTHER:
|
||||||
|
client->ps.weapCommon.offhandPrimary = offHandClass;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
client->ps.weapCommon.offhandSecondary = offHandClass;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Touch_Item(weapEnt, ent, 0);
|
||||||
|
weapEnt->active = 0;
|
||||||
|
|
||||||
|
if (weapEnt->r.isInUse)
|
||||||
|
{
|
||||||
|
Game::G_FreeEntity(weapEnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::level->initializing = 0;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < std::extent_v<decltype(Game::playerState_s::weaponsEquipped)>; ++i)
|
||||||
|
{
|
||||||
|
const auto index = ent->client->ps.weaponsEquipped[i];
|
||||||
|
if (index != 0)
|
||||||
|
{
|
||||||
|
Game::Add_Ammo(ent, index, 0, 998, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCommand::AddScriptFunctions()
|
void ClientCommand::AddScriptFunctions()
|
||||||
|
@ -6,6 +6,8 @@ namespace Components
|
|||||||
|
|
||||||
Dvar::Var Dedicated::SVRandomMapRotation;
|
Dvar::Var Dedicated::SVRandomMapRotation;
|
||||||
Dvar::Var Dedicated::SVLanOnly;
|
Dvar::Var Dedicated::SVLanOnly;
|
||||||
|
Dvar::Var Dedicated::SVDontRotate;
|
||||||
|
Dvar::Var Dedicated::COMLogFilter;
|
||||||
|
|
||||||
bool Dedicated::IsEnabled()
|
bool Dedicated::IsEnabled()
|
||||||
{
|
{
|
||||||
@ -35,7 +37,7 @@ namespace Components
|
|||||||
std::memcpy(reinterpret_cast<void*>(0x66E1CB0), &fastfiles, sizeof(fastfiles));
|
std::memcpy(reinterpret_cast<void*>(0x66E1CB0), &fastfiles, sizeof(fastfiles));
|
||||||
Game::R_LoadGraphicsAssets();
|
Game::R_LoadGraphicsAssets();
|
||||||
|
|
||||||
if (Dvar::Var("com_logFilter").get<bool>())
|
if (COMLogFilter.get<bool>())
|
||||||
{
|
{
|
||||||
Utils::Hook::Nop(0x647466, 5); // 'dvar set' lines
|
Utils::Hook::Nop(0x647466, 5); // 'dvar set' lines
|
||||||
Utils::Hook::Nop(0x5DF4F2, 5); // 'sending splash open' lines
|
Utils::Hook::Nop(0x5DF4F2, 5); // 'sending splash open' lines
|
||||||
@ -96,7 +98,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(-1, 0, list.data());
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, list.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dedicated::TimeWrapStub(Game::errorParm_t code, const char* message)
|
void Dedicated::TimeWrapStub(Game::errorParm_t code, const char* message)
|
||||||
@ -129,21 +131,22 @@ namespace Components
|
|||||||
const auto tokens = Utils::String::Split(rotation, ' ');
|
const auto tokens = Utils::String::Split(rotation, ' ');
|
||||||
std::vector<std::pair<std::string, std::string>> mapRotationPair;
|
std::vector<std::pair<std::string, std::string>> mapRotationPair;
|
||||||
|
|
||||||
for (auto i = 0u; i < (tokens.size() - 1); i += 2)
|
for (std::size_t i = 0; i < (tokens.size() - 1); i += 2)
|
||||||
{
|
{
|
||||||
if (i + 1 >= tokens.size()) break;
|
if (i + 1 >= tokens.size()) break;
|
||||||
|
|
||||||
const auto& key = tokens[i];
|
const auto& key = tokens[i];
|
||||||
const auto& value = tokens[i + 1];
|
const auto& value = tokens[i + 1];
|
||||||
mapRotationPair.push_back(std::make_pair(key, value));
|
mapRotationPair.emplace_back(std::make_pair(key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto seed = Utils::Cryptography::Rand::GenerateInt();
|
const auto seed = Utils::Cryptography::Rand::GenerateInt();
|
||||||
std::shuffle(std::begin(mapRotationPair), std::end(mapRotationPair), std::default_random_engine(seed));
|
std::shuffle(mapRotationPair.begin(), mapRotationPair.end(), std::default_random_engine(seed));
|
||||||
|
|
||||||
// Rebuild map rotation using the randomized key/values
|
// Rebuild map rotation using the randomized key/values
|
||||||
rotation.clear();
|
rotation.clear();
|
||||||
for (auto j = 0u; j < mapRotationPair.size(); j++)
|
|
||||||
|
for (std::size_t j = 0; j < mapRotationPair.size(); j++)
|
||||||
{
|
{
|
||||||
const auto& pair = mapRotationPair[j];
|
const auto& pair = mapRotationPair[j];
|
||||||
rotation.append(pair.first);
|
rotation.append(pair.first);
|
||||||
@ -151,33 +154,79 @@ namespace Components
|
|||||||
rotation.append(pair.second);
|
rotation.append(pair.second);
|
||||||
|
|
||||||
if (j != mapRotationPair.size() - 1)
|
if (j != mapRotationPair.size() - 1)
|
||||||
rotation.append(" ");
|
rotation.append(" "); // No space on last element
|
||||||
}
|
}
|
||||||
|
|
||||||
Dvar::Var("sv_mapRotationCurrent").set(rotation);
|
Dvar::Var("sv_mapRotationCurrent").set(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Dedicated::ApplyMapRotation()
|
||||||
|
{
|
||||||
|
auto rotation = Dvar::Var("sv_mapRotationCurrent").get<std::string>();
|
||||||
|
const auto tokens = Utils::String::Split(rotation, ' ');
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < (tokens.size() - 1); i += 2)
|
||||||
|
{
|
||||||
|
if (i + 1 >= tokens.size())
|
||||||
|
{
|
||||||
|
Dvar::Var("sv_mapRotationCurrent").set("");
|
||||||
|
Command::Execute("map_rotate", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& key = tokens[i];
|
||||||
|
const auto& value = tokens[i + 1];
|
||||||
|
|
||||||
|
if (key == "map")
|
||||||
|
{
|
||||||
|
// Rebuild map rotation string
|
||||||
|
rotation.clear();
|
||||||
|
for (std::size_t j = (i + 2); j < tokens.size(); ++j)
|
||||||
|
{
|
||||||
|
if (j != (i + 2)) rotation += " ";
|
||||||
|
rotation += tokens[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
Dvar::Var("sv_mapRotationCurrent").set(rotation);
|
||||||
|
|
||||||
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER,"Loading new map: %s\n", value.data());
|
||||||
|
Command::Execute(Utils::String::VA("map %s", value.data()), true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == "gametype")
|
||||||
|
{
|
||||||
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Applying new gametype: %s\n", value.data());
|
||||||
|
Dvar::Var("g_gametype").set(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Unsupported maprotation key '%s'!\n", key.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Dedicated::MapRotate()
|
void Dedicated::MapRotate()
|
||||||
{
|
{
|
||||||
if (!Dedicated::IsEnabled() && Dvar::Var("sv_dontrotate").get<bool>())
|
if (!Dedicated::IsEnabled() && Dedicated::SVDontRotate.get<bool>())
|
||||||
{
|
{
|
||||||
Dvar::Var("sv_dontrotate").set(false);
|
Dedicated::SVDontRotate.set(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>())
|
if (Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>())
|
||||||
{
|
{
|
||||||
Logger::Print("Not performing map rotation as we are hosting a party!\n");
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Not performing map rotation as we are hosting a party!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Print("Rotating map...\n");
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Rotating map...\n");
|
||||||
const auto mapRotation = Dvar::Var("sv_mapRotation").get<std::string>();
|
const auto mapRotation = Dvar::Var("sv_mapRotation").get<std::string>();
|
||||||
|
|
||||||
// if nothing, just restart
|
// if nothing, just restart
|
||||||
if (mapRotation.empty())
|
if (mapRotation.empty())
|
||||||
{
|
{
|
||||||
Logger::Print("No rotation defined, restarting map.\n");
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "No rotation defined, restarting map.\n");
|
||||||
|
|
||||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
if (!Dvar::Var("sv_cheats").get<bool>())
|
||||||
{
|
{
|
||||||
@ -194,11 +243,11 @@ namespace Components
|
|||||||
// First, check if the string contains nothing
|
// First, check if the string contains nothing
|
||||||
if (Dvar::Var("sv_mapRotationCurrent").get<std::string>().empty())
|
if (Dvar::Var("sv_mapRotationCurrent").get<std::string>().empty())
|
||||||
{
|
{
|
||||||
Logger::Print("Current map rotation has finished, reloading...\n");
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Current map rotation has finished, reloading...\n");
|
||||||
|
|
||||||
if (Dedicated::SVRandomMapRotation.get<bool>())
|
if (Dedicated::SVRandomMapRotation.get<bool>())
|
||||||
{
|
{
|
||||||
Logger::Print("Randomizing map rotation\n");
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Randomizing map rotation\n");
|
||||||
Dedicated::RandomizeMapRotation();
|
Dedicated::RandomizeMapRotation();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -207,48 +256,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rotation = Dvar::Var("sv_mapRotationCurrent").get<std::string>();
|
Dedicated::ApplyMapRotation();
|
||||||
|
|
||||||
auto tokens = Utils::String::Split(rotation, ' ');
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < (tokens.size() - 1); i += 2)
|
|
||||||
{
|
|
||||||
if (i + 1 >= tokens.size())
|
|
||||||
{
|
|
||||||
Dvar::Var("sv_mapRotationCurrent").set("");
|
|
||||||
Command::Execute("map_rotate", true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string key = tokens[i];
|
|
||||||
std::string value = tokens[i + 1];
|
|
||||||
|
|
||||||
if (key == "map")
|
|
||||||
{
|
|
||||||
// Rebuild map rotation string
|
|
||||||
rotation.clear();
|
|
||||||
for (unsigned int j = (i + 2); j < tokens.size(); ++j)
|
|
||||||
{
|
|
||||||
if (j != (i + 2)) rotation += " ";
|
|
||||||
rotation += tokens[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
Dvar::Var("sv_mapRotationCurrent").set(rotation);
|
|
||||||
|
|
||||||
Logger::Print("Loading new map: %s\n", value.data());
|
|
||||||
Command::Execute(Utils::String::VA("map %s", value.data()), true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (key == "gametype")
|
|
||||||
{
|
|
||||||
Logger::Print("Applying new gametype: %s\n", value.data());
|
|
||||||
Dvar::Var("g_gametype").set(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::Print("Unsupported maprotation key '%s', motherfucker!\n", key.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dedicated::Heartbeat()
|
void Dedicated::Heartbeat()
|
||||||
@ -264,7 +272,7 @@ namespace Components
|
|||||||
|
|
||||||
Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort));
|
Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort));
|
||||||
|
|
||||||
Logger::Print("Sending heartbeat to master: %s:%u\n", masterServerName, masterPort);
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Sending heartbeat to master: %s:%u\n", masterServerName, masterPort);
|
||||||
Network::SendCommand(master, "heartbeat", "IW4");
|
Network::SendCommand(master, "heartbeat", "IW4");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,8 +354,13 @@ namespace Components
|
|||||||
{
|
{
|
||||||
// Map rotation
|
// Map rotation
|
||||||
Utils::Hook::Set(0x4152E8, Dedicated::MapRotate);
|
Utils::Hook::Set(0x4152E8, Dedicated::MapRotate);
|
||||||
Dvar::Register<bool>("sv_dontrotate", false, Game::dvar_flag::DVAR_CHEAT, "");
|
Dvar::OnInit([]
|
||||||
Dvar::Register<bool>("com_logFilter", true, Game::dvar_flag::DVAR_LATCH, "Removes ~95% of unneeded lines from the log");
|
{
|
||||||
|
Dedicated::SVDontRotate = Dvar::Register<bool>("sv_dontRotate", false,
|
||||||
|
Game::dvar_flag::DVAR_NONE, "");
|
||||||
|
Dedicated::COMLogFilter = Dvar::Register<bool>("com_logFilter", true,
|
||||||
|
Game::dvar_flag::DVAR_LATCH, "Removes ~95% of unneeded lines from the log");
|
||||||
|
});
|
||||||
|
|
||||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,8 @@ namespace Components
|
|||||||
|
|
||||||
static SteamID PlayerGuids[18][2];
|
static SteamID PlayerGuids[18][2];
|
||||||
static Dvar::Var SVLanOnly;
|
static Dvar::Var SVLanOnly;
|
||||||
|
static Dvar::Var SVDontRotate;
|
||||||
|
static Dvar::Var COMLogFilter;
|
||||||
|
|
||||||
static bool IsEnabled();
|
static bool IsEnabled();
|
||||||
|
|
||||||
@ -18,6 +20,7 @@ namespace Components
|
|||||||
static Dvar::Var SVRandomMapRotation;
|
static Dvar::Var SVRandomMapRotation;
|
||||||
|
|
||||||
static void RandomizeMapRotation();
|
static void RandomizeMapRotation();
|
||||||
|
static void ApplyMapRotation();
|
||||||
static void MapRotate();
|
static void MapRotate();
|
||||||
static void InitDedicatedServer();
|
static void InitDedicatedServer();
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("discovery", [](Network::Address address, std::string data)
|
Network::OnPacket("discovery", [](Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (address.isSelf()) return;
|
if (address.isSelf()) return;
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ namespace Components
|
|||||||
Network::SendCommand(address, "discoveryResponse", data);
|
Network::SendCommand(address, "discoveryResponse", data);
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("discoveryResponse", [](Network::Address address, std::string data)
|
Network::OnPacket("discoveryResponse", [](Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (address.isSelf()) return;
|
if (address.isSelf()) return;
|
||||||
|
|
||||||
|
@ -373,7 +373,7 @@ namespace Components
|
|||||||
if (!ps->weapIndex)
|
if (!ps->weapIndex)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const auto* weaponDef = Game::BG_GetWeaponDef(ps->weapIndex);
|
const auto* weaponDef = Game::BG_GetWeaponDef(static_cast<std::uint32_t>(ps->weapIndex));
|
||||||
|
|
||||||
return weaponDef->offhandClass != Game::OFFHAND_CLASS_NONE;
|
return weaponDef->offhandClass != Game::OFFHAND_CLASS_NONE;
|
||||||
}
|
}
|
||||||
@ -448,7 +448,7 @@ namespace Components
|
|||||||
if (!AimAssist_IsLockonActive(input->localClientNum))
|
if (!AimAssist_IsLockonActive(input->localClientNum))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto* weaponDef = Game::BG_GetWeaponDef(aaGlob.ps.weapIndex);
|
const auto* weaponDef = Game::BG_GetWeaponDef(static_cast<std::uint32_t>(aaGlob.ps.weapIndex));
|
||||||
if (weaponDef->requireLockonToFire)
|
if (weaponDef->requireLockonToFire)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -532,7 +532,7 @@ namespace Components
|
|||||||
if (!ps->weapIndex)
|
if (!ps->weapIndex)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const auto* weaponDef = Game::BG_GetWeaponDef(ps->weapIndex);
|
const auto* weaponDef = Game::BG_GetWeaponDef(static_cast<std::uint32_t>(ps->weapIndex));
|
||||||
if (weaponDef->requireLockonToFire)
|
if (weaponDef->requireLockonToFire)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -565,7 +565,7 @@ namespace Components
|
|||||||
if (!AimAssist_IsSlowdownActive(&aaGlob.ps))
|
if (!AimAssist_IsSlowdownActive(&aaGlob.ps))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto* weaponDef = Game::BG_GetWeaponDef(aaGlob.ps.weapIndex);
|
const auto* weaponDef = Game::BG_GetWeaponDef(static_cast<std::uint32_t>(aaGlob.ps.weapIndex));
|
||||||
const auto aimAssistRange = AimAssist_Lerp(weaponDef->aimAssistRange, weaponDef->aimAssistRangeAds, aaGlob.adsLerp) * aim_aimAssistRangeScale.get<float>();
|
const auto aimAssistRange = AimAssist_Lerp(weaponDef->aimAssistRange, weaponDef->aimAssistRangeAds, aaGlob.adsLerp) * aim_aimAssistRangeScale.get<float>();
|
||||||
const auto screenTarget = AimAssist_GetBestTarget(&aaGlob, aimAssistRange, aaGlob.tweakables.slowdownRegionWidth, aaGlob.tweakables.slowdownRegionHeight);
|
const auto screenTarget = AimAssist_GetBestTarget(&aaGlob, aimAssistRange, aaGlob.tweakables.slowdownRegionWidth, aaGlob.tweakables.slowdownRegionHeight);
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
std::string Network::SelectedPacket;
|
std::string Network::SelectedPacket;
|
||||||
Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
|
Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
|
||||||
std::map<std::string, Utils::Slot<Network::Callback>> Network::PacketHandlers;
|
// Packet interception
|
||||||
|
std::unordered_map<std::string, Network::NetworkCallback> Network::Callbacks;
|
||||||
|
|
||||||
Network::Address::Address(const std::string& addrString)
|
Network::Address::Address(const std::string& addrString)
|
||||||
{
|
{
|
||||||
@ -143,11 +144,6 @@ namespace Components
|
|||||||
return (this->getType() != Game::netadrtype_t::NA_BAD && this->getType() >= Game::netadrtype_t::NA_BOT && this->getType() <= Game::netadrtype_t::NA_IP);
|
return (this->getType() != Game::netadrtype_t::NA_BAD && this->getType() >= Game::netadrtype_t::NA_BOT && this->getType() <= Game::netadrtype_t::NA_IP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::Handle(const std::string& packet, Utils::Slot<Network::Callback> callback)
|
|
||||||
{
|
|
||||||
Network::PacketHandlers[Utils::String::ToLower(packet)] = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Network::OnStart(Utils::Slot<Network::CallbackRaw> callback)
|
void Network::OnStart(Utils::Slot<Network::CallbackRaw> callback)
|
||||||
{
|
{
|
||||||
Network::StartupSignal.connect(callback);
|
Network::StartupSignal.connect(callback);
|
||||||
@ -227,71 +223,6 @@ namespace Components
|
|||||||
Network::BroadcastRange(100, 65536, data);
|
Network::BroadcastRange(100, 65536, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Network::PacketInterceptionHandler(const char* packet)
|
|
||||||
{
|
|
||||||
// Packet rate limit.
|
|
||||||
static uint32_t packets = 0;
|
|
||||||
static int lastClean = 0;
|
|
||||||
|
|
||||||
if ((Game::Sys_Milliseconds() - lastClean) > 1'000)
|
|
||||||
{
|
|
||||||
packets = 0;
|
|
||||||
lastClean = Game::Sys_Milliseconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((++packets) > NETWORK_MAX_PACKETS_PER_SECOND)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string packetCommand = packet;
|
|
||||||
auto pos = packetCommand.find_first_of("\\\n ");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
packetCommand = packetCommand.substr(0, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
packetCommand = Utils::String::ToLower(packetCommand);
|
|
||||||
|
|
||||||
// Check if custom handler exists
|
|
||||||
for (auto i = Network::PacketHandlers.begin(); i != Network::PacketHandlers.end(); ++i)
|
|
||||||
{
|
|
||||||
if (Utils::String::ToLower(i->first) == packetCommand)
|
|
||||||
{
|
|
||||||
Network::SelectedPacket = i->first;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No interception
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Network::DeployPacket(Game::netadr_t* from, Game::msg_t* msg)
|
|
||||||
{
|
|
||||||
if (Network::PacketHandlers.find(Network::SelectedPacket) != Network::PacketHandlers.end())
|
|
||||||
{
|
|
||||||
std::string data;
|
|
||||||
|
|
||||||
size_t offset = Network::SelectedPacket.size() + 4 + 1;
|
|
||||||
|
|
||||||
if (static_cast<size_t>(msg->cursize) > offset)
|
|
||||||
{
|
|
||||||
data.append(msg->data + offset, msg->cursize - offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove trailing 0x00 byte
|
|
||||||
// Actually, don't remove it, it might be part of the packet. Send correctly formatted packets instead!
|
|
||||||
//if (data.size() && !data[data.size() - 1]) data.pop_back();
|
|
||||||
|
|
||||||
Network::PacketHandlers[Network::SelectedPacket](from, data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::Print("Error: Network packet intercepted, but handler is missing!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Network::NetworkStart()
|
void Network::NetworkStart()
|
||||||
{
|
{
|
||||||
Network::StartupSignal();
|
Network::StartupSignal();
|
||||||
@ -312,31 +243,6 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void Network::DeployPacketStub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
lea eax, [esp + 0C54h]
|
|
||||||
|
|
||||||
pushad
|
|
||||||
|
|
||||||
push ebp // Command
|
|
||||||
push eax // Address pointer
|
|
||||||
call Network::DeployPacket
|
|
||||||
add esp, 8h
|
|
||||||
|
|
||||||
popad
|
|
||||||
|
|
||||||
mov al, 1
|
|
||||||
pop edi
|
|
||||||
pop esi
|
|
||||||
pop ebp
|
|
||||||
pop ebx
|
|
||||||
add esp, 0C40h
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void Network::PacketErrorCheck()
|
__declspec(naked) void Network::PacketErrorCheck()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
@ -370,6 +276,60 @@ namespace Components
|
|||||||
Utils::Hook::Call<void(Game::client_t*, Game::msg_t*)>(0x414D40)(client, msg);
|
Utils::Hook::Call<void(Game::client_t*, Game::msg_t*)>(0x414D40)(client, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Network::OnPacket(const std::string& command, const NetworkCallback& callback)
|
||||||
|
{
|
||||||
|
Network::Callbacks[Utils::String::ToLower(command)] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Network::HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message)
|
||||||
|
{
|
||||||
|
const auto cmd_string = Utils::String::ToLower(command);
|
||||||
|
const auto handler = Network::Callbacks.find(cmd_string);
|
||||||
|
|
||||||
|
const auto offset = cmd_string.size() + 5;
|
||||||
|
if (static_cast<std::size_t>(message->cursize) < offset || handler == Network::Callbacks.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data(message->data + offset, message->cursize - offset);
|
||||||
|
|
||||||
|
Address address_ = address;
|
||||||
|
handler->second(address_, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void Network::CL_HandleCommandStub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
lea eax, [esp + 0xC54] // address
|
||||||
|
|
||||||
|
pushad
|
||||||
|
|
||||||
|
push ebp // msg_t
|
||||||
|
push edi // Command name
|
||||||
|
push eax // netadr_t pointer
|
||||||
|
call Network::HandleCommand
|
||||||
|
add esp, 0xC
|
||||||
|
|
||||||
|
test al, al
|
||||||
|
|
||||||
|
popad
|
||||||
|
|
||||||
|
jz unhandled
|
||||||
|
|
||||||
|
// Exit CL_DispatchConnectionlessPacket
|
||||||
|
push 0x5A9E0E
|
||||||
|
retn
|
||||||
|
|
||||||
|
unhandled:
|
||||||
|
// Proceed
|
||||||
|
push 0x5AA719
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Network::Network()
|
Network::Network()
|
||||||
{
|
{
|
||||||
AssertSize(Game::netadr_t, 20);
|
AssertSize(Game::netadr_t, 20);
|
||||||
@ -397,19 +357,16 @@ namespace Components
|
|||||||
// Install startup handler
|
// Install startup handler
|
||||||
Utils::Hook(0x4FD4D4, Network::NetworkStartStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x4FD4D4, Network::NetworkStartStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Install interception handler
|
|
||||||
Utils::Hook(0x5AA709, Network::PacketInterceptionHandler, HOOK_CALL).install()->quick();
|
|
||||||
|
|
||||||
// Prevent recvfrom error spam
|
// Prevent recvfrom error spam
|
||||||
Utils::Hook(0x46531A, Network::PacketErrorCheck, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x46531A, Network::PacketErrorCheck, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Install packet deploy hook
|
|
||||||
Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub);
|
|
||||||
|
|
||||||
// Fix server freezer exploit
|
// Fix server freezer exploit
|
||||||
Utils::Hook(0x626996, Network::SV_ExecuteClientMessageStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x626996, Network::SV_ExecuteClientMessageStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
Network::Handle("resolveAddress", [](Address address, const std::string& /*data*/)
|
// Handle client packets
|
||||||
|
Utils::Hook(0x5AA703, Network::CL_HandleCommandStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
Network::OnPacket("resolveAddress", [](const Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
Network::SendRaw(address, address.getString());
|
Network::SendRaw(address, address.getString());
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define NETWORK_MAX_PACKETS_PER_SECOND 100'000
|
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
class Network : public Component
|
class Network : public Component
|
||||||
@ -10,7 +8,7 @@ namespace Components
|
|||||||
class Address
|
class Address
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Address() { setType(Game::netadrtype_t::NA_BAD); };
|
Address() { setType(Game::netadrtype_t::NA_BAD); }
|
||||||
Address(const std::string& addrString);
|
Address(const std::string& addrString);
|
||||||
Address(sockaddr* addr);
|
Address(sockaddr* addr);
|
||||||
Address(sockaddr addr) : Address(&addr) {}
|
Address(sockaddr addr) : Address(&addr) {}
|
||||||
@ -18,8 +16,8 @@ namespace Components
|
|||||||
Address(sockaddr_in* addr) : Address(reinterpret_cast<sockaddr*>(addr)) {}
|
Address(sockaddr_in* addr) : Address(reinterpret_cast<sockaddr*>(addr)) {}
|
||||||
Address(Game::netadr_t addr) : address(addr) {}
|
Address(Game::netadr_t addr) : address(addr) {}
|
||||||
Address(Game::netadr_t* addr) : Address(*addr) {}
|
Address(Game::netadr_t* addr) : Address(*addr) {}
|
||||||
Address(const Address& obj) : address(obj.address) {};
|
Address(const Address& obj) = default;
|
||||||
bool operator!=(const Address &obj) const { return !(*this == obj); };
|
bool operator!=(const Address &obj) const { return !(*this == obj); }
|
||||||
bool operator==(const Address &obj) const;
|
bool operator==(const Address &obj) const;
|
||||||
|
|
||||||
void setPort(unsigned short port);
|
void setPort(unsigned short port);
|
||||||
@ -48,14 +46,14 @@ namespace Components
|
|||||||
Game::netadr_t address;
|
Game::netadr_t address;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void(Callback)(Address address, const std::string& data);
|
|
||||||
typedef void(CallbackRaw)();
|
typedef void(CallbackRaw)();
|
||||||
|
|
||||||
|
using NetworkCallback = std::function<void(Address&, const std::string&)>;
|
||||||
|
|
||||||
Network();
|
Network();
|
||||||
|
|
||||||
static unsigned short GetPort();
|
static unsigned short GetPort();
|
||||||
|
|
||||||
static void Handle(const std::string& packet, Utils::Slot<Callback> callback);
|
|
||||||
static void OnStart(Utils::Slot<CallbackRaw> callback);
|
static void OnStart(Utils::Slot<CallbackRaw> callback);
|
||||||
|
|
||||||
// Send quake-styled binary data
|
// Send quake-styled binary data
|
||||||
@ -74,14 +72,12 @@ namespace Components
|
|||||||
static void BroadcastRange(unsigned int min, unsigned int max, const std::string& data);
|
static void BroadcastRange(unsigned int min, unsigned int max, const std::string& data);
|
||||||
static void BroadcastAll(const std::string& data);
|
static void BroadcastAll(const std::string& data);
|
||||||
|
|
||||||
|
static void OnPacket(const std::string& command, const NetworkCallback& callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::string SelectedPacket;
|
static std::string SelectedPacket;
|
||||||
static Utils::Signal<CallbackRaw> StartupSignal;
|
static Utils::Signal<CallbackRaw> StartupSignal;
|
||||||
static std::map<std::string, Utils::Slot<Callback>> PacketHandlers;
|
static std::unordered_map<std::string, NetworkCallback> Callbacks;
|
||||||
|
|
||||||
static int PacketInterceptionHandler(const char* packet);
|
|
||||||
static void DeployPacket(Game::netadr_t* from, Game::msg_t* msg);
|
|
||||||
static void DeployPacketStub();
|
|
||||||
|
|
||||||
static void NetworkStart();
|
static void NetworkStart();
|
||||||
static void NetworkStartStub();
|
static void NetworkStartStub();
|
||||||
@ -89,6 +85,10 @@ namespace Components
|
|||||||
static void PacketErrorCheck();
|
static void PacketErrorCheck();
|
||||||
|
|
||||||
static void SV_ExecuteClientMessageStub(Game::client_t* client, Game::msg_t* msg);
|
static void SV_ExecuteClientMessageStub(Game::client_t* client, Game::msg_t* msg);
|
||||||
|
|
||||||
|
static bool HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message);
|
||||||
|
|
||||||
|
static void CL_HandleCommandStub();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ namespace Components
|
|||||||
Party::JoinContainer Party::Container;
|
Party::JoinContainer Party::Container;
|
||||||
std::map<uint64_t, Network::Address> Party::LobbyMap;
|
std::map<uint64_t, Network::Address> Party::LobbyMap;
|
||||||
|
|
||||||
|
Dvar::Var Party::PartyEnable;
|
||||||
|
|
||||||
SteamID Party::GenerateLobbyId()
|
SteamID Party::GenerateLobbyId()
|
||||||
{
|
{
|
||||||
SteamID id;
|
SteamID id;
|
||||||
@ -137,7 +139,7 @@ namespace Components
|
|||||||
|
|
||||||
bool Party::IsInLobby()
|
bool Party::IsInLobby()
|
||||||
{
|
{
|
||||||
return (!Dvar::Var("sv_running").get<bool>() && Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>());
|
return (!Dvar::Var("sv_running").get<bool>() && PartyEnable.get<bool>() && Dvar::Var("party_host").get<bool>());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Party::IsInUserMapLobby()
|
bool Party::IsInUserMapLobby()
|
||||||
@ -145,9 +147,14 @@ namespace Components
|
|||||||
return (Party::IsInLobby() && Maps::IsUserMap(Dvar::Var("ui_mapname").get<const char*>()));
|
return (Party::IsInLobby() && Maps::IsUserMap(Dvar::Var("ui_mapname").get<const char*>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Party::IsEnabled()
|
||||||
|
{
|
||||||
|
return PartyEnable.get<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
Party::Party()
|
Party::Party()
|
||||||
{
|
{
|
||||||
static Game::dvar_t* partyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsEnabled(), Game::dvar_flag::DVAR_NONE, "Enable party system").get<Game::dvar_t*>();
|
Party::PartyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsEnabled(), Game::dvar_flag::DVAR_NONE, "Enable party system");
|
||||||
Dvar::Register<bool>("xblive_privatematch", true, Game::dvar_flag::DVAR_WRITEPROTECTED, "");
|
Dvar::Register<bool>("xblive_privatematch", true, Game::dvar_flag::DVAR_WRITEPROTECTED, "");
|
||||||
|
|
||||||
// various changes to SV_DirectConnect-y stuff to allow non-party joinees
|
// various changes to SV_DirectConnect-y stuff to allow non-party joinees
|
||||||
@ -225,6 +232,7 @@ namespace Components
|
|||||||
Utils::Hook::Nop(0x4077A1, 5); // PartyMigrate_Frame
|
Utils::Hook::Nop(0x4077A1, 5); // PartyMigrate_Frame
|
||||||
|
|
||||||
// Patch playlist stuff for non-party behavior
|
// Patch playlist stuff for non-party behavior
|
||||||
|
static Game::dvar_t* partyEnable = Party::PartyEnable.get<Game::dvar_t*>();
|
||||||
Utils::Hook::Set<Game::dvar_t**>(0x4A4093, &partyEnable);
|
Utils::Hook::Set<Game::dvar_t**>(0x4A4093, &partyEnable);
|
||||||
Utils::Hook::Set<Game::dvar_t**>(0x4573F1, &partyEnable);
|
Utils::Hook::Set<Game::dvar_t**>(0x4573F1, &partyEnable);
|
||||||
Utils::Hook::Set<Game::dvar_t**>(0x5B1A0C, &partyEnable);
|
Utils::Hook::Set<Game::dvar_t**>(0x5B1A0C, &partyEnable);
|
||||||
@ -301,7 +309,7 @@ namespace Components
|
|||||||
}, Scheduler::Pipeline::CLIENT);
|
}, Scheduler::Pipeline::CLIENT);
|
||||||
|
|
||||||
// Basic info handler
|
// Basic info handler
|
||||||
Network::Handle("getInfo", [](Network::Address address, const std::string& data)
|
Network::OnPacket("getInfo", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
int botCount = 0;
|
int botCount = 0;
|
||||||
int clientCount = 0;
|
int clientCount = 0;
|
||||||
@ -321,7 +329,6 @@ namespace Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
|
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
|
||||||
//maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
|
|
||||||
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData_s*>(0x1081C00));
|
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData_s*>(0x1081C00));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +376,7 @@ namespace Components
|
|||||||
// 1 - Party, use Steam_JoinLobby to connect
|
// 1 - Party, use Steam_JoinLobby to connect
|
||||||
// 2 - Match, use CL_ConnectFromParty to connect
|
// 2 - Match, use CL_ConnectFromParty to connect
|
||||||
|
|
||||||
if (Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>()) // Party hosting
|
if (PartyEnable.get<bool>() && Dvar::Var("party_host").get<bool>()) // Party hosting
|
||||||
{
|
{
|
||||||
info.set("matchtype", "1");
|
info.set("matchtype", "1");
|
||||||
}
|
}
|
||||||
@ -388,7 +395,7 @@ namespace Components
|
|||||||
Network::SendCommand(address, "infoResponse", "\\" + info.build());
|
Network::SendCommand(address, "infoResponse", "\\" + info.build());
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("infoResponse", [](Network::Address address, const std::string& data)
|
Network::OnPacket("infoResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
Utils::InfoString info(data);
|
Utils::InfoString info(data);
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ namespace Components
|
|||||||
static bool IsInUserMapLobby();
|
static bool IsInUserMapLobby();
|
||||||
static bool IsInLobby();
|
static bool IsInLobby();
|
||||||
|
|
||||||
|
static bool IsEnabled();
|
||||||
|
|
||||||
static std::string GetMotd();
|
static std::string GetMotd();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -44,6 +46,8 @@ namespace Components
|
|||||||
static JoinContainer Container;
|
static JoinContainer Container;
|
||||||
static std::map<uint64_t, Network::Address> LobbyMap;
|
static std::map<uint64_t, Network::Address> LobbyMap;
|
||||||
|
|
||||||
|
static Dvar::Var PartyEnable;
|
||||||
|
|
||||||
static SteamID GenerateLobbyId();
|
static SteamID GenerateLobbyId();
|
||||||
|
|
||||||
static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::dvar_flag flag, const char* description);
|
static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::dvar_flag flag, const char* description);
|
||||||
|
@ -43,9 +43,10 @@ namespace Components
|
|||||||
return Utils::Hook::Call<DWORD(const char**)>(0x4C0350)(buffer);
|
return Utils::Hook::Call<DWORD(const char**)>(0x4C0350)(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::PlaylistRequest(Network::Address address, const std::string& data)
|
void Playlist::PlaylistRequest(const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
std::string password = Dvar::Var("g_password").get<std::string>();
|
const auto password = Dvar::Var("g_password").get<std::string>();
|
||||||
|
|
||||||
if (password.length())
|
if (password.length())
|
||||||
{
|
{
|
||||||
if (password != data)
|
if (password != data)
|
||||||
@ -66,7 +67,7 @@ namespace Components
|
|||||||
Network::SendCommand(address, "playlistResponse", list.SerializeAsString());
|
Network::SendCommand(address, "playlistResponse", list.SerializeAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::PlaylistReponse(Network::Address address, const std::string& data)
|
void Playlist::PlaylistReponse(const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (Party::PlaylistAwaiting())
|
if (Party::PlaylistAwaiting())
|
||||||
{
|
{
|
||||||
@ -83,8 +84,8 @@ namespace Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Generate buffer and hash
|
// Generate buffer and hash
|
||||||
std::string compressedData(list.buffer());
|
const auto& compressedData = list.buffer();
|
||||||
unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(compressedData);
|
const auto hash = Utils::Cryptography::JenkinsOneAtATime::Compute(compressedData);
|
||||||
|
|
||||||
//Validate hashes
|
//Validate hashes
|
||||||
if (hash != list.hash())
|
if (hash != list.hash())
|
||||||
@ -114,7 +115,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::PlaylistInvalidPassword(Network::Address /*address*/, const std::string& /*data*/)
|
void Playlist::PlaylistInvalidPassword([[maybe_unused]] const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
Party::PlaylistError("Error: Invalid Password for Party.");
|
Party::PlaylistError("Error: Invalid Password for Party.");
|
||||||
}
|
}
|
||||||
@ -186,8 +187,8 @@ namespace Components
|
|||||||
Utils::Hook::Set<BYTE>(0x4D6E60, 0xC3);
|
Utils::Hook::Set<BYTE>(0x4D6E60, 0xC3);
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Handle("getPlaylist", PlaylistRequest);
|
Network::OnPacket("getPlaylist", PlaylistRequest);
|
||||||
Network::Handle("playlistResponse", PlaylistReponse);
|
Network::OnPacket("playlistResponse", PlaylistReponse);
|
||||||
Network::Handle("playlistInvalidPassword", PlaylistInvalidPassword);
|
Network::OnPacket("playlistInvalidPassword", PlaylistInvalidPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@ namespace Components
|
|||||||
|
|
||||||
static DWORD StorePlaylistStub(const char** buffer);
|
static DWORD StorePlaylistStub(const char** buffer);
|
||||||
|
|
||||||
static void PlaylistRequest(Network::Address address, const std::string& data);
|
static void PlaylistRequest(const Network::Address& address, const std::string& data);
|
||||||
static void PlaylistReponse(Network::Address address, const std::string& data);
|
static void PlaylistReponse(const Network::Address& address, const std::string& data);
|
||||||
static void PlaylistInvalidPassword(Network::Address address, const std::string& data);
|
static void PlaylistInvalidPassword(const Network::Address& address, const std::string& data);
|
||||||
|
|
||||||
static void MapNameCopy(char *dest, const char *src, int destsize);
|
static void MapNameCopy(char *dest, const char *src, int destsize);
|
||||||
static void SetMapName(const char* cvar, const char* value);
|
static void SetMapName(const char* cvar, const char* value);
|
||||||
|
@ -229,6 +229,16 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Game::dvar_t* QuickPatch::Dvar_RegisterConMinicon(const char* dvarName, [[maybe_unused]] bool value, unsigned __int16 flags, const char* description)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
constexpr auto value_ = true;
|
||||||
|
#else
|
||||||
|
constexpr auto value_ = false;
|
||||||
|
#endif
|
||||||
|
return Game::Dvar_RegisterBool(dvarName, value_, flags, description);
|
||||||
|
}
|
||||||
|
|
||||||
QuickPatch::QuickPatch()
|
QuickPatch::QuickPatch()
|
||||||
{
|
{
|
||||||
// Filtering any mapents that is intended for Spec:Ops gamemode (CODO) and prevent them from spawning
|
// Filtering any mapents that is intended for Spec:Ops gamemode (CODO) and prevent them from spawning
|
||||||
@ -251,6 +261,8 @@ namespace Components
|
|||||||
Utils::Hook(0x51B13B, QuickPatch::Dvar_RegisterAspectRatioDvar, HOOK_CALL).install()->quick();
|
Utils::Hook(0x51B13B, QuickPatch::Dvar_RegisterAspectRatioDvar, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x5063F3, QuickPatch::SetAspectRatioStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x5063F3, QuickPatch::SetAspectRatioStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x4FA448, QuickPatch::Dvar_RegisterConMinicon, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// Make sure preDestroy is called when the game shuts down
|
// Make sure preDestroy is called when the game shuts down
|
||||||
Scheduler::OnGameShutdown(Loader::PreDestroy);
|
Scheduler::OnGameShutdown(Loader::PreDestroy);
|
||||||
|
|
||||||
@ -705,17 +717,6 @@ namespace Components
|
|||||||
|
|
||||||
// Disable cheat protection for dvars
|
// Disable cheat protection for dvars
|
||||||
Utils::Hook::Set<BYTE>(0x647682, 0xEB);
|
Utils::Hook::Set<BYTE>(0x647682, 0xEB);
|
||||||
|
|
||||||
// Constantly draw the mini console
|
|
||||||
Utils::Hook::Set<BYTE>(0x412A45, 0xEB);
|
|
||||||
|
|
||||||
Scheduler::Loop([]
|
|
||||||
{
|
|
||||||
if (*reinterpret_cast<Game::Font_s**>(0x62E4BAC))
|
|
||||||
{
|
|
||||||
Game::Con_DrawMiniConsole(0, 2, 4, (Game::CL_IsCgameInitialized() ? 1.0f : 0.4f));
|
|
||||||
}
|
|
||||||
}, Scheduler::Pipeline::RENDERER);
|
|
||||||
#else
|
#else
|
||||||
// Remove missing tag message
|
// Remove missing tag message
|
||||||
Utils::Hook::Nop(0x4EBF1A, 5);
|
Utils::Hook::Nop(0x4EBF1A, 5);
|
||||||
|
@ -27,5 +27,7 @@ namespace Components
|
|||||||
|
|
||||||
static void CL_KeyEvent_OnEscape();
|
static void CL_KeyEvent_OnEscape();
|
||||||
static void CL_KeyEvent_ConsoleEscape_Stub();
|
static void CL_KeyEvent_ConsoleEscape_Stub();
|
||||||
|
|
||||||
|
static Game::dvar_t* Dvar_RegisterConMinicon(const char* dvarName, bool value, unsigned __int16 flags, const char* description);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -82,19 +82,20 @@ namespace Components
|
|||||||
RCon::RconLogRequests = Dvar::Register<bool>("rcon_log_requests", false, Game::dvar_flag::DVAR_NONE, "Print remote commands in the output log");
|
RCon::RconLogRequests = Dvar::Register<bool>("rcon_log_requests", false, Game::dvar_flag::DVAR_NONE, "Print remote commands in the output log");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Network::Handle("rcon", [](Network::Address address, const std::string& _data)
|
Network::OnPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
std::string data = _data;
|
std::string data_ = data;
|
||||||
Utils::String::Trim(data);
|
|
||||||
auto pos = data.find_first_of(" ");
|
Utils::String::Trim(data_);
|
||||||
|
const auto pos = data.find_first_of(' ');
|
||||||
if (pos == std::string::npos)
|
if (pos == std::string::npos)
|
||||||
{
|
{
|
||||||
Logger::Print("Invalid RCon request from %s\n", address.getCString());
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon request from %s\n", address.getCString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string password = data.substr(0, pos);
|
auto password = data.substr(0, pos);
|
||||||
std::string command = data.substr(pos + 1);
|
auto command = data.substr(pos + 1);
|
||||||
|
|
||||||
// B3 sends the password inside quotes :S
|
// B3 sends the password inside quotes :S
|
||||||
if (!password.empty() && password[0] == '"' && password.back() == '"')
|
if (!password.empty() && password[0] == '"' && password.back() == '"')
|
||||||
@ -103,11 +104,11 @@ namespace Components
|
|||||||
password.erase(password.begin());
|
password.erase(password.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string svPassword = RCon::RconPassword.get<std::string>();
|
const auto svPassword = RCon::RconPassword.get<std::string>();
|
||||||
|
|
||||||
if (svPassword.empty())
|
if (svPassword.empty())
|
||||||
{
|
{
|
||||||
Logger::Print("RCon request from %s dropped. No password set!\n", address.getCString());
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "RCon request from %s dropped. No password set!\n", address.getCString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +121,7 @@ namespace Components
|
|||||||
if (RCon::RconLogRequests.get<bool>())
|
if (RCon::RconLogRequests.get<bool>())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
Logger::Print("Executing RCon request from %s: %s\n", address.getCString(), command.data());
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Executing RCon request from %s: %s\n", address.getCString(), command.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::PipeOutput([](const std::string& output)
|
Logger::PipeOutput([](const std::string& output)
|
||||||
@ -137,11 +138,11 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::Print("Invalid RCon password sent from %s\n", address.getCString());
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon password sent from %s\n", address.getCString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("rconRequest", [](Network::Address address, const std::string& /*data*/)
|
Network::OnPacket("rconRequest", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
RCon::BackdoorContainer.address = address;
|
RCon::BackdoorContainer.address = address;
|
||||||
RCon::BackdoorContainer.challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
RCon::BackdoorContainer.challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
||||||
@ -150,7 +151,7 @@ namespace Components
|
|||||||
Network::SendCommand(address, "rconAuthorization", RCon::BackdoorContainer.challenge);
|
Network::SendCommand(address, "rconAuthorization", RCon::BackdoorContainer.challenge);
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("rconExecute", [](Network::Address address, const std::string& data)
|
Network::OnPacket("rconExecute", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (address != RCon::BackdoorContainer.address) return; // Invalid IP
|
if (address != RCon::BackdoorContainer.address) return; // Invalid IP
|
||||||
if (!RCon::BackdoorContainer.timestamp || (Game::Sys_Milliseconds() - RCon::BackdoorContainer.timestamp) > (1000 * 10)) return; // Timeout
|
if (!RCon::BackdoorContainer.timestamp || (Game::Sys_Milliseconds() - RCon::BackdoorContainer.timestamp) > (1000 * 10)) return; // Timeout
|
||||||
|
@ -624,11 +624,11 @@ namespace Components
|
|||||||
|
|
||||||
if (key == nullptr)
|
if (key == nullptr)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1StorageRemove: Illegal parameter!\n");
|
Game::Scr_ParamError(0, "^1StorageRemove: Illegal parameter!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Script::ScriptStorage.count(key))
|
if (!Script::ScriptStorage.contains(key))
|
||||||
{
|
{
|
||||||
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key));
|
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key));
|
||||||
return;
|
return;
|
||||||
@ -643,11 +643,11 @@ namespace Components
|
|||||||
|
|
||||||
if (key == nullptr)
|
if (key == nullptr)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1StorageGet: Illegal parameter!\n");
|
Game::Scr_ParamError(0, "^1StorageGet: Illegal parameter!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Script::ScriptStorage.count(key))
|
if (!Script::ScriptStorage.contains(key))
|
||||||
{
|
{
|
||||||
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key));
|
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key));
|
||||||
return;
|
return;
|
||||||
@ -663,11 +663,11 @@ namespace Components
|
|||||||
|
|
||||||
if (key == nullptr)
|
if (key == nullptr)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1StorageHas: Illegal parameter!\n");
|
Game::Scr_ParamError(0, "^1StorageHas: Illegal parameter!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::Scr_AddInt(static_cast<int>(Script::ScriptStorage.count(key)));
|
Game::Scr_AddBool(Script::ScriptStorage.contains(key));
|
||||||
});
|
});
|
||||||
|
|
||||||
Script::AddFunction("StorageClear", [] // gsc: StorageClear();
|
Script::AddFunction("StorageClear", [] // gsc: StorageClear();
|
||||||
|
@ -45,13 +45,29 @@ namespace Components
|
|||||||
|
|
||||||
if (params.size() >= 4)
|
if (params.size() >= 4)
|
||||||
{
|
{
|
||||||
const auto* dvarName = params[3];
|
const auto* name = params.get(3);
|
||||||
const auto* dvar = Game::Dvar_FindVar(dvarName);
|
// If it's a command don't execute it
|
||||||
|
if (Command::Find(name) != nullptr)
|
||||||
if (Command::Find(dvarName) ||
|
|
||||||
(dvar != nullptr && dvar->flags & (Game::DVAR_WRITEPROTECTED | Game::DVAR_CHEAT | Game::DVAR_READONLY)))
|
|
||||||
{
|
{
|
||||||
Logger::Print(0, "CL_SelectStringTableEntryInDvar_f: illegal parameter\n");
|
Logger::Print(0, "CL_SelectStringTableEntryInDvar_f: parameter is a command\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* dvar = Game::Dvar_FindVar(name);
|
||||||
|
if (dvar == nullptr)
|
||||||
|
{
|
||||||
|
// If it's not a dvar let it continue
|
||||||
|
Game::CL_SelectStringTableEntryInDvar_f();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto disallowedFlags = (Game::DVAR_CHEAT | Game::DVAR_WRITEPROTECTED
|
||||||
|
| Game::DVAR_READONLY | Game::DVAR_EXTERNAL | Game::DVAR_LATCH);
|
||||||
|
|
||||||
|
// If it's a dvar check that it does not have disallowed flags
|
||||||
|
if ((dvar->flags & disallowedFlags) != 0)
|
||||||
|
{
|
||||||
|
Logger::Print(0, "CL_SelectStringTableEntryInDvar_f: parameter is a protected dvar\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,17 +193,17 @@ namespace Components
|
|||||||
// Add uifeeder
|
// Add uifeeder
|
||||||
UIFeeder::Add(13.0f, ServerInfo::GetPlayerCount, ServerInfo::GetPlayerText, ServerInfo::SelectPlayer);
|
UIFeeder::Add(13.0f, ServerInfo::GetPlayerCount, ServerInfo::GetPlayerText, ServerInfo::SelectPlayer);
|
||||||
|
|
||||||
Network::Handle("getStatus", [](Network::Address address, const std::string& data)
|
Network::OnPacket("getStatus", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
std::string playerList;
|
std::string playerList;
|
||||||
|
|
||||||
Utils::InfoString info = ServerInfo::GetInfo();
|
Utils::InfoString info = ServerInfo::GetInfo();
|
||||||
info.set("challenge", Utils::ParseChallenge(data));
|
info.set("challenge", Utils::ParseChallenge(data));
|
||||||
|
|
||||||
for (int i = 0; i < atoi(info.get("sv_maxclients").data()); ++i) // Maybe choose 18 here?
|
for (auto i = 0; i < atoi(info.get("sv_maxclients").data()); ++i) // Maybe choose 18 here?
|
||||||
{
|
{
|
||||||
int score = 0;
|
auto score = 0;
|
||||||
int ping = 0;
|
auto ping = 0;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
if (Dvar::Var("sv_running").get<bool>())
|
if (Dvar::Var("sv_running").get<bool>())
|
||||||
@ -217,7 +217,7 @@ namespace Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Score and ping are irrelevant
|
// Score and ping are irrelevant
|
||||||
const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData_t*>(0x1081C00), i);
|
const auto* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData_t*>(0x1081C00), i);
|
||||||
if (!namePtr || !namePtr[0]) continue;
|
if (!namePtr || !namePtr[0]) continue;
|
||||||
|
|
||||||
name = namePtr;
|
name = namePtr;
|
||||||
@ -229,7 +229,7 @@ namespace Components
|
|||||||
Network::SendCommand(address, "statusResponse", "\\" + info.build() + "\n" + playerList + "\n");
|
Network::SendCommand(address, "statusResponse", "\\" + info.build() + "\n" + playerList + "\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("statusResponse", [](Network::Address address, const std::string& data)
|
Network::OnPacket("statusResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (ServerInfo::PlayerContainer.target == address)
|
if (ServerInfo::PlayerContainer.target == address)
|
||||||
{
|
{
|
||||||
|
@ -451,7 +451,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerList::Insert(Network::Address address, Utils::InfoString info)
|
void ServerList::Insert(const Network::Address& address, const Utils::InfoString& info)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
|
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
|
||||||
|
|
||||||
@ -795,7 +795,7 @@ namespace Components
|
|||||||
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
|
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
|
||||||
Localization::Set("MPUI_SERVERQUERIED", "Servers: 0\nPlayers: 0 (0)");
|
Localization::Set("MPUI_SERVERQUERIED", "Servers: 0\nPlayers: 0 (0)");
|
||||||
|
|
||||||
Network::Handle("getServersResponse", [](Network::Address address, const std::string& data)
|
Network::OnPacket("getServersResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (ServerList::RefreshContainer.host != address) return; // Only parse from host we sent to
|
if (ServerList::RefreshContainer.host != address) return; // Only parse from host we sent to
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ namespace Components
|
|||||||
static void RefreshVisibleListInternal(UIScript::Token, bool refresh = false);
|
static void RefreshVisibleListInternal(UIScript::Token, bool refresh = false);
|
||||||
static void UpdateVisibleList(UIScript::Token);
|
static void UpdateVisibleList(UIScript::Token);
|
||||||
static void InsertRequest(Network::Address address);
|
static void InsertRequest(Network::Address address);
|
||||||
static void Insert(Network::Address address, Utils::InfoString info);
|
static void Insert(const Network::Address& address, const Utils::InfoString& info);
|
||||||
|
|
||||||
static ServerInfo* GetCurrentServer();
|
static ServerInfo* GetCurrentServer();
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ namespace Components
|
|||||||
|
|
||||||
Utils::Cryptography::ECC::Key Session::SignatureKey;
|
Utils::Cryptography::ECC::Key Session::SignatureKey;
|
||||||
|
|
||||||
std::map<std::string, Utils::Slot<Network::Callback>> Session::PacketHandlers;
|
std::unordered_map<std::string, Network::NetworkCallback> Session::PacketHandlers;
|
||||||
|
|
||||||
std::queue<std::pair<Network::Address, std::string>> Session::SignatureQueue;
|
std::queue<std::pair<Network::Address, std::string>> Session::SignatureQueue;
|
||||||
|
|
||||||
@ -58,10 +58,10 @@ namespace Components
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::Handle(const std::string& packet, Utils::Slot<Network::Callback> callback)
|
void Session::Handle(const std::string& packet, const Network::NetworkCallback& callback)
|
||||||
{
|
{
|
||||||
#ifdef DISABLE_SESSION
|
#ifdef DISABLE_SESSION
|
||||||
Network::Handle(packet, callback);
|
Network::OnPacket(packet, callback);
|
||||||
#else
|
#else
|
||||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||||
Session::PacketHandlers[packet] = callback;
|
Session::PacketHandlers[packet] = callback;
|
||||||
@ -150,7 +150,7 @@ namespace Components
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Handle("sessionSyn", [](Network::Address address, const std::string& data)
|
Network::OnPacket("sessionSyn", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
Session::Frame frame;
|
Session::Frame frame;
|
||||||
frame.challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
frame.challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
||||||
@ -161,13 +161,13 @@ namespace Components
|
|||||||
Network::SendCommand(address, "sessionAck", frame.challenge);
|
Network::SendCommand(address, "sessionAck", frame.challenge);
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("sessionAck", [](Network::Address address, const std::string& data)
|
Network::OnPacket("sessionAck", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||||
Session::SignatureQueue.push({ address, data });
|
Session::SignatureQueue.push({ address, data });
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("sessionFin", [](Network::Address address, const std::string& data)
|
Network::OnPacket("sessionFin", [](Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ namespace Components
|
|||||||
void preDestroy() override;
|
void preDestroy() override;
|
||||||
|
|
||||||
static void Send(Network::Address target, const std::string& command, const std::string& data = "");
|
static void Send(Network::Address target, const std::string& command, const std::string& data = "");
|
||||||
static void Handle(const std::string& packet, Utils::Slot<Network::Callback> callback);
|
static void Handle(const std::string& packet, const Network::NetworkCallback& callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool Terminate;
|
static bool Terminate;
|
||||||
@ -46,7 +46,7 @@ namespace Components
|
|||||||
|
|
||||||
static Utils::Cryptography::ECC::Key SignatureKey;
|
static Utils::Cryptography::ECC::Key SignatureKey;
|
||||||
|
|
||||||
static std::map<std::string, Utils::Slot<Network::Callback>> PacketHandlers;
|
static std::unordered_map<std::string, Network::NetworkCallback> PacketHandlers;
|
||||||
|
|
||||||
static std::queue<std::pair<Network::Address, std::string>> SignatureQueue;
|
static std::queue<std::pair<Network::Address, std::string>> SignatureQueue;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ namespace Components
|
|||||||
|
|
||||||
if (Game::Scr_GetNumParam() >= 3u)
|
if (Game::Scr_GetNumParam() >= 3u)
|
||||||
{
|
{
|
||||||
duration = static_cast<int>(Game::Scr_GetFloat(2) * 1000.0);
|
duration = static_cast<int>(Game::Scr_GetFloat(2) * 1000.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
int delay = 0;
|
int delay = 0;
|
||||||
@ -70,16 +70,16 @@ namespace Components
|
|||||||
// set snapshot num to 1 behind (T6 does this, why shouldn't we?)
|
// set snapshot num to 1 behind (T6 does this, why shouldn't we?)
|
||||||
for (int i = 0; i < *Game::svs_clientCount; ++i)
|
for (int i = 0; i < *Game::svs_clientCount; ++i)
|
||||||
{
|
{
|
||||||
Game::svs_clients[i].snapNum = *reinterpret_cast<DWORD*>(0x31D9384) - 1;
|
Game::svs_clients[i].snapNum = *Game::svs_time - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlowMotion::DrawConnectionInterruptedStub(int /*a1*/)
|
void SlowMotion::DrawConnectionInterruptedStub(int /*a1*/)
|
||||||
{
|
{
|
||||||
// if (!*reinterpret_cast<bool*>(0x1AD8ED0) && !*reinterpret_cast<bool*>(0x1AD8EEC) && !*reinterpret_cast<int*>(0x1AD78F8))
|
// if (!*reinterpret_cast<bool*>(0x1AD8ED0) && !*reinterpret_cast<bool*>(0x1AD8EEC) && !*reinterpret_cast<int*>(0x1AD78F8))
|
||||||
// {
|
// {
|
||||||
// Utils::Hook::Call<void(int)>(0x454A70)(a1);
|
// Utils::Hook::Call<void(int)>(0x454A70)(a1);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
SlowMotion::SlowMotion()
|
SlowMotion::SlowMotion()
|
||||||
|
76
src/Components/Modules/UserInfo.cpp
Normal file
76
src/Components/Modules/UserInfo.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
std::unordered_map<int, UserInfo::userInfoMap> UserInfo::UserInfoOverrides;
|
||||||
|
|
||||||
|
void UserInfo::SV_GetUserInfo_Stub(int index, char* buffer, int bufferSize)
|
||||||
|
{
|
||||||
|
Utils::Hook::Call<void(int, char*, int)>(0x49A160)(index, buffer, bufferSize);
|
||||||
|
|
||||||
|
Utils::InfoString map(buffer);
|
||||||
|
|
||||||
|
if (!UserInfoOverrides.contains(index))
|
||||||
|
{
|
||||||
|
UserInfoOverrides[index] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [key, val] : UserInfoOverrides[index])
|
||||||
|
{
|
||||||
|
if (val.empty())
|
||||||
|
{
|
||||||
|
map.remove(key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
map.set(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto userInfo = map.build();
|
||||||
|
strncpy_s(buffer, bufferSize, userInfo.data(), _TRUNCATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfo::ClearClientOverrides(const int client)
|
||||||
|
{
|
||||||
|
UserInfoOverrides[client].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfo::ClearAllOverrides()
|
||||||
|
{
|
||||||
|
UserInfoOverrides.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfo::AddScriptMethods()
|
||||||
|
{
|
||||||
|
Script::AddMethod("SetName", [](Game::scr_entref_t entref) // gsc: self SetName(<string>)
|
||||||
|
{
|
||||||
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
|
const auto* name = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
UserInfoOverrides[ent->s.number]["name"] = name;
|
||||||
|
Game::ClientUserinfoChanged(ent->s.number);
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddMethod("ResetName", [](Game::scr_entref_t entref) // gsc: self ResetName()
|
||||||
|
{
|
||||||
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
|
|
||||||
|
UserInfoOverrides[ent->s.number].erase("name");
|
||||||
|
Game::ClientUserinfoChanged(ent->s.number);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
UserInfo::UserInfo()
|
||||||
|
{
|
||||||
|
Utils::Hook(0x445268, SV_GetUserInfo_Stub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x478B04, SV_GetUserInfo_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
AddScriptMethods();
|
||||||
|
|
||||||
|
Script::OnVMShutdown([]
|
||||||
|
{
|
||||||
|
ClearAllOverrides();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
21
src/Components/Modules/UserInfo.hpp
Normal file
21
src/Components/Modules/UserInfo.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class UserInfo : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UserInfo();
|
||||||
|
|
||||||
|
static void ClearClientOverrides(int client);
|
||||||
|
static void ClearAllOverrides();
|
||||||
|
|
||||||
|
private:
|
||||||
|
using userInfoMap = std::unordered_map<std::string, std::string>;
|
||||||
|
static std::unordered_map<int, userInfoMap> UserInfoOverrides;
|
||||||
|
|
||||||
|
static void SV_GetUserInfo_Stub(int index, char* buffer, int bufferSize);
|
||||||
|
|
||||||
|
static void AddScriptMethods();
|
||||||
|
};
|
||||||
|
}
|
@ -39,7 +39,7 @@ namespace Main
|
|||||||
popad
|
popad
|
||||||
|
|
||||||
push 6BAA2Fh // Continue init routine
|
push 6BAA2Fh // Continue init routine
|
||||||
push 6CA062h // ___security_init_cookie
|
push 6CA062h // __security_init_cookie
|
||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -68,6 +68,7 @@ namespace Game
|
|||||||
|
|
||||||
Com_Error_t Com_Error = Com_Error_t(0x4B22D0);
|
Com_Error_t Com_Error = Com_Error_t(0x4B22D0);
|
||||||
Com_Printf_t Com_Printf = Com_Printf_t(0x402500);
|
Com_Printf_t Com_Printf = Com_Printf_t(0x402500);
|
||||||
|
Com_PrintError_t Com_PrintError = Com_PrintError_t(0x4F8C70);
|
||||||
Com_PrintMessage_t Com_PrintMessage = Com_PrintMessage_t(0x4AA830);
|
Com_PrintMessage_t Com_PrintMessage = Com_PrintMessage_t(0x4AA830);
|
||||||
Com_EndParseSession_t Com_EndParseSession = Com_EndParseSession_t(0x4B80B0);
|
Com_EndParseSession_t Com_EndParseSession = Com_EndParseSession_t(0x4B80B0);
|
||||||
Com_BeginParseSession_t Com_BeginParseSession = Com_BeginParseSession_t(0x4AAB80);
|
Com_BeginParseSession_t Com_BeginParseSession = Com_BeginParseSession_t(0x4AAB80);
|
||||||
@ -156,6 +157,10 @@ namespace Game
|
|||||||
G_LogPrintf_t G_LogPrintf = G_LogPrintf_t(0x4B0150);
|
G_LogPrintf_t G_LogPrintf = G_LogPrintf_t(0x4B0150);
|
||||||
G_GetWeaponIndexForName_t G_GetWeaponIndexForName = G_GetWeaponIndexForName_t(0x49E540);
|
G_GetWeaponIndexForName_t G_GetWeaponIndexForName = G_GetWeaponIndexForName_t(0x49E540);
|
||||||
G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = G_SpawnEntitiesFromString_t(0x4D8840);
|
G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = G_SpawnEntitiesFromString_t(0x4D8840);
|
||||||
|
G_Spawn_t G_Spawn = G_Spawn_t(0x4226F0);
|
||||||
|
G_FreeEntity_t G_FreeEntity = G_FreeEntity_t(0x44C9D0);
|
||||||
|
G_SpawnItem_t G_SpawnItem = G_SpawnItem_t(0x403770);
|
||||||
|
G_GetItemClassname_t G_GetItemClassname = G_GetItemClassname_t(0x492630);
|
||||||
G_PrintEntities_t G_PrintEntities = G_PrintEntities_t(0x4E6A50);
|
G_PrintEntities_t G_PrintEntities = G_PrintEntities_t(0x4E6A50);
|
||||||
G_GetEntityTypeName_t G_GetEntityTypeName = G_GetEntityTypeName_t(0x4EB810);
|
G_GetEntityTypeName_t G_GetEntityTypeName = G_GetEntityTypeName_t(0x4EB810);
|
||||||
|
|
||||||
@ -431,6 +436,12 @@ namespace Game
|
|||||||
IN_Init_t IN_Init = IN_Init_t(0x45D620);
|
IN_Init_t IN_Init = IN_Init_t(0x45D620);
|
||||||
IN_Shutdown_t IN_Shutdown = IN_Shutdown_t(0x426360);
|
IN_Shutdown_t IN_Shutdown = IN_Shutdown_t(0x426360);
|
||||||
|
|
||||||
|
Touch_Item_t Touch_Item = Touch_Item_t(0x44FA20);
|
||||||
|
|
||||||
|
Add_Ammo_t Add_Ammo = Add_Ammo_t(0x4E1480);
|
||||||
|
|
||||||
|
ClientUserinfoChanged_t ClientUserinfoChanged = ClientUserinfoChanged_t(0x445240);
|
||||||
|
|
||||||
player_die_t player_die = player_die_t(0x42BC70);
|
player_die_t player_die = player_die_t(0x42BC70);
|
||||||
|
|
||||||
XAssetHeader* DB_XAssetPool = reinterpret_cast<XAssetHeader*>(0x7998A8);
|
XAssetHeader* DB_XAssetPool = reinterpret_cast<XAssetHeader*>(0x7998A8);
|
||||||
@ -505,7 +516,7 @@ namespace Game
|
|||||||
FxEffectDef*** varFxEffectDefHandle = reinterpret_cast<FxEffectDef***>(0x112ACC0);
|
FxEffectDef*** varFxEffectDefHandle = reinterpret_cast<FxEffectDef***>(0x112ACC0);
|
||||||
PhysCollmap*** varPhysCollmapPtr = reinterpret_cast<PhysCollmap***>(0x112B440);
|
PhysCollmap*** varPhysCollmapPtr = reinterpret_cast<PhysCollmap***>(0x112B440);
|
||||||
PhysPreset*** varPhysPresetPtr = reinterpret_cast<PhysPreset***>(0x112B378);
|
PhysPreset*** varPhysPresetPtr = reinterpret_cast<PhysPreset***>(0x112B378);
|
||||||
Game::MaterialPass** varMaterialPass = reinterpret_cast<Game::MaterialPass**>(0x112A960);
|
MaterialPass** varMaterialPass = reinterpret_cast<MaterialPass**>(0x112A960);
|
||||||
snd_alias_list_t*** varsnd_alias_list_name = reinterpret_cast<snd_alias_list_t***>(0x112AF38);
|
snd_alias_list_t*** varsnd_alias_list_name = reinterpret_cast<snd_alias_list_t***>(0x112AF38);
|
||||||
|
|
||||||
FxElemField* s_elemFields = reinterpret_cast<FxElemField*>(0x73B848);
|
FxElemField* s_elemFields = reinterpret_cast<FxElemField*>(0x73B848);
|
||||||
@ -568,6 +579,8 @@ namespace Game
|
|||||||
|
|
||||||
int* g_waitingForKey = reinterpret_cast<int*>(0x63A50FC);
|
int* g_waitingForKey = reinterpret_cast<int*>(0x63A50FC);
|
||||||
|
|
||||||
|
unsigned long* _tls_index = reinterpret_cast<unsigned long*>(0x66D94A8);
|
||||||
|
|
||||||
void Sys_LockRead(FastCriticalSection* critSect)
|
void Sys_LockRead(FastCriticalSection* critSect)
|
||||||
{
|
{
|
||||||
InterlockedIncrement(&critSect->readCount);
|
InterlockedIncrement(&critSect->readCount);
|
||||||
|
@ -40,7 +40,7 @@ namespace Game
|
|||||||
typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* filename);
|
typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* filename);
|
||||||
extern BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj;
|
extern BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj;
|
||||||
|
|
||||||
typedef WeaponDef* (__cdecl * BG_GetWeaponDef_t)(int weaponIndex);
|
typedef WeaponDef*(__cdecl * BG_GetWeaponDef_t)(unsigned int weaponIndex);
|
||||||
extern BG_GetWeaponDef_t BG_GetWeaponDef;
|
extern BG_GetWeaponDef_t BG_GetWeaponDef;
|
||||||
|
|
||||||
typedef const char*(__cdecl * BG_GetEntityTypeName_t)(const int eType);
|
typedef const char*(__cdecl * BG_GetEntityTypeName_t)(const int eType);
|
||||||
@ -139,6 +139,9 @@ namespace Game
|
|||||||
typedef void(__cdecl * Com_Printf_t)(int channel, const char *fmt, ...);
|
typedef void(__cdecl * Com_Printf_t)(int channel, const char *fmt, ...);
|
||||||
extern Com_Printf_t Com_Printf;
|
extern Com_Printf_t Com_Printf;
|
||||||
|
|
||||||
|
typedef void(__cdecl * Com_PrintError_t)(int channel, const char* fmt, ...);
|
||||||
|
extern Com_PrintError_t Com_PrintError;
|
||||||
|
|
||||||
typedef void(__cdecl * Com_PrintMessage_t)(int channel, const char *msg, int error);
|
typedef void(__cdecl * Com_PrintMessage_t)(int channel, const char *msg, int error);
|
||||||
extern Com_PrintMessage_t Com_PrintMessage;
|
extern Com_PrintMessage_t Com_PrintMessage;
|
||||||
|
|
||||||
@ -389,6 +392,18 @@ namespace Game
|
|||||||
typedef void(__cdecl * G_SpawnEntitiesFromString_t)();
|
typedef void(__cdecl * G_SpawnEntitiesFromString_t)();
|
||||||
extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString;
|
extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString;
|
||||||
|
|
||||||
|
typedef gentity_s*(__cdecl * G_Spawn_t)();
|
||||||
|
extern G_Spawn_t G_Spawn;
|
||||||
|
|
||||||
|
typedef void(__cdecl * G_FreeEntity_t)(gentity_s* ed);
|
||||||
|
extern G_FreeEntity_t G_FreeEntity;
|
||||||
|
|
||||||
|
typedef void(__cdecl * G_SpawnItem_t)(gentity_s* ent, int item);
|
||||||
|
extern G_SpawnItem_t G_SpawnItem;
|
||||||
|
|
||||||
|
typedef void(__cdecl * G_GetItemClassname_t)(int item, gentity_s* ent);
|
||||||
|
extern G_GetItemClassname_t G_GetItemClassname;
|
||||||
|
|
||||||
typedef void(__cdecl * G_PrintEntities_t)();
|
typedef void(__cdecl * G_PrintEntities_t)();
|
||||||
extern G_PrintEntities_t G_PrintEntities;
|
extern G_PrintEntities_t G_PrintEntities;
|
||||||
|
|
||||||
@ -834,7 +849,7 @@ namespace Game
|
|||||||
typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID);
|
typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID);
|
||||||
extern SV_GameClientNum_Score_t SV_GameClientNum_Score;
|
extern SV_GameClientNum_Score_t SV_GameClientNum_Score;
|
||||||
|
|
||||||
typedef void(__cdecl * SV_GameSendServerCommand_t)(int clientNum, /*svscmd_type*/int type, const char* text);
|
typedef void(__cdecl * SV_GameSendServerCommand_t)(int clientNum, svscmd_type type, const char* text);
|
||||||
extern SV_GameSendServerCommand_t SV_GameSendServerCommand;
|
extern SV_GameSendServerCommand_t SV_GameSendServerCommand;
|
||||||
|
|
||||||
typedef void(__cdecl * SV_Cmd_TokenizeString_t)(const char* string);
|
typedef void(__cdecl * SV_Cmd_TokenizeString_t)(const char* string);
|
||||||
@ -1029,6 +1044,15 @@ namespace Game
|
|||||||
typedef void(__cdecl * IN_Shutdown_t)();
|
typedef void(__cdecl * IN_Shutdown_t)();
|
||||||
extern IN_Shutdown_t IN_Shutdown;
|
extern IN_Shutdown_t IN_Shutdown;
|
||||||
|
|
||||||
|
typedef void(__cdecl * Touch_Item_t)(gentity_s* ent, gentity_s* other, int touched);
|
||||||
|
extern Touch_Item_t Touch_Item;
|
||||||
|
|
||||||
|
typedef void(__cdecl * Add_Ammo_t)(gentity_s* ent, unsigned int weaponIndex, unsigned char weaponModel, int count, int fillClip);
|
||||||
|
extern Add_Ammo_t Add_Ammo;
|
||||||
|
|
||||||
|
typedef void(__cdecl * ClientUserinfoChanged_t)(int clientNum);
|
||||||
|
extern ClientUserinfoChanged_t ClientUserinfoChanged;
|
||||||
|
|
||||||
typedef void(__cdecl * player_die_t)(gentity_s* self, const gentity_s* inflictor, gentity_s* attacker, int damage, int meansOfDeath, int iWeapon, const float* vDir, const hitLocation_t hitLoc, int psTimeOffset);
|
typedef void(__cdecl * player_die_t)(gentity_s* self, const gentity_s* inflictor, gentity_s* attacker, int damage, int meansOfDeath, int iWeapon, const float* vDir, const hitLocation_t hitLoc, int psTimeOffset);
|
||||||
extern player_die_t player_die;
|
extern player_die_t player_die;
|
||||||
|
|
||||||
@ -1173,6 +1197,8 @@ namespace Game
|
|||||||
|
|
||||||
extern int* g_waitingForKey;
|
extern int* g_waitingForKey;
|
||||||
|
|
||||||
|
extern unsigned long* _tls_index;
|
||||||
|
|
||||||
void Sys_LockRead(FastCriticalSection* critSect);
|
void Sys_LockRead(FastCriticalSection* critSect);
|
||||||
void Sys_UnlockRead(FastCriticalSection* critSect);
|
void Sys_UnlockRead(FastCriticalSection* critSect);
|
||||||
|
|
||||||
|
@ -135,24 +135,24 @@ namespace Game
|
|||||||
|
|
||||||
enum dvar_flag : unsigned __int16
|
enum dvar_flag : unsigned __int16
|
||||||
{
|
{
|
||||||
DVAR_NONE = 0x0, // No flags
|
DVAR_NONE = 0, // No flags
|
||||||
DVAR_ARCHIVE = 0x1, // Set to cause it to be saved to config_mp.cfg of the client
|
DVAR_ARCHIVE = 1 << 0, // Set to cause it to be saved to config_mp.cfg of the client
|
||||||
DVAR_LATCH = 0x2, // Will only change when C code next does a Dvar_Get(), so it can't be changed
|
DVAR_LATCH = 1 << 1, // Will only change when C code next does a Dvar_Get(), so it can't be changed
|
||||||
// without proper initialization. Modified will be set, even though the value hasn't changed yet
|
// without proper initialization. Modified will be set, even though the value hasn't changed yet
|
||||||
DVAR_CHEAT = 0x4, // Can not be changed if cheats are disabled
|
DVAR_CHEAT = 1 << 2, // Can not be changed if cheats are disabled
|
||||||
DVAR_CODINFO = 0x8, // On change, this is sent to all clients (if you are host)
|
DVAR_CODINFO = 1 << 3, // On change, this is sent to all clients (if you are host)
|
||||||
DVAR_SCRIPTINFO = 0x10,
|
DVAR_SCRIPTINFO = 1 << 4,
|
||||||
DVAR_UNKNOWN20 = 0x20,
|
DVAR_UNKNOWN20 = 1 << 5,
|
||||||
DVAR_CHANGEABLE_RESET = 0x40,
|
DVAR_CHANGEABLE_RESET = 1 << 6,
|
||||||
DVAR_UNKNOWN80 = 0x80,
|
DVAR_UNKNOWN80 = 1 << 7,
|
||||||
DVAR_EXTERNAL = 0x100, // Created by a set command
|
DVAR_EXTERNAL = 1 << 8, // Created by a set command
|
||||||
DVAR_USERINFO = 0x200, // Sent to server on connect or change
|
DVAR_USERINFO = 1 << 9, // Sent to server on connect or change
|
||||||
DVAR_SERVERINFO = 0x400, // Sent in response to front end requests
|
DVAR_SERVERINFO = 1 << 10, // Sent in response to front end requests
|
||||||
DVAR_WRITEPROTECTED = 0x800,
|
DVAR_WRITEPROTECTED = 1 << 11,
|
||||||
DVAR_SYSTEMINFO = 0x1000, // Will be duplicated on all clients
|
DVAR_SYSTEMINFO = 1 << 12, // Will be duplicated on all clients
|
||||||
DVAR_READONLY = 0x2000, // Read only (same as DVAR_WRITEPROTECTED?)
|
DVAR_READONLY = 1 << 13, // Read only (same as DVAR_WRITEPROTECTED?)
|
||||||
DVAR_SAVED = 0x4000,
|
DVAR_SAVED = 1 << 14,
|
||||||
DVAR_AUTOEXEC = 0x8000,
|
DVAR_AUTOEXEC = 1 << 15, // isLoadingAutoExecGlobalFlag is always false so it should be never set by the game
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ImageCategory : char
|
enum ImageCategory : char
|
||||||
@ -234,6 +234,13 @@ namespace Game
|
|||||||
CS_ACTIVE = 0x5,
|
CS_ACTIVE = 0x5,
|
||||||
} clientstate_t;
|
} clientstate_t;
|
||||||
|
|
||||||
|
enum serverState_t
|
||||||
|
{
|
||||||
|
SS_DEAD = 0x0,
|
||||||
|
SS_LOADING = 0x1,
|
||||||
|
SS_GAME = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
enum errorParm_t
|
enum errorParm_t
|
||||||
{
|
{
|
||||||
ERR_FATAL = 0x0,
|
ERR_FATAL = 0x0,
|
||||||
@ -336,6 +343,12 @@ namespace Game
|
|||||||
HITLOC_NUM
|
HITLOC_NUM
|
||||||
} hitLocation_t;
|
} hitLocation_t;
|
||||||
|
|
||||||
|
enum svscmd_type
|
||||||
|
{
|
||||||
|
SV_CMD_CAN_IGNORE = 0x0,
|
||||||
|
SV_CMD_RELIABLE = 0x1,
|
||||||
|
};
|
||||||
|
|
||||||
struct FxEffectDef;
|
struct FxEffectDef;
|
||||||
struct pathnode_t;
|
struct pathnode_t;
|
||||||
struct pathnode_tree_t;
|
struct pathnode_tree_t;
|
||||||
@ -1614,7 +1627,6 @@ namespace Game
|
|||||||
int groundEntityNum;
|
int groundEntityNum;
|
||||||
int loopSound;
|
int loopSound;
|
||||||
int surfType;
|
int surfType;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
int brushModel;
|
int brushModel;
|
||||||
@ -1623,7 +1635,6 @@ namespace Game
|
|||||||
int xmodel;
|
int xmodel;
|
||||||
int primaryLight;
|
int primaryLight;
|
||||||
} index;
|
} index;
|
||||||
|
|
||||||
int clientNum;
|
int clientNum;
|
||||||
int iHeadIcon;
|
int iHeadIcon;
|
||||||
int iHeadIconTeam;
|
int iHeadIconTeam;
|
||||||
@ -1635,8 +1646,23 @@ namespace Game
|
|||||||
unsigned __int16 weapon;
|
unsigned __int16 weapon;
|
||||||
int legsAnim;
|
int legsAnim;
|
||||||
int torsoAnim;
|
int torsoAnim;
|
||||||
int un1;
|
union
|
||||||
int un2;
|
{
|
||||||
|
int eventParm2;
|
||||||
|
int hintString;
|
||||||
|
int fxId;
|
||||||
|
int helicopterStage;
|
||||||
|
} un1;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int hintType;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned __int16 vehicleXModel;
|
||||||
|
char weaponModel;
|
||||||
|
} __s1;
|
||||||
|
int actorFlags;
|
||||||
|
} un2;
|
||||||
clientLinkInfo_t clientLinkInfo;
|
clientLinkInfo_t clientLinkInfo;
|
||||||
unsigned int partBits[6];
|
unsigned int partBits[6];
|
||||||
int clientMask[1];
|
int clientMask[1];
|
||||||
@ -2146,6 +2172,8 @@ namespace Game
|
|||||||
cLeaf_t leaf;
|
cLeaf_t leaf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(cmodel_t) == 0x44);
|
||||||
|
|
||||||
struct TriggerModel
|
struct TriggerModel
|
||||||
{
|
{
|
||||||
int contents;
|
int contents;
|
||||||
|
@ -2,12 +2,22 @@
|
|||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
|
InfoString::InfoString(const std::string& buffer)
|
||||||
|
{
|
||||||
|
this->parse(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
void InfoString::set(const std::string& key, const std::string& value)
|
void InfoString::set(const std::string& key, const std::string& value)
|
||||||
{
|
{
|
||||||
this->keyValuePairs[key] = value;
|
this->keyValuePairs[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string InfoString::get(const std::string& key)
|
void InfoString::remove(const std::string& key)
|
||||||
|
{
|
||||||
|
this->keyValuePairs.erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string InfoString::get(const std::string& key) const
|
||||||
{
|
{
|
||||||
const auto value = this->keyValuePairs.find(key);
|
const auto value = this->keyValuePairs.find(key);
|
||||||
if (value != this->keyValuePairs.end())
|
if (value != this->keyValuePairs.end())
|
||||||
@ -15,7 +25,7 @@ namespace Utils
|
|||||||
return value->second;
|
return value->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfoString::parse(std::string buffer)
|
void InfoString::parse(std::string buffer)
|
||||||
@ -25,17 +35,17 @@ namespace Utils
|
|||||||
buffer = buffer.substr(1);
|
buffer = buffer.substr(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto KeyValues = Utils::String::Split(buffer, '\\');
|
const auto keyValues = Utils::String::Split(buffer, '\\');
|
||||||
|
|
||||||
for (size_t i = 0; !KeyValues.empty() && i < (KeyValues.size() - 1); i += 2)
|
for (std::size_t i = 0; !keyValues.empty() && i < (keyValues.size() - 1); i += 2)
|
||||||
{
|
{
|
||||||
const auto& key = KeyValues[i];
|
const auto& key = keyValues[i];
|
||||||
const auto& value = KeyValues[i + 1];
|
const auto& value = keyValues[i + 1];
|
||||||
this->keyValuePairs[key] = value;
|
this->keyValuePairs[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string InfoString::build()
|
std::string InfoString::build() const
|
||||||
{
|
{
|
||||||
std::string infoString;
|
std::string infoString;
|
||||||
|
|
||||||
@ -54,16 +64,18 @@ namespace Utils
|
|||||||
return infoString;
|
return infoString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
void InfoString::dump()
|
void InfoString::dump()
|
||||||
{
|
{
|
||||||
for (const auto& [key, value] : this->keyValuePairs)
|
for (const auto& [key, value] : this->keyValuePairs)
|
||||||
{
|
{
|
||||||
OutputDebugStringA(Utils::String::VA("%s: %s\n", key.data(), value.data()));
|
OutputDebugStringA(String::VA("%s: %s\n", key.data(), value.data()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
json11::Json InfoString::to_json()
|
json11::Json InfoString::to_json() const
|
||||||
{
|
{
|
||||||
return this->keyValuePairs;
|
return {this->keyValuePairs};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,23 @@ namespace Utils
|
|||||||
class InfoString
|
class InfoString
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InfoString() {};
|
InfoString() = default;
|
||||||
InfoString(const std::string& buffer) : InfoString() { this->parse(buffer); };
|
explicit InfoString(const std::string& buffer);
|
||||||
|
|
||||||
void set(const std::string& key, const std::string& value);
|
void set(const std::string& key, const std::string& value);
|
||||||
std::string get(const std::string& key);
|
void remove(const std::string& key);
|
||||||
std::string build();
|
|
||||||
|
|
||||||
|
[[nodiscard]] std::string get(const std::string& key) const;
|
||||||
|
[[nodiscard]] std::string build() const;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
void dump();
|
void dump();
|
||||||
|
#endif
|
||||||
|
|
||||||
json11::Json to_json();
|
[[nodiscard]] json11::Json to_json() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::string, std::string> keyValuePairs;
|
std::unordered_map<std::string, std::string> keyValuePairs;
|
||||||
void parse(std::string buffer);
|
void parse(std::string buffer);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -127,4 +127,7 @@ namespace Utils
|
|||||||
mutable std::recursive_mutex mutex;
|
mutable std::recursive_mutex mutex;
|
||||||
std::vector<Slot<T>> slots;
|
std::vector<Slot<T>> slots;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto VectorCopy(T a, T b) { b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; }
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user