mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +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> gameObjectDisplayIdWmoCache_; // displayId → WMO modelId
|
||||
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 nextGameObjectWmoModelId_ = 40000;
|
||||
bool gameObjectLookupsBuilt_ = false;
|
||||
|
|
@ -184,6 +191,7 @@ private:
|
|||
uint32_t mountInstanceId_ = 0;
|
||||
uint32_t mountModelId_ = 0;
|
||||
uint32_t pendingMountDisplayId_ = 0; // Deferred mount load (0 = none pending)
|
||||
bool weaponsSheathed_ = false;
|
||||
void processPendingMount();
|
||||
bool creatureLookupsBuilt_ = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ struct ActiveTransport {
|
|||
glm::vec3 serverLinearVelocity;
|
||||
float serverAngularVelocity;
|
||||
bool hasServerVelocity;
|
||||
bool allowBootstrapVelocity; // Disable DBC bootstrap when spawn/path mismatch is clearly invalid
|
||||
};
|
||||
|
||||
class TransportManager {
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@ void Application::logoutToLogin() {
|
|||
}
|
||||
npcsSpawned = false;
|
||||
playerCharacterSpawned = false;
|
||||
weaponsSheathed_ = false;
|
||||
world.reset();
|
||||
if (renderer) {
|
||||
// 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();
|
||||
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();
|
||||
if (world) {
|
||||
world->update(deltaTime);
|
||||
|
|
@ -1235,6 +1246,16 @@ void Application::setupUICallbacks() {
|
|||
// Server-authoritative movement - set initial position from spawn data
|
||||
glm::vec3 canonicalPos(x, y, z);
|
||||
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");
|
||||
});
|
||||
|
||||
|
|
@ -1329,13 +1350,15 @@ void Application::setupUICallbacks() {
|
|||
|
||||
transportManager->registerTransport(guid, wmoInstanceId, pathId, canonicalSpawnPos);
|
||||
} else {
|
||||
pendingTransportMoves_[guid] = PendingTransportMove{x, y, z, orientation};
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
pendingTransportMoves_[guid] = PendingTransportMove{x, y, z, orientation};
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1879,6 +1902,13 @@ void Application::loadEquippedWeapons() {
|
|||
{ game::EquipSlot::OFF_HAND, 2 },
|
||||
};
|
||||
|
||||
if (weaponsSheathed_) {
|
||||
for (const auto& ws : weaponSlots) {
|
||||
charRenderer->detachWeapon(charInstanceId, ws.attachmentId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& ws : weaponSlots) {
|
||||
const auto& equipSlot = inventory.getEquipSlot(ws.slot);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ void TransportManager::registerTransport(uint64_t guid, uint32_t wmoInstanceId,
|
|||
transport.guid = guid;
|
||||
transport.wmoInstanceId = wmoInstanceId;
|
||||
transport.pathId = pathId;
|
||||
transport.allowBootstrapVelocity = true;
|
||||
|
||||
// CRITICAL: Set basePosition from spawn position and t=0 offset
|
||||
// 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
|
||||
glm::vec3 firstWaypoint = path.points[0].pos;
|
||||
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,
|
||||
": firstWaypoint mismatch! spawnPos=(", spawnWorldPos.x, ",", spawnWorldPos.y, ",", spawnWorldPos.z, ")",
|
||||
" firstWaypoint=(", firstWaypoint.x, ",", firstWaypoint.y, ",", firstWaypoint.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
|
||||
|
|
@ -580,7 +589,7 @@ void TransportManager::updateServerTransport(uint64_t guid, const glm::vec3& pos
|
|||
// Bootstrap velocity from mapped DBC path on first authoritative sample.
|
||||
// This avoids "stalled at dock" when server sends sparse transport snapshots.
|
||||
auto pathIt2 = paths_.find(transport->pathId);
|
||||
if (pathIt2 != paths_.end()) {
|
||||
if (transport->allowBootstrapVelocity && pathIt2 != paths_.end()) {
|
||||
const auto& path = pathIt2->second;
|
||||
if (path.points.size() >= 2 && path.durationMs > 0) {
|
||||
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();
|
||||
size_t nextIdx = (bestIdx + 1) % n;
|
||||
uint32_t t0 = path.points[bestIdx].tMs;
|
||||
uint32_t t1 = path.points[nextIdx].tMs;
|
||||
if (nextIdx == 0 && t1 <= t0 && path.durationMs > 0) {
|
||||
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;
|
||||
}
|
||||
constexpr float kMaxBootstrapNearestDist = 80.0f;
|
||||
if (bestDistSq > (kMaxBootstrapNearestDist * kMaxBootstrapNearestDist)) {
|
||||
LOG_WARNING("Transport 0x", std::hex, guid, std::dec,
|
||||
" skipping DBC bootstrap velocity: nearest path point too far (dist=",
|
||||
std::sqrt(bestDistSq), ", path=", transport->pathId, ")");
|
||||
} 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 kMaxSpeed = 60.0f;
|
||||
if (speed > kMaxSpeed) {
|
||||
v *= (kMaxSpeed / speed);
|
||||
}
|
||||
transport->serverLinearVelocity = v;
|
||||
transport->serverAngularVelocity = 0.0f;
|
||||
transport->hasServerVelocity = true;
|
||||
size_t n = path.points.size();
|
||||
size_t nextIdx = (bestIdx + 1) % n;
|
||||
uint32_t t0 = path.points[bestIdx].tMs;
|
||||
uint32_t t1 = path.points[nextIdx].tMs;
|
||||
if (nextIdx == 0 && t1 <= t0 && path.durationMs > 0) {
|
||||
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 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) {
|
||||
LOG_INFO("Transport 0x", std::hex, guid, std::dec,
|
||||
" bootstrapped velocity from DBC path ", transport->pathId,
|
||||
" v=(", transport->serverLinearVelocity.x, ", ",
|
||||
transport->serverLinearVelocity.y, ", ",
|
||||
transport->serverLinearVelocity.z, ")");
|
||||
if (transport->hasServerVelocity) {
|
||||
LOG_INFO("Transport 0x", std::hex, guid, std::dec,
|
||||
" bootstrapped velocity from DBC path ", transport->pathId,
|
||||
" v=(", transport->serverLinearVelocity.x, ", ",
|
||||
transport->serverLinearVelocity.y, ", ",
|
||||
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