[GSC]: Script aliases (#610)

This commit is contained in:
Edo 2022-11-29 14:18:10 +00:00 committed by GitHub
parent c3d6b20644
commit b195d96abb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 243 additions and 176 deletions

View File

@ -7,10 +7,10 @@ namespace Components
struct BotMovementInfo struct BotMovementInfo
{ {
int buttons; // Actions std::int32_t buttons; // Actions
int8_t forward; std::int8_t forward;
int8_t right; std::int8_t right;
uint16_t weapon; std::uint16_t weapon;
bool active; bool active;
}; };
@ -19,7 +19,7 @@ namespace Components
struct BotAction struct BotAction
{ {
std::string action; std::string action;
int key; std::int32_t key;
}; };
static const BotAction BotActions[] = static const BotAction BotActions[] =
@ -47,7 +47,7 @@ namespace Components
static bool loadedNames = false; // Load file only once static bool loadedNames = false; // Load file only once
const char* botName; const char* botName;
if (Bots::BotNames.empty() && !loadedNames) if (BotNames.empty() && !loadedNames)
{ {
FileSystem::File bots("bots.txt"); FileSystem::File bots("bots.txt");
loadedNames = true; loadedNames = true;
@ -59,20 +59,20 @@ namespace Components
for (auto& name : names) for (auto& name : names)
{ {
Utils::String::Replace(name, "\r", ""); Utils::String::Replace(name, "\r", "");
name = Utils::String::Trim(name); Utils::String::Trim(name);
if (!name.empty()) if (!name.empty())
{ {
Bots::BotNames.push_back(name); BotNames.push_back(name);
} }
} }
} }
} }
if (!Bots::BotNames.empty()) if (!BotNames.empty())
{ {
botId %= Bots::BotNames.size(); botId %= BotNames.size();
botName = Bots::BotNames[botId++].data(); botName = BotNames[botId++].data();
} }
else else
{ {
@ -125,8 +125,7 @@ namespace Components
void Bots::AddMethods() void Bots::AddMethods()
{ {
Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot(); Script::AddMethMultiple(GScr_isTestClient, false, {"IsTestClient", "IsBot"}); // Usage: self IsTestClient();
Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient();
Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop(); Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
{ {
@ -240,7 +239,8 @@ namespace Components
return; return;
} }
Game::usercmd_s userCmd = {0}; Game::usercmd_s userCmd;
ZeroMemory(&userCmd, sizeof(Game::usercmd_s));
userCmd.serverTime = *Game::svs_time; userCmd.serverTime = *Game::svs_time;
@ -260,7 +260,7 @@ namespace Components
pushad pushad
push edi push edi
call Bots::BotAiAction call BotAiAction
add esp, 4 add esp, 4
popad popad
@ -284,7 +284,7 @@ namespace Components
push [esp + 0x20 + 0x8] push [esp + 0x20 + 0x8]
push [esp + 0x20 + 0x8] push [esp + 0x20 + 0x8]
call Bots::G_SelectWeaponIndex call G_SelectWeaponIndex
add esp, 0x8 add esp, 0x8
popad popad
@ -307,12 +307,12 @@ namespace Components
Utils::Hook::Set<const char*>(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\""); Utils::Hook::Set<const char*>(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\"");
// Intercept sprintf for the connect string // Intercept sprintf for the connect string
Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick(); Utils::Hook(0x48ADAB, BuildConnectString, HOOK_CALL).install()->quick();
Utils::Hook(0x627021, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick(); Utils::Hook(0x627021, SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x627241, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick(); Utils::Hook(0x627241, SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x441B80, Bots::G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick(); Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
// Reset BotMovementInfo.active when client is dropped // Reset BotMovementInfo.active when client is dropped
Events::OnClientDisconnect([](const int clientNum) Events::OnClientDisconnect([](const int clientNum)
@ -365,10 +365,10 @@ namespace Components
Toast::Show("cardicon_headshot", "^2Success", Utils::String::VA("Spawning %d %s...", count, (count == 1 ? "bot" : "bots")), 3000); Toast::Show("cardicon_headshot", "^2Success", Utils::String::VA("Spawning %d %s...", count, (count == 1 ? "bot" : "bots")), 3000);
Logger::Debug("Spawning {} {}", count, (count == 1 ? "bot" : "bots")); Logger::Debug("Spawning {} {}", count, (count == 1 ? "bot" : "bots"));
Bots::Spawn(count); Spawn(count);
}); });
Bots::AddMethods(); AddMethods();
// In case a loaded mod didn't call "BotStop" before the VM shutdown // In case a loaded mod didn't call "BotStop" before the VM shutdown
Events::OnVMShutdown([] Events::OnVMShutdown([]

View File

@ -318,7 +318,7 @@ namespace Components
return; return;
} }
if (strncmp(BugName->current.string, "bug", 3) != 0) if (std::strncmp(BugName->current.string, "bug", 3) != 0)
{ {
Game::Dvar_SetString(BugName, "bug0"); Game::Dvar_SetString(BugName, "bug0");
return; return;

View File

@ -229,7 +229,8 @@ namespace Components
// Don't perform any checks if name didn't change // Don't perform any checks if name didn't change
if (name == lastValidName) return; if (name == lastValidName) return;
std::string saneName = TextRenderer::StripAllTextIcons(TextRenderer::StripColors(Utils::String::Trim(name))); Utils::String::Trim(name);
std::string saneName = TextRenderer::StripAllTextIcons(TextRenderer::StripColors(name));
if (saneName.size() < 3 || (saneName[0] == '[' && saneName[1] == '{')) if (saneName.size() < 3 || (saneName[0] == '[' && saneName[1] == '{'))
{ {
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Username '{}' is invalid. It must at least be 3 characters long and not appear empty!\n", name); Logger::PrintError(Game::CON_CHANNEL_ERROR, "Username '{}' is invalid. It must at least be 3 characters long and not appear empty!\n", name);

View File

@ -3,8 +3,8 @@
namespace Components namespace Components
{ {
std::unordered_map<std::string, Script::ScriptFunction> Script::CustomScrFunctions; std::vector<Script::ScriptFunction> Script::CustomScrFunctions;
std::unordered_map<std::string, Script::ScriptMethod> Script::CustomScrMethods; std::vector<Script::ScriptMethod> Script::CustomScrMethods;
std::string Script::ScriptName; std::string Script::ScriptName;
std::vector<std::string> Script::ScriptNameStack; std::vector<std::string> Script::ScriptNameStack;
@ -278,43 +278,74 @@ namespace Components
Game::GScr_LoadGameTypeScript(); Game::GScr_LoadGameTypeScript();
} }
void Script::AddFunction(const std::string& name, Game::BuiltinFunction func, bool type) void Script::AddFunction(const std::string& name, const Game::BuiltinFunction func, const bool type)
{ {
ScriptFunction toAdd; ScriptFunction toAdd;
toAdd.actionFunc = func; toAdd.actionFunc = func;
toAdd.type = type; toAdd.type = type;
toAdd.aliases.push_back({Utils::String::ToLower(name)});
CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd); CustomScrFunctions.emplace_back(toAdd);
} }
void Script::AddMethod(const std::string& name, Game::BuiltinMethod func, bool type) void Script::AddMethod(const std::string& name, const Game::BuiltinMethod func, const bool type)
{ {
ScriptMethod toAdd; ScriptMethod toAdd;
toAdd.actionFunc = func; toAdd.actionFunc = func;
toAdd.type = type; toAdd.type = type;
toAdd.aliases.push_back({Utils::String::ToLower(name)});
CustomScrMethods.insert_or_assign(Utils::String::ToLower(name), toAdd); CustomScrMethods.emplace_back(toAdd);
}
void Script::AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames aliases)
{
ScriptFunction toAdd;
auto aliasesToAdd = Utils::String::ApplyToLower(aliases);
toAdd.actionFunc = func;
toAdd.type = type;
toAdd.aliases = std::move(aliasesToAdd);
CustomScrFunctions.emplace_back(toAdd);
}
void Script::AddMethMultiple(Game::BuiltinMethod func, bool type, scriptNames aliases)
{
ScriptMethod toAdd;
auto aliasesToAdd = Utils::String::ApplyToLower(aliases);
toAdd.actionFunc = func;
toAdd.type = type;
toAdd.aliases = std::move(aliasesToAdd);
CustomScrMethods.emplace_back(toAdd);
} }
Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type) Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type)
{ {
if (pName != nullptr) if (pName != nullptr)
{ {
// If no function was found let's call game's function const auto name = Utils::String::ToLower(*pName);
if (const auto itr = CustomScrFunctions.find(Utils::String::ToLower(*pName)); itr != CustomScrFunctions.end()) for (const auto& func : CustomScrFunctions)
{ {
*type = itr->second.type; if (std::ranges::find(func.aliases, name) != func.aliases.end())
return itr->second.actionFunc; {
*type = func.type;
return func.actionFunc;
}
} }
} }
else else
{ {
for (const auto& [name, builtin] : CustomScrFunctions) for (const auto& func : CustomScrFunctions)
{ {
Game::Scr_RegisterFunction(reinterpret_cast<int>(builtin.actionFunc), name.data()); const auto& name = func.aliases[0];
Game::Scr_RegisterFunction(reinterpret_cast<int>(func.actionFunc), name.data());
} }
} }
// If no function was found let's call game's function
return Utils::Hook::Call<Game::BuiltinFunction(const char**, int*)>(0x5FA2B0)(pName, type); // BuiltIn_GetFunction return Utils::Hook::Call<Game::BuiltinFunction(const char**, int*)>(0x5FA2B0)(pName, type); // BuiltIn_GetFunction
} }
@ -322,21 +353,26 @@ namespace Components
{ {
if (pName != nullptr) if (pName != nullptr)
{ {
// If no method was found let's call game's function const auto name = Utils::String::ToLower(*pName);
if (const auto itr = CustomScrMethods.find(Utils::String::ToLower(*pName)); itr != CustomScrMethods.end()) for (const auto& meth : CustomScrMethods)
{ {
*type = itr->second.type; if (std::ranges::find(meth.aliases, name) != meth.aliases.end())
return itr->second.actionFunc; {
*type = meth.type;
return meth.actionFunc;
}
} }
} }
else else
{ {
for (const auto& [name, builtin] : CustomScrMethods) for (const auto& meth : CustomScrMethods)
{ {
Game::Scr_RegisterFunction(reinterpret_cast<int>(builtin.actionFunc), name.data()); const auto& name = meth.aliases[0];
Game::Scr_RegisterFunction(reinterpret_cast<int>(meth.actionFunc), name.data());
} }
} }
// If no method was found let's call game's function
return Utils::Hook::Call<Game::BuiltinMethod(const char**, int*)>(0x5FA360)(pName, type); // Player_GetMethod return Utils::Hook::Call<Game::BuiltinMethod(const char**, int*)>(0x5FA360)(pName, type); // Player_GetMethod
} }

View File

@ -7,9 +7,13 @@ namespace Components
public: public:
Script(); Script();
using scriptNames = std::vector<std::string>;
static void AddFunction(const std::string& name, Game::BuiltinFunction func, bool type = false); static void AddFunction(const std::string& name, Game::BuiltinFunction func, bool type = false);
static void AddMethod(const std::string& name, Game::BuiltinMethod func, bool type = false); static void AddMethod(const std::string& name, Game::BuiltinMethod func, bool type = false);
static void AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames);
static void AddMethMultiple(Game::BuiltinMethod func, bool type, scriptNames);
static Game::client_t* GetClient(const Game::gentity_t* gentity); static Game::client_t* GetClient(const Game::gentity_t* gentity);
static const char* GetCodePosForParam(int index); static const char* GetCodePosForParam(int index);
@ -40,16 +44,18 @@ namespace Components
{ {
Game::BuiltinFunction actionFunc; Game::BuiltinFunction actionFunc;
bool type; bool type;
scriptNames aliases;
}; };
struct ScriptMethod struct ScriptMethod
{ {
Game::BuiltinMethod actionFunc; Game::BuiltinMethod actionFunc;
bool type; bool type;
scriptNames aliases;
}; };
static std::unordered_map<std::string, ScriptFunction> CustomScrFunctions; static std::vector<ScriptFunction> CustomScrFunctions;
static std::unordered_map<std::string, ScriptMethod> CustomScrMethods; static std::vector<ScriptMethod> CustomScrMethods;
static std::string ScriptName; static std::string ScriptName;
static std::vector<std::string> ScriptNameStack; static std::vector<std::string> ScriptNameStack;

View File

@ -273,30 +273,6 @@ namespace Components
}); });
} }
void ScriptExtension::Scr_TableLookupIStringByRow()
{
if (Game::Scr_GetNumParam() < 3)
{
Game::Scr_Error("USAGE: tableLookupIStringByRow( filename, rowNum, returnValueColumnNum )\n");
return;
}
const auto* fileName = Game::Scr_GetString(0);
const auto rowNum = Game::Scr_GetInt(1);
const auto returnValueColumnNum = Game::Scr_GetInt(2);
const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable;
if (table == nullptr)
{
Game::Scr_ParamError(0, Utils::String::VA("%s does not exist\n", fileName));
return;
}
const auto* value = Game::StringTable_GetColumnValueForRow(table, rowNum, returnValueColumnNum);
Game::Scr_AddIString(value);
}
void ScriptExtension::AddEntityFields() void ScriptExtension::AddEntityFields()
{ {
AddEntityField("entityflags", Game::F_INT, AddEntityField("entityflags", Game::F_INT,
@ -330,16 +306,10 @@ namespace Components
AddEntityFields(); AddEntityFields();
AddClientFields(); AddClientFields();
// Correct builtin function pointer
Utils::Hook::Set<Game::BuiltinFunction>(0x79A90C, Scr_TableLookupIStringByRow);
Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
// Fix format string in Scr_RandomFloatRange
Utils::Hook::Set<const char*>(0x5F10C6, "Scr_RandomFloatRange parms: %f %f ");
} }
} }

