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

This commit is contained in:
FateJH 2017-09-15 22:26:36 -04:00
parent a4b14e5da4
commit 99b019714b
5 changed files with 215 additions and 40 deletions

View file

@ -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)
}
}

View file

@ -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)
}
}
}
}

View file

@ -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)
}

View file

@ -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

View file

@ -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.