diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 3d1bbc0fa..42fd7a35b 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -12,6 +12,7 @@ import net.psforever.objects.serverobject.locks.IFFLockDefinition import net.psforever.objects.serverobject.mblocker.LockerDefinition import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition import net.psforever.objects.serverobject.terminals._ +import net.psforever.objects.serverobject.tube.SpawnTubeDefinition import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType} import net.psforever.types.PlanetSideEmpire @@ -486,6 +487,8 @@ object GlobalDefinitions { */ val order_terminal = new OrderTerminalDefinition + val ams_respawn_tube = new SpawnTubeDefinition(49) { Name = "ams_respawn_tube" } + val order_terminala = new OrderTerminalABDefinition(613) val order_terminalb = new OrderTerminalABDefinition(614) @@ -2347,6 +2350,7 @@ object GlobalDefinitions { ams.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax ams.MountPoints += 1 -> 0 ams.MountPoints += 2 -> 0 + ams.Utilities += 2 -> UtilityType.ams_respawn_tube ams.Utilities += 3 -> UtilityType.order_terminala ams.Utilities += 4 -> UtilityType.order_terminalb ams.Deployment = true diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/SpawnTubeConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/SpawnTubeConverter.scala new file mode 100644 index 000000000..56671f989 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/definition/converter/SpawnTubeConverter.scala @@ -0,0 +1,11 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.definition.converter + +import net.psforever.objects.serverobject.tube.SpawnTube +import net.psforever.packet.game.objectcreate.CommonTerminalData + +import scala.util.{Success, Try} + +class SpawnTubeConverter extends ObjectCreateConverter[SpawnTube]() { + override def ConstructorData(obj : SpawnTube) : Try[CommonTerminalData] = { Success(CommonTerminalData(obj.Faction)) } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTube.scala b/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTube.scala new file mode 100644 index 000000000..9d9baac9d --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTube.scala @@ -0,0 +1,34 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.tube + +import net.psforever.objects.definition.ObjectDefinition +import net.psforever.objects.serverobject.structures.Amenity + +class SpawnTube(tubeDef : ObjectDefinition) extends Amenity { + def Definition : ObjectDefinition = tubeDef +} + +object SpawnTube { + def apply(tubeDef : ObjectDefinition) : SpawnTube = { + new SpawnTube(tubeDef) + } + +// import akka.actor.ActorContext +// import net.psforever.types.Vector3 +// /** +// * Instantiate an configure a `SpawnTube` object +// * @param pos the position (used to determine spawn point) +// * @param orient the orientation (used to indicate spawn direction) +// * @param id the unique id that will be assigned to this entity +// * @param context a context to allow the object to properly set up `ActorSystem` functionality +// * @return the `SpawnTube` object +// */ +// def Constructor(pos : Vector3, orient : Vector3)(id : Int, context : ActorContext) : SpawnTube = { +// import net.psforever.objects.GlobalDefinitions +// +// val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube) +// obj.Position = pos +// obj.Orientation = orient +// obj +// } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala new file mode 100644 index 000000000..2c15119f3 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeControl.scala @@ -0,0 +1,17 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.tube + +import akka.actor.Actor +import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} + +/** + * An `Actor` that handles messages being dispatched to a specific `SpawnTube`. + * @param tube the `SpawnTube` object being governed + */ +class SpawnTubeControl(tube : SpawnTube) extends Actor with FactionAffinityBehavior.Check { + def FactionObject : FactionAffinity = tube + + def receive : Receive = checkBehavior.orElse { case _ =>; } + + override def toString : String = tube.Definition.Name +} \ No newline at end of file diff --git a/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala new file mode 100644 index 000000000..33937ec83 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala @@ -0,0 +1,26 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.serverobject.tube + +import akka.actor.ActorContext +import net.psforever.objects.definition.ObjectDefinition +import net.psforever.objects.definition.converter.SpawnTubeConverter +import net.psforever.objects.serverobject.structures.Amenity + +class SpawnTubeDefinition(object_id : Int) extends ObjectDefinition(object_id) { + Packet = new SpawnTubeConverter +} + +object SpawnTubeDefinition { + /** + * Assemble some logic for a provided object. + * @param obj an `Amenity` object; + * anticipating a `Terminal` object using this same definition + * @param context hook to the local `Actor` system + */ + def Setup(obj : Amenity, context : ActorContext) : Unit = { + import akka.actor.{ActorRef, Props} + if(obj.Actor == ActorRef.noSender) { + obj.Actor = context.actorOf(Props(classOf[SpawnTubeControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}") + } + } +} \ No newline at end of file diff --git a/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala b/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala index 5259c3255..720e62d86 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala @@ -5,6 +5,7 @@ import akka.actor.ActorContext import net.psforever.objects.{GlobalDefinitions, Vehicle} import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.terminals.{OrderTerminalABDefinition, Terminal} +import net.psforever.objects.serverobject.tube.{SpawnTube, SpawnTubeDefinition} /** * An `Enumeration` of the available vehicular utilities.
@@ -17,6 +18,7 @@ import net.psforever.objects.serverobject.terminals.{OrderTerminalABDefinition, object UtilityType extends Enumeration { type Type = Value val + ams_respawn_tube, order_terminala, order_terminalb = Value @@ -83,6 +85,8 @@ object Utility { * @return the `Amenity` object */ private def BuildUtilityFunc(util : UtilityType.Value) : Amenity = util match { + case UtilityType.ams_respawn_tube => + SpawnTube(GlobalDefinitions.ams_respawn_tube) case UtilityType.order_terminala => Terminal(GlobalDefinitions.order_terminala) case UtilityType.order_terminalb => @@ -95,6 +99,8 @@ object Utility { * @return the `Amenity` object */ private def SelectUtilitySetupFunc(util : UtilityType.Value) : UtilLogic = util match { + case UtilityType.ams_respawn_tube => + SpawnTubeDefinition.Setup case UtilityType.order_terminala => OrderTerminalABDefinition.Setup case UtilityType.order_terminalb => diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 431aca9f5..643e45ec1 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -406,7 +406,7 @@ object GamePacketOpcode extends Enumeration { // 0x48 case 0x48 => game.TimeOfDayMessage.decode case 0x49 => noDecoder(UnknownMessage73) - case 0x4a => noDecoder(SpawnRequestMessage) + case 0x4a => game.SpawnRequestMessage.decode case 0x4b => game.DeployRequestMessage.decode case 0x4c => noDecoder(UnknownMessage76) case 0x4d => game.RepairMessage.decode diff --git a/common/src/main/scala/net/psforever/packet/game/AvatarDeadStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/AvatarDeadStateMessage.scala index 57b3b6855..4b7ad326d 100644 --- a/common/src/main/scala/net/psforever/packet/game/AvatarDeadStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/AvatarDeadStateMessage.scala @@ -1,23 +1,36 @@ // Copyright (c) 2017 PSForever package net.psforever.packet.game -import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.types.Vector3 import scodec.Codec import scodec.codecs._ +object DeadState extends Enumeration { + type Type = Value + + val + Nothing, + Dead, + Release, + RespawnTime + = Value + + implicit val codec = PacketHelpers.createEnumerationCodec(this, uintL(3)) +} + /** * na - * @param unk1 0 = nothing, 1 = waiting for a rez, 2 = auto map to select spawn, 3 = respawn time - * @param unk2 na - * @param unk3 spawn penality - * @param pos last victim's position + * @param state avatar's relationship with the world + * @param timer_max total length of respawn countdown, in milliseconds + * @param timer initial length of the respawn timer, in milliseconds + * @param pos last position * @param unk4 na * @param unk5 na */ -final case class AvatarDeadStateMessage(unk1 : Int, - unk2 : Long, - unk3 : Long, +final case class AvatarDeadStateMessage(state : DeadState.Value, + timer_max : Long, + timer : Long, pos : Vector3, unk4 : Long, unk5 : Boolean) @@ -29,9 +42,9 @@ final case class AvatarDeadStateMessage(unk1 : Int, object AvatarDeadStateMessage extends Marshallable[AvatarDeadStateMessage] { implicit val codec : Codec[AvatarDeadStateMessage] = ( - ("unk1" | uintL(3)) :: - ("unk2" | uint32L) :: - ("unk3" | uint32L) :: + ("state" | DeadState.codec) :: + ("timer_max" | uint32L) :: + ("timer" | uint32L) :: ("pos" | Vector3.codec_pos) :: ("unk4" | uint32L) :: ("unk5" | bool) diff --git a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala index 036bd5ff8..bc09a063a 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala @@ -74,31 +74,34 @@ import scodec.codecs._ * `36 - CR. Value is the CR`
* `43 - Info on avatar name : 0 = Nothing, 1 = "(LD)" message`
* `53 - LFS. Value is 1 to flag LFS`
- * `54 - Player "Aura". Values are : 0 for nothing, 1 for plasma, 2 for ancient, 3 for plasma + ancient,
- * 4 for LLU?, 5 for plasma + LLU?, 6 for ancient + LLU?, 7 for plasma + ancient + LLU?, 8 for fire,
- * 9 for plasma + fire, 10 for ancient + fire, 11 for plasma + ancient + fire,
- * 12 for LLU? + fire, 13 for plasma + LLU? + fire, 14 for ancient + LLU? + fire,
- * 15 for plasma + ancient + LLU? + fire,`
+ * `54 - Player "Aura". Values can be expressed in the first byte's lower nibble:`
+ * - 0 is nothing
+ * - 1 is plasma
+ * - 2 is ancient
+ * - 4 is LLU (?)
+ * - 8 is fire
+ * -- e.g., 13 = 8 + 4 + 1 = fire and LLU and plasma
* `55 - "Someone is attempting to Heal you". Value is 1`
* `56 - "Someone is attempting to Repair you". Value is 1`
* `73 - "You are locked into the Core Beam. Charging your Module now.". Value is 1 to active`
* `77 - Cavern Facility Captures. Value is the number of captures`
* `78 - Cavern Kills. Value is the number of kills`
- * `106 - Custom Head` + * `106 - Custom Head`
* Client to Server :
* `106 - Custom Head`
*
- * Vehicles:
- * 0 - Vehicle base health
- * 10 - Driver seat permissions (0 = Locked, 1 = Group, 3 = Empire)
- * 11 - Gunner seat(s) permissions (same)
- * 12 - Passenger seat(s) permissions (same)
- * 13 - Trunk permissions (same)
- * 21 - Asserts first time event eligibility / makes owner if no owner is assigned
- * 22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)
- * 68 - ???
- * 80 - Damage vehicle (unknown value)
- * 113 - ??? + * `Vehicles:`
+ * `0 - Vehicle base health`
+ * `10 - Driver seat permissions (0 = Locked, 1 = Group, 3 = Empire)`
+ * `11 - Gunner seat(s) permissions (same)`
+ * `12 - Passenger seat(s) permissions (same)`
+ * `13 - Trunk permissions (same)`
+ * `21 - Asserts first time event eligibility / makes owner if no owner is assigned`
+ * `22 - Toggles gunner and passenger mount points (1 = hides, 0 = reveals; this also locks their permissions)`
+ * `68 - ???`
+ * `80 - Damage vehicle (unknown value)`
+ * `81 - ???`
+ * `113 - ???` * @param player_guid the player * @param attribute_type na * @param attribute_value na diff --git a/common/src/main/scala/net/psforever/packet/game/SpawnRequestMessage.scala b/common/src/main/scala/net/psforever/packet/game/SpawnRequestMessage.scala new file mode 100644 index 000000000..cbb35a959 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/SpawnRequestMessage.scala @@ -0,0 +1,28 @@ +// Copyright (c) 2017 PSForever +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import scodec.Codec +import scodec.codecs._ + + +final case class SpawnRequestMessage(unk1 : Int, + unk2 : Long, + unk3 : Int, + unk4 : Int, + unk5 : Int) + extends PlanetSideGamePacket { + type Packet = SpawnRequestMessage + def opcode = GamePacketOpcode.SpawnRequestMessage + def encode = SpawnRequestMessage.encode(this) +} + +object SpawnRequestMessage extends Marshallable[SpawnRequestMessage] { + implicit val codec : Codec[SpawnRequestMessage] = ( + ("unk1" | uint16L) :: + ("unk2" | uint32L) :: + ("unk3" | uint16L) :: + ("unk4" | uint16L) :: + ("unk5" | uintL(10)) + ).as[SpawnRequestMessage] +} diff --git a/common/src/test/scala/game/AvatarDeadStateMessageTest.scala b/common/src/test/scala/game/AvatarDeadStateMessageTest.scala index 69e31d245..ef8747279 100644 --- a/common/src/test/scala/game/AvatarDeadStateMessageTest.scala +++ b/common/src/test/scala/game/AvatarDeadStateMessageTest.scala @@ -13,7 +13,7 @@ class AvatarDeadStateMessageTest extends Specification { "decode" in { PacketCoding.DecodePacket(string).require match { case AvatarDeadStateMessage(unk1,unk2,unk3,pos,unk4,unk5) => - unk1 mustEqual 1 + unk1 mustEqual DeadState.Dead unk2 mustEqual 300000 unk3 mustEqual 300000 pos mustEqual Vector3(6552.617f,4602.375f,60.90625f) @@ -25,7 +25,7 @@ class AvatarDeadStateMessageTest extends Specification { } "encode" in { - val msg = AvatarDeadStateMessage(1, 300000, 300000, Vector3(6552.617f,4602.375f,60.90625f), 2, true) + val msg = AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3(6552.617f,4602.375f,60.90625f), 2, true) 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 eade334d3..a0e4ce30d 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -24,7 +24,8 @@ import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.mblocker.Locker 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.serverobject.terminals.Terminal.TerminalMessage +import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState} import net.psforever.objects.zones.{InterstellarCluster, Zone} import net.psforever.packet.game.objectcreate._ import net.psforever.types._ @@ -976,28 +977,30 @@ class WorldSessionActor extends Actor with MDCContextAware { case Zone.ClientInitialization(/*initList*/_) => //TODO iterate over initList; for now, just do this sendResponse( - BuildingInfoUpdateMessage( - PlanetSideGUID(6), //Ceryshen - PlanetSideGUID(2), //Anguta - 8, //80% NTU - true, //Base hacked - PlanetSideEmpire.NC, //Base hacked by NC - 600000, //10 minutes remaining for hack - PlanetSideEmpire.VS, //Base owned by VS - 0, //!! Field != 0 will cause malformed packet. See class def. - None, - PlanetSideGeneratorState.Critical, //Generator critical - true, //Respawn tubes destroyed - true, //Force dome active - 16, //Tech plant lattice benefit - 0, - Nil, //!! Field > 0 will cause malformed packet. See class def. - 0, - false, - 8, //!! Field != 8 will cause malformed packet. See class def. - None, - true, //Boosted spawn room pain field - true //Boosted generator room pain field + PacketCoding.CreateGamePacket(0, + BuildingInfoUpdateMessage( + PlanetSideGUID(6), //Ceryshen + PlanetSideGUID(2), //Anguta + 8, //80% NTU + false, //Base hacked + PlanetSideEmpire.VS, //Base hacked by NC + 0, //10 minutes remaining for hack + PlanetSideEmpire.VS, //Base owned by VS + 0, //!! Field != 0 will cause malformed packet. See class def. + None, + PlanetSideGeneratorState.Critical, //Generator critical + true, //Respawn tubes destroyed + true, //Force dome active + 16, //Tech plant lattice benefit + 0, + Nil, //!! Field > 0 will cause malformed packet. See class def. + 0, + false, + 8, //!! Field != 8 will cause malformed packet. See class def. + None, + true, //Boosted spawn room pain field + true //Boosted generator room pain field + ) ) ) sendResponse(ContinentalLockUpdateMessage(PlanetSideGUID(13), PlanetSideEmpire.VS)) // "The VS have captured the VS Sanctuary." @@ -1148,6 +1151,7 @@ class WorldSessionActor extends Actor with MDCContextAware { } var player : Player = null + var testVehicle : Boolean = false def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match { case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) => @@ -1158,7 +1162,8 @@ class WorldSessionActor extends Actor with MDCContextAware { player = Player("TestCharacter"+sessionId.toString, PlanetSideEmpire.VS, CharacterGender.Female, 41, 1) //player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f) //player.Position = Vector3(3523.039f, 2855.5078f, 90.859375f) - player.Position = Vector3(3561.0f, 2854.0f, 90.859375f) + //player.Position = Vector3(3561.0f, 2854.0f, 90.859375f) + player.Position = Vector3(3983.0f, 4344.0f, 266.0f) player.Orientation = Vector3(0f, 0f, 90f) player.Certifications += CertificationType.StandardAssault player.Certifications += CertificationType.MediumAssault @@ -1222,7 +1227,7 @@ class WorldSessionActor extends Actor with MDCContextAware { //TODO check if can spawn on last continent/location from player? //TODO if yes, get continent guid accessors //TODO if no, get sanctuary guid accessors and reset the player's expectations - galaxy ! InterstellarCluster.GetWorld("home3") + galaxy ! InterstellarCluster.GetWorld("z6") case default => log.error("Unsupported " + default + " in " + msg) } @@ -1325,6 +1330,11 @@ class WorldSessionActor extends Actor with MDCContextAware { player.Crouching = is_crouching player.Jumping = is_jumping + if(is_crouching && !testVehicle) { + testVehicle = true + self ! PacketCoding.CreateGamePacket(0, ItemTransactionMessage(PlanetSideGUID(3353), TransactionType.Buy, 0, "ams", 0, PlanetSideGUID(0))) + } + val wepInHand : Boolean = player.Slot(player.DrawnSlot).Equipment match { case Some(item) => item.Definition == GlobalDefinitions.bolt_driver case None => false @@ -1387,14 +1397,29 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vector, unk1, unk2, unk3, unk4, time_alive) => //log.info("ProjectileState: " + msg) + case msg @ ReleaseAvatarRequestMessage() => + log.info(s"ReleaseAvatarRequest: ${player.GUID} on ${continent.Id} has released") + sendResponse(PacketCoding.CreateGamePacket(0, AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, 2, true))) + //sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(player.GUID, 6, 1))) + + case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) => + log.info(s"SpawnRequestMessage: $msg") + case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) => // TODO: Prevents log spam, but should be handled correctly if (messagetype != ChatMessageType.CMT_TOGGLE_GM) { log.info("Chat: " + msg) } + if(messagetype == ChatMessageType.CMT_SUICIDE) { + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(player.GUID, 0, 0))) + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(player.GUID, 2, 0))) + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(player.GUID, 4, 0))) + sendResponse(PacketCoding.CreateGamePacket(0, AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, player.Position, 2, true))) + } + if (messagetype == ChatMessageType.CMT_VOICE) { - sendResponse(ChatMsg(ChatMessageType.CMT_VOICE, false, "IlllIIIlllIlIllIlllIllI", contents, None)) + sendResponse(PacketCoding.CreateGamePacket(0, ChatMsg(ChatMessageType.CMT_VOICE, false, player.Name, contents, None))) } // TODO: handle this appropriately @@ -2986,8 +3011,8 @@ class WorldSessionActor extends Actor with MDCContextAware { def DeploymentActivities(obj : Deployment.DeploymentObject) : Unit = { obj match { case vehicle : Vehicle => - //TODO we should not have to do this imho - ReloadVehicleAccessPermissions(vehicle) + ReloadVehicleAccessPermissions(vehicle) //TODO we should not have to do this imho + sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(obj.GUID, 81, 1))) case _ => ; } }