mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix chest interaction and measure server RTT for latency meter
Chests (and lockboxes, coffers, etc.) failed to open because CMSG_LOOT was only sent on Classic/Turtle expansions, and only when GO type was already cached as type 3. Fix: always send CMSG_LOOT after CMSG_GAMEOBJ_USE (server silently ignores it for non-lootable objects). Also broaden CMSG_GAMEOBJ_REPORT_USE to all non-mailbox WotLK GOs. Latency meter: record pingTimestamp_ in sendPing() and compute RTT in handlePong(); add toggleable "Show Latency Meter" checkbox in Interface settings (saved to settings.cfg).
This commit is contained in:
parent
14f672ab6a
commit
711cb966ef
4 changed files with 42 additions and 16 deletions
|
|
@ -1862,6 +1862,7 @@ private:
|
||||||
float timeSinceLastMoveHeartbeat_ = 0.0f; // Periodic movement heartbeat to keep server position synced
|
float timeSinceLastMoveHeartbeat_ = 0.0f; // Periodic movement heartbeat to keep server position synced
|
||||||
float moveHeartbeatInterval_ = 0.5f;
|
float moveHeartbeatInterval_ = 0.5f;
|
||||||
uint32_t lastLatency = 0; // Last measured latency (milliseconds)
|
uint32_t lastLatency = 0; // Last measured latency (milliseconds)
|
||||||
|
std::chrono::steady_clock::time_point pingTimestamp_; // Time CMSG_PING was sent
|
||||||
|
|
||||||
// Player GUID and map
|
// Player GUID and map
|
||||||
uint64_t playerGuid = 0;
|
uint64_t playerGuid = 0;
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ private:
|
||||||
bool pendingMinimapRotate = false;
|
bool pendingMinimapRotate = false;
|
||||||
bool pendingMinimapSquare = false;
|
bool pendingMinimapSquare = false;
|
||||||
bool pendingMinimapNpcDots = false;
|
bool pendingMinimapNpcDots = false;
|
||||||
|
bool pendingShowLatencyMeter = true;
|
||||||
bool pendingSeparateBags = true;
|
bool pendingSeparateBags = true;
|
||||||
bool pendingAutoLoot = false;
|
bool pendingAutoLoot = false;
|
||||||
|
|
||||||
|
|
@ -159,6 +160,7 @@ private:
|
||||||
bool minimapRotate_ = false;
|
bool minimapRotate_ = false;
|
||||||
bool minimapSquare_ = false;
|
bool minimapSquare_ = false;
|
||||||
bool minimapNpcDots_ = false;
|
bool minimapNpcDots_ = false;
|
||||||
|
bool showLatencyMeter_ = true; // Show server latency indicator
|
||||||
bool minimapSettingsApplied_ = false;
|
bool minimapSettingsApplied_ = false;
|
||||||
bool volumeSettingsApplied_ = false; // True once saved volume settings applied to audio managers
|
bool volumeSettingsApplied_ = false; // True once saved volume settings applied to audio managers
|
||||||
bool msaaSettingsApplied_ = false; // True once saved MSAA setting applied to renderer
|
bool msaaSettingsApplied_ = false; // True once saved MSAA setting applied to renderer
|
||||||
|
|
|
||||||
|
|
@ -7642,6 +7642,9 @@ void GameHandler::sendPing() {
|
||||||
LOG_DEBUG("Sending CMSG_PING (heartbeat)");
|
LOG_DEBUG("Sending CMSG_PING (heartbeat)");
|
||||||
LOG_DEBUG(" Sequence: ", pingSequence);
|
LOG_DEBUG(" Sequence: ", pingSequence);
|
||||||
|
|
||||||
|
// Record send time for RTT measurement
|
||||||
|
pingTimestamp_ = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
// Build and send ping packet
|
// Build and send ping packet
|
||||||
auto packet = PingPacket::build(pingSequence, lastLatency);
|
auto packet = PingPacket::build(pingSequence, lastLatency);
|
||||||
socket->send(packet);
|
socket->send(packet);
|
||||||
|
|
@ -7663,7 +7666,12 @@ void GameHandler::handlePong(network::Packet& packet) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("Heartbeat acknowledged (sequence: ", data.sequence, ")");
|
// Measure round-trip time
|
||||||
|
auto rtt = std::chrono::steady_clock::now() - pingTimestamp_;
|
||||||
|
lastLatency = static_cast<uint32_t>(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(rtt).count());
|
||||||
|
|
||||||
|
LOG_DEBUG("Heartbeat acknowledged (sequence: ", data.sequence, ", latency: ", lastLatency, "ms)");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GameHandler::nextMovementTimestampMs() {
|
uint32_t GameHandler::nextMovementTimestampMs() {
|
||||||
|
|
@ -15077,10 +15085,12 @@ void GameHandler::performGameObjectInteractionNow(uint64_t guid) {
|
||||||
// animation/sound and expects the client to request the mail list.
|
// animation/sound and expects the client to request the mail list.
|
||||||
bool isMailbox = false;
|
bool isMailbox = false;
|
||||||
bool chestLike = false;
|
bool chestLike = false;
|
||||||
// Chest-type game objects (type=3): on all expansions, also send CMSG_LOOT so
|
// Always send CMSG_LOOT after CMSG_GAMEOBJ_USE for any gameobject that could be
|
||||||
// the server opens the loot response. Other harvestable/interactive types rely
|
// lootable. The server silently ignores CMSG_LOOT for non-lootable objects
|
||||||
// on the server auto-sending SMSG_LOOT_RESPONSE after CMSG_GAMEOBJ_USE.
|
// (doors, buttons, etc.), so this is safe. Not sending it is the main reason
|
||||||
bool shouldSendLoot = isActiveExpansion("classic") || isActiveExpansion("turtle");
|
// chests fail to open when their GO type is not yet cached or their name doesn't
|
||||||
|
// contain the word "chest" (e.g. lockboxes, coffers, strongboxes, caches).
|
||||||
|
bool shouldSendLoot = true;
|
||||||
if (entity && entity->getType() == ObjectType::GAMEOBJECT) {
|
if (entity && entity->getType() == ObjectType::GAMEOBJECT) {
|
||||||
auto go = std::static_pointer_cast<GameObject>(entity);
|
auto go = std::static_pointer_cast<GameObject>(entity);
|
||||||
auto* info = getCachedGameObjectInfo(go->getEntry());
|
auto* info = getCachedGameObjectInfo(go->getEntry());
|
||||||
|
|
@ -15096,22 +15106,20 @@ void GameHandler::performGameObjectInteractionNow(uint64_t guid) {
|
||||||
refreshMailList();
|
refreshMailList();
|
||||||
} else if (info && info->type == 3) {
|
} else if (info && info->type == 3) {
|
||||||
chestLike = true;
|
chestLike = true;
|
||||||
// Type-3 chests require CMSG_LOOT on all expansions (AzerothCore WotLK included)
|
|
||||||
shouldSendLoot = true;
|
|
||||||
} else if (turtleMode) {
|
|
||||||
// Turtle compatibility: keep eager loot open behavior.
|
|
||||||
shouldSendLoot = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!chestLike && !goName.empty()) {
|
if (!chestLike && !goName.empty()) {
|
||||||
std::string lower = goName;
|
std::string lower = goName;
|
||||||
std::transform(lower.begin(), lower.end(), lower.begin(),
|
std::transform(lower.begin(), lower.end(), lower.begin(),
|
||||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||||
chestLike = (lower.find("chest") != std::string::npos);
|
chestLike = (lower.find("chest") != std::string::npos ||
|
||||||
if (chestLike) shouldSendLoot = true;
|
lower.find("lockbox") != std::string::npos ||
|
||||||
|
lower.find("strongbox") != std::string::npos ||
|
||||||
|
lower.find("coffer") != std::string::npos ||
|
||||||
|
lower.find("cache") != std::string::npos);
|
||||||
}
|
}
|
||||||
// For WotLK chest-like gameobjects, also send CMSG_GAMEOBJ_REPORT_USE.
|
// For WotLK, CMSG_GAMEOBJ_REPORT_USE is required for chests (and is harmless for others).
|
||||||
if (!isMailbox && chestLike && isActiveExpansion("wotlk")) {
|
if (!isMailbox && isActiveExpansion("wotlk")) {
|
||||||
network::Packet reportUse(wireOpcode(Opcode::CMSG_GAMEOBJ_REPORT_USE));
|
network::Packet reportUse(wireOpcode(Opcode::CMSG_GAMEOBJ_REPORT_USE));
|
||||||
reportUse.writeUInt64(guid);
|
reportUse.writeUInt64(guid);
|
||||||
socket->send(reportUse);
|
socket->send(reportUse);
|
||||||
|
|
|
||||||
|
|
@ -8581,6 +8581,7 @@ void GameScreen::renderSettingsWindow() {
|
||||||
pendingMinimapRotate = minimapRotate_;
|
pendingMinimapRotate = minimapRotate_;
|
||||||
pendingMinimapSquare = minimapSquare_;
|
pendingMinimapSquare = minimapSquare_;
|
||||||
pendingMinimapNpcDots = minimapNpcDots_;
|
pendingMinimapNpcDots = minimapNpcDots_;
|
||||||
|
pendingShowLatencyMeter = showLatencyMeter_;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
if (auto* minimap = renderer->getMinimap()) {
|
if (auto* minimap = renderer->getMinimap()) {
|
||||||
minimap->setRotateWithCamera(minimapRotate_);
|
minimap->setRotateWithCamera(minimapRotate_);
|
||||||
|
|
@ -8952,6 +8953,16 @@ void GameScreen::renderSettingsWindow() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::SeparatorText("Network");
|
||||||
|
ImGui::Spacing();
|
||||||
|
if (ImGui::Checkbox("Show Latency Meter", &pendingShowLatencyMeter)) {
|
||||||
|
showLatencyMeter_ = pendingShowLatencyMeter;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextDisabled("(ms indicator near minimap)");
|
||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
@ -10080,9 +10091,9 @@ void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) {
|
||||||
break; // Show at most one queue slot indicator
|
break; // Show at most one queue slot indicator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latency indicator (shown when in world and last latency is known)
|
// Latency indicator (toggleable in Interface settings)
|
||||||
uint32_t latMs = gameHandler.getLatencyMs();
|
uint32_t latMs = gameHandler.getLatencyMs();
|
||||||
if (latMs > 0 && gameHandler.getState() == game::WorldState::IN_WORLD) {
|
if (showLatencyMeter_ && latMs > 0 && gameHandler.getState() == game::WorldState::IN_WORLD) {
|
||||||
ImVec4 latColor;
|
ImVec4 latColor;
|
||||||
if (latMs < 100) latColor = ImVec4(0.3f, 1.0f, 0.3f, 0.8f); // Green < 100ms
|
if (latMs < 100) latColor = ImVec4(0.3f, 1.0f, 0.3f, 0.8f); // Green < 100ms
|
||||||
else if (latMs < 250) latColor = ImVec4(1.0f, 1.0f, 0.3f, 0.8f); // Yellow < 250ms
|
else if (latMs < 250) latColor = ImVec4(1.0f, 1.0f, 0.3f, 0.8f); // Yellow < 250ms
|
||||||
|
|
@ -10340,6 +10351,7 @@ void GameScreen::saveSettings() {
|
||||||
out << "minimap_rotate=" << (pendingMinimapRotate ? 1 : 0) << "\n";
|
out << "minimap_rotate=" << (pendingMinimapRotate ? 1 : 0) << "\n";
|
||||||
out << "minimap_square=" << (pendingMinimapSquare ? 1 : 0) << "\n";
|
out << "minimap_square=" << (pendingMinimapSquare ? 1 : 0) << "\n";
|
||||||
out << "minimap_npc_dots=" << (pendingMinimapNpcDots ? 1 : 0) << "\n";
|
out << "minimap_npc_dots=" << (pendingMinimapNpcDots ? 1 : 0) << "\n";
|
||||||
|
out << "show_latency_meter=" << (pendingShowLatencyMeter ? 1 : 0) << "\n";
|
||||||
out << "separate_bags=" << (pendingSeparateBags ? 1 : 0) << "\n";
|
out << "separate_bags=" << (pendingSeparateBags ? 1 : 0) << "\n";
|
||||||
out << "show_action_bar2=" << (pendingShowActionBar2 ? 1 : 0) << "\n";
|
out << "show_action_bar2=" << (pendingShowActionBar2 ? 1 : 0) << "\n";
|
||||||
out << "action_bar2_offset_x=" << pendingActionBar2OffsetX << "\n";
|
out << "action_bar2_offset_x=" << pendingActionBar2OffsetX << "\n";
|
||||||
|
|
@ -10440,6 +10452,9 @@ void GameScreen::loadSettings() {
|
||||||
int v = std::stoi(val);
|
int v = std::stoi(val);
|
||||||
minimapNpcDots_ = (v != 0);
|
minimapNpcDots_ = (v != 0);
|
||||||
pendingMinimapNpcDots = minimapNpcDots_;
|
pendingMinimapNpcDots = minimapNpcDots_;
|
||||||
|
} else if (key == "show_latency_meter") {
|
||||||
|
showLatencyMeter_ = (std::stoi(val) != 0);
|
||||||
|
pendingShowLatencyMeter = showLatencyMeter_;
|
||||||
} else if (key == "separate_bags") {
|
} else if (key == "separate_bags") {
|
||||||
pendingSeparateBags = (std::stoi(val) != 0);
|
pendingSeparateBags = (std::stoi(val) != 0);
|
||||||
inventoryScreen.setSeparateBags(pendingSeparateBags);
|
inventoryScreen.setSeparateBags(pendingSeparateBags);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue