From 2b8e879c2e522c53e71721c18fbe2a08c1922dc0 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 13 May 2020 21:24:46 -0400 Subject: [PATCH 1/4] restored vehicle decay upon a vehicle becoming unowned; PlayerSource should be much more simple --- .../scala/net/psforever/objects/Vehicle.scala | 10 ++++ .../net/psforever/objects/Vehicles.scala | 33 +++++++++++-- .../objects/ballistics/PlayerSource.scala | 5 +- .../objects/vehicles/VehicleControl.scala | 49 ++++++++++++++++--- .../src/main/scala/WorldSessionActor.scala | 19 ------- 5 files changed, 86 insertions(+), 30 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala index c5c1ed9a..6db09377 100644 --- a/common/src/main/scala/net/psforever/objects/Vehicle.scala +++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala @@ -605,6 +605,16 @@ object Vehicle { */ final case class UpdateShieldsCharge(vehicle : Vehicle) + /** + * Change a vehicle's internal ownership property to match that of the target player. + * @param player the person who will own the vehicle, or `None` if the vehicle will go unowned + */ + final case class Ownership(player : Option[Player]) + + object Ownership { + def apply(player : Player) : Ownership = Ownership(Some(player)) + } + /** * Overloaded constructor. * @param vehicleDef the vehicle's definition entry diff --git a/common/src/main/scala/net/psforever/objects/Vehicles.scala b/common/src/main/scala/net/psforever/objects/Vehicles.scala index 4725ed27..2ae6d910 100644 --- a/common/src/main/scala/net/psforever/objects/Vehicles.scala +++ b/common/src/main/scala/net/psforever/objects/Vehicles.scala @@ -41,6 +41,34 @@ object Vehicles { } } + /** + * Disassociate a vehicle fromthe player who owns it. + * @param guid the unique identifier for that vehicle + * @param vehicle the vehicle + * @return the vehicle, if it had a previous owner; + * `None`, otherwise + */ + def Disown(guid : PlanetSideGUID, vehicle : Vehicle) : Option[Vehicle] = vehicle.Zone.GUID(vehicle.Owner) match { + case Some(player : Player) => + if(player.VehicleOwned.contains(guid)) { + player.VehicleOwned = None + vehicle.Zone.VehicleEvents ! VehicleServiceMessage(player.Name, VehicleAction.Ownership(player.GUID, PlanetSideGUID(0))) + } + vehicle.AssignOwnership(None) + val empire = VehicleLockState.Empire.id + val factionChannel = s"${vehicle.Faction}" + (0 to 2).foreach(group => { + vehicle.PermissionGroup(group, empire) + vehicle.Zone.VehicleEvents ! VehicleServiceMessage(factionChannel, + VehicleAction.SeatPermissions(Service.defaultPlayerGUID, guid, group, empire) + ) + }) + ReloadAccessPermissions(vehicle, player.Name) + Some(vehicle) + case _ => + None + } + /** * Disassociate a player from a vehicle that he owns. * The vehicle must exist in the game world on the specified continent. @@ -82,13 +110,12 @@ object Vehicles { val pguid = player.GUID if(vehicle.Owner.contains(pguid)) { vehicle.AssignOwnership(None) - val factionChannel = s"${vehicle.Faction}" - vehicle.Zone.VehicleEvents ! VehicleServiceMessage(factionChannel, VehicleAction.Ownership(pguid, PlanetSideGUID(0))) + vehicle.Zone.VehicleEvents ! VehicleServiceMessage(player.Name, VehicleAction.Ownership(pguid, PlanetSideGUID(0))) val vguid = vehicle.GUID val empire = VehicleLockState.Empire.id (0 to 2).foreach(group => { vehicle.PermissionGroup(group, empire) - vehicle.Zone.VehicleEvents ! VehicleServiceMessage(factionChannel, VehicleAction.SeatPermissions(pguid, vguid, group, empire)) + vehicle.Zone.VehicleEvents ! VehicleServiceMessage(s"${vehicle.Faction}", VehicleAction.SeatPermissions(pguid, vguid, group, empire)) }) ReloadAccessPermissions(vehicle, player.Name) Some(vehicle) diff --git a/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala b/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala index e2a34fc2..c2204d37 100644 --- a/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala +++ b/common/src/main/scala/net/psforever/objects/ballistics/PlayerSource.scala @@ -2,7 +2,7 @@ package net.psforever.objects.ballistics import net.psforever.objects.Player -import net.psforever.objects.definition.ObjectDefinition +import net.psforever.objects.definition.{ExoSuitDefinition, ObjectDefinition} import net.psforever.objects.vital.resistance.ResistanceProfile import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3} @@ -35,6 +35,7 @@ final case class PlayerSource(name : String, object PlayerSource { def apply(tplayer : Player) : PlayerSource = { PlayerSource(tplayer.Name, tplayer.CharId, tplayer.Definition, tplayer.Faction, tplayer.ExoSuit, tplayer.VehicleSeated.nonEmpty, - tplayer.Health, tplayer.Armor, tplayer.Position, tplayer.Orientation, tplayer.Velocity, tplayer.asInstanceOf[ResistanceProfile]) + tplayer.Health, tplayer.Armor, tplayer.Position, tplayer.Orientation, tplayer.Velocity, + ExoSuitDefinition.Select(tplayer.ExoSuit, tplayer.Faction)) } } diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala index c0fa10ff..e475e8eb 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala @@ -2,7 +2,7 @@ package net.psforever.objects.vehicles import akka.actor.{Actor, ActorRef, Cancellable} -import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, SimpleItem, Vehicle, Vehicles} +import net.psforever.objects._ import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource} import net.psforever.objects.equipment.JammableMountedWeapons import net.psforever.objects.serverobject.CommonMessages @@ -73,11 +73,31 @@ class VehicleControl(vehicle : Vehicle) extends Actor .orElse(takesDamage) .orElse(canBeRepairedByNanoDispenser) .orElse { - case msg : Mountable.TryMount => + case Vehicle.Ownership(None) => + LoseOwnership() + + case Vehicle.Ownership(Some(player)) => + GainOwnership(player) + + case msg @ Mountable.TryMount(player, seat_num) => tryMountBehavior.apply(msg) - if(MountableObject.Seats.values.exists(_.isOccupied)) { - decaying = false - decayTimer.cancel + val obj = MountableObject + if(obj.Seats.values.exists(_.isOccupied)) { + if(seat_num == 0 && !obj.OwnerName.contains(player.Name)) { + //whatever vehicle was previously owned + vehicle.Zone.GUID(player.VehicleOwned) match { + case Some(v : Vehicle) => + v.Actor ! Vehicle.Ownership(None) + case _ => + player.VehicleOwned = None + } + LoseOwnership() //lose our current ownership + GainOwnership(player) //gain new ownership + } + else { + decaying = false + decayTimer.cancel + } } case msg : Mountable.TryDismount => @@ -88,7 +108,6 @@ class VehicleControl(vehicle : Vehicle) extends Actor if(!obj.Seats(0).isOccupied) { obj.Velocity = Some(Vector3.Zero) } - if(!decaying && obj.Owner.isEmpty && obj.Seats.values.forall(!_.isOccupied)) { decaying = true decayTimer = context.system.scheduler.scheduleOnce(MountableObject.Definition.DeconstructionTime.getOrElse(5 minutes), self, VehicleControl.PrepareForDeletion()) @@ -221,6 +240,24 @@ class VehicleControl(vehicle : Vehicle) extends Actor super.TryJammerEffectActivate(target, cause) } } + + def LoseOwnership() : Unit = { + val obj = MountableObject + Vehicles.Disown(obj.GUID, obj) + if(!decaying && obj.Seats.values.forall(!_.isOccupied)) { + decaying = true + decayTimer = context.system.scheduler.scheduleOnce(obj.Definition.DeconstructionTime.getOrElse(5 minutes), self, VehicleControl.PrepareForDeletion()) + } + } + + def GainOwnership(player : Player) : Unit = { + Vehicles.Own(MountableObject, player) match { + case Some(_) => + decaying = false + decayTimer.cancel + case None => ; + } + } } object VehicleControl { diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 66e8ab68..fa8e0093 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -792,9 +792,6 @@ class WorldSessionActor extends Actor failWithError(s"ListAccountCharacters: no connection - ${e.getMessage}") } - case VehicleLoaded(_ /*vehicle*/) => ; - //currently being handled by VehicleSpawnPad.LoadVehicle during testing phase - case Zone.ClientInitialization(zone) => Thread.sleep(connectionState) val continentNumber = zone.Number @@ -2291,19 +2288,6 @@ class WorldSessionActor extends Actor sendResponse(PlanetsideAttributeMessage(obj_guid, 113, capacitor)) } if(seat_num == 0) { - //simplistic vehicle ownership management - obj.Owner match { - case Some(owner_guid) => - continent.GUID(owner_guid) match { - case Some(previous_owner : Player) => - if(previous_owner.VehicleOwned.contains(obj_guid)) { - previous_owner.VehicleOwned = None //simplistic ownership management, player loses vehicle ownership - } - case _ => ; - } - case None => ; - } - Vehicles.Own(obj, tplayer) if(obj.Definition == GlobalDefinitions.quadstealth) { //wraith cloak state matches the cloak state of the driver //phantasm doesn't uncloak if the driver is uncloaked and no other vehicle cloaks @@ -6488,7 +6472,6 @@ class WorldSessionActor extends Actor TaskResolver.GiveTask( new Task() { private val localVehicle = vehicle - private val localAnnounce = self override def isComplete : Task.Resolution.Value = { if(localVehicle.HasGUID) { @@ -6502,7 +6485,6 @@ class WorldSessionActor extends Actor def Execute(resolver : ActorRef) : Unit = { log.info(s"Vehicle $localVehicle is registered") resolver ! scala.util.Success(this) - localAnnounce ! VehicleLoaded(localVehicle) //alerts WSA } }, List(GUIDTask.RegisterVehicle(vehicle)(continent.GUID)) ) @@ -11226,7 +11208,6 @@ object WorldSessionActor { private final case class CreateCharacter(name : String, head : Int, voice : CharacterVoice.Value, gender : CharacterGender.Value, empire : PlanetSideEmpire.Value) private final case class ListAccountCharacters() private final case class SetCurrentAvatar(tplayer : Player) - private final case class VehicleLoaded(vehicle : Vehicle) private final case class ZoningReset() final val ftes = ( From 6ef6d6834f9f4cf4d1209308546a0dc26feb16a6 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 13 May 2020 21:53:55 -0400 Subject: [PATCH 2/4] vehicle ownership change on zone change and on logout --- .../account/AccountPersistenceService.scala | 35 ++++--------------- .../src/main/scala/WorldSessionActor.scala | 8 +++++ 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/common/src/main/scala/services/account/AccountPersistenceService.scala b/common/src/main/scala/services/account/AccountPersistenceService.scala index bbc261f2..98d50227 100644 --- a/common/src/main/scala/services/account/AccountPersistenceService.scala +++ b/common/src/main/scala/services/account/AccountPersistenceService.scala @@ -277,7 +277,7 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver : AvatarLogout(avatar) inZone.GUID(avatar.VehicleOwned) match { case Some(obj : Vehicle) if obj.OwnerName.contains(avatar.name) => - obj.AssignOwnership(None) + obj.Actor ! Vehicle.Ownership(None) case _ => ; } taskResolver.tell(GUIDTask.UnregisterLocker(avatar.Locker)(inZone.GUID), context.parent) @@ -296,7 +296,6 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver : * @see `Avatar` * @see `AvatarAction.ObjectDelete` * @see `AvatarServiceMessage` - * @see `DisownVehicle` * @see `GUIDTask.UnregisterAvatar` * @see `Player` * @see `Zone.AvatarEvents` @@ -309,7 +308,11 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver : val parent = context.parent player.Position = Vector3.Zero player.Health = 0 - DisownVehicle(player) + inZone.GUID(player.VehicleOwned) match { + case Some(vehicle : Vehicle) if vehicle.OwnerName.contains(player.Name) => + vehicle.Actor ! Vehicle.Ownership(None) + case _ => ; + } inZone.Population.tell(Zone.Population.Release(avatar), parent) inZone.AvatarEvents.tell(AvatarServiceMessage(inZone.Id, AvatarAction.ObjectDelete(pguid, pguid)), parent) AvatarLogout(avatar) @@ -334,32 +337,6 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver : Deployables.Disown(inZone, avatar, parent) inZone.Population.tell(Zone.Population.Leave(avatar), parent) } - - /** - * Vehicle cleanup that is specific to log out behavior. - * @see `Vehicles.Disown` - * @see `RemoverActor.AddTask` - * @see `RemoverActor.ClearSpecific` - * @see `Vehicle.Flying` - * @see `VehicleDefinition.DeconstructionTime` - * @see `VehicleServiceMessage.Decon` - * @see `Zone.VehicleEvents` - */ - def DisownVehicle(player : Player) : Unit = { - Vehicles.Disown(player, inZone) match { - case Some(vehicle) if vehicle.Health == 0 || (vehicle.Seats.values.forall(seat => !seat.isOccupied) && vehicle.Owner.isEmpty) => - vehicle.Actor ! Vehicle.Deconstruct( - if(vehicle.Flying) { - //TODO gravity - None //immediate deconstruction - } - else { - vehicle.Definition.DeconstructionTime //normal deconstruction - } - ) - case _ => ; - } - } } /** diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index fa8e0093..5039370f 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -9889,6 +9889,14 @@ class WorldSessionActor extends Actor * It also sets up actions for the new zone loading process. */ def LoadZoneCommonTransferActivity() : Unit = { + if(player.VehicleOwned.nonEmpty && player.VehicleSeated != player.VehicleOwned) { + continent.GUID(player.VehicleOwned) match { + case Some(vehicle : Vehicle) => + vehicle.Actor ! Vehicle.Ownership(None) + case _ => ; + } + player.VehicleOwned = None + } RemoveBoomerTriggersFromInventory().foreach(obj => { taskResolver ! GUIDTask.UnregisterObjectTask(obj)(continent.GUID) }) From 8f339801cf1211498b275715bd1aec7719b1999b Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 13 May 2020 22:59:01 -0400 Subject: [PATCH 3/4] max exo-suits did not look like one another; revert to Standard upon final clean-up --- .../objects/definition/ExoSuitDefinition.scala | 14 ++++++++++++++ .../avatar/support/CorpseRemovalActor.scala | 6 +++++- .../src/test/scala/objects/PlayerControlTest.scala | 4 ++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala index da957db9..0c65b55b 100644 --- a/common/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/definition/ExoSuitDefinition.scala @@ -110,6 +110,20 @@ class ExoSuitDefinition(private val suitType : ExoSuitType.Value) extends BasicD } def Use : ExoSuitDefinition = this + + def canEqual(other: Any): Boolean = other.isInstanceOf[ExoSuitDefinition] + + override def equals(other: Any): Boolean = other match { + case that: ExoSuitDefinition => + (that canEqual this) && + suitType == that.suitType + case _ => false + } + + override def hashCode(): Int = { + val state = Seq(suitType) + state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b) + } } class SpecialExoSuitDefinition(private val suitType : ExoSuitType.Value) extends ExoSuitDefinition(suitType) { diff --git a/common/src/main/scala/services/avatar/support/CorpseRemovalActor.scala b/common/src/main/scala/services/avatar/support/CorpseRemovalActor.scala index b0aece43..5c601294 100644 --- a/common/src/main/scala/services/avatar/support/CorpseRemovalActor.scala +++ b/common/src/main/scala/services/avatar/support/CorpseRemovalActor.scala @@ -3,6 +3,7 @@ package services.avatar.support import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.Player +import net.psforever.types.ExoSuitType import services.{RemoverActor, Service} import services.avatar.{AvatarAction, AvatarServiceMessage} @@ -28,6 +29,9 @@ class CorpseRemovalActor extends RemoverActor { def ClearanceTest(entry : RemoverActor.Entry) : Boolean = !entry.zone.Corpses.contains(entry.obj) def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = { - GUIDTask.UnregisterPlayer(entry.obj.asInstanceOf[Player])(entry.zone.GUID) + val player = entry.obj.asInstanceOf[Player] + val task = GUIDTask.UnregisterPlayer(player)(entry.zone.GUID) + player.ExoSuit = ExoSuitType.Standard + task } } diff --git a/common/src/test/scala/objects/PlayerControlTest.scala b/common/src/test/scala/objects/PlayerControlTest.scala index 6092b610..2fa7419a 100644 --- a/common/src/test/scala/objects/PlayerControlTest.scala +++ b/common/src/test/scala/objects/PlayerControlTest.scala @@ -467,7 +467,7 @@ class PlayerControlDeathStandingTest extends ActorTest { assert( msg_avatar(7) match { case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _)) - if killer == player1Source && victim == PlayerSource(player2) => true + if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) => true case _ => false } ) @@ -585,7 +585,7 @@ class PlayerControlDeathSeatedTest extends ActorTest { assert( msg_avatar(8) match { case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _)) - if killer == player1Source && victim == PlayerSource(player2) => true + if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) => true case _ => false } ) From d3c45a2ae1aa6c239c1ba9316836a92f187a2449 Mon Sep 17 00:00:00 2001 From: FateJH Date: Thu, 14 May 2020 12:16:09 -0400 Subject: [PATCH 4/4] clarification that the player has actually sat down --- .../net/psforever/objects/vehicles/VehicleControl.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala index e475e8eb..6a62b822 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala @@ -82,7 +82,9 @@ class VehicleControl(vehicle : Vehicle) extends Actor case msg @ Mountable.TryMount(player, seat_num) => tryMountBehavior.apply(msg) val obj = MountableObject - if(obj.Seats.values.exists(_.isOccupied)) { + //check that the player has actually been sat in the expected seat + if(obj.PassengerInSeat(player).contains(seat_num)) { + //if the driver seat, change ownership if(seat_num == 0 && !obj.OwnerName.contains(player.Name)) { //whatever vehicle was previously owned vehicle.Zone.GUID(player.VehicleOwned) match { @@ -108,6 +110,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor if(!obj.Seats(0).isOccupied) { obj.Velocity = Some(Vector3.Zero) } + //are we already decaying? are we unowned? is no one seated anywhere? if(!decaying && obj.Owner.isEmpty && obj.Seats.values.forall(!_.isOccupied)) { decaying = true decayTimer = context.system.scheduler.scheduleOnce(MountableObject.Definition.DeconstructionTime.getOrElse(5 minutes), self, VehicleControl.PrepareForDeletion())