diff --git a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala
deleted file mode 100644
index 3bbd2619a..000000000
--- a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.continent
-
-import akka.actor.{Actor, Props}
-import net.psforever.objects.Player
-
-import scala.annotation.tailrec
-
-class IntergalacticCluster(zones : List[Zone]) extends Actor {
- private[this] val log = org.log4s.getLogger
- log.info("Starting interplanetary cluster ...")
-
- override def preStart() : Unit = {
- super.preStart()
- for(zone <- zones) {
- log.info(s"Built continent ${zone.ZoneId}")
- zone.Actor = context.actorOf(Props(classOf[ZoneActor], zone), s"${zone.ZoneId}-actor")
- }
- }
-
- def receive : Receive = {
- case IntergalacticCluster.GetWorld(zoneId) =>
- log.info(s"Asked to find $zoneId")
- findWorldInCluster(zones.iterator, zoneId) match {
- case Some(continent) =>
- sender ! IntergalacticCluster.GiveWorld(zoneId, continent)
- case None =>
- log.error(s"Requested zone with id $zoneId could not be found")
- }
-
- case IntergalacticCluster.RequestZoneInitialization(tplayer) =>
- zones.foreach(zone => {
- sender ! Zone.ClientInitialization(zone.ClientInitialization())
- })
- sender ! IntergalacticCluster.ClientInitializationComplete(tplayer)
-
- case _ => ;
- }
-
- @tailrec private def findWorldInCluster(iter : Iterator[Zone], zoneId : String) : Option[Zone] = {
- if(!iter.hasNext) {
- None
- }
- else {
- val cont = iter.next
- if(cont.ZoneId == zoneId) {
- Some(cont)
- }
- else {
- findWorldInCluster(iter, zoneId)
- }
- }
- }
-}
-
-object IntergalacticCluster {
- final case class GetWorld(zoneId : String)
-
- final case class GiveWorld(zoneId : String, zone : Zone)
-
- final case class RequestZoneInitialization(tplayer : Player)
-
- final case class ClientInitializationComplete(tplayer : Player)
-}
diff --git a/common/src/main/scala/net/psforever/objects/continent/ServerObjectBuilder.scala b/common/src/main/scala/net/psforever/objects/continent/ServerObjectBuilder.scala
deleted file mode 100644
index b5ca451ea..000000000
--- a/common/src/main/scala/net/psforever/objects/continent/ServerObjectBuilder.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.continent
-
-import akka.actor.ActorContext
-import net.psforever.objects.PlanetSideGameObject
-import net.psforever.objects.guid.NumberPoolHub
-
-trait ServerObjectBuilder {
- def Build(implicit context : ActorContext, guid : NumberPoolHub) : PlanetSideGameObject
-}
diff --git a/common/src/main/scala/net/psforever/objects/continent/TerminalObjectBuilder.scala b/common/src/main/scala/net/psforever/objects/continent/TerminalObjectBuilder.scala
deleted file mode 100644
index ae59d2c84..000000000
--- a/common/src/main/scala/net/psforever/objects/continent/TerminalObjectBuilder.scala
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.continent
-
-import net.psforever.objects.terminals.{Terminal, TerminalDefinition}
-
-class TerminalObjectBuilder(tdef : TerminalDefinition, id : Int) extends ServerObjectBuilder {
- import akka.actor.ActorContext
- import net.psforever.objects.guid.NumberPoolHub
-
- def Build(implicit context : ActorContext, guid : NumberPoolHub) : Terminal = {
- val obj = Terminal(tdef)
- guid.register(obj, id)
- obj.Actor
- obj
- }
-}
-
-object TerminalObjectBuilder {
- def apply(tdef : TerminalDefinition, id : Int) : TerminalObjectBuilder = {
- new TerminalObjectBuilder(tdef, id)
- }
-}
diff --git a/common/src/main/scala/net/psforever/objects/continent/Zone.scala b/common/src/main/scala/net/psforever/objects/continent/Zone.scala
deleted file mode 100644
index 33ba16238..000000000
--- a/common/src/main/scala/net/psforever/objects/continent/Zone.scala
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.continent
-
-import akka.actor.{ActorContext, ActorRef, Props}
-import net.psforever.objects.{PlanetSideGameObject, Player}
-import net.psforever.objects.equipment.Equipment
-import net.psforever.objects.guid.NumberPoolHub
-import net.psforever.objects.guid.actor.{NumberPoolAccessorActor, NumberPoolActor}
-import net.psforever.objects.guid.selector.RandomSelector
-import net.psforever.objects.guid.source.LimitedNumberSource
-import net.psforever.packet.GamePacket
-import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.Vector3
-
-import scala.collection.mutable.ListBuffer
-
-class Zone(id : String, map : ZoneMap, zoneNumber : Int) {
- private var actor = ActorRef.noSender
- private var accessor : ActorRef = ActorRef.noSender
- //private var startupUtilities : List[ServerObjectBuilder] = List()
- private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536))
-
- def Actor : ActorRef = actor
-
- def Init(implicit context : ActorContext) : Unit = {
- //TODO wrong initialization
- implicit val guid = this.guid
- val pool = guid.AddPool("pool", (200 to 1000).toList)
- pool.Selector = new RandomSelector
- val poolActor = context.actorOf(Props(classOf[NumberPoolActor], pool), name = s"$ZoneId-poolActor")
- accessor = context.actorOf(Props(classOf[NumberPoolAccessorActor], guid, pool, poolActor), s"$ZoneId-accessor")
-
- map.LocalObjects.foreach({builderObject =>
- builderObject.Build
- })
- }
-
- def Actor_=(zoneActor : ActorRef) : ActorRef = {
- if(actor == ActorRef.noSender) {
- actor = zoneActor
- }
- Actor
- }
-
- private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
-
- def ZoneId : String = id
-
- def ZoneNumber : Int = zoneNumber
-
- def Map : ZoneMap = map
-
- def GUID : ActorRef = accessor
-
- def GUID(hub : NumberPoolHub) : ActorRef = {
- if(actor == ActorRef.noSender) {
- guid = hub
- }
- Actor
- }
-
- def GUID(object_guid : PlanetSideGUID) : Option[PlanetSideGameObject] = GUID(object_guid.guid)
-
- def GUID(object_guid : Int) : Option[PlanetSideGameObject] = guid(object_guid) match {
- case Some(obj) =>
- Some(obj.asInstanceOf[PlanetSideGameObject]) //potential casting error
- case None =>
- None
- }
-
- def EquipmentOnGround : ListBuffer[Equipment] = equipmentOnGround
-
-// def AddUtility(obj : ServerObjectBuilder) : Unit = {
-// startupUtilities = startupUtilities :+ obj
-// }
-//
-// def StartupUtilities : List[ServerObjectBuilder] = {
-// val utilities = startupUtilities
-// startupUtilities = Nil
-// utilities
-// }
-
- def ClientInitialization() : List[GamePacket] = {
- List.empty[GamePacket]
- }
-
- def ClientConfiguration() : List[GamePacket] = {
- List.empty[GamePacket]
- }
-}
-
-object Zone {
- final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3)
-
- final case class GetItemOnGround(player : Player, item_guid : PlanetSideGUID)
-
- final case class ItemFromGround(player : Player, item : Equipment)
-
- final case class ClientInitialization(list : List[GamePacket])
-
- def apply(id : String, map : ZoneMap, number : Int) : Zone = {
- new Zone(id, map, number)
- }
-}
diff --git a/common/src/main/scala/net/psforever/objects/continent/ZoneMap.scala b/common/src/main/scala/net/psforever/objects/continent/ZoneMap.scala
deleted file mode 100644
index ee0edbffb..000000000
--- a/common/src/main/scala/net/psforever/objects/continent/ZoneMap.scala
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.continent
-
-class ZoneMap(private val name : String) {
- private var localObjects : List[ServerObjectBuilder] = List()
-
- def Name : String = name
-
- def LocalObject(obj : ServerObjectBuilder) : Unit = {
- localObjects = localObjects :+ obj
- }
-
- def LocalObjects : List[ServerObjectBuilder] = {
- val utilities = localObjects
- localObjects = Nil
- utilities
- }
-}
diff --git a/common/src/main/scala/net/psforever/objects/zones/InterstellarCluster.scala b/common/src/main/scala/net/psforever/objects/zones/InterstellarCluster.scala
new file mode 100644
index 000000000..e09d55c27
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/zones/InterstellarCluster.scala
@@ -0,0 +1,113 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.zones
+
+import akka.actor.{Actor, Props}
+import net.psforever.objects.Player
+
+import scala.annotation.tailrec
+
+/**
+ * The root of the universe of one-continent planets, codified by the game's "Interstellar Map."
+ * Constructs each zone and thus instigates the construction of every server object in the game world.
+ * The nanite flow connecting all of these `Zone`s is called the "Intercontinental Lattice."
+ *
+ * The process of "construction" and "initialization" and "configuration" are referenced at this level.
+ * These concepts are not the same thing;
+ * the distinction is important.
+ * "Construction" and "instantiation" of the cluster merely produces the "facade" of the different `Zone` entities.
+ * In such a `List`, every built `Zone` is capable of being a destination on the "Intercontinental lattice."
+ * "Initialization" and "configuration" of the cluster refers to the act of completing the "Intercontinental Lattice"
+ * by connecting different terminus warp gates together.
+ * Other activities involve event management and managing wide-reaching and factional attributes.
+ * @param zones a `List` of continental `Zone` arenas
+ */
+class InterstellarCluster(zones : List[Zone]) extends Actor {
+ private[this] val log = org.log4s.getLogger
+ log.info("Starting interplanetary cluster ...")
+
+ /**
+ * Create a `ZoneActor` for each `Zone`.
+ * That `Actor` is sent a packet that would start the construction of the `Zone`'s server objects.
+ * The process is maintained this way to allow every planet to be created and configured in separate stages.
+ */
+ override def preStart() : Unit = {
+ super.preStart()
+ for(zone <- zones) {
+ log.info(s"Built continent ${zone.Id}")
+ zone.Actor = context.actorOf(Props(classOf[ZoneActor], zone), s"${zone.Id}-actor")
+ zone.Actor ! Zone.Init()
+ }
+ }
+
+ def receive : Receive = {
+ case InterstellarCluster.GetWorld(zoneId) =>
+ log.info(s"Asked to find $zoneId")
+ findWorldInCluster(zones.iterator, zoneId) match {
+ case Some(continent) =>
+ sender ! InterstellarCluster.GiveWorld(zoneId, continent)
+ case None =>
+ log.error(s"Requested zone $zoneId could not be found")
+ }
+
+ case InterstellarCluster.RequestClientInitialization(tplayer) =>
+ zones.foreach(zone => {
+ sender ! Zone.ClientInitialization(zone.ClientInitialization()) //do this for each Zone
+ })
+ sender ! InterstellarCluster.ClientInitializationComplete(tplayer) //will be processed after all Zones
+
+ case _ => ;
+ }
+
+ /**
+ * Search through the `List` of `Zone` entities and find the one with the matching designation.
+ * @param iter an `Iterator` of `Zone` entities
+ * @param zoneId the name of the `Zone`
+ * @return the discovered `Zone`
+ */
+ @tailrec private def findWorldInCluster(iter : Iterator[Zone], zoneId : String) : Option[Zone] = {
+ if(!iter.hasNext) {
+ None
+ }
+ else {
+ val cont = iter.next
+ if(cont.Id == zoneId) {
+ Some(cont)
+ }
+ else {
+ findWorldInCluster(iter, zoneId)
+ }
+ }
+ }
+}
+
+object InterstellarCluster {
+
+ /**
+ * Request a hard reference to a `Zone`.
+ * @param zoneId the name of the `Zone`
+ */
+ final case class GetWorld(zoneId : String)
+
+ /**
+ * Provide a hard reference to a `Zone`.
+ * @param zoneId the name of the `Zone`
+ * @param zone the `Zone`
+ */
+ final case class GiveWorld(zoneId : String, zone : Zone)
+
+ /**
+ * Signal to the cluster that a new client needs to be initialized for all listed `Zone` destinations.
+ * @param tplayer the `Player` belonging to the client;
+ * may be superfluous
+ * @see `Zone`
+ */
+ final case class RequestClientInitialization(tplayer : Player)
+
+ /**
+ * Return signal intended to inform the original sender that all `Zone`s have finished being initialized.
+ * @param tplayer the `Player` belonging to the client;
+ * may be superfluous
+ * @see `WorldSessionActor`
+ */
+ final case class ClientInitializationComplete(tplayer : Player)
+}
diff --git a/common/src/main/scala/net/psforever/objects/zones/ServerObjectBuilder.scala b/common/src/main/scala/net/psforever/objects/zones/ServerObjectBuilder.scala
new file mode 100644
index 000000000..2635df096
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/zones/ServerObjectBuilder.scala
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.zones
+
+import akka.actor.ActorContext
+import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.guid.NumberPoolHub
+
+/**
+ * Wrapper `Trait` designed to be extended to implement custom object instantiation logic at the `ZoneMap` level.
+ * @see `Zone.Init`
+ */
+trait ServerObjectBuilder {
+ /**
+ * Instantiate and configure the given server object
+ * (at a later time compared to the construction of the builder class).
+ *
+ * Externally, it expects a `context` to properly integrate within an `ActorSystem`
+ * and is provided with a source for globally unique identifiers to integrate into the `Zone`.
+ * Neither is required of the `return` type, however.
+ * @param context a context to allow the object to properly set up `ActorSystem` functionality;
+ * defaults to `null`
+ * @param guid the local globally unique identifier system to complete the process of object introduction;
+ * defaults to `null`
+ * @return the object that was created and integrated into the `Zone`
+ */
+ def Build(implicit context : ActorContext = null, guid : NumberPoolHub = null) : PlanetSideGameObject
+}
diff --git a/common/src/main/scala/net/psforever/objects/zones/TerminalObjectBuilder.scala b/common/src/main/scala/net/psforever/objects/zones/TerminalObjectBuilder.scala
new file mode 100644
index 000000000..2ced4d84d
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/zones/TerminalObjectBuilder.scala
@@ -0,0 +1,33 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.zones
+
+import net.psforever.objects.terminals.{Terminal, TerminalDefinition}
+
+/**
+ * Wrapper `Class` designed to instantiate a `Terminal` server object.
+ * @param tdef a `TerminalDefinition` object, indicating the specific functionality of the resulting `Terminal`
+ * @param id the globally unique identifier to which this `Terminal` will be registered
+ */
+class TerminalObjectBuilder(private val tdef : TerminalDefinition, private val id : Int) extends ServerObjectBuilder {
+ import akka.actor.ActorContext
+ import net.psforever.objects.guid.NumberPoolHub
+
+ def Build(implicit context : ActorContext, guid : NumberPoolHub) : Terminal = {
+ val obj = Terminal(tdef)
+ guid.register(obj, id) //non-Actor GUID registration
+ obj.Actor //it's necessary to register beforehand because the Actor name utilizes the GUID
+ obj
+ }
+}
+
+object TerminalObjectBuilder {
+ /**
+ * Overloaded constructor for a `TerminalObjectBuilder`.
+ * @param tdef a `TerminalDefinition` object
+ * @param id a globally unique identifier
+ * @return a `TerminalObjectBuilder` object
+ */
+ def apply(tdef : TerminalDefinition, id : Int) : TerminalObjectBuilder = {
+ new TerminalObjectBuilder(tdef, id)
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala
new file mode 100644
index 000000000..21497a74d
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -0,0 +1,259 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.zones
+
+import akka.actor.{ActorContext, ActorRef, Props}
+import net.psforever.objects.{PlanetSideGameObject, Player}
+import net.psforever.objects.equipment.Equipment
+import net.psforever.objects.guid.NumberPoolHub
+import net.psforever.objects.guid.actor.{NumberPoolAccessorActor, NumberPoolActor}
+import net.psforever.objects.guid.selector.RandomSelector
+import net.psforever.objects.guid.source.LimitedNumberSource
+import net.psforever.packet.GamePacket
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types.Vector3
+
+import scala.collection.mutable.ListBuffer
+
+/**
+ * A server object representing the one-landmass planets as well as the individual subterranean caverns.
+ *
+ * The concept of a "zone" is synonymous to the common vernacular "continent,"
+ * commonly referred by names such as Hossin or Ishundar and internally identified as c2 and c7, respectively.
+ * A `Zone` is composed of the abstracted concept of all the information pertinent for the simulation of the environment.
+ * That is, "everything about the continent."
+ * Physically, server objects and dynamic game objects are maintained through a local unique identifier system.
+ * Static server objects originate from the `ZoneMap`.
+ * Dynamic game objects originate from player characters.
+ * (Write more later.)
+ * @param zoneId the privileged name that can be used as the second parameter in the packet `LoadMapMessage`
+ * @param zoneMap the map of server objects upon which this `Zone` is based
+ * @param zoneNumber the numerical index of the `Zone` as it is recognized in a variety of packets;
+ * also used by `LivePlayerList` to indicate a specific `Zone`
+ * @see `ZoneMap`
+ * `LoadMapMessage`
+ * `LivePlayerList`
+ */
+class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
+ /** Governs general synchronized external requests. */
+ private var actor = ActorRef.noSender
+
+ /** Used by the globally unique identifier system to coordinate requests. */
+ private var accessor : ActorRef = ActorRef.noSender
+ /** The basic support structure for the globally unique number system used by this `Zone`. */
+ private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536))
+ /** A synchronized `List` of items (`Equipment`) dropped by players on the ground and can be collected again. */
+ private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
+ /** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
+ private var ground : ActorRef = ActorRef.noSender
+
+ /**
+ * Establish the basic accessible conditions necessary for a functional `Zone`.
+ *
+ * Called from the `Actor` that governs this `Zone` when it is passed a constructor reference to the `Zone`.
+ * Specifically, the order of calling follows: `InterstellarCluster.preStart -> ZoneActor.receive(Zone.Init()) -> Zone.Init`.
+ * The basic method performs three main operations.
+ * First, the `Actor`-driven aspect of the globally unique identifier system for this `Zone` is finalized.
+ * Second, all supporting `Actor` agents are created, e.g., `ground`.
+ * Third, the `ZoneMap` server objects are loaded and constructed within that aforementioned system.
+ * To avoid being called more than once, there is a test whether the `accessor` for the globally unique identifier system has been changed.
+ * @param context a reference to an `ActorContext` necessary for `Props`
+ */
+ def Init(implicit context : ActorContext) : Unit = {
+ if(accessor == ActorRef.noSender) {
+ //TODO wrong initialization for GUID
+ implicit val guid = this.guid
+ //passed into builderObject.Build implicitly
+ val pool = guid.AddPool("pool", (200 to 1000).toList)
+ pool.Selector = new RandomSelector
+ val poolActor = context.actorOf(Props(classOf[NumberPoolActor], pool), name = s"$Id-poolActor")
+ accessor = context.actorOf(Props(classOf[NumberPoolAccessorActor], guid, pool, poolActor), s"$Id-accessor")
+ ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
+
+ Map.LocalObjects.foreach({ builderObject =>
+ builderObject.Build
+ })
+ }
+ }
+
+ /**
+ * A reference to the primary `Actor` that governs this `Zone`.
+ * @return an `ActorRef`
+ * @see `ZoneActor`
+ * `Zone.Init`
+ */
+ def Actor : ActorRef = actor
+
+ /**
+ * Give this `Zone` an `Actor` that will govern its interactions sequentially.
+ * @param zoneActor an `ActorRef` for this `Zone`;
+ * will not overwrite any existing governance unless `noSender`
+ * @return an `ActorRef`
+ * @see `ZoneActor`
+ */
+ def Actor_=(zoneActor : ActorRef) : ActorRef = {
+ if(actor == ActorRef.noSender) {
+ actor = zoneActor
+ }
+ Actor
+ }
+
+ /**
+ * The privileged name that can be used as the second parameter in the packet `LoadMapMessage`.
+ * @return the name
+ */
+ def Id : String = zoneId
+
+ /**
+ * The map of server objects upon which this `Zone` is based
+ * @return the map
+ */
+ def Map : ZoneMap = zoneMap
+
+ /**
+ * The numerical index of the `Zone` as it is recognized in a variety of packets.
+ * @return the abstract index position of this `Zone`
+ */
+ def Number : Int = zoneNumber
+
+ /**
+ * The globally unique identifier system is synchronized via an `Actor` to ensure that concurrent requests do not clash.
+ * A clash is merely when the same number is produced more than once by the same system due to concurrent requests.
+ * @return synchronized reference to the globally unique identifier system
+ */
+ def GUID : ActorRef = accessor
+
+ /**
+ * Replace the current globally unique identifier system with a new one.
+ * The replacement will not occur if the current system is populated or if its synchronized reference has been created.
+ * @return synchronized reference to the globally unique identifier system
+ */
+ def GUID(hub : NumberPoolHub) : ActorRef = {
+ if(actor == ActorRef.noSender && guid.Pools.map({case ((_, pool)) => pool.Count}).sum == 0) {
+ guid = hub
+ }
+ Actor
+ }
+
+ /**
+ * Recover an object from the globally unique identifier system by the number that was assigned previously.
+ * @param object_guid the globally unique identifier requested
+ * @return the associated object, if it exists
+ * @see `GUID(Int)`
+ */
+ def GUID(object_guid : PlanetSideGUID) : Option[PlanetSideGameObject] = GUID(object_guid.guid)
+
+ /**
+ * Recover an object from the globally unique identifier system by the number that was assigned previously.
+ * The object must be upcast into due to the differtence between the storage type and the return type.
+ * @param object_guid the globally unique identifier requested
+ * @return the associated object, if it exists
+ * @see `NumberPoolHub(Int)`
+ */
+ def GUID(object_guid : Int) : Option[PlanetSideGameObject] = guid(object_guid) match {
+ case Some(obj) =>
+ Some(obj.asInstanceOf[PlanetSideGameObject])
+ case None =>
+ None
+ }
+
+ /**
+ * The `List` of items (`Equipment`) dropped by players on the ground and can be collected again.
+ * @return the `List` of `Equipment`
+ */
+ def EquipmentOnGround : List[Equipment] = equipmentOnGround.toList
+
+ /**
+ * Coordinate `Equipment` that has been dropped on the ground or to-be-dropped on the ground.
+ * @return synchronized reference to the ground
+ * @see `ZoneGroundActor`
+ * `Zone.DropItemOnGround`
+ * `Zone.GetItemOnGround`
+ * `Zone.ItemFromGround`
+ */
+ def Ground : ActorRef = ground
+
+ /**
+ * Provide bulk correspondence on all map entities that can be composed into packet messages and reported to a client.
+ * These messages are sent in this fashion at the time of joining the server:
+ * - `BroadcastWarpgateUpdateMessage`
+ * - `BuildingInfoUpdateMessage`
+ * - `CaptureFlagUpdateMessage`
+ * - `ContinentalLockUpdateMessage`
+ * - `DensityLevelUpdateMessage`
+ * - `ModuleLimitsMessage`
+ * - `VanuModuleUpdateMessage`
+ * - `ZoneForcedCavernConnectionMessage`
+ * - `ZoneInfoMessage`
+ * - `ZoneLockInfoMessage`
+ * - `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]
+ }
+}
+
+object Zone {
+ /**
+ * Message to initialize the `Zone`.
+ * @see `Zone.Init(implicit ActorContext)`
+ */
+ final case class Init()
+
+ /**
+ * Message to relinguish an item and place in on the ground.
+ * @param item the piece of `Equipment`
+ * @param pos where it is dropped
+ * @param orient in which direction it is facing when dropped
+ */
+ final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3)
+
+ /**
+ * Message to attempt to acquire an item from the ground (before somoene else?).
+ * @param player who wants the piece of `Equipment`
+ * @param item_guid the unique identifier of the piece of `Equipment`
+ */
+ final case class GetItemOnGround(player : Player, item_guid : PlanetSideGUID)
+
+ /**
+ * Message to give an item from the ground to a specific user.
+ * @param player who wants the piece of `Equipment`
+ * @param item the piece of `Equipment`
+ */
+ final case class ItemFromGround(player : Player, item : Equipment)
+
+ /**
+ * Message to report the packet messages that initialize the client.
+ * @param list a `List` of `GamePacket` messages
+ * @see `Zone.ClientInitialization()`
+ * `InterstallarCluster`
+ */
+ final case class ClientInitialization(list : List[GamePacket])
+
+ /**
+ * Overloaded constructor.
+ * @param id the privileged name that can be used as the second parameter in the packet `LoadMapMessage`
+ * @param map the map of server objects upon which this `Zone` is based
+ * @param number the numerical index of the `Zone` as it is recognized in a variety of packets
+ * @return a `Zone` object
+ */
+ def apply(id : String, map : ZoneMap, number : Int) : Zone = {
+ new Zone(id, map, number)
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala
new file mode 100644
index 000000000..811b6ef9f
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala
@@ -0,0 +1,20 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.zones
+
+import akka.actor.Actor
+
+/**
+ * na
+ * @param zone the `Zone` governed by this `Actor`
+ */
+class ZoneActor(zone : Zone) extends Actor {
+ private[this] val log = org.log4s.getLogger
+
+ def receive : Receive = {
+ case Zone.Init() =>
+ zone.Init
+
+ case msg =>
+ log.warn(s"Received unexpected message - $msg")
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala
similarity index 62%
rename from common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala
rename to common/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala
index 9d45a3630..5a001e47d 100644
--- a/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala
@@ -1,41 +1,47 @@
// Copyright (c) 2017 PSForever
-package net.psforever.objects.continent
+package net.psforever.objects.zones
import akka.actor.Actor
import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.PlanetSideGUID
import scala.annotation.tailrec
+import scala.collection.mutable.ListBuffer
-class ZoneActor(zone : Zone) extends Actor {
- private[this] val log = org.log4s.getLogger
-
- override def preStart() : Unit = {
- super.preStart()
- zone.Init
- }
+/**
+ * na
+ * @param equipmentOnGround a `List` of items (`Equipment`) dropped by players on the ground and can be collected again
+ */
+class ZoneGroundActor(equipmentOnGround : ListBuffer[Equipment]) extends Actor {
+ //private[this] val log = org.log4s.getLogger
def receive : Receive = {
case Zone.DropItemOnGround(item, pos, orient) =>
item.Position = pos
item.Orientation = orient
- zone.EquipmentOnGround += item
+ equipmentOnGround += item
case Zone.GetItemOnGround(player, item_guid) =>
FindItemOnGround(item_guid) match {
case Some(item) =>
sender ! Zone.ItemFromGround(player, item)
case None =>
- log.warn(s"item on ground $item_guid was requested by $player for pickup but was not found")
+ org.log4s.getLogger.warn(s"item on ground $item_guid was requested by $player for pickup but was not found")
}
case _ => ;
}
+ /**
+ * Shift through objects on the ground to find the location of a specific item.
+ * @param item_guid the global unique identifier of the piece of `Equipment` being sought
+ * @return the index of the object matching `item_guid`, if found;
+ * `None`, otherwise
+ */
private def FindItemOnGround(item_guid : PlanetSideGUID) : Option[Equipment] = {
- recursiveFindItemOnGround(zone.EquipmentOnGround.iterator, item_guid) match {
+ recursiveFindItemOnGround(equipmentOnGround.iterator, item_guid) match {
case Some(index) =>
- Some(zone.EquipmentOnGround.remove(index))
+ Some(equipmentOnGround.remove(index))
case None =>
None
}
diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala
new file mode 100644
index 000000000..b1cf2e9a3
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala
@@ -0,0 +1,43 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.zones
+
+/**
+ * The fixed instantiation and relation of a series of server objects.
+ *
+ * Asides from a `List` of server objects to be built, the operation between any server objects
+ * and the connected functionality emerging from more complex data structures is codified by this object.
+ * In the former case, all `Terminal` server objects for a `Zone` are to be defined herein.
+ * In the latter case, the arrangement of server objects into groups called facilities is also to be defined herein.
+ * Much like a `BasicDefinition` to an object, `ZoneMap` should not maintain mutable information for the companion `Zone`.
+ * Use it as a blueprint.
+ *
+ * The "training zones" are the best example of the difference between a `ZoneMap` and a `Zone.`
+ * `tzdrtr` is the Terran Republic driving course.
+ * `tzdrvs` is the Vanu Sovereignty driving course.
+ * While each course can have different objects and object states (`Zone`),
+ * both courses have the same basic server objects because they are built from the same blueprint (`ZoneMap`).
+ * @param name the privileged name that can be used as the first parameter in the packet `LoadMapMessage`
+ * @see `ServerObjectBuilder`
+ * `LoadMapMessage`
+ */
+class ZoneMap(private val name : String) {
+ private var localObjects : List[ServerObjectBuilder] = List()
+
+ def Name : String = name
+
+ /**
+ * Append the builder for a server object to the list of builders known to this `ZoneMap`.
+ * @param obj the builder for a server object
+ */
+ def LocalObject(obj : ServerObjectBuilder) : Unit = {
+ localObjects = localObjects :+ obj
+ }
+
+ /**
+ * The list of all server object builder wrappers that have been assigned to this `ZoneMap`.
+ * @return the `List` of all `ServerObjectBuilders` known to this `ZoneMap`
+ */
+ def LocalObjects : List[ServerObjectBuilder] = {
+ localObjects
+ }
+}
diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala
index 9f1ee2529..c1403fc43 100644
--- a/pslogin/src/main/scala/PsLogin.scala
+++ b/pslogin/src/main/scala/PsLogin.scala
@@ -3,7 +3,7 @@ import java.net.InetAddress
import java.io.File
import java.util.Locale
-import akka.actor.{ActorContext, ActorRef, ActorSystem, Props}
+import akka.actor.{ActorRef, ActorSystem, Props}
import akka.routing.RandomPool
import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.classic.joran.JoranConfigurator
@@ -12,7 +12,7 @@ import ch.qos.logback.core.status._
import ch.qos.logback.core.util.StatusPrinter
import com.typesafe.config.ConfigFactory
import net.psforever.crypto.CryptoInterface
-import net.psforever.objects.continent.{IntergalacticCluster, TerminalObjectBuilder, Zone, ZoneMap}
+import net.psforever.objects.zones.{InterstellarCluster, TerminalObjectBuilder, Zone, ZoneMap}
import net.psforever.objects.guid.TaskResolver
import org.slf4j
import org.fusesource.jansi.Ansi._
@@ -202,7 +202,7 @@ object PsLogin {
val serviceManager = ServiceManager.boot
serviceManager ! ServiceManager.Register(RandomPool(50).props(Props[TaskResolver]), "taskResolver")
serviceManager ! ServiceManager.Register(Props[AvatarService], "avatar")
- serviceManager ! ServiceManager.Register(Props(classOf[IntergalacticCluster], createContinents()), "galaxy")
+ serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], createContinents()), "galaxy")
/** Create two actors for handling the login and world server endpoints */
loginRouter = Props(new SessionRouter("Login", loginTemplate))
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 070d27c33..f11ab3434 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -11,7 +11,7 @@ import org.log4s.MDC
import MDCContextAware.Implicits._
import ServiceManager.Lookup
import net.psforever.objects._
-import net.psforever.objects.continent.{Zone, IntergalacticCluster}
+import net.psforever.objects.zones.{InterstellarCluster, Zone}
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment._
import net.psforever.objects.guid.{Task, TaskResolver}
@@ -410,7 +410,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0)))
- case IntergalacticCluster.GiveWorld(zoneId, zone) =>
+ case InterstellarCluster.GiveWorld(zoneId, zone) =>
log.info(s"Zone $zoneId has been loaded")
player.Continent = zoneId
continent = zone
@@ -419,7 +419,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case PlayerLoaded(tplayer) =>
log.info(s"Player $tplayer has been loaded")
//init for whole server
- galaxy ! IntergalacticCluster.RequestZoneInitialization(tplayer)
+ galaxy ! InterstellarCluster.RequestClientInitialization(tplayer)
case PlayerFailedToLoad(tplayer) =>
player.Continent match {
@@ -460,9 +460,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, BroadcastWarpgateUpdateMessage(PlanetSideGUID(13), PlanetSideGUID(1), false, false, true))) // VS Sanctuary: Inactive Warpgate -> Broadcast Warpgate
sendResponse(PacketCoding.CreateGamePacket(0, ZonePopulationUpdateMessage(PlanetSideGUID(13), 414, 138, 0, 138, 0, 138, 0, 138, 0)))
- case IntergalacticCluster.ClientInitializationComplete(tplayer)=>
+ case InterstellarCluster.ClientInitializationComplete(tplayer)=>
//this will cause the client to send back a BeginZoningMessage packet (see below)
- sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage(continent.Map.Name, continent.ZoneId, 40100,25,true,3770441820L))) //VS Sanctuary
+ sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L))) //VS Sanctuary
log.info("Load the now-registered player")
//load the now-registered player
tplayer.Spawn
@@ -474,7 +474,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case SetCurrentAvatar(tplayer) =>
val guid = tplayer.GUID
- LivePlayerList.Assign(continent.ZoneNumber, sessionId, guid)
+ LivePlayerList.Assign(continent.Number, sessionId, guid)
sendResponse(PacketCoding.CreateGamePacket(0, SetCurrentAvatarMessage(guid,0,0)))
sendResponse(PacketCoding.CreateGamePacket(0, CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT)))
@@ -633,7 +633,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 ! IntergalacticCluster.GetWorld("home3")
+ galaxy ! InterstellarCluster.GetWorld("home3")
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
@@ -657,7 +657,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ReplicationStreamMessage(5, Some(6), Vector(SquadListing())))) //clear squad list
//load active players in zone
- LivePlayerList.ZonePopulation(continent.ZoneNumber, _ => true).foreach(char => {
+ LivePlayerList.ZonePopulation(continent.Number, _ => true).foreach(char => {
sendResponse(
PacketCoding.CreateGamePacket(0,
ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get)
@@ -752,7 +752,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(item.GUID == item_guid) {
val orient : Vector3 = Vector3(0f, 0f, player.Orientation.z)
player.FreeHand.Equipment = None
- continent.Actor ! Zone.DropItemOnGround(item, player.Position, orient) //TODO do I need to wait for callback?
+ continent.Ground ! Zone.DropItemOnGround(item, player.Position, orient)
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item.GUID, player.Position, 0f, 0f, player.Orientation.z)))
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, player.Position, orient, item))
}
@@ -765,7 +765,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) =>
log.info("PickupItem: " + msg)
- continent.Actor ! Zone.GetItemOnGround(player, item_guid)
+ continent.Ground ! Zone.GetItemOnGround(player, item_guid)
case msg @ ReloadMessage(item_guid, ammo_clip, unk1) =>
log.info("Reload: " + msg)