working weapon concealment
This commit is contained in:
@@ -53,6 +53,34 @@ static std::vector<std::string> SplitString(const char* value, char separator)
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::string NormalizeSchemaClassName(std::string typeName)
|
||||
{
|
||||
while (!typeName.empty() && (typeName.back() == '*' || typeName.back() == '&' || typeName.back() == ' '))
|
||||
typeName.pop_back();
|
||||
while (!typeName.empty() && typeName.front() == ' ')
|
||||
typeName.erase(typeName.begin());
|
||||
|
||||
constexpr const char* classPrefix = "class ";
|
||||
constexpr const char* structPrefix = "struct ";
|
||||
if (typeName.rfind(classPrefix, 0) == 0)
|
||||
typeName.erase(0, strlen(classPrefix));
|
||||
else if (typeName.rfind(structPrefix, 0) == 0)
|
||||
typeName.erase(0, strlen(structPrefix));
|
||||
|
||||
return typeName;
|
||||
}
|
||||
|
||||
static bool IsPointerSchemaType(const std::string& typeName)
|
||||
{
|
||||
for (auto it = typeName.rbegin(); it != typeName.rend(); ++it)
|
||||
{
|
||||
if (*it == ' ')
|
||||
continue;
|
||||
return *it == '*';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void OverrideManager::Clear()
|
||||
{
|
||||
for (const auto& rule : m_rules)
|
||||
@@ -75,7 +103,7 @@ void OverrideManager::Compact()
|
||||
RebuildIndex();
|
||||
}
|
||||
|
||||
bool OverrideManager::ResolveFieldPath(const char* className, const char* fieldPath, int32_t& offset, int32_t& size, std::string& error) const
|
||||
bool OverrideManager::ResolveFieldPath(const char* className, const char* fieldPath, int32_t& offset, int32_t& size, std::vector<FieldAddressStep>& addressSteps, std::string& error) const
|
||||
{
|
||||
if (!className || !className[0] || !fieldPath || !fieldPath[0])
|
||||
{
|
||||
@@ -92,6 +120,7 @@ bool OverrideManager::ResolveFieldPath(const char* className, const char* fieldP
|
||||
|
||||
offset = 0;
|
||||
size = 0;
|
||||
addressSteps.clear();
|
||||
std::string currentClass = className;
|
||||
for (size_t i = 0; i < parts.size(); ++i)
|
||||
{
|
||||
@@ -102,8 +131,11 @@ bool OverrideManager::ResolveFieldPath(const char* className, const char* fieldP
|
||||
return false;
|
||||
}
|
||||
|
||||
offset += field.offset;
|
||||
size = field.size;
|
||||
FieldAddressStep step {};
|
||||
step.offset = field.offset;
|
||||
step.dereference = i + 1 < parts.size() && IsPointerSchemaType(field.typeName);
|
||||
addressSteps.push_back(step);
|
||||
if (i + 1 < parts.size())
|
||||
{
|
||||
if (field.typeName.empty())
|
||||
@@ -111,13 +143,119 @@ bool OverrideManager::ResolveFieldPath(const char* className, const char* fieldP
|
||||
error = "intermediate field has no schema type name";
|
||||
return false;
|
||||
}
|
||||
currentClass = field.typeName;
|
||||
currentClass = NormalizeSchemaClassName(field.typeName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!addressSteps.empty())
|
||||
offset = addressSteps.front().offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t* ResolveRuleAddress(const CompiledOverrideRule& rule, void* entityData)
|
||||
{
|
||||
auto* address = static_cast<uint8_t*>(entityData);
|
||||
for (const auto& step : rule.addressSteps)
|
||||
{
|
||||
address += step.offset;
|
||||
if (!step.dereference)
|
||||
continue;
|
||||
|
||||
address = *reinterpret_cast<uint8_t**>(address);
|
||||
if (!address)
|
||||
return nullptr;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
static uint8_t* ResolveAddressSteps(const std::vector<FieldAddressStep>& steps, void* entityData)
|
||||
{
|
||||
auto* address = static_cast<uint8_t*>(entityData);
|
||||
for (const auto& step : steps)
|
||||
{
|
||||
address += step.offset;
|
||||
if (!step.dereference)
|
||||
continue;
|
||||
|
||||
address = *reinterpret_cast<uint8_t**>(address);
|
||||
if (!address)
|
||||
return nullptr;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
static void SaveAndWrite(std::vector<AppliedOverride>& applied, uint8_t* address, const void* value, size_t size)
|
||||
{
|
||||
AppliedOverride saved {};
|
||||
saved.address = address;
|
||||
saved.original.resize(size);
|
||||
memcpy(saved.original.data(), address, saved.original.size());
|
||||
memcpy(address, value, size);
|
||||
applied.push_back(saved);
|
||||
}
|
||||
|
||||
static void SaveAndWriteIfChanged(std::vector<AppliedOverride>& applied, uint8_t* address, const void* value, size_t size)
|
||||
{
|
||||
if (memcmp(address, value, size) == 0)
|
||||
return;
|
||||
|
||||
SaveAndWrite(applied, address, value, size);
|
||||
}
|
||||
|
||||
static void ApplyVectorFirstFromField(const CompiledOverrideRule& rule, void* entityData, std::vector<AppliedOverride>& applied)
|
||||
{
|
||||
auto* vectorAddress = ResolveAddressSteps(rule.addressSteps, entityData);
|
||||
auto* sourceAddress = ResolveAddressSteps(rule.sourceAddressSteps, entityData);
|
||||
if (!vectorAddress || !sourceAddress)
|
||||
return;
|
||||
|
||||
const uint32_t sourceHandle = *reinterpret_cast<uint32_t*>(sourceAddress);
|
||||
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;
|
||||
|
||||
auto* countAddress = reinterpret_cast<uint8_t*>(&*reinterpret_cast<int32_t*>(vectorAddress));
|
||||
if (sourceHandle == invalidHandle)
|
||||
{
|
||||
const int32_t emptyCount = 0;
|
||||
SaveAndWriteIfChanged(applied, countAddress, &emptyCount, sizeof(emptyCount));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!elements)
|
||||
return;
|
||||
|
||||
const int32_t activeOnlyCount = 1;
|
||||
SaveAndWriteIfChanged(applied, countAddress, &activeOnlyCount, sizeof(activeOnlyCount));
|
||||
SaveAndWriteIfChanged(applied, elements, &sourceHandle, sizeof(sourceHandle));
|
||||
}
|
||||
|
||||
void OverrideManager::RebuildIndex()
|
||||
{
|
||||
m_rulesByEntity.clear();
|
||||
@@ -139,6 +277,17 @@ bool OverrideManager::MarkEntityFieldDirtyByOffset(int entityIndex, int32_t offs
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OverrideManager::MarkEntityFullDirty(int entityIndex)
|
||||
{
|
||||
auto* identity = FindIdentityByIndex(entityIndex);
|
||||
if (!identity || !identity->m_pInstance)
|
||||
return false;
|
||||
|
||||
NetworkStateChangedData data(true);
|
||||
identity->m_pInstance->NetworkStateChanged(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
int OverrideManager::AddRuleBytes(int recipientSlot, int entityIndex, const char* className, const char* fieldPath, const void* value, int valueSize, std::string& error)
|
||||
{
|
||||
if (recipientSlot < -1 || recipientSlot >= 64)
|
||||
@@ -163,7 +312,7 @@ int OverrideManager::AddRuleBytes(int recipientSlot, int entityIndex, const char
|
||||
rule.entityIndex = entityIndex;
|
||||
rule.className = className ? className : "";
|
||||
rule.fieldPath = fieldPath ? fieldPath : "";
|
||||
if (!ResolveFieldPath(rule.className.c_str(), rule.fieldPath.c_str(), rule.offset, rule.size, error))
|
||||
if (!ResolveFieldPath(rule.className.c_str(), rule.fieldPath.c_str(), rule.offset, rule.size, rule.addressSteps, error))
|
||||
return 0;
|
||||
if (rule.size > 0 && valueSize > rule.size)
|
||||
{
|
||||
@@ -174,8 +323,72 @@ 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();
|
||||
MarkEntityFieldDirtyByOffset(entityIndex, rule.offset);
|
||||
MarkEntityFullDirty(entityIndex);
|
||||
return rule.id;
|
||||
}
|
||||
|
||||
int OverrideManager::AddRuleVectorFirstFromField(int recipientSlot, int entityIndex, const char* className, const char* vectorFieldPath, const char* sourceFieldPath, std::string& error)
|
||||
{
|
||||
if (recipientSlot < -1 || recipientSlot >= 64)
|
||||
{
|
||||
error = "recipient slot must be all/-1 or 0..63";
|
||||
return 0;
|
||||
}
|
||||
if (entityIndex < 0)
|
||||
{
|
||||
error = "entity index must be >= 0";
|
||||
return 0;
|
||||
}
|
||||
|
||||
CompiledOverrideRule rule;
|
||||
rule.id = m_nextId++;
|
||||
rule.kind = OverrideRuleKind::VectorFirstFromField;
|
||||
rule.recipientSlot = recipientSlot;
|
||||
rule.entityIndex = entityIndex;
|
||||
rule.className = className ? className : "";
|
||||
rule.fieldPath = vectorFieldPath ? vectorFieldPath : "";
|
||||
rule.sourceFieldPath = sourceFieldPath ? sourceFieldPath : "";
|
||||
if (!ResolveFieldPath(rule.className.c_str(), rule.fieldPath.c_str(), rule.offset, rule.size, rule.addressSteps, error))
|
||||
return 0;
|
||||
|
||||
int32_t sourceOffset = 0;
|
||||
if (!ResolveFieldPath(rule.className.c_str(), rule.sourceFieldPath.c_str(), sourceOffset, rule.sourceSize, rule.sourceAddressSteps, error))
|
||||
return 0;
|
||||
if (rule.sourceSize > 0 && rule.sourceSize < 4)
|
||||
{
|
||||
error = "source field is smaller than a handle";
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -186,7 +399,7 @@ bool OverrideManager::RemoveRule(int id)
|
||||
if (rule.id != id)
|
||||
continue;
|
||||
rule.enabled = false;
|
||||
MarkEntityFieldDirtyByOffset(rule.entityIndex, rule.offset);
|
||||
MarkEntityFullDirty(rule.entityIndex);
|
||||
RebuildIndex();
|
||||
return true;
|
||||
}
|
||||
@@ -197,7 +410,8 @@ bool OverrideManager::MarkEntityFieldDirty(int entityIndex, const char* classNam
|
||||
{
|
||||
int32_t offset = 0;
|
||||
int32_t size = 0;
|
||||
if (!ResolveFieldPath(className, fieldPath, offset, size, error))
|
||||
std::vector<FieldAddressStep> addressSteps;
|
||||
if (!ResolveFieldPath(className, fieldPath, offset, size, addressSteps, error))
|
||||
return false;
|
||||
return MarkEntityFieldDirtyByOffset(entityIndex, offset);
|
||||
}
|
||||
@@ -212,19 +426,69 @@ std::vector<AppliedOverride> OverrideManager::ApplyForPackedEntity(CPlayerSlot r
|
||||
if (found == m_rulesByEntity.end())
|
||||
return applied;
|
||||
|
||||
int effectiveRecipient = recipient.Get();
|
||||
int uniqueRuleRecipient = -2;
|
||||
for (size_t ruleIndex : found->second)
|
||||
{
|
||||
const auto& rule = m_rules[ruleIndex];
|
||||
if (!rule.enabled || (rule.recipientSlot != -1 && rule.recipientSlot != recipient.Get()))
|
||||
if (!rule.enabled || rule.recipientSlot < 0)
|
||||
continue;
|
||||
if (uniqueRuleRecipient == -2)
|
||||
uniqueRuleRecipient = rule.recipientSlot;
|
||||
else if (uniqueRuleRecipient != rule.recipientSlot)
|
||||
{
|
||||
uniqueRuleRecipient = -2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const auto& rule = m_rules[ruleIndex];
|
||||
if (!rule.enabled)
|
||||
continue;
|
||||
|
||||
auto* address = reinterpret_cast<uint8_t*>(entityData) + rule.offset;
|
||||
AppliedOverride saved {};
|
||||
saved.address = address;
|
||||
saved.original.resize(rule.value.size());
|
||||
memcpy(saved.original.data(), address, saved.original.size());
|
||||
memcpy(address, rule.value.data(), rule.value.size());
|
||||
applied.push_back(saved);
|
||||
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)
|
||||
{
|
||||
ApplyVectorFirstFromField(rule, entityData, applied);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* address = ResolveRuleAddress(rule, entityData);
|
||||
if (!address)
|
||||
continue;
|
||||
|
||||
SaveAndWrite(applied, address, rule.value.data(), rule.value.size());
|
||||
}
|
||||
|
||||
return applied;
|
||||
|
||||
Reference in New Issue
Block a user