View File

@ -27,6 +27,5 @@ namespace Components
static void AddMethods(); static void AddMethods();
static void AddEntityFields(); static void AddEntityFields();
static void AddClientFields(); static void AddClientFields();
static void Scr_TableLookupIStringByRow();
}; };
} }

View File

@ -20,8 +20,38 @@ namespace Components
return &Game::g_hudelems[entref.entnum]; return &Game::g_hudelems[entref.entnum];
} }
void ScriptPatches::Scr_TableLookupIStringByRow_Hk()
{
if (Game::Scr_GetNumParam() < 3)
{
Game::Scr_Error("USAGE: tableLookupIStringByRow( filename, rowNum, returnValueColumnNum )\n");
return;
}
const auto* fileName = Game::Scr_GetString(0);
const auto rowNum = Game::Scr_GetInt(1);
const auto returnValueColumnNum = Game::Scr_GetInt(2);
const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable;
if (table == nullptr)
{
Game::Scr_ParamError(0, Utils::String::VA("%s does not exist\n", fileName));
return;
}
const auto* value = Game::StringTable_GetColumnValueForRow(table, rowNum, returnValueColumnNum);
Game::Scr_AddIString(value);
}
ScriptPatches::ScriptPatches() ScriptPatches::ScriptPatches()
{ {
// Fix format string in Scr_RandomFloatRange
Utils::Hook::Set<const char*>(0x5F10C6, "Scr_RandomFloatRange parms: %f %f ");
// Correct builtin function pointer
Utils::Hook::Set<Game::BuiltinFunction>(0x79A90C, Scr_TableLookupIStringByRow_Hk);
Script::AddMethod("ClearHudText", [](Game::scr_entref_t entref) -> void Script::AddMethod("ClearHudText", [](Game::scr_entref_t entref) -> void
{ {
auto* hud = HECmd_GetHudElem(entref); auto* hud = HECmd_GetHudElem(entref);

View File

@ -9,5 +9,7 @@ namespace Components
private: private:
static Game::game_hudelem_s* HECmd_GetHudElem(Game::scr_entref_t entref); static Game::game_hudelem_s* HECmd_GetHudElem(Game::scr_entref_t entref);
static void Scr_TableLookupIStringByRow_Hk();
}; };
} }

View File

@ -22,7 +22,7 @@ namespace Components
_vsnprintf_s(buf, _TRUNCATE, message, va); _vsnprintf_s(buf, _TRUNCATE, message, va);
va_end(va); va_end(va);
Logger::MessagePrint(channel, {buf}); MessagePrint(channel, {buf});
} }
void Logger::MessagePrint(const int channel, const std::string& msg) void Logger::MessagePrint(const int channel, const std::string& msg)
@ -42,14 +42,14 @@ namespace Components
return; return;
} }
if (!Logger::IsConsoleReady()) if (!IsConsoleReady())
{ {
OutputDebugStringA(out.data()); OutputDebugStringA(out.data());
} }
if (!Game::Sys_IsMainThread()) if (!Game::Sys_IsMainThread())
{ {
Logger::EnqueueMessage(msg); EnqueueMessage(msg);
} }
else else
{ {
@ -67,14 +67,14 @@ namespace Components
const auto out = std::format("^2{}\n", msg); const auto out = std::format("^2{}\n", msg);
#endif #endif
Logger::MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out); MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out);
} }
void Logger::PrintInternal(int channel, std::string_view fmt, std::format_args&& args) void Logger::PrintInternal(int channel, std::string_view fmt, std::format_args&& args)
{ {
const auto msg = std::vformat(fmt, args); const auto msg = std::vformat(fmt, args);
Logger::MessagePrint(channel, msg); MessagePrint(channel, msg);
} }
void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view fmt, std::format_args&& args) void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view fmt, std::format_args&& args)
@ -92,7 +92,7 @@ namespace Components
const auto msg = "^1Error: " + std::vformat(fmt, args); const auto msg = "^1Error: " + std::vformat(fmt, args);
++(*Game::com_errorPrintsCount); ++(*Game::com_errorPrintsCount);
Logger::MessagePrint(channel, msg); MessagePrint(channel, msg);
if (*Game::cls_uiStarted != 0 && (*Game::com_fixedConsolePosition == 0)) if (*Game::cls_uiStarted != 0 && (*Game::com_fixedConsolePosition == 0))
{ {
@ -104,36 +104,36 @@ namespace Components
{ {
const auto msg = "^3" + std::vformat(fmt, args); const auto msg = "^3" + std::vformat(fmt, args);
Logger::MessagePrint(channel, msg); MessagePrint(channel, msg);
} }
void Logger::Frame() void Logger::Frame()
{ {
std::unique_lock _(Logger::MessageMutex); std::unique_lock _(MessageMutex);
for (auto i = Logger::MessageQueue.begin(); i != Logger::MessageQueue.end();) for (auto i = MessageQueue.begin(); i != MessageQueue.end();)
{ {
Game::Com_PrintMessage(Game::CON_CHANNEL_DONT_FILTER, i->data(), 0); Game::Com_PrintMessage(Game::CON_CHANNEL_DONT_FILTER, i->data(), 0);
if (!Logger::IsConsoleReady()) if (!IsConsoleReady())
{ {
OutputDebugStringA(i->data()); OutputDebugStringA(i->data());
} }
i = Logger::MessageQueue.erase(i); i = MessageQueue.erase(i);
} }
} }
void Logger::PipeOutput(const std::function<void(const std::string&)>& callback) void Logger::PipeOutput(const std::function<void(const std::string&)>& callback)
{ {
Logger::PipeCallback = callback; PipeCallback = callback;
} }
void Logger::PrintMessagePipe(const char* data) void Logger::PrintMessagePipe(const char* data)
{ {
if (Logger::PipeCallback) if (PipeCallback)
{ {
Logger::PipeCallback(data); PipeCallback(data);
} }
} }
@ -144,7 +144,7 @@ namespace Components
return; return;
} }
for (const auto& addr : Logger::LoggingAddresses[gLog & 1]) for (const auto& addr : LoggingAddresses[gLog & 1])
{ {
Network::SendCommand(addr, "print", data); Network::SendCommand(addr, "print", data);
} }
@ -169,20 +169,20 @@ namespace Components
} }
// Allow the network log to run even if logFile was not opened // Allow the network log to run even if logFile was not opened
Logger::NetworkLog(string, true); NetworkLog(string, true);
} }
__declspec(naked) void Logger::PrintMessage_Stub() __declspec(naked) void Logger::PrintMessage_Stub()
{ {
__asm __asm
{ {
mov eax, Logger::PipeCallback mov eax, PipeCallback
test eax, eax test eax, eax
jz returnPrint jz returnPrint
pushad pushad
push [esp + 28h] push [esp + 28h]
call Logger::PrintMessagePipe call PrintMessagePipe
add esp, 4h add esp, 4h
popad popad
retn retn
@ -191,7 +191,7 @@ namespace Components
pushad pushad
push 0 push 0
push [esp + 2Ch] push [esp + 2Ch]
call Logger::NetworkLog call NetworkLog
add esp, 8h add esp, 8h
popad popad
@ -205,8 +205,8 @@ namespace Components
void Logger::EnqueueMessage(const std::string& message) void Logger::EnqueueMessage(const std::string& message)
{ {
std::unique_lock _(Logger::MessageMutex); std::unique_lock _(MessageMutex);
Logger::MessageQueue.push_back(message); MessageQueue.push_back(message);
} }
void Logger::RedirectOSPath(const char* file, char* folder) void Logger::RedirectOSPath(const char* file, char* folder)
@ -232,7 +232,7 @@ namespace Components
push [esp + 28h] push [esp + 28h]
push [esp + 30h] push [esp + 30h]
call Logger::RedirectOSPath call RedirectOSPath
add esp, 8h add esp, 8h
@ -256,9 +256,9 @@ namespace Components
Network::Address addr(params->get(1)); Network::Address addr(params->get(1));
if (std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr) == Logger::LoggingAddresses[0].end()) if (std::find(LoggingAddresses[0].begin(), LoggingAddresses[0].end(), addr) == LoggingAddresses[0].end())
{ {
Logger::LoggingAddresses[0].push_back(addr); LoggingAddresses[0].push_back(addr);
} }
}); });
@ -267,37 +267,37 @@ namespace Components
if (params->size() < 2) return; if (params->size() < 2) return;
const auto num = atoi(params->get(1)); const auto num = atoi(params->get(1));
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[0].size()) if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < LoggingAddresses[0].size())
{ {
auto addr = Logger::LoggingAddresses[0].begin() + num; auto addr = Logger::LoggingAddresses[0].begin() + num;
Logger::Print("Address {} removed\n", addr->getString()); Print("Address {} removed\n", addr->getString());
Logger::LoggingAddresses[0].erase(addr); LoggingAddresses[0].erase(addr);
} }
else else
{ {
Network::Address addr(params->get(1)); Network::Address addr(params->get(1));
const auto i = std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr); const auto i = std::find(LoggingAddresses[0].begin(), LoggingAddresses[0].end(), addr);
if (i != Logger::LoggingAddresses[0].end()) if (i != LoggingAddresses[0].end())
{ {
Logger::LoggingAddresses[0].erase(i); LoggingAddresses[0].erase(i);
Logger::Print("Address {} removed\n", addr.getString()); Print("Address {} removed\n", addr.getString());
} }
else else
{ {
Logger::Print("Address {} not found!\n", addr.getString()); Print("Address {} not found!\n", addr.getString());
} }
} }
}); });
Command::AddSV("log_list", [](Command::Params*) Command::AddSV("log_list", []([[maybe_unused]] Command::Params* params)
{ {
Logger::Print("# ID: Address\n"); Print("# ID: Address\n");
Logger::Print("-------------\n"); Print("-------------\n");
for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i) for (unsigned int i = 0; i < LoggingAddresses[0].size(); ++i)
{ {
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[0][i].getString()); Print("#{:03d}: {}\n", i, LoggingAddresses[0][i].getString());
} }
}); });
@ -307,9 +307,9 @@ namespace Components
const Network::Address addr(params->get(1)); const Network::Address addr(params->get(1));
if (std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr) == Logger::LoggingAddresses[1].end()) if (std::find(LoggingAddresses[1].begin(), LoggingAddresses[1].end(), addr) == LoggingAddresses[1].end())
{ {
Logger::LoggingAddresses[1].push_back(addr); LoggingAddresses[1].push_back(addr);
} }
}); });
@ -318,37 +318,37 @@ namespace Components
if (params->size() < 2) return; if (params->size() < 2) return;
const auto num = std::atoi(params->get(1)); const auto num = std::atoi(params->get(1));
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[1].size()) if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < LoggingAddresses[1].size())
{ {
const auto addr = Logger::LoggingAddresses[1].begin() + num; const auto addr = LoggingAddresses[1].begin() + num;
Logger::Print("Address {} removed\n", addr->getString()); Print("Address {} removed\n", addr->getString());
Logger::LoggingAddresses[1].erase(addr); LoggingAddresses[1].erase(addr);
} }
else else
{ {
const Network::Address addr(params->get(1)); const Network::Address addr(params->get(1));
const auto i = std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr); const auto i = std::ranges::find(LoggingAddresses[1].begin(), LoggingAddresses[1].end(), addr);
if (i != Logger::LoggingAddresses[1].end()) if (i != LoggingAddresses[1].end())
{ {
Logger::LoggingAddresses[1].erase(i); LoggingAddresses[1].erase(i);
Logger::Print("Address {} removed\n", addr.getString()); Print("Address {} removed\n", addr.getString());
} }
else else
{ {
Logger::Print("Address {} not found!\n", addr.getString()); Print("Address {} not found!\n", addr.getString());
} }
} }
}); });
Command::AddSV("g_log_list", [](Command::Params*) Command::AddSV("g_log_list", [](Command::Params*)
{ {
Logger::Print("# ID: Address\n"); Print("# ID: Address\n");
Logger::Print("-------------\n"); Print("-------------\n");
for (std::size_t i = 0; i < Logger::LoggingAddresses[1].size(); ++i) for (std::size_t i = 0; i < LoggingAddresses[1].size(); ++i)
{ {
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[1][i].getString()); Print("#{:03d}: {}\n", i, LoggingAddresses[1][i].getString());
} }
}); });
} }
@ -356,30 +356,28 @@ namespace Components
Logger::Logger() Logger::Logger()
{ {
Dvar::Register<bool>("iw4x_onelog", false, Game::DVAR_LATCH | Game::DVAR_ARCHIVE, "Only write the game log to the 'userraw' OS folder"); Dvar::Register<bool>("iw4x_onelog", false, Game::DVAR_LATCH | Game::DVAR_ARCHIVE, "Only write the game log to the 'userraw' OS folder");
Utils::Hook(0x642139, Logger::BuildOSPath_Stub, HOOK_JUMP).install()->quick(); Utils::Hook(0x642139, BuildOSPath_Stub, HOOK_JUMP).install()->quick();
Logger::PipeOutput(nullptr); Scheduler::Loop(Frame, Scheduler::Pipeline::SERVER);
Scheduler::Loop(Logger::Frame, Scheduler::Pipeline::SERVER); Utils::Hook(Game::G_LogPrintf, G_LogPrintf_Hk, HOOK_JUMP).install()->quick();
Utils::Hook(Game::Com_PrintMessage, PrintMessage_Stub, HOOK_JUMP).install()->quick();
Utils::Hook(Game::G_LogPrintf, Logger::G_LogPrintf_Hk, HOOK_JUMP).install()->quick();
Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessage_Stub, HOOK_JUMP).install()->quick();
if (Loader::IsPerformingUnitTests()) if (Loader::IsPerformingUnitTests())
{ {
Utils::Hook(Game::Com_Printf, Logger::Print_Stub, HOOK_JUMP).install()->quick(); Utils::Hook(Game::Com_Printf, Print_Stub, HOOK_JUMP).install()->quick();
} }
Events::OnSVInit(Logger::AddServerCommands); Events::OnSVInit(AddServerCommands);
} }
Logger::~Logger() Logger::~Logger()
{ {
Logger::LoggingAddresses[0].clear(); LoggingAddresses[0].clear();
Logger::LoggingAddresses[1].clear(); LoggingAddresses[1].clear();
std::unique_lock lock(Logger::MessageMutex); std::unique_lock lock(MessageMutex);
Logger::MessageQueue.clear(); MessageQueue.clear();
lock.unlock(); lock.unlock();
// Flush the console log // Flush the console log

View File

@ -59,7 +59,7 @@ namespace Components
for (auto& node : nodeList) for (auto& node : nodeList)
{ {
Utils::String::Replace(node, "\r", ""); Utils::String::Replace(node, "\r", "");
node = Utils::String::Trim(node); Utils::String::Trim(node);
Node::Add(node); Node::Add(node);
} }
} }

