From 4e5bb3a2529bda30349505e5a8112b2281fffcd4 Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Mon, 27 Apr 2020 21:31:53 -0400 Subject: [PATCH] Spawn Pad Fix (#394) * reset pad order queueing system if the player forces a system fix; blocking dismounting of vehicle in certain situations, such as the vehicle just being spawned * test fixes --- .../pad/VehicleSpawnControl.scala | 2 + .../VehicleSpawnControlConcealPlayer.scala | 2 +- .../VehicleSpawnControlDriverControl.scala | 1 + .../VehicleSpawnControlLoadVehicle.scala | 2 +- .../process/VehicleSpawnControlRailJack.scala | 1 + .../src/main/scala/WorldSessionActor.scala | 89 +++++++++++-------- .../actor/objects/VehicleSpawnPadTest.scala | 3 +- 7 files changed, 62 insertions(+), 38 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala index 13733f01e..11353e5c4 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala @@ -112,6 +112,8 @@ class VehicleSpawnControl(pad : VehicleSpawnPad) extends VehicleSpawnControlBase case None => ; } trackedOrder = None + handleOrderFunc = NewTasking + pad.Zone.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad) //cautious animation reset concealPlayer ! akka.actor.Kill //should cause the actor to restart, which will abort any trapped messages case _ => ; diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala index fe5350c92..06b234bf9 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala @@ -26,7 +26,7 @@ class VehicleSpawnControlConcealPlayer(pad : VehicleSpawnPad) extends VehicleSpa def receive : Receive = { case order @ VehicleSpawnControl.Order(driver, _) => //TODO how far can the driver stray from the Terminal before his order is cancelled? - if(driver.Continent == pad.Continent && driver.VehicleSeated.isEmpty) { + if(driver.Continent == pad.Continent && driver.VehicleSeated.isEmpty && driver.isAlive) { trace(s"hiding ${driver.Name}") pad.Zone.VehicleEvents ! VehicleSpawnPad.ConcealPlayer(driver.GUID) context.system.scheduler.scheduleOnce(2000 milliseconds, loadVehicle, order) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala index 1fa79b0de..dbe76c7ad 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala @@ -28,6 +28,7 @@ class VehicleSpawnControlDriverControl(pad : VehicleSpawnPad) extends VehicleSpa else { trace(s"${driver.Name} is not seated in ${vehicle.Definition.Name}; vehicle controls might have been locked") } + vehicle.MountedIn = None finalClear ! order case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) => diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala index a2ec92129..bc1477f83 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala @@ -28,7 +28,7 @@ class VehicleSpawnControlLoadVehicle(pad : VehicleSpawnPad) extends VehicleSpawn def receive : Receive = { case order @ VehicleSpawnControl.Order(driver, vehicle) => - if(driver.Continent == pad.Continent && vehicle.Health > 0) { + if(driver.Continent == pad.Continent && vehicle.Health > 0 && driver.isAlive) { trace(s"loading the ${vehicle.Definition.Name}") vehicle.Position = vehicle.Position - Vector3.z(if(GlobalDefinitions.isFlightVehicle(vehicle.Definition)) 9 else 5) //appear below the trench and doors vehicle.Cloaked = vehicle.Definition.CanCloak && driver.Cloaked diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala index 0621905eb..553dce4bd 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala @@ -26,6 +26,7 @@ class VehicleSpawnControlRailJack(pad : VehicleSpawnPad) extends VehicleSpawnCon def receive : Receive = { case order @ VehicleSpawnControl.Order(_, vehicle) => + vehicle.MountedIn = pad.GUID pad.Zone.VehicleEvents ! VehicleSpawnPad.AttachToRails(vehicle, pad) context.system.scheduler.scheduleOnce(10 milliseconds, seatDriver, order) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 27eabeb23..3de9e5a3e 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -5024,9 +5024,17 @@ class WorldSessionActor extends Actor // TODO: Make sure this is the correct response for all cases ValidObject(object_guid) match { case Some(vehicle : Vehicle) => - if((player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID)) || - (player.Faction == vehicle.Faction && - ((vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed))) { + /* line 1a: player is admin (and overrules other access requirements) */ + /* line 1b: vehicle and player (as the owner) acknowledge each other */ + /* line 1c: vehicle is the same faction as player and either the owner is absent or the vehicle is destroyed */ + /* line 2: vehicle is not mounted in anything or, if it is, its seats are empty */ + if( + (admin || + (player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID)) || + (player.Faction == vehicle.Faction && ((vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed)) + ) && + (vehicle.MountedIn.isEmpty || !vehicle.Seats.values.exists(_.isOccupied)) + ) { vehicle.Actor ! Vehicle.Deconstruct() log.info(s"RequestDestroy: vehicle $vehicle") } @@ -5995,45 +6003,56 @@ class WorldSessionActor extends Actor } if(player.GUID == player_guid) { //normally disembarking from a seat - player.VehicleSeated match { - case Some(obj_guid) => - interstellarFerry.orElse(continent.GUID(obj_guid)) match { - case Some(obj : Mountable) => - obj.PassengerInSeat(player) match { - case Some(0) if controlled.nonEmpty => - log.warn(s"DismountVehicleMsg: can not dismount from vehicle as driver while server has asserted control; please wait ...") - case Some(seat_num : Int) => - obj.Actor ! Mountable.TryDismount(player, seat_num) - if(interstellarFerry.isDefined) { - //short-circuit the temporary channel for transferring between zones, the player is no longer doing that - //see above in VehicleResponse.TransferPassenger case - interstellarFerry = None - } - // Deconstruct the vehicle if the driver has bailed out and the vehicle is capable of flight - //todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle - //todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct. - //todo: kick cargo passengers out. To be added after PR #216 is merged - obj match { - case v : Vehicle if bailType == BailType.Bailed && seat_num == 0 && v.Flying => - v.Actor ! Vehicle.Deconstruct() // Immediately deconstruct vehicle - case _ => ; - } - - case None => - dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in mountable $obj_guid") - } - case _ => - dismountWarning(s"DismountVehicleMsg: can not find mountable entity $obj_guid") - } - case None => + (interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match { + case out @ Some(obj : Vehicle) => + if(obj.MountedIn.isEmpty) out else None + case out @ Some(_ : Mountable) => + out + case _ => dismountWarning(s"DismountVehicleMsg: player $player_guid not considered seated in a mountable entity") + None + }) match { + case Some(obj : Mountable) => + obj.PassengerInSeat(player) match { + case Some(0) if controlled.nonEmpty => + log.warn(s"DismountVehicleMsg: can not dismount from vehicle as driver while server has asserted control; please wait ...") + case Some(seat_num : Int) => + obj.Actor ! Mountable.TryDismount(player, seat_num) + if(interstellarFerry.isDefined) { + //short-circuit the temporary channel for transferring between zones, the player is no longer doing that + //see above in VehicleResponse.TransferPassenger case + interstellarFerry = None + } + // Deconstruct the vehicle if the driver has bailed out and the vehicle is capable of flight + //todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle + //todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct. + //todo: kick cargo passengers out. To be added after PR #216 is merged + obj match { + case v : Vehicle if bailType == BailType.Bailed && seat_num == 0 && v.Flying => + continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent)) + continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(obj, continent, Some(0 seconds))) // Immediately deconstruct vehicle + case _ => ; + } + + case None => + dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in mountable ${player.VehicleSeated}") + } + case _ => + dismountWarning(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}") } } else { //kicking someone else out of a seat; need to own that seat/mountable player.VehicleOwned match { case Some(obj_guid) => - (ValidObject(obj_guid), ValidObject(player_guid)) match { + ((ValidObject(obj_guid), ValidObject(player_guid)) match { + case (vehicle @ Some(obj : Vehicle), tplayer) => + if(obj.MountedIn.isEmpty) (vehicle, tplayer) else (None, None) + case (mount @ Some(obj : Mountable), tplayer) => + (mount, tplayer) + case _ => + (None, None) + }) match { case (Some(obj : Mountable), Some(tplayer : Player)) => obj.PassengerInSeat(tplayer) match { case Some(seat_num : Int) => diff --git a/pslogin/src/test/scala/actor/objects/VehicleSpawnPadTest.scala b/pslogin/src/test/scala/actor/objects/VehicleSpawnPadTest.scala index 03d48ebd1..bf24f4d21 100644 --- a/pslogin/src/test/scala/actor/objects/VehicleSpawnPadTest.scala +++ b/pslogin/src/test/scala/actor/objects/VehicleSpawnPadTest.scala @@ -218,7 +218,7 @@ object VehicleSpawnPadControlTest { val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) val weapon = vehicle.WeaponControlledFromSeat(1).get.asInstanceOf[Tool] val guid : NumberPoolHub = new NumberPoolHub(LimitedNumberSource(5)) - guid.AddPool("test-pool", (0 to 3).toList) + guid.AddPool("test-pool", (0 to 5).toList) guid.register(vehicle, "test-pool") guid.register(weapon, "test-pool") guid.register(weapon.AmmoSlot.Box, "test-pool") @@ -241,6 +241,7 @@ object VehicleSpawnPadControlTest { pad.Owner = new Building("Building", building_guid = 0, map_id = 0, zone, StructureType.Building, GlobalDefinitions.building) pad.Owner.Faction = faction pad.Zone = zone + guid.register(pad, "test-pool") val player = Player(Avatar("test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)) guid.register(player, "test-pool") player.Zone = zone