clean up debugging outputs

This commit is contained in:
2026-08-10 17:02:48 +02:00
parent 9a23169b6b
commit 1e348c5abb
5 changed files with 27 additions and 151 deletions

View File

@@ -97,19 +97,23 @@ It demonstrates:
- Coloring dropped AK-47s red for Terrorist viewers.
- Coloring dropped M4A1-S blue for CT viewers.
Concealed-carry handle spoof:
Concealed-carry weapon-list spoof:
```text
examples/CounterStrikeSharp/SendProxyConcealedCarry/
```
It hides owned primary weapons, secondary weapons, and grenades from every recipient except the owning player by spoofing weapon-related handles to `0xffffffff`:
It hides a player's non-active carried weapons from every recipient except the owning player by spoofing the player/weapon association:
- `CBasePlayerPawn::m_pWeaponServices.m_hActiveWeapon`
- `CBasePlayerPawn::m_pWeaponServices.m_hLastWeapon`
- `CBaseEntity::m_hOwnerEntity` on carried weapon entities
- `CBasePlayerPawn::m_pWeaponServices.m_hMyWeapons`
- source handle: `CBasePlayerPawn::m_pWeaponServices.m_hActiveWeapon`
- `CBaseEntity::m_hOwnerEntity = INVALID_EHANDLE` on non-active carried weapon entities
- `CBaseEntity::m_CBodyComponent.m_pSceneNode.m_hParent = INVALID_EHANDLE`
- `CBaseEntity::m_CBodyComponent.m_pSceneNode.m_hierarchyAttachName = 0`
- `CCSWeaponBase::m_hPrevOwner = INVALID_EHANDLE`
- `CBaseCSGrenade::m_bIsHeldByPlayer = false` for non-active grenades
It intentionally does not set `EF_NODRAW` and does not remove or drop the real weapons.
This corresponds to CounterStrikeSharp's `player.PlayerPawn.Value.WeaponServices?.MyWeapons`, plus the weapon entity relationship fields that attach carried weapons to the owning pawn. During packing for non-owner recipients, native code temporarily masks non-active `m_hMyWeapons` elements to `INVALID_EHANDLE`; the CSS sample also spoofs non-active carried weapon entities as detached/unowned. It intentionally does not set `EF_NODRAW`, use render mode tricks, or remove/drop the real weapons.
This sample periodically reconciles desired rules and diffs them against active rule IDs. For production, prefer event-driven updates on player connect/disconnect/team changes/spawn/death and weapon pickup/drop/create/delete.

View File