View File

@ -18,7 +18,7 @@ namespace Components
} }
std::string readablePlayerName(buffer); std::string readablePlayerName(buffer);
readablePlayerName = Utils::String::Trim(readablePlayerName); Utils::String::Trim(readablePlayerName);
if (readablePlayerName.size() < 3) if (readablePlayerName.size() < 3)
{ {

View File

@ -127,8 +127,8 @@ namespace Components
Network::OnClientPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data) Network::OnClientPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
{ {
std::string data_ = data; std::string data_ = data;
Utils::String::Trim(data_); Utils::String::Trim(data_);
const auto pos = data.find_first_of(' '); const auto pos = data.find_first_of(' ');
if (pos == std::string::npos) if (pos == std::string::npos)
{ {

View File

@ -28,23 +28,24 @@
#pragma warning(disable: 4244) #pragma warning(disable: 4244)
#include <DbgHelp.h> #include <DbgHelp.h>
#include <sstream>
#include <fstream>
#include <cctype>
#include <regex>
#include <thread>
#include <future>
#include <unordered_map>
#include <queue>
#include <algorithm> #include <algorithm>
#include <limits> #include <cctype>
#include <chrono>
#include <cmath> #include <cmath>
#include <filesystem> #include <filesystem>
#include <optional>
#include <random>
#include <chrono>
#include <format> #include <format>
#include <fstream>
#include <future>
#include <limits>
#include <optional>
#include <queue>
#include <random>
#include <ranges>
#include <regex>
#include <source_location> #include <source_location>
#include <sstream>
#include <thread>
#include <unordered_map>
#pragma warning(pop) #pragma warning(pop)

View File

@ -145,9 +145,9 @@ namespace Utils::String
} }
// Trim from both ends // Trim from both ends
std::string& Trim(std::string& str) void Trim(std::string& str)
{ {
return LTrim(RTrim(str)); LTrim(RTrim(str));
} }
std::string Convert(const std::wstring& wstr) std::string Convert(const std::wstring& wstr)

View File

@ -11,7 +11,7 @@ namespace Utils::String
VAProvider() : currentBuffer(0) {} VAProvider() : currentBuffer(0) {}
~VAProvider() = default; ~VAProvider() = default;
const char* get(const char* format, va_list ap) [[nodiscard]] const char* get(const char* format, va_list ap)
{ {
++this->currentBuffer %= ARRAYSIZE(this->stringPool); ++this->currentBuffer %= ARRAYSIZE(this->stringPool);
auto entry = &this->stringPool[this->currentBuffer]; auto entry = &this->stringPool[this->currentBuffer];
@ -82,37 +82,61 @@ namespace Utils::String
} }
} }
const char* VA(const char* fmt, ...); [[nodiscard]] const char* VA(const char* fmt, ...);
std::string ToLower(const std::string& text); [[nodiscard]] std::string ToLower(const std::string& text);
std::string ToUpper(const std::string& text); [[nodiscard]] std::string ToUpper(const std::string& text);
bool Compare(const std::string& lhs, const std::string& rhs); template <class OutputIter>
[[nodiscard]] OutputIter ApplyToLower(OutputIter container)
{
OutputIter result;
std::ranges::transform(container, std::back_inserter(result), [](const std::string& s) -> std::string
{
return ToLower(s);
});
std::vector<std::string> Split(const std::string& str, char delim); return result;
}
template <class OutputIter>
[[nodiscard]] OutputIter ApplyToUpper(OutputIter container)
{
OutputIter result;
std::ranges::transform(container, std::back_inserter(result), [](const std::string& s) -> std::string
{
return ToUpper(s);
});
return result;
}
[[nodiscard]] bool Compare(const std::string& lhs, const std::string& rhs);
[[nodiscard]] std::vector<std::string> Split(const std::string& str, char delim);
void Replace(std::string& str, const std::string& from, const std::string& to); void Replace(std::string& str, const std::string& from, const std::string& to);
bool StartsWith(const std::string& haystack, const std::string& needle); [[nodiscard]] bool StartsWith(const std::string& haystack, const std::string& needle);
bool EndsWith(const std::string& haystack, const std::string& needle); [[nodiscard]] bool EndsWith(const std::string& haystack, const std::string& needle);
bool IsNumber(const std::string& str); [[nodiscard]] bool IsNumber(const std::string& str);
std::string& LTrim(std::string& str); std::string& LTrim(std::string& str);
std::string& RTrim(std::string& str); std::string& RTrim(std::string& str);
std::string& Trim(std::string& str); void Trim(std::string& str);
std::string Convert(const std::wstring& wstr); [[nodiscard]] std::string Convert(const std::wstring& wstr);
std::wstring Convert(const std::string& str); [[nodiscard]] std::wstring Convert(const std::string& str);
std::string FormatTimeSpan(int milliseconds); [[nodiscard]] std::string FormatTimeSpan(int milliseconds);
std::string FormatBandwidth(std::size_t bytes, int milliseconds); [[nodiscard]] std::string FormatBandwidth(std::size_t bytes, int milliseconds);
std::string DumpHex(const std::string& data, const std::string& separator = " "); [[nodiscard]] std::string DumpHex(const std::string& data, const std::string& separator = " ");
std::string XOR(std::string str, char value); [[nodiscard]] std::string XOR(std::string str, char value);
std::string EncodeBase64(const char* input, unsigned long inputSize); [[nodiscard]] std::string EncodeBase64(const char* input, unsigned long inputSize);
std::string EncodeBase64(const std::string& input); [[nodiscard]] std::string EncodeBase64(const std::string& input);
std::string EncodeBase128(const std::string& input); [[nodiscard]] std::string EncodeBase128(const std::string& input);
} }