mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-20 09:17:36 +01:00
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
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(); }
|
||||
@@ -65,6 +66,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);
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user