mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Auto-detect coinage and inventory fields
This commit is contained in:
parent
24d780e669
commit
bf39c0b900
3 changed files with 80 additions and 2 deletions
|
|
@ -1140,6 +1140,10 @@ private:
|
|||
};
|
||||
std::unordered_map<uint64_t, LocalLootState> localLootState_;
|
||||
uint64_t playerMoneyCopper_ = 0;
|
||||
// Some servers/custom clients shift update field indices. We can auto-detect coinage by correlating
|
||||
// money-notify deltas with update-field diffs and then overriding UF::PLAYER_FIELD_COINAGE at runtime.
|
||||
uint32_t pendingMoneyDelta_ = 0;
|
||||
float pendingMoneyDeltaTimer_ = 0.0f;
|
||||
|
||||
// Gossip
|
||||
bool gossipWindowOpen = false;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,9 @@ public:
|
|||
/** Get the wire index for a logical field. Returns 0xFFFF if unknown. */
|
||||
uint16_t index(UF field) const;
|
||||
|
||||
/** Override a wire index at runtime (used for auto-detecting custom field layouts). */
|
||||
void setIndex(UF field, uint16_t idx) { fieldMap_[static_cast<uint16_t>(field)] = idx; }
|
||||
|
||||
/** Check if a field is mapped. */
|
||||
bool hasField(UF field) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -255,6 +255,14 @@ void GameHandler::update(float deltaTime) {
|
|||
clearTarget();
|
||||
}
|
||||
|
||||
if (pendingMoneyDeltaTimer_ > 0.0f) {
|
||||
pendingMoneyDeltaTimer_ -= deltaTime;
|
||||
if (pendingMoneyDeltaTimer_ <= 0.0f) {
|
||||
pendingMoneyDeltaTimer_ = 0.0f;
|
||||
pendingMoneyDelta_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Send periodic heartbeat if in world
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
timeSinceLastPing += deltaTime;
|
||||
|
|
@ -992,6 +1000,8 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
uint32_t amount = packet.readUInt32();
|
||||
playerMoneyCopper_ += amount;
|
||||
pendingMoneyDelta_ = amount;
|
||||
pendingMoneyDeltaTimer_ = 2.0f;
|
||||
LOG_INFO("Looted ", amount, " copper (total: ", playerMoneyCopper_, ")");
|
||||
}
|
||||
break;
|
||||
|
|
@ -2765,6 +2775,46 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
return true;
|
||||
};
|
||||
|
||||
auto maybeDetectCoinageIndex = [&](const std::map<uint16_t, uint32_t>& oldFields,
|
||||
const std::map<uint16_t, uint32_t>& newFields) {
|
||||
if (pendingMoneyDelta_ == 0 || pendingMoneyDeltaTimer_ <= 0.0f) return;
|
||||
if (oldFields.empty() || newFields.empty()) return;
|
||||
|
||||
constexpr uint32_t kMaxPlausibleCoinage = 2147483647u;
|
||||
std::vector<uint16_t> candidates;
|
||||
candidates.reserve(8);
|
||||
|
||||
for (const auto& [idx, newVal] : newFields) {
|
||||
auto itOld = oldFields.find(idx);
|
||||
if (itOld == oldFields.end()) continue;
|
||||
uint32_t oldVal = itOld->second;
|
||||
if (newVal < oldVal) continue;
|
||||
uint32_t delta = newVal - oldVal;
|
||||
if (delta != pendingMoneyDelta_) continue;
|
||||
if (newVal > kMaxPlausibleCoinage) continue;
|
||||
candidates.push_back(idx);
|
||||
}
|
||||
|
||||
if (candidates.empty()) return;
|
||||
|
||||
uint16_t current = fieldIndex(UF::PLAYER_FIELD_COINAGE);
|
||||
uint16_t chosen = candidates[0];
|
||||
if (std::find(candidates.begin(), candidates.end(), current) != candidates.end()) {
|
||||
chosen = current;
|
||||
} else {
|
||||
std::sort(candidates.begin(), candidates.end());
|
||||
chosen = candidates[0];
|
||||
}
|
||||
|
||||
if (chosen != current && current != 0xFFFF) {
|
||||
updateFieldTable_.setIndex(UF::PLAYER_FIELD_COINAGE, chosen);
|
||||
LOG_WARNING("Auto-detected PLAYER_FIELD_COINAGE index: ", chosen, " (was ", current, ")");
|
||||
}
|
||||
|
||||
pendingMoneyDelta_ = 0;
|
||||
pendingMoneyDeltaTimer_ = 0.0f;
|
||||
};
|
||||
|
||||
// Process out-of-range objects first
|
||||
for (uint64_t guid : data.outOfRangeGuids) {
|
||||
if (entityManager.hasEntity(guid)) {
|
||||
|
|
@ -3089,6 +3139,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
// Auto-detect coinage index using the previous snapshot vs this full snapshot.
|
||||
maybeDetectCoinageIndex(lastPlayerFields_, block.fields);
|
||||
|
||||
lastPlayerFields_ = block.fields;
|
||||
detectInventorySlotBases(block.fields);
|
||||
|
||||
|
|
@ -3352,6 +3405,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
}
|
||||
// Update XP / inventory slot / skill fields for player entity
|
||||
if (block.guid == playerGuid) {
|
||||
std::map<uint16_t, uint32_t> oldFieldsSnapshot = lastPlayerFields_;
|
||||
if (block.hasMovement && block.runSpeed > 0.1f && block.runSpeed < 100.0f) {
|
||||
serverRunSpeed_ = block.runSpeed;
|
||||
// Some server dismount paths update run speed without updating mount display field.
|
||||
|
|
@ -3368,6 +3422,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
for (const auto& [key, val] : block.fields) {
|
||||
lastPlayerFields_[key] = val;
|
||||
}
|
||||
maybeDetectCoinageIndex(oldFieldsSnapshot, lastPlayerFields_);
|
||||
detectInventorySlotBases(block.fields);
|
||||
bool slotsChanged = false;
|
||||
const uint16_t ufPlayerXp = fieldIndex(UF::PLAYER_XP);
|
||||
|
|
@ -4924,7 +4979,7 @@ uint64_t GameHandler::resolveOnlineItemGuid(uint32_t itemId) const {
|
|||
|
||||
void GameHandler::detectInventorySlotBases(const std::map<uint16_t, uint32_t>& fields) {
|
||||
if (invSlotBase_ >= 0 && packSlotBase_ >= 0) return;
|
||||
if (onlineItems_.empty() || fields.empty()) return;
|
||||
if (fields.empty()) return;
|
||||
|
||||
std::vector<uint16_t> matchingPairs;
|
||||
matchingPairs.reserve(32);
|
||||
|
|
@ -4935,7 +4990,23 @@ void GameHandler::detectInventorySlotBases(const std::map<uint16_t, uint32_t>& f
|
|||
if (itHigh == fields.end()) continue;
|
||||
uint64_t guid = (uint64_t(itHigh->second) << 32) | low;
|
||||
if (guid == 0) continue;
|
||||
if (onlineItems_.count(guid)) {
|
||||
// Primary signal: GUID pairs that match spawned ITEM objects.
|
||||
if (!onlineItems_.empty() && onlineItems_.count(guid)) {
|
||||
matchingPairs.push_back(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback signal (when ITEM objects haven't been seen yet):
|
||||
// collect any plausible non-zero GUID pairs and derive a base by density.
|
||||
if (matchingPairs.empty()) {
|
||||
for (const auto& [idx, low] : fields) {
|
||||
if ((idx % 2) != 0) continue;
|
||||
auto itHigh = fields.find(static_cast<uint16_t>(idx + 1));
|
||||
if (itHigh == fields.end()) continue;
|
||||
uint64_t guid = (uint64_t(itHigh->second) << 32) | low;
|
||||
if (guid == 0) continue;
|
||||
// Heuristic: item GUIDs tend to be non-trivial and change often; ignore tiny values.
|
||||
if (guid < 0x10000ull) continue;
|
||||
matchingPairs.push_back(idx);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue