From 99b019714b4e06c388b82b7c6a0bdd960b29c3f9 Mon Sep 17 00:00:00 2001 From: FateJH Date: Fri, 15 Sep 2017 22:26:36 -0400 Subject: [PATCH 1/8] added basic Continent management system as a service; hooked it into WSA, at least as far as home3 is concerned; transferred GUID test functions to continent-level --- .../objects/continent/Continent.scala | 59 ++++++++++++++++++ .../objects/continent/ContinentActor.scala | 62 +++++++++++++++++++ .../continent/IntergalacticCluster.scala | 46 ++++++++++++++ pslogin/src/main/scala/PsLogin.scala | 28 +++------ .../src/main/scala/WorldSessionActor.scala | 60 ++++++++++++------ 5 files changed, 215 insertions(+), 40 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/continent/Continent.scala create mode 100644 common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala create mode 100644 common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala diff --git a/common/src/main/scala/net/psforever/objects/continent/Continent.scala b/common/src/main/scala/net/psforever/objects/continent/Continent.scala new file mode 100644 index 00000000..e084babb --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/continent/Continent.scala @@ -0,0 +1,59 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.continent + +import akka.actor.{ActorContext, ActorRef, Props} +import net.psforever.objects.Player +import net.psforever.objects.entity.IdentifiableEntity +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.game.PlanetSideGUID +import net.psforever.types.Vector3 + +import scala.collection.mutable.ListBuffer + +class Continent(zoneId : String, map : String) { + private var actor = ActorRef.noSender + private val guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536)) + private var accessor : ActorRef = ActorRef.noSender + + def Actor(implicit context : ActorContext) : ActorRef = { + if(actor == ActorRef.noSender) { + actor = context.actorOf(Props(classOf[ContinentActor], this), s"$zoneId-actor") + + val pool = guid.AddPool("pool", (400 to 599).toList) + val poolActor = context.actorOf(Props(classOf[NumberPoolActor], pool), name = s"$ZoneId-poolActor") + pool.Selector = new RandomSelector + accessor = context.actorOf(Props(classOf[NumberPoolAccessorActor], guid, pool, poolActor), s"$ZoneId-accessor") + } + actor + } + + private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]() + + def ZoneId : String = zoneId + + def Map : String = map + + def GUID : ActorRef = accessor + + def GUID(object_guid : PlanetSideGUID) : Option[IdentifiableEntity] = guid(object_guid.guid) + + def EquipmentOnGround : ListBuffer[Equipment] = equipmentOnGround +} + +object Continent { + final def Nowhere : Continent = { Continent("", "") } //TODO needs overrides + + final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3) + + final case class GetItemOnGround(player : Player, item_guid : PlanetSideGUID) + + final case class GiveItemFromGround(player : Player, item : Equipment) + + def apply(zoneId : String, map : String) : Continent = { + new Continent(zoneId, map) + } +} diff --git a/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala b/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala new file mode 100644 index 00000000..c81bf90d --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala @@ -0,0 +1,62 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.continent + +import akka.actor.Actor +import net.psforever.objects.equipment.Equipment +import net.psforever.packet.game.PlanetSideGUID + +import scala.annotation.tailrec + +class ContinentActor(continent : Continent) extends Actor { + private[this] val log = org.log4s.getLogger + import Continent._ + + def receive : Receive = { + case DropItemOnGround(item, pos, orient) => + item.Position = pos + item.Orientation = orient + continent.EquipmentOnGround += item + + case GetItemOnGround(player, item_guid) => + FindItemOnGround(item_guid) match { + case Some(item) => + sender ! GiveItemFromGround(player, item) + case None => + log.warn(s"item on ground $item_guid was requested by $player for pickup but was not found") + } + + case _ => ; + } + + private def FindItemOnGround(item_guid : PlanetSideGUID) : Option[Equipment] = { + recursiveFindItemOnGround(continent.EquipmentOnGround.iterator, item_guid) match { + case Some(index) => + Some(continent.EquipmentOnGround.remove(index)) + case None => + None + } + } + + /** + * Shift through objects on the ground to find the location of a specific item. + * @param iter an `Iterator` of `Equipment` + * @param item_guid the global unique identifier of the piece of `Equipment` being sought + * @param index the current position in the array-list structure used to create the `Iterator` + * @return the index of the object matching `item_guid`, if found; + * `None`, otherwise + */ + @tailrec private def recursiveFindItemOnGround(iter : Iterator[Equipment], item_guid : PlanetSideGUID, index : Int = 0) : Option[Int] = { + if(!iter.hasNext) { + None + } + else { + val item : Equipment = iter.next + if(item.GUID == item_guid) { + Some(index) + } + else { + recursiveFindItemOnGround(iter, item_guid, index + 1) + } + } + } +} diff --git a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala new file mode 100644 index 00000000..6c4a880a --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala @@ -0,0 +1,46 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects.continent + +import akka.actor.Actor + +import scala.annotation.tailrec + +class IntergalacticCluster(continents : List[Continent]) extends Actor { + //private[this] val log = org.log4s.getLogger + for(continent <- continents) { + continent.Actor //seed context + } + + def receive : Receive = { + case IntergalacticCluster.GetWorld(zoneId) => + findWorldInCluster(continents.iterator, zoneId) match { + case Some(continent) => + sender ! IntergalacticCluster.GiveWorld(zoneId, continent) + case None => + sender ! IntergalacticCluster.GiveWorld(zoneId, Continent.Nowhere) + } + + case _ => ; + } + + @tailrec private def findWorldInCluster(iter : Iterator[Continent], zoneId : String) : Option[Continent] = { + 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 : Continent) +} diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index fab1d3e7..e8376895 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -12,10 +12,8 @@ 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.guid.{NumberPoolHub, TaskResolver} -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.objects.continent.{Continent, IntergalacticCluster} +import net.psforever.objects.guid.TaskResolver import org.slf4j import org.fusesource.jansi.Ansi._ import org.fusesource.jansi.Ansi.Color._ @@ -89,7 +87,7 @@ object PsLogin { configurator.doConfigure(logfile) } catch { - case je : JoranException => ; + case _ : JoranException => ; } if(loggerHasErrors(lc)) { @@ -202,23 +200,9 @@ object PsLogin { */ val serviceManager = ServiceManager.boot - - //experimental guid code - val hub = new NumberPoolHub(new LimitedNumberSource(65536)) - val pool1 = hub.AddPool("test1", (400 to 599).toList) - val poolActor1 = system.actorOf(Props(classOf[NumberPoolActor], pool1), name = "poolActor1") - pool1.Selector = new RandomSelector - val pool2 = hub.AddPool("test2", (600 to 799).toList) - val poolActor2 = system.actorOf(Props(classOf[NumberPoolActor], pool2), name = "poolActor2") - pool2.Selector = new RandomSelector - - serviceManager ! ServiceManager.Register(Props(classOf[NumberPoolAccessorActor], hub, pool1, poolActor1), "accessor1") - serviceManager ! ServiceManager.Register(Props(classOf[NumberPoolAccessorActor], hub, pool2, poolActor2), "accessor2") - - //task resolver serviceManager ! ServiceManager.Register(RandomPool(50).props(Props[TaskResolver]), "taskResolver") - serviceManager ! ServiceManager.Register(Props[AvatarService], "avatar") + serviceManager ! ServiceManager.Register(Props(classOf[IntergalacticCluster], createContinents()), "galaxy") /** Create two actors for handling the login and world server endpoints */ loginRouter = Props(new SessionRouter("Login", loginTemplate)) @@ -235,6 +219,10 @@ object PsLogin { } } + def createContinents() : List[Continent] = { + Continent("home3","map13") :: Nil + } + def main(args : Array[String]) : Unit = { Locale.setDefault(Locale.US); // to have floats with dots, not comma... this.args = args diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 9a3e3931..4aae3f2a 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -11,6 +11,7 @@ import org.log4s.MDC import MDCContextAware.Implicits._ import ServiceManager.Lookup import net.psforever.objects._ +import net.psforever.objects.continent.{Continent, IntergalacticCluster} import net.psforever.objects.entity.IdentifiableEntity import net.psforever.objects.equipment._ import net.psforever.objects.guid.{Task, TaskResolver} @@ -24,21 +25,16 @@ import scala.annotation.tailrec import scala.util.Success class WorldSessionActor extends Actor with MDCContextAware { + import WorldSessionActor._ private[this] val log = org.log4s.getLogger - private final case class PokeClient() - private final case class ServerLoaded() - private final case class PlayerLoaded(tplayer : Player) - private final case class ListAccountCharacters() - private final case class SetCurrentAvatar(tplayer : Player) - private final case class Continent_GiveItemFromGround(tplyaer : Player, item : Option[Equipment]) //TODO wrong place, move later - var sessionId : Long = 0 var leftRef : ActorRef = ActorRef.noSender var rightRef : ActorRef = ActorRef.noSender var avatarService = Actor.noSender - var accessor = Actor.noSender var taskResolver = Actor.noSender + var galaxy = Actor.noSender + var continent : Continent = Continent.Nowhere var clientKeepAlive : Cancellable = WorldSessionActor.DefaultCancellable @@ -71,8 +67,8 @@ class WorldSessionActor extends Actor with MDCContextAware { } context.become(Started) ServiceManager.serviceManager ! Lookup("avatar") - ServiceManager.serviceManager ! Lookup("accessor1") ServiceManager.serviceManager ! Lookup("taskResolver") + ServiceManager.serviceManager ! Lookup("galaxy") case _ => log.error("Unknown message") @@ -83,12 +79,12 @@ class WorldSessionActor extends Actor with MDCContextAware { case ServiceManager.LookupResult("avatar", endpoint) => avatarService = endpoint log.info("ID: " + sessionId + " Got avatar service " + endpoint) - case ServiceManager.LookupResult("accessor1", endpoint) => - accessor = endpoint - log.info("ID: " + sessionId + " Got guid service " + endpoint) case ServiceManager.LookupResult("taskResolver", endpoint) => taskResolver = endpoint log.info("ID: " + sessionId + " Got task resolver service " + endpoint) + case ServiceManager.LookupResult("galaxy", endpoint) => + galaxy = endpoint + log.info("ID: " + sessionId + " Got galaxy service " + endpoint) case ctrl @ ControlPacket(_, _) => handlePktContainer(ctrl) @@ -413,6 +409,11 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0))) + case IntergalacticCluster.GiveWorld(zoneId, zone) => + player.Continent = zoneId + continent = zone + taskResolver ! RegisterAvatar(player) + case PlayerLoaded(tplayer) => log.info(s"Player $tplayer has been loaded") //init for whole server @@ -456,6 +457,18 @@ class WorldSessionActor extends Actor with MDCContextAware { avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.LoadPlayer(tplayer.GUID, tplayer.Definition.Packet.ConstructorData(tplayer).get)) log.debug(s"ObjectCreateDetailedMessage: ${tplayer.Definition.Packet.DetailedConstructorData(tplayer).get}") + case PlayerFailedToLoad(tplayer) => + player.Continent match { + case "tzshvs" => + failWithError(s"$tplayer failed to load anywhere") + case "tzdrvs" => + galaxy ! IntergalacticCluster.GetWorld("tzshvs") + case "home3" => + galaxy ! IntergalacticCluster.GetWorld("tzdrvs") + case _ => + galaxy ! IntergalacticCluster.GetWorld("home3") + } + case SetCurrentAvatar(tplayer) => //avatar-specific val guid = tplayer.GUID @@ -488,7 +501,7 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(pkt) case default => - failWithError(s"Invalid packet class received: $default") + log.warn(s"Invalid packet class received: $default") } def handlePkt(pkt : PlanetSidePacket) : Unit = pkt match { @@ -496,7 +509,7 @@ class WorldSessionActor extends Actor with MDCContextAware { handleControlPkt(ctrl) case game : PlanetSideGamePacket => handleGamePkt(game) - case default => failWithError(s"Invalid packet class received: $default") + case default => log.error(s"Invalid packet class received: $default") } def handlePktContainer(pkt : PlanetSidePacketContainer) : Unit = pkt match { @@ -504,7 +517,7 @@ class WorldSessionActor extends Actor with MDCContextAware { handleControlPkt(ctrlPkt) case game @ GamePacket(opcode, seq, gamePkt) => handleGamePkt(gamePkt) - case default => failWithError(s"Invalid packet container class received: $default") + case default => log.warn(s"Invalid packet container class received: $default") } def handleControlPkt(pkt : PlanetSideControlPacket) = { @@ -667,7 +680,7 @@ class WorldSessionActor extends Actor with MDCContextAware { //check can spawn on last continent/location from player //if yes, get continent guid accessors //if no, get sanctuary guid accessors and reset the player's expectations - taskResolver ! RegisterAvatar(player) + galaxy ! IntergalacticCluster.GetWorld("home3") import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global @@ -1181,7 +1194,7 @@ class WorldSessionActor extends Actor with MDCContextAware { TaskResolver.GiveTask( new Task() { private val localObject = obj - private val localAccessor = accessor + private val localAccessor = continent.GUID override def isComplete : Task.Resolution.Value = { try { @@ -1316,7 +1329,7 @@ class WorldSessionActor extends Actor with MDCContextAware { TaskResolver.GiveTask( new Task() { private val localObject = obj - private val localAccessor = accessor + private val localAccessor = continent.GUID override def isComplete : Task.Resolution.Value = { try { @@ -1561,7 +1574,7 @@ class WorldSessionActor extends Actor with MDCContextAware { def failWithError(error : String) = { log.error(error) - //sendResponse(PacketCoding.CreateControlPacket(ConnectionClose())) + sendResponse(PacketCoding.CreateControlPacket(ConnectionClose())) } def sendResponse(cont : PlanetSidePacketContainer) : Unit = { @@ -1581,7 +1594,14 @@ class WorldSessionActor extends Actor with MDCContextAware { } object WorldSessionActor { - final case class ResponseToSelf(pkt : GamePacket) + private final case class ResponseToSelf(pkt : GamePacket) + private final case class PokeClient() + private final case class ServerLoaded() + private final case class PlayerLoaded(tplayer : Player) + private final case class PlayerFailedToLoad(tplayer : Player) + private final case class ListAccountCharacters() + private final case class SetCurrentAvatar(tplayer : Player) + private final case class Continent_GiveItemFromGround(tplyaer : Player, item : Option[Equipment]) //TODO wrong place, move later /** * A placeholder `Cancellable` object. From ce8d61a4d3633eed864b285cd77a1ba46476ae5a Mon Sep 17 00:00:00 2001 From: FateJH Date: Sat, 16 Sep 2017 02:04:34 -0400 Subject: [PATCH 2/8] transferred control of dropping and collecting objects to the Continent scope; solve a bug that had to do with collecting dropped equipment --- .../objects/continent/Continent.scala | 2 +- .../objects/continent/ContinentActor.scala | 2 +- .../src/main/scala/WorldSessionActor.scala | 134 ++++++------------ 3 files changed, 43 insertions(+), 95 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/continent/Continent.scala b/common/src/main/scala/net/psforever/objects/continent/Continent.scala index e084babb..87b8b943 100644 --- a/common/src/main/scala/net/psforever/objects/continent/Continent.scala +++ b/common/src/main/scala/net/psforever/objects/continent/Continent.scala @@ -51,7 +51,7 @@ object Continent { final case class GetItemOnGround(player : Player, item_guid : PlanetSideGUID) - final case class GiveItemFromGround(player : Player, item : Equipment) + final case class ItemFromGround(player : Player, item : Equipment) def apply(zoneId : String, map : String) : Continent = { new Continent(zoneId, map) diff --git a/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala b/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala index c81bf90d..ff771560 100644 --- a/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala +++ b/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala @@ -20,7 +20,7 @@ class ContinentActor(continent : Continent) extends Actor { case GetItemOnGround(player, item_guid) => FindItemOnGround(item_guid) match { case Some(item) => - sender ! GiveItemFromGround(player, item) + sender ! ItemFromGround(player, item) case None => log.warn(s"item on ground $item_guid was requested by $player for pickup but was not found") } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 4aae3f2a..6433fd4f 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -477,26 +477,33 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))) //temporary location - case Continent_GiveItemFromGround(tplayer, item) => - item match { - case Some(obj) => - val obj_guid = obj.GUID - tplayer.Fit(obj) match { - case Some(slot) => - PickupItemFromGround(obj_guid) - tplayer.Slot(slot).Equipment = item - sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(tplayer.GUID, obj_guid, slot))) - avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(tplayer.GUID, obj_guid)) - if(-1 < slot && slot < 5) { - avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentInHand(tplayer.GUID, slot, obj)) - } - case None => - DropItemOnGround(obj, obj.Position, obj.Orientation) //restore + case Continent.ItemFromGround(tplayer, item) => + val obj_guid = item.GUID + val player_guid = tplayer.GUID + tplayer.Fit(item) match { + case Some(slot) => + tplayer.Slot(slot).Equipment = item + //sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(tplayer.GUID, obj_guid, slot))) + avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(player_guid, obj_guid)) + val definition = item.Definition + sendResponse( + PacketCoding.CreateGamePacket(0, + ObjectCreateDetailedMessage( + definition.ObjectId, + obj_guid, + ObjectCreateMessageParent(player_guid, slot), + definition.Packet.DetailedConstructorData(item).get + ) + ) + ) + if(-1 < slot && slot < 5) { + avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentInHand(player_guid, slot, item)) } - case None => ; + case None => + continent.Actor ! Continent.DropItemOnGround(item, item.Position, item.Orientation) //restore } - case WorldSessionActor.ResponseToSelf(pkt) => + case ResponseToSelf(pkt) => log.info(s"Received a direct message: $pkt") sendResponse(pkt) @@ -705,17 +712,17 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, ReplicationStreamMessage(5, Some(6), Vector(SquadListing())))) //clear squad list //all players are part of the same zone right now, so don't expect much - val continent = player.Continent + val playerContinent = player.Continent val player_guid = player.GUID - LivePlayerList.WorldPopulation({ case (_, char : Player) => char.Continent == continent && char.HasGUID && char.GUID != player_guid}).foreach(char => { + LivePlayerList.WorldPopulation({ case (_, char : Player) => char.Continent == playerContinent && char.HasGUID && char.GUID != player_guid}).foreach(char => { sendResponse( PacketCoding.CreateGamePacket(0, ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get) ) ) }) - //all items are part of a single zone right now, so don't expect much - WorldSessionActor.equipmentOnGround.foreach(item => { + //render Equipment that was dropped into world before player arrived + continent.EquipmentOnGround.toList.foreach(item => { val definition = item.Definition sendResponse( PacketCoding.CreateGamePacket(0, @@ -728,7 +735,7 @@ class WorldSessionActor extends Actor with MDCContextAware { ) }) - avatarService ! Join("home3") + avatarService ! Join(playerContinent) self ! SetCurrentAvatar(player) case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) => @@ -800,10 +807,11 @@ class WorldSessionActor extends Actor with MDCContextAware { player.FreeHand.Equipment match { case Some(item) => if(item.GUID == item_guid) { + val orient : Vector3 = Vector3(0f, 0f, player.Orientation.z) player.FreeHand.Equipment = None - DropItemOnGround(item, player.Position, player.Orientation) + continent.Actor ! Continent.DropItemOnGround(item, player.Position, orient) //TODO do I need to wait for callback? 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, player.Orientation, item)) + avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, player.Position, orient, item)) } else { log.warn(s"item in hand was ${item.GUID} but trying to drop $item_guid; nothing will be dropped") @@ -814,7 +822,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) => log.info("PickupItem: " + msg) - self ! Continent_GiveItemFromGround(player, PickupItemFromGround(item_guid)) + continent.Actor ! Continent.GetItemOnGround(player, item_guid) case msg @ ReloadMessage(item_guid, ammo_clip, unk1) => log.info("Reload: " + msg) @@ -929,9 +937,10 @@ class WorldSessionActor extends Actor with MDCContextAware { case None => //item2 does not fit; drop on ground val pos = player.Position - val orient = player.Orientation - DropItemOnGround(item2, pos, player.Orientation) - sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item2.GUID, pos, 0f, 0f, orient.z))) //ground + val playerOrient = player.Orientation + val orient : Vector3 = Vector3(0f, 0f, playerOrient.z) + continent.Actor ! Continent.DropItemOnGround(item2, pos, orient) + sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item2.GUID, pos, 0f, 0f, playerOrient.z))) //ground avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, pos, orient, item2)) } @@ -1270,7 +1279,7 @@ class WorldSessionActor extends Actor with MDCContextAware { override def onSuccess() : Unit = { val definition = localObject.Definition - localAnnounce ! WorldSessionActor.ResponseToSelf( + localAnnounce ! ResponseToSelf( PacketCoding.CreateGamePacket(0, ObjectCreateDetailedMessage( definition.ObjectId, @@ -1406,7 +1415,7 @@ class WorldSessionActor extends Actor with MDCContextAware { } override def onSuccess() : Unit = { - localAnnounce ! WorldSessionActor.ResponseToSelf(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(localObjectGUID, 0))) + localAnnounce ! ResponseToSelf(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(localObjectGUID, 0))) if(0 <= localIndex && localIndex < 5) { avatarService ! AvatarServiceMessage(localTarget.Continent, AvatarAction.ObjectDelete(localTarget.GUID, localObjectGUID)) } @@ -1517,61 +1526,6 @@ class WorldSessionActor extends Actor with MDCContextAware { } } - /** - * Add an object to the local `List` of objects on the ground. - * @param item the `Equipment` to be dropped - * @param pos where the `item` will be dropped - * @param orient in what direction the item will face when dropped - * @return the global unique identifier of the object - */ - private def DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3) : PlanetSideGUID = { - item.Position = pos - item.Orientation = orient - WorldSessionActor.equipmentOnGround += item - item.GUID - } - - // private def FindItemOnGround(item_guid : PlanetSideGUID) : Option[Equipment] = { - // equipmentOnGround.find(item => item.GUID == item_guid) - // } - - /** - * Remove an object from the local `List` of objects on the ground. - * @param item_guid the `Equipment` to be picked up - * @return the object being picked up - */ - private def PickupItemFromGround(item_guid : PlanetSideGUID) : Option[Equipment] = { - recursiveFindItemOnGround(WorldSessionActor.equipmentOnGround.iterator, item_guid) match { - case Some(index) => - Some(WorldSessionActor.equipmentOnGround.remove(index)) - case None => - None - } - } - - /** - * Shift through objects on the ground to find the location of a specific item. - * @param iter an `Iterator` of `Equipment` - * @param item_guid the global unique identifier of the piece of `Equipment` being sought - * @param index the current position in the array-list structure used to create the `Iterator` - * @return the index of the object matching `item_guid`, if found; - * `None`, otherwise - */ - @tailrec private def recursiveFindItemOnGround(iter : Iterator[Equipment], item_guid : PlanetSideGUID, index : Int = 0) : Option[Int] = { - if(!iter.hasNext) { - None - } - else { - val item : Equipment = iter.next - if(item.GUID == item_guid) { - Some(index) - } - else { - recursiveFindItemOnGround(iter, item_guid, index + 1) - } - } - } - def failWithError(error : String) = { log.error(error) sendResponse(PacketCoding.CreateControlPacket(ConnectionClose())) @@ -1593,15 +1547,15 @@ class WorldSessionActor extends Actor with MDCContextAware { } } +final case class ResponseToSelf(pkt : GamePacket) + object WorldSessionActor { - private final case class ResponseToSelf(pkt : GamePacket) private final case class PokeClient() private final case class ServerLoaded() private final case class PlayerLoaded(tplayer : Player) private final case class PlayerFailedToLoad(tplayer : Player) private final case class ListAccountCharacters() private final case class SetCurrentAvatar(tplayer : Player) - private final case class Continent_GiveItemFromGround(tplyaer : Player, item : Option[Equipment]) //TODO wrong place, move later /** * A placeholder `Cancellable` object. @@ -1611,12 +1565,6 @@ object WorldSessionActor { def isCancelled() : Boolean = true } - //TODO this is a temporary local system; replace it in the future - //in the future, items dropped on the ground will be managed by a data structure on an external Actor representing the continent - //like so: WSA -> /GetItemOnGround/ -> continent -> /GiveItemFromGround/ -> WSA - import scala.collection.mutable.ListBuffer - private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]() - def Distance(pos1 : Vector3, pos2 : Vector3) : Float = { math.sqrt(DistanceSquared(pos1, pos2)).toFloat } From 402d4c5b3e10acd68897c04b5fc76de557916f6b Mon Sep 17 00:00:00 2001 From: FateJH Date: Tue, 19 Sep 2017 20:51:48 -0400 Subject: [PATCH 3/8] renamed Continent* to Zone*; fleshed out example loading fucntionality --- .../psforever/objects/LivePlayerList.scala | 176 ++++++++++++++---- .../continent/IntergalacticCluster.scala | 19 +- .../continent/{Continent.scala => Zone.scala} | 38 +++- .../{ContinentActor.scala => ZoneActor.scala} | 4 +- pslogin/src/main/scala/PsLogin.scala | 6 +- .../src/main/scala/WorldSessionActor.scala | 99 +++++----- 6 files changed, 238 insertions(+), 104 deletions(-) rename common/src/main/scala/net/psforever/objects/continent/{Continent.scala => Zone.scala} (63%) rename common/src/main/scala/net/psforever/objects/continent/{ContinentActor.scala => ZoneActor.scala} (95%) diff --git a/common/src/main/scala/net/psforever/objects/LivePlayerList.scala b/common/src/main/scala/net/psforever/objects/LivePlayerList.scala index c0ad0e8f..c0e39856 100644 --- a/common/src/main/scala/net/psforever/objects/LivePlayerList.scala +++ b/common/src/main/scala/net/psforever/objects/LivePlayerList.scala @@ -3,6 +3,7 @@ package net.psforever.objects import net.psforever.packet.game.PlanetSideGUID +import scala.annotation.tailrec import scala.collection.concurrent.{Map, TrieMap} /** @@ -12,18 +13,28 @@ import scala.collection.concurrent.{Map, TrieMap} private class LivePlayerList { /** key - the session id; value - a `Player` object */ private val sessionMap : Map[Long, Player] = new TrieMap[Long, Player] - /** key - the global unique identifier; value - the session id */ - private val playerMap : Map[Int, Long] = new TrieMap[Int, Long] + /** the index of the List corresponds to zone number 1-32 with 0 being "Nowhere" */ + /** each mapping: key - the global unique identifier; value - the session id */ + private val zoneMap : List[Map[Int, Long]] = List.fill(33)(new TrieMap[Int,Long]) def WorldPopulation(predicate : ((_, Player)) => Boolean) : List[Player] = { - sessionMap.filter(predicate).map({ case(_, char) => char }).toList + sessionMap.filter(predicate).values.toList + } + + def ZonePopulation(zone : Int, predicate : ((_, Player)) => Boolean) : List[Player] = { + zoneMap.lift(zone) match { + case Some(map) => + val list = map.values.toList + sessionMap.filter({ case ((sess, _)) => list.contains(sess) }).filter(predicate).values.toList + case None => + Nil + } } def Add(sessionId : Long, player : Player) : Boolean = { sessionMap.values.find(char => char.equals(player)) match { case None => sessionMap.putIfAbsent(sessionId, player).isEmpty - true case Some(_) => false } @@ -32,46 +43,62 @@ private class LivePlayerList { def Remove(sessionId : Long) : Option[Player] = { sessionMap.remove(sessionId) match { case Some(char) => - playerMap.find({ case(_, sess) => sess == sessionId }) match { - case Some((guid, _)) => - playerMap.remove(guid) - case None => ; - } + zoneMap.foreach(zone => { + recursiveRemoveSession(zone.iterator, sessionId) match { + case Some(guid) => + zone.remove(guid) + case None => ; + } + }) Some(char) case None => None } } - def Get(guid : PlanetSideGUID) : Option[Player] = { - Get(guid.guid) + @tailrec private def recursiveRemoveSession(iter : Iterator[(Int, Long)], sessionId : Long) : Option[Int] = { + if(!iter.hasNext) { + None + } + else { + val (guid : Int, sess : Long) = iter.next + if(sess == sessionId) { + Some(guid) + } + else { + recursiveRemoveSession(iter, sessionId) + } + } } - def Get(guid : Int) : Option[Player] = { - playerMap.get(guid) match { - case Some(sess) => - sessionMap.get(sess) - case _ => + def Get(zone : Int, guid : PlanetSideGUID) : Option[Player] = { + Get(zone, guid.guid) + } + + def Get(zone : Int, guid : Int) : Option[Player] = { + zoneMap.lift(zone) match { + case Some(map) => + map.get(guid) match { + case Some(sessionId) => + sessionMap.get(sessionId) + case _ => + None + } + case None => None } } - def Assign(sessionId : Long, guid : PlanetSideGUID) : Boolean = Assign(sessionId, guid.guid) + def Assign(zone: Int, sessionId : Long, guid : PlanetSideGUID) : Boolean = Assign(zone, sessionId, guid.guid) - def Assign(sessionId : Long, guid : Int) : Boolean = { - sessionMap.find({ case(sess, _) => sess == sessionId}) match { - case Some((_, char)) => - if(char.GUID.guid == guid) { - playerMap.find({ case(_, sess) => sess == sessionId }) match { - case Some((id, _)) => - playerMap.remove(id) - case None => ; - } - playerMap.put(guid, sessionId) - true - } - else { - false + def Assign(zone : Int, sessionId : Long, guid : Int) : Boolean = { + sessionMap.get(sessionId) match { + case Some(_) => + zoneMap.lift(zone) match { + case Some(zn) => + AssignToZone(zn, sessionId, guid) + case None => + false } case None => @@ -79,10 +106,36 @@ private class LivePlayerList { } } + private def AssignToZone(zone : Map[Int, Long], sessionId : Long, guid : Int) : Boolean = { + zone.get(guid) match { + case Some(_) => + false + case None => + zone(guid) = sessionId + true + } + } + + def Drop(zone : Int, guid : PlanetSideGUID) : Option[Player] = Drop(zone, guid.guid) + + def Drop(zone : Int, guid : Int) : Option[Player] = { + zoneMap.lift(zone) match { + case Some(map) => + map.remove(guid) match { + case Some(sessionId) => + sessionMap.get(sessionId) + case None => + None + } + case None => + None + } + } + def Shutdown : List[Player] = { val list = sessionMap.values.toList sessionMap.clear - playerMap.clear + zoneMap.foreach(map => map.clear()) list } } @@ -90,20 +143,26 @@ private class LivePlayerList { /** * A class for storing `Player` mappings for users that are currently online. * The mapping system is tightly coupled between the `Player` class and to an instance of `WorldSessionActor`. - * A loose coupling between the current globally unique identifier (GUID) and the user is also present.
+ * Looser couplings exist between the instance of `WorldSessionActor` and a given `Player`'s globally unique id. + * These looser couplings are zone-specific. + * Though the user may have local knowledge of the zone they inhabit on their `Player` object, + * it should not be trusted.
*
* Use:
* 1) When a users logs in during `WorldSessionActor`, associate that user's session id and the character.
*        `LivePlayerList.Add(session, player)`
* 2) When that user's chosen character is declared his avatar using `SetCurrentAvatarMessage`, * also associate the user's session with their current GUID.
- *        `LivePlayerList.Assign(session, guid)`
+ *        `LivePlayerList.Assign(zone, session, guid)`
* 3) Repeat the previous step for as many times the user's GUID changes, especially during the aforementioned condition.
* 4a) In between the previous two steps, a user's character may be referenced by their current GUID.
- *        `LivePlayerList.Get(guid)`
+ *        `LivePlayerList.Get(zone, guid)`
* 4b) Also in between those same previous steps, a range of characters may be queried based on provided statistics.
*        `LivePlayerList.WorldPopulation(...)`
- * 5) When the user leaves the game, his character's entries are removed from the mappings.
+ *        `LivePlayerList.ZonePopulation(zone, ...)`
+ * 5) When the user navigates away from a region completely, their entry is forgotten.
+ *        `LivePlayerList.Drop(zone, guid)`
+ * 6) When the user leaves the game entirely, his character's entries are removed from the mappings.
*        `LivePlayerList.Remove(session)` */ object LivePlayerList { @@ -114,7 +173,7 @@ object LivePlayerList { * Given some criteria, examine the mapping of user characters and find the ones that fulfill the requirements.
*
* Note the signature carefully. - * A two-element tuple is checked, but only the second element of that tuple - a character - is eligible for being queried. + * A two-element tuple is checked, but only the second element of that tuple - a `Player` - is eligible for being queried. * The first element is ignored. * Even a predicate as simple as `{ case ((x : Long, _)) => x > 0 }` will not work for that reason. * @param predicate the conditions for filtering the live `Player`s @@ -122,6 +181,19 @@ object LivePlayerList { */ def WorldPopulation(predicate : ((_, Player)) => Boolean) : List[Player] = Instance.WorldPopulation(predicate) + /** + * Given some criteria, examine the mapping of user characters for a zone and find the ones that fulfill the requirements.
+ *
+ * Note the signature carefully. + * A two-element tuple is checked, but only the second element of that tuple - a `Player` - is eligible for being queried. + * The first element is ignored. + * Even a predicate as simple as `{ case ((x : Long, _)) => x > 0 }` will not work for that reason. + * @param zone the number of the zone + * @param predicate the conditions for filtering the live `Player`s + * @return a list of users's `Player`s that fit the criteria + */ + def ZonePopulation(zone : Int, predicate : ((_, Player)) => Boolean) : List[Player] = Instance.ZonePopulation(zone, predicate) + /** * Create a mapped entry between the user's session and a user's character. * Neither the player nor the session may exist in the current mappings if this is to work. @@ -142,39 +214,61 @@ object LivePlayerList { /** * Get a user's character from the mappings. + * @param zone the number of the zone * @param guid the current GUID of the character * @return the character, if it can be found using the GUID */ - def Get(guid : PlanetSideGUID) : Option[Player] = Instance.Get(guid) + def Get(zone : Int, guid : PlanetSideGUID) : Option[Player] = Instance.Get(zone, guid) /** * Get a user's character from the mappings. + * @param zone the number of the zone * @param guid the current GUID of the character * @return the character, if it can be found using the GUID */ - def Get(guid : Int) : Option[Player] = Instance.Get(guid) + def Get(zone : Int, guid : Int) : Option[Player] = Instance.Get(zone, guid) /** * Given a session that maps to a user's character, create a mapping between the character's current GUID and the session. * If the user already has a GUID in the mappings, remove it and assert the new one. + * @param zone the number of the zone * @param sessionId the session * @param guid the GUID to associate with the character; * technically, it has already been assigned and should be findable using `{character}.GUID.guid` * @return `true`, if the mapping was created; * `false`, if the session can not be found or if the character's GUID doesn't match the one provided */ - def Assign(sessionId : Long, guid : PlanetSideGUID) : Boolean = Instance.Assign(sessionId, guid) + def Assign(zone : Int, sessionId : Long, guid : PlanetSideGUID) : Boolean = Instance.Assign(zone, sessionId, guid) /** * Given a session that maps to a user's character, create a mapping between the character's current GUID and the session. * If the user already has a GUID in the mappings, remove it and assert the new one. + * @param zone the number of the zone * @param sessionId the session * @param guid the GUID to associate with the character; * technically, it has already been assigned and should be findable using `{character}.GUID.guid` * @return `true`, if the mapping was created; * `false`, if the session can not be found or if the character's GUID doesn't match the one provided */ - def Assign(sessionId : Long, guid : Int) : Boolean = Instance.Assign(sessionId, guid) + def Assign(zone : Int, sessionId : Long, guid : Int) : Boolean = Instance.Assign(zone, sessionId, guid) + + /** + * Given a GUID, remove any record of it. + * @param zone the number of the zone + * @param guid a GUID associated with the character; + * it does not have to be findable using `{character}.GUID.guid` + * @return any `Player` that may have been associated with this GUID + */ + def Drop(zone : Int, guid : PlanetSideGUID) : Option[Player] = Instance.Drop(zone, guid) + + /** + * Given a GUID, remove any record of it. + * @param zone the number of the zone + * @param guid a GUID associated with the character; + * it does not have to be findable using `{character}.GUID.guid` + * @return any `Player` that may have been associated with this GUID + */ + def Drop(zone : Int, guid : Int) : Option[Player] = Instance.Drop(zone, guid) /** * Hastily remove all mappings and ids. diff --git a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala index 6c4a880a..bd8dc83a 100644 --- a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala +++ b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala @@ -2,10 +2,11 @@ package net.psforever.objects.continent import akka.actor.Actor +import net.psforever.objects.Player import scala.annotation.tailrec -class IntergalacticCluster(continents : List[Continent]) extends Actor { +class IntergalacticCluster(continents : List[Zone]) extends Actor { //private[this] val log = org.log4s.getLogger for(continent <- continents) { continent.Actor //seed context @@ -17,13 +18,19 @@ class IntergalacticCluster(continents : List[Continent]) extends Actor { case Some(continent) => sender ! IntergalacticCluster.GiveWorld(zoneId, continent) case None => - sender ! IntergalacticCluster.GiveWorld(zoneId, Continent.Nowhere) + sender ! IntergalacticCluster.GiveWorld(zoneId, Zone.Nowhere) } + case IntergalacticCluster.RequestZoneInitialization(tplayer) => + continents.foreach(zone => { + sender ! Zone.ZoneInitialization(zone.ZoneInitialization()) + }) + sender ! IntergalacticCluster.ZoneInitializationComplete(tplayer) + case _ => ; } - @tailrec private def findWorldInCluster(iter : Iterator[Continent], zoneId : String) : Option[Continent] = { + @tailrec private def findWorldInCluster(iter : Iterator[Zone], zoneId : String) : Option[Zone] = { if(!iter.hasNext) { None } @@ -42,5 +49,9 @@ class IntergalacticCluster(continents : List[Continent]) extends Actor { object IntergalacticCluster { final case class GetWorld(zoneId : String) - final case class GiveWorld(zoneId : String, zone : Continent) + final case class GiveWorld(zoneId : String, zone : Zone) + + final case class RequestZoneInitialization(tplayer : Player) + + final case class ZoneInitializationComplete(tplayer : Player) } diff --git a/common/src/main/scala/net/psforever/objects/continent/Continent.scala b/common/src/main/scala/net/psforever/objects/continent/Zone.scala similarity index 63% rename from common/src/main/scala/net/psforever/objects/continent/Continent.scala rename to common/src/main/scala/net/psforever/objects/continent/Zone.scala index 87b8b943..f6ff2244 100644 --- a/common/src/main/scala/net/psforever/objects/continent/Continent.scala +++ b/common/src/main/scala/net/psforever/objects/continent/Zone.scala @@ -9,21 +9,22 @@ 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 Continent(zoneId : String, map : String) { +class Zone(id : String, zoneNumber : Int, map : String) { private var actor = ActorRef.noSender - private val guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536)) + private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(6)) private var accessor : ActorRef = ActorRef.noSender def Actor(implicit context : ActorContext) : ActorRef = { if(actor == ActorRef.noSender) { - actor = context.actorOf(Props(classOf[ContinentActor], this), s"$zoneId-actor") + actor = context.actorOf(Props(classOf[ZoneActor], this), s"$id-actor") - val pool = guid.AddPool("pool", (400 to 599).toList) + val pool = guid.AddPool("pool", 6 :: Nil)//(400 to 599).toList) val poolActor = context.actorOf(Props(classOf[NumberPoolActor], pool), name = s"$ZoneId-poolActor") pool.Selector = new RandomSelector accessor = context.actorOf(Props(classOf[NumberPoolAccessorActor], guid, pool, poolActor), s"$ZoneId-accessor") @@ -33,19 +34,36 @@ class Continent(zoneId : String, map : String) { private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]() - def ZoneId : String = zoneId + def ZoneId : String = id + + def ZoneNumber : Int = zoneNumber def Map : String = map def GUID : ActorRef = accessor + def GUID_=(guidSrc : NumberPoolHub) : ActorRef = { + if(accessor == ActorRef.noSender) { + guid = guidSrc + } + accessor + } + def GUID(object_guid : PlanetSideGUID) : Option[IdentifiableEntity] = guid(object_guid.guid) def EquipmentOnGround : ListBuffer[Equipment] = equipmentOnGround + + def ZoneInitialization() : List[GamePacket] = { + List.empty[GamePacket] + } + + def ZoneConfiguration() : List[GamePacket] = { + List.empty[GamePacket] + } } -object Continent { - final def Nowhere : Continent = { Continent("", "") } //TODO needs overrides +object Zone { + final def Nowhere : Zone = { Zone("nowhere", 0, "nowhere") } //TODO needs overrides final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3) @@ -53,7 +71,9 @@ object Continent { final case class ItemFromGround(player : Player, item : Equipment) - def apply(zoneId : String, map : String) : Continent = { - new Continent(zoneId, map) + final case class ZoneInitialization(list : List[GamePacket]) + + def apply(zoneId : String, zoneNumber : Int, map : String) : Zone = { + new Zone(zoneId, zoneNumber, map) } } diff --git a/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala b/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala similarity index 95% rename from common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala rename to common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala index ff771560..309c129d 100644 --- a/common/src/main/scala/net/psforever/objects/continent/ContinentActor.scala +++ b/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala @@ -7,9 +7,9 @@ import net.psforever.packet.game.PlanetSideGUID import scala.annotation.tailrec -class ContinentActor(continent : Continent) extends Actor { +class ZoneActor(continent : Zone) extends Actor { private[this] val log = org.log4s.getLogger - import Continent._ + import Zone._ def receive : Receive = { case DropItemOnGround(item, pos, orient) => diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index e8376895..178fc645 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -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.{Continent, IntergalacticCluster} +import net.psforever.objects.continent.{Zone, IntergalacticCluster} import net.psforever.objects.guid.TaskResolver import org.slf4j import org.fusesource.jansi.Ansi._ @@ -219,8 +219,8 @@ object PsLogin { } } - def createContinents() : List[Continent] = { - Continent("home3","map13") :: Nil + def createContinents() : List[Zone] = { + Zone("home3",13,"map13") :: Nil } def main(args : Array[String]) : Unit = { diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 6433fd4f..ee1c6289 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.{Continent, IntergalacticCluster} +import net.psforever.objects.continent.{Zone, IntergalacticCluster} import net.psforever.objects.entity.IdentifiableEntity import net.psforever.objects.equipment._ import net.psforever.objects.guid.{Task, TaskResolver} @@ -34,7 +34,7 @@ class WorldSessionActor extends Actor with MDCContextAware { var avatarService = Actor.noSender var taskResolver = Actor.noSender var galaxy = Actor.noSender - var continent : Continent = Continent.Nowhere + var continent : Zone = Zone.Nowhere var clientKeepAlive : Cancellable = WorldSessionActor.DefaultCancellable @@ -45,10 +45,12 @@ class WorldSessionActor extends Actor with MDCContextAware { avatarService ! Leave() LivePlayerList.Remove(sessionId) match { case Some(tplayer) => - val guid = tplayer.GUID - avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(guid, guid)) - taskResolver ! UnregisterAvatar(tplayer) - //TODO normally, the actual player avatar persists a minute or so after the user disconnects + if(tplayer.HasGUID) { + val guid = tplayer.GUID + avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(guid, guid)) + taskResolver ! UnregisterAvatar(tplayer) + //TODO normally, the actual player avatar persists a minute or so after the user disconnects + } case None => ; } } @@ -417,7 +419,23 @@ 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) + + case PlayerFailedToLoad(tplayer) => + player.Continent match { + case "tzshvs" => + log.error(s"$tplayer failed to load anywhere") + self ! IntergalacticCluster.GiveWorld("", Zone.Nowhere) + case "tzdrvs" => + galaxy ! IntergalacticCluster.GetWorld("tzshvs") + case "home3" => + galaxy ! IntergalacticCluster.GetWorld("tzdrvs") + case _ => + galaxy ! IntergalacticCluster.GetWorld("home3") + } + + case Zone.ZoneInitialization(initList) => + //TODO iterate over initList; for now, just do this sendResponse( PacketCoding.CreateGamePacket(0, BuildingInfoUpdateMessage( @@ -447,8 +465,11 @@ class WorldSessionActor extends Actor with MDCContextAware { ) sendResponse(PacketCoding.CreateGamePacket(0, ContinentalLockUpdateMessage(PlanetSideGUID(13), PlanetSideEmpire.VS))) // "The VS have captured the VS Sanctuary." sendResponse(PacketCoding.CreateGamePacket(0, BroadcastWarpgateUpdateMessage(PlanetSideGUID(13), PlanetSideGUID(1), false, false, true))) // VS Sanctuary: Inactive Warpgate -> Broadcast Warpgate - //LoadMapMessage -> BeginZoningMessage - sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage("map13","home3",40100,25,true,3770441820L))) //VS Sanctuary + sendResponse(PacketCoding.CreateGamePacket(0, ZonePopulationUpdateMessage(PlanetSideGUID(13), 414, 138, 0, 138, 0, 138, 0, 138, 0))) + + case IntergalacticCluster.ZoneInitializationComplete(tplayer)=> + //this will cause the client to send back a BeginZoningMessage packet (see below) + sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage(continent.Map, continent.ZoneId, 40100,25,true,3770441820L))) //VS Sanctuary //load the now-registered player tplayer.Spawn sendResponse(PacketCoding.CreateGamePacket(0, @@ -457,33 +478,18 @@ class WorldSessionActor extends Actor with MDCContextAware { avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.LoadPlayer(tplayer.GUID, tplayer.Definition.Packet.ConstructorData(tplayer).get)) log.debug(s"ObjectCreateDetailedMessage: ${tplayer.Definition.Packet.DetailedConstructorData(tplayer).get}") - case PlayerFailedToLoad(tplayer) => - player.Continent match { - case "tzshvs" => - failWithError(s"$tplayer failed to load anywhere") - case "tzdrvs" => - galaxy ! IntergalacticCluster.GetWorld("tzshvs") - case "home3" => - galaxy ! IntergalacticCluster.GetWorld("tzdrvs") - case _ => - galaxy ! IntergalacticCluster.GetWorld("home3") - } - case SetCurrentAvatar(tplayer) => - //avatar-specific val guid = tplayer.GUID - LivePlayerList.Assign(sessionId, guid) + LivePlayerList.Assign(continent.ZoneNumber, sessionId, guid) sendResponse(PacketCoding.CreateGamePacket(0, SetCurrentAvatarMessage(guid,0,0))) sendResponse(PacketCoding.CreateGamePacket(0, CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))) - //temporary location - case Continent.ItemFromGround(tplayer, item) => + case Zone.ItemFromGround(tplayer, item) => val obj_guid = item.GUID val player_guid = tplayer.GUID tplayer.Fit(item) match { case Some(slot) => tplayer.Slot(slot).Equipment = item - //sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(tplayer.GUID, obj_guid, slot))) avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(player_guid, obj_guid)) val definition = item.Definition sendResponse( @@ -500,7 +506,7 @@ class WorldSessionActor extends Actor with MDCContextAware { avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentInHand(player_guid, slot, item)) } case None => - continent.Actor ! Continent.DropItemOnGround(item, item.Position, item.Orientation) //restore + continent.Actor ! Zone.DropItemOnGround(item, item.Position, item.Orientation) //restore } case ResponseToSelf(pkt) => @@ -603,7 +609,7 @@ class WorldSessionActor extends Actor with MDCContextAware { player = Player("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1) player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f) player.Orientation = Vector3(0f, 0f, 90f) - player.Continent = "home3" + //player.Continent = "home3" player.Slot(0).Equipment = beamer1 player.Slot(2).Equipment = suppressor1 player.Slot(4).Equipment = forceblade1 @@ -684,13 +690,14 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, ActionResultMessage(false, Some(1)))) case CharacterRequestAction.Select => LivePlayerList.Add(sessionId, player) - //check can spawn on last continent/location from player - //if yes, get continent guid accessors - //if no, get sanctuary guid accessors and reset the player's expectations + //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") import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global + clientKeepAlive.cancel clientKeepAlive = context.system.scheduler.schedule(0 seconds, 500 milliseconds, self, PokeClient()) case default => log.error("Unsupported " + default + " in " + msg) @@ -701,27 +708,25 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ BeginZoningMessage() => log.info("Reticulating splines ...") - //map-specific initializations (VS sanctuary) + //map-specific initializations + //TODO continent.ZoneConfiguration() sendResponse(PacketCoding.CreateGamePacket(0, SetEmpireMessage(PlanetSideGUID(2), PlanetSideEmpire.VS))) //HART building C sendResponse(PacketCoding.CreateGamePacket(0, SetEmpireMessage(PlanetSideGUID(29), PlanetSideEmpire.NC))) //South Villa Gun Tower - sendResponse(PacketCoding.CreateGamePacket(0, object2Hex)) + //sendResponse(PacketCoding.CreateGamePacket(0, object2Hex)) //sendResponse(PacketCoding.CreateGamePacket(0, furyHex)) - sendResponse(PacketCoding.CreateGamePacket(0, ZonePopulationUpdateMessage(PlanetSideGUID(13), 414, 138, 0, 138, 0, 138, 0, 138, 0))) sendResponse(PacketCoding.CreateGamePacket(0, TimeOfDayMessage(1191182336))) sendResponse(PacketCoding.CreateGamePacket(0, ReplicationStreamMessage(5, Some(6), Vector(SquadListing())))) //clear squad list - //all players are part of the same zone right now, so don't expect much - val playerContinent = player.Continent - val player_guid = player.GUID - LivePlayerList.WorldPopulation({ case (_, char : Player) => char.Continent == playerContinent && char.HasGUID && char.GUID != player_guid}).foreach(char => { + //load active players in zone + LivePlayerList.ZonePopulation(continent.ZoneNumber, _ => true).foreach(char => { sendResponse( PacketCoding.CreateGamePacket(0, ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get) ) ) }) - //render Equipment that was dropped into world before player arrived + //render Equipment that was dropped into zone before the player arrived continent.EquipmentOnGround.toList.foreach(item => { val definition = item.Definition sendResponse( @@ -735,7 +740,7 @@ class WorldSessionActor extends Actor with MDCContextAware { ) }) - avatarService ! Join(playerContinent) + avatarService ! Join(player.Continent) self ! SetCurrentAvatar(player) case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) => @@ -809,7 +814,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 ! Continent.DropItemOnGround(item, player.Position, orient) //TODO do I need to wait for callback? + continent.Actor ! Zone.DropItemOnGround(item, player.Position, orient) //TODO do I need to wait for callback? 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)) } @@ -822,7 +827,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) => log.info("PickupItem: " + msg) - continent.Actor ! Continent.GetItemOnGround(player, item_guid) + continent.Actor ! Zone.GetItemOnGround(player, item_guid) case msg @ ReloadMessage(item_guid, ammo_clip, unk1) => log.info("Reload: " + msg) @@ -939,7 +944,7 @@ class WorldSessionActor extends Actor with MDCContextAware { val pos = player.Position val playerOrient = player.Orientation val orient : Vector3 = Vector3(0f, 0f, playerOrient.z) - continent.Actor ! Continent.DropItemOnGround(item2, pos, orient) + continent.Actor ! Zone.DropItemOnGround(item2, pos, orient) sendResponse(PacketCoding.CreateGamePacket(0, ObjectDetachMessage(player.GUID, item2.GUID, pos, 0f, 0f, playerOrient.z))) //ground avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, pos, orient, item2)) } @@ -1324,6 +1329,10 @@ class WorldSessionActor extends Actor with MDCContextAware { localAnnounce ! PlayerLoaded(localPlayer) //alerts WSA resolver ! scala.util.Success(localPlayer) } + + override def onFailure(ex : Throwable) : Unit = { + localAnnounce ! PlayerFailedToLoad(localPlayer) //alerts WSA + } }, RegisterObjectTask(tplayer) +: (holsterTasks ++ fifthHolsterTask ++ inventoryTasks) ) } @@ -1547,9 +1556,9 @@ class WorldSessionActor extends Actor with MDCContextAware { } } -final case class ResponseToSelf(pkt : GamePacket) - object WorldSessionActor { + final case class ResponseToSelf(pkt : GamePacket) + private final case class PokeClient() private final case class ServerLoaded() private final case class PlayerLoaded(tplayer : Player) From 0c7c4dc58f94fbed62900cdca9fcd9e630c0da3f Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 20 Sep 2017 23:19:19 -0400 Subject: [PATCH 4/8] prototyping utility addition to Zones; resolved issue with Avatar registration causing onFailure case to fire on success --- .../continent/IntergalacticCluster.scala | 4 +- .../psforever/objects/continent/Zone.scala | 14 +++++-- .../objects/terminals/Terminal.scala | 4 ++ pslogin/src/main/scala/PsLogin.scala | 10 ++++- .../src/main/scala/WorldSessionActor.scala | 37 ++++++------------- 5 files changed, 37 insertions(+), 32 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala index bd8dc83a..d883cdad 100644 --- a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala +++ b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala @@ -7,13 +7,15 @@ import net.psforever.objects.Player import scala.annotation.tailrec class IntergalacticCluster(continents : List[Zone]) extends Actor { - //private[this] val log = org.log4s.getLogger + private[this] val log = org.log4s.getLogger for(continent <- continents) { + log.info(s"Built continent ${continent.ZoneId}") continent.Actor //seed context } def receive : Receive = { case IntergalacticCluster.GetWorld(zoneId) => + log.info(s"Asked to find $zoneId") findWorldInCluster(continents.iterator, zoneId) match { case Some(continent) => sender ! IntergalacticCluster.GiveWorld(zoneId, continent) diff --git a/common/src/main/scala/net/psforever/objects/continent/Zone.scala b/common/src/main/scala/net/psforever/objects/continent/Zone.scala index f6ff2244..b681b701 100644 --- a/common/src/main/scala/net/psforever/objects/continent/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/continent/Zone.scala @@ -6,7 +6,7 @@ import net.psforever.objects.Player import net.psforever.objects.entity.IdentifiableEntity 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.actor.{NumberPoolAccessorActor, NumberPoolActor, Register} import net.psforever.objects.guid.selector.RandomSelector import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.packet.GamePacket @@ -17,17 +17,20 @@ import scala.collection.mutable.ListBuffer class Zone(id : String, zoneNumber : Int, map : String) { private var actor = ActorRef.noSender - private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(6)) + private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536)) private var accessor : ActorRef = ActorRef.noSender + private var startupUtilities : List[(IdentifiableEntity, Int)] = List() def Actor(implicit context : ActorContext) : ActorRef = { if(actor == ActorRef.noSender) { actor = context.actorOf(Props(classOf[ZoneActor], this), s"$id-actor") - val pool = guid.AddPool("pool", 6 :: Nil)//(400 to 599).toList) + val pool = guid.AddPool("pool", (200 to 400).toList) val poolActor = context.actorOf(Props(classOf[NumberPoolActor], pool), name = s"$ZoneId-poolActor") pool.Selector = new RandomSelector accessor = context.actorOf(Props(classOf[NumberPoolAccessorActor], guid, pool, poolActor), s"$ZoneId-accessor") + + startupUtilities.foreach({case ((obj, uid)) => accessor ! Register(obj, uid, actor)}) } actor } @@ -53,6 +56,10 @@ class Zone(id : String, zoneNumber : Int, map : String) { def EquipmentOnGround : ListBuffer[Equipment] = equipmentOnGround + def AddUtility(obj : IdentifiableEntity, id : Int) : Unit = { + startupUtilities = startupUtilities :+ (obj, id) + } + def ZoneInitialization() : List[GamePacket] = { List.empty[GamePacket] } @@ -63,6 +70,7 @@ class Zone(id : String, zoneNumber : Int, map : String) { } object Zone { + final def BlankInitFunction() : Unit = { } final def Nowhere : Zone = { Zone("nowhere", 0, "nowhere") } //TODO needs overrides final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3) diff --git a/common/src/main/scala/net/psforever/objects/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/terminals/Terminal.scala index 7c19d142..81f464fe 100644 --- a/common/src/main/scala/net/psforever/objects/terminals/Terminal.scala +++ b/common/src/main/scala/net/psforever/objects/terminals/Terminal.scala @@ -142,6 +142,10 @@ object Terminal { */ final case class InfantryLoadout(exosuit : ExoSuitType.Value, subtype : Int = 0, holsters : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange + def apply(tdef : TerminalDefinition) : Terminal = { + new Terminal(tdef) + } + import net.psforever.packet.game.PlanetSideGUID def apply(guid : PlanetSideGUID, tdef : TerminalDefinition) : Terminal = { val obj = new Terminal(tdef) diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index 178fc645..e3ec8db4 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -12,8 +12,10 @@ 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.{Zone, IntergalacticCluster} +import net.psforever.objects.continent.{IntergalacticCluster, Zone} import net.psforever.objects.guid.TaskResolver +import net.psforever.objects.terminals.{OrderTerminalDefinition, Terminal} +import net.psforever.packet.game.PlanetSideGUID import org.slf4j import org.fusesource.jansi.Ansi._ import org.fusesource.jansi.Ansi.Color._ @@ -220,7 +222,11 @@ object PsLogin { } def createContinents() : List[Zone] = { - Zone("home3",13,"map13") :: Nil + val home3 = Zone("home3",13,"map13") + home3.AddUtility(Terminal(new OrderTerminalDefinition), 336) + + home3 :: + Nil } def main(args : Array[String]) : Unit = { diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index ee1c6289..eb988d12 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -412,6 +412,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) => + log.info(s"Zone $zoneId has been loaded") player.Continent = zoneId continent = zone taskResolver ! RegisterAvatar(player) @@ -424,8 +425,8 @@ class WorldSessionActor extends Actor with MDCContextAware { case PlayerFailedToLoad(tplayer) => player.Continent match { case "tzshvs" => - log.error(s"$tplayer failed to load anywhere") - self ! IntergalacticCluster.GiveWorld("", Zone.Nowhere) + failWithError(s"$tplayer failed to load anywhere") + //self ! IntergalacticCluster.GiveWorld("", Zone.Nowhere) case "tzdrvs" => galaxy ! IntergalacticCluster.GetWorld("tzshvs") case "home3" => @@ -434,7 +435,7 @@ class WorldSessionActor extends Actor with MDCContextAware { galaxy ! IntergalacticCluster.GetWorld("home3") } - case Zone.ZoneInitialization(initList) => + case Zone.ZoneInitialization(/*initList*/_) => //TODO iterate over initList; for now, just do this sendResponse( PacketCoding.CreateGamePacket(0, @@ -470,6 +471,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case IntergalacticCluster.ZoneInitializationComplete(tplayer)=> //this will cause the client to send back a BeginZoningMessage packet (see below) sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage(continent.Map, continent.ZoneId, 40100,25,true,3770441820L))) //VS Sanctuary + log.info("Load the now-registered player") //load the now-registered player tplayer.Spawn sendResponse(PacketCoding.CreateGamePacket(0, @@ -1322,7 +1324,12 @@ class WorldSessionActor extends Actor with MDCContextAware { private val localAnnounce = self override def isComplete : Task.Resolution.Value = { - Task.Resolution.Incomplete + if(localPlayer.HasGUID) { + Task.Resolution.Success + } + else { + Task.Resolution.Incomplete + } } def Execute(resolver : ActorRef) : Unit = { @@ -1462,17 +1469,6 @@ class WorldSessionActor extends Actor with MDCContextAware { tplayer.Holsters().foreach(holster => { SetCharacterSelectScreenGUID_SelectEquipment(holster.Equipment, gen) }) -// tplayer.Inventory.Items.foreach({ case((_, entry : InventoryItem)) => -// SetCharacterSelectScreenGUID_SelectEquipment(Some(entry.obj), gen) -// }) -// tplayer.Slot(5).Equipment match { -// case Some(locker) => -// locker.GUID = PlanetSideGUID(gen.getAndIncrement) -// locker.asInstanceOf[LockerContainer].Inventory.Items.foreach({ case((_, entry : InventoryItem)) => -// SetCharacterSelectScreenGUID_SelectEquipment(Some(entry.obj), gen) -// }) -// case None => ; -// } tplayer.GUID = PlanetSideGUID(gen.getAndIncrement) } @@ -1504,17 +1500,6 @@ class WorldSessionActor extends Actor with MDCContextAware { tplayer.Holsters().foreach(holster => { RemoveCharacterSelectScreenGUID_SelectEquipment(holster.Equipment) }) -// tplayer.Inventory.Items.foreach({ case((_, entry : InventoryItem)) => -// RemoveCharacterSelectScreenGUID_SelectEquipment(Some(entry.obj)) -// }) -// tplayer.Slot(5).Equipment match { -// case Some(locker) => -// locker.Invalidate() -// locker.asInstanceOf[LockerContainer].Inventory.Items.foreach({ case((_, entry : InventoryItem)) => -// RemoveCharacterSelectScreenGUID_SelectEquipment(Some(entry.obj)) -// }) -// case None => ; -// } tplayer.Invalidate() } From 981acadae55c75a9cb81ca24c4776f982112d02f Mon Sep 17 00:00:00 2001 From: FateJH Date: Thu, 21 Sep 2017 12:24:28 -0400 Subject: [PATCH 5/8] using three Terminals known to the continent home3 --- .../continent/IntergalacticCluster.scala | 6 +++--- .../psforever/objects/continent/Zone.scala | 19 ++++++++++++------- pslogin/src/main/scala/PsLogin.scala | 5 ++++- .../src/main/scala/WorldSessionActor.scala | 19 +++++++++++-------- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala index d883cdad..e40a6f20 100644 --- a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala +++ b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala @@ -25,9 +25,9 @@ class IntergalacticCluster(continents : List[Zone]) extends Actor { case IntergalacticCluster.RequestZoneInitialization(tplayer) => continents.foreach(zone => { - sender ! Zone.ZoneInitialization(zone.ZoneInitialization()) + sender ! Zone.ClientInitialization(zone.ClientInitialization()) }) - sender ! IntergalacticCluster.ZoneInitializationComplete(tplayer) + sender ! IntergalacticCluster.ClientInitializationComplete(tplayer) case _ => ; } @@ -55,5 +55,5 @@ object IntergalacticCluster { final case class RequestZoneInitialization(tplayer : Player) - final case class ZoneInitializationComplete(tplayer : Player) + final case class ClientInitializationComplete(tplayer : Player) } diff --git a/common/src/main/scala/net/psforever/objects/continent/Zone.scala b/common/src/main/scala/net/psforever/objects/continent/Zone.scala index b681b701..3340ecc1 100644 --- a/common/src/main/scala/net/psforever/objects/continent/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/continent/Zone.scala @@ -2,7 +2,7 @@ package net.psforever.objects.continent import akka.actor.{ActorContext, ActorRef, Props} -import net.psforever.objects.Player +import net.psforever.objects.{PlanetSideGameObject, Player} import net.psforever.objects.entity.IdentifiableEntity import net.psforever.objects.equipment.Equipment import net.psforever.objects.guid.NumberPoolHub @@ -25,12 +25,12 @@ class Zone(id : String, zoneNumber : Int, map : String) { if(actor == ActorRef.noSender) { actor = context.actorOf(Props(classOf[ZoneActor], this), s"$id-actor") - val pool = guid.AddPool("pool", (200 to 400).toList) + val pool = guid.AddPool("pool", (200 to 1000).toList) val poolActor = context.actorOf(Props(classOf[NumberPoolActor], pool), name = s"$ZoneId-poolActor") pool.Selector = new RandomSelector accessor = context.actorOf(Props(classOf[NumberPoolAccessorActor], guid, pool, poolActor), s"$ZoneId-accessor") - startupUtilities.foreach({case ((obj, uid)) => accessor ! Register(obj, uid, actor)}) + startupUtilities.foreach({case ((obj, uid)) => accessor ! Register(obj, uid, actor) }) } actor } @@ -52,7 +52,12 @@ class Zone(id : String, zoneNumber : Int, map : String) { accessor } - def GUID(object_guid : PlanetSideGUID) : Option[IdentifiableEntity] = guid(object_guid.guid) + def GUID(object_guid : PlanetSideGUID) : Option[PlanetSideGameObject] = guid(object_guid.guid) match { + case Some(obj) => + Some(obj.asInstanceOf[PlanetSideGameObject]) //potential casting error + case None => + None + } def EquipmentOnGround : ListBuffer[Equipment] = equipmentOnGround @@ -60,11 +65,11 @@ class Zone(id : String, zoneNumber : Int, map : String) { startupUtilities = startupUtilities :+ (obj, id) } - def ZoneInitialization() : List[GamePacket] = { + def ClientInitialization() : List[GamePacket] = { List.empty[GamePacket] } - def ZoneConfiguration() : List[GamePacket] = { + def ClientConfiguration() : List[GamePacket] = { List.empty[GamePacket] } } @@ -79,7 +84,7 @@ object Zone { final case class ItemFromGround(player : Player, item : Equipment) - final case class ZoneInitialization(list : List[GamePacket]) + final case class ClientInitialization(list : List[GamePacket]) def apply(zoneId : String, zoneNumber : Int, map : String) : Zone = { new Zone(zoneId, zoneNumber, map) diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index e3ec8db4..fafffbbe 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -223,7 +223,10 @@ object PsLogin { def createContinents() : List[Zone] = { val home3 = Zone("home3",13,"map13") - home3.AddUtility(Terminal(new OrderTerminalDefinition), 336) + val orderTerm = new OrderTerminalDefinition + home3.AddUtility(Terminal(orderTerm), 853) + home3.AddUtility(Terminal(orderTerm), 855) + home3.AddUtility(Terminal(orderTerm), 860) home3 :: Nil diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index eb988d12..0f83b0a3 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -17,7 +17,7 @@ import net.psforever.objects.equipment._ import net.psforever.objects.guid.{Task, TaskResolver} import net.psforever.objects.guid.actor.{Register, Unregister} import net.psforever.objects.inventory.{GridInventory, InventoryItem} -import net.psforever.objects.terminals.{OrderTerminalDefinition, Terminal} +import net.psforever.objects.terminals.Terminal import net.psforever.packet.game.objectcreate._ import net.psforever.types._ @@ -435,7 +435,7 @@ class WorldSessionActor extends Actor with MDCContextAware { galaxy ! IntergalacticCluster.GetWorld("home3") } - case Zone.ZoneInitialization(/*initList*/_) => + case Zone.ClientInitialization(/*initList*/_) => //TODO iterate over initList; for now, just do this sendResponse( PacketCoding.CreateGamePacket(0, @@ -468,7 +468,7 @@ 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.ZoneInitializationComplete(tplayer)=> + case IntergalacticCluster.ClientInitializationComplete(tplayer)=> //this will cause the client to send back a BeginZoningMessage packet (see below) sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage(continent.Map, continent.ZoneId, 40100,25,true,3770441820L))) //VS Sanctuary log.info("Load the now-registered player") @@ -584,8 +584,6 @@ class WorldSessionActor extends Actor with MDCContextAware { } } - val terminal = Terminal(PlanetSideGUID(55000), new OrderTerminalDefinition) - import net.psforever.objects.GlobalDefinitions._ //this part is created by the player (should be in case of ConnectToWorldRequestMessage, maybe) val energy_cell_box1 = AmmoBox(energy_cell) @@ -711,7 +709,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ BeginZoningMessage() => log.info("Reticulating splines ...") //map-specific initializations - //TODO continent.ZoneConfiguration() + //TODO continent.ClientConfiguration() sendResponse(PacketCoding.CreateGamePacket(0, SetEmpireMessage(PlanetSideGUID(2), PlanetSideEmpire.VS))) //HART building C sendResponse(PacketCoding.CreateGamePacket(0, SetEmpireMessage(PlanetSideGUID(29), PlanetSideEmpire.NC))) //South Villa Gun Tower //sendResponse(PacketCoding.CreateGamePacket(0, object2Hex)) @@ -990,9 +988,14 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ GenericObjectStateMsg(object_guid, unk1) => log.info("GenericObjectState: " + msg) - case msg @ ItemTransactionMessage(terminal_guid, transaction_type, item_page, item_name, unk1, item_guid) => - terminal.Actor ! Terminal.Request(player, msg) + case msg @ ItemTransactionMessage(terminal_guid, _, _, _, _, _) => log.info("ItemTransaction: " + msg) + continent.GUID(terminal_guid) match { + case Some(term : Terminal) => + term.Actor ! Terminal.Request(player, msg) + case Some(obj : PlanetSideGameObject) => ; + case None => ; + } case msg @ FavoritesRequest(player_guid, unk, action, line, label) => if(player.GUID == player_guid) { From 2076100b50cea5ab4174ac44fcc4bc7c354bdd6d Mon Sep 17 00:00:00 2001 From: FateJH Date: Thu, 21 Sep 2017 22:38:41 -0400 Subject: [PATCH 6/8] reorganized initialization of Zones and Terminals --- .../continent/IntergalacticCluster.scala | 19 ++++--- .../psforever/objects/continent/Zone.scala | 52 ++++++++++++------- .../objects/continent/ZoneActor.scala | 22 +++++--- pslogin/src/main/scala/PsLogin.scala | 24 ++++++--- 4 files changed, 76 insertions(+), 41 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala index e40a6f20..d58a5a08 100644 --- a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala +++ b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala @@ -1,22 +1,27 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.continent -import akka.actor.Actor +import akka.actor.{Actor, Props} import net.psforever.objects.Player import scala.annotation.tailrec -class IntergalacticCluster(continents : List[Zone]) extends Actor { +class IntergalacticCluster(zones : List[Zone]) extends Actor { private[this] val log = org.log4s.getLogger - for(continent <- continents) { - log.info(s"Built continent ${continent.ZoneId}") - continent.Actor //seed context + 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(continents.iterator, zoneId) match { + findWorldInCluster(zones.iterator, zoneId) match { case Some(continent) => sender ! IntergalacticCluster.GiveWorld(zoneId, continent) case None => @@ -24,7 +29,7 @@ class IntergalacticCluster(continents : List[Zone]) extends Actor { } case IntergalacticCluster.RequestZoneInitialization(tplayer) => - continents.foreach(zone => { + zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) }) sender ! IntergalacticCluster.ClientInitializationComplete(tplayer) diff --git a/common/src/main/scala/net/psforever/objects/continent/Zone.scala b/common/src/main/scala/net/psforever/objects/continent/Zone.scala index 3340ecc1..533db3af 100644 --- a/common/src/main/scala/net/psforever/objects/continent/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/continent/Zone.scala @@ -17,22 +17,29 @@ import scala.collection.mutable.ListBuffer class Zone(id : String, zoneNumber : Int, map : String) { private var actor = ActorRef.noSender - private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536)) private var accessor : ActorRef = ActorRef.noSender - private var startupUtilities : List[(IdentifiableEntity, Int)] = List() + private var startupUtilities : List[(PlanetSideGameObject, Int)] = List() + private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536)) - def Actor(implicit context : ActorContext) : ActorRef = { + def Actor : ActorRef = actor + + def Init(implicit context : ActorContext) : Unit = { + //TODO wrong initialization + 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") + + StartupUtilities.foreach({case ((obj, uid)) => + guid.register(obj, uid) + }) + } + + def Actor_=(zoneActor : ActorRef) : ActorRef = { if(actor == ActorRef.noSender) { - actor = context.actorOf(Props(classOf[ZoneActor], this), s"$id-actor") - - val pool = guid.AddPool("pool", (200 to 1000).toList) - val poolActor = context.actorOf(Props(classOf[NumberPoolActor], pool), name = s"$ZoneId-poolActor") - pool.Selector = new RandomSelector - accessor = context.actorOf(Props(classOf[NumberPoolAccessorActor], guid, pool, poolActor), s"$ZoneId-accessor") - - startupUtilities.foreach({case ((obj, uid)) => accessor ! Register(obj, uid, actor) }) + actor = zoneActor } - actor + Actor } private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]() @@ -45,14 +52,16 @@ class Zone(id : String, zoneNumber : Int, map : String) { def GUID : ActorRef = accessor - def GUID_=(guidSrc : NumberPoolHub) : ActorRef = { - if(accessor == ActorRef.noSender) { - guid = guidSrc + def GUID(hub : NumberPoolHub) : ActorRef = { + if(actor == ActorRef.noSender) { + guid = hub } - accessor + Actor } - def GUID(object_guid : PlanetSideGUID) : Option[PlanetSideGameObject] = guid(object_guid.guid) match { + 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 => @@ -61,10 +70,16 @@ class Zone(id : String, zoneNumber : Int, map : String) { def EquipmentOnGround : ListBuffer[Equipment] = equipmentOnGround - def AddUtility(obj : IdentifiableEntity, id : Int) : Unit = { + def AddUtility(obj : PlanetSideGameObject, id : Int) : Unit = { startupUtilities = startupUtilities :+ (obj, id) } + def StartupUtilities : List[(IdentifiableEntity, Int)] = { + val utilities = startupUtilities + startupUtilities = Nil + utilities + } + def ClientInitialization() : List[GamePacket] = { List.empty[GamePacket] } @@ -75,7 +90,6 @@ class Zone(id : String, zoneNumber : Int, map : String) { } object Zone { - final def BlankInitFunction() : Unit = { } final def Nowhere : Zone = { Zone("nowhere", 0, "nowhere") } //TODO needs overrides final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3) diff --git a/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala b/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala index 309c129d..87cce612 100644 --- a/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala +++ b/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala @@ -3,24 +3,30 @@ package net.psforever.objects.continent import akka.actor.Actor import net.psforever.objects.equipment.Equipment +import net.psforever.objects.guid.actor.Register import net.psforever.packet.game.PlanetSideGUID import scala.annotation.tailrec -class ZoneActor(continent : Zone) extends Actor { +class ZoneActor(zone : Zone) extends Actor { private[this] val log = org.log4s.getLogger - import Zone._ + + override def preStart() : Unit = { + super.preStart() + log.info(s"Calling ${zone.ZoneId} init ...") + zone.Init + } def receive : Receive = { - case DropItemOnGround(item, pos, orient) => + case Zone.DropItemOnGround(item, pos, orient) => item.Position = pos item.Orientation = orient - continent.EquipmentOnGround += item + zone.EquipmentOnGround += item - case GetItemOnGround(player, item_guid) => + case Zone.GetItemOnGround(player, item_guid) => FindItemOnGround(item_guid) match { case Some(item) => - sender ! ItemFromGround(player, 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") } @@ -29,9 +35,9 @@ class ZoneActor(continent : Zone) extends Actor { } private def FindItemOnGround(item_guid : PlanetSideGUID) : Option[Equipment] = { - recursiveFindItemOnGround(continent.EquipmentOnGround.iterator, item_guid) match { + recursiveFindItemOnGround(zone.EquipmentOnGround.iterator, item_guid) match { case Some(index) => - Some(continent.EquipmentOnGround.remove(index)) + Some(zone.EquipmentOnGround.remove(index)) case None => None } diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index fafffbbe..a14a5a3f 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.{ActorRef, ActorSystem, Props} +import akka.actor.{ActorContext, ActorRef, ActorSystem, Props} import akka.routing.RandomPool import ch.qos.logback.classic.LoggerContext import ch.qos.logback.classic.joran.JoranConfigurator @@ -15,7 +15,6 @@ import net.psforever.crypto.CryptoInterface import net.psforever.objects.continent.{IntergalacticCluster, Zone} import net.psforever.objects.guid.TaskResolver import net.psforever.objects.terminals.{OrderTerminalDefinition, Terminal} -import net.psforever.packet.game.PlanetSideGUID import org.slf4j import org.fusesource.jansi.Ansi._ import org.fusesource.jansi.Ansi.Color._ @@ -222,11 +221,22 @@ object PsLogin { } def createContinents() : List[Zone] = { - val home3 = Zone("home3",13,"map13") - val orderTerm = new OrderTerminalDefinition - home3.AddUtility(Terminal(orderTerm), 853) - home3.AddUtility(Terminal(orderTerm), 855) - home3.AddUtility(Terminal(orderTerm), 860) + val home3 = new Zone("home3",13,"map13") { + override def Init(implicit context : ActorContext) : Unit = { + val orderTerm = new OrderTerminalDefinition + val obj853 = Terminal(orderTerm) + val obj855 = Terminal(orderTerm) + val obj860 = Terminal(orderTerm) + AddUtility(obj853, 853) + AddUtility(obj855, 855) + AddUtility(obj860, 860) + + super.Init + obj853.Actor + obj855.Actor + obj860.Actor + } + } home3 :: Nil From 186c4ccba9f297d8049272a973ebd3dc282e6a55 Mon Sep 17 00:00:00 2001 From: FateJH Date: Fri, 22 Sep 2017 21:34:08 -0400 Subject: [PATCH 7/8] introduced a 'more elegant' method of adding objects to the Zones via ServerObjectBuilder; pushed information about specifics of Zone into ZoneMap object; shuffled around initialization responsibility to make this agreeable --- .../psforever/objects/GlobalDefinitions.scala | 4 ++ .../continent/IntergalacticCluster.scala | 2 +- .../continent/ServerObjectBuilder.scala | 10 +++ .../continent/TerminalObjectBuilder.scala | 22 ++++++ .../psforever/objects/continent/Zone.scala | 38 +++++----- .../objects/continent/ZoneActor.scala | 2 - .../psforever/objects/continent/ZoneMap.scala | 18 +++++ pslogin/src/main/scala/PsLogin.scala | 24 ++----- .../src/main/scala/WorldSessionActor.scala | 70 ++----------------- 9 files changed, 84 insertions(+), 106 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/continent/ServerObjectBuilder.scala create mode 100644 common/src/main/scala/net/psforever/objects/continent/TerminalObjectBuilder.scala create mode 100644 common/src/main/scala/net/psforever/objects/continent/ZoneMap.scala diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index b8666006..e6dcec51 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -6,6 +6,7 @@ import net.psforever.objects.definition.converter.{CommandDetonaterConverter, Lo import net.psforever.objects.equipment.CItem.DeployedItem import net.psforever.objects.equipment._ import net.psforever.objects.inventory.InventoryTile +import net.psforever.objects.terminals.OrderTerminalDefinition import net.psforever.packet.game.objectcreate.ObjectClass import net.psforever.types.PlanetSideEmpire @@ -1169,4 +1170,7 @@ object GlobalDefinitions { fury.Weapons += 1 -> fury_weapon_systema fury.TrunkSize = InventoryTile(11, 11) fury.TrunkOffset = 30 + + val + orderTerminal = new OrderTerminalDefinition } diff --git a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala index d58a5a08..3bbd2619 100644 --- a/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala +++ b/common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala @@ -25,7 +25,7 @@ class IntergalacticCluster(zones : List[Zone]) extends Actor { case Some(continent) => sender ! IntergalacticCluster.GiveWorld(zoneId, continent) case None => - sender ! IntergalacticCluster.GiveWorld(zoneId, Zone.Nowhere) + log.error(s"Requested zone with id $zoneId could not be found") } case IntergalacticCluster.RequestZoneInitialization(tplayer) => diff --git a/common/src/main/scala/net/psforever/objects/continent/ServerObjectBuilder.scala b/common/src/main/scala/net/psforever/objects/continent/ServerObjectBuilder.scala new file mode 100644 index 00000000..b5ca451e --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/continent/ServerObjectBuilder.scala @@ -0,0 +1,10 @@ +// 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 new file mode 100644 index 00000000..ae59d2c8 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/continent/TerminalObjectBuilder.scala @@ -0,0 +1,22 @@ +// 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 index 533db3af..33ba1623 100644 --- a/common/src/main/scala/net/psforever/objects/continent/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/continent/Zone.scala @@ -3,10 +3,9 @@ package net.psforever.objects.continent import akka.actor.{ActorContext, ActorRef, Props} import net.psforever.objects.{PlanetSideGameObject, Player} -import net.psforever.objects.entity.IdentifiableEntity import net.psforever.objects.equipment.Equipment import net.psforever.objects.guid.NumberPoolHub -import net.psforever.objects.guid.actor.{NumberPoolAccessorActor, NumberPoolActor, Register} +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 @@ -15,23 +14,24 @@ import net.psforever.types.Vector3 import scala.collection.mutable.ListBuffer -class Zone(id : String, zoneNumber : Int, map : String) { +class Zone(id : String, map : ZoneMap, zoneNumber : Int) { private var actor = ActorRef.noSender private var accessor : ActorRef = ActorRef.noSender - private var startupUtilities : List[(PlanetSideGameObject, Int)] = List() + //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") - StartupUtilities.foreach({case ((obj, uid)) => - guid.register(obj, uid) + map.LocalObjects.foreach({builderObject => + builderObject.Build }) } @@ -48,7 +48,7 @@ class Zone(id : String, zoneNumber : Int, map : String) { def ZoneNumber : Int = zoneNumber - def Map : String = map + def Map : ZoneMap = map def GUID : ActorRef = accessor @@ -70,15 +70,15 @@ class Zone(id : String, zoneNumber : Int, map : String) { def EquipmentOnGround : ListBuffer[Equipment] = equipmentOnGround - def AddUtility(obj : PlanetSideGameObject, id : Int) : Unit = { - startupUtilities = startupUtilities :+ (obj, id) - } - - def StartupUtilities : List[(IdentifiableEntity, Int)] = { - val utilities = startupUtilities - startupUtilities = Nil - utilities - } +// 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] @@ -90,8 +90,6 @@ class Zone(id : String, zoneNumber : Int, map : String) { } object Zone { - final def Nowhere : Zone = { Zone("nowhere", 0, "nowhere") } //TODO needs overrides - final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3) final case class GetItemOnGround(player : Player, item_guid : PlanetSideGUID) @@ -100,7 +98,7 @@ object Zone { final case class ClientInitialization(list : List[GamePacket]) - def apply(zoneId : String, zoneNumber : Int, map : String) : Zone = { - new Zone(zoneId, zoneNumber, map) + 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/ZoneActor.scala b/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala index 87cce612..9d45a363 100644 --- a/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala +++ b/common/src/main/scala/net/psforever/objects/continent/ZoneActor.scala @@ -3,7 +3,6 @@ package net.psforever.objects.continent import akka.actor.Actor import net.psforever.objects.equipment.Equipment -import net.psforever.objects.guid.actor.Register import net.psforever.packet.game.PlanetSideGUID import scala.annotation.tailrec @@ -13,7 +12,6 @@ class ZoneActor(zone : Zone) extends Actor { override def preStart() : Unit = { super.preStart() - log.info(s"Calling ${zone.ZoneId} init ...") zone.Init } diff --git a/common/src/main/scala/net/psforever/objects/continent/ZoneMap.scala b/common/src/main/scala/net/psforever/objects/continent/ZoneMap.scala new file mode 100644 index 00000000..ee0edbff --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/continent/ZoneMap.scala @@ -0,0 +1,18 @@ +// 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/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index a14a5a3f..9f1ee252 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -12,9 +12,8 @@ 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, Zone} +import net.psforever.objects.continent.{IntergalacticCluster, TerminalObjectBuilder, Zone, ZoneMap} import net.psforever.objects.guid.TaskResolver -import net.psforever.objects.terminals.{OrderTerminalDefinition, Terminal} import org.slf4j import org.fusesource.jansi.Ansi._ import org.fusesource.jansi.Ansi.Color._ @@ -221,22 +220,13 @@ object PsLogin { } def createContinents() : List[Zone] = { - val home3 = new Zone("home3",13,"map13") { - override def Init(implicit context : ActorContext) : Unit = { - val orderTerm = new OrderTerminalDefinition - val obj853 = Terminal(orderTerm) - val obj855 = Terminal(orderTerm) - val obj860 = Terminal(orderTerm) - AddUtility(obj853, 853) - AddUtility(obj855, 855) - AddUtility(obj860, 860) - - super.Init - obj853.Actor - obj855.Actor - obj860.Actor - } + val map13 = new ZoneMap("map13") { + import net.psforever.objects.GlobalDefinitions._ + LocalObject(TerminalObjectBuilder(orderTerminal, 853)) + LocalObject(TerminalObjectBuilder(orderTerminal, 855)) + LocalObject(TerminalObjectBuilder(orderTerminal, 860)) } + val home3 = Zone("home3", map13, 13) home3 :: Nil diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 0f83b0a3..070d27c3 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -34,7 +34,7 @@ class WorldSessionActor extends Actor with MDCContextAware { var avatarService = Actor.noSender var taskResolver = Actor.noSender var galaxy = Actor.noSender - var continent : Zone = Zone.Nowhere + var continent : Zone = null var clientKeepAlive : Cancellable = WorldSessionActor.DefaultCancellable @@ -358,7 +358,6 @@ class WorldSessionActor extends Actor with MDCContextAware { avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.PlanetsideAttribute(tplayer.GUID, 4, tplayer.Armor)) //re-draw equipment held in free hand beforeFreeHand match { - //TODO was any previous free hand item deleted? case Some(item) => tplayer.FreeHand.Equipment = beforeFreeHand val definition = item.Definition @@ -424,15 +423,8 @@ class WorldSessionActor extends Actor with MDCContextAware { case PlayerFailedToLoad(tplayer) => player.Continent match { - case "tzshvs" => - failWithError(s"$tplayer failed to load anywhere") - //self ! IntergalacticCluster.GiveWorld("", Zone.Nowhere) - case "tzdrvs" => - galaxy ! IntergalacticCluster.GetWorld("tzshvs") - case "home3" => - galaxy ! IntergalacticCluster.GetWorld("tzdrvs") case _ => - galaxy ! IntergalacticCluster.GetWorld("home3") + failWithError(s"$tplayer failed to load anywhere") } case Zone.ClientInitialization(/*initList*/_) => @@ -470,7 +462,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case IntergalacticCluster.ClientInitializationComplete(tplayer)=> //this will cause the client to send back a BeginZoningMessage packet (see below) - sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage(continent.Map, continent.ZoneId, 40100,25,true,3770441820L))) //VS Sanctuary + sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage(continent.Map.Name, continent.ZoneId, 40100,25,true,3770441820L))) //VS Sanctuary log.info("Load the now-registered player") //load the now-registered player tplayer.Spawn @@ -585,7 +577,7 @@ class WorldSessionActor extends Actor with MDCContextAware { } import net.psforever.objects.GlobalDefinitions._ - //this part is created by the player (should be in case of ConnectToWorldRequestMessage, maybe) + //this part is created by WSA based on the database query (should be in case of ConnectToWorldRequestMessage, maybe) val energy_cell_box1 = AmmoBox(energy_cell) val energy_cell_box2 = AmmoBox(energy_cell, 16) val bullet_9mm_box1 = AmmoBox(bullet_9mm) @@ -609,7 +601,6 @@ class WorldSessionActor extends Actor with MDCContextAware { player = Player("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1) player.Position = Vector3(3674.8438f, 2726.789f, 91.15625f) player.Orientation = Vector3(0f, 0f, 90f) - //player.Continent = "home3" player.Slot(0).Equipment = beamer1 player.Slot(2).Equipment = suppressor1 player.Slot(4).Equipment = forceblade1 @@ -621,57 +612,6 @@ class WorldSessionActor extends Actor with MDCContextAware { player.Slot(39).Equipment = rek player.Slot(5).Equipment.get.asInstanceOf[LockerContainer].Inventory += 0 -> extra_rek - //for player2 - val energy_cell_box3 = AmmoBox(PlanetSideGUID(187), energy_cell) - val energy_cell_box4 = AmmoBox(PlanetSideGUID(177), energy_cell, 16) - val bullet_9mm_box5 = AmmoBox(PlanetSideGUID(183), bullet_9mm) - val bullet_9mm_box6 = AmmoBox(PlanetSideGUID(184), bullet_9mm) - val bullet_9mm_box7 = AmmoBox(PlanetSideGUID(185), bullet_9mm) - val bullet_9mm_box8 = AmmoBox(PlanetSideGUID(179), bullet_9mm, 25) - val bullet_9mm_AP_box2 = AmmoBox(PlanetSideGUID(186), bullet_9mm_AP) - val melee_ammo_box2 = AmmoBox(PlanetSideGUID(181), melee_ammo) - - val - beamer2 = Tool(PlanetSideGUID(176), beamer) - beamer2.AmmoSlots.head.Box = energy_cell_box4 - val - suppressor2 = Tool(PlanetSideGUID(178), suppressor) - suppressor2.AmmoSlots.head.Box = bullet_9mm_box8 - val - forceblade2 = Tool(PlanetSideGUID(180), forceblade) - forceblade2.AmmoSlots.head.Box = melee_ammo_box2 - val - rek2 = SimpleItem(PlanetSideGUID(188), remote_electronics_kit) - val - player2 = Player(PlanetSideGUID(275), "Doppelganger", PlanetSideEmpire.NC, CharacterGender.Female, 41, 1) - player2.Position = Vector3(3680f, 2726.789f, 91.15625f) - player2.Orientation = Vector3(0f, 0f, 0f) - player2.Continent = "home3" - player2.Slot(0).Equipment = beamer2 - player2.Slot(2).Equipment = suppressor2 - player2.Slot(4).Equipment = forceblade2 - player2.Slot(5).Equipment.get.GUID = PlanetSideGUID(182) - player2.Slot(6).Equipment = bullet_9mm_box5 - player2.Slot(9).Equipment = bullet_9mm_box6 - player2.Slot(12).Equipment = bullet_9mm_box7 - player2.Slot(33).Equipment = bullet_9mm_AP_box2 - player2.Slot(36).Equipment = energy_cell_box3 - player2.Slot(39).Equipment = rek2 - player2.Spawn - - val hellfire_ammo_box = AmmoBox(PlanetSideGUID(432), hellfire_ammo) - - val - fury1 = Vehicle(PlanetSideGUID(313), fury) - fury1.Faction = PlanetSideEmpire.VS - fury1.Position = Vector3(3674.8438f, 2732f, 91.15625f) - fury1.Orientation = Vector3(0.0f, 0.0f, 90.0f) - fury1.WeaponControlledFromSeat(0).get.GUID = PlanetSideGUID(300) - fury1.WeaponControlledFromSeat(0).get.AmmoSlots.head.Box = hellfire_ammo_box - - val object2Hex = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(275), player2.Definition.Packet.ConstructorData(player2).get) - val furyHex = ObjectCreateMessage(ObjectClass.fury, PlanetSideGUID(313), fury1.Definition.Packet.ConstructorData(fury1).get) - def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match { case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) => val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate" @@ -712,8 +652,6 @@ class WorldSessionActor extends Actor with MDCContextAware { //TODO continent.ClientConfiguration() sendResponse(PacketCoding.CreateGamePacket(0, SetEmpireMessage(PlanetSideGUID(2), PlanetSideEmpire.VS))) //HART building C sendResponse(PacketCoding.CreateGamePacket(0, SetEmpireMessage(PlanetSideGUID(29), PlanetSideEmpire.NC))) //South Villa Gun Tower - //sendResponse(PacketCoding.CreateGamePacket(0, object2Hex)) - //sendResponse(PacketCoding.CreateGamePacket(0, furyHex)) sendResponse(PacketCoding.CreateGamePacket(0, TimeOfDayMessage(1191182336))) sendResponse(PacketCoding.CreateGamePacket(0, ReplicationStreamMessage(5, Some(6), Vector(SquadListing())))) //clear squad list From 61535bc232e6c6ff3dde85787a75d92a2c17acc1 Mon Sep 17 00:00:00 2001 From: FateJH Date: Sat, 23 Sep 2017 20:34:41 -0400 Subject: [PATCH 8/8] moved files from /continent/ to /zones/; separated Actor for managing items on the ground; added comments --- .../continent/IntergalacticCluster.scala | 64 ----- .../continent/ServerObjectBuilder.scala | 10 - .../continent/TerminalObjectBuilder.scala | 22 -- .../psforever/objects/continent/Zone.scala | 104 ------- .../psforever/objects/continent/ZoneMap.scala | 18 -- .../objects/zones/InterstellarCluster.scala | 113 ++++++++ .../objects/zones/ServerObjectBuilder.scala | 27 ++ .../objects/zones/TerminalObjectBuilder.scala | 33 +++ .../net/psforever/objects/zones/Zone.scala | 259 ++++++++++++++++++ .../psforever/objects/zones/ZoneActor.scala | 20 ++ .../ZoneGroundActor.scala} | 30 +- .../net/psforever/objects/zones/ZoneMap.scala | 43 +++ pslogin/src/main/scala/PsLogin.scala | 6 +- .../src/main/scala/WorldSessionActor.scala | 20 +- 14 files changed, 526 insertions(+), 243 deletions(-) delete mode 100644 common/src/main/scala/net/psforever/objects/continent/IntergalacticCluster.scala delete mode 100644 common/src/main/scala/net/psforever/objects/continent/ServerObjectBuilder.scala delete mode 100644 common/src/main/scala/net/psforever/objects/continent/TerminalObjectBuilder.scala delete mode 100644 common/src/main/scala/net/psforever/objects/continent/Zone.scala delete mode 100644 common/src/main/scala/net/psforever/objects/continent/ZoneMap.scala create mode 100644 common/src/main/scala/net/psforever/objects/zones/InterstellarCluster.scala create mode 100644 common/src/main/scala/net/psforever/objects/zones/ServerObjectBuilder.scala create mode 100644 common/src/main/scala/net/psforever/objects/zones/TerminalObjectBuilder.scala create mode 100644 common/src/main/scala/net/psforever/objects/zones/Zone.scala create mode 100644 common/src/main/scala/net/psforever/objects/zones/ZoneActor.scala rename common/src/main/scala/net/psforever/objects/{continent/ZoneActor.scala => zones/ZoneGroundActor.scala} (62%) create mode 100644 common/src/main/scala/net/psforever/objects/zones/ZoneMap.scala 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 3bbd2619..00000000 --- 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 b5ca451e..00000000 --- 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 ae59d2c8..00000000 --- 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 33ba1623..00000000 --- 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 ee0edbff..00000000 --- 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 00000000..e09d55c2 --- /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 00000000..2635df09 --- /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 00000000..2ced4d84 --- /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 00000000..21497a74 --- /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 00000000..811b6ef9 --- /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 9d45a363..5a001e47 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 00000000..b1cf2e9a --- /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 9f1ee252..c1403fc4 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 070d27c3..f11ab343 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)