Feature/set get stat (#485)
* [Script]: GetStat & SetStat from CoD4(x) * [Stats] Remove limitation * [Bullet] Clean this up
This commit is contained in:
@ -20,7 +20,7 @@ namespace Components
|
||||
}
|
||||
|
||||
// Game's code
|
||||
if (surfaceType != Game::materialSurfType_t::SURF_TYPE_DEFAULT)
|
||||
if (surfaceType != Game::SURF_TYPE_DEFAULT)
|
||||
{
|
||||
return (*Game::penetrationDepthTable)[weapDef->penetrateType][surfaceType];
|
||||
}
|
||||
|
@ -272,18 +272,5 @@ namespace Components
|
||||
// clanName in CG_Obituary
|
||||
Utils::Hook(0x586DD6, PlayerName::GetClientName, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x586E2A, PlayerName::GetClientName, HOOK_CALL).install()->quick();
|
||||
|
||||
Command::Add("statGet", [](Command::Params* params)
|
||||
{
|
||||
if (params->size() < 2)
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_SERVER, "statget usage: statget <index>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = std::atoi(params->get(1));
|
||||
const auto stat = Game::LiveStorage_GetStat(0, index);
|
||||
Logger::Print(Game::CON_CHANNEL_SYSTEM, "Stat {}: {}\n", index, stat);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ namespace Components
|
||||
void ScriptExtension::AddMethods()
|
||||
{
|
||||
// ScriptExtension methods
|
||||
Script::AddMethod("GetIp", [](Game::scr_entref_t entref) // gsc: self GetIp()
|
||||
Script::AddMethod("GetIp", [](const Game::scr_entref_t entref) // gsc: self GetIp()
|
||||
{
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
@ -259,7 +259,7 @@ namespace Components
|
||||
Game::Scr_AddString(ip.data());
|
||||
});
|
||||
|
||||
Script::AddMethod("GetPing", [](Game::scr_entref_t entref) // gsc: self GetPing()
|
||||
Script::AddMethod("GetPing", [](const Game::scr_entref_t entref) // gsc: self GetPing()
|
||||
{
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
@ -267,7 +267,7 @@ namespace Components
|
||||
Game::Scr_AddInt(client->ping);
|
||||
});
|
||||
|
||||
Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing(<int>)
|
||||
Script::AddMethod("SetPing", [](const Game::scr_entref_t entref) // gsc: self SetPing(<int>)
|
||||
{
|
||||
auto ping = Game::Scr_GetInt(0);
|
||||
|
||||
@ -306,7 +306,7 @@ namespace Components
|
||||
|
||||
void ScriptExtension::AddEntityFields()
|
||||
{
|
||||
AddEntityField("entityflags", Game::fieldtype_t::F_INT,
|
||||
AddEntityField("entityflags", Game::F_INT,
|
||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||
{
|
||||
ent->flags = Game::Scr_GetInt(0);
|
||||
@ -319,7 +319,7 @@ namespace Components
|
||||
|
||||
void ScriptExtension::AddClientFields()
|
||||
{
|
||||
AddClientField("clientflags", Game::fieldtype_t::F_INT,
|
||||
AddClientField("clientflags", Game::F_INT,
|
||||
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||
{
|
||||
pSelf->flags = Game::Scr_GetInt(0);
|
||||
|
@ -2,18 +2,18 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::unordered_map<std::int32_t, std::function<bool(Command::Params*)>> ServerCommands::Commands;
|
||||
std::unordered_map<std::int32_t, ServerCommands::serverCommandHandler> ServerCommands::Commands;
|
||||
|
||||
void ServerCommands::OnCommand(std::int32_t cmd, std::function<bool(Command::Params*)> callback)
|
||||
void ServerCommands::OnCommand(std::int32_t cmd, const serverCommandHandler& callback)
|
||||
{
|
||||
ServerCommands::Commands.insert_or_assign(cmd, std::move(callback));
|
||||
Commands.insert_or_assign(cmd, callback);
|
||||
}
|
||||
|
||||
bool ServerCommands::OnServerCommand()
|
||||
{
|
||||
Command::ClientParams params;
|
||||
|
||||
for (const auto& [id, callback] : ServerCommands::Commands)
|
||||
for (const auto& [id, callback] : Commands)
|
||||
{
|
||||
if (params.size() >= 1)
|
||||
{
|
||||
@ -33,7 +33,10 @@ namespace Components
|
||||
{
|
||||
push eax
|
||||
pushad
|
||||
|
||||
// Missing localClientNum!
|
||||
call ServerCommands::OnServerCommand
|
||||
|
||||
mov [esp + 20h], eax
|
||||
popad
|
||||
pop eax
|
||||
@ -63,7 +66,7 @@ namespace Components
|
||||
ServerCommands::ServerCommands()
|
||||
{
|
||||
// Server command receive hook
|
||||
Utils::Hook(0x59449F, ServerCommands::CG_DeployServerCommand_Stub).install()->quick();
|
||||
Utils::Hook(0x59449F, CG_DeployServerCommand_Stub).install()->quick();
|
||||
Utils::Hook::Nop(0x5944A4, 6);
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,11 @@ namespace Components
|
||||
public:
|
||||
ServerCommands();
|
||||
|
||||
static void OnCommand(std::int32_t cmd, std::function<bool(Command::Params*)> callback);
|
||||
using serverCommandHandler = std::function<bool(Command::Params*)>;
|
||||
static void OnCommand(std::int32_t cmd, const serverCommandHandler& callback);
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::int32_t, std::function<bool(Command::Params*)>> Commands;
|
||||
static std::unordered_map<std::int32_t, serverCommandHandler> Commands;
|
||||
|
||||
static bool OnServerCommand();
|
||||
static void CG_DeployServerCommand_Stub();
|
||||
|
@ -1,10 +1,11 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "GSC/Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
int64_t* Stats::GetStatsID()
|
||||
std::int64_t* Stats::GetStatsID()
|
||||
{
|
||||
static int64_t id = 0x110000100001337;
|
||||
static std::int64_t id = 0x110000100001337;
|
||||
return &id;
|
||||
}
|
||||
|
||||
@ -58,7 +59,7 @@ namespace Components
|
||||
|
||||
void Stats::UpdateClasses([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||
{
|
||||
Stats::SendStats();
|
||||
SendStats();
|
||||
}
|
||||
|
||||
int Stats::SaveStats(char* dest, const char* folder, const char* buffer, size_t length)
|
||||
@ -73,18 +74,64 @@ namespace Components
|
||||
return Utils::Hook::Call<int(char*, const char*, const char*, size_t)>(0x426450)(dest, folder, buffer, length);
|
||||
}
|
||||
|
||||
void Stats::AddScriptFunctions()
|
||||
{
|
||||
Script::AddMethod("GetStat", [](const Game::scr_entref_t entref)
|
||||
{
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto index = Game::Scr_GetInt(0);
|
||||
|
||||
if (index < 0 || index > 3499)
|
||||
{
|
||||
Game::Scr_ParamError(0, Utils::String::VA("GetStat: invalid index %i", index));
|
||||
}
|
||||
|
||||
if (ent->client->sess.connected <= Game::CON_DISCONNECTED)
|
||||
{
|
||||
Game::Scr_Error("GetStat: called on a disconnected player");
|
||||
}
|
||||
|
||||
Game::Scr_AddInt(Game::SV_GetClientStat(ent->s.number, index));
|
||||
});
|
||||
|
||||
Script::AddMethod("SetStat", [](const Game::scr_entref_t entref)
|
||||
{
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
|
||||
const auto iNumParms = Game::Scr_GetNumParam();
|
||||
if (iNumParms != 2)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("GetStat: takes 2 arguments, got %u.\n", iNumParms));
|
||||
}
|
||||
|
||||
const auto index = Game::Scr_GetInt(0);
|
||||
if (index < 0 || index > 3499)
|
||||
{
|
||||
Game::Scr_ParamError(0, Utils::String::VA("setstat: invalid index %i", index));
|
||||
}
|
||||
|
||||
const auto value = Game::Scr_GetInt(1);
|
||||
if (index < 2000 && (value < 0 || value > 255))
|
||||
{
|
||||
Game::Scr_ParamError(1, Utils::String::VA("setstat: index %i is a byte value, and you're trying to set it to %i", index, value));
|
||||
}
|
||||
|
||||
Game::SV_SetClientStat(ent->s.number, index, value);
|
||||
});
|
||||
}
|
||||
|
||||
Stats::Stats()
|
||||
{
|
||||
// This UIScript should be added in the onClose code of the cac_popup menu,
|
||||
// so everytime the create-a-class window is closed, and a client is connected
|
||||
// to a server, the stats data of the client will be reuploaded to the server.
|
||||
// allowing the player to change their classes while connected to a server.
|
||||
UIScript::Add("UpdateClasses", Stats::UpdateClasses);
|
||||
UIScript::Add("UpdateClasses", UpdateClasses);
|
||||
|
||||
// Allow playerdata to be changed while connected to a server
|
||||
Utils::Hook::Set<BYTE>(0x4376FD, 0xEB);
|
||||
|
||||
// ToDo: Allow playerdata changes in setPlayerData UI script.
|
||||
// TODO: Allow playerdata changes in setPlayerData UI script.
|
||||
|
||||
// Rename stat file
|
||||
Utils::Hook::SetString(0x71C048, "iw4x.stat");
|
||||
@ -92,8 +139,9 @@ namespace Components
|
||||
// Patch stats steamid
|
||||
Utils::Hook::Nop(0x682EBF, 20);
|
||||
Utils::Hook::Nop(0x6830B1, 20);
|
||||
Utils::Hook(0x682EBF, Stats::GetStatsID, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x6830B1, Stats::GetStatsID, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook(0x682EBF, GetStatsID, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x6830B1, GetStatsID, HOOK_CALL).install()->quick();
|
||||
//Utils::Hook::Set<BYTE>(0x68323A, 0xEB);
|
||||
|
||||
// Never use remote stat saving
|
||||
@ -103,6 +151,34 @@ namespace Components
|
||||
Utils::Hook::Nop(0x402CE6, 2);
|
||||
|
||||
// Write stats to mod folder if a mod is loaded
|
||||
Utils::Hook(0x682F7B, Stats::SaveStats, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x682F7B, SaveStats, HOOK_CALL).install()->quick();
|
||||
|
||||
AddScriptFunctions();
|
||||
|
||||
// Skip silly Com_Error (LiveStorage_SetStat)
|
||||
Utils::Hook::Set<BYTE>(0x4CC5F9, 0xEB);
|
||||
|
||||
// 'M' Seems to be used on Xbox only for parsing platform specific ranks
|
||||
ServerCommands::OnCommand('M', [](Command::Params* params)
|
||||
{
|
||||
const auto* arg1 = params->get(1);
|
||||
const auto* arg2 = params->get(2);
|
||||
|
||||
Game::LiveStorage_SetStat(Game::CL_ControllerIndexFromClientNum(0), std::atoi(arg1), std::atoi(arg2));
|
||||
return true;
|
||||
});
|
||||
|
||||
Command::Add("statGet", []([[maybe_unused]] Command::Params* params)
|
||||
{
|
||||
if (params->size() < 2)
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_SERVER, "statget usage: statget <index>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = std::atoi(params->get(1));
|
||||
const auto stat = Game::LiveStorage_GetStat(0, index);
|
||||
Logger::Print(Game::CON_CHANNEL_SYSTEM, "Stat {}: {}\n", index, stat);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,12 @@ namespace Components
|
||||
|
||||
private:
|
||||
static void UpdateClasses([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||
|
||||
static void SendStats();
|
||||
static int SaveStats(char* dest, const char* folder, const char* buffer, size_t length);
|
||||
|
||||
static int64_t* GetStatsID();
|
||||
static std::int64_t* GetStatsID();
|
||||
|
||||
static void AddScriptFunctions();
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user