mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-30 07:11:00 +00:00
commit
be0902fbc4
|
|
@ -6,6 +6,8 @@ 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
|
||||
|
||||
object GlobalDefinitions {
|
||||
|
|
@ -1235,4 +1237,7 @@ object GlobalDefinitions {
|
|||
fury.Weapons += 1 -> fury_weapon_systema
|
||||
fury.TrunkSize = InventoryTile(11, 11)
|
||||
fury.TrunkOffset = 30
|
||||
|
||||
val
|
||||
orderTerminal = new OrderTerminalDefinition
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.<br>
|
||||
* 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.<br>
|
||||
* <br>
|
||||
* Use:<br>
|
||||
* 1) When a users logs in during `WorldSessionActor`, associate that user's session id and the character.<br>
|
||||
* `LivePlayerList.Add(session, player)`<br>
|
||||
* 2) When that user's chosen character is declared his avatar using `SetCurrentAvatarMessage`,
|
||||
* also associate the user's session with their current GUID.<br>
|
||||
* `LivePlayerList.Assign(session, guid)`<br>
|
||||
* `LivePlayerList.Assign(zone, session, guid)`<br>
|
||||
* 3) Repeat the previous step for as many times the user's GUID changes, especially during the aforementioned condition.<br>
|
||||
* 4a) In between the previous two steps, a user's character may be referenced by their current GUID.<br>
|
||||
* `LivePlayerList.Get(guid)`<br>
|
||||
* `LivePlayerList.Get(zone, guid)`<br>
|
||||
* 4b) Also in between those same previous steps, a range of characters may be queried based on provided statistics.<br>
|
||||
* `LivePlayerList.WorldPopulation(...)`<br>
|
||||
* 5) When the user leaves the game, his character's entries are removed from the mappings.<br>
|
||||
* `LivePlayerList.ZonePopulation(zone, ...)`<br>
|
||||
* 5) When the user navigates away from a region completely, their entry is forgotten.<br>
|
||||
* `LivePlayerList.Drop(zone, guid)`<br>
|
||||
* 6) When the user leaves the game entirely, his character's entries are removed from the mappings.<br>
|
||||
* `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.<br>
|
||||
* <br>
|
||||
* 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.<br>
|
||||
* <br>
|
||||
* 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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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."<br>
|
||||
* <br>
|
||||
* 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)
|
||||
}
|
||||
|
|
@ -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).<br>
|
||||
* <br>
|
||||
* 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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
259
common/src/main/scala/net/psforever/objects/zones/Zone.scala
Normal file
259
common/src/main/scala/net/psforever/objects/zones/Zone.scala
Normal file
|
|
@ -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.<br>
|
||||
* <br>
|
||||
* 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`<br>
|
||||
* `LoadMapMessage`<br>
|
||||
* `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`.<br>
|
||||
* <br>
|
||||
* 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`<br>
|
||||
* `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`<br>
|
||||
* `Zone.DropItemOnGround`<br>
|
||||
* `Zone.GetItemOnGround`<br>
|
||||
* `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:<br>
|
||||
* - `BroadcastWarpgateUpdateMessage`<br>
|
||||
* - `BuildingInfoUpdateMessage`<br>
|
||||
* - `CaptureFlagUpdateMessage`<br>
|
||||
* - `ContinentalLockUpdateMessage`<br>
|
||||
* - `DensityLevelUpdateMessage`<br>
|
||||
* - `ModuleLimitsMessage`<br>
|
||||
* - `VanuModuleUpdateMessage`<br>
|
||||
* - `ZoneForcedCavernConnectionMessage`<br>
|
||||
* - `ZoneInfoMessage`<br>
|
||||
* - `ZoneLockInfoMessage`<br>
|
||||
* - `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`:<br>
|
||||
* - `HackMessage`<br>
|
||||
* - `PlanetsideAttributeMessage`<br>
|
||||
* - `SetEmpireMessage`<br>
|
||||
* - `TimeOfDayMessage`<br>
|
||||
* - `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()`<br>
|
||||
* `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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
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
|
||||
|
||||
/**
|
||||
* 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
|
||||
equipmentOnGround += item
|
||||
|
||||
case Zone.GetItemOnGround(player, item_guid) =>
|
||||
FindItemOnGround(item_guid) match {
|
||||
case Some(item) =>
|
||||
sender ! Zone.ItemFromGround(player, item)
|
||||
case None =>
|
||||
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(equipmentOnGround.iterator, item_guid) match {
|
||||
case Some(index) =>
|
||||
Some(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
|
||||
/**
|
||||
* The fixed instantiation and relation of a series of server objects.<br>
|
||||
* <br>
|
||||
* 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.<br>
|
||||
* <br>
|
||||
* 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`<br>
|
||||
* `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
|
||||
}
|
||||
}
|
||||
|
|
@ -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.zones.{InterstellarCluster, TerminalObjectBuilder, Zone, ZoneMap}
|
||||
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[InterstellarCluster], createContinents()), "galaxy")
|
||||
|
||||
/** Create two actors for handling the login and world server endpoints */
|
||||
loginRouter = Props(new SessionRouter("Login", loginTemplate))
|
||||
|
|
@ -235,6 +219,19 @@ object PsLogin {
|
|||
}
|
||||
}
|
||||
|
||||
def createContinents() : List[Zone] = {
|
||||
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
|
||||
}
|
||||
|
||||
def main(args : Array[String]) : Unit = {
|
||||
Locale.setDefault(Locale.US); // to have floats with dots, not comma...
|
||||
this.args = args
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@ import org.log4s.MDC
|
|||
import MDCContextAware.Implicits._
|
||||
import ServiceManager.Lookup
|
||||
import net.psforever.objects._
|
||||
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}
|
||||
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._
|
||||
|
||||
|
|
@ -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 : Zone = null
|
||||
|
||||
var clientKeepAlive : Cancellable = WorldSessionActor.DefaultCancellable
|
||||
|
||||
|
|
@ -49,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 => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -71,8 +69,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 +81,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)
|
||||
|
|
@ -360,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
|
||||
|
|
@ -413,10 +410,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0)))
|
||||
|
||||
case InterstellarCluster.GiveWorld(zoneId, zone) =>
|
||||
log.info(s"Zone $zoneId has been loaded")
|
||||
player.Continent = zoneId
|
||||
continent = zone
|
||||
taskResolver ! RegisterAvatar(player)
|
||||
|
||||
case PlayerLoaded(tplayer) =>
|
||||
log.info(s"Player $tplayer has been loaded")
|
||||
//init for whole server
|
||||
//...
|
||||
galaxy ! InterstellarCluster.RequestClientInitialization(tplayer)
|
||||
|
||||
case PlayerFailedToLoad(tplayer) =>
|
||||
player.Continent match {
|
||||
case _ =>
|
||||
failWithError(s"$tplayer failed to load anywhere")
|
||||
}
|
||||
|
||||
case Zone.ClientInitialization(/*initList*/_) =>
|
||||
//TODO iterate over initList; for now, just do this
|
||||
sendResponse(
|
||||
PacketCoding.CreateGamePacket(0,
|
||||
BuildingInfoUpdateMessage(
|
||||
|
|
@ -446,8 +458,12 @@ 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 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.Id, 40100,25,true,3770441820L))) //VS Sanctuary
|
||||
log.info("Load the now-registered player")
|
||||
//load the now-registered player
|
||||
tplayer.Spawn
|
||||
sendResponse(PacketCoding.CreateGamePacket(0,
|
||||
|
|
@ -457,38 +473,42 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
log.debug(s"ObjectCreateDetailedMessage: ${tplayer.Definition.Packet.DetailedConstructorData(tplayer).get}")
|
||||
|
||||
case SetCurrentAvatar(tplayer) =>
|
||||
//avatar-specific
|
||||
val guid = tplayer.GUID
|
||||
LivePlayerList.Assign(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)))
|
||||
|
||||
//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 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
|
||||
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 ! Zone.DropItemOnGround(item, item.Position, item.Orientation) //restore
|
||||
}
|
||||
|
||||
case WorldSessionActor.ResponseToSelf(pkt) =>
|
||||
case ResponseToSelf(pkt) =>
|
||||
log.info(s"Received a direct message: $pkt")
|
||||
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 +516,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 +524,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) = {
|
||||
|
|
@ -556,10 +576,8 @@ 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)
|
||||
//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)
|
||||
|
|
@ -583,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.Certifications += CertificationType.StandardAssault
|
||||
player.Certifications += CertificationType.MediumAssault
|
||||
player.Certifications += CertificationType.StandardExoSuit
|
||||
|
|
@ -602,57 +619,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"
|
||||
|
|
@ -671,13 +637,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
|
||||
taskResolver ! RegisterAvatar(player)
|
||||
//TODO check if can spawn on last continent/location from player?
|
||||
//TODO if yes, get continent guid accessors
|
||||
//TODO if no, get sanctuary guid accessors and reset the player's expectations
|
||||
galaxy ! InterstellarCluster.GetWorld("home3")
|
||||
|
||||
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)
|
||||
|
|
@ -688,28 +655,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ BeginZoningMessage() =>
|
||||
log.info("Reticulating splines ...")
|
||||
//map-specific initializations (VS sanctuary)
|
||||
//map-specific initializations
|
||||
//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, 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 continent = player.Continent
|
||||
val player_guid = player.GUID
|
||||
LivePlayerList.WorldPopulation({ case (_, char : Player) => char.Continent == continent && char.HasGUID && char.GUID != player_guid}).foreach(char => {
|
||||
//load active players in zone
|
||||
LivePlayerList.ZonePopulation(continent.Number, _ => true).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 zone before the player arrived
|
||||
continent.EquipmentOnGround.toList.foreach(item => {
|
||||
val definition = item.Definition
|
||||
sendResponse(
|
||||
PacketCoding.CreateGamePacket(0,
|
||||
|
|
@ -722,7 +685,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
})
|
||||
|
||||
avatarService ! Join("home3")
|
||||
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) =>
|
||||
|
|
@ -794,10 +757,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.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, 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")
|
||||
|
|
@ -808,7 +772,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.Ground ! Zone.GetItemOnGround(player, item_guid)
|
||||
|
||||
case msg @ ReloadMessage(item_guid, ammo_clip, unk1) =>
|
||||
log.info("Reload: " + msg)
|
||||
|
|
@ -923,9 +887,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 ! 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))
|
||||
}
|
||||
|
||||
|
|
@ -971,9 +936,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) {
|
||||
|
|
@ -1191,7 +1161,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 {
|
||||
|
|
@ -1267,7 +1237,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,
|
||||
|
|
@ -1305,13 +1275,22 @@ 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 = {
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
|
@ -1326,7 +1305,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 {
|
||||
|
|
@ -1403,7 +1382,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))
|
||||
}
|
||||
|
|
@ -1441,17 +1420,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)
|
||||
}
|
||||
|
||||
|
|
@ -1483,17 +1451,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()
|
||||
}
|
||||
|
||||
|
|
@ -1514,64 +1471,9 @@ 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()))
|
||||
sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))
|
||||
}
|
||||
|
||||
def sendResponse(cont : PlanetSidePacketContainer) : Unit = {
|
||||
|
|
@ -1593,6 +1495,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
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)
|
||||
private final case class PlayerFailedToLoad(tplayer : Player)
|
||||
private final case class ListAccountCharacters()
|
||||
private final case class SetCurrentAvatar(tplayer : Player)
|
||||
|
||||
/**
|
||||
* A placeholder `Cancellable` object.
|
||||
*/
|
||||
|
|
@ -1601,12 +1510,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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue