mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 17:43:52 +00:00
Queue transport pre-spawn moves and add Z weapon sheath toggle
This commit is contained in:
parent
d6e7b0809c
commit
5d63bb0988
4 changed files with 120 additions and 47 deletions
|
|
@ -176,6 +176,13 @@ private:
|
||||||
std::unordered_map<uint32_t, uint32_t> gameObjectDisplayIdModelCache_; // displayId → M2 modelId
|
std::unordered_map<uint32_t, uint32_t> gameObjectDisplayIdModelCache_; // displayId → M2 modelId
|
||||||
std::unordered_map<uint32_t, uint32_t> gameObjectDisplayIdWmoCache_; // displayId → WMO modelId
|
std::unordered_map<uint32_t, uint32_t> gameObjectDisplayIdWmoCache_; // displayId → WMO modelId
|
||||||
std::unordered_map<uint64_t, GameObjectInstanceInfo> gameObjectInstances_; // guid → instance info
|
std::unordered_map<uint64_t, GameObjectInstanceInfo> gameObjectInstances_; // guid → instance info
|
||||||
|
struct PendingTransportMove {
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
float z = 0.0f;
|
||||||
|
float orientation = 0.0f;
|
||||||
|
};
|
||||||
|
std::unordered_map<uint64_t, PendingTransportMove> pendingTransportMoves_; // guid -> latest pre-registration move
|
||||||
uint32_t nextGameObjectModelId_ = 20000;
|
uint32_t nextGameObjectModelId_ = 20000;
|
||||||
uint32_t nextGameObjectWmoModelId_ = 40000;
|
uint32_t nextGameObjectWmoModelId_ = 40000;
|
||||||
bool gameObjectLookupsBuilt_ = false;
|
bool gameObjectLookupsBuilt_ = false;
|
||||||
|
|
@ -184,6 +191,7 @@ private:
|
||||||
uint32_t mountInstanceId_ = 0;
|
uint32_t mountInstanceId_ = 0;
|
||||||
uint32_t mountModelId_ = 0;
|
uint32_t mountModelId_ = 0;
|
||||||
uint32_t pendingMountDisplayId_ = 0; // Deferred mount load (0 = none pending)
|
uint32_t pendingMountDisplayId_ = 0; // Deferred mount load (0 = none pending)
|
||||||
|
bool weaponsSheathed_ = false;
|
||||||
void processPendingMount();
|
void processPendingMount();
|
||||||
bool creatureLookupsBuilt_ = false;
|
bool creatureLookupsBuilt_ = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ struct ActiveTransport {
|
||||||
glm::vec3 serverLinearVelocity;
|
glm::vec3 serverLinearVelocity;
|
||||||
float serverAngularVelocity;
|
float serverAngularVelocity;
|
||||||
bool hasServerVelocity;
|
bool hasServerVelocity;
|
||||||
|
bool allowBootstrapVelocity; // Disable DBC bootstrap when spawn/path mismatch is clearly invalid
|
||||||
};
|
};
|
||||||
|
|
||||||
class TransportManager {
|
class TransportManager {
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,7 @@ void Application::logoutToLogin() {
|
||||||
}
|
}
|
||||||
npcsSpawned = false;
|
npcsSpawned = false;
|
||||||
playerCharacterSpawned = false;
|
playerCharacterSpawned = false;
|
||||||
|
weaponsSheathed_ = false;
|
||||||
world.reset();
|
world.reset();
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
// Remove old player model so it doesn't persist into next session
|
// Remove old player model so it doesn't persist into next session
|
||||||
|
|
@ -408,6 +409,16 @@ void Application::update(float deltaTime) {
|
||||||
auto gh2 = std::chrono::high_resolution_clock::now();
|
auto gh2 = std::chrono::high_resolution_clock::now();
|
||||||
ghTime += std::chrono::duration<float, std::milli>(gh2 - gh1).count();
|
ghTime += std::chrono::duration<float, std::milli>(gh2 - gh1).count();
|
||||||
|
|
||||||
|
// Toggle weapon sheathe state with Z (ignored while UI captures keyboard).
|
||||||
|
{
|
||||||
|
const bool uiWantsKeyboard = ImGui::GetIO().WantCaptureKeyboard;
|
||||||
|
auto& input = Input::getInstance();
|
||||||
|
if (!uiWantsKeyboard && input.isKeyJustPressed(SDL_SCANCODE_Z)) {
|
||||||
|
weaponsSheathed_ = !weaponsSheathed_;
|
||||||
|
loadEquippedWeapons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto w1 = std::chrono::high_resolution_clock::now();
|
auto w1 = std::chrono::high_resolution_clock::now();
|
||||||
if (world) {
|
if (world) {
|
||||||
world->update(deltaTime);
|
world->update(deltaTime);
|
||||||
|
|
@ -1235,6 +1246,16 @@ void Application::setupUICallbacks() {
|
||||||
// Server-authoritative movement - set initial position from spawn data
|
// Server-authoritative movement - set initial position from spawn data
|
||||||
glm::vec3 canonicalPos(x, y, z);
|
glm::vec3 canonicalPos(x, y, z);
|
||||||
transportManager->updateServerTransport(guid, canonicalPos, orientation);
|
transportManager->updateServerTransport(guid, canonicalPos, orientation);
|
||||||
|
|
||||||
|
// If a move packet arrived before registration completed, replay latest now.
|
||||||
|
auto pendingIt = pendingTransportMoves_.find(guid);
|
||||||
|
if (pendingIt != pendingTransportMoves_.end()) {
|
||||||
|
const PendingTransportMove pending = pendingIt->second;
|
||||||
|
transportManager->updateServerTransport(guid, glm::vec3(pending.x, pending.y, pending.z), pending.orientation);
|
||||||
|
LOG_INFO("Replayed queued transport move for GUID=0x", std::hex, guid, std::dec,
|
||||||
|
" pos=(", pending.x, ", ", pending.y, ", ", pending.z, ") orientation=", pending.orientation);
|
||||||
|
pendingTransportMoves_.erase(pendingIt);
|
||||||
|
}
|
||||||
LOG_INFO("Transport registered - server-authoritative movement");
|
LOG_INFO("Transport registered - server-authoritative movement");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1329,13 +1350,15 @@ void Application::setupUICallbacks() {
|
||||||
|
|
||||||
transportManager->registerTransport(guid, wmoInstanceId, pathId, canonicalSpawnPos);
|
transportManager->registerTransport(guid, wmoInstanceId, pathId, canonicalSpawnPos);
|
||||||
} else {
|
} else {
|
||||||
|
pendingTransportMoves_[guid] = PendingTransportMove{x, y, z, orientation};
|
||||||
LOG_WARNING("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
LOG_WARNING("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
||||||
" - WMO instance not found");
|
" - WMO instance not found (queued move for replay)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
pendingTransportMoves_[guid] = PendingTransportMove{x, y, z, orientation};
|
||||||
LOG_WARNING("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
LOG_WARNING("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
||||||
" - entity not found in EntityManager");
|
" - entity not found in EntityManager (queued move for replay)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1879,6 +1902,13 @@ void Application::loadEquippedWeapons() {
|
||||||
{ game::EquipSlot::OFF_HAND, 2 },
|
{ game::EquipSlot::OFF_HAND, 2 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (weaponsSheathed_) {
|
||||||
|
for (const auto& ws : weaponSlots) {
|
||||||
|
charRenderer->detachWeapon(charInstanceId, ws.attachmentId);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& ws : weaponSlots) {
|
for (const auto& ws : weaponSlots) {
|
||||||
const auto& equipSlot = inventory.getEquipSlot(ws.slot);
|
const auto& equipSlot = inventory.getEquipSlot(ws.slot);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ void TransportManager::registerTransport(uint64_t guid, uint32_t wmoInstanceId,
|
||||||
transport.guid = guid;
|
transport.guid = guid;
|
||||||
transport.wmoInstanceId = wmoInstanceId;
|
transport.wmoInstanceId = wmoInstanceId;
|
||||||
transport.pathId = pathId;
|
transport.pathId = pathId;
|
||||||
|
transport.allowBootstrapVelocity = true;
|
||||||
|
|
||||||
// CRITICAL: Set basePosition from spawn position and t=0 offset
|
// CRITICAL: Set basePosition from spawn position and t=0 offset
|
||||||
// For stationary paths (1 waypoint), just use spawn position directly
|
// For stationary paths (1 waypoint), just use spawn position directly
|
||||||
|
|
@ -59,12 +60,20 @@ void TransportManager::registerTransport(uint64_t guid, uint32_t wmoInstanceId,
|
||||||
// Sanity check: firstWaypoint should match spawnWorldPos
|
// Sanity check: firstWaypoint should match spawnWorldPos
|
||||||
glm::vec3 firstWaypoint = path.points[0].pos;
|
glm::vec3 firstWaypoint = path.points[0].pos;
|
||||||
glm::vec3 waypointDiff = spawnWorldPos - firstWaypoint;
|
glm::vec3 waypointDiff = spawnWorldPos - firstWaypoint;
|
||||||
if (glm::length(waypointDiff) > 1.0f) {
|
const float mismatchDist = glm::length(waypointDiff);
|
||||||
|
if (mismatchDist > 1.0f) {
|
||||||
LOG_WARNING("Transport 0x", std::hex, guid, std::dec, " path ", pathId,
|
LOG_WARNING("Transport 0x", std::hex, guid, std::dec, " path ", pathId,
|
||||||
": firstWaypoint mismatch! spawnPos=(", spawnWorldPos.x, ",", spawnWorldPos.y, ",", spawnWorldPos.z, ")",
|
": firstWaypoint mismatch! spawnPos=(", spawnWorldPos.x, ",", spawnWorldPos.y, ",", spawnWorldPos.z, ")",
|
||||||
" firstWaypoint=(", firstWaypoint.x, ",", firstWaypoint.y, ",", firstWaypoint.z, ")",
|
" firstWaypoint=(", firstWaypoint.x, ",", firstWaypoint.y, ",", firstWaypoint.z, ")",
|
||||||
" diff=(", waypointDiff.x, ",", waypointDiff.y, ",", waypointDiff.z, ")");
|
" diff=(", waypointDiff.x, ",", waypointDiff.y, ",", waypointDiff.z, ")");
|
||||||
}
|
}
|
||||||
|
const bool firstWaypointIsOrigin = glm::dot(firstWaypoint, firstWaypoint) < 0.0001f;
|
||||||
|
if (mismatchDist > 500.0f || (firstWaypointIsOrigin && mismatchDist > 50.0f)) {
|
||||||
|
transport.allowBootstrapVelocity = false;
|
||||||
|
LOG_WARNING("Transport 0x", std::hex, guid, std::dec,
|
||||||
|
" disabling DBC bootstrap velocity due to large spawn/path mismatch (dist=",
|
||||||
|
mismatchDist, ", firstIsOrigin=", firstWaypointIsOrigin, ")");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transport.rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); // Identity quaternion
|
transport.rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); // Identity quaternion
|
||||||
|
|
@ -580,7 +589,7 @@ void TransportManager::updateServerTransport(uint64_t guid, const glm::vec3& pos
|
||||||
// Bootstrap velocity from mapped DBC path on first authoritative sample.
|
// Bootstrap velocity from mapped DBC path on first authoritative sample.
|
||||||
// This avoids "stalled at dock" when server sends sparse transport snapshots.
|
// This avoids "stalled at dock" when server sends sparse transport snapshots.
|
||||||
auto pathIt2 = paths_.find(transport->pathId);
|
auto pathIt2 = paths_.find(transport->pathId);
|
||||||
if (pathIt2 != paths_.end()) {
|
if (transport->allowBootstrapVelocity && pathIt2 != paths_.end()) {
|
||||||
const auto& path = pathIt2->second;
|
const auto& path = pathIt2->second;
|
||||||
if (path.points.size() >= 2 && path.durationMs > 0) {
|
if (path.points.size() >= 2 && path.durationMs > 0) {
|
||||||
glm::vec3 local = position - transport->basePosition;
|
glm::vec3 local = position - transport->basePosition;
|
||||||
|
|
@ -595,54 +604,79 @@ void TransportManager::updateServerTransport(uint64_t guid, const glm::vec3& pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t n = path.points.size();
|
constexpr float kMaxBootstrapNearestDist = 80.0f;
|
||||||
size_t nextIdx = (bestIdx + 1) % n;
|
if (bestDistSq > (kMaxBootstrapNearestDist * kMaxBootstrapNearestDist)) {
|
||||||
uint32_t t0 = path.points[bestIdx].tMs;
|
LOG_WARNING("Transport 0x", std::hex, guid, std::dec,
|
||||||
uint32_t t1 = path.points[nextIdx].tMs;
|
" skipping DBC bootstrap velocity: nearest path point too far (dist=",
|
||||||
if (nextIdx == 0 && t1 <= t0 && path.durationMs > 0) {
|
std::sqrt(bestDistSq), ", path=", transport->pathId, ")");
|
||||||
t1 = path.durationMs;
|
|
||||||
}
|
|
||||||
if (t1 <= t0 && n > 2) {
|
|
||||||
size_t prevIdx = (bestIdx + n - 1) % n;
|
|
||||||
t0 = path.points[prevIdx].tMs;
|
|
||||||
t1 = path.points[bestIdx].tMs;
|
|
||||||
glm::vec3 seg = path.points[bestIdx].pos - path.points[prevIdx].pos;
|
|
||||||
float dtSeg = static_cast<float>(t1 - t0) / 1000.0f;
|
|
||||||
if (dtSeg > 0.001f) {
|
|
||||||
glm::vec3 v = seg / dtSeg;
|
|
||||||
float speed = glm::length(v);
|
|
||||||
constexpr float kMaxSpeed = 60.0f;
|
|
||||||
if (speed > kMaxSpeed) {
|
|
||||||
v *= (kMaxSpeed / speed);
|
|
||||||
}
|
|
||||||
transport->serverLinearVelocity = v;
|
|
||||||
transport->serverAngularVelocity = 0.0f;
|
|
||||||
transport->hasServerVelocity = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
glm::vec3 seg = path.points[nextIdx].pos - path.points[bestIdx].pos;
|
size_t n = path.points.size();
|
||||||
float dtSeg = static_cast<float>(t1 - t0) / 1000.0f;
|
size_t nextIdx = (bestIdx + 1) % n;
|
||||||
if (dtSeg > 0.001f) {
|
uint32_t t0 = path.points[bestIdx].tMs;
|
||||||
glm::vec3 v = seg / dtSeg;
|
uint32_t t1 = path.points[nextIdx].tMs;
|
||||||
float speed = glm::length(v);
|
if (nextIdx == 0 && t1 <= t0 && path.durationMs > 0) {
|
||||||
constexpr float kMaxSpeed = 60.0f;
|
t1 = path.durationMs;
|
||||||
if (speed > kMaxSpeed) {
|
}
|
||||||
v *= (kMaxSpeed / speed);
|
if (t1 <= t0 && n > 2) {
|
||||||
}
|
size_t prevIdx = (bestIdx + n - 1) % n;
|
||||||
transport->serverLinearVelocity = v;
|
t0 = path.points[prevIdx].tMs;
|
||||||
transport->serverAngularVelocity = 0.0f;
|
t1 = path.points[bestIdx].tMs;
|
||||||
transport->hasServerVelocity = true;
|
glm::vec3 seg = path.points[bestIdx].pos - path.points[prevIdx].pos;
|
||||||
|
float dtSeg = static_cast<float>(t1 - t0) / 1000.0f;
|
||||||
|
if (dtSeg > 0.001f) {
|
||||||
|
glm::vec3 v = seg / dtSeg;
|
||||||
|
float speed = glm::length(v);
|
||||||
|
constexpr float kMinBootstrapSpeed = 0.25f;
|
||||||
|
constexpr float kMaxSpeed = 60.0f;
|
||||||
|
if (speed < kMinBootstrapSpeed) {
|
||||||
|
speed = 0.0f;
|
||||||
|
}
|
||||||
|
if (speed > kMaxSpeed) {
|
||||||
|
v *= (kMaxSpeed / speed);
|
||||||
|
}
|
||||||
|
if (speed >= kMinBootstrapSpeed) {
|
||||||
|
transport->serverLinearVelocity = v;
|
||||||
|
transport->serverAngularVelocity = 0.0f;
|
||||||
|
transport->hasServerVelocity = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glm::vec3 seg = path.points[nextIdx].pos - path.points[bestIdx].pos;
|
||||||
|
float dtSeg = static_cast<float>(t1 - t0) / 1000.0f;
|
||||||
|
if (dtSeg > 0.001f) {
|
||||||
|
glm::vec3 v = seg / dtSeg;
|
||||||
|
float speed = glm::length(v);
|
||||||
|
constexpr float kMinBootstrapSpeed = 0.25f;
|
||||||
|
constexpr float kMaxSpeed = 60.0f;
|
||||||
|
if (speed < kMinBootstrapSpeed) {
|
||||||
|
speed = 0.0f;
|
||||||
|
}
|
||||||
|
if (speed > kMaxSpeed) {
|
||||||
|
v *= (kMaxSpeed / speed);
|
||||||
|
}
|
||||||
|
if (speed >= kMinBootstrapSpeed) {
|
||||||
|
transport->serverLinearVelocity = v;
|
||||||
|
transport->serverAngularVelocity = 0.0f;
|
||||||
|
transport->hasServerVelocity = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (transport->hasServerVelocity) {
|
if (transport->hasServerVelocity) {
|
||||||
LOG_INFO("Transport 0x", std::hex, guid, std::dec,
|
LOG_INFO("Transport 0x", std::hex, guid, std::dec,
|
||||||
" bootstrapped velocity from DBC path ", transport->pathId,
|
" bootstrapped velocity from DBC path ", transport->pathId,
|
||||||
" v=(", transport->serverLinearVelocity.x, ", ",
|
" v=(", transport->serverLinearVelocity.x, ", ",
|
||||||
transport->serverLinearVelocity.y, ", ",
|
transport->serverLinearVelocity.y, ", ",
|
||||||
transport->serverLinearVelocity.z, ")");
|
transport->serverLinearVelocity.z, ")");
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Transport 0x", std::hex, guid, std::dec,
|
||||||
|
" skipped DBC bootstrap velocity (segment too short/static)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (!transport->allowBootstrapVelocity) {
|
||||||
|
LOG_INFO("Transport 0x", std::hex, guid, std::dec,
|
||||||
|
" DBC bootstrap velocity disabled for this transport");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue