From cfe2dffa44377e0a8ba7e584a42e0120d8237143 Mon Sep 17 00:00:00 2001 From: Mazo Date: Mon, 21 May 2018 06:45:01 +0100 Subject: [PATCH 1/4] Fix implant terminals having disconnected characters permanently occupying seat --- pslogin/src/main/scala/WorldSessionActor.scala | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index bfbfe5cf..47e82190 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -171,12 +171,21 @@ class WorldSessionActor extends Actor with MDCContextAware { * @param player_guid the player */ def DismountVehicleOnLogOut(vehicle_guid : PlanetSideGUID, player_guid : PlanetSideGUID) : Unit = { - val vehicle = continent.GUID(vehicle_guid).get.asInstanceOf[Vehicle] - vehicle.Seat(vehicle.PassengerInSeat(player).get).get.Occupant = None + // Use mountable type instead of Vehicle type to ensure mountables such as implant terminals are correctly dismounted on logout + val mountable = continent.GUID(vehicle_guid).get.asInstanceOf[Mountable] + mountable.Seat(mountable.PassengerInSeat(player).get).get.Occupant = None + + // If this is a player constructed vehicle then start deconstruction timer + // todo: Will base guns implement Vehicle type? Don't want those to deconstruct + continent.GUID(vehicle_guid) match { + case Some(vehicle : Vehicle) => if(vehicle.Seats.values.count(_.isOccupied) == 0) { vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 600L) //start vehicle decay (10m) } - vehicleService ! Service.Leave(Some(s"${vehicle.Actor}")) + case _ => ; + } + + vehicleService ! Service.Leave(Some(s"${mountable.Actor}")) vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, 0, true, vehicle_guid)) } From 6f65603942bfb4affac8570b80dad14e56d37532 Mon Sep 17 00:00:00 2001 From: Mazo Date: Mon, 21 May 2018 06:47:21 +0100 Subject: [PATCH 2/4] Add BailType enumeration and update parameters for vehicle dismounting with correct naming and type --- .../packet/game/DismountVehicleMsg.scala | 11 ++++---- .../scala/net/psforever/types/BailType.scala | 15 ++++++++++ .../scala/game/DismountVehicleMsgTest.scala | 9 +++--- .../src/main/scala/WorldSessionActor.scala | 28 ++++++++++--------- .../services/vehicle/VehicleAction.scala | 4 +-- .../services/vehicle/VehicleResponse.scala | 6 ++-- .../services/vehicle/VehicleService.scala | 8 +++--- .../src/test/scala/VehicleServiceTest.scala | 4 +-- 8 files changed, 52 insertions(+), 33 deletions(-) create mode 100644 common/src/main/scala/net/psforever/types/BailType.scala diff --git a/common/src/main/scala/net/psforever/packet/game/DismountVehicleMsg.scala b/common/src/main/scala/net/psforever/packet/game/DismountVehicleMsg.scala index 991ef028..ed72f2e9 100644 --- a/common/src/main/scala/net/psforever/packet/game/DismountVehicleMsg.scala +++ b/common/src/main/scala/net/psforever/packet/game/DismountVehicleMsg.scala @@ -4,16 +4,17 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} import scodec.Codec import scodec.codecs._ +import net.psforever.types.BailType /** * Dispatched by the client when the player wishes to get out of a vehicle. * @param player_guid the player - * @param unk1 na + * @param bailType The dismount action e.g. normal dismount, kicked by owner, bailed * @param unk2 na */ final case class DismountVehicleMsg(player_guid : PlanetSideGUID, - unk1 : Int, //maybe, seat number? - unk2 : Boolean) //maybe, bailing? + bailType : BailType.Value, + wasKickedByDriver : Boolean) // Seems to be true if a passenger was manually kicked by the vehicle owner extends PlanetSideGamePacket { type Packet = DismountVehicleMsg def opcode = GamePacketOpcode.DismountVehicleMsg @@ -23,7 +24,7 @@ final case class DismountVehicleMsg(player_guid : PlanetSideGUID, object DismountVehicleMsg extends Marshallable[DismountVehicleMsg] { implicit val codec : Codec[DismountVehicleMsg] = ( ("player_guid" | PlanetSideGUID.codec) :: - ("unk1" | uint4L) :: - ("unk2" | bool) + ("bailType" | BailType.codec) :: + ("wasKickedByDriver" | bool) ).as[DismountVehicleMsg] } diff --git a/common/src/main/scala/net/psforever/types/BailType.scala b/common/src/main/scala/net/psforever/types/BailType.scala new file mode 100644 index 00000000..a2bff67f --- /dev/null +++ b/common/src/main/scala/net/psforever/types/BailType.scala @@ -0,0 +1,15 @@ +// Copyright (c) 2017 PSForever +package net.psforever.types + +import net.psforever.packet.PacketHelpers +import scodec.codecs._ + +object BailType extends Enumeration { + type Type = Value + + val Normal = Value(0) + val Kicked = Value(4) // User was kicked out by vehicle owner or locked from vehicle + val Bailed = Value(8) // User bailed out + + implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L) +} diff --git a/common/src/test/scala/game/DismountVehicleMsgTest.scala b/common/src/test/scala/game/DismountVehicleMsgTest.scala index 9af50103..2081509d 100644 --- a/common/src/test/scala/game/DismountVehicleMsgTest.scala +++ b/common/src/test/scala/game/DismountVehicleMsgTest.scala @@ -5,23 +5,24 @@ import org.specs2.mutable._ import net.psforever.packet._ import net.psforever.packet.game._ import scodec.bits._ +import net.psforever.types.BailType class DismountVehicleMsgTest extends Specification { val string = hex"0F C609 00" "decode" in { PacketCoding.DecodePacket(string).require match { - case DismountVehicleMsg(player_guid, unk1, unk2) => + case DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) => player_guid mustEqual PlanetSideGUID(2502) - unk1 mustEqual 0 - unk2 mustEqual false + bailType mustEqual 0 + wasKickedByDriver mustEqual false case _ => ko } } "encode" in { - val msg = DismountVehicleMsg(PlanetSideGUID(2502), 0, false) + val msg = DismountVehicleMsg(PlanetSideGUID(2502), BailType.Normal, false) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 47e82190..c792a49a 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -448,9 +448,9 @@ class WorldSessionActor extends Actor with MDCContextAware { //sendResponse(GenericObjectActionMessage(player_guid, 36)) sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1)) - case VehicleResponse.DismountVehicle(unk1, unk2) => + case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) => if(tplayer_guid != guid) { - sendResponse(DismountVehicleMsg(guid, unk1, unk2)) + sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver)) } case VehicleResponse.DeployRequest(object_guid, state, unk1, unk2, pos) => @@ -476,8 +476,10 @@ class WorldSessionActor extends Actor with MDCContextAware { ) } - case VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid) => - sendResponse(DismountVehicleMsg(guid, unk1, unk2)) + case msg @ VehicleResponse.KickPassenger(seat_num, wasKickedByDriver, vehicle_guid) => + // seat_num seems to be correct if passenger is kicked manually by driver, but always seems to return 4 if user is kicked by seat permissions + log.info(s"$msg") + sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver)) if(tplayer_guid == guid) { continent.GUID(vehicle_guid) match { case Some(obj : Vehicle) => @@ -655,8 +657,8 @@ class WorldSessionActor extends Actor with MDCContextAware { val obj_guid : PlanetSideGUID = obj.GUID val player_guid : PlanetSideGUID = tplayer.GUID log.info(s"DismountVehicleMsg: $player_guid dismounts $obj @ $seat_num") - sendResponse(DismountVehicleMsg(player_guid, seat_num, false)) - vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, seat_num, false)) + sendResponse(DismountVehicleMsg(player_guid, BailType.Normal, false)) + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, BailType.Normal, false)) case Mountable.CanDismount(obj : Vehicle, seat_num) => val player_guid : PlanetSideGUID = tplayer.GUID @@ -664,8 +666,8 @@ class WorldSessionActor extends Actor with MDCContextAware { //disembarking self log.info(s"DismountVehicleMsg: $player_guid dismounts $obj @ $seat_num") TotalDriverVehicleControl(obj) - sendResponse(DismountVehicleMsg(player_guid, seat_num, false)) - vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, seat_num, false)) + sendResponse(DismountVehicleMsg(player_guid, BailType.Normal, false)) + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, BailType.Normal, false)) UnAccessContents(obj) } else { @@ -1655,7 +1657,7 @@ class WorldSessionActor extends Actor with MDCContextAware { val player_guid = player.GUID sendResponse(ObjectDeleteMessage(player_guid, 0)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0)) - self ! PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, 0, true)) //let vehicle try to clean up its fields + self ! PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, BailType.Normal, true)) //let vehicle try to clean up its fields import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global @@ -2416,21 +2418,21 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ WarpgateRequest(continent_guid, building_guid, dest_building_guid, dest_continent_guid, unk1, unk2) => log.info("WarpgateRequest: " + msg) - case msg @ MountVehicleMsg(player_guid, mountable_guid, unk) => + case msg @ MountVehicleMsg(player_guid, mountable_guid, entry_point) => log.info("MountVehicleMsg: "+msg) continent.GUID(mountable_guid) match { case Some(obj : Mountable) => - obj.GetSeatFromMountPoint(unk) match { + obj.GetSeatFromMountPoint(entry_point) match { case Some(seat_num) => obj.Actor ! Mountable.TryMount(player, seat_num) case None => - log.warn(s"MountVehicleMsg: attempted to board mountable $mountable_guid's seat $unk, but no seat exists there") + log.warn(s"MountVehicleMsg: attempted to board mountable $mountable_guid's seat $entry_point, but no seat exists there") } case None | Some(_) => log.warn(s"MountVehicleMsg: not a mountable thing") } - case msg @ DismountVehicleMsg(player_guid, unk1, unk2) => + case msg @ DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) => //TODO optimize this later log.info(s"DismountVehicleMsg: $msg") //common warning for this section diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala index 66afa834..c4505065 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala @@ -6,7 +6,7 @@ import net.psforever.objects.equipment.Equipment import net.psforever.objects.zones.Zone import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.objectcreate.ConstructorData -import net.psforever.types.{DriveState, Vector3} +import net.psforever.types.{DriveState, Vector3, BailType} object VehicleAction { trait Action @@ -14,7 +14,7 @@ object VehicleAction { final case class Awareness(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action - final case class DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action + final case class DismountVehicle(player_guid : PlanetSideGUID, bailType : BailType.Value, unk2 : Boolean) extends Action final case class InventoryState(player_guid : PlanetSideGUID, obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Action final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action final case class LoadVehicle(player_guid : PlanetSideGUID, vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Action diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala index 5c2fb5b2..ce99e8e3 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala @@ -4,7 +4,7 @@ package services.vehicle import net.psforever.objects.{PlanetSideGameObject, Vehicle} import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.objectcreate.ConstructorData -import net.psforever.types.{DriveState, Vector3} +import net.psforever.types.{DriveState, Vector3, BailType} object VehicleResponse { trait Response @@ -15,9 +15,9 @@ object VehicleResponse { final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Response final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response final case class DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response - final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response + final case class DismountVehicle(bailType : BailType.Value , unk2 : Boolean) extends Response final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response - final case class KickPassenger(unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Response + final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response final case class LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response final case class ResetSpawnPad(pad_guid : PlanetSideGUID) extends Response diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala index e820db05..6f335aa5 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala @@ -52,17 +52,17 @@ class VehicleService extends Actor { VehicleEvents.publish( VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DeployRequest(object_guid, state, unk1, unk2, pos)) ) - case VehicleAction.DismountVehicle(player_guid, unk1, unk2) => + case VehicleAction.DismountVehicle(player_guid, bailType, unk2) => VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(unk1, unk2)) + VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(bailType, unk2)) ) case VehicleAction.InventoryState(player_guid, obj, parent_guid, start, con_data) => VehicleEvents.publish( VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data)) ) - case VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid) => + case VehicleAction.KickPassenger(player_guid, seat_num, kickedByDriver, vehicle_guid) => VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid)) + VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(seat_num, kickedByDriver, vehicle_guid)) ) case VehicleAction.LoadVehicle(player_guid, vehicle, vtype, vguid, vdata) => VehicleEvents.publish( diff --git a/pslogin/src/test/scala/VehicleServiceTest.scala b/pslogin/src/test/scala/VehicleServiceTest.scala index d8cd0d4f..82d0ba6e 100644 --- a/pslogin/src/test/scala/VehicleServiceTest.scala +++ b/pslogin/src/test/scala/VehicleServiceTest.scala @@ -114,8 +114,8 @@ class DismountVehicleTest extends ActorTest { "pass DismountVehicle" in { val service = system.actorOf(Props[VehicleService], "v-service") service ! Service.Join("test") - service ! VehicleServiceMessage("test", VehicleAction.DismountVehicle(PlanetSideGUID(10), 0, false)) - expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.DismountVehicle(0, false))) + service ! VehicleServiceMessage("test", VehicleAction.DismountVehicle(PlanetSideGUID(10), BailType.Normal, false)) + expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.DismountVehicle(BailType.Normal, false))) } } } From def736194c27e16d164de5f9dd0987f941530544 Mon Sep 17 00:00:00 2001 From: Mazo Date: Mon, 21 May 2018 06:48:18 +0100 Subject: [PATCH 3/4] Automatically despawn vehicles capable of flight if the driver bails out as a temporary fix for aircraft floating in the air with no pilot --- pslogin/src/main/scala/WorldSessionActor.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index c792a49a..3b1ce7b1 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -2448,6 +2448,14 @@ class WorldSessionActor extends Actor with MDCContextAware { obj.PassengerInSeat(player) match { case Some(seat_num : Int) => obj.Actor ! Mountable.TryDismount(player, seat_num) + + // 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. + if(bailType == BailType.Bailed && seat_num == 0 && GlobalDefinitions.isFlightVehicle(obj.asInstanceOf[Vehicle].Definition)) { + vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj.asInstanceOf[Vehicle], continent, 0L) // Immediately deconstruct vehicle + } + case None => dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in mountable $obj_guid") } From 1d732196f0a8768948a1c8139281f80087d41dc4 Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Mon, 21 May 2018 08:48:14 -0400 Subject: [PATCH 4/4] Update DismountVehicleMsgTest.scala 0 -> BailType.Normal Should have caught this in the prior merge. --- common/src/test/scala/game/DismountVehicleMsgTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/test/scala/game/DismountVehicleMsgTest.scala b/common/src/test/scala/game/DismountVehicleMsgTest.scala index 2081509d..f5851bbe 100644 --- a/common/src/test/scala/game/DismountVehicleMsgTest.scala +++ b/common/src/test/scala/game/DismountVehicleMsgTest.scala @@ -14,7 +14,7 @@ class DismountVehicleMsgTest extends Specification { PacketCoding.DecodePacket(string).require match { case DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) => player_guid mustEqual PlanetSideGUID(2502) - bailType mustEqual 0 + bailType mustEqual BailType.Normal wasKickedByDriver mustEqual false case _ => ko