Feature/set get stat (#485)
* [Script]: GetStat & SetStat from CoD4(x) * [Stats] Remove limitation * [Bullet] Clean this up
This commit is contained in:
parent
be1bc5fd10
commit
e49194ff65
@ -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();
|
||||
};
|
||||
}
|
||||
|
@ -138,6 +138,7 @@ namespace Game
|
||||
Live_GetLocalClientName_t Live_GetLocalClientName = Live_GetLocalClientName_t(0x441FC0);
|
||||
|
||||
LiveStorage_GetStat_t LiveStorage_GetStat = LiveStorage_GetStat_t(0x471F60);
|
||||
LiveStorage_SetStat_t LiveStorage_SetStat = LiveStorage_SetStat_t(0x4CC5D0);
|
||||
|
||||
Scr_AddSourceBuffer_t Scr_AddSourceBuffer = Scr_AddSourceBuffer_t(0x61ABC0);
|
||||
|
||||
|
@ -347,6 +347,9 @@ namespace Game
|
||||
typedef int(*LiveStorage_GetStat_t)(int controllerIndex, int index);
|
||||
extern LiveStorage_GetStat_t LiveStorage_GetStat;
|
||||
|
||||
typedef void(*LiveStorage_SetStat_t)(int controllerIndex, int index, int value);
|
||||
extern LiveStorage_SetStat_t LiveStorage_SetStat;
|
||||
|
||||
typedef char*(*Scr_AddSourceBuffer_t)(const char* filename, const char* extFilename, const char* codePos, bool archive);
|
||||
extern Scr_AddSourceBuffer_t Scr_AddSourceBuffer;
|
||||
|
||||
|
@ -6,6 +6,7 @@ namespace Game
|
||||
SV_IsTestClient_t SV_IsTestClient = SV_IsTestClient_t(0x4D6E40);
|
||||
SV_GameClientNum_Score_t SV_GameClientNum_Score = SV_GameClientNum_Score_t(0x469AC0);
|
||||
SV_GameSendServerCommand_t SV_GameSendServerCommand = SV_GameSendServerCommand_t(0x4BC3A0);
|
||||
SV_SendServerCommand_t SV_SendServerCommand = SV_SendServerCommand_t(0x4255A0);
|
||||
SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString = SV_Cmd_TokenizeString_t(0x4B5780);
|
||||
SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString = SV_Cmd_EndTokenizedString_t(0x464750);
|
||||
SV_Cmd_ArgvBuffer_t SV_Cmd_ArgvBuffer = SV_Cmd_ArgvBuffer_t(0x40BB60);
|
||||
@ -52,6 +53,60 @@ namespace Game
|
||||
}
|
||||
}
|
||||
|
||||
int SV_GetClientStat(int clientNum, int index)
|
||||
{
|
||||
assert(svs_clients[clientNum].statPacketsReceived == (1 << MAX_STATPACKETS) - 1);
|
||||
assert(svs_clients[clientNum].header.state >= CS_CONNECTED);
|
||||
|
||||
if (index < 2000)
|
||||
{
|
||||
// Skip first 4 bytes of the binary blob
|
||||
return svs_clients[clientNum].stats.__s0.binary[index];
|
||||
}
|
||||
|
||||
if (index < 3498)
|
||||
{
|
||||
return svs_clients[clientNum].stats.__s0.data[index - 2000];
|
||||
}
|
||||
|
||||
assert(0 && "Unhandled stat index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SV_SetClientStat(int clientNum, int index, int value)
|
||||
{
|
||||
assert(svs_clients[clientNum].statPacketsReceived == (1 << MAX_STATPACKETS) - 1);
|
||||
assert(svs_clients[clientNum].header.state >= CS_CONNECTED);
|
||||
|
||||
if (index < 2000)
|
||||
{
|
||||
assert(value >= 0 && value <= 255);
|
||||
|
||||
if (svs_clients[clientNum].stats.__s0.binary[index] == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
svs_clients[clientNum].stats.__s0.binary[index] = static_cast<unsigned char>(value);
|
||||
}
|
||||
else if (index < 3498)
|
||||
{
|
||||
if (svs_clients[clientNum].stats.__s0.data[index - 2000] == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
svs_clients[clientNum].stats.__s0.data[index - 2000] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0 && "Unhandled stat index");
|
||||
return;
|
||||
}
|
||||
|
||||
SV_SendServerCommand(&svs_clients[clientNum], SV_CMD_RELIABLE, "%c %i %i", 'M', index, value);
|
||||
}
|
||||
|
||||
void SV_BotUserMove(client_t* client)
|
||||
{
|
||||
static DWORD SV_BotUserMove_t = 0x626E50;
|
||||
|
@ -14,6 +14,9 @@ namespace Game
|
||||
typedef void(*SV_GameSendServerCommand_t)(int clientNum, svscmd_type type, const char* text);
|
||||
extern SV_GameSendServerCommand_t SV_GameSendServerCommand;
|
||||
|
||||
typedef void(*SV_SendServerCommand_t)(client_t* cl, svscmd_type type, const char* fmt, ...);
|
||||
extern SV_SendServerCommand_t SV_SendServerCommand;
|
||||
|
||||
typedef void(*SV_Cmd_TokenizeString_t)(const char* string);
|
||||
extern SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString;
|
||||
|
||||
@ -47,6 +50,8 @@ namespace Game
|
||||
typedef client_t* (*SV_FindClientByAddress_t)(netadr_t from, int qport, int remoteClientIndex);
|
||||
extern SV_FindClientByAddress_t SV_FindClientByAddress;
|
||||
|
||||
constexpr auto MAX_STATPACKETS = 7;
|
||||
|
||||
extern int* svs_time;
|
||||
extern int* sv_serverId_value;
|
||||
extern int* svs_clientCount;
|
||||
@ -57,5 +62,7 @@ namespace Game
|
||||
extern int SV_GetServerThreadOwnsGame();
|
||||
extern void SV_GameDropClient(int clientNum, const char* reason);
|
||||
extern void SV_DropAllBots();
|
||||
extern int SV_GetClientStat(int clientNum, int index);
|
||||
extern void SV_SetClientStat(int clientNum, int index, int value);
|
||||
extern void SV_BotUserMove(client_t* client);
|
||||
}
|
||||
|
@ -6023,12 +6023,6 @@ namespace Game
|
||||
float keys[1];
|
||||
};
|
||||
|
||||
union $81775853B5F1E1C6748A82ED93FC367C
|
||||
{
|
||||
FxElemVisuals visuals[32];
|
||||
FxElemMarkVisuals markVisuals[16];
|
||||
};
|
||||
|
||||
struct FxEditorTrailDef
|
||||
{
|
||||
FxTrailVertex verts[64];
|
||||
@ -6081,7 +6075,11 @@ namespace Game
|
||||
FxFloatRange emitDistVariance;
|
||||
char elemType;
|
||||
int visualCount;
|
||||
$81775853B5F1E1C6748A82ED93FC367C ___u42;
|
||||
union
|
||||
{
|
||||
FxElemVisuals visuals[32];
|
||||
FxElemMarkVisuals markVisuals[16];
|
||||
} ___u42;
|
||||
int trailSplitDist;
|
||||
int trailSplitArcDist;
|
||||
int trailSplitTime;
|
||||
@ -6583,7 +6581,15 @@ namespace Game
|
||||
int bIsTestClient; // 269040
|
||||
int serverID; // 269044
|
||||
bool usingOnlineStatsOffline;
|
||||
char stats[8192];
|
||||
struct
|
||||
{
|
||||
unsigned int checksum;
|
||||
struct
|
||||
{
|
||||
unsigned char binary[2000];
|
||||
int data[1547];
|
||||
} __s0;
|
||||
} stats;
|
||||
char statsModifiedFlags[1024];
|
||||
bool statsModified;
|
||||
char statPacketsReceived;
|
||||
|
Loading…
Reference in New Issue
Block a user