From b292739b5468cb780020ce083f7ff0709b376f92 Mon Sep 17 00:00:00 2001 From: FateJH Date: Fri, 5 Jan 2018 21:04:55 -0500 Subject: [PATCH] synchronized weapon fire, weapon dry fire, reload cycle, and reload of ammunition from inventory contents --- .../src/main/scala/WorldSessionActor.scala | 226 ++++++++++++++++-- .../scala/services/avatar/AvatarAction.scala | 5 +- .../services/avatar/AvatarResponse.scala | 5 +- .../scala/services/avatar/AvatarService.scala | 16 +- .../services/vehicle/VehicleAction.scala | 3 +- .../services/vehicle/VehicleResponse.scala | 3 +- .../services/vehicle/VehicleService.scala | 6 + .../main/test/scala/AvatarServiceTest.scala | 4 +- 8 files changed, 237 insertions(+), 31 deletions(-) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 9beb218ac..8d1566fc6 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -23,11 +23,11 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState} import net.psforever.objects.zones.{InterstellarCluster, Zone} -import net.psforever.packet.game.objectcreate.{DetailedCharacterData, _} +import net.psforever.packet.game.objectcreate._ import net.psforever.types._ import services._ -import services.avatar._ -import services.local._ +import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse} +import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse} import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse} import scala.annotation.tailrec @@ -47,6 +47,7 @@ class WorldSessionActor extends Actor with MDCContextAware { var galaxy : ActorRef = Actor.noSender var continent : Zone = null var progressBarValue : Option[Float] = None + var shooting : Option[PlanetSideGUID] = None var clientKeepAlive : Cancellable = DefaultCancellable.obj var progressBarUpdate : Cancellable = DefaultCancellable.obj @@ -145,6 +146,16 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, ArmorChangedMessage(guid, suit, subtype))) } + case AvatarResponse.ChangeFireState_Start(weapon_guid) => + if(player.GUID != guid) { + sendResponse(PacketCoding.CreateGamePacket(0, ChangeFireStateMessage_Start(weapon_guid))) + } + + case AvatarResponse.ChangeFireState_Stop(weapon_guid) => + if(player.GUID != guid) { + sendResponse(PacketCoding.CreateGamePacket(0, ChangeFireStateMessage_Stop(weapon_guid))) + } + case AvatarResponse.ConcealPlayer() => if(player.GUID != guid) { sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectActionMessage(guid, 36))) @@ -215,7 +226,7 @@ class WorldSessionActor extends Actor with MDCContextAware { ((distanceSq < 900 || weaponInHand) && time > 200) || (distanceSq < 10000 && time > 500) || (distanceSq < 160000 && (msg.is_jumping || time < 200)) || - (distanceSq < 160000 && msg.vel.isEmpty && time > 2000) || + (distanceSq < 160000 && (msg.vel.isEmpty || Vector3.MagnitudeSquared(msg.vel.get).toInt == 0) && time > 2000) || (distanceSq < 160000 && time > 1000) || (distanceSq > 160000 && time > 5000)) { @@ -240,9 +251,14 @@ class WorldSessionActor extends Actor with MDCContextAware { } } - case AvatarResponse.Reload(mag) => + case AvatarResponse.Reload(item_guid) => if(player.GUID != guid) { - sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(guid, mag, 0))) + sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(item_guid, 1, 0))) + } + + case AvatarResponse.WeaponDryFire(weapon_guid) => + if(player.GUID != guid) { + sendResponse(PacketCoding.CreateGamePacket(0, WeaponJammedMessage(weapon_guid))) } case _ => ; @@ -283,6 +299,20 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, ChildObjectStateMessage(object_guid, pitch, yaw))) } + case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) => + if(player.GUID != guid) { + //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? + val obj_guid = obj.GUID + sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(obj_guid, 0))) + sendResponse(PacketCoding.CreateGamePacket(0, + ObjectCreateDetailedMessage( + obj.Definition.ObjectId, + obj_guid, + ObjectCreateMessageParent(parent_guid, start), + con_data) + )) + } + case VehicleResponse.DismountVehicle(unk1, unk2) => if(player.GUID != guid) { sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2))) @@ -1306,10 +1336,19 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ ChangeFireStateMessage_Start(item_guid) => log.info("ChangeFireState_Start: " + msg) + if(shooting.isEmpty) { + //TODO check that player can shoot item_guid? + shooting = Some(item_guid) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Start(player.GUID, item_guid)) + } case msg @ ChangeFireStateMessage_Stop(item_guid) => log.info("ChangeFireState_Stop: " + msg) - progressBarUpdate.cancel + if(shooting.contains(item_guid)) { + shooting = None + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, item_guid)) + } + progressBarUpdate.cancel //TODO independent action? case msg @ EmoteMsg(avatar_guid, emote) => log.info("Emote: " + msg) @@ -1339,7 +1378,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ ReloadMessage(item_guid, ammo_clip, unk1) => log.info("Reload: " + msg) - val reloadValue : Int = player.VehicleSeated match { + (player.VehicleSeated match { case Some(vehicle_guid) => //weapon is vehicle turret? continent.GUID(vehicle_guid) match { case Some(vehicle : Vehicle) => @@ -1347,29 +1386,68 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(seat_num) => vehicle.WeaponControlledFromSeat(seat_num) match { case Some(item : Tool) => - item.FireMode.Magazine + //TODO check that item controlled by seat is item_guid? + (Some(vehicle), Some(item)) case _ => ; - 0 + (None, None) } case None => ; - 0 + (None, None) } case _ => ; - 0 + (None, None) } case None => //not in vehicle; weapon in hand? - log.info(s"${player.DrawnSlot} -> ${player.Slot(player.DrawnSlot).Equipment}") player.Slot(player.DrawnSlot).Equipment match { //TODO check that item in hand is item_guid? case Some(item : Tool) => - item.FireMode.Magazine - case Some(_) | None => ; - 0 + (Some(player), Some(item)) + case _ => ; + (None, None) } - } - if(reloadValue > 0) { - //TODO hunt for ammunition in backpack/trunk - sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(item_guid, reloadValue, unk1))) + }) match { + case (Some(obj), Some(tool : Tool)) => + val currentMagazine : Int = tool.Magazine + val magazineSize : Int = tool.MaxMagazine + val reloadValue : Int = magazineSize - currentMagazine + if(magazineSize > 0 && reloadValue > 0) { + FindReloadAmmunition(obj, tool.AmmoType, reloadValue).reverse match { + case Nil => + log.warn(s"ReloadMessage: no ammunition could be found for $item_guid") + case list @ x :: xs => + val (deleteFunc, modifyFunc) : ((Int, PlanetSideGUID)=>Unit, (AmmoBox, Int)=>Unit) = obj match { + case (veh : Vehicle) => + (DeleteAmmunitionInVehicle(veh), ModifyAmmunitionInVehicle(veh)) + case _ => + (DeleteAmmunition(obj), ModifyAmmunition(obj)) + } + xs.foreach(item => { + deleteFunc(item.start, item.obj.GUID) + }) + val box = x.obj.asInstanceOf[AmmoBox] + val tailReloadValue : Int = if(xs.isEmpty) { 0 } else { xs.map(_.obj.asInstanceOf[AmmoBox].Capacity).reduceLeft(_ + _) } + val sumReloadValue : Int = box.Capacity + tailReloadValue + val actualReloadValue = (if(sumReloadValue <= reloadValue) { + deleteFunc(x.start, box.GUID) + sumReloadValue + } + else { + modifyFunc(box, reloadValue - tailReloadValue) + reloadValue + }) + currentMagazine + log.info(s"ReloadMessage: success, $tool <- $actualReloadValue ${tool.AmmoType}") + tool.Magazine = actualReloadValue + sendResponse(PacketCoding.CreateGamePacket(0, ReloadMessage(item_guid, actualReloadValue, unk1))) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Reload(player.GUID, item_guid)) + } + } + else { + log.warn(s"ReloadMessage: item $item_guid can not reload (full=$magazineSize, want=$reloadValue)") + } + case (_, Some(_)) => + log.error(s"ReloadMessage: the object that was found for $item_guid was not a Tool") + case (_, None) => + log.error(s"ReloadMessage: can not find $item_guid") } case msg @ ObjectHeldMessage(avatar_guid, held_holsters, unk1) => @@ -1673,9 +1751,60 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ WeaponDelayFireMessage(seq_time, weapon_guid) => log.info("WeaponDelayFire: " + msg) + (player.VehicleSeated match { + case Some(vehicle_guid) => + continent.GUID(vehicle_guid) match { + case Some(obj : Vehicle) => + obj.PassengerInSeat(player) match { + case Some(seat_num) => + obj.WeaponControlledFromSeat(seat_num) + case None => + None + } + case _ => + None + } + case None => + player.Slot(player.DrawnSlot).Equipment + }) match { + case Some(tool : Tool) => + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.WeaponDryFire(player.GUID, weapon_guid)) + case _ => ; + } case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) => log.info("WeaponFire: " + msg) + (player.VehicleSeated match { + case Some(vehicle_guid) => + continent.GUID(vehicle_guid) match { + case Some(obj : Vehicle) => + obj.PassengerInSeat(player) match { + case Some(seat_num) => + obj.WeaponControlledFromSeat(seat_num) + case None => + None + } + case _ => + None + } + case None => + player.Slot(player.DrawnSlot).Equipment + }) match { + case Some(tool : Tool) => + if(tool.Magazine <= 0) { //safety: enforce ammunition depletion + tool.Magazine = 0 + sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(tool.AmmoSlot.Box.GUID, weapon_guid, 0))) + sendResponse(PacketCoding.CreateGamePacket(0, ChangeFireStateMessage_Stop(weapon_guid))) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, weapon_guid)) + sendResponse(PacketCoding.CreateGamePacket(0, WeaponDryFireMessage(weapon_guid))) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.WeaponDryFire(player.GUID, weapon_guid)) + } + else { //shooting + tool.Magazine = tool.Magazine - 1 + //TODO other stuff + } + case _ => ; + } case msg @ WeaponLazeTargetPositionMessage(weapon, pos1, pos2) => log.info("Lazing position: " + pos2.toString) @@ -1727,9 +1856,7 @@ class WorldSessionActor extends Actor with MDCContextAware { val seats = obj.Seats.values seats.find(seat => seat.Occupant.contains(player)) match { case Some(seat) => - val vel = obj.Velocity.getOrElse(Vector3(0f, 0f, 0f)) - val has_vel : Int = math.abs(vel.x * vel.y * vel.z).toInt - if(seat.Bailable || obj.Velocity.isEmpty || has_vel == 0) { //ugh, float comparison + if(seat.Bailable || obj.Velocity.isEmpty || Vector3.MagnitudeSquared(obj.Velocity.get).toInt == 0) { //ugh, float comparison seat.Occupant = None //special actions obj match { @@ -2355,6 +2482,59 @@ class WorldSessionActor extends Actor with MDCContextAware { }) } + /** + * Within a specified `Container`, find the smallest number of `AmmoBox` objects of a certain type of `Ammo` + * whose sum capacities is greater than, or equal to, a `desiredAmount`.
+ *
+ * In an occupied `List` of returned `Inventory` entries, all but the last entry is considered emptied. + * The last entry may require having its `Capacity` be set to a non-zero number. + * @param obj the `Container` to search + * @param ammoType the type of `Ammo` to search for + * @param desiredAmount how much ammunition is requested to be found + * @return a `List` of all discovered entries totaling approximately the amount of the requested `Ammo` + */ + def FindReloadAmmunition(obj : Container, ammoType : Ammo.Value, desiredAmount : Int) : List[InventoryItem] = { + var currentAmount : Int = 0 + obj.Inventory.Items + .map({ case ((_, item)) => item }) + .filter(obj => { + obj.obj match { + case (box : AmmoBox) => + box.AmmoType == ammoType + case _ => + false + } + }) + .toList + .sortBy(_.start) + .takeWhile(entry => { + val previousAmount = currentAmount + currentAmount += entry.obj.asInstanceOf[AmmoBox].Capacity + previousAmount < desiredAmount + }) + } + + private def DeleteAmmunition(obj : PlanetSideGameObject with Container)(start : Int, item_guid : PlanetSideGUID) : Unit = { + obj.Inventory -= start + sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(item_guid, 0))) + } + + private def DeleteAmmunitionInVehicle(obj : Vehicle)(start : Int, item_guid : PlanetSideGUID) : Unit = { + DeleteAmmunition(obj)(start, item_guid) + vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player.GUID, item_guid)) + } + + private def ModifyAmmunition(obj : PlanetSideGameObject with Container)(box : AmmoBox, reloadValue : Int) : Unit = { + val capacity = box.Capacity - reloadValue + box.Capacity = capacity + sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(box.GUID, obj.GUID, capacity))) + } + + private def ModifyAmmunitionInVehicle(obj : Vehicle)(box : AmmoBox, reloadValue : Int) : Unit = { + val capacity = ModifyAmmunition(obj)(box, reloadValue) + vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.InventoryState(player.GUID, box, obj.GUID, obj.Find(box).get, box.Definition.Packet.DetailedConstructorData(box).get)) + } + /** * A predicate used to determine if an `InventoryItem` object contains `Equipment` that should be dropped. * Used to filter through lists of object data before it is placed into a player's inventory. diff --git a/pslogin/src/main/scala/services/avatar/AvatarAction.scala b/pslogin/src/main/scala/services/avatar/AvatarAction.scala index 3b9f54e65..ef0d4926d 100644 --- a/pslogin/src/main/scala/services/avatar/AvatarAction.scala +++ b/pslogin/src/main/scala/services/avatar/AvatarAction.scala @@ -10,6 +10,8 @@ object AvatarAction { trait Action final case class ArmorChanged(player_guid : PlanetSideGUID, suit : ExoSuitType.Value, subtype : Int) extends Action + final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action + final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action //final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Action final case class EquipmentInHand(player_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action @@ -21,7 +23,8 @@ object AvatarAction { final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action - final case class Reload(player_guid : PlanetSideGUID, mag : Int) extends Action + final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action + final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action // final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action // final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action // final case class HitHintReturn(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action diff --git a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala index abcf6246b..0a8928254 100644 --- a/pslogin/src/main/scala/services/avatar/AvatarResponse.scala +++ b/pslogin/src/main/scala/services/avatar/AvatarResponse.scala @@ -10,6 +10,8 @@ object AvatarResponse { trait Response final case class ArmorChanged(suit : ExoSuitType.Value, subtype : Int) extends Response + final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response + final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response final case class ConcealPlayer() extends Response //final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Response final case class EquipmentInHand(slot : Int, item : Equipment) extends Response @@ -21,7 +23,8 @@ object AvatarResponse { final case class ObjectHeld(slot : Int) extends Response final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response - final case class Reload(mag : Int) extends Response + final case class Reload(weapon_guid : PlanetSideGUID) extends Response + final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response // final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response // final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response // final case class HitHintReturn(itemID : PlanetSideGUID) extends Response diff --git a/pslogin/src/main/scala/services/avatar/AvatarService.scala b/pslogin/src/main/scala/services/avatar/AvatarService.scala index ac32c07e2..09196cb17 100644 --- a/pslogin/src/main/scala/services/avatar/AvatarService.scala +++ b/pslogin/src/main/scala/services/avatar/AvatarService.scala @@ -39,6 +39,14 @@ class AvatarService extends Actor { AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ArmorChanged(suit, subtype)) ) + case AvatarAction.ChangeFireState_Start(player_guid, weapon_guid) => + AvatarEvents.publish( + AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ChangeFireState_Start(weapon_guid)) + ) + case AvatarAction.ChangeFireState_Stop(player_guid, weapon_guid) => + AvatarEvents.publish( + AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ChangeFireState_Stop(weapon_guid)) + ) case AvatarAction.ConcealPlayer(player_guid) => AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer()) @@ -71,9 +79,13 @@ class AvatarService extends Actor { AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon)) ) - case AvatarAction.Reload(player_guid, mag) => + case AvatarAction.Reload(player_guid, weapon_guid) => AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(mag)) + AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid)) + ) + case AvatarAction.WeaponDryFire(player_guid, weapon_guid) => + AvatarEvents.publish( + AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.WeaponDryFire(weapon_guid)) ) case _ => ; } diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala index 7887a023b..5bf6a5d4c 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package services.vehicle -import net.psforever.objects.Vehicle +import net.psforever.objects.{PlanetSideGameObject, Vehicle} import net.psforever.objects.equipment.Equipment import net.psforever.objects.zones.Zone import net.psforever.packet.game.PlanetSideGUID @@ -14,6 +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 DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, 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 final case class MountVehicle(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, seat : Int) extends Action diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala index 1b7de780f..32c084125 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package services.vehicle -import net.psforever.objects.Vehicle +import net.psforever.objects.{PlanetSideGameObject, Vehicle} import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.objectcreate.ConstructorData import net.psforever.types.Vector3 @@ -12,6 +12,7 @@ object VehicleResponse { final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response final case class DismountVehicle(unk1 : Int, 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 LoadVehicle(vehicle : Vehicle, vtype : Int, vguid : PlanetSideGUID, vdata : ConstructorData) extends Response final case class MountVehicle(object_guid : PlanetSideGUID, seat : Int) extends Response diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala index 003c8dc3c..9fa88ee36 100644 --- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala +++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala @@ -2,6 +2,8 @@ package services.vehicle import akka.actor.{Actor, ActorRef, Props} +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.packet.game.objectcreate.ConstructorData import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor, VehicleContextActor} import services.{GenericEventBus, Service} @@ -47,6 +49,10 @@ class VehicleService extends Actor { VehicleEvents.publish( VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw)) ) + 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.DismountVehicle(player_guid, unk1, unk2) => VehicleEvents.publish( VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(unk1, unk2)) diff --git a/pslogin/src/main/test/scala/AvatarServiceTest.scala b/pslogin/src/main/test/scala/AvatarServiceTest.scala index a89748005..9c43ac25f 100644 --- a/pslogin/src/main/test/scala/AvatarServiceTest.scala +++ b/pslogin/src/main/test/scala/AvatarServiceTest.scala @@ -176,8 +176,8 @@ class AvatarServiceCTest extends ActorTest { "pass Reload" in { val service = system.actorOf(Props[AvatarService], "service") service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.Reload(PlanetSideGUID(10), 35)) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.Reload(35))) + service ! AvatarServiceMessage("test", AvatarAction.Reload(PlanetSideGUID(10), PlanetSideGUID(40))) + expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.Reload(PlanetSideGUID(40)))) } } }