remake - v3

This commit is contained in:
2026-08-10 14:22:23 +02:00
parent b0e8588bef
commit aed1ea6053

View File

@@ -3,7 +3,6 @@
#include "common.h" #include "common.h"
#include "detour.h" #include "detour.h"
#include "gameconfig.h" #include "gameconfig.h"
#include "icvar.h"
#include "iserver.h" #include "iserver.h"
#include "module.h" #include "module.h"
#include "overrides.h" #include "overrides.h"
@@ -27,43 +26,6 @@ CGameEntitySystem* g_entitySystem = nullptr;
bool g_enabled = true; bool g_enabled = true;
bool g_armed = false; bool g_armed = false;
static int ForceFullUpdateForAllClients()
{
if (!g_networkServerService || !g_gameConfig)
return 0;
CNetworkGameServerBase* server = g_networkServerService->GetIGameServer();
if (!server)
return 0;
int listOffset = g_gameConfig->GetOffset("CNetworkGameServer_ClientList");
int deltaTickOffset = g_gameConfig->GetOffset("CServerSideClient_DeltaTick");
if (listOffset < 0 || deltaTickOffset < 0)
return 0;
auto* list = reinterpret_cast<uint8_t*>(server) + listOffset;
int count = *reinterpret_cast<int*>(list);
if (count <= 0 || count > 256)
return 0;
auto** clients = *reinterpret_cast<void***>(list + 8);
if (!clients)
return 0;
int forced = 0;
for (int i = 0; i < count; ++i)
{
void* client = clients[i];
if (!client)
continue;
auto* deltaTick = reinterpret_cast<int*>(reinterpret_cast<uint8_t*>(client) + deltaTickOffset);
*deltaTick = -1;
++forced;
}
return forced;
}
void SPMessage(const char* msg, ...) void SPMessage(const char* msg, ...)
{ {
va_list args; va_list args;
@@ -97,154 +59,6 @@ CGameEntitySystem* GameEntitySystem()
return g_entitySystem; return g_entitySystem;
} }
CON_COMMAND_F(sp_enable, "Enable SendProxy overrides", FCVAR_NONE)
{
if (args.ArgC() >= 2)
g_enabled = atoi(args[1]) != 0;
SPMessage("enabled=%d\n", g_enabled ? 1 : 0);
}
CON_COMMAND_F(sp_arm, "Arm SendProxy override application after the map is fully loaded", FCVAR_NONE)
{
if (args.ArgC() >= 2)
g_armed = atoi(args[1]) != 0;
SPMessage("armed=%d\n", g_armed ? 1 : 0);
}
CON_COMMAND_F(sp_trace, "Enable SendProxy tracing", FCVAR_NONE)
{
if (args.ArgC() >= 2)
g_overrides.SetTrace(atoi(args[1]) != 0);
SPMessage("trace=%d\n", g_overrides.Trace() ? 1 : 0);
}
CON_COMMAND_F(sp_dump, "Dump SendProxy state", FCVAR_NONE)
{
SPMessage("enabled=%d armed=%d entitySystem=%p\n", g_enabled ? 1 : 0, g_armed ? 1 : 0, GameEntitySystem());
DumpDetourStats();
g_overrides.Dump();
}
CON_COMMAND_F(sp_fullupdate, "Force full snapshot update for all connected clients", FCVAR_NONE)
{
int forced = ForceFullUpdateForAllClients();
RequestSnapshotFullUpdates();
SPMessage("forced full update for %d clients; queued snapshot fallback\n", forced);
}
CON_COMMAND_F(sp_entities, "sp_entities [substring] [limit]", FCVAR_NONE)
{
CGameEntitySystem* entitySystem = GameEntitySystem();
if (!entitySystem)
{
SPWarning("no entity system\n");
return;
}
const char* filter = args.ArgC() >= 2 ? args[1] : "";
int limit = args.ArgC() >= 3 ? atoi(args[2]) : 80;
if (limit <= 0)
limit = 80;
auto healthField = schema::FindField("CBaseEntity", "m_iHealth");
int printed = 0;
int visited = 0;
for (auto* identity = entitySystem->m_EntityList.m_pFirstActiveEntity; identity && visited < 16384; identity = identity->m_pNext, ++visited)
{
const char* designer = identity->m_designerName.String();
const char* classDesigner = identity->m_pClass ? identity->m_pClass->m_designerName.String() : "";
if (filter && *filter && !V_stristr(designer ? designer : "", filter) && !V_stristr(classDesigner ? classDesigner : "", filter))
continue;
int health = -999999;
if (identity->m_pInstance && healthField.found)
health = *reinterpret_cast<int*>(reinterpret_cast<uint8_t*>(identity->m_pInstance) + healthField.offset);
SPMessage("entity idx=%d inst=%p designer=%s class=%s health=%d\n",
identity->m_EHandle.GetEntryIndex(),
identity->m_pInstance,
designer ? designer : "",
classDesigner ? classDesigner : "",
health);
if (++printed >= limit)
break;
}
SPMessage("entities printed=%d visited=%d filter=%s\n", printed, visited, filter ? filter : "");
}
CON_COMMAND_F(sp_clear, "Clear SendProxy rules", FCVAR_NONE)
{
g_overrides.Clear();
SPMessage("rules cleared\n");
}
CON_COMMAND_F(sp_set_exact, "sp_set_exact <recipient|all> <entity_index> <ClassName> <field.path> <type> <value>", FCVAR_NONE)
{
if (args.ArgC() < 7)
{
SPWarning("usage: sp_set_exact <recipient|all> <entity_index> <ClassName> <field.path> <type> <value>\n");
return;
}
int recipientSlot = !V_stricmp(args[1], "all") ? -1 : atoi(args[1]);
int entityIndex = atoi(args[2]);
std::string error;
int id = g_overrides.AddCompiledRule(recipientSlot, entityIndex, args[3], args[4], args[5], args[6], error);
if (!id)
{
SPWarning("%s\n", error.c_str());
return;
}
g_enabled = true;
g_armed = true;
SPMessage("enabled=1 armed=1 rule_id=%d\n", id);
}
CON_COMMAND_F(sp_unset, "sp_unset <rule_id>", FCVAR_NONE)
{
if (args.ArgC() < 2)
{
SPWarning("usage: sp_unset <rule_id>\n");
return;
}
int id = atoi(args[1]);
if (!g_overrides.RemoveRule(id))
{
SPWarning("rule %d not found\n", id);
return;
}
SPMessage("rule %d removed\n", id);
}
CON_COMMAND_F(sp_dirty, "sp_dirty <entity_index> <ClassName> <field.path>", FCVAR_NONE)
{
if (args.ArgC() < 4)
{
SPWarning("usage: sp_dirty <entity_index> <ClassName> <field.path>\n");
return;
}
std::string error;
if (!g_overrides.MarkEntityFieldDirty(atoi(args[1]), args[2], args[3], error))
{
SPWarning("%s\n", error.empty() ? "failed to mark field dirty" : error.c_str());
return;
}
SPMessage("marked entity %d %s::%s dirty\n", atoi(args[1]), args[2], args[3]);
}
CON_COMMAND_F(sp_set, "sp_set <recipient|all> <entity|bot|all> <ClassName> <field> <type> <value>", FCVAR_NONE)
{
const char* argv[8] {};
for (int i = 0; i < args.ArgC() && i < 8; ++i)
argv[i] = args[i];
std::string error;
if (!g_overrides.AddFromTokens(args.ArgC(), argv, error))
SPWarning("%s\n", error.c_str());
}
extern "C" __attribute__((visibility("default"))) int SendProxy_SetOverride(int recipientSlot, int entityIndex, const char* className, const char* fieldPath, const char* type, const char* value) extern "C" __attribute__((visibility("default"))) int SendProxy_SetOverride(int recipientSlot, int entityIndex, const char* className, const char* fieldPath, const char* type, const char* value)
{ {
std::string error; std::string error;
@@ -282,159 +96,18 @@ extern "C" __attribute__((visibility("default"))) bool SendProxy_MarkDirty(int e
return true; return true;
} }
CON_COMMAND_F(sp_weapon_test, "sp_weapon_test <weapon_entity_substring|all> <item_definition_index>", FCVAR_NONE)
{
if (args.ArgC() < 3)
{
SPWarning("usage: sp_weapon_test <weapon_entity_substring|all> <item_definition_index>\n");
return;
}
std::string error;
if (!g_overrides.AddWeaponItemDefRule(-1, args[1], static_cast<uint16_t>(atoi(args[2])), error))
SPWarning("%s\n", error.c_str());
}
static void SetHealthMoneyCommand(const CCommand& args)
{
if (args.ArgC() < 2)
{
SPWarning("usage: sp_health_money <0|1>\n");
return;
}
bool enable = atoi(args[1]) != 0;
std::string error;
if (!g_overrides.SetHealthMoneyTest(enable, error))
{
SPWarning("%s\n", error.c_str());
return;
}
if (enable)
{
g_enabled = true;
g_armed = true;
SPMessage("enabled=1 armed=1\n");
SPMessage("marked health dirty for %d players\n", g_overrides.MarkPlayerHealthDirty());
}
else if (g_overrides.Empty())
{
g_armed = false;
SPMessage("armed=0\n");
SPMessage("marked health dirty for %d players\n", g_overrides.MarkPlayerHealthDirty());
}
}
CON_COMMAND_F(sp_health_money, "sp_health_money <0|1>", FCVAR_NONE)
{
SetHealthMoneyCommand(args);
}
CON_COMMAND_F(sp_health_money_test, "sp_health_money_test <0|1>", FCVAR_NONE)
{
SetHealthMoneyCommand(args);
}
CON_COMMAND_F(sp_health_42, "sp_health_42 <0|1>", FCVAR_NONE)
{
if (args.ArgC() < 2)
{
SPWarning("usage: sp_health_42 <0|1>\n");
return;
}
bool enable = atoi(args[1]) != 0;
std::string error;
if (!g_overrides.SetHealth42Test(enable, error))
{
SPWarning("%s\n", error.c_str());
return;
}
if (enable)
{
g_enabled = true;
g_armed = true;
SPMessage("enabled=1 armed=1\n");
SPMessage("marked health dirty for %d players\n", g_overrides.MarkPlayerHealthDirty());
}
else if (g_overrides.Empty())
{
g_armed = false;
SPMessage("armed=0\n");
SPMessage("marked health dirty for %d players\n", g_overrides.MarkPlayerHealthDirty());
}
}
CON_COMMAND_F(sp_health_recipient, "sp_health_recipient <slot|all|clear> [value]", FCVAR_NONE)
{
if (args.ArgC() < 2)
{
SPWarning("usage: sp_health_recipient <slot|all|clear> [value]\n");
return;
}
if (!V_stricmp(args[1], "clear"))
{
g_overrides.ClearRecipientHealthValues();
SPMessage("marked health dirty for %d players\n", g_overrides.MarkPlayerHealthDirty());
return;
}
if (args.ArgC() < 3)
{
SPWarning("usage: sp_health_recipient <slot|all|clear> [value]\n");
return;
}
int recipientSlot = !V_stricmp(args[1], "all") ? -1 : atoi(args[1]);
int value = atoi(args[2]);
std::string error;
if (!g_overrides.SetRecipientHealthValue(recipientSlot, value, error))
{
SPWarning("%s\n", error.c_str());
return;
}
if (!g_overrides.Health42Test())
{
if (!g_overrides.SetHealth42Test(true, error))
{
SPWarning("%s\n", error.c_str());
return;
}
}
g_enabled = true;
g_armed = true;
SPMessage("enabled=1 armed=1\n");
SPMessage("marked health dirty for %d players\n", g_overrides.MarkPlayerHealthDirty());
}
CON_COMMAND_F(sp_schema_fields, "sp_schema_fields <ClassName> [substring]", FCVAR_NONE)
{
if (args.ArgC() < 2)
{
SPWarning("usage: sp_schema_fields <ClassName> [substring]\n");
return;
}
schema::DumpFields(args[1], args.ArgC() >= 3 ? args[2] : "");
}
PLUGIN_EXPOSE(SendProxyPlugin, g_SendProxy); PLUGIN_EXPOSE(SendProxyPlugin, g_SendProxy);
bool SendProxyPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool late) bool SendProxyPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool late)
{ {
PLUGIN_SAVEVARS(); PLUGIN_SAVEVARS();
ICvar* icvar = nullptr;
GET_V_IFACE_CURRENT(GetEngineFactory, g_engine, IVEngineServer2, SOURCE2ENGINETOSERVER_INTERFACE_VERSION); GET_V_IFACE_CURRENT(GetEngineFactory, g_engine, IVEngineServer2, SOURCE2ENGINETOSERVER_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetEngineFactory, g_networkServerService, INetworkServerService, NETWORKSERVERSERVICE_INTERFACE_VERSION); GET_V_IFACE_ANY(GetEngineFactory, g_networkServerService, INetworkServerService, NETWORKSERVERSERVICE_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetServerFactory, g_gameClients, IServerGameClients, SOURCE2GAMECLIENTS_INTERFACE_VERSION); GET_V_IFACE_ANY(GetServerFactory, g_gameClients, IServerGameClients, SOURCE2GAMECLIENTS_INTERFACE_VERSION);
GET_V_IFACE_CURRENT(GetEngineFactory, g_schemaSystem, ISchemaSystem, SCHEMASYSTEM_INTERFACE_VERSION); GET_V_IFACE_CURRENT(GetEngineFactory, g_schemaSystem, ISchemaSystem, SCHEMASYSTEM_INTERFACE_VERSION);
GET_V_IFACE_CURRENT(GetEngineFactory, g_gameResourceService, IGameResourceService, GAMERESOURCESERVICESERVER_INTERFACE_VERSION); GET_V_IFACE_CURRENT(GetEngineFactory, g_gameResourceService, IGameResourceService, GAMERESOURCESERVICESERVER_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetFileSystemFactory, g_fileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION); GET_V_IFACE_ANY(GetFileSystemFactory, g_fileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION);
GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION);
modules::engine = new CModule(ROOTBIN, "engine2"); modules::engine = new CModule(ROOTBIN, "engine2");
modules::server = new CModule(GAMEBIN, "server"); modules::server = new CModule(GAMEBIN, "server");
@@ -459,10 +132,7 @@ bool SendProxyPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxle
} }
g_SMAPI->AddListener(this, this); g_SMAPI->AddListener(this, this);
g_pCVar = icvar;
META_CONVAR_REGISTER(FCVAR_RELEASE | FCVAR_CLIENT_CAN_EXECUTE | FCVAR_GAMEDLL);
g_engine->ServerCommand("execifexists addons/sendproxy/configs/sendproxy.cfg\n");
SPMessage("loaded\n"); SPMessage("loaded\n");
return true; return true;
} }
@@ -470,7 +140,6 @@ bool SendProxyPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxle
bool SendProxyPlugin::Unload(char*, size_t) bool SendProxyPlugin::Unload(char*, size_t)
{ {
ShutdownSnapshotDetour(); ShutdownSnapshotDetour();
ConVar_Unregister();
delete g_gameConfig; delete g_gameConfig;
delete modules::engine; delete modules::engine;
delete modules::server; delete modules::server;