diff options
author | Machiavelli <machiavelli.trinity@gmail.com> | 2013-02-16 02:11:42 +0100 |
---|---|---|
committer | Machiavelli <machiavelli.trinity@gmail.com> | 2013-02-16 02:11:42 +0100 |
commit | 60cef65b5ca5f9550360a23eeb71a2478ca5af71 (patch) | |
tree | fb0e95843291172ea3785caa5dd22f91b75ceff1 | |
parent | a75fe0b2bfe76f82e771254b640d8509eff66468 (diff) |
Core/Vehicle: Make the passenger join event asynchronous
Prevents a crash in the following scenario:
Player 1 enters vehicle X
Seat on vehicle X is taken by Player 2
Player 2 is removed from vehicle X
Vehicle X dismisses (but player 1 did not even finish AddPasssenger
call)
Fixes #4271
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 2 | ||||
-rwxr-xr-x | src/server/game/Entities/Vehicle/Vehicle.cpp | 178 | ||||
-rw-r--r-- | src/server/game/Entities/Vehicle/Vehicle.h | 27 |
3 files changed, 139 insertions, 68 deletions
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 1a41c25d97c..62facf6a69e 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -332,6 +332,7 @@ class UnitAI; class Totem; class Transport; class Vehicle; +class VehicleJoinEvent; class TransportBase; class SpellCastTargets; @@ -2183,6 +2184,7 @@ class Unit : public WorldObject uint32 GetRedirectThreatPercent() { return _redirectThreadInfo.GetThreatPct(); } Unit* GetRedirectThreatTarget() { return _redirectThreadInfo.GetTargetGUID() ? GetUnit(*this, _redirectThreadInfo.GetTargetGUID()) : NULL; } + friend class VehicleJoinEvent; bool IsAIEnabled, NeedChangeAI; bool CreateVehicleKit(uint32 id, uint32 creatureEntry); void RemoveVehicleKit(); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 96f2b6d73dd..b6e47f239db 100755 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -30,9 +30,10 @@ #include "SpellInfo.h" #include "MoveSplineInit.h" #include "TemporarySummon.h" +#include "EventProcessor.h" Vehicle::Vehicle(Unit* unit, VehicleEntry const* vehInfo, uint32 creatureEntry) : -_me(unit), _vehicleInfo(vehInfo), _usableSeatNum(0), _creatureEntry(creatureEntry), _status(STATUS_NONE) +_me(unit), _vehicleInfo(vehInfo), UsableSeatNum(0), _creatureEntry(creatureEntry), _status(STATUS_NONE) { for (uint32 i = 0; i < MAX_VEHICLE_SEATS; ++i) { @@ -41,7 +42,7 @@ _me(unit), _vehicleInfo(vehInfo), _usableSeatNum(0), _creatureEntry(creatureEntr { Seats.insert(std::make_pair(i, VehicleSeat(veSeat))); if (veSeat->CanEnterOrExit()) - ++_usableSeatNum; + ++UsableSeatNum; } } @@ -123,10 +124,12 @@ void Vehicle::Uninstall() "Check Uninstall/PassengerBoarded script hooks for errors.", _me->GetGUIDLow(), _me->GetEntry()); return; } + _status = STATUS_UNINSTALLING; sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::Uninstall Entry: %u, GuidLow: %u", _creatureEntry, _me->GetGUIDLow()); RemoveAllPassengers(); + if (GetBase()->GetTypeId() == TYPEID_UNIT) sScriptMgr->OnUninstall(this); } @@ -136,14 +139,14 @@ void Vehicle::Reset(bool evading /*= false*/) sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::Reset Entry: %u, GuidLow: %u", _creatureEntry, _me->GetGUIDLow()); if (_me->GetTypeId() == TYPEID_PLAYER) { - if (_usableSeatNum) + if (UsableSeatNum) _me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); } else { ApplyAllImmunities(); InstallAllAccessories(evading); - if (_usableSeatNum) + if (UsableSeatNum) _me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); } @@ -215,6 +218,19 @@ void Vehicle::RemoveAllPassengers() // ASSERT(!itr->second.passenger); } +void Vehicle::RemovePendingPassengers() +{ + sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::RemovePendingPassengers. Entry: %u, GuidLow: %u", _creatureEntry, _me->GetGUIDLow()); + + /// Setting to_Abort to true will cause @VehicleJoinEvent::Abort to be executed on next @Unit::UpdateEvents call + /// This will properly "reset" the pending join process for the passenger. + while (VehicleJoinEvent* e = _pendingJoinEvents.front()) + { + e->to_Abort = true; + _pendingJoinEvents.pop_front(); + } +} + bool Vehicle::HasEmptySeat(int8 seatId) const { SeatMap::const_iterator seat = Seats.find(seatId); @@ -317,7 +333,17 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) if (unit->GetVehicle() != this) return false; + // The seat selection code may kick other passengers off the vehicle. + // While the validity of the following may be arguable, it is possible that when such a passenger + // exits the vehicle will dismiss. That's why the actual adding the passenger to the vehicle is scheduled + // asynchronously, so it can be cancelled easily in case the vehicle is uninstalled meanwhile. SeatMap::iterator seat; + sLog->outDebug(LOG_FILTER_VEHICLES, "Unit %s scheduling enter vehicle entry %u id %u dbguid %u seat %d", + unit->GetName().c_str(), _me->GetEntry(), _vehicleInfo->m_ID, _me->GetGUIDLow(), (int32)seat->first); + VehicleJoinEvent* e = new VehicleJoinEvent(this, unit, seat); + unit->m_Events.AddEvent(e, unit->m_Events.CalculateTime(0)); + _pendingJoinEvents.push_back(e); + if (seatId < 0) // no specific seat requirement { for (seat = Seats.begin(); seat != Seats.end(); ++seat) @@ -325,13 +351,19 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) break; if (seat == Seats.end()) // no available seat + { + CancelJoinEvent(e); return false; + } } else { seat = Seats.find(seatId); if (seat == Seats.end()) + { + CancelJoinEvent(e); return false; + } if (seat->second.Passenger) { @@ -344,65 +376,6 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) ASSERT(!seat->second.Passenger); } - sLog->outDebug(LOG_FILTER_VEHICLES, "Unit %s enter vehicle entry %u id %u dbguid %u seat %d", unit->GetName().c_str(), _me->GetEntry(), _vehicleInfo->m_ID, _me->GetGUIDLow(), (int32)seat->first); - - seat->second.Passenger = unit->GetGUID(); - if (seat->second.SeatInfo->CanEnterOrExit()) - { - ASSERT(_usableSeatNum); - --_usableSeatNum; - if (!_usableSeatNum) - { - if (_me->GetTypeId() == TYPEID_PLAYER) - _me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); - else - _me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - } - } - - if (seat->second.SeatInfo->m_flags && !(seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING)) - unit->AddUnitState(UNIT_STATE_ONVEHICLE); - - unit->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); - VehicleSeatEntry const* veSeat = seat->second.SeatInfo; - unit->m_movementInfo.t_pos.m_positionX = veSeat->m_attachmentOffsetX; - unit->m_movementInfo.t_pos.m_positionY = veSeat->m_attachmentOffsetY; - unit->m_movementInfo.t_pos.m_positionZ = veSeat->m_attachmentOffsetZ; - unit->m_movementInfo.t_pos.SetOrientation(0); - unit->m_movementInfo.t_time = 0; // 1 for player - unit->m_movementInfo.t_seat = seat->first; - unit->m_movementInfo.t_guid = _me->GetGUID(); - - if (_me->GetTypeId() == TYPEID_UNIT && unit->GetTypeId() == TYPEID_PLAYER && - seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_CAN_CONTROL) - ASSERT(_me->SetCharmedBy(unit, CHARM_TYPE_VEHICLE)) - - if (_me->IsInWorld()) - { - unit->SendClearTarget(); // SMSG_BREAK_TARGET - unit->SetControlled(true, UNIT_STATE_ROOT); // SMSG_FORCE_ROOT - In some cases we send SMSG_SPLINE_MOVE_ROOT here (for creatures) - // also adds MOVEMENTFLAG_ROOT - Movement::MoveSplineInit init(unit); - init.DisableTransportPathTransformations(); - init.MoveTo(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ); - init.SetFacing(0.0f); - init.SetTransportEnter(); - init.Launch(); - - if (_me->GetTypeId() == TYPEID_UNIT) - { - if (_me->ToCreature()->IsAIEnabled) - _me->ToCreature()->AI()->PassengerBoarded(unit, seat->first, true); - - // update all passenger's positions - //Passenger's spline OR vehicle movement will update positions - //RelocatePassengers(_me->GetPositionX(), _me->GetPositionY(), _me->GetPositionZ(), _me->GetOrientation()); - } - } - - if (GetBase()->GetTypeId() == TYPEID_UNIT) - sScriptMgr->OnAddPassenger(this, unit, seatId); - return true; } @@ -419,14 +392,14 @@ void Vehicle::RemovePassenger(Unit* unit) seat->second.Passenger = 0; if (seat->second.SeatInfo->CanEnterOrExit()) { - if (!_usableSeatNum) + if (!UsableSeatNum) { if (_me->GetTypeId() == TYPEID_PLAYER) _me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); else _me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); } - ++_usableSeatNum; + ++UsableSeatNum; } unit->ClearUnitState(UNIT_STATE_ONVEHICLE); @@ -549,3 +522,78 @@ void Vehicle::CalculatePassengerOffset(float& x, float& y, float& z, float& o) y = (iny - inx * tan(GetBase()->GetOrientation())) / (cos(GetBase()->GetOrientation()) + std::sin(GetBase()->GetOrientation()) * tan(GetBase()->GetOrientation())); x = (inx + iny * tan(GetBase()->GetOrientation())) / (cos(GetBase()->GetOrientation()) + std::sin(GetBase()->GetOrientation()) * tan(GetBase()->GetOrientation())); } + +void Vehicle::CancelJoinEvent(VehicleJoinEvent* e) +{ + e->to_Abort = true; + _pendingJoinEvents.pop_back(); +} + +bool VehicleJoinEvent::Execute(uint64, uint32) +{ + ASSERT(Passenger->GetVehicle() == Target); + + Seat->second.Passenger = Passenger->GetGUID(); + if (Seat->second.SeatInfo->CanEnterOrExit()) + { + ASSERT(Target->UsableSeatNum); + --(Target->UsableSeatNum); + if (!Target->UsableSeatNum) + { + if (Target->GetBase()->GetTypeId() == TYPEID_PLAYER) + Target->GetBase()->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); + else + Target->GetBase()->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + } + } + + if (Seat->second.SeatInfo->m_flags && !(Seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING)) + Passenger->AddUnitState(UNIT_STATE_ONVEHICLE); + + Passenger->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + VehicleSeatEntry const* veSeat = Seat->second.SeatInfo; + Passenger->m_movementInfo.t_pos.m_positionX = veSeat->m_attachmentOffsetX; + Passenger->m_movementInfo.t_pos.m_positionY = veSeat->m_attachmentOffsetY; + Passenger->m_movementInfo.t_pos.m_positionZ = veSeat->m_attachmentOffsetZ; + Passenger->m_movementInfo.t_pos.SetOrientation(0.0f); + Passenger->m_movementInfo.t_time = 0; // 1 for player + Passenger->m_movementInfo.t_seat = Seat->first; + Passenger->m_movementInfo.t_guid = Target->GetBase()->GetGUID(); + + if (Target->GetBase()->GetTypeId() == TYPEID_UNIT && Passenger->GetTypeId() == TYPEID_PLAYER && + Seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_CAN_CONTROL) + ASSERT(Target->GetBase()->SetCharmedBy(Passenger, CHARM_TYPE_VEHICLE)) + + if (Target->GetBase()->IsInWorld()) + { + Passenger->SendClearTarget(); // SMSG_BREAK_TARGET + Passenger->SetControlled(true, UNIT_STATE_ROOT); // SMSG_FORCE_ROOT - In some cases we send SMSG_SPLINE_MOVE_ROOT here (for creatures) + // also adds MOVEMENTFLAG_ROOT + Movement::MoveSplineInit init(Passenger); + init.DisableTransportPathTransformations(); + init.MoveTo(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ); + init.SetFacing(0.0f); + init.SetTransportEnter(); + init.Launch(); + + if (Target->GetBase()->GetTypeId() == TYPEID_UNIT) + { + if (Target->GetBase()->ToCreature()->IsAIEnabled) + Target->GetBase()->ToCreature()->AI()->PassengerBoarded(Passenger, Seat->first, true); + + // update all passenger's positions + //Passenger's spline OR vehicle movement will update positions + //RelocatePassengers(_me->GetPositionX(), _me->GetPositionY(), _me->GetPositionZ(), _me->GetOrientation()); + } + } + + if (Target->GetBase()->GetTypeId() == TYPEID_UNIT) + sScriptMgr->OnAddPassenger(Target, Passenger, Seat->first); + + return true; +} + +void VehicleJoinEvent::Abort(uint64) +{ + Passenger->m_vehicle = NULL; +} diff --git a/src/server/game/Entities/Vehicle/Vehicle.h b/src/server/game/Entities/Vehicle/Vehicle.h index 823fb72b8a8..f8fa7f9b64a 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.h +++ b/src/server/game/Entities/Vehicle/Vehicle.h @@ -23,12 +23,12 @@ #include "Object.h" #include "VehicleDefines.h" #include "Unit.h" +#include <deque> struct VehicleEntry; - class Unit; - typedef std::set<uint64> GuidSet; +class VehicleJoinEvent; class Vehicle : public TransportBase { @@ -54,6 +54,7 @@ class Vehicle : public TransportBase void RemovePassenger(Unit* passenger); void RelocatePassengers(); void RemoveAllPassengers(); + void RemovePendingPassengers(); void Dismiss(); void TeleportVehicle(float x, float y, float z, float ang); bool IsVehicleInUse() { return Seats.begin() != Seats.end(); } @@ -66,6 +67,10 @@ class Vehicle : public TransportBase VehicleSeatEntry const* GetSeatForPassenger(Unit* passenger); protected: + friend class VehicleJoinEvent; + uint32 UsableSeatNum; // Number of seats that match VehicleSeatEntry::UsableByPlayer, used for proper display flags + + protected: friend bool Unit::CreateVehicleKit(uint32 id, uint32 creatureEntry); Vehicle(Unit* unit, VehicleEntry const* vehInfo, uint32 creatureEntry); friend void Unit::RemoveVehicleKit(); @@ -91,9 +96,25 @@ class Vehicle : public TransportBase Unit* _me; VehicleEntry const* _vehicleInfo; GuidSet vehiclePlayers; - uint32 _usableSeatNum; // Number of seats that match VehicleSeatEntry::UsableByPlayer, used for proper display flags + uint32 _creatureEntry; // Can be different than me->GetBase()->GetEntry() in case of players Status _status; Position m_lastShootPos; + + std::deque<VehicleJoinEvent*> _pendingJoinEvents; + void CancelJoinEvent(VehicleJoinEvent* e); +}; + +class VehicleJoinEvent : public BasicEvent +{ + friend class Vehicle; + protected: + VehicleJoinEvent(Vehicle* v, Unit* u, SeatMap::iterator& itr) : Target(v), Passenger(u), Seat(itr) {} + bool Execute(uint64, uint32); + void Abort(uint64); + + Vehicle* Target; + Unit* Passenger; + SeatMap::iterator& Seat; }; #endif |