@@ -10,6 +10,11 @@ public sealed class SendProxyConcealedCarryPlugin : BasePlugin
{
private const string WeaponListField = "m_pWeaponServices.m_hMyWeapons";
private const string ActiveWeaponField = "m_pWeaponServices.m_hActiveWeapon";
private const string EntityClass = "CBaseEntity";
private static readonly byte[] InvalidHandle = UInt32Bytes(0xffffffff);
private static readonly byte[] EmptyToken = UInt32Bytes(0);
private static readonly byte[] NoAttachment = Int16Bytes(-1);
private static readonly byte[] FalseByte = [0];
private readonly Dictionary<RuleKey, int> _activeRules = new();
private SendProxyNative? _sendProxy;
@@ -110,36 +115,8 @@ public sealed class SendProxyConcealedCarryPlugin : BasePlugin
if (viewer.Slot == weapon.OwnerSlot)
continue;
desired[new RuleKey(
ToEngineSlot(viewer),
weapon.EntityIndex,
"CBaseEntity",
"m_hOwnerEntity")] = UInt32Bytes(0xffffffff);
desired[new RuleKey(
ToEngineSlot(viewer),
weapon.EntityIndex,
"CBaseEntity",
"m_CBodyComponent.m_pSceneNode.m_hParent.m_hOwner")] = UInt32Bytes(0xffffffff);
desired[new RuleKey(
ToEngineSlot(viewer),
weapon.EntityIndex,
"CBaseEntity",
"m_CBodyComponent.m_pSceneNode.m_hParent.m_name")] = UInt32Bytes(0);
desired[new RuleKey(
ToEngineSlot(viewer),
weapon.EntityIndex,
"CBaseEntity",
"m_CBodyComponent.m_pSceneNode.m_nParentAttachmentOrBone")] = Int16Bytes(-1);
desired[new RuleKey(
ToEngineSlot(viewer),
weapon.EntityIndex,
"CBaseEntity",
"m_CBodyComponent.m_pSceneNode.m_hierarchyAttachName")] = UInt32Bytes(0);
desired[new RuleKey(
ToEngineSlot(viewer),
weapon.EntityIndex,
"CBaseEntity",
"m_CBodyComponent.m_pSceneNode.m_bForceParentToBeNetworked")] = new byte[] { 0 };
int recipient = ToEngineSlot(viewer);
AddAssociationDisconnectRules(desired, recipient, weapon.EntityIndex);
}
}
@@ -206,13 +183,6 @@ public sealed class SendProxyConcealedCarryPlugin : BasePlugin
}
}
private static byte[] UInt16Bytes(ushort value)
{
byte[] bytes = new byte[2];
BitConverter.TryWriteBytes(bytes, value);
return bytes;
}
private static byte[] Int16Bytes(short value)
{
byte[] bytes = new byte[2];
@@ -227,6 +197,16 @@ public sealed class SendProxyConcealedCarryPlugin : BasePlugin
return bytes;
}
private static void AddAssociationDisconnectRules(Dictionary<RuleKey, byte[]> desired, int recipientSlot, int weaponEntityIndex)
{
desired[new RuleKey(recipientSlot, weaponEntityIndex, EntityClass, "m_hOwnerEntity")] = InvalidHandle;
desired[new RuleKey(recipientSlot, weaponEntityIndex, EntityClass, "m_CBodyComponent.m_pSceneNode.m_hParent.m_hOwner")] = InvalidHandle;
desired[new RuleKey(recipientSlot, weaponEntityIndex, EntityClass, "m_CBodyComponent.m_pSceneNode.m_hParent.m_name")] = EmptyToken;
desired[new RuleKey(recipientSlot, weaponEntityIndex, EntityClass, "m_CBodyComponent.m_pSceneNode.m_nParentAttachmentOrBone")] = NoAttachment;
desired[new RuleKey(recipientSlot, weaponEntityIndex, EntityClass, "m_CBodyComponent.m_pSceneNode.m_hierarchyAttachName")] = EmptyToken;
desired[new RuleKey(recipientSlot, weaponEntityIndex, EntityClass, "m_CBodyComponent.m_pSceneNode.m_bForceParentToBeNetworked")] = FalseByte;
}
private static int ToEngineSlot(CCSPlayerController player)
{
return Math.Max(0, player.Slot - 1);

View File

@@ -26,8 +26,6 @@ static uint64_t g_packCalls = 0;
static uint64_t g_packEntityCalls = 0;
static uint64_t g_packEntityGenericSpoofs = 0;
static uint64_t g_fullUpdateWrites = 0;
static uint64_t g_packEntitiesDebugLogs = 0;
static uint64_t g_packEntityRuleDebugLogs = 0;
static int g_fullUpdateBudget = 0;
static thread_local CPlayerSlot g_currentRecipient(-1);
static thread_local CPlayerSlot g_lastSnapshotRecipient(-1);
@@ -79,20 +77,6 @@ static void Detour_PackEntities(void* self, void* arg1, int arg2, void* arg3, vo
{
++g_packCalls;
CPlayerSlot recipient((arg2 >= 0 && arg2 < 64) ? arg2 : -1);
if (g_packEntitiesDebugLogs < 16)
{
SPMessage("pack_entities call %llu self=%p arg1=%p arg2=%d recipient=%d args=[%p %p %p %p]\n",
static_cast<unsigned long long>(g_packCalls),
self,
arg1,
arg2,
recipient.Get(),
arg3,
arg4,
arg5,
arg6);
++g_packEntitiesDebugLogs;
}
ScopedRecipient scopedRecipient(recipient);
g_packEntities(self, arg1, arg2, arg3, arg4, arg5, arg6);
}
@@ -110,24 +94,6 @@ static void Detour_PackEntity(void* self, void* arg1, int entityIndex, void* ent
CPlayerSlot recipient = g_currentRecipient;
if (recipient.Get() < 0 && g_lastSnapshotRecipient.Get() >= 0)
recipient = g_lastSnapshotRecipient;
if (g_packEntityRuleDebugLogs < 64 && g_overrides.HasRulesForEntity(entityIndex))
{
SPMessage("pack_entity ruled call %llu entity=%d current=%d lastSnapshot=%d args self=%p arg1=%p data=%p arg4=%p arg5=%p arg6=%p arg7=%p arg8=%p arg9=%p\n",
static_cast<unsigned long long>(g_packEntityCalls),
entityIndex,
g_currentRecipient.Get(),
g_lastSnapshotRecipient.Get(),
self,
arg1,
entityData,
arg4,
arg5,
arg6,
arg7,
arg8,
arg9);
++g_packEntityRuleDebugLogs;
}
auto applied = g_overrides.ApplyForPackedEntity(recipient, entityIndex, entityData);
g_packEntityGenericSpoofs += applied.size();
g_packEntity(self, arg1, entityIndex, entityData, arg4, arg5, arg6, arg7, arg8, arg9);

View File

@@ -214,29 +214,6 @@ static void ApplyVectorFirstFromField(const CompiledOverrideRule& rule, void* en
constexpr uint32_t invalidHandle = 0xffffffff;
const int32_t count = *reinterpret_cast<int32_t*>(vectorAddress);
auto* elements = *reinterpret_cast<uint8_t**>(vectorAddress + 8);
if (!rule.debugLogged)
{
uint32_t h0 = elements && count > 0 ? *reinterpret_cast<uint32_t*>(elements) : invalidHandle;
uint32_t h1 = elements && count > 1 ? *reinterpret_cast<uint32_t*>(elements + 4) : invalidHandle;
uint32_t h2 = elements && count > 2 ? *reinterpret_cast<uint32_t*>(elements + 8) : invalidHandle;
const auto* raw = reinterpret_cast<const uint8_t*>(vectorAddress);
SPMessage("vector apply rule %d recipient=%d entity=%d vec=%p src=%p count=%d elems=%p active=%08x first=[%08x %08x %08x] raw=%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
rule.id,
rule.recipientSlot,
rule.entityIndex,
vectorAddress,
sourceAddress,
count,
elements,
sourceHandle,
h0,
h1,
h2,
raw[0], raw[1], raw[2], raw[3], raw[4], raw[5], raw[6], raw[7],
raw[8], raw[9], raw[10], raw[11], raw[12], raw[13], raw[14], raw[15],
raw[16], raw[17], raw[18], raw[19], raw[20], raw[21], raw[22], raw[23]);
rule.debugLogged = true;
}
if (count < 0 || count > 128)
return;
@@ -323,19 +300,6 @@ int OverrideManager::AddRuleBytes(int recipientSlot, int entityIndex, const char
auto* bytes = static_cast<const uint8_t*>(value);
rule.value.assign(bytes, bytes + valueSize);
m_rules.push_back(rule);
if (rule.fieldPath.find("m_hMyWeapons") != std::string::npos)
{
SPMessage("rule %d recipient=%d entity=%d %s::%s size=%d valueSize=%d topOffset=%d steps=%zu\n",
rule.id,
rule.recipientSlot,
rule.entityIndex,
rule.className.c_str(),
rule.fieldPath.c_str(),
rule.size,
valueSize,
rule.offset,
rule.addressSteps.size());
}
RebuildIndex();
MarkEntityFullDirty(entityIndex);
return rule.id;
@@ -375,18 +339,6 @@ int OverrideManager::AddRuleVectorFirstFromField(int recipientSlot, int entityIn
}
m_rules.push_back(rule);
SPMessage("vector-first rule %d recipient=%d entity=%d %s::%s <- %s vectorSize=%d sourceSize=%d topOffset=%d steps=%zu sourceSteps=%zu\n",
rule.id,
rule.recipientSlot,
rule.entityIndex,
rule.className.c_str(),
rule.fieldPath.c_str(),
rule.sourceFieldPath.c_str(),
rule.size,
rule.sourceSize,
rule.offset,
rule.addressSteps.size(),
rule.sourceAddressSteps.size());
RebuildIndex();
MarkEntityFullDirty(entityIndex);
return rule.id;
@@ -443,18 +395,7 @@ std::vector<AppliedOverride> OverrideManager::ApplyForPackedEntity(CPlayerSlot r
}
if ((effectiveRecipient < 0 || effectiveRecipient == 0 || effectiveRecipient == 1) && uniqueRuleRecipient >= 0)
{
static int fallbackLogs = 0;
if (fallbackLogs < 64)
{
SPMessage("entity=%d unresolved recipient current=%d using unique rule recipient=%d\n",
entityIndex,
effectiveRecipient,
uniqueRuleRecipient);
++fallbackLogs;
}
effectiveRecipient = uniqueRuleRecipient;
}
for (size_t ruleIndex : found->second)
{
@@ -463,20 +404,7 @@ std::vector<AppliedOverride> OverrideManager::ApplyForPackedEntity(CPlayerSlot r
continue;
if (rule.recipientSlot != -1 && rule.recipientSlot != effectiveRecipient)
{
if (!rule.recipientMismatchLogged)
{
SPMessage("rule %d entity=%d matched packed entity but skipped recipient rule=%d current=%d field=%s::%s\n",
rule.id,
entityIndex,
rule.recipientSlot,
effectiveRecipient,
rule.className.c_str(),
rule.fieldPath.c_str());
rule.recipientMismatchLogged = true;
}
continue;
}
if (rule.kind == OverrideRuleKind::VectorFirstFromField)
{

View File

@@ -41,8 +41,6 @@ struct CompiledOverrideRule
std::vector<uint8_t> value;
OverrideRuleKind kind {OverrideRuleKind::Bytes};
bool enabled {true};
mutable bool debugLogged {false};
mutable bool recipientMismatchLogged {false};
};
class OverrideManager