diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/WarpGate.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/WarpGate.scala
new file mode 100644
index 000000000..784685377
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/WarpGate.scala
@@ -0,0 +1,22 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.serverobject.structures
+
+import akka.actor.ActorContext
+import net.psforever.objects.zones.Zone
+
+class WarpGate(id : Int, zone : Zone) extends Building(id, zone) {
+ //TODO stuff later
+}
+
+object WarpGate {
+ def apply(id : Int, zone : Zone) : WarpGate = {
+ new WarpGate(id, zone)
+ }
+
+ def Structure(id : Int, zone : Zone, context : ActorContext) : WarpGate = {
+ import akka.actor.Props
+ val obj = new WarpGate(id, zone)
+ obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-gate")
+ obj
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ImplantTerminalInterfaceDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ImplantTerminalInterfaceDefinition.scala
index 1549d76f3..51c62e4ea 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ImplantTerminalInterfaceDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ImplantTerminalInterfaceDefinition.scala
@@ -16,7 +16,7 @@ import net.psforever.packet.game.objectcreate.ObjectClass
*/
class ImplantTerminalInterfaceDefinition extends TerminalDefinition(ObjectClass.implant_terminal_interface) {
Packet = new ImplantTerminalInterfaceConverter
- Name = "implante_terminal_interface"
+ Name = "implant_terminal_interface"
private val implants : Map[String, ImplantDefinition] = Map (
"advanced_regen" -> GlobalDefinitions.advanced_regen,
diff --git a/common/src/main/scala/net/psforever/objects/zones/InterstellarCluster.scala b/common/src/main/scala/net/psforever/objects/zones/InterstellarCluster.scala
index e09d55c27..ddd70cca2 100644
--- a/common/src/main/scala/net/psforever/objects/zones/InterstellarCluster.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/InterstellarCluster.scala
@@ -50,9 +50,7 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
}
case InterstellarCluster.RequestClientInitialization(tplayer) =>
- zones.foreach(zone => {
- sender ! Zone.ClientInitialization(zone.ClientInitialization()) //do this for each Zone
- })
+ zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) })
sender ! InterstellarCluster.ClientInitializationComplete(tplayer) //will be processed after all Zones
case _ => ;
diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala
index 13e055048..f084443df 100644
--- a/common/src/main/scala/net/psforever/objects/zones/Zone.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -10,7 +10,6 @@ import net.psforever.objects.guid.actor.UniqueNumberSystem
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.structures.{Amenity, Building}
-import net.psforever.packet.GamePacket
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.Vector3
@@ -218,6 +217,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
def Transport : ActorRef = transport
+ def Buildings : Map[Int, Building] = buildings
+
def Building(id : Int) : Option[Building] = {
buildings.get(id)
}
@@ -250,25 +251,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
* - `ZonePopulationUpdateMessage`
* @return a `List` of `GamePacket` messages
*/
- def ClientInitialization() : List[GamePacket] = {
- //TODO unimplemented
- List.empty[GamePacket]
- }
-
- /**
- * Provide bulk correspondence on all server objects that can be composed into packet messages and reported to a client.
- * These messages are sent in this fashion at the time of joining a specific `Zone`:
- * - `HackMessage`
- * - `PlanetsideAttributeMessage`
- * - `SetEmpireMessage`
- * - `TimeOfDayMessage`
- * - `WeatherMessage`
- * @return a `List` of `GamePacket` messages
- */
- def ClientConfiguration() : List[GamePacket] = {
- //TODO unimplemented
- List.empty[GamePacket]
- }
+ def ClientInitialization() : Zone = this
}
object Zone {
@@ -308,11 +291,11 @@ object Zone {
/**
* Message to report the packet messages that initialize the client.
- * @param list a `List` of `GamePacket` messages
+ * @param zone a `Zone` to have its buildings and continental parameters turned into packet data
* @see `Zone.ClientInitialization()`
* `InterstallarCluster`
*/
- final case class ClientInitialization(list : List[GamePacket])
+ final case class ClientInitialization(zone : Zone)
/**
* Overloaded constructor.
diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index 21ae82f69..1c1185470 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -436,8 +436,8 @@ object GamePacketOpcode extends Enumeration {
case 0x60 => game.FavoritesMessage.decode
case 0x61 => game.ObjectDetectedMessage.decode
case 0x62 => game.SplashHitMessage.decode
- case 0x63 => noDecoder(SetChatFilterMessage)
- case 0x64 => noDecoder(AvatarSearchCriteriaMessage)
+ case 0x63 => game.SetChatFilterMessage.decode
+ case 0x64 => game.AvatarSearchCriteriaMessage.decode
case 0x65 => noDecoder(AvatarSearchResponse)
case 0x66 => game.WeaponJammedMessage.decode
case 0x67 => noDecoder(LinkDeadAwarenessMsg)
@@ -468,7 +468,7 @@ object GamePacketOpcode extends Enumeration {
case 0x7c => game.DismountBuildingMsg.decode
case 0x7d => noDecoder(UnknownMessage125)
case 0x7e => noDecoder(UnknownMessage126)
- case 0x7f => noDecoder(AvatarStatisticsMessage)
+ case 0x7f => game.AvatarStatisticsMessage.decode
// OPCODES 0x80-8f
case 0x80 => noDecoder(GenericObjectAction2Message)
@@ -561,7 +561,7 @@ object GamePacketOpcode extends Enumeration {
case 0xca => noDecoder(OutfitBenefitMessage)
case 0xcb => noDecoder(EmpireChangeTimeMessage)
case 0xcc => noDecoder(ClockCalibrationMessage)
- case 0xcd => noDecoder(DensityLevelUpdateMessage)
+ case 0xcd => game.DensityLevelUpdateMessage.decode
case 0xce => noDecoder(ActOfGodMessage)
case 0xcf => noDecoder(AvatarAwardMessage)
diff --git a/common/src/main/scala/net/psforever/packet/game/AvatarImplantMessage.scala b/common/src/main/scala/net/psforever/packet/game/AvatarImplantMessage.scala
index 8369a6425..4505578f9 100644
--- a/common/src/main/scala/net/psforever/packet/game/AvatarImplantMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/AvatarImplantMessage.scala
@@ -1,40 +1,48 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game
-import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
-import net.psforever.types.ImplantType
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import scodec.Codec
import scodec.codecs._
/**
- * Change the state of the implant.
+ * An `Enumeration` for all the actions that can be applied to implants and implant slots.
+ */
+object ImplantAction extends Enumeration {
+ type Type = Value
+
+ val
+ Add,
+ Remove,
+ Initialization,
+ Activation,
+ UnlockMessage,
+ OutOfStamina
+ = Value
+
+ implicit val codec = PacketHelpers.createEnumerationCodec(this, uintL(3))
+}
+
+/**
+ * Change the state of the implant.
+ * Spawn messages for certain implant-related events.
*
* The implant Second Wind is technically an invalid `ImplantType` for this packet.
* This owes to the unique activation trigger for that implant - a near-death experience of ~0HP.
+ * @see `ImplantType`
* @param player_guid the player
- * @param action
- * 0 : add implant
- * with status = 0 to 9 (from ImplantType)
- * 1 : remove implant
- * seems work with any value in status
- * 2 : init implant
- * status : 0 to "uninit"
- * status : 1 to init
- * 3 : activate implant
- * status : 0 to desactivate
- * status : 1 to activate
- * 4 : number of implant slots unlocked
- * status : 0 = no implant slot
- * status : 1 = first implant slot + "implant message"
- * status : 2 or 3 = unlock second & third slots
- * 5 : out of stamina message
- * status : 0 to stop the lock
- * status : 1 to active the lock
+ * @param action how to affect the implant or the slot
* @param implantSlot : from 0 to 2
- * @param status : see action
+ * @param status : a value that depends on context from `ImplantAction`:
+ * `Add` - 0-9 depending on the `ImplantType`
+ * `Remove` - any valid value; field is not significant to this action
+ * `Initialization` - 0 to revoke slot; 1 to allocate implant slot
+ * `Activation` - 0 to deactivate implant; 1 to activate implant
+ * `UnlockMessage` - 0-3 as an unlocked implant slot; display a message
+ * `OutOfStamina` - lock implant; 0 to lock; 1 to unlock; display a message
*/
final case class AvatarImplantMessage(player_guid : PlanetSideGUID,
- action : Int,
+ action : ImplantAction.Value,
implantSlot : Int,
status : Int)
extends PlanetSideGamePacket {
@@ -46,7 +54,7 @@ final case class AvatarImplantMessage(player_guid : PlanetSideGUID,
object AvatarImplantMessage extends Marshallable[AvatarImplantMessage] {
implicit val codec : Codec[AvatarImplantMessage] = (
("player_guid" | PlanetSideGUID.codec) ::
- ("action" | uintL(3)) ::
+ ("action" | ImplantAction.codec) ::
("implantSlot" | uint2L) ::
("status" | uint4L)
).as[AvatarImplantMessage]
diff --git a/common/src/main/scala/net/psforever/packet/game/AvatarSearchCriteriaMessage.scala b/common/src/main/scala/net/psforever/packet/game/AvatarSearchCriteriaMessage.scala
new file mode 100644
index 000000000..b6ed5c526
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/AvatarSearchCriteriaMessage.scala
@@ -0,0 +1,44 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
+import scodec.{Attempt, Codec, Err}
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+/**
+ * na
+ * @param unk1 na
+ * @param unk2 na
+ */
+final case class AvatarSearchCriteriaMessage(unk1 : PlanetSideGUID,
+ unk2 : List[Int])
+ extends PlanetSideGamePacket {
+ type Packet = AvatarSearchCriteriaMessage
+ def opcode = GamePacketOpcode.AvatarSearchCriteriaMessage
+ def encode = AvatarSearchCriteriaMessage.encode(this)
+}
+
+object AvatarSearchCriteriaMessage extends Marshallable[AvatarSearchCriteriaMessage] {
+ implicit val codec : Codec[AvatarSearchCriteriaMessage] = (
+ ("unk1" | PlanetSideGUID.codec) ::
+ ("unk2" | PacketHelpers.listOfNSized(6, uint8L))
+ ).exmap[AvatarSearchCriteriaMessage] (
+ {
+ case a :: b :: HNil =>
+ Attempt.Successful(AvatarSearchCriteriaMessage(a, b))
+ },
+ {
+ case AvatarSearchCriteriaMessage(a, b) =>
+ if(b.length != 6) {
+ Attempt.Failure(Err("list must have 6 entries"))
+ }
+ else if(b.count(i => { i < 0 || i > 255 }) > 0) {
+ Attempt.Failure(Err("list entries must be 0-255 inclusive"))
+ }
+ else {
+ Attempt.Successful(a :: b :: HNil)
+ }
+ }
+ )
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/AvatarStatisticsMessage.scala b/common/src/main/scala/net/psforever/packet/game/AvatarStatisticsMessage.scala
new file mode 100644
index 000000000..9b510f629
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/AvatarStatisticsMessage.scala
@@ -0,0 +1,105 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
+import scodec.{Attempt, Codec, Err}
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+import scala.annotation.switch
+
+/**
+ * na
+ * @param unk1 na
+ * @param unk2 na
+ * @param unk3 na
+ */
+final case class Statistics(unk1 : Option[Int],
+ unk2 : Option[Int],
+ unk3 : List[Long])
+
+/**
+ * na
+ * @param unk na
+ * @param stats na
+ */
+final case class AvatarStatisticsMessage(unk : Int,
+ stats : Statistics)
+ extends PlanetSideGamePacket {
+ type Packet = AvatarStatisticsMessage
+ def opcode = GamePacketOpcode.AvatarStatisticsMessage
+ def encode = AvatarStatisticsMessage.encode(this)
+}
+
+object AvatarStatisticsMessage extends Marshallable[AvatarStatisticsMessage] {
+ /**
+ * na
+ */
+ private val longCodec : Codec[Statistics] = ulong(32).hlist.exmap (
+ {
+ case n :: HNil =>
+ Attempt.Successful(Statistics(None,None, List(n)))
+ },
+ {
+ case Statistics(_, _, Nil) =>
+ Attempt.Failure(Err("missing value (32-bit)"))
+
+ case Statistics(_, _, n) =>
+ Attempt.Successful(n.head :: HNil)
+ }
+ )
+
+ /**
+ * na
+ */
+ private val complexCodec : Codec[Statistics] = (
+ uint(5) ::
+ uintL(11) ::
+ PacketHelpers.listOfNSized(8, uint32L)
+ ).exmap[Statistics] (
+ {
+ case a :: b :: c :: HNil =>
+ Attempt.Successful(Statistics(Some(a), Some(b), c))
+ },
+ {
+ case Statistics(None, _, _) =>
+ Attempt.Failure(Err("missing value (5-bit)"))
+
+ case Statistics(_, None, _) =>
+ Attempt.Failure(Err("missing value (11-bit)"))
+
+ case Statistics(a, b, c) =>
+ if(c.length != 8) {
+ Attempt.Failure(Err("list must have 8 entries"))
+ }
+ else {
+ Attempt.Successful(a.get :: b.get :: c :: HNil)
+ }
+ }
+ )
+
+ /**
+ * na
+ * @param n na
+ * @return na
+ */
+ private def selectCodec(n : Int) : Codec[Statistics] = (n : @switch) match {
+ case 2 | 3 =>
+ longCodec
+ case _ =>
+ complexCodec
+ }
+
+ implicit val codec : Codec[AvatarStatisticsMessage] = (
+ ("unk" | uint(3)) >>:~ { unk =>
+ ("stats" | selectCodec(unk)).hlist
+ }).as[AvatarStatisticsMessage]
+}
+
+object Statistics {
+ def apply(unk : Long) : Statistics =
+ Statistics(None, None, List(unk))
+
+ def apply(unk1 : Int, unk2 : Int, unk3 : List[Long]) : Statistics =
+ Statistics(Some(unk1), Some(unk2), unk3)
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/BroadcastWarpgateUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/BroadcastWarpgateUpdateMessage.scala
index d786560b3..89dac67f2 100644
--- a/common/src/main/scala/net/psforever/packet/game/BroadcastWarpgateUpdateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/BroadcastWarpgateUpdateMessage.scala
@@ -17,17 +17,17 @@ import scodec.codecs._
* I believe these `Boolean` values actually indicate some measure of warpgate operation.
* Geowarps, for example, though their appearance does not change, recieve this packet.
* Moreover, they can operate as a receiving-end broadcast gate.
- * @param continent_guid identifies the zone (continent)
- * @param building_guid identifies the warpgate (see `BuildingInfoUpdateMessage`)
+ * @param continent_id the zone
+ * @param building_id the warp gate (see `BuildingInfoUpdateMessage`)
* @param unk1 na
* @param unk2 na
- * @param is_broadcast if true, the gate replaces its destination text with "Broadcast"
+ * @param broadcast if true, the gate replaces its destination text with "Broadcast"
*/
-final case class BroadcastWarpgateUpdateMessage(continent_guid : PlanetSideGUID,
- building_guid : PlanetSideGUID,
+final case class BroadcastWarpgateUpdateMessage(continent_id : Int,
+ building_id : Int,
unk1 : Boolean,
unk2 : Boolean,
- is_broadcast : Boolean)
+ broadcast : Boolean)
extends PlanetSideGamePacket {
type Packet = BroadcastWarpgateUpdateMessage
def opcode = GamePacketOpcode.BroadcastWarpgateUpdateMessage
@@ -36,10 +36,10 @@ final case class BroadcastWarpgateUpdateMessage(continent_guid : PlanetSideGUID,
object BroadcastWarpgateUpdateMessage extends Marshallable[BroadcastWarpgateUpdateMessage] {
implicit val codec : Codec[BroadcastWarpgateUpdateMessage] = (
- ("continent_guid" | PlanetSideGUID.codec) ::
- ("building_guid" | PlanetSideGUID.codec) ::
+ ("continent_id" | uint16L) ::
+ ("building_id" | uint16L) ::
("unk1" | bool) ::
("unk2" | bool) ::
- ("is_broadcast" | bool)
+ ("broadcast" | bool)
).as[BroadcastWarpgateUpdateMessage]
}
diff --git a/common/src/main/scala/net/psforever/packet/game/BuildingInfoUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/BuildingInfoUpdateMessage.scala
index 9887e4093..753a0a72a 100644
--- a/common/src/main/scala/net/psforever/packet/game/BuildingInfoUpdateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/BuildingInfoUpdateMessage.scala
@@ -87,8 +87,8 @@ final case class Additional3(unk1 : Boolean,
* 064 - Health Module
* 128 - Pain Module
* `
- * @param continent_guid the continent (zone)
- * @param building_guid the building
+ * @param continent_id the continent (zone)
+ * @param building_id the building
* @param ntu_level if the building has a silo, the amount of NTU in that silo;
* NTU is reported in multiples of 10%;
* valid for 0 (0%) to 10 (100%)
@@ -117,8 +117,8 @@ final case class Additional3(unk1 : Boolean,
* @param boost_spawn_pain if the building has spawn tubes, the (boosted) strength of its enemy pain field
* @param boost_generator_pain if the building has a generator, the (boosted) strength of its enemy pain field
*/
-final case class BuildingInfoUpdateMessage(continent_guid : PlanetSideGUID,
- building_guid : PlanetSideGUID,
+final case class BuildingInfoUpdateMessage(continent_id : Int,
+ building_id : Int,
ntu_level : Int,
is_hacked : Boolean,
empire_hack : PlanetSideEmpire.Value,
@@ -171,8 +171,8 @@ object BuildingInfoUpdateMessage extends Marshallable[BuildingInfoUpdateMessage]
).as[Additional3]
implicit val codec : Codec[BuildingInfoUpdateMessage] = (
- ("continent_guid" | PlanetSideGUID.codec) ::
- ("building_guid" | PlanetSideGUID.codec) ::
+ ("continent_id" | uint16L) ::
+ ("building_id" | uint16L) ::
("ntu_level" | uint4L) ::
("is_hacked" | bool ) ::
("empire_hack" | PlanetSideEmpire.codec) ::
diff --git a/common/src/main/scala/net/psforever/packet/game/ContinentalLockUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ContinentalLockUpdateMessage.scala
index 1aa707084..204db94c0 100644
--- a/common/src/main/scala/net/psforever/packet/game/ContinentalLockUpdateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/ContinentalLockUpdateMessage.scala
@@ -12,10 +12,10 @@ import scodec.codecs._
* This generates the event message "The [empire] have captured [continent]."
* If the continent_guid is not a valid zone, no message is displayed.
* If empire is not a valid empire, or refers to the neutral or Black Ops forces, no message is displayed.
- * @param continent_guid identifies the zone (continent)
+ * @param continent_id identifies the zone (continent)
* @param empire identifies the empire
*/
-final case class ContinentalLockUpdateMessage(continent_guid : PlanetSideGUID,
+final case class ContinentalLockUpdateMessage(continent_id : Int,
empire : PlanetSideEmpire.Value)
extends PlanetSideGamePacket {
type Packet = ContinentalLockUpdateMessage
@@ -25,7 +25,7 @@ final case class ContinentalLockUpdateMessage(continent_guid : PlanetSideGUID,
object ContinentalLockUpdateMessage extends Marshallable[ContinentalLockUpdateMessage] {
implicit val codec : Codec[ContinentalLockUpdateMessage] = (
- ("continent_guid" | PlanetSideGUID.codec) ::
+ ("continent_id" | uint16L) ::
("empire" | PlanetSideEmpire.codec)
).as[ContinentalLockUpdateMessage]
}
diff --git a/common/src/main/scala/net/psforever/packet/game/DelayedPathMountMsg.scala b/common/src/main/scala/net/psforever/packet/game/DelayedPathMountMsg.scala
index 4c713e096..643898e6e 100644
--- a/common/src/main/scala/net/psforever/packet/game/DelayedPathMountMsg.scala
+++ b/common/src/main/scala/net/psforever/packet/game/DelayedPathMountMsg.scala
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 PSForever.net to present
+// Copyright (c) 2017 PSForever
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
diff --git a/common/src/main/scala/net/psforever/packet/game/DensityLevelUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/DensityLevelUpdateMessage.scala
new file mode 100644
index 000000000..957367aeb
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/DensityLevelUpdateMessage.scala
@@ -0,0 +1,47 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
+import scodec.{Attempt, Codec, Err}
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+/**
+ * na
+ * @param zone_id the continent
+ * @param building_id the building
+ * @param density na
+ */
+final case class DensityLevelUpdateMessage(zone_id : Int,
+ building_id : Int,
+ density : List[Int])
+ extends PlanetSideGamePacket {
+ type Packet = DensityLevelUpdateMessage
+ def opcode = GamePacketOpcode.DensityLevelUpdateMessage
+ def encode = DensityLevelUpdateMessage.encode(this)
+}
+
+object DensityLevelUpdateMessage extends Marshallable[DensityLevelUpdateMessage] {
+ implicit val codec : Codec[DensityLevelUpdateMessage] = (
+ ("zone_id" | uint16L) ::
+ ("building_id" | uint16L) ::
+ ("density" | PacketHelpers.listOfNSized(8, uint(3)))
+ ).exmap[DensityLevelUpdateMessage] (
+ {
+ case a :: b :: c :: HNil =>
+ Attempt.Successful(DensityLevelUpdateMessage(a, b, c))
+ },
+ {
+ case DensityLevelUpdateMessage(a, b, c) =>
+ if(c.length != 8) {
+ Attempt.Failure(Err("list must have 8 entries"))
+ }
+ else if(c.count(i => { i < 0 || i > 7 }) > 0) {
+ Attempt.Failure(Err("list entries must be 0-7 inclusive"))
+ }
+ else {
+ Attempt.Successful(a :: b :: c :: HNil)
+ }
+ }
+ )
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/FriendsResponse.scala b/common/src/main/scala/net/psforever/packet/game/FriendsResponse.scala
index 49c1f215a..8eb97abe5 100644
--- a/common/src/main/scala/net/psforever/packet/game/FriendsResponse.scala
+++ b/common/src/main/scala/net/psforever/packet/game/FriendsResponse.scala
@@ -6,6 +6,22 @@ import scodec.Codec
import scodec.codecs._
import shapeless.{::, HNil}
+object FriendAction extends Enumeration {
+ type Type = Value
+
+ val
+ InitializeFriendList,
+ AddFriend,
+ RemoveFriend,
+ UpdateFriend,
+ InitializeIgnoreList,
+ AddIgnoredPlayer,
+ RemoveIgnoredPlayer
+ = Value
+
+ implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
+}
+
/**
* An entry in the list of players known to and tracked by this player.
* They're called "friends" even though they can be used for a list of ignored players as well.
@@ -37,7 +53,7 @@ final case class Friend(name : String,
* @param unk3 na; always `true`?
* @param friends a list of `Friend`s
*/
-final case class FriendsResponse(action : Int,
+final case class FriendsResponse(action : FriendAction.Value,
unk1 : Int,
unk2 : Boolean,
unk3 : Boolean,
@@ -66,7 +82,7 @@ object Friend extends Marshallable[Friend] {
object FriendsResponse extends Marshallable[FriendsResponse] {
implicit val codec : Codec[FriendsResponse] = (
- ("action" | uintL(3)) ::
+ ("action" | FriendAction.codec) ::
("unk1" | uint4L) ::
("unk2" | bool) ::
("unk3" | bool) ::
@@ -76,8 +92,8 @@ object FriendsResponse extends Marshallable[FriendsResponse] {
})
).xmap[FriendsResponse] (
{
- case act :: u1 :: u2 :: u3 :: num :: friend1 :: friends :: HNil =>
- val friendList : List[Friend] = if(friend1.isDefined) { friend1.get :: friends } else { friends }
+ case act :: u1 :: u2 :: u3 :: _ :: friend1 :: friends :: HNil =>
+ val friendList : List[Friend] = if(friend1.isDefined) { friend1.get +: friends } else { friends }
FriendsResponse(act, u1, u2, u3, friendList)
},
{
diff --git a/common/src/main/scala/net/psforever/packet/game/HackMessage.scala b/common/src/main/scala/net/psforever/packet/game/HackMessage.scala
index 2225033e3..492e5b53d 100644
--- a/common/src/main/scala/net/psforever/packet/game/HackMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/HackMessage.scala
@@ -47,6 +47,8 @@ object HackState extends Enumeration {
* Upon the hack's completion, the target on the client will automatically revert back to its original state, if possible.
* (It will still be necessary to alert this change from the server's perspective.)
* @param unk1 na;
+ * 0 commonly;
+ * 3 for building objects during login phase;
* hack type?
* @param target_guid the target of the hack
* @param player_guid the player
diff --git a/common/src/main/scala/net/psforever/packet/game/HotSpotUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/HotSpotUpdateMessage.scala
index 839ae77a4..f605363b7 100644
--- a/common/src/main/scala/net/psforever/packet/game/HotSpotUpdateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/HotSpotUpdateMessage.scala
@@ -32,13 +32,13 @@ final case class HotSpotInfo(x : Float,
*
* Exploration:
* What does (zone) priority entail?
- * @param continent_guid the zone (continent)
+ * @param continent_id the zone
* @param priority na
* @param spots a List of HotSpotInfo
*/
-final case class HotSpotUpdateMessage(continent_guid : PlanetSideGUID,
+final case class HotSpotUpdateMessage(continent_id : Int,
priority : Int,
- spots : List[HotSpotInfo] = Nil)
+ spots : List[HotSpotInfo])
extends PlanetSideGamePacket {
type Packet = HotSpotUpdateMessage
def opcode = GamePacketOpcode.HotSpotUpdateMessage
@@ -47,9 +47,9 @@ final case class HotSpotUpdateMessage(continent_guid : PlanetSideGUID,
object HotSpotInfo extends Marshallable[HotSpotInfo] {
/*
- the scale is technically not "correct"
- the client is looking for a normal 0-8192 value
- we are trying to enforce a more modest graphic scale at 128.0f
+ the client is looking for a normal 0-8192 value where default is 1.0f
+ we try to enforce a more modest graphic scale where default is 64.0f (arbitrary)
+ personally, I'd like scale to equal the sprite width in map units but the pulsation makes it hard to apply
*/
implicit val codec : Codec[HotSpotInfo] = {
("x" | newcodecs.q_float(0.0, 8192.0, 20)) ::
@@ -60,8 +60,8 @@ object HotSpotInfo extends Marshallable[HotSpotInfo] {
object HotSpotUpdateMessage extends Marshallable[HotSpotUpdateMessage] {
implicit val codec : Codec[HotSpotUpdateMessage] = (
- ("continent_guid" | PlanetSideGUID.codec) ::
+ ("continent_guid" | uint16L) ::
("priority" | uint4L) ::
- ("spots" | PacketHelpers.listOfNAligned(longL(8), 4, HotSpotInfo.codec))
+ ("spots" | PacketHelpers.listOfNAligned(longL(12), 0, HotSpotInfo.codec))
).as[HotSpotUpdateMessage]
}
diff --git a/common/src/main/scala/net/psforever/packet/game/SetChatFilterMessage.scala b/common/src/main/scala/net/psforever/packet/game/SetChatFilterMessage.scala
new file mode 100644
index 000000000..c51f9f4bb
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/SetChatFilterMessage.scala
@@ -0,0 +1,96 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
+import scodec.{Attempt, Codec}
+import scodec.codecs._
+import shapeless.{::, HNil}
+
+/**
+ * An `Enumeration` of the valid chat channels.
+ */
+object ChatChannel extends Enumeration {
+ type Type = Value
+
+ val
+ Unknown,
+ Tells,
+ Local,
+ Squad,
+ Outfit,
+ Command,
+ Platoon,
+ Broadcast,
+ SquadLeader
+ = Value
+
+ implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(7))
+}
+
+/**
+ * Which comm. channels are allowed to display in the main chat window.
+ * The server sends a `SetChatFilterMessage` and the client responds with the same during login.
+ *
+ * Nine channels exist.
+ * Their values can be modified by radio buttons found under the current chat window's "Options" pane.
+ * Each time the client updates the channel permissions, it sends this packet to the server nine times.
+ * The packet starts with the previous channel filter states and then updates each channel sequentially.
+ *
+ * The `send_channel` and the `channel_filter` values are in the following order:
+ * Unknown, Tells, Local, Squad, Outfit, Command, Platoon, Broadcast, Squad Leader
+ * The first channel is unlisted.
+ * @param send_channel automatically select the fully qualified channel to which the user sends messages
+ * @param origin where this packet was dispatched;
+ * `true`, from the server; `false`, from the client
+ * @param whitelist each channel permitted to post its messages;
+ * when evaluated from a packet, always in original order
+ */
+final case class SetChatFilterMessage(send_channel : ChatChannel.Value,
+ origin : Boolean,
+ whitelist : List[ChatChannel.Value])
+ extends PlanetSideGamePacket {
+ type Packet = SetChatFilterMessage
+ def opcode = GamePacketOpcode.SetChatFilterMessage
+ def encode = SetChatFilterMessage.encode(this)
+}
+
+object SetChatFilterMessage extends Marshallable[SetChatFilterMessage] {
+ /**
+ * Transform a `List` of `Boolean` values into a `List` of `ChatChannel` values.
+ * @param filters the boolean values representing ordered channel filters
+ * @return the names of the channels permitted
+ */
+ private def stateArrayToChannelFilters(filters : List[Boolean]) : List[ChatChannel.Value] = {
+ (0 until 9)
+ .filter(channel => { filters(channel) })
+ .map(channel => ChatChannel(channel))
+ .toList
+ }
+
+ /**
+ * Transform a `List` of `ChatChannel` values into a `List` of `Boolean` values.
+ * @param filters the names of the channels permitted
+ * @return the boolean values representing ordered channel filters
+ */
+ private def channelFiltersToStateArray(filters : List[ChatChannel.Value]) : List[Boolean] = {
+ import scala.collection.mutable.ListBuffer
+ val list = ListBuffer.fill(9)(false)
+ filters.foreach(channel => { list(channel.id) = true })
+ list.toList
+ }
+
+ implicit val codec : Codec[SetChatFilterMessage] = (
+ ("send_channel" | ChatChannel.codec) ::
+ ("origin" | bool) ::
+ ("whitelist" | PacketHelpers.listOfNSized(9, bool))
+ ).exmap[SetChatFilterMessage] (
+ {
+ case a :: b :: c :: HNil =>
+ Attempt.Successful(SetChatFilterMessage(a, b, stateArrayToChannelFilters(c)))
+ },
+ {
+ case SetChatFilterMessage(a, b, c) =>
+ Attempt.Successful(a :: b :: channelFiltersToStateArray(c) :: HNil)
+ }
+ )
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/ZoneForcedCavernConnectionsMessage.scala b/common/src/main/scala/net/psforever/packet/game/ZoneForcedCavernConnectionsMessage.scala
index fc35796ec..0a10fe33a 100644
--- a/common/src/main/scala/net/psforever/packet/game/ZoneForcedCavernConnectionsMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/ZoneForcedCavernConnectionsMessage.scala
@@ -10,7 +10,7 @@ import scodec.codecs._
* @param zone the zone
* @param unk na
*/
-final case class ZoneForcedCavernConnectionsMessage(zone : PlanetSideGUID,
+final case class ZoneForcedCavernConnectionsMessage(zone : Int,
unk : Int)
extends PlanetSideGamePacket {
type Packet = ZoneForcedCavernConnectionsMessage
@@ -20,7 +20,7 @@ final case class ZoneForcedCavernConnectionsMessage(zone : PlanetSideGUID,
object ZoneForcedCavernConnectionsMessage extends Marshallable[ZoneForcedCavernConnectionsMessage] {
implicit val codec : Codec[ZoneForcedCavernConnectionsMessage] = (
- ("zone" | PlanetSideGUID.codec) ::
+ ("zone" | uint16L) ::
("unk" | uint2L)
).as[ZoneForcedCavernConnectionsMessage]
}
diff --git a/common/src/main/scala/net/psforever/packet/game/ZoneLockInfoMessage.scala b/common/src/main/scala/net/psforever/packet/game/ZoneLockInfoMessage.scala
index 83ae5609c..6d0f6d5ac 100644
--- a/common/src/main/scala/net/psforever/packet/game/ZoneLockInfoMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/ZoneLockInfoMessage.scala
@@ -14,7 +14,7 @@ import scodec.codecs._
* @param unk na;
* usually `true`
*/
-final case class ZoneLockInfoMessage(zone : PlanetSideGUID,
+final case class ZoneLockInfoMessage(zone : Int,
lock_status : Boolean,
unk : Boolean)
extends PlanetSideGamePacket {
@@ -25,7 +25,7 @@ final case class ZoneLockInfoMessage(zone : PlanetSideGUID,
object ZoneLockInfoMessage extends Marshallable[ZoneLockInfoMessage] {
implicit val codec : Codec[ZoneLockInfoMessage] = (
- ("zone" | PlanetSideGUID.codec) ::
+ ("zone" | uint16L) ::
("lock_status" | bool) ::
("unk" | bool)
).as[ZoneLockInfoMessage]
diff --git a/common/src/main/scala/net/psforever/packet/game/ZonePopulationUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ZonePopulationUpdateMessage.scala
index 22f3a43d0..0235913d0 100644
--- a/common/src/main/scala/net/psforever/packet/game/ZonePopulationUpdateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/ZonePopulationUpdateMessage.scala
@@ -32,7 +32,7 @@ import scodec.codecs._
* Sanctuary zones possess strange queue values that are occasionally zero'd.
* They do not have a lock icon and may not limit populations the same way as normal zones.
*
- * @param continent_guid identifies the zone (continent)
+ * @param zone_id the continent
* @param zone_queue the maximum population of all three (four) empires that can join this zone
* @param tr_queue the maximum number of TR players that can join this zone
* @param tr_pop the current TR population in this zone
@@ -43,7 +43,7 @@ import scodec.codecs._
* @param bo_queue the maximum number of Black OPs players that can join this zone
* @param bo_pop the current Black OPs population in this zone
*/
-final case class ZonePopulationUpdateMessage(continent_guid : PlanetSideGUID,
+final case class ZonePopulationUpdateMessage(zone_id : Int,
zone_queue : Long,
tr_queue : Long,
tr_pop : Long,
@@ -61,7 +61,7 @@ final case class ZonePopulationUpdateMessage(continent_guid : PlanetSideGUID,
object ZonePopulationUpdateMessage extends Marshallable[ZonePopulationUpdateMessage] {
implicit val codec : Codec[ZonePopulationUpdateMessage] = (
- ("continent_guid" | PlanetSideGUID.codec) ::
+ ("zone_id" | uint16L) ::
("zone_queue" | uint32L) ::
("tr_queue" | uint32L) :: ("tr_pop" | uint32L) ::
("nc_queue" | uint32L) :: ("nc_pop" | uint32L) ::
diff --git a/common/src/test/scala/game/AvatarImplantMessageTest.scala b/common/src/test/scala/game/AvatarImplantMessageTest.scala
index c52d50db0..78b269603 100644
--- a/common/src/test/scala/game/AvatarImplantMessageTest.scala
+++ b/common/src/test/scala/game/AvatarImplantMessageTest.scala
@@ -4,7 +4,6 @@ package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
-import net.psforever.types.ImplantType
import scodec.bits._
class AvatarImplantMessageTest extends Specification {
@@ -14,7 +13,7 @@ class AvatarImplantMessageTest extends Specification {
PacketCoding.DecodePacket(string).require match {
case AvatarImplantMessage(player_guid, unk1, unk2, implant) =>
player_guid mustEqual PlanetSideGUID(3171)
- unk1 mustEqual 3
+ unk1 mustEqual ImplantAction.Activation
unk2 mustEqual 1
implant mustEqual 1
case _ =>
@@ -23,7 +22,7 @@ class AvatarImplantMessageTest extends Specification {
}
"encode" in {
- val msg = AvatarImplantMessage(PlanetSideGUID(3171), 3, 1, 1)
+ val msg = AvatarImplantMessage(PlanetSideGUID(3171), ImplantAction.Activation, 1, 1)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
diff --git a/common/src/test/scala/game/AvatarSearchCriteriaMessageTest.scala b/common/src/test/scala/game/AvatarSearchCriteriaMessageTest.scala
new file mode 100644
index 000000000..d0175d061
--- /dev/null
+++ b/common/src/test/scala/game/AvatarSearchCriteriaMessageTest.scala
@@ -0,0 +1,49 @@
+// Copyright (c) 2017 PSForever
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import scodec.bits._
+
+class AvatarSearchCriteriaMessageTest extends Specification {
+ val string = hex"64 C604 00 00 00 00 00 00"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case AvatarSearchCriteriaMessage(unk1, unk2) =>
+ unk1 mustEqual PlanetSideGUID(1222)
+ unk2.length mustEqual 6
+ unk2.head mustEqual 0
+ unk2(1) mustEqual 0
+ unk2(2) mustEqual 0
+ unk2(3) mustEqual 0
+ unk2(4) mustEqual 0
+ unk2(5) mustEqual 0
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = AvatarSearchCriteriaMessage(PlanetSideGUID(1222), List(0, 0, 0, 0, 0, 0))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+
+ "encode (failure; wrong number of list entries)" in {
+ val msg = AvatarSearchCriteriaMessage(PlanetSideGUID(1222), List(0))
+ PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
+ }
+
+ "encode (failure; list number too big)" in {
+ val msg = AvatarSearchCriteriaMessage(PlanetSideGUID(1222), List(0, 0, 0, 0, 0, 256))
+ PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
+ }
+
+ "encode (failure; list number too small)" in {
+ val msg = AvatarSearchCriteriaMessage(PlanetSideGUID(1222), List(0, 0, 0, -1, 0, 0))
+ PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
+ }
+}
diff --git a/common/src/test/scala/game/AvatarStatisticsMessageTest.scala b/common/src/test/scala/game/AvatarStatisticsMessageTest.scala
new file mode 100644
index 000000000..bca185fb5
--- /dev/null
+++ b/common/src/test/scala/game/AvatarStatisticsMessageTest.scala
@@ -0,0 +1,79 @@
+// Copyright (c) 2017 PSForever
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import scodec.bits._
+
+class AvatarStatisticsMessageTest extends Specification {
+ val string_long = hex"7F 4 00000000 0"
+ val string_complex = hex"7F 01 3C 40 20 00 00 00 C0 00 00 00 00 00 00 00 20 00 00 00 20 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00"
+
+ "decode (long)" in {
+ PacketCoding.DecodePacket(string_long).require match {
+ case AvatarStatisticsMessage(unk, stats) =>
+ unk mustEqual 2
+ stats.unk1 mustEqual None
+ stats.unk2 mustEqual None
+ stats.unk3.length mustEqual 1
+ stats.unk3.head mustEqual 0
+ case _ =>
+ ko
+ }
+ }
+
+ "decode (complex)" in {
+ PacketCoding.DecodePacket(string_complex).require match {
+ case AvatarStatisticsMessage(unk, stats) =>
+ unk mustEqual 0
+ stats.unk1 mustEqual Some(1)
+ stats.unk2 mustEqual Some(572)
+ stats.unk3.length mustEqual 8
+ stats.unk3.head mustEqual 1
+ stats.unk3(1) mustEqual 6
+ stats.unk3(2) mustEqual 0
+ stats.unk3(3) mustEqual 1
+ stats.unk3(4) mustEqual 1
+ stats.unk3(5) mustEqual 2
+ stats.unk3(6) mustEqual 0
+ stats.unk3(7) mustEqual 0
+ case _ =>
+ ko
+ }
+ }
+
+ "encode (long)" in {
+ val msg = AvatarStatisticsMessage(2, Statistics(0L))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_long
+ }
+
+ "encode (complex)" in {
+ val msg = AvatarStatisticsMessage(0, Statistics(1, 572, List[Long](1,6,0,1,1,2,0,0)))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_complex
+ }
+
+ "encode (failure; long; missing value)" in {
+ val msg = AvatarStatisticsMessage(0, Statistics(None, None,List(0L)))
+ PacketCoding.EncodePacket(msg).isFailure mustEqual true
+ }
+
+ "encode (failure; complex; missing value (5-bit))" in {
+ val msg = AvatarStatisticsMessage(0, Statistics(None, Some(572), List[Long](1,6,0,1,1,2,0,0)))
+ PacketCoding.EncodePacket(msg).isFailure mustEqual true
+ }
+
+ "encode (failure; complex; missing value (11-bit))" in {
+ val msg = AvatarStatisticsMessage(0, Statistics(Some(1), None, List[Long](1,6,0,1,1,2,0,0)))
+ PacketCoding.EncodePacket(msg).isFailure mustEqual true
+ }
+
+ "encode (failure; complex; wrong number of list entries)" in {
+ val msg = AvatarStatisticsMessage(0, Statistics(Some(1), None, List[Long](1,6,0,1)))
+ PacketCoding.EncodePacket(msg).isFailure mustEqual true
+ }
+}
diff --git a/common/src/test/scala/game/BroadcastWarpgateUpdateMessageTest.scala b/common/src/test/scala/game/BroadcastWarpgateUpdateMessageTest.scala
index 49bf1221c..eaeed5e8b 100644
--- a/common/src/test/scala/game/BroadcastWarpgateUpdateMessageTest.scala
+++ b/common/src/test/scala/game/BroadcastWarpgateUpdateMessageTest.scala
@@ -12,8 +12,8 @@ class BroadcastWarpgateUpdateMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
case BroadcastWarpgateUpdateMessage(continent_guid, building_guid, state1, state2, state3) =>
- continent_guid mustEqual PlanetSideGUID(13)
- building_guid mustEqual PlanetSideGUID(1)
+ continent_guid mustEqual 13
+ building_guid mustEqual 1
state1 mustEqual false
state2 mustEqual false
state3 mustEqual true
@@ -23,7 +23,7 @@ class BroadcastWarpgateUpdateMessageTest extends Specification {
}
"encode" in {
- val msg = BroadcastWarpgateUpdateMessage(PlanetSideGUID(13), PlanetSideGUID(1), false, false, true)
+ val msg = BroadcastWarpgateUpdateMessage(13, 1, false, false, true)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
diff --git a/common/src/test/scala/game/BuildingInfoUpdateMessageTest.scala b/common/src/test/scala/game/BuildingInfoUpdateMessageTest.scala
index edbd476f5..983f25beb 100644
--- a/common/src/test/scala/game/BuildingInfoUpdateMessageTest.scala
+++ b/common/src/test/scala/game/BuildingInfoUpdateMessageTest.scala
@@ -12,29 +12,29 @@ class BuildingInfoUpdateMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
- case BuildingInfoUpdateMessage(continent_guid : PlanetSideGUID,
- building_guid : PlanetSideGUID,
- ntu_level : Int,
- is_hacked : Boolean,
- empire_hack : PlanetSideEmpire.Value,
- hack_time_remaining : Long,
- empire_own : PlanetSideEmpire.Value,
- unk1 : Long,
- unk1x : Option[Additional1],
- generator_state : PlanetSideGeneratorState.Value,
- spawn_tubes_normal : Boolean,
- force_dome_active : Boolean,
- lattice_benefit : Int,
- unk3 : Int,
- unk4 : List[Additional2],
- unk5 : Long,
- unk6 : Boolean,
- unk7 : Int,
- unk7x : Option[Additional3],
- boost_spawn_pain : Boolean,
- boost_generator_pain : Boolean) =>
- continent_guid mustEqual PlanetSideGUID(4)
- building_guid mustEqual PlanetSideGUID(9)
+ case BuildingInfoUpdateMessage(continent_guid,
+ building_guid,
+ ntu_level,
+ is_hacked,
+ empire_hack,
+ hack_time_remaining,
+ empire_own,
+ unk1,
+ unk1x,
+ generator_state,
+ spawn_tubes_normal,
+ force_dome_active,
+ lattice_benefit,
+ unk3,
+ unk4,
+ unk5,
+ unk6,
+ unk7,
+ unk7x,
+ boost_spawn_pain,
+ boost_generator_pain) =>
+ continent_guid mustEqual 4
+ building_guid mustEqual 9
ntu_level mustEqual 1
is_hacked mustEqual false
empire_hack mustEqual PlanetSideEmpire.NEUTRAL
@@ -61,8 +61,8 @@ class BuildingInfoUpdateMessageTest extends Specification {
}
"encode" in {
- val msg = BuildingInfoUpdateMessage(PlanetSideGUID(4),
- PlanetSideGUID(9),
+ val msg = BuildingInfoUpdateMessage(4,
+ 9,
1,
false,
PlanetSideEmpire.NEUTRAL,
diff --git a/common/src/test/scala/game/ContinentalLockUpdateMessageTest.scala b/common/src/test/scala/game/ContinentalLockUpdateMessageTest.scala
index 3f231341e..4db015235 100644
--- a/common/src/test/scala/game/ContinentalLockUpdateMessageTest.scala
+++ b/common/src/test/scala/game/ContinentalLockUpdateMessageTest.scala
@@ -13,7 +13,7 @@ class ContinentalLockUpdateMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
case ContinentalLockUpdateMessage(continent_guid, empire) =>
- continent_guid mustEqual PlanetSideGUID(22)
+ continent_guid mustEqual 22
empire mustEqual PlanetSideEmpire.NC
case _ =>
ko
@@ -21,7 +21,7 @@ class ContinentalLockUpdateMessageTest extends Specification {
}
"encode" in {
- val msg = ContinentalLockUpdateMessage(PlanetSideGUID(22), PlanetSideEmpire.NC)
+ val msg = ContinentalLockUpdateMessage(22, PlanetSideEmpire.NC)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
diff --git a/common/src/test/scala/game/DensityLevelUpdateMessageTest.scala b/common/src/test/scala/game/DensityLevelUpdateMessageTest.scala
new file mode 100644
index 000000000..aa5e1f551
--- /dev/null
+++ b/common/src/test/scala/game/DensityLevelUpdateMessageTest.scala
@@ -0,0 +1,52 @@
+// Copyright (c) 2017 PSForever
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import scodec.bits._
+
+class DensityLevelUpdateMessageTest extends Specification {
+ val string = hex"cd 0100 1f4e 000000"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case DensityLevelUpdateMessage(zone_id, building_id, unk) =>
+ zone_id mustEqual 1
+ building_id mustEqual 19999
+ unk.length mustEqual 8
+ unk.head mustEqual 0
+ unk(1) mustEqual 0
+ unk(2) mustEqual 0
+ unk(3) mustEqual 0
+ unk(4) mustEqual 0
+ unk(5) mustEqual 0
+ unk(6) mustEqual 0
+ unk(7) mustEqual 0
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = DensityLevelUpdateMessage(1, 19999, List(0,0, 0,0, 0,0, 0,0))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+
+ "encode (failure; wrong number of list entries)" in {
+ val msg = DensityLevelUpdateMessage(1, 19999, List(0))
+ PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
+ }
+
+ "encode (failure; list number too big)" in {
+ val msg = DensityLevelUpdateMessage(1, 19999, List(0,0, 0,0, 0,0, 0,8))
+ PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
+ }
+
+ "encode (failure; list number too small)" in {
+ val msg = DensityLevelUpdateMessage(1, 19999, List(0,0, 0,0, 0,-1, 0,0))
+ PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
+ }
+}
diff --git a/common/src/test/scala/game/FriendsResponseTest.scala b/common/src/test/scala/game/FriendsResponseTest.scala
index 47a13c13c..3b30d6b5f 100644
--- a/common/src/test/scala/game/FriendsResponseTest.scala
+++ b/common/src/test/scala/game/FriendsResponseTest.scala
@@ -14,7 +14,7 @@ class FriendsResponseTest extends Specification {
"decode (one friend)" in {
PacketCoding.DecodePacket(stringOneFriend).require match {
case FriendsResponse(action, unk2, unk3, unk4, list) =>
- action mustEqual 3
+ action mustEqual FriendAction.UpdateFriend
unk2 mustEqual 0
unk3 mustEqual true
unk4 mustEqual true
@@ -29,7 +29,7 @@ class FriendsResponseTest extends Specification {
"decode (multiple friends)" in {
PacketCoding.DecodePacket(stringManyFriends).require match {
case FriendsResponse(action, unk2, unk3, unk4, list) =>
- action mustEqual 0
+ action mustEqual FriendAction.InitializeFriendList
unk2 mustEqual 0
unk3 mustEqual true
unk4 mustEqual true
@@ -52,7 +52,7 @@ class FriendsResponseTest extends Specification {
"decode (short)" in {
PacketCoding.DecodePacket(stringShort).require match {
case FriendsResponse(action, unk2, unk3, unk4, list) =>
- action mustEqual 4
+ action mustEqual FriendAction.InitializeIgnoreList
unk2 mustEqual 0
unk3 mustEqual true
unk4 mustEqual true
@@ -63,7 +63,7 @@ class FriendsResponseTest extends Specification {
}
"encode (one friend)" in {
- val msg = FriendsResponse(3, 0, true, true,
+ val msg = FriendsResponse(FriendAction.UpdateFriend, 0, true, true,
Friend("KurtHectic-G", false) ::
Nil)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@@ -72,7 +72,7 @@ class FriendsResponseTest extends Specification {
}
"encode (multiple friends)" in {
- val msg = FriendsResponse(0, 0, true, true,
+ val msg = FriendsResponse(FriendAction.InitializeFriendList, 0, true, true,
Friend("Angello-W", false) ::
Friend("thephattphrogg", false) ::
Friend("Kimpossible12", false) ::
@@ -85,7 +85,7 @@ class FriendsResponseTest extends Specification {
}
"encode (short)" in {
- val msg = FriendsResponse(4, 0, true, true)
+ val msg = FriendsResponse(FriendAction.InitializeIgnoreList, 0, true, true)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringShort
diff --git a/common/src/test/scala/game/HotSpotUpdateMessageTest.scala b/common/src/test/scala/game/HotSpotUpdateMessageTest.scala
index 129f508c1..72413f284 100644
--- a/common/src/test/scala/game/HotSpotUpdateMessageTest.scala
+++ b/common/src/test/scala/game/HotSpotUpdateMessageTest.scala
@@ -4,17 +4,19 @@ package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
+import net.psforever.types.Vector3
import scodec.bits._
class HotSpotUpdateMessageTest extends Specification {
- val stringClear = hex"9F 0500 1 00 0"
- val stringOne = hex"9F 0500 1 01 0 00 2E9 00 145 80000 0"
- val stringTwo = hex"9F 0500 5 02 0 00 D07 00 8CA 80000 00 BEA 00 4C4 80000"
+ val stringClear = hex"9F 0500 1 000"
+ val stringOne = hex"9F 0500 1 010 002E9 00145 80000 0"
+ val stringTwo = hex"9F 0500 5 020 00D07 008CA 80000 00BEA 004C4 80000"
+ val stringThree = hex"9F 0A00 4 030 00FC8 00F0A 80000 002E9 00BEA 80000 00FC8 00BEA 80000 0"
"decode (clear)" in {
PacketCoding.DecodePacket(stringClear).require match {
- case HotSpotUpdateMessage(continent_guid, unk, spots) =>
- continent_guid mustEqual PlanetSideGUID(5)
+ case HotSpotUpdateMessage(continent_id, unk, spots) =>
+ continent_id mustEqual 5
unk mustEqual 1
spots.size mustEqual 0
case _ =>
@@ -24,13 +26,11 @@ class HotSpotUpdateMessageTest extends Specification {
"decode (one)" in {
PacketCoding.DecodePacket(stringOne).require match {
- case HotSpotUpdateMessage(continent_guid, unk, spots) =>
- continent_guid mustEqual PlanetSideGUID(5)
+ case HotSpotUpdateMessage(continent_id, unk, spots) =>
+ continent_id mustEqual 5
unk mustEqual 1
spots.size mustEqual 1
- spots.head.x mustEqual 4700.0f
- spots.head.y mustEqual 2600.0f
- spots.head.scale mustEqual 64.0f
+ spots.head mustEqual HotSpotInfo(4700.0f, 2600.0f, 64.0f)
case _ =>
ko
}
@@ -38,36 +38,52 @@ class HotSpotUpdateMessageTest extends Specification {
"decode (two)" in {
PacketCoding.DecodePacket(stringTwo).require match {
- case HotSpotUpdateMessage(continent_guid, unk, spots) =>
- continent_guid mustEqual PlanetSideGUID(5)
+ case HotSpotUpdateMessage(continent_id, unk, spots) =>
+ continent_id mustEqual 5
unk mustEqual 5
spots.size mustEqual 2
- spots.head.x mustEqual 4000.0f
- spots.head.y mustEqual 5400.0f
- spots.head.scale mustEqual 64.0f
- spots(1).x mustEqual 5500.0f
- spots(1).y mustEqual 2200.0f
- spots(1).scale mustEqual 64.0f
+ spots.head mustEqual HotSpotInfo(4000.0f, 5400.0f, 64.0f)
+ spots(1) mustEqual HotSpotInfo(5500.0f, 2200.0f, 64.0f)
+ case _ =>
+ ko
+ }
+ }
+
+ "decode (three)" in {
+ PacketCoding.DecodePacket(stringThree).require match {
+ case HotSpotUpdateMessage(continent_id, unk, spots) =>
+ continent_id mustEqual 10
+ unk mustEqual 4
+ spots.size mustEqual 3
+ spots.head mustEqual HotSpotInfo(4600.0f, 5600.0f, 64.0f)
+ spots(1) mustEqual HotSpotInfo(4700.0f, 5500.0f, 64.0f)
+ spots(2) mustEqual HotSpotInfo(4600.0f, 5500.0f, 64.0f)
case _ =>
ko
}
}
"encode (clear)" in {
- val msg = HotSpotUpdateMessage(PlanetSideGUID(5),1)
+ val msg = HotSpotUpdateMessage(5,1, Nil)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringClear
}
"encode (one)" in {
- val msg = HotSpotUpdateMessage(PlanetSideGUID(5),1, HotSpotInfo(4700.0f, 2600.0f, 64.0f)::Nil)
+ val msg = HotSpotUpdateMessage(5,1, List(HotSpotInfo(4700.0f, 2600.0f, 64.0f)))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringOne
}
"encode (two)" in {
- val msg = HotSpotUpdateMessage(PlanetSideGUID(5),5, HotSpotInfo(4000.0f, 5400.0f, 64.0f)::HotSpotInfo(5500.0f, 2200.0f, 64.0f)::Nil)
+ val msg = HotSpotUpdateMessage(5,5, List(HotSpotInfo(4000.0f, 5400.0f, 64.0f), HotSpotInfo(5500.0f, 2200.0f, 64.0f)))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringTwo
}
+
+ "encode (three)" in {
+ val msg = HotSpotUpdateMessage(10,4, List(HotSpotInfo(4600.0f, 5600.0f, 64.0f), HotSpotInfo(4700.0f, 5500.0f, 64.0f), HotSpotInfo(4600.0f, 5500.0f, 64.0f)))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+ pkt mustEqual stringThree
+ }
}
diff --git a/common/src/test/scala/game/SetChatFilterMessageTest.scala b/common/src/test/scala/game/SetChatFilterMessageTest.scala
new file mode 100644
index 000000000..7bf9f5185
--- /dev/null
+++ b/common/src/test/scala/game/SetChatFilterMessageTest.scala
@@ -0,0 +1,75 @@
+// Copyright (c) 2017 PSForever
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import scodec.bits._
+
+class SetChatFilterMessageTest extends Specification {
+ val string = hex"63 05FF80"
+ val string_custom = hex"63 05C180"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case SetChatFilterMessage(send, origin, filters) =>
+ send mustEqual ChatChannel.Local
+ origin mustEqual true
+ filters.length mustEqual 9
+ filters.head mustEqual ChatChannel.Unknown
+ filters(1) mustEqual ChatChannel.Tells
+ filters(2) mustEqual ChatChannel.Local
+ filters(3) mustEqual ChatChannel.Squad
+ filters(4) mustEqual ChatChannel.Outfit
+ filters(5) mustEqual ChatChannel.Command
+ filters(6) mustEqual ChatChannel.Platoon
+ filters(7) mustEqual ChatChannel.Broadcast
+ filters(8) mustEqual ChatChannel.SquadLeader
+ case _ =>
+ ko
+ }
+ }
+
+ "decode (custom)" in {
+ PacketCoding.DecodePacket(string_custom).require match {
+ case SetChatFilterMessage(send, origin, filters) =>
+ send mustEqual ChatChannel.Local
+ origin mustEqual true
+ filters.length mustEqual 4
+ filters.head mustEqual ChatChannel.Unknown
+ filters(1) mustEqual ChatChannel.Tells
+ filters(2) mustEqual ChatChannel.Broadcast
+ filters(3) mustEqual ChatChannel.SquadLeader
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = SetChatFilterMessage(ChatChannel.Local, true, List(ChatChannel.Unknown, ChatChannel.Tells, ChatChannel.Local, ChatChannel.Squad, ChatChannel.Outfit, ChatChannel.Command, ChatChannel.Platoon, ChatChannel.Broadcast, ChatChannel.SquadLeader))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+
+ "encode (success; same channel listed multiple times)" in {
+ val msg = SetChatFilterMessage(ChatChannel.Local, true, List(ChatChannel.Unknown, ChatChannel.Unknown, ChatChannel.Tells, ChatChannel.Tells, ChatChannel.Local, ChatChannel.Squad, ChatChannel.Outfit, ChatChannel.Command, ChatChannel.Platoon, ChatChannel.Broadcast, ChatChannel.SquadLeader))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+
+ "encode (success; out of order)" in {
+ val msg = SetChatFilterMessage(ChatChannel.Local, true, List(ChatChannel.Squad, ChatChannel.Outfit, ChatChannel.SquadLeader, ChatChannel.Unknown, ChatChannel.Command, ChatChannel.Platoon, ChatChannel.Broadcast, ChatChannel.Tells, ChatChannel.Local))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+
+ "encode (success; custom)" in {
+ val msg = SetChatFilterMessage(ChatChannel.Local, true, List(ChatChannel.Unknown, ChatChannel.Tells, ChatChannel.Broadcast, ChatChannel.SquadLeader))
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string_custom
+ }
+}
diff --git a/common/src/test/scala/game/ZoneForcedCavernConnectionsMessageTest.scala b/common/src/test/scala/game/ZoneForcedCavernConnectionsMessageTest.scala
index d963928d1..a31736ae3 100644
--- a/common/src/test/scala/game/ZoneForcedCavernConnectionsMessageTest.scala
+++ b/common/src/test/scala/game/ZoneForcedCavernConnectionsMessageTest.scala
@@ -13,7 +13,7 @@ class ZoneForcedCavernConnectionsMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
case ZoneForcedCavernConnectionsMessage(zone, unk) =>
- zone mustEqual PlanetSideGUID(32)
+ zone mustEqual 32
unk mustEqual 1
case _ =>
ko
@@ -21,7 +21,7 @@ class ZoneForcedCavernConnectionsMessageTest extends Specification {
}
"encode" in {
- val msg = ZoneForcedCavernConnectionsMessage(PlanetSideGUID(32), 1)
+ val msg = ZoneForcedCavernConnectionsMessage(32, 1)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
diff --git a/common/src/test/scala/game/ZoneLockInfoMesageTest.scala b/common/src/test/scala/game/ZoneLockInfoMesageTest.scala
index 01fb3316e..a4238a38d 100644
--- a/common/src/test/scala/game/ZoneLockInfoMesageTest.scala
+++ b/common/src/test/scala/game/ZoneLockInfoMesageTest.scala
@@ -12,7 +12,7 @@ class ZoneLockInfoMesageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
case ZoneLockInfoMessage(zone, locked, unk) =>
- zone mustEqual PlanetSideGUID(27)
+ zone mustEqual 27
locked mustEqual false
unk mustEqual true
case _ =>
@@ -21,7 +21,7 @@ class ZoneLockInfoMesageTest extends Specification {
}
"encode" in {
- val msg = ZoneLockInfoMessage(PlanetSideGUID(27), false, true)
+ val msg = ZoneLockInfoMessage(27, false, true)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
diff --git a/common/src/test/scala/game/ZonePopulationUpdateMessageTest.scala b/common/src/test/scala/game/ZonePopulationUpdateMessageTest.scala
index c43832fb8..468002180 100644
--- a/common/src/test/scala/game/ZonePopulationUpdateMessageTest.scala
+++ b/common/src/test/scala/game/ZonePopulationUpdateMessageTest.scala
@@ -12,7 +12,7 @@ class ZonePopulationUpdateMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
case ZonePopulationUpdateMessage(continent_guid, zone_queue, tr_queue, tr_pop, nc_queue, nc_pop, vs_queue, vs_pop, bo_queue, bo_pop) =>
- continent_guid mustEqual PlanetSideGUID(4)
+ continent_guid mustEqual 4
zone_queue mustEqual 414
tr_queue mustEqual 138
tr_pop mustEqual 37
@@ -28,7 +28,7 @@ class ZonePopulationUpdateMessageTest extends Specification {
}
"encode" in {
- val msg = ZonePopulationUpdateMessage(PlanetSideGUID(4), 414, 138, 37, 138, 37, 138, 37, 138, 37)
+ val msg = ZonePopulationUpdateMessage(4, 414, 138, 37, 138, 37, 138, 37, 138, 37)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
diff --git a/common/src/test/scala/objects/BuildingTest.scala b/common/src/test/scala/objects/BuildingTest.scala
index 122f3d993..dc533b1b8 100644
--- a/common/src/test/scala/objects/BuildingTest.scala
+++ b/common/src/test/scala/objects/BuildingTest.scala
@@ -6,7 +6,7 @@ import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.{Door, DoorControl}
-import net.psforever.objects.serverobject.structures.{Amenity, Building, BuildingControl}
+import net.psforever.objects.serverobject.structures.{Amenity, Building, BuildingControl, WarpGate}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire
@@ -98,6 +98,19 @@ class BuildingTest extends Specification {
}
}
+class WarpGateTest extends Specification {
+ "WarpGate" should {
+ "construct" in {
+ val bldg = WarpGate(10, Zone.Nowhere)
+ bldg.Id mustEqual 10
+ bldg.Actor mustEqual ActorRef.noSender
+ bldg.Amenities mustEqual Nil
+ bldg.Zone mustEqual Zone.Nowhere
+ bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL
+ }
+ }
+}
+
class BuildingControl1Test extends ActorTest {
"Building Control" should {
"construct" in {
diff --git a/common/src/test/scala/objects/ServerObjectBuilderTest.scala b/common/src/test/scala/objects/ServerObjectBuilderTest.scala
index 73f802031..903e2e50d 100644
--- a/common/src/test/scala/objects/ServerObjectBuilderTest.scala
+++ b/common/src/test/scala/objects/ServerObjectBuilderTest.scala
@@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever
package objects
-import akka.actor.{Actor, Props}
+import akka.actor.{Actor, ActorContext, Props}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.objects.serverobject.ServerObjectBuilder
-import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder}
+import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate}
import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3
@@ -14,7 +14,23 @@ import scala.concurrent.duration.Duration
class BuildingBuilderTest extends ActorTest {
"Building object" should {
"build" in {
- val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], 10, Zone.Nowhere), "building")
+ val structure : (Int,Zone,ActorContext)=>Building = Building.Structure
+ val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, 10, Zone.Nowhere), "building")
+ actor ! "!"
+
+ val reply = receiveOne(Duration.create(1000, "ms"))
+ assert(reply.isInstanceOf[Building])
+ assert(reply.asInstanceOf[Building].Id == 10)
+ assert(reply.asInstanceOf[Building].Zone == Zone.Nowhere)
+ }
+ }
+}
+
+class WarpGateBuilderTest extends ActorTest {
+ "WarpGate object" should {
+ "build" in {
+ val structure : (Int,Zone,ActorContext)=>Building = WarpGate.Structure
+ val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, 10, Zone.Nowhere), "wgate")
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
@@ -167,10 +183,10 @@ object ServerObjectBuilderTest {
}
}
- class BuildingTestActor(building_id : Int, zone : Zone) extends Actor {
+ class BuildingTestActor(structure_con : (Int,Zone,ActorContext)=>Building, building_id : Int, zone : Zone) extends Actor {
def receive : Receive = {
case _ =>
- sender ! FoundationBuilder(Building.Structure).Build(building_id, zone)(context)
+ sender ! FoundationBuilder(structure_con).Build(building_id, zone)(context)
}
}
}
diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala
index d8c854c8c..ab19d2dbb 100644
--- a/pslogin/src/main/scala/Maps.scala
+++ b/pslogin/src/main/scala/Maps.scala
@@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
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.structures.{Building, FoundationBuilder}
+import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.types.Vector3
@@ -48,15 +48,27 @@ object Maps {
val map12 = new ZoneMap("map12")
val map13 = new ZoneMap("map13") {
- LocalObject(ServerObjectBuilder(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180))))
- LocalObject(ServerObjectBuilder(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180))))
- LocalObject(ServerObjectBuilder(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0))))
- LocalObject(ServerObjectBuilder(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0))))
+ LocalBuilding(1, FoundationBuilder(WarpGate.Structure))
+ LocalBuilding(2, FoundationBuilder(WarpGate.Structure))
+ LocalBuilding(3, FoundationBuilder(WarpGate.Structure))
+
+ LocalObject(ServerObjectBuilder(372, Door.Constructor))
+ LocalObject(ServerObjectBuilder(373, Door.Constructor))
+
+ LocalObject(ServerObjectBuilder(520, ImplantTerminalMech.Constructor)) //Hart B
+ LocalObject(ServerObjectBuilder(853, Terminal.Constructor(order_terminal)))
+ LocalObject(ServerObjectBuilder(855, Terminal.Constructor(order_terminal)))
+ LocalObject(ServerObjectBuilder(860, Terminal.Constructor(order_terminal)))
+ LocalObject(ServerObjectBuilder(1081, Terminal.Constructor(implant_terminal_interface))) //tube 520
+ TerminalToInterface(520, 1081)
+
+ LocalBuilding(2, FoundationBuilder(Building.Structure)) //HART building C
+ LocalObject(ServerObjectBuilder(186, Terminal.Constructor(cert_terminal)))
+ LocalObject(ServerObjectBuilder(187, Terminal.Constructor(cert_terminal)))
+ LocalObject(ServerObjectBuilder(188, Terminal.Constructor(cert_terminal)))
LocalObject(ServerObjectBuilder(362, Door.Constructor))
LocalObject(ServerObjectBuilder(370, Door.Constructor))
LocalObject(ServerObjectBuilder(371, Door.Constructor))
- LocalObject(ServerObjectBuilder(372, Door.Constructor))
- LocalObject(ServerObjectBuilder(373, Door.Constructor))
LocalObject(ServerObjectBuilder(374, Door.Constructor))
LocalObject(ServerObjectBuilder(375, Door.Constructor))
LocalObject(ServerObjectBuilder(394, Door.Constructor))
@@ -66,7 +78,6 @@ object Maps {
LocalObject(ServerObjectBuilder(398, Door.Constructor))
LocalObject(ServerObjectBuilder(462, Door.Constructor))
LocalObject(ServerObjectBuilder(463, Door.Constructor))
- LocalObject(ServerObjectBuilder(520, ImplantTerminalMech.Constructor)) //Hart B
LocalObject(ServerObjectBuilder(522, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(523, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(524, ImplantTerminalMech.Constructor)) //Hart C
@@ -75,10 +86,6 @@ object Maps {
LocalObject(ServerObjectBuilder(527, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(528, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(529, ImplantTerminalMech.Constructor)) //Hart C
- LocalObject(ServerObjectBuilder(556, IFFLock.Constructor))
- LocalObject(ServerObjectBuilder(557, IFFLock.Constructor))
- LocalObject(ServerObjectBuilder(558, IFFLock.Constructor))
- LocalObject(ServerObjectBuilder(559, IFFLock.Constructor))
LocalObject(ServerObjectBuilder(686, Locker.Constructor))
LocalObject(ServerObjectBuilder(687, Locker.Constructor))
LocalObject(ServerObjectBuilder(688, Locker.Constructor))
@@ -87,17 +94,10 @@ object Maps {
LocalObject(ServerObjectBuilder(691, Locker.Constructor))
LocalObject(ServerObjectBuilder(692, Locker.Constructor))
LocalObject(ServerObjectBuilder(693, Locker.Constructor))
- LocalObject(ServerObjectBuilder(186, Terminal.Constructor(cert_terminal)))
- LocalObject(ServerObjectBuilder(187, Terminal.Constructor(cert_terminal)))
- LocalObject(ServerObjectBuilder(188, Terminal.Constructor(cert_terminal)))
LocalObject(ServerObjectBuilder(842, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(843, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(844, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(845, Terminal.Constructor(order_terminal)))
- LocalObject(ServerObjectBuilder(853, Terminal.Constructor(order_terminal)))
- LocalObject(ServerObjectBuilder(855, Terminal.Constructor(order_terminal)))
- LocalObject(ServerObjectBuilder(860, Terminal.Constructor(order_terminal)))
- LocalObject(ServerObjectBuilder(1081, Terminal.Constructor(implant_terminal_interface))) //tube 520
LocalObject(ServerObjectBuilder(1082, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1083, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1084, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
@@ -106,16 +106,6 @@ object Maps {
LocalObject(ServerObjectBuilder(1087, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1088, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1089, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
- LocalObject(ServerObjectBuilder(1063, Terminal.Constructor(ground_vehicle_terminal)))
- LocalObject(ServerObjectBuilder(500,
- VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
- )) //TODO guid not correct
- LocalObject(ServerObjectBuilder(304, Terminal.Constructor(dropship_vehicle_terminal)))
- LocalObject(ServerObjectBuilder(501,
- VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
- )) //TODO guid not correct
-
- LocalBuilding(2, FoundationBuilder(Building.Structure))
ObjectToBuilding(186, 2)
ObjectToBuilding(187, 2)
ObjectToBuilding(188, 2)
@@ -139,30 +129,14 @@ object Maps {
ObjectToBuilding(843, 2)
ObjectToBuilding(844, 2)
ObjectToBuilding(845, 2)
- ObjectToBuilding(853, 2) //TODO check building_id
- ObjectToBuilding(855, 2) //TODO check building_id
- ObjectToBuilding(860, 2) //TODO check building_id
- ObjectToBuilding(1063, 2) //TODO unowned courtyard terminal?
- ObjectToBuilding(500, 2) //TODO unowned courtyard spawnpad?
- ObjectToBuilding(304, 2) //TODO unowned courtyard terminal?
- ObjectToBuilding(501, 2) //TODO unowned courtyard spawnpad?
-
- LocalBuilding(29, FoundationBuilder(Building.Structure))
- ObjectToBuilding(330, 29)
- ObjectToBuilding(332, 29)
- ObjectToBuilding(556, 29)
- ObjectToBuilding(558, 29)
-
- //ObjectToBuilding(1081, ?)
- //ObjectToBuilding(520, ?)
-
- DoorToLock(330, 558)
- DoorToLock(331, 559)
- DoorToLock(332, 556)
- DoorToLock(333, 557)
- TerminalToSpawnPad(1063, 500)
- TerminalToSpawnPad(304, 501)
- TerminalToInterface(520, 1081)
+ ObjectToBuilding(1082, 2)
+ ObjectToBuilding(1083, 2)
+ ObjectToBuilding(1084, 2)
+ ObjectToBuilding(1085, 2)
+ ObjectToBuilding(1086, 2)
+ ObjectToBuilding(1087, 2)
+ ObjectToBuilding(1088, 2)
+ ObjectToBuilding(1089, 2)
TerminalToInterface(522, 1082)
TerminalToInterface(523, 1083)
TerminalToInterface(524, 1084)
@@ -171,6 +145,50 @@ object Maps {
TerminalToInterface(527, 1087)
TerminalToInterface(528, 1088)
TerminalToInterface(529, 1089)
+
+ LocalBuilding(29, FoundationBuilder(Building.Structure)) //South Villa Gun Tower
+ LocalObject(ServerObjectBuilder(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180))))
+ LocalObject(ServerObjectBuilder(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180))))
+ LocalObject(ServerObjectBuilder(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0))))
+ LocalObject(ServerObjectBuilder(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0))))
+ LocalObject(ServerObjectBuilder(556, IFFLock.Constructor))
+ LocalObject(ServerObjectBuilder(557, IFFLock.Constructor))
+ LocalObject(ServerObjectBuilder(558, IFFLock.Constructor))
+ LocalObject(ServerObjectBuilder(559, IFFLock.Constructor))
+ ObjectToBuilding(330, 29)
+ ObjectToBuilding(331, 29)
+ ObjectToBuilding(332, 29)
+ ObjectToBuilding(333, 29)
+ ObjectToBuilding(556, 29)
+ ObjectToBuilding(557, 29)
+ ObjectToBuilding(558, 29)
+ ObjectToBuilding(559, 29)
+ DoorToLock(330, 558)
+ DoorToLock(331, 559)
+ DoorToLock(332, 556)
+ DoorToLock(333, 557)
+
+ LocalBuilding(51, FoundationBuilder(Building.Structure))
+ LocalObject(ServerObjectBuilder(304, Terminal.Constructor(dropship_vehicle_terminal)))
+ LocalObject(ServerObjectBuilder(292,
+ VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
+ ))
+ ObjectToBuilding(304, 51)
+ ObjectToBuilding(292, 51)
+ TerminalToSpawnPad(304, 292)
+
+ LocalBuilding(77, FoundationBuilder(Building.Structure))
+ LocalObject(ServerObjectBuilder(1063, Terminal.Constructor(ground_vehicle_terminal)))
+ LocalObject(ServerObjectBuilder(706,
+ VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
+ ))
+ ObjectToBuilding(1063, 77)
+ ObjectToBuilding(706, 77)
+ TerminalToSpawnPad(1063, 706)
+
+ ObjectToBuilding(853, 2) //TODO check building_id
+ ObjectToBuilding(855, 2) //TODO check building_id
+ ObjectToBuilding(860, 2) //TODO check building_id
}
val map14 = new ZoneMap("map13")
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index a7c9d8f3d..cbd3698d0 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -26,6 +26,9 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
+import net.psforever.objects.serverobject.structures.{Building, WarpGate}
+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._
import net.psforever.types._
@@ -798,7 +801,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
val slot = slotNumber.get
log.info(s"$message - put in slot $slot")
- sendResponse(AvatarImplantMessage(tplayer.GUID, 0, slot, implant_type.id))
+ sendResponse(AvatarImplantMessage(tplayer.GUID, ImplantAction.Add, slot, implant_type.id))
sendResponse(ItemTransactionResultMessage(terminal_guid, TransactionType.Learn, true))
}
else {
@@ -833,7 +836,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
val slot = slotNumber.get
log.info(s"$tplayer is selling $implant_type - take from slot $slot")
- sendResponse(AvatarImplantMessage(tplayer.GUID, 1, slot, 0))
+ sendResponse(AvatarImplantMessage(tplayer.GUID, ImplantAction.Remove, slot, 0))
sendResponse(ItemTransactionResultMessage(terminal_guid, TransactionType.Sell, true))
}
else {
@@ -976,40 +979,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
case VehicleLoaded(_/*vehicle*/) => ;
//currently being handled by VehicleSpawnPad.LoadVehicle during testing phase
- case Zone.ClientInitialization(/*initList*/_) =>
- //TODO iterate over initList; for now, just do this
- sendResponse(
- BuildingInfoUpdateMessage(
- PlanetSideGUID(6), //Ceryshen
- PlanetSideGUID(2), //Anguta
- 8, //80% NTU
- false, //Base hacked
- PlanetSideEmpire.NEUTRAL, //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.Normal, //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."
- sendResponse(BroadcastWarpgateUpdateMessage(PlanetSideGUID(13), PlanetSideGUID(1), false, false, true)) // VS Sanctuary: Inactive Warpgate -> Broadcast Warpgate
- sendResponse(ZonePopulationUpdateMessage(PlanetSideGUID(13), 414, 138, 0, 138, 0, 138, 0, 138, 0))
+ case Zone.ClientInitialization(zone) =>
+ val continentNumber = zone.Number
+ val poplist = LivePlayerList.ZonePopulation(continentNumber, _ => true)
+ val popBO = 0 //TODO black ops test (partition)
+ val popTR = poplist.count(_.Faction == PlanetSideEmpire.TR)
+ val popNC = poplist.count(_.Faction == PlanetSideEmpire.NC)
+ val popVS = poplist.count(_.Faction == PlanetSideEmpire.VS)
+
+ zone.Buildings.foreach({ case(id, building) => initBuilding(continentNumber, id, building) })
+ sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
+ sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
+ //CaptureFlagUpdateMessage()
+ //VanuModuleUpdateMessage()
+ //ModuleLimitsMessage()
+ sendResponse(ZoneInfoMessage(continentNumber, true, 0))
+ sendResponse(ZoneLockInfoMessage(continentNumber, false, true))
+ sendResponse(ZoneForcedCavernConnectionsMessage(continentNumber, 0))
+ sendResponse(HotSpotUpdateMessage(continentNumber, 1, Nil)) //normally set in bulk; should be fine doing per continent
case InterstellarCluster.ClientInitializationComplete(tplayer)=>
- //this will cause the client to send back a BeginZoningMessage packet (see below)
- sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L)) //VS Sanctuary
+ //PropertyOverrideMessage
+ sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1))
+ sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
+ sendResponse(FriendsResponse(FriendAction.InitializeFriendList, 0, true, true, Nil))
+ sendResponse(FriendsResponse(FriendAction.InitializeIgnoreList, 0, true, true, Nil))
+
+ //LoadMapMessage will cause the client to send back a BeginZoningMessage packet (see below)
+ sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L))
log.info("Load the now-registered player")
//load the now-registered player
tplayer.Spawn
@@ -1023,13 +1020,35 @@ class WorldSessionActor extends Actor with MDCContextAware {
val guid = tplayer.GUID
LivePlayerList.Assign(continent.Number, sessionId, guid)
sendResponse(SetCurrentAvatarMessage(guid,0,0))
- sendResponse(CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))
- sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on
- sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
- (1 to 73).foreach( i => {
+ (0 until DetailedCharacterData.numberOfImplantSlots(tplayer.BEP)).foreach(slot => {
+ sendResponse(AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 1)) //init implant slot
+ sendResponse(AvatarImplantMessage(guid, ImplantAction.Activation, slot, 0)) //deactivate implant
+ //TODO: if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
+ })
+
+ sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
+ //TODO: if Medkit does not have shortcut, add to a free slot or write over slot 64
+ sendResponse(CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))
+ sendResponse(ChangeShortcutBankMessage(guid, 0))
+ //FavoritesMessage
+ sendResponse(SetChatFilterMessage(ChatChannel.Local, false, ChatChannel.values.toList)) //TODO will not always be "on" like this
+ sendResponse(AvatarDeadStateMessage(DeadState.Nothing, 0,0, tplayer.Position, 0, true))
+ sendResponse(PlanetsideAttributeMessage(guid, 53, 1))
+ sendResponse(AvatarSearchCriteriaMessage(guid, List(0,0,0,0,0,0)))
+ (1 to 73).foreach(i => {
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(i), 67, 0))
})
+ (0 to 30).foreach(i => { //TODO 30 for a new character only?
+ sendResponse(AvatarStatisticsMessage(2, Statistics(0L)))
+ })
+ //AvatarAwardMessage
+ //DisplayAwardMessage
+ //SquadDefinitionActionMessage and SquadDetailDefinitionUpdateMessage
+ //MapObjectStateBlockMessage and ObjectCreateMessage
+ //TacticsMessage
+
+ sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on
case Zone.ItemFromGround(tplayer, item) =>
val obj_guid = item.GUID
@@ -1241,19 +1260,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ BeginZoningMessage() =>
log.info("Reticulating splines ...")
//map-specific initializations
- //TODO continent.ClientConfiguration()
- sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1))
-
- sendResponse(SetEmpireMessage(PlanetSideGUID(2), PlanetSideEmpire.VS)) //HART building C
- sendResponse(SetEmpireMessage(PlanetSideGUID(29), PlanetSideEmpire.NC)) //South Villa Gun Tower
-
+ configZone(continent) //todo density
sendResponse(TimeOfDayMessage(1191182336))
- sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
-
- sendResponse(ZonePopulationUpdateMessage(PlanetSideGUID(6), 414, 138, 0, 138, 0, 138, 0, 138, 0))
+ //custom
+ sendResponse(ContinentalLockUpdateMessage(13, PlanetSideEmpire.VS)) // "The VS have captured the VS Sanctuary."
(1 to 255).foreach(i => { sendResponse(SetEmpireMessage(PlanetSideGUID(i), PlanetSideEmpire.VS)) })
- //render Equipment that was dropped into zone before the player arrived
+ //render Equipment that was dropped into zone before the player arrived
continent.EquipmentOnGround.foreach(item => {
val definition = item.Definition
sendResponse(
@@ -1408,6 +1421,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) =>
log.info(s"SpawnRequestMessage: $msg")
+ case msg @ SetChatFilterMessage(send_channel, origin, whitelist) =>
+ log.info("SetChatFilters: " + 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) {
@@ -2019,11 +2035,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("ItemTransaction: " + msg)
continent.GUID(terminal_guid) match {
case Some(term : Terminal) =>
- if(player.Faction == term.Faction) {
- term.Actor ! Terminal.Request(player, msg)
- }
- case Some(obj : PlanetSideGameObject) => ;
- case None => ;
+ log.info(s"ItemTransaction: ${term.Definition.Name} found")
+ term.Actor ! Terminal.Request(player, msg)
+ case Some(obj : PlanetSideGameObject) =>
+ log.error(s"ItemTransaction: $obj is not a terminal")
+ case _ =>
+ log.error(s"ItemTransaction: $terminal_guid does not exist")
}
case msg @ FavoritesRequest(player_guid, unk, action, line, label) =>
@@ -3050,6 +3067,111 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.error(s"DeployRequest: $obj can not transition to $state - $reason$mobileShift")
}
+ /**
+ * For a given continental structure, determine the method of generating server-join client configuration packets.
+ * @param continentNumber the zone id
+ * @param buildingNumber the building id
+ * @param building the building object
+ */
+ def initBuilding(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
+ building match {
+ case _ : WarpGate =>
+ initGate(continentNumber, buildingNumber, building)
+ case _ : Building =>
+ initFacility(continentNumber, buildingNumber, building)
+ }
+ }
+
+ /**
+ * For a given facility structure, configure a client by dispatching the appropriate packets.
+ * Pay special attention to the details of `BuildingInfoUpdateMessage` when preparing this packet.
+ * @see `BuildingInfoUpdateMessage`
+ * @see `DensityLevelUpdateMessage`
+ * @param continentNumber the zone id
+ * @param buildingNumber the building id
+ * @param building the building object
+ */
+ def initFacility(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
+ sendResponse(
+ BuildingInfoUpdateMessage(
+ continentNumber, //Zone
+ buildingNumber, //Facility
+ 8, //NTU%
+ false, //Hacked
+ PlanetSideEmpire.NEUTRAL, //Base hacked by
+ 0, //Time remaining for hack (ms)
+ building.Faction, //Base owned by
+ 0, //!! Field != 0 will cause malformed packet. See class def.
+ None,
+ PlanetSideGeneratorState.Normal, //Generator state
+ true, //Respawn tubes operating state
+ false, //Force dome state
+ 0, //Lattice benefits
+ 0, //!! Field > 0 will cause malformed packet. See class def.
+ Nil,
+ 0,
+ false,
+ 8, //!! Field != 8 will cause malformed packet. See class def.
+ None,
+ false, //Boosted spawn room pain field
+ false //Boosted generator room pain field
+ )
+ )
+ sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0,0, 0,0, 0,0, 0,0)))
+ }
+
+ /**
+ * For a given lattice warp gate structure, configure a client by dispatching the appropriate packets.
+ * Unlike other facilities, gates do not have complicated `BuildingInfoUpdateMessage` packets.
+ * Also unlike facilities, gates have an additional packet.
+ * @see `BuildingInfoUpdateMessage`
+ * @see `DensityLevelUpdateMessage`
+ * @see `BroadcastWarpgateUpdateMessage`
+ * @param continentNumber the zone id
+ * @param buildingNumber the building id
+ * @param building the building object
+ */
+ def initGate(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
+ sendResponse(
+ BuildingInfoUpdateMessage(
+ continentNumber, buildingNumber,
+ 0,
+ false,
+ PlanetSideEmpire.NEUTRAL,
+ 0,
+ building.Faction,
+ 0,
+ None,
+ PlanetSideGeneratorState.Normal,
+ true,
+ false,
+ 0,
+ 0,
+ Nil,
+ 0,
+ false,
+ 8,
+ None,
+ false,
+ false
+ )
+ )
+ sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0,0, 0,0, 0,0, 0,0)))
+ sendResponse(BroadcastWarpgateUpdateMessage(continentNumber, buildingNumber, false, false, true))
+ }
+
+ def configZone(zone : Zone) : Unit = {
+ zone.Buildings.foreach({case (id, building) =>
+ sendResponse(SetEmpireMessage(PlanetSideGUID(id), building.Faction))
+ building.Amenities.foreach(amenity => {
+ val amenityId = amenity.GUID
+ sendResponse(PlanetsideAttributeMessage(amenityId, 50, 0))
+ sendResponse(PlanetsideAttributeMessage(amenityId, 51, 0))
+ })
+ sendResponse(HackMessage(3, PlanetSideGUID(id), PlanetSideGUID(0), 0, 3212836864L, HackState.HackCleared, 8))
+ })
+ }
+
def failWithError(error : String) = {
log.error(error)
sendResponse(ConnectionClose())
diff --git a/pslogin/src/main/scala/Zones.scala b/pslogin/src/main/scala/Zones.scala
index 60de7f9c8..352c27294 100644
--- a/pslogin/src/main/scala/Zones.scala
+++ b/pslogin/src/main/scala/Zones.scala
@@ -39,7 +39,7 @@ object Zones {
super.Init(context)
import net.psforever.types.PlanetSideEmpire
- Building(2).get.Faction = PlanetSideEmpire.VS //HART building C
+ Buildings.values.foreach(building => { building.Faction = PlanetSideEmpire.VS })
Building(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower
}
}