mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-09 07:00:27 +00:00
renamed Continent* to Zone*; fleshed out example loading fucntionality
This commit is contained in:
parent
ce8d61a4d3
commit
402d4c5b3e
6 changed files with 238 additions and 104 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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) =>
|
||||
Loading…
Add table
Add a link
Reference in a new issue