Merge pull request #196 from Fate-JH/spawn-tube

The Real Resurrection Update
This commit is contained in:
Fate-JH 2018-04-19 07:45:47 -04:00 committed by GitHub
commit d8fe6bab28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 4562 additions and 1410 deletions

View file

@ -0,0 +1,206 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition}
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.types.{CertificationType, CharacterGender, ImplantType, PlanetSideEmpire}
import scala.annotation.tailrec
import scala.collection.mutable
class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : CharacterGender.Value, val head : Int, val voice : Int) {
/** Battle Experience Points */
private var bep : Long = 0
/** Command Experience Points */
private var cep : Long = 0
/** Certifications */
private val certs : mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]()
/** Implants<br>
* Unlike other objects, the maximum number of `ImplantSlots` are built into the `Avatar`.
* Additionally, implants do not have tightly-coupled "`Definition` objects" that explain a formal implant object.
* The `ImplantDefinition` objects themselves are moved around as if they were the implants.
* The terms externally used for the states of process is "installed" and "uninstalled."
* @see `ImplantSlot`
* @see `DetailedCharacterData.implants`
*/
private val implants : Array[ImplantSlot] = Array.fill[ImplantSlot](3)(new ImplantSlot)
/** Loadouts */
private val loadouts : Array[Option[Loadout]] = Array.fill[Option[Loadout]](10)(None)
/** Locker (fifth inventory slot) */
private val locker : LockerContainer = LockerContainer()
def BEP : Long = bep
def BEP_=(battleExperiencePoints : Long) : Long = {
bep = math.max(0L, math.min(battleExperiencePoints, 4294967295L))
BEP
}
def Certifications : mutable.Set[CertificationType.Value] = certs
def CEP : Long = cep
def CEP_=(commandExperiencePoints : Long) : Long = {
cep = math.max(0L, math.min(commandExperiencePoints, 4294967295L))
CEP
}
/**
* Retrieve the three implant slots for this player.
* @return an `Array` of `ImplantSlot` objects
*/
def Implants : Array[ImplantSlot] = implants
/**
* What kind of implant is installed into the given slot number?
* @see `ImplantType`
* @param slot the slot number
* @return the tye of implant
*/
def Implant(slot : Int) : ImplantType.Value = {
if(-1 < slot && slot < implants.length) { implants(slot).Implant } else { ImplantType.None }
}
/**
* Given a new implant, assign it into a vacant implant slot on this player.<br>
* <br>
* The implant must be unique in terms of which implants have already been assigned to this player.
* Multiple of a type of implant being assigned at once is not supported.
* Additionally, the implant is inserted into the earliest yet-unknown but vacant slot.
* Implant slots are vacant by just being unlocked or by having their previous implant uninstalled.
* @param implant the implant being installed
* @return the index of the `ImplantSlot` where the implant was installed
*/
def InstallImplant(implant : ImplantDefinition) : Option[Int] = {
implants.find({p => p.Installed.contains(implant) || p.Implant == implant.Type}) match { //try to find the installed implant
case None =>
recursiveFindImplantInSlot(implants.iterator, ImplantType.None) match { //install in a free slot
case Some(slot) =>
implants(slot).Implant = implant
Some(slot)
case None =>
None
}
case Some(_) =>
None
}
}
/**
* Remove a specific implant from a player's allocated installed implants.<br>
* <br>
* Due to the exclusiveness of installed implants,
* any implant slot with a matching `Definition` can be uninstalled safely.
* (There will never be any doubles.)
* This operation can lead to an irregular pattern of installed and uninstalled `ImplantSlot` objects.
* Despite that breach of pattern, the logic here is consistent as demonstrated by the client and by packets.
* The client also assigns and removes implants based on slot numbers that only express availability of a "slot."
* @see `AvatarImplantMessage.implantSlot`
* @param implantType the type of implant being uninstalled
* @return the index of the `ImplantSlot` where the implant was found and uninstalled
*/
def UninstallImplant(implantType : ImplantType.Value) : Option[Int] = {
recursiveFindImplantInSlot(implants.iterator, implantType) match {
case Some(slot) =>
implants(slot).Implant = None
Some(slot)
case None =>
None
}
}
/**
* Locate the index of the encountered implant type.
* Functional implants may be exclusive in as far as the input `Iterator`'s source is concerned,
* but any number of `ImplantType.None` values are alway allowed in the source in any order.
* @param iter an `Iterator` of `ImplantSlot` objects
* @param implantType the target implant being sought
* @param index a defaulted index value representing the structure underlying the `Iterator` param
* @return the index where the target implant is installed
*/
@tailrec private def recursiveFindImplantInSlot(iter : Iterator[ImplantSlot], implantType : ImplantType.Value, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val slot = iter.next
if(slot.Unlocked && slot.Implant == implantType) {
Some(index)
}
else {
recursiveFindImplantInSlot(iter, implantType, index + 1)
}
}
}
def ResetAllImplants() : Unit = {
implants.foreach(slot => {
slot.Installed match {
case Some(_) =>
slot.Initialized = false
case None => ;
}
})
}
def SaveLoadout(owner : Player, label : String, line : Int) : Unit = {
if(line > -1 && line < 10) {
loadouts(line) = Some(Loadout.Create(owner, label))
}
}
def LoadLoadout(line : Int) : Option[Loadout] = loadouts.lift(line).getOrElse(None)
def DeleteLoadout(line : Int) : Unit = {
loadouts(line) = None
}
def Locker : LockerContainer = locker
def FifthSlot : EquipmentSlot = {
new OffhandEquipmentSlot(EquipmentSize.Inventory) {
Equipment = locker
}
}
def Definition : AvatarDefinition = Avatar.definition
/*
Merit Commendations and Ribbons
*/
// private var tosRibbon : MeritCommendation.Value = MeritCommendation.None
// private var upperRibbon : MeritCommendation.Value = MeritCommendation.None
// private var middleRibbon : MeritCommendation.Value = MeritCommendation.None
// private var lowerRibbon : MeritCommendation.Value = MeritCommendation.None
def canEqual(other: Any): Boolean = other.isInstanceOf[Avatar]
override def equals(other : Any) : Boolean = other match {
case that: Avatar =>
(that canEqual this) &&
name == that.name &&
faction == that.faction &&
sex == that.sex &&
head == that.head &&
voice == that.voice
case _ =>
false
}
override def hashCode() : Int = {
val state = Seq(name, faction, sex, head, voice)
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
}
override def toString: String = Avatar.toString(this)
}
object Avatar {
final private val definition : AvatarDefinition = new AvatarDefinition(121)
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Avatar = {
new Avatar(name, faction, sex, head, voice)
}
def toString(avatar : Avatar) : String = s"${avatar.faction} ${avatar.name}"
}

View file

@ -487,7 +487,7 @@ object GlobalDefinitions {
*/ */
val order_terminal = new OrderTerminalDefinition val order_terminal = new OrderTerminalDefinition
val ams_respawn_tube = new SpawnTubeDefinition(49) { Name = "ams_respawn_tube" } val ams_respawn_tube = new SpawnTubeDefinition(49)
val matrix_terminalc = new MatrixTerminalDefinition(519) val matrix_terminalc = new MatrixTerminalDefinition(519)
@ -509,6 +509,12 @@ object GlobalDefinitions {
val vehicle_terminal_combined = new VehicleTerminalCombinedDefinition val vehicle_terminal_combined = new VehicleTerminalCombinedDefinition
val spawn_terminal = new MatrixTerminalDefinition(812)
val respawn_tube = new SpawnTubeDefinition(732)
val respawn_tube_tower = new SpawnTubeDefinition(733)
val spawn_pad = new VehicleSpawnPadDefinition val spawn_pad = new VehicleSpawnPadDefinition
val mb_locker = new LockerDefinition val mb_locker = new LockerDefinition
@ -643,7 +649,7 @@ object GlobalDefinitions {
* @param faction the faction * @param faction the faction
* @return the `ToolDefinition` for the launcher * @return the `ToolDefinition` for the launcher
*/ */
def AntiVehicular(faction : PlanetSideEmpire.Value) : ToolDefinition = { def AntiVehicularLauncher(faction : PlanetSideEmpire.Value) : ToolDefinition = {
faction match { faction match {
case PlanetSideEmpire.TR => striker case PlanetSideEmpire.TR => striker
case PlanetSideEmpire.NC => hunterseeker case PlanetSideEmpire.NC => hunterseeker
@ -683,6 +689,17 @@ object GlobalDefinitions {
} }
} }
def isMaxArms(tdef : ToolDefinition) : Boolean = {
tdef match {
case `trhev_dualcycler` | `nchev_scattercannon` | `vshev_quasar`
| `trhev_pounder` | `nchev_falcon` | `vshev_comet`
| `trhev_burster` | `nchev_sparrow` | `vshev_starfire` =>
true
case _ =>
false
}
}
def AIMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = { def AIMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
faction match { faction match {
case PlanetSideEmpire.TR => trhev_dualcycler case PlanetSideEmpire.TR => trhev_dualcycler

View file

@ -1,9 +1,6 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects package net.psforever.objects
import net.psforever.packet.game.PlanetSideGUID
import scala.annotation.tailrec
import scala.collection.concurrent.{Map, TrieMap} import scala.collection.concurrent.{Map, TrieMap}
/** /**
@ -12,157 +9,42 @@ import scala.collection.concurrent.{Map, TrieMap}
*/ */
private class LivePlayerList { private class LivePlayerList {
/** key - the session id; value - a `Player` object */ /** key - the session id; value - a `Player` object */
private val sessionMap : Map[Long, Player] = new TrieMap[Long, Player] private val sessionMap : Map[Long, Avatar] = new TrieMap[Long, Avatar]
/** 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] = { def WorldPopulation(predicate : ((_, Avatar)) => Boolean) : List[Avatar] = {
sessionMap.filter(predicate).values.toList sessionMap.filter(predicate).values.toList
} }
def ZonePopulation(zone : Int, predicate : ((_, Player)) => Boolean) : List[Player] = { def Add(sessionId : Long, avatar : Avatar) : Boolean = {
zoneMap.lift(zone) match { sessionMap.values.find(char => char.equals(avatar)) match {
case Some(map) =>
val list = map.values.toList
sessionMap.filter({ case ((sess, _)) => list.contains(sess) }).filter(predicate).values.toList
case None => case None =>
Nil sessionMap.putIfAbsent(sessionId, avatar).isEmpty
}
}
def Add(sessionId : Long, player : Player) : Boolean = {
sessionMap.values.find(char => char.equals(player)) match {
case None =>
sessionMap.putIfAbsent(sessionId, player).isEmpty
case Some(_) => case Some(_) =>
false false
} }
} }
def Remove(sessionId : Long) : Option[Player] = { def Remove(sessionId : Long) : Option[Avatar] = {
sessionMap.remove(sessionId) match { sessionMap.remove(sessionId)
case Some(char) =>
zoneMap.foreach(zone => {
recursiveRemoveSession(zone.iterator, sessionId) match {
case Some(guid) =>
zone.remove(guid)
case None => ;
}
})
Some(char)
case None =>
None
}
} }
@tailrec private def recursiveRemoveSession(iter : Iterator[(Int, Long)], sessionId : Long) : Option[Int] = { def Shutdown : List[Avatar] = {
if(!iter.hasNext) {
None
}
else {
val (guid : Int, sess : Long) = iter.next
if(sess == sessionId) {
Some(guid)
}
else {
recursiveRemoveSession(iter, sessionId)
}
}
}
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(zone: Int, sessionId : Long, guid : PlanetSideGUID) : Boolean = Assign(zone, sessionId, guid.guid)
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 =>
false
}
}
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 val list = sessionMap.values.toList
sessionMap.clear sessionMap.clear
zoneMap.foreach(map => map.clear())
list list
} }
} }
/** /**
* A class for storing `Player` mappings for users that are currently online. * 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`. * The mapping system is tightly coupled between the `Avatar` class and to an instance of `WorldSessionActor`.
* 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> * <br>
* Use:<br> * Use:<br>
* 1) When a users logs in during `WorldSessionActor`, associate that user's session id and the character.<br> * 1) When a users logs in during `WorldSessionActor`, associate that user's session id and their character (avatar).<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Add(session, player)`<br> * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Add(session, avatar)`<br>
* 2) When that user's chosen character is declared his avatar using `SetCurrentAvatarMessage`, * 2) In between the previous two steps, a range of characters may be queried based on provided statistics.<br>
* also associate the user's session with their current GUID.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`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>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`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>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.WorldPopulation(...)`<br> * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.WorldPopulation(...)`<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.ZonePopulation(zone, ...)`<br> * 3) When the user leaves the game entirely, his character's entry is removed from the mapping.<br>
* 5) When the user navigates away from a region completely, their entry is forgotten.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Drop(zone, guid)`<br>
* 6) When the user leaves the game entirely, his character's entries are removed from the mappings.<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Remove(session)` * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`LivePlayerList.Remove(session)`
*/ */
object LivePlayerList { object LivePlayerList {
@ -179,100 +61,28 @@ object LivePlayerList {
* @param predicate the conditions for filtering the live `Player`s * @param predicate the conditions for filtering the live `Player`s
* @return a list of users's `Player`s that fit the criteria * @return a list of users's `Player`s that fit the criteria
*/ */
def WorldPopulation(predicate : ((_, Player)) => Boolean) : List[Player] = Instance.WorldPopulation(predicate) def WorldPopulation(predicate : ((_, Avatar)) => Boolean) : List[Avatar] = 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. * 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. * Neither the player nor the session may exist in the current mappings if this is to work.
* @param sessionId the session * @param sessionId the session
* @param player the character * @param avatar the character
* @return `true`, if the session was association was made; `false`, otherwise * @return `true`, if the session was association was made; `false`, otherwise
*/ */
def Add(sessionId : Long, player : Player) : Boolean = Instance.Add(sessionId, player) def Add(sessionId : Long, avatar : Avatar) : Boolean = Instance.Add(sessionId, avatar)
/** /**
* Remove all entries related to the given session identifier from the mappings. * Remove all entries related to the given session identifier from the mappings.
* The player no longer counts as "online." * The character no longer counts as "online."
* This function cleans up __all__ associations - those created by `Add`, and those created by `Assign`.
* @param sessionId the session * @param sessionId the session
* @return any character that was afffected by the mapping removal * @return any character that was afffected by the mapping removal
*/ */
def Remove(sessionId : Long) : Option[Player] = Instance.Remove(sessionId) def Remove(sessionId : Long) : Option[Avatar] = Instance.Remove(sessionId)
/**
* 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(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(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(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(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. * Hastily remove all mappings and ids.
* @return an unsorted list of the characters that were still online * @return an unsorted list of the characters that were still online
*/ */
def Shutdown : List[Player] = Instance.Shutdown def Shutdown : List[Avatar] = Instance.Shutdown
} }

View file

@ -8,14 +8,6 @@ import net.psforever.types.ExoSuitType
import scala.annotation.tailrec import scala.annotation.tailrec
//trait Loadout {
// def Label : String
// def VisibleSlots : List[Loadout.SimplifiedEntry]
// def Inventory : List[Loadout.SimplifiedEntry]
// def ExoSuit : ExoSuitType.Value
// def Subtype : Int
//}
/** /**
* From a `Player` their current exo-suit and their `Equipment`, retain a set of instructions to reconstruct this arrangement.<br> * From a `Player` their current exo-suit and their `Equipment`, retain a set of instructions to reconstruct this arrangement.<br>
* <br> * <br>
@ -113,7 +105,9 @@ object Loadout {
/** /**
* A basic `Trait` connecting all of the `Equipment` blueprints. * A basic `Trait` connecting all of the `Equipment` blueprints.
*/ */
sealed trait Simplification sealed trait Simplification {
def definition : ObjectDefinition
}
/** /**
* An entry in the `Loadout`, wrapping around a slot index and what is in the slot index. * An entry in the `Loadout`, wrapping around a slot index and what is in the slot index.
@ -125,17 +119,17 @@ object Loadout {
/** /**
* The simplified form of an `AmmoBox`. * The simplified form of an `AmmoBox`.
* @param adef the `AmmoBoxDefinition` that describes this future object * @param definition the `AmmoBoxDefinition` that describes this future object
* @param capacity the amount of ammunition, if any, to initialize; * @param capacity the amount of ammunition, if any, to initialize;
* if `None`, then the previous `AmmoBoxDefinition` will be referenced for the amount later * if `None`, then the previous `AmmoBoxDefinition` will be referenced for the amount later
*/ */
final case class ShorthandAmmoBox(adef : AmmoBoxDefinition, capacity : Int) extends Simplification final case class ShorthandAmmoBox(definition : AmmoBoxDefinition, capacity : Int) extends Simplification
/** /**
* The simplified form of a `Tool`. * The simplified form of a `Tool`.
* @param tdef the `ToolDefinition` that describes this future object * @param definition the `ToolDefinition` that describes this future object
* @param ammo the blueprints to construct the correct number of ammunition slots in the `Tool` * @param ammo the blueprints to construct the correct number of ammunition slots in the `Tool`
*/ */
final case class ShorthandTool(tdef : ToolDefinition, ammo : List[ShorthandAmmoSlot]) extends Simplification final case class ShorthandTool(definition : ToolDefinition, ammo : List[ShorthandAmmoSlot]) extends Simplification
/** /**
* The simplified form of a `Tool` `FireMode` * The simplified form of a `Tool` `FireMode`
* @param ammoIndex the index that points to the type of ammunition this slot currently uses * @param ammoIndex the index that points to the type of ammunition this slot currently uses
@ -144,19 +138,19 @@ object Loadout {
final case class ShorthandAmmoSlot(ammoIndex : Int, ammo : ShorthandAmmoBox) final case class ShorthandAmmoSlot(ammoIndex : Int, ammo : ShorthandAmmoBox)
/** /**
* The simplified form of a `ConstructionItem`. * The simplified form of a `ConstructionItem`.
* @param cdef the `ConstructionItemDefinition` that describes this future object * @param definition the `ConstructionItemDefinition` that describes this future object
*/ */
final case class ShorthandConstructionItem(cdef : ConstructionItemDefinition) extends Simplification final case class ShorthandConstructionItem(definition : ConstructionItemDefinition) extends Simplification
/** /**
* The simplified form of a `SimpleItem`. * The simplified form of a `SimpleItem`.
* @param sdef the `SimpleItemDefinition` that describes this future object * @param definition the `SimpleItemDefinition` that describes this future object
*/ */
final case class ShorthandSimpleItem(sdef : SimpleItemDefinition) extends Simplification final case class ShorthandSimpleItem(definition : SimpleItemDefinition) extends Simplification
/** /**
* The simplified form of a `Kit`. * The simplified form of a `Kit`.
* @param kdef the `KitDefinition` that describes this future object * @param definition the `KitDefinition` that describes this future object
*/ */
final case class ShorthandKit(kdef : KitDefinition) extends Simplification final case class ShorthandKit(definition : KitDefinition) extends Simplification
def DetermineSubtype(player : Player) : Int = { def DetermineSubtype(player : Player) : Int = {
if(player.ExoSuit == ExoSuitType.MAX) { if(player.ExoSuit == ExoSuitType.MAX) {

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects package net.psforever.objects
import net.psforever.objects.definition.{AvatarDefinition, ImplantDefinition} import net.psforever.objects.definition.AvatarDefinition
import net.psforever.objects.equipment.{Equipment, EquipmentSize} import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem} import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
@ -9,15 +9,9 @@ import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types._ import net.psforever.types._
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.collection.mutable
import scala.util.{Success, Try} import scala.util.{Success, Try}
class Player(private val name : String, class Player(private val core : Avatar) extends PlanetSideGameObject with FactionAffinity with Container {
private val faction : PlanetSideEmpire.Value,
private val sex : CharacterGender.Value,
private val head : Int,
private val voice : Int
) extends PlanetSideGameObject with FactionAffinity with Container {
private var alive : Boolean = false private var alive : Boolean = false
private var backpack : Boolean = false private var backpack : Boolean = false
private var health : Int = 0 private var health : Int = 0
@ -29,46 +23,20 @@ class Player(private val name : String,
private var exosuit : ExoSuitType.Value = ExoSuitType.Standard private var exosuit : ExoSuitType.Value = ExoSuitType.Standard
private val freeHand : EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory) private val freeHand : EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory)
private val holsters : Array[EquipmentSlot] = Array.fill[EquipmentSlot](5)(new EquipmentSlot) private val holsters : Array[EquipmentSlot] = Array.fill[EquipmentSlot](5)(new EquipmentSlot)
private val fifthSlot : EquipmentSlot = new OffhandEquipmentSlot(EquipmentSize.Inventory)
private val inventory : GridInventory = GridInventory() private val inventory : GridInventory = GridInventory()
private var drawnSlot : Int = Player.HandsDownSlot private var drawnSlot : Int = Player.HandsDownSlot
private var lastDrawnSlot : Int = Player.HandsDownSlot private var lastDrawnSlot : Int = Player.HandsDownSlot
private val loadouts : Array[Option[Loadout]] = Array.fill[Option[Loadout]](10)(None)
private var bep : Long = 0
private var cep : Long = 0
private val certifications : mutable.Set[CertificationType.Value] = mutable.Set[CertificationType.Value]()
/**
* Unlike other objects, the maximum number of `ImplantSlots` are built into the `Player`.
* Additionally, "implants" do not have tightly-coupled "`Definition` objects" that explain a formal implant object.
* The `ImplantDefinition` objects themselves are moved around as if they were the implants.
* The term internally used for this process is "installed" and "uninstalled."
* @see `ImplantSlot`
* @see `DetailedCharacterData.implants`
* @see `AvatarConverter.MakeImplantEntries`
*/
private val implants : Array[ImplantSlot] = Array.fill[ImplantSlot](3)(new ImplantSlot)
// private var tosRibbon : MeritCommendation.Value = MeritCommendation.None
// private var upperRibbon : MeritCommendation.Value = MeritCommendation.None
// private var middleRibbon : MeritCommendation.Value = MeritCommendation.None
// private var lowerRibbon : MeritCommendation.Value = MeritCommendation.None
private var facingYawUpper : Float = 0f private var facingYawUpper : Float = 0f
private var crouching : Boolean = false private var crouching : Boolean = false
private var jumping : Boolean = false private var jumping : Boolean = false
private var cloaked : Boolean = false private var cloaked : Boolean = false
private var backpackAccess : Option[PlanetSideGUID] = None private var backpackAccess : Option[PlanetSideGUID] = None
private var admin : Boolean = false
private var spectator : Boolean = false
private var vehicleSeated : Option[PlanetSideGUID] = None private var vehicleSeated : Option[PlanetSideGUID] = None
private var vehicleOwned : Option[PlanetSideGUID] = None private var vehicleOwned : Option[PlanetSideGUID] = None
private var continent : String = "home2" //the zone id private var continent : String = "home2" //the zone id
private val playerDef : AvatarDefinition = Player.definition //TODO could be a var
//SouNourS things //SouNourS things
/** Last medkituse. */ /** Last medkituse. */
@ -76,23 +44,20 @@ class Player(private val name : String,
var death_by : Int = 0 var death_by : Int = 0
var lastSeenStreamMessage : Array[Long] = Array.fill[Long](65535)(0L) var lastSeenStreamMessage : Array[Long] = Array.fill[Long](65535)(0L)
var lastShotSeq_time : Int = -1 var lastShotSeq_time : Int = -1
/** The player is shooting. */
var shooting : Boolean = false
/** From PlanetsideAttributeMessage */ /** From PlanetsideAttributeMessage */
var PlanetsideAttribute : Array[Long] = Array.ofDim(120) var PlanetsideAttribute : Array[Long] = Array.ofDim(120)
Player.SuitSetup(this, ExoSuit) Player.SuitSetup(this, ExoSuit)
fifthSlot.Equipment = new LockerContainer //the fifth slot is the player's "locker"
def Name : String = name def Name : String = core.name
def Faction : PlanetSideEmpire.Value = faction def Faction : PlanetSideEmpire.Value = core.faction
def Sex : CharacterGender.Value = sex def Sex : CharacterGender.Value = core.sex
def Voice : Int = voice def Head : Int = core.head
def Head : Int = head def Voice : Int = core.voice
def isAlive : Boolean = alive def isAlive : Boolean = alive
@ -173,9 +138,7 @@ class Player(private val name : String,
holsters(slot) holsters(slot)
} }
else if(slot == 5) { else if(slot == 5) {
new OffhandEquipmentSlot(EquipmentSize.Inventory) { core.FifthSlot
Equipment = fifthSlot.Equipment
}
} }
else if(slot == Player.FreeHandSlot) { else if(slot == Player.FreeHandSlot) {
freeHand freeHand
@ -189,7 +152,7 @@ class Player(private val name : String,
def Inventory : GridInventory = inventory def Inventory : GridInventory = inventory
def Locker : LockerContainer = fifthSlot.Equipment.get.asInstanceOf[LockerContainer] def Locker : LockerContainer = core.Locker
def Fit(obj : Equipment) : Option[Int] = { def Fit(obj : Equipment) : Option[Int] = {
recursiveHolsterFit(holsters.iterator, obj.Size) match { recursiveHolsterFit(holsters.iterator, obj.Size) match {
@ -220,20 +183,6 @@ class Player(private val name : String,
} }
} }
def Equip(slot : Int, obj : Equipment) : Boolean = {
if(-1 < slot && slot < 5) {
holsters(slot).Equipment = obj
true
}
else if(slot == Player.FreeHandSlot) {
freeHand.Equipment = obj
true
}
else {
inventory += slot -> obj
}
}
def FreeHand = freeHand def FreeHand = freeHand
def FreeHand_=(item : Option[Equipment]) : Option[Equipment] = { def FreeHand_=(item : Option[Equipment]) : Option[Equipment] = {
@ -243,33 +192,22 @@ class Player(private val name : String,
FreeHand.Equipment FreeHand.Equipment
} }
def SaveLoadout(label : String, line : Int) : Unit = {
loadouts(line) = Some(Loadout.Create(this, label))
}
def LoadLoadout(line : Int) : Option[Loadout] = loadouts(line)
def DeleteLoadout(line : Int) : Unit = {
loadouts(line) = None
}
def Find(obj : Equipment) : Option[Int] = Find(obj.GUID) def Find(obj : Equipment) : Option[Int] = Find(obj.GUID)
def Find(guid : PlanetSideGUID) : Option[Int] = { def Find(guid : PlanetSideGUID) : Option[Int] = {
findInHolsters(holsters.iterator, guid) match { findInHolsters(holsters.iterator, guid)
.orElse(findInInventory(inventory.Items.values.iterator, guid)) match {
case Some(index) => case Some(index) =>
Some(index) Some(index)
case None => case None =>
findInInventory(inventory.Items.values.iterator, guid) match { if(Locker.Find(guid).isDefined) {
case Some(index) => Some(5)
Some(index) }
case None => else if(freeHand.Equipment.isDefined && freeHand.Equipment.get.GUID == guid) {
if(freeHand.Equipment.isDefined && freeHand.Equipment.get.GUID == guid) { Some(Player.FreeHandSlot)
Some(Player.FreeHandSlot) }
} else {
else { None
None
}
} }
} }
} }
@ -349,27 +287,13 @@ class Player(private val name : String,
exosuit = suit exosuit = suit
} }
def BEP : Long = bep def LoadLoadout(line : Int) : Option[Loadout] = core.LoadLoadout(line)
def BEP_=(battleExperiencePoints : Long) : Long = { def BEP : Long = core.BEP
bep = math.max(0L, math.min(battleExperiencePoints, 4294967295L))
BEP
}
def CEP : Long = cep def CEP : Long = core.CEP
def CEP_=(commandExperiencePoints : Long) : Long = { def Certifications : Set[CertificationType.Value] = core.Certifications.toSet
cep = math.max(0L, math.min(commandExperiencePoints, 4294967295L))
CEP
}
def Certifications : mutable.Set[CertificationType.Value] = certifications
/**
* Retrieve the three implant slots for this player.
* @return an `Array` of `ImplantSlot` objects
*/
def Implants : Array[ImplantSlot] = implants
/** /**
* What kind of implant is installed into the given slot number? * What kind of implant is installed into the given slot number?
@ -377,91 +301,17 @@ class Player(private val name : String,
* @param slot the slot number * @param slot the slot number
* @return the tye of implant * @return the tye of implant
*/ */
def Implant(slot : Int) : ImplantType.Value = { def Implant(slot : Int) : ImplantType.Value = core.Implant(slot)
if(-1 < slot && slot < implants.length) { implants(slot).Implant } else { ImplantType.None }
}
/** /**
* Given a new implant, assign it into a vacant implant slot on this player.<br> * A read-only `Array` of tuples representing important information about all unlocked implant slots.
* <br> * @return a maximum of three implant types, initialization times, and active flags
* The implant must be unique in terms of which implants have already been assigned to this player.
* Multiple of a type of implant being assigned at once is not supported.
* Additionally, the implant is inserted into the earliest yet-unknown but vacant slot.
* Implant slots are vacant by just being unlocked or by having their previous implant uninstalled.
* @param implant the implant being installed
* @return the index of the `ImplantSlot` where the implant was installed
*/ */
def InstallImplant(implant : ImplantDefinition) : Option[Int] = { def Implants : Array[(ImplantType.Value, Long, Boolean)] = {
implants.find({p => p.Installed.contains(implant)}) match { //try to find the installed implant core.Implants.takeWhile(_.Unlocked).map( implant => { (implant.Implant, implant.MaxTimer, implant.Active) })
case None =>
recursiveFindImplantInSlot(implants.iterator, ImplantType.None) match { //install in a free slot
case Some(slot) =>
implants(slot).Implant = implant
Some(slot)
case None =>
None
}
case Some(_) =>
None
}
} }
/** def ResetAllImplants() : Unit = core.ResetAllImplants()
* Remove a specific implant from a player's allocated installed implants.<br>
* <br>
* Due to the exclusiveness of installed implants,
* any implant slot with a matching `Definition` can be uninstalled safely.
* (There will never be any doubles.)
* This operation can lead to an irregular pattern of installed and uninstalled `ImplantSlot` objects.
* Despite that breach of pattern, the logic here is consistent as demonstrated by the client and by packets.
* The client also assigns and removes implants based on slot numbers that only express availability of a "slot."
* @see `AvatarImplantMessage.implantSlot`
* @param implantType the type of implant being uninstalled
* @return the index of the `ImplantSlot` where the implant was found and uninstalled
*/
def UninstallImplant(implantType : ImplantType.Value) : Option[Int] = {
recursiveFindImplantInSlot(implants.iterator, implantType) match {
case Some(slot) =>
implants(slot).Implant = None
Some(slot)
case None =>
None
}
}
/**
* Locate the index of the encountered implant type.
* Functional implants may be exclusive in as far as the input `Iterator`'s source is concerned,
* but any number of `ImplantType.None` values are alway allowed in the source in any order.
* @param iter an `Iterator` of `ImplantSlot` objects
* @param implantType the target implant being sought
* @param index a defaulted index value representing the structure underlying the `Iterator` param
* @return the index where the target implant is installed
*/
@tailrec private def recursiveFindImplantInSlot(iter : Iterator[ImplantSlot], implantType : ImplantType.Value, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val slot = iter.next
if(slot.Unlocked && slot.Implant == implantType) {
Some(index)
}
else {
recursiveFindImplantInSlot(iter, implantType, index + 1)
}
}
}
def ResetAllImplants() : Unit = {
implants.foreach(slot => {
slot.Installed match {
case Some(_) =>
slot.Initialized = false
case None => ;
}
})
}
def FacingYawUpper : Float = facingYawUpper def FacingYawUpper : Float = facingYawUpper
@ -524,10 +374,6 @@ class Player(private val name : String,
isBackpack && (backpackAccess.isEmpty || backpackAccess.contains(player.GUID)) isBackpack && (backpackAccess.isEmpty || backpackAccess.contains(player.GUID))
} }
def Admin : Boolean = admin
def Spectator : Boolean = spectator
def VehicleSeated : Option[PlanetSideGUID] = vehicleSeated def VehicleSeated : Option[PlanetSideGUID] = vehicleSeated
def VehicleSeated_=(guid : PlanetSideGUID) : Option[PlanetSideGUID] = VehicleSeated_=(Some(guid)) def VehicleSeated_=(guid : PlanetSideGUID) : Option[PlanetSideGUID] = VehicleSeated_=(Some(guid))
@ -553,52 +399,34 @@ class Player(private val name : String,
Continent Continent
} }
def Definition : AvatarDefinition = playerDef def Definition : AvatarDefinition = core.Definition
override def toString : String = {
Player.toString(this)
}
def canEqual(other: Any): Boolean = other.isInstanceOf[Player] def canEqual(other: Any): Boolean = other.isInstanceOf[Player]
override def equals(other : Any) : Boolean = other match { override def equals(other : Any) : Boolean = other match {
case that: Player => case that: Player =>
(that canEqual this) && (that canEqual this) &&
name == that.name && core == that.core
faction == that.faction &&
sex == that.sex &&
voice == that.voice &&
head == that.head
case _ => case _ =>
false false
} }
override def hashCode() : Int = { override def hashCode() : Int = {
val state = Seq(name, faction, sex, voice, head) core.hashCode()
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
} }
override def toString : String = Player.toString(this)
} }
object Player { object Player {
final private val definition : AvatarDefinition = new AvatarDefinition(121) final val LockerSlot : Int = 5
final val FreeHandSlot : Int = 250 final val FreeHandSlot : Int = 250
final val HandsDownSlot : Int = 255 final val HandsDownSlot : Int = 255
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Player = { def apply(core : Avatar) : Player = {
new Player(name, faction, sex, head, voice) new Player(core)
} }
// /**
// * Change the type of `AvatarDefinition` is used to define the player.
// * @param player the player
// * @param avatarDef the player's new definition entry
// * @return the changed player
// */
// def apply(player : Player, avatarDef : AvatarDefinition) : Player = {
// player.playerDef = avatarDef
// player
// }
def SuitSetup(player : Player, eSuit : ExoSuitType.Value) : Unit = { def SuitSetup(player : Player, eSuit : ExoSuitType.Value) : Unit = {
val esuitDef : ExoSuitDefinition = ExoSuitDefinition.Select(eSuit) val esuitDef : ExoSuitDefinition = ExoSuitDefinition.Select(eSuit)
//exosuit //exosuit
@ -611,37 +439,11 @@ object Player {
(0 until 5).foreach(index => { player.Slot(index).Size = esuitDef.Holster(index) }) (0 until 5).foreach(index => { player.Slot(index).Size = esuitDef.Holster(index) })
} }
def Administrate(player : Player, isAdmin : Boolean) : Player = { def Respawn(player : Player) : Player = {
player.admin = isAdmin
player
}
def Spectate(player : Player, isSpectator : Boolean) : Player = {
player.spectator = isSpectator
player
}
def Release(player : Player) : Player = {
if(player.Release) { if(player.Release) {
val obj = new Player(player.Name, player.Faction, player.Sex, player.Voice, player.Head) val obj = new Player(player.core)
obj.VehicleOwned = player.VehicleOwned obj.VehicleOwned = player.VehicleOwned
obj.Continent = player.Continent obj.Continent = player.Continent
//hand over loadouts
(0 until 10).foreach(index => {
obj.loadouts(index) = player.loadouts(index)
})
//hand over implants
(0 until 3).foreach(index => {
if(obj.Implants(index).Unlocked = player.Implants(index).Unlocked) {
obj.Implants(index).Implant = player.Implants(index).Installed
}
})
//hand over knife
obj.Slot(4).Equipment = player.Slot(4).Equipment
player.Slot(4).Equipment = None
//hand over ???
obj.fifthSlot.Equipment = player.fifthSlot.Equipment
player.fifthSlot.Equipment = None
obj obj
} }
else { else {
@ -650,7 +452,7 @@ object Player {
} }
def toString(obj : Player) : String = { def toString(obj : Player) : String = {
val name : String = if(obj.VehicleSeated.isDefined) { s"[${obj.name}, ${obj.VehicleSeated.get.guid}]" } else { obj.Name } val guid = if(obj.HasGUID) { s" ${obj.Continent}-${obj.GUID.guid}" } else { "" }
s"[player $name, ${obj.Faction} (${obj.Health}/${obj.MaxHealth})(${obj.Armor}/${obj.MaxArmor})]" s"${obj.core}$guid ${obj.Health}/${obj.MaxHealth} ${obj.Armor}/${obj.MaxArmor}"
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.definition.converter package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, ImplantSlot, Player} import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, Cosmetics, DetailedCharacterData, DrawnSlot, ImplantEffects, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle} import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, Cosmetics, DetailedCharacterData, DrawnSlot, ImplantEffects, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
import net.psforever.types.{GrenadeState, ImplantType} import net.psforever.types.{GrenadeState, ImplantType}
@ -136,18 +136,12 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
private def MakeImplantEntries(obj : Player) : List[ImplantEntry] = { private def MakeImplantEntries(obj : Player) : List[ImplantEntry] = {
val numImplants : Int = DetailedCharacterData.numberOfImplantSlots(obj.BEP) val numImplants : Int = DetailedCharacterData.numberOfImplantSlots(obj.BEP)
val implants = obj.Implants val implants = obj.Implants
(0 until numImplants).map(index => { obj.Implants.map({ case(implant, initialization, active) =>
val slot = implants(index) if(initialization == 0) {
slot.Installed match { ImplantEntry(implant, None)
case Some(implant) => }
if(slot.Initialized) { else {
ImplantEntry(slot.Implant, None) ImplantEntry(implant, Some(math.max(0,initialization).toInt))
}
else {
ImplantEntry(slot.Implant, Some(implant.Initialization.toInt))
}
case None =>
ImplantEntry(ImplantType.None, None)
} }
}).toList }).toList
} }
@ -157,14 +151,14 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
* @param iter an `Iterator` of `ImplantSlot` objects * @param iter an `Iterator` of `ImplantSlot` objects
* @return the effect of an active implant * @return the effect of an active implant
*/ */
@tailrec private def recursiveMakeImplantEffects(iter : Iterator[ImplantSlot]) : Option[ImplantEffects.Value] = { @tailrec private def recursiveMakeImplantEffects(iter : Iterator[(ImplantType.Value, Long, Boolean)]) : Option[ImplantEffects.Value] = {
if(!iter.hasNext) { if(!iter.hasNext) {
None None
} }
else { else {
val slot = iter.next val(implant, _, active) = iter.next
if(slot.Active) { if(active) {
slot.Implant match { implant match {
case ImplantType.AdvancedRegen => case ImplantType.AdvancedRegen =>
Some(ImplantEffects.RegenEffects) Some(ImplantEffects.RegenEffects)
case ImplantType.DarklightVision => case ImplantType.DarklightVision =>

View file

@ -0,0 +1,124 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, InternalSlot, InventoryData, PlacementData, RibbonBars}
import net.psforever.types.{CharacterGender, GrenadeState, Vector3}
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
class CorpseConverter extends AvatarConverter {
override def ConstructorData(obj : Player) : Try[CharacterData] =
Failure(new Exception("CorpseConverter should not be used to generate CharacterData"))
override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
Success(
DetailedCharacterData(
MakeAppearanceData(obj),
0, 0, 0, 0, 0, 0, 0,
Nil, Nil, Nil, Nil,
None,
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
DrawnSlot.None
)
)
}
/**
* Compose some data from a `Player` into a representation common to both `CharacterData` and `DetailedCharacterData`.
* @param obj the `Player` game object
* @return the resulting `CharacterAppearanceData`
*/
private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
CharacterAppearanceData(
PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, 0),
0,
false,
false,
obj.ExoSuit,
"",
0,
true,
obj.Orientation.y, //TODO is this important?
0,
true,
GrenadeState.None,
false,
false,
false,
RibbonBars()
)
}
/**
* Given a player with an inventory, convert the contents of that inventory into converted-decoded packet data.
* The inventory is not represented in a `0x17` `Player`, so the conversion is only valid for `0x18` avatars.
* It will always be "`Detailed`".
* @param obj the `Player` game object
* @return a list of all items that were in the inventory in decoded packet form
*/
private def MakeInventory(obj : Player) : List[InternalSlot] = {
obj.Inventory.Items
.map({
case(_, item) =>
val equip : Equipment = item.obj
BuildEquipment(item.start, equip)
}).toList
}
/**
* Given a player with equipment holsters, convert the contents of those holsters into converted-decoded packet data.
* The decoded packet form is determined by the function in the parameters as both `0x17` and `0x18` conversions are available,
* with exception to the contents of the fifth slot.
* The fifth slot is only represented if the `Player` is an `0x18` type.
* @param obj the `Player` game object
* @return a list of all items that were in the holsters in decoded packet form
*/
private def MakeHolsters(obj : Player) : List[InternalSlot] = {
recursiveMakeHolsters(obj.Holsters().iterator)
}
/**
* Given some equipment holsters, convert the contents of those holsters into converted-decoded packet data.
* @param iter an `Iterator` of `EquipmentSlot` objects that are a part of the player's holsters
* @param list the current `List` of transformed data
* @param index which holster is currently being explored
* @return the `List` of inventory data created from the holsters
*/
@tailrec private def recursiveMakeHolsters(iter : Iterator[EquipmentSlot], list : List[InternalSlot] = Nil, index : Int = 0) : List[InternalSlot] = {
if(!iter.hasNext) {
list
}
else {
val slot : EquipmentSlot = iter.next
if(slot.Equipment.isDefined) {
val equip : Equipment = slot.Equipment.get
recursiveMakeHolsters(
iter,
list :+ BuildEquipment(index, equip),
index + 1
)
}
else {
recursiveMakeHolsters(iter, list, index + 1)
}
}
}
/**
* A builder method for turning an object into `0x17` decoded packet form.
* @param index the position of the object
* @param equip the game object
* @return the game object in decoded packet form
*/
private def BuildEquipment(index : Int, equip : Equipment) : InternalSlot = {
InternalSlot(equip.Definition.ObjectId, equip.GUID, index, equip.Definition.Packet.DetailedConstructorData(equip).get)
}
}
object CorpseConverter {
val converter = new CorpseConverter
}

View file

@ -22,7 +22,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
PlanetSideGUID(0) //if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //TODO is this really Owner? PlanetSideGUID(0) //if(obj.Owner.isDefined) { obj.Owner.get } else { PlanetSideGUID(0) } //TODO is this really Owner?
), ),
0, 0,
obj.Health / obj.MaxHealth * 255, //TODO not precise 255 * obj.Health / obj.MaxHealth, //TODO not precise
false, false, false, false,
obj.DeploymentState, obj.DeploymentState,
false, false,

View file

@ -1,7 +1,13 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.guid package net.psforever.objects.guid
import net.psforever.objects.vehicles.Utility import akka.actor.ActorRef
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.{EquipmentSlot, LockerContainer, Player, Tool, Vehicle}
import net.psforever.objects.inventory.Container
import scala.annotation.tailrec
/** /**
* The basic compiled tasks for assigning (registering) and revoking (unregistering) globally unique identifiers.<br> * The basic compiled tasks for assigning (registering) and revoking (unregistering) globally unique identifiers.<br>
@ -14,17 +20,13 @@ import net.psforever.objects.vehicles.Utility
* It will get passed from the more complicated functions down into the less complicated functions, * It will get passed from the more complicated functions down into the less complicated functions,
* until it has found the basic number assignment functionality.<br> * until it has found the basic number assignment functionality.<br>
* <br> * <br>
* All functions produce a `TaskResolver.GiveTask` container object that is expected to be used by a `TaskResolver`. * All functions produce a `TaskResolver.GiveTask` container object
* These "task containers" can also be unpackaged into their tasks, sorted into other containers, * or a list of `TaskResolver.GiveTask` container objects that is expected to be used by a `TaskResolver` `Actor`.
* and combined with other "task containers" to enact more complicated sequences of operations. * These "task containers" can also be unpackaged into their component tasks, sorted into other containers,
* and combined with other tasks to enact more complicated sequences of operations.
* Almost all tasks have an explicit registering and an unregistering activity defined for it.
*/ */
object GUIDTask { object GUIDTask {
import akka.actor.ActorRef
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.{EquipmentSlot, Player, Tool, Vehicle}
import scala.annotation.tailrec
/** /**
* Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers.<br> * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers.<br>
* <br> * <br>
@ -76,9 +78,34 @@ object GUIDTask {
TaskResolver.GiveTask(RegisterObjectTask(obj).task, ammoTasks) TaskResolver.GiveTask(RegisterObjectTask(obj).task, ammoTasks)
} }
/**
* Construct tasking that registers a `LockerContainer` object
* with a globally unique identifier selected from a pool of numbers.
* @param obj the object being registered
* @param guid implicit reference to a unique number system
* @see `GUIDTask.UnregisterLocker`
* @return a `TaskResolver.GiveTask` message
*/
def RegisterLocker(obj : LockerContainer)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
}
/**
* Construct tasking that registers the objects that are within the given container's inventory
* with a globally unique identifier selected from a pool of numbers for each object.
* @param container the storage unit in which objects can be found
* @param guid implicit reference to a unique number system
* @see `GUID.UnregisterInventory`<br>
* `Container`
* @return a list of `TaskResolver.GiveTask` messages
*/
def RegisterInventory(container : Container)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
container.Inventory.Items.values.map(entry => { RegisterEquipment(entry.obj)}).toList
}
/** /**
* Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers, * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers,
* after determining whether the object is complex (`Tool`) or simple.<br> * after determining whether the object is complex (`Tool` or `Locker`) or is simple.<br>
* <br> * <br>
* The objects in this case are specifically `Equipment`, a subclass of the basic register-able `IdentifiableEntity`. * The objects in this case are specifically `Equipment`, a subclass of the basic register-able `IdentifiableEntity`.
* About five subclasses of `Equipment` exist, but they decompose into two groups - "complex objects" and "simple objects." * About five subclasses of `Equipment` exist, but they decompose into two groups - "complex objects" and "simple objects."
@ -96,6 +123,8 @@ object GUIDTask {
obj match { obj match {
case tool : Tool => case tool : Tool =>
RegisterTool(tool) RegisterTool(tool)
case locker : LockerContainer =>
RegisterLocker(locker)
case _ => case _ =>
RegisterObjectTask(obj) RegisterObjectTask(obj)
} }
@ -118,17 +147,24 @@ object GUIDTask {
* @return a `TaskResolver.GiveTask` message * @return a `TaskResolver.GiveTask` message
*/ */
def RegisterAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = { def RegisterAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.LockerContainer val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
import net.psforever.objects.inventory.InventoryItem val lockerTask = List(RegisterLocker(tplayer.Locker))
val holsterTasks = recursiveHolsterTaskBuilding(tplayer.Holsters().iterator, RegisterEquipment) val inventoryTasks = RegisterInventory(tplayer)
val fifthHolsterTask = tplayer.Slot(5).Equipment match { TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
case Some(locker) => }
RegisterObjectTask(locker) :: locker.asInstanceOf[LockerContainer].Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)}).toList
case None => /**
List.empty[TaskResolver.GiveTask]; * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers, as a `Player`.<br>
} * <br>
val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)}) * Similar to `RegisterAvatar` but the locker components are skipped.
TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ fifthHolsterTask ++ inventoryTasks) * @param tplayer the `Player` object being registered
* @param guid implicit reference to a unique number system
* @return a `TaskResolver.GiveTask` message
*/
def RegisterPlayer(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
val inventoryTasks = RegisterInventory(tplayer)
TaskResolver.GiveTask(GUIDTask.RegisterObjectTask(tplayer)(guid).task, holsterTasks ++ inventoryTasks)
} }
/** /**
@ -149,10 +185,9 @@ object GUIDTask {
* @return a `TaskResolver.GiveTask` message * @return a `TaskResolver.GiveTask` message
*/ */
def RegisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = { def RegisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.inventory.InventoryItem val weaponTasks = VisibleSlotTaskBuilding(vehicle.Weapons.values, RegisterEquipment)
val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => RegisterEquipment(entry.Equipment.get)}).toList val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).values.map(util => { RegisterObjectTask(util())}).toList
val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).map({case (_ : Int, util : Utility) => RegisterObjectTask(util())}).toList val inventoryTasks = RegisterInventory(vehicle)
val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => RegisterEquipment(entry.obj)})
TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks) TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
} }
@ -201,9 +236,33 @@ object GUIDTask {
TaskResolver.GiveTask(UnregisterObjectTask(obj).task, ammoTasks) TaskResolver.GiveTask(UnregisterObjectTask(obj).task, ammoTasks)
} }
/**
* Construct tasking that unregisters a `LockerContainer` object from a globally unique identifier system.
* @param obj the object being unregistered
* @param guid implicit reference to a unique number system
* @see `GUIDTask.RegisterLocker`
* @return a `TaskResolver.GiveTask` message
*/
def UnregisterLocker(obj : LockerContainer)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
TaskResolver.GiveTask(UnregisterObjectTask(obj).task, UnregisterInventory(obj))
}
/**
* Construct tasking that unregisters the objects that are within the given container's inventory
* from a globally unique identifier system.
* @param container the storage unit in which objects can be found
* @param guid implicit reference to a unique number system
* @see `GUIDTask.RegisterInventory`<br>
* `Container`
* @return a list of `TaskResolver.GiveTask` messages
*/
def UnregisterInventory(container : Container)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
container.Inventory.Items.values.map(entry => { UnregisterEquipment(entry.obj)}).toList
}
/** /**
* Construct tasking that unregisters an object from a globally unique identifier system * Construct tasking that unregisters an object from a globally unique identifier system
* after determining whether the object is complex (`Tool`) or simple.<br> * after determining whether the object is complex (`Tool` or `Locker`) or is simple.<br>
* <br> * <br>
* This task performs an operation that reverses the effect of `RegisterEquipment`. * This task performs an operation that reverses the effect of `RegisterEquipment`.
* @param obj the `Equipment` object being unregistered * @param obj the `Equipment` object being unregistered
@ -215,6 +274,8 @@ object GUIDTask {
obj match { obj match {
case tool : Tool => case tool : Tool =>
UnregisterTool(tool) UnregisterTool(tool)
case locker : LockerContainer =>
UnregisterLocker(locker)
case _ => case _ =>
UnregisterObjectTask(obj) UnregisterObjectTask(obj)
} }
@ -230,36 +291,58 @@ object GUIDTask {
* @return a `TaskResolver.GiveTask` message * @return a `TaskResolver.GiveTask` message
*/ */
def UnregisterAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = { def UnregisterAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.LockerContainer val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
import net.psforever.objects.inventory.InventoryItem val lockerTask = List(UnregisterLocker(tplayer.Locker))
val holsterTasks = recursiveHolsterTaskBuilding(tplayer.Holsters().iterator, UnregisterEquipment) val inventoryTasks = UnregisterInventory(tplayer)
val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)}) TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
val fifthHolsterTask = tplayer.Slot(5).Equipment match {
case Some(locker) =>
UnregisterObjectTask(locker) :: locker.asInstanceOf[LockerContainer].Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)}).toList
case None =>
List.empty[TaskResolver.GiveTask];
}
TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ fifthHolsterTask ++ inventoryTasks)
} }
/** /**
* Construct tasking that unregisters a `Vehicle` object from a globally unique identifier system.<br> * Construct tasking that unregisters a portion of a `Player` object from a globally unique identifier system.<br>
* <br> * <br>
* This task performs an operation that reverses the effect of `RegisterVehicle`. * Similar to `UnregisterAvatar` but the locker components are skipped.
* @param vehicle the `Vehicle` object being unregistered * This task performs an operation that reverses the effect of `RegisterPlayer`.
* @param guid implicit reference to a unique number system * @param tplayer the `Player` object being unregistered
* @see `GUIDTask.RegisterVehicle` * @param guid implicit reference to a unique number system
* @return a `TaskResolver.GiveTask` message * @see `GUIDTask.RegisterAvatar`
*/ * @return a `TaskResolver.GiveTask` message
*/
def UnregisterPlayer(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
val inventoryTasks = UnregisterInventory(tplayer)
TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(tplayer).task, holsterTasks ++ inventoryTasks)
}
/**
* Construct tasking that unregisters a `Vehicle` object from a globally unique identifier system.<br>
* <br>
* This task performs an operation that reverses the effect of `RegisterVehicle`.
* @param vehicle the `Vehicle` object being unregistered
* @param guid implicit reference to a unique number system
* @see `GUIDTask.RegisterVehicle`
* @return a `TaskResolver.GiveTask` message
*/
def UnregisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = { def UnregisterVehicle(vehicle : Vehicle)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.inventory.InventoryItem val weaponTasks = VisibleSlotTaskBuilding(vehicle.Weapons.values, UnregisterEquipment)
val weaponTasks = vehicle.Weapons.map({ case(_ : Int, entry : EquipmentSlot) => UnregisterTool(entry.Equipment.get.asInstanceOf[Tool]) }).toList val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).values.map(util => { UnregisterObjectTask(util())}).toList
val utilTasks = Vehicle.EquipmentUtilities(vehicle.Utilities).map({case (_ : Int, util : Utility) => UnregisterObjectTask(util())}).toList val inventoryTasks = UnregisterInventory(vehicle)
val inventoryTasks = vehicle.Trunk.Items.map({ case((_ : Int, entry : InventoryItem)) => UnregisterEquipment(entry.obj)})
TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks) TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
} }
/**
* Construct tasking that allocates work upon encountered `Equipment` objects
* in reference to a globally unique identifier system of a pool of numbers.
* "Visible slots" are locations that can be viewed by multiple observers across a number of clients.
* @param list an `Iterable` sequence of `EquipmentSlot` objects that may or may not have equipment
* @param func the function used to build tasking from any discovered `Equipment`;
* strictly either `RegisterEquipment` or `UnregisterEquipment`
* @param guid implicit reference to a unique number system
* @return a list of `TaskResolver.GiveTask` messages
*/
def VisibleSlotTaskBuilding(list : Iterable[EquipmentSlot], func : ((Equipment)=>TaskResolver.GiveTask))(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
recursiveVisibleSlotTaskBuilding(list.iterator, func)
}
/** /**
* Iterate over a group of `EquipmentSlot`s, some of which may be occupied with an item. * Iterate over a group of `EquipmentSlot`s, some of which may be occupied with an item.
* Use `func` on any discovered `Equipment` to transform items into tasking, and add the tasking to a `List`. * Use `func` on any discovered `Equipment` to transform items into tasking, and add the tasking to a `List`.
@ -267,18 +350,19 @@ object GUIDTask {
* @param func the function used to build tasking from any discovered `Equipment`; * @param func the function used to build tasking from any discovered `Equipment`;
* strictly either `RegisterEquipment` or `UnregisterEquipment` * strictly either `RegisterEquipment` or `UnregisterEquipment`
* @param list a persistent `List` of `Equipment` tasking * @param list a persistent `List` of `Equipment` tasking
* @see `VisibleSlotTaskBuilding`
* @return a `List` of `Equipment` tasking * @return a `List` of `Equipment` tasking
*/ */
@tailrec private def recursiveHolsterTaskBuilding(iter : Iterator[EquipmentSlot], func : ((Equipment)=>TaskResolver.GiveTask), list : List[TaskResolver.GiveTask] = Nil)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = { @tailrec private def recursiveVisibleSlotTaskBuilding(iter : Iterator[EquipmentSlot], func : ((Equipment)=>TaskResolver.GiveTask), list : List[TaskResolver.GiveTask] = Nil)(implicit guid : ActorRef) : List[TaskResolver.GiveTask] = {
if(!iter.hasNext) { if(!iter.hasNext) {
list list
} }
else { else {
iter.next.Equipment match { iter.next.Equipment match {
case Some(item) => case Some(item) =>
recursiveHolsterTaskBuilding(iter, func, list :+ func(item)) recursiveVisibleSlotTaskBuilding(iter, func, list :+ func(item))
case None => case None =>
recursiveHolsterTaskBuilding(iter, func, list) recursiveVisibleSlotTaskBuilding(iter, func, list)
} }
} }
} }

View file

@ -287,12 +287,7 @@ class UniqueNumberSystem(private val guid : NumberPoolHub, private val poolActor
* @see `UniqueNumberSystem.UnregistrationProcess(Option[GUIDRequest], Int, Int)` * @see `UniqueNumberSystem.UnregistrationProcess(Option[GUIDRequest], Int, Int)`
*/ */
private def NoCallbackReturnNumber(number : Int, poolName : String) : Unit = { private def NoCallbackReturnNumber(number : Int, poolName : String) : Unit = {
poolActors.get(poolName) match { poolActors(poolName) ! NumberPoolActor.ReturnNumber(number, Some(Long.MinValue))
case Some(pool) =>
pool ! NumberPoolActor.ReturnNumber(number, Some(Long.MinValue))
case None =>
log.error(s"critical: tried to return number $number but did not find pool $poolName")
}
} }
/** /**
@ -316,12 +311,7 @@ class UniqueNumberSystem(private val guid : NumberPoolHub, private val poolActor
* @see `UniqueNumberSystem.RegistrationProcess(Option[GUIDRequest], Int, Int)` * @see `UniqueNumberSystem.RegistrationProcess(Option[GUIDRequest], Int, Int)`
*/ */
private def NoCallbackGetSpecificNumber(number : Int, poolName : String) : Unit = { private def NoCallbackGetSpecificNumber(number : Int, poolName : String) : Unit = {
poolActors.get(poolName) match { poolActors(poolName) ! NumberPoolActor.GetSpecificNumber(number, Some(Long.MinValue))
case Some(pool) =>
pool ! NumberPoolActor.GetSpecificNumber(number, Some(Long.MinValue))
case None =>
log.error(s"critical: tried to re-register number $number but did not find pool $poolName")
}
} }
} }

View file

@ -18,7 +18,7 @@ import net.psforever.objects.guid.NumberPoolHub
* a closed number space, which is also the `Zone`. * a closed number space, which is also the `Zone`.
* It utilizes those qualities of the enclosing region to construct the entity within that region.<br> * It utilizes those qualities of the enclosing region to construct the entity within that region.<br>
* <br> * <br>
* Example: `ServerObjectBuilder(n, function)` * Example: `ServerObjectBuilder(n, function)`<br>
* Example: `new ServerBuilderObject[A](n, function)`, where `function` is a `(Int,Context)=>A` * Example: `new ServerBuilderObject[A](n, function)`, where `function` is a `(Int,Context)=>A`
* @see `ZoneMap` * @see `ZoneMap`
* @see `Zone.Init` * @see `Zone.Init`
@ -28,7 +28,7 @@ import net.psforever.objects.guid.NumberPoolHub
* can be inferred from the output of `constructor` * can be inferred from the output of `constructor`
*/ */
class ServerObjectBuilder[A <: PlanetSideServerObject](private val id : Int, class ServerObjectBuilder[A <: PlanetSideServerObject](private val id : Int,
private val constructor : (Int, ActorContext) => A private val constructor : ServerObjectBuilder.ConstructorType[A]
) { ) {
/** /**
* Instantiate and configure the given server object. * Instantiate and configure the given server object.
@ -49,6 +49,8 @@ class ServerObjectBuilder[A <: PlanetSideServerObject](private val id : Int,
} }
object ServerObjectBuilder { object ServerObjectBuilder {
type ConstructorType[A <: PlanetSideServerObject] = (Int, ActorContext)=>A
/** /**
* Overloaded constructor. * Overloaded constructor.
* @param id the unqiue id that will be assigned to this entity * @param id the unqiue id that will be assigned to this entity
@ -56,7 +58,7 @@ object ServerObjectBuilder {
* @tparam A any object that extends from PlanetSideServerObject that will be produced by this class * @tparam A any object that extends from PlanetSideServerObject that will be produced by this class
* @return a `ServerObjectBuilder` object * @return a `ServerObjectBuilder` object
*/ */
def apply[A <: PlanetSideServerObject](id : Int, constructor : (Int, ActorContext) => A) : ServerObjectBuilder[A] = { def apply[A <: PlanetSideServerObject](id : Int, constructor : ConstructorType[A]) : ServerObjectBuilder[A] = {
new ServerObjectBuilder[A](id, constructor) new ServerObjectBuilder[A](id, constructor)
} }
} }

View file

@ -6,14 +6,19 @@ import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire import net.psforever.types.{PlanetSideEmpire, Vector3}
class Building(private val id : Int, private val zone : Zone) extends PlanetSideServerObject { class Building(private val mapId : Int, private val zone : Zone, private val buildingType : StructureType.Value) extends PlanetSideServerObject {
/**
* The mapId is the identifier number used in BuildingInfoUpdateMessage.
* The modelId is the identifier number used in SetEmpireMessage.
*/
private var modelId : Option[Int] = None
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var amenities : List[Amenity] = List.empty private var amenities : List[Amenity] = List.empty
GUID = PlanetSideGUID(0) GUID = PlanetSideGUID(0)
def Id : Int = id def Id : Int = mapId
def Faction : PlanetSideEmpire.Value = faction def Faction : PlanetSideEmpire.Value = faction
@ -32,25 +37,44 @@ class Building(private val id : Int, private val zone : Zone) extends PlanetSide
def Zone : Zone = zone def Zone : Zone = zone
def ModelId : Int = modelId.getOrElse(Id)
def ModelId_=(id : Int) : Int = ModelId_=(Some(id))
def ModelId_=(id : Option[Int]) : Int = {
modelId = id
ModelId
}
def BuildingType : StructureType.Value = buildingType
def Definition: ObjectDefinition = Building.BuildingDefinition def Definition: ObjectDefinition = Building.BuildingDefinition
} }
object Building { object Building {
final val NoBuilding : Building = new Building(0, Zone.Nowhere) { final val NoBuilding : Building = new Building(0, Zone.Nowhere, StructureType.Platform) {
override def Faction_=(faction : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL override def Faction_=(faction : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
override def Amenities_=(obj : Amenity) : List[Amenity] = Nil override def Amenities_=(obj : Amenity) : List[Amenity] = Nil
} }
final val BuildingDefinition : ObjectDefinition = new ObjectDefinition(0) { Name = "building" } final val BuildingDefinition : ObjectDefinition = new ObjectDefinition(0) { Name = "building" }
def apply(id : Int, zone : Zone) : Building = { def apply(id : Int, zone : Zone, buildingType : StructureType.Value) : Building = {
new Building(id, zone) new Building(id, zone, buildingType)
} }
def Structure(id : Int, zone : Zone, context : ActorContext) : Building = { def Structure(buildingType : StructureType.Value, location : Vector3)(id : Int, zone : Zone, context : ActorContext) : Building = {
import akka.actor.Props import akka.actor.Props
val obj = new Building(id, zone) val obj = new Building(id, zone, buildingType)
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-building") obj.Position = location
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
obj
}
def Structure(buildingType : StructureType.Value)(id : Int, zone : Zone, context : ActorContext) : Building = {
import akka.actor.Props
val obj = new Building(id, zone, buildingType)
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
obj obj
} }
} }

View file

@ -0,0 +1,20 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.structures
/**
* An `Enumeration` of the kinds of building structures found in the game.
* This is merely a kludge for more a future, more complicated internal object that handles base operations.
*/
object StructureType extends Enumeration {
type Type = Value
val
Bridge,
Building, //generic
Bunker,
Facility,
Platform, //outdoor amenities like the spawn pads in sanctuary
Tower,
WarpGate
= Value
}

View file

@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.structures
import akka.actor.ActorContext import akka.actor.ActorContext
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
class WarpGate(id : Int, zone : Zone) extends Building(id, zone) { class WarpGate(id : Int, zone : Zone) extends Building(id, zone, StructureType.WarpGate) {
//TODO stuff later //TODO stuff later
} }

View file

@ -339,8 +339,8 @@ object EquipmentTerminalDefinition {
import net.psforever.objects.Loadout._ import net.psforever.objects.Loadout._
entry match { entry match {
case obj : ShorthandTool => case obj : ShorthandTool =>
val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.adef }) val ammo : List[AmmoBoxDefinition] = obj.ammo.map(fmode => { fmode.ammo.definition })
val tool = Tool(obj.tdef) val tool = Tool(obj.definition)
//makes Tools where an ammo slot may have one of its alternate ammo types //makes Tools where an ammo slot may have one of its alternate ammo types
(0 until tool.MaxAmmoSlot).foreach(index => { (0 until tool.MaxAmmoSlot).foreach(index => {
val slot = tool.AmmoSlots(index) val slot = tool.AmmoSlots(index)
@ -350,16 +350,16 @@ object EquipmentTerminalDefinition {
tool tool
case obj : ShorthandAmmoBox => case obj : ShorthandAmmoBox =>
MakeAmmoBox(obj.adef, Some(obj.capacity)) MakeAmmoBox(obj.definition, Some(obj.capacity))
case obj : ShorthandConstructionItem => case obj : ShorthandConstructionItem =>
MakeConstructionItem(obj.cdef) MakeConstructionItem(obj.definition)
case obj : ShorthandSimpleItem => case obj : ShorthandSimpleItem =>
MakeSimpleItem(obj.sdef) MakeSimpleItem(obj.definition)
case obj : ShorthandKit => case obj : ShorthandKit =>
MakeKit(obj.kdef) MakeKit(obj.definition)
} }
} }

View file

@ -19,8 +19,11 @@ class MatrixTerminalDefinition(object_id : Int) extends TerminalDefinition(objec
else if(object_id == 519) { else if(object_id == 519) {
"matrix_terminalc" "matrix_terminalc"
} }
else if(object_id == 812) {
"spawn_terminal"
}
else { else {
throw new IllegalArgumentException("terminal must be object id 517-519") throw new IllegalArgumentException("terminal must be object id 517-519 or 812")
} }
override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal() override def Buy(player : Player, msg : ItemTransactionMessage) : Terminal.Exchange = Terminal.NoDeal()

View file

@ -1,34 +1,58 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.tube package net.psforever.objects.serverobject.tube
import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.structures.Amenity
class SpawnTube(tubeDef : ObjectDefinition) extends Amenity { /**
def Definition : ObjectDefinition = tubeDef * An owned server object that is used as a placeholder for the position and direction
* that infantry will be arranged upon spawning into the game world.
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class SpawnTube(tDef : SpawnTubeDefinition) extends Amenity {
def Definition : SpawnTubeDefinition = tDef
} }
object SpawnTube { object SpawnTube {
def apply(tubeDef : ObjectDefinition) : SpawnTube = { /**
new SpawnTube(tubeDef) * Overloaded constructor.
* @param tDef the spawn tube's definition entry
* @return a `SpawnTube` object
*/
def apply(tDef : SpawnTubeDefinition) : SpawnTube = {
new SpawnTube(tDef)
} }
// import akka.actor.ActorContext import akka.actor.ActorContext
// import net.psforever.types.Vector3 import net.psforever.types.Vector3
// /** /**
// * Instantiate an configure a `SpawnTube` object * Instantiate an configure a `SpawnTube` object
// * @param pos the position (used to determine spawn point) * @param pos the position (used to determine spawn point)
// * @param orient the orientation (used to indicate spawn direction) * @param orient the orientation (used to indicate spawn direction)
// * @param id the unique id that will be assigned to this entity * @param id the unique id that will be assigned to this entity
// * @param context a context to allow the object to properly set up `ActorSystem` functionality * @param context a context to allow the object to properly set up `ActorSystem` functionality
// * @return the `SpawnTube` object * @return the `SpawnTube` object
// */ */
// def Constructor(pos : Vector3, orient : Vector3)(id : Int, context : ActorContext) : SpawnTube = { def Constructor(pos : Vector3, orient : Vector3)(id : Int, context : ActorContext) : SpawnTube = {
// import net.psforever.objects.GlobalDefinitions Constructor(GlobalDefinitions.respawn_tube, pos, orient)(id, context)
// }
// val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube)
// obj.Position = pos /**
// obj.Orientation = orient * Instantiate an configure a `SpawnTube` object with the given definition
// obj * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
// } * @param pos the position (used to determine spawn point)
* @param orient the orientation (used to indicate spawn direction)
* @param id the unique id that will be assigned to this entity
* @param context a context to allow the object to properly set up `ActorSystem` functionality
* @return the `SpawnTube` object
*/
def Constructor(tdef : SpawnTubeDefinition, pos : Vector3, orient : Vector3)(id : Int, context : ActorContext) : SpawnTube = {
import akka.actor.Props
val obj = SpawnTube(tdef)
obj.Position = pos
obj.Orientation = orient
obj.Actor = context.actorOf(Props(classOf[SpawnTubeControl], obj), s"${tdef.Name}_$id")
obj
}
} }

View file

@ -6,7 +6,24 @@ import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.definition.converter.SpawnTubeConverter import net.psforever.objects.definition.converter.SpawnTubeConverter
import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.structures.Amenity
/**
* The definition for any `VehicleSpawnPad`.
* Currently, all tubes identify as object id 49 - `ams_respawn_tube` - configured manually.
* @see `GlobalDefinitions.ams_respawn_tube`
*/
class SpawnTubeDefinition(object_id : Int) extends ObjectDefinition(object_id) { class SpawnTubeDefinition(object_id : Int) extends ObjectDefinition(object_id) {
Name = if(object_id == 49) {
"ams_respawn_tube"
}
else if(object_id == 732) {
"respawn_tube"
}
else if(object_id == 733) {
"respawn_tube_tower"
}
else {
throw new IllegalArgumentException("terminal must be object id 49, 732, or 733")
}
Packet = new SpawnTubeConverter Packet = new SpawnTubeConverter
} }

View file

@ -2,7 +2,6 @@
package net.psforever.objects.zones package net.psforever.objects.zones
import akka.actor.{Actor, Props} import akka.actor.{Actor, Props}
import net.psforever.objects.Player
import scala.annotation.tailrec import scala.annotation.tailrec
@ -42,16 +41,25 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
def receive : Receive = { def receive : Receive = {
case InterstellarCluster.GetWorld(zoneId) => case InterstellarCluster.GetWorld(zoneId) =>
log.info(s"Asked to find $zoneId") log.info(s"Asked to find $zoneId")
findWorldInCluster(zones.iterator, zoneId) match { recursiveFindWorldInCluster(zones.iterator, _.Id == zoneId) match {
case Some(continent) => case Some(continent) =>
sender ! InterstellarCluster.GiveWorld(zoneId, continent) sender ! InterstellarCluster.GiveWorld(zoneId, continent)
case None => case None =>
log.error(s"Requested zone $zoneId could not be found") log.error(s"Requested zone $zoneId could not be found")
} }
case InterstellarCluster.RequestClientInitialization(tplayer) => case InterstellarCluster.RequestClientInitialization() =>
zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) }) zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) })
sender ! InterstellarCluster.ClientInitializationComplete(tplayer) //will be processed after all Zones sender ! InterstellarCluster.ClientInitializationComplete() //will be processed after all Zones
case msg @ Zone.Lattice.RequestSpawnPoint(zone_number, _, _) =>
recursiveFindWorldInCluster(zones.iterator, _.Number == zone_number) match {
case Some(zone) =>
zone.Actor forward msg
case None => //zone_number does not exist
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
}
case _ => ; case _ => ;
} }
@ -59,20 +67,20 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
/** /**
* Search through the `List` of `Zone` entities and find the one with the matching designation. * Search through the `List` of `Zone` entities and find the one with the matching designation.
* @param iter an `Iterator` of `Zone` entities * @param iter an `Iterator` of `Zone` entities
* @param zoneId the name of the `Zone` * @param predicate a condition to check against to determine when the appropriate `Zone` is discovered
* @return the discovered `Zone` * @return the discovered `Zone`
*/ */
@tailrec private def findWorldInCluster(iter : Iterator[Zone], zoneId : String) : Option[Zone] = { @tailrec private def recursiveFindWorldInCluster(iter : Iterator[Zone], predicate : Zone=>Boolean) : Option[Zone] = {
if(!iter.hasNext) { if(!iter.hasNext) {
None None
} }
else { else {
val cont = iter.next val cont = iter.next
if(cont.Id == zoneId) { if(predicate.apply(cont)) {
Some(cont) Some(cont)
} }
else { else {
findWorldInCluster(iter, zoneId) recursiveFindWorldInCluster(iter, predicate)
} }
} }
} }
@ -95,17 +103,41 @@ object InterstellarCluster {
/** /**
* Signal to the cluster that a new client needs to be initialized for all listed `Zone` destinations. * 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` * @see `Zone`
*/ */
final case class RequestClientInitialization(tplayer : Player) final case class RequestClientInitialization()
/** /**
* Return signal intended to inform the original sender that all `Zone`s have finished being initialized. * 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` * @see `WorldSessionActor`
*/ */
final case class ClientInitializationComplete(tplayer : Player) final case class ClientInitializationComplete()
} }
/*
// List[Building] --> List[List[(Amenity, Building)]] --> List[(SpawnTube*, Building)]
zone.LocalLattice.Buildings.values
.filter(_.Faction == player.Faction)
.map(building => { building.Amenities.map { _ -> building } })
.flatMap( _.filter({ case(amenity, _) => amenity.isInstanceOf[SpawnTube] }) )
*/
/*
zone.Buildings.values.filter(building => {
(
if(spawn_zone == 6) { Set(StructureType.Tower) }
else if(spawn_zone == 7) { Set(StructureType.Facility, StructureType.Building) }
else { Set.empty[StructureType.Value] }
).contains(building.BuildingType) &&
building.Amenities.exists(_.isInstanceOf[SpawnTube]) &&
building.Faction == player.Faction &&
building.Position != Vector3.Zero
})
.toSeq
.sortBy(building => {
Vector3.DistanceSquared(player.Position, building.Position) < Vector3.DistanceSquared(player.Position, building.Position)
})
.map(building => { building.Amenities.map { _ -> building } })
.flatMap( _.filter({ case(amenity, _) => amenity.isInstanceOf[SpawnTube] }) )
).headOption
*/

View file

@ -3,17 +3,19 @@ package net.psforever.objects.zones
import akka.actor.{ActorContext, ActorRef, Props} import akka.actor.{ActorContext, ActorRef, Props}
import akka.routing.RandomPool import akka.routing.RandomPool
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle} import net.psforever.objects.{Avatar, PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.UniqueNumberSystem import net.psforever.objects.guid.actor.UniqueNumberSystem
import net.psforever.objects.guid.selector.RandomSelector import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.structures.{Amenity, Building}
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.Vector3 import net.psforever.types.Vector3
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.collection.concurrent.TrieMap
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
import scala.collection.immutable.{Map => PairMap} import scala.collection.immutable.{Map => PairMap}
@ -44,8 +46,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
private var accessor : ActorRef = ActorRef.noSender private var accessor : ActorRef = ActorRef.noSender
/** The basic support structure for the globally unique number system used by this `Zone`. */ /** The basic support structure for the globally unique number system used by this `Zone`. */
private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536)) private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536))
guid.AddPool("environment", (0 to 2000).toList) guid.AddPool("environment", (0 to 3000).toList) //TODO tailer ro suit requirements of zone
guid.AddPool("dynamic", (2001 to 10000).toList).Selector = new RandomSelector //TODO unlump pools later; do not make too big guid.AddPool("dynamic", (3001 to 10000).toList).Selector = new RandomSelector //TODO unlump pools later; do not make too big
/** A synchronized `List` of items (`Equipment`) dropped by players on the ground and can be collected again. */ /** A synchronized `List` of items (`Equipment`) dropped by players on the ground and can be collected again. */
private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]() private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */ /** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
@ -54,8 +56,16 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
private var vehicles : List[Vehicle] = List[Vehicle]() private var vehicles : List[Vehicle] = List[Vehicle]()
/** */ /** */
private var transport : ActorRef = ActorRef.noSender private var transport : ActorRef = ActorRef.noSender
/** */
private val players : TrieMap[Avatar, Option[Player]] = TrieMap[Avatar, Option[Player]]()
/** */
private val corpses : ListBuffer[Player] = ListBuffer[Player]()
/** */
private var population : ActorRef = ActorRef.noSender
private var buildings : PairMap[Int, Building] = PairMap.empty[Int, Building] private var buildings : PairMap[Int, Building] = PairMap.empty[Int, Building]
/** key - spawn zone id, value - buildings belonging to spawn zone */
private var spawnGroups : Map[Building, List[SpawnTube]] = PairMap[Building, List[SpawnTube]]()
/** /**
* Establish the basic accessible conditions necessary for a functional `Zone`.<br> * Establish the basic accessible conditions necessary for a functional `Zone`.<br>
@ -66,7 +76,12 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
* First, the `Actor`-driven aspect of the globally unique identifier system for this `Zone` is finalized. * 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`. * Second, all supporting `Actor` agents are created, e.g., `ground`.
* Third, the `ZoneMap` server objects are loaded and constructed within that aforementioned system. * 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. * To avoid being called more than once, there is a test whether the `accessor` for the globally unique identifier system has been changed.<br>
* <br>
* Execution of this operation should be fail-safe.
* The chances of failure should be mitigated or skipped.
* An testing routine should be run after the fact on the results of the process.
* @see `ZoneActor.ZoneSetupCheck`
* @param context a reference to an `ActorContext` necessary for `Props` * @param context a reference to an `ActorContext` necessary for `Props`
*/ */
def Init(implicit context : ActorContext) : Unit = { def Init(implicit context : ActorContext) : Unit = {
@ -75,12 +90,12 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns") accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground") ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this), s"$Id-vehicles") transport = context.actorOf(Props(classOf[ZoneVehicleActor], this), s"$Id-vehicles")
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
Map.LocalObjects.foreach({ builderObject => Map.LocalObjects.foreach({ builderObject => builderObject.Build })
builderObject.Build
})
MakeBuildings(context) MakeBuildings(context)
AssignAmenities() AssignAmenities()
CreateSpawnGroups()
} }
} }
@ -176,6 +191,12 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
def Vehicles : List[Vehicle] = vehicles def Vehicles : List[Vehicle] = vehicles
def Players : List[Avatar] = players.keys.toList
def LivePlayers : List[Player] = players.values.collect( { case Some(tplayer) => tplayer }).toList
def Corpses : List[Player] = corpses.toList
def AddVehicle(vehicle : Vehicle) : List[Vehicle] = { def AddVehicle(vehicle : Vehicle) : List[Vehicle] = {
vehicles = vehicles :+ vehicle vehicles = vehicles :+ vehicle
Vehicles Vehicles
@ -217,6 +238,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
def Transport : ActorRef = transport def Transport : ActorRef = transport
def Population : ActorRef = population
def Buildings : Map[Int, Building] = buildings def Buildings : Map[Int, Building] = buildings
def Building(id : Int) : Option[Building] = { def Building(id : Int) : Option[Building] = {
@ -235,26 +258,55 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
}) })
} }
private def CreateSpawnGroups() : Unit = {
buildings.values
.filterNot { _.Position == Vector3.Zero }
.map(building => { building -> building.Amenities.collect { case(obj : SpawnTube) => obj } })
.filter( { case((_, spawns)) => spawns.nonEmpty })
.foreach { SpawnGroups }
}
def SpawnGroups() : Map[Building, List[SpawnTube]] = spawnGroups
def SpawnGroups(building : Building) : List[SpawnTube] = SpawnGroups(building.Id)
def SpawnGroups(buildingId : Int) : List[SpawnTube] = {
spawnGroups.find({ case((building, _)) => building.Id == buildingId }) match {
case Some((_, list)) =>
list
case None =>
List.empty[SpawnTube]
}
}
def SpawnGroups(spawns : (Building, List[SpawnTube])) : Map[Building, List[SpawnTube]] = {
val (building, tubes) = spawns
val entry : Map[Building, List[SpawnTube]] = PairMap(building -> tubes)
spawnGroups = spawnGroups ++ entry
entry
}
/** /**
* Provide bulk correspondence on all map entities that can be composed into packet messages and reported to a client. * 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> * These messages are sent in this fashion at the time of joining the server:<br>
* - `BroadcastWarpgateUpdateMessage`<br>
* - `BuildingInfoUpdateMessage`<br> * - `BuildingInfoUpdateMessage`<br>
* - `DensityLevelUpdateMessage`<br>
* - `BroadcastWarpgateUpdateMessage`<br>
* - `CaptureFlagUpdateMessage`<br> * - `CaptureFlagUpdateMessage`<br>
* - `ContinentalLockUpdateMessage`<br> * - `ContinentalLockUpdateMessage`<br>
* - `DensityLevelUpdateMessage`<br>
* - `ModuleLimitsMessage`<br> * - `ModuleLimitsMessage`<br>
* - `VanuModuleUpdateMessage`<br> * - `VanuModuleUpdateMessage`<br>
* - `ZoneForcedCavernConnectionMessage`<br> * - `ZoneForcedCavernConnectionMessage`<br>
* - `ZoneInfoMessage`<br> * - `ZoneInfoMessage`<br>
* - `ZoneLockInfoMessage`<br> * - `ZoneLockInfoMessage`<br>
* - `ZonePopulationUpdateMessage` * - `ZonePopulationUpdateMessage`
* @return a `List` of `GamePacket` messages * @return the `Zone` object
*/ */
def ClientInitialization() : Zone = this def ClientInitialization() : Zone = this
} }
object Zone { object Zone {
/** Default value, non-zone area. */
final val Nowhere : Zone = new Zone("nowhere", new ZoneMap("nowhere"), 99) final val Nowhere : Zone = new Zone("nowhere", new ZoneMap("nowhere"), 99)
/** /**
@ -263,6 +315,97 @@ object Zone {
*/ */
final case class Init() final case class Init()
object Population {
/**
* Message that introduces a user, by their `Avatar`, into a `Zone`.
* That user will be counted as part of that zone's population.
* The `avatar` may associate `Player` objects with itself in the future.
* @param avatar the `Avatar` object
*/
final case class Join(avatar : Avatar)
/**
* Message that excuses a user, by their `Avatar`, into a `Zone`.
* That user will not longer be counted as part of that zone's population.
* @see `PlayerHasLeft`
* @param avatar the `Avatar` object
*/
final case class Leave(avatar : Avatar)
/**
* Message that instructs the zone to disassociate a `Player` from this `Actor`.
* @see `PlayerAlreadySpawned`<br>
* `PlayerCanNotSpawn`
* @param avatar the `Avatar` object
* @param player the `Player` object
*/
final case class Spawn(avatar : Avatar, player : Player)
/**
* Message that instructs the zone to disassociate a `Player` from this `Actor`.
* @see `PlayerHasLeft`
* @param avatar the `Avatar` object
*/
final case class Release(avatar : Avatar)
/**
* Message that acts in reply to `Leave(avatar)` or `Release(avatar)`.
* In the former case, the avatar will have successfully left the zone, and `player` may be defined.
* In the latter case, the avatar did not initially `Join` the zone, and `player` is `None`.
* This message should not be considered a failure or a success case.
* @see `Release`<br>
* `Leave`
* @param zone the `Zone` object
* @param player the `Player` object
*/
final case class PlayerHasLeft(zone : Zone, player : Option[Player]) //Leave(avatar), but still has a player
/**
* Message that acts in reply to `Spawn(avatar, player)`, but the avatar already has a player.
* @param player the `Player` object
*/
final case class PlayerAlreadySpawned(zone : Zone, player : Player)
/**
* Message that acts in reply to `Spawn(avatar, player)`, but the avatar did not initially `Join` this zone.
* @param zone the `Zone` object
* @param player the `Player` object
*/
final case class PlayerCanNotSpawn(zone : Zone, player : Player)
}
object Corpse {
/**
* Message that reports to the zone of a freshly dead player.
* @param player the dead `Player`
*/
final case class Add(player : Player)
/**
* Message that tells the zone to no longer mind the dead player.
* @param player the dead `Player`
*/
final case class Remove(player : Player)
}
object Lattice {
/**
* Message requesting that the current zone determine where a `player` can spawn.
* @param zone_number this zone's numeric identifier
* @param player the `Player` object
* @param spawn_group the category of spawn points the request wants searched
*/
final case class RequestSpawnPoint(zone_number : Int, player : Player, spawn_group : Int)
/**
* Message that returns a discovered spawn point to a request source.
* @param zone_id the zone's text identifier
* @param building the `Building` in which the spawnpoint is located
* @param spawn_tube the spawn point holding object
*/
final case class SpawnPoint(zone_id : String, building : Building, spawn_tube : SpawnTube)
/**
* Message that informs a request source that a spawn point could not be discovered with the previous criteria.
* @param zone_number this zone's numeric identifier
* @param spawn_group the spawn point holding object;
* if `None`, then the previous `zone_number` could not be found;
* otherwise, no spawn points could be found in the zone
*/
final case class NoValidSpawnPoint(zone_number : Int, spawn_group : Option[Int])
}
/** /**
* Message to relinguish an item and place in on the ground. * Message to relinguish an item and place in on the ground.
* @param item the piece of `Equipment` * @param item the piece of `Equipment`

View file

@ -1,8 +1,12 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.objects.zones package net.psforever.objects.zones
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.types.Vector3
import org.log4s.Logger import org.log4s.Logger
/** /**
@ -12,29 +16,108 @@ import org.log4s.Logger
class ZoneActor(zone : Zone) extends Actor { class ZoneActor(zone : Zone) extends Actor {
private[this] val log = org.log4s.getLogger private[this] val log = org.log4s.getLogger
def receive : Receive = { def receive : Receive = Init
def Init : Receive = {
case Zone.Init() => case Zone.Init() =>
zone.Init zone.Init
ZoneSetupCheck() ZoneSetupCheck()
context.become(Processing)
case _ => ;
}
def Processing : Receive = {
//frwd to Population Actor
case msg @ Zone.Population.Join =>
zone.Population forward msg
case msg @ Zone.Population.Leave =>
zone.Population forward msg
case msg @ Zone.Population.Spawn =>
zone.Population forward msg
case msg @ Zone.Population.Release =>
zone.Population forward msg
case msg @ Zone.Corpse.Add =>
zone.Population forward msg
case msg @ Zone.Corpse.Remove =>
zone.Population forward msg
//frwd to Ground Actor
case msg @ Zone.DropItemOnGround =>
zone.Ground forward msg
case msg @ Zone.GetItemOnGround =>
zone.Ground forward msg
//frwd to Vehicle Actor
case msg @ Zone.SpawnVehicle =>
zone.Transport forward msg
case msg @ Zone.DespawnVehicle =>
zone.Transport forward msg
//own
case Zone.Lattice.RequestSpawnPoint(zone_number, player, spawn_group) =>
if(zone_number == zone.Number) {
val buildingTypeSet = if(spawn_group == 6) {
Set(StructureType.Tower)
}
else if(spawn_group == 7) {
Set(StructureType.Facility, StructureType.Building)
}
else {
Set.empty[StructureType.Value]
}
val playerPosition = player.Position.xy
zone.SpawnGroups()
.filter({ case((building, _)) =>
building.Faction == player.Faction && buildingTypeSet.contains(building.BuildingType)
})
.toSeq
.sortBy({ case ((building, _)) =>
Vector3.DistanceSquared(playerPosition, building.Position.xy)
})
.headOption match {
case Some((building, List(tube))) =>
sender ! Zone.Lattice.SpawnPoint(zone.Id, building, tube)
case Some((building, tubes)) =>
sender ! Zone.Lattice.SpawnPoint(zone.Id, building, scala.util.Random.shuffle(tubes).head)
case None =>
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, Some(spawn_group))
}
}
else { //wrong zone_number
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
}
case msg => case msg =>
log.warn(s"Received unexpected message - $msg") log.warn(s"Received unexpected message - $msg")
} }
def ZoneSetupCheck(): Unit = { def ZoneSetupCheck() : Int = {
import ZoneActor._ import ZoneActor._
def guid(id : Int) = zone.GUID(id)
val map = zone.Map val map = zone.Map
def guid(id : Int) = zone.GUID(id)
val slog = org.log4s.getLogger(s"zone/${zone.Id}/sanity") val slog = org.log4s.getLogger(s"zone/${zone.Id}/sanity")
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog) val errors = new AtomicInteger(0)
val validateObject : (Int, (PlanetSideGameObject)=>Boolean, String) => Boolean = ValidateObject(guid, slog, errors)
//check base to object associations //check base to object associations
map.ObjectToBuilding.foreach({ case((object_guid, base_id)) => map.ObjectToBuilding.foreach({ case((object_guid, building_id)) =>
if(zone.Building(base_id).isEmpty) { if(zone.Building(building_id).isEmpty) {
slog.error(s"expected a building at id #$base_id") slog.error(s"expected a building at id #$building_id")
errors.incrementAndGet()
} }
if(guid(object_guid).isEmpty) { if(guid(object_guid).isEmpty) {
slog.error(s"expected object id $object_guid to exist, but it did not") slog.error(s"expected object id $object_guid to exist, but it did not")
errors.incrementAndGet()
} }
}) })
@ -55,11 +138,11 @@ class ZoneActor(zone : Zone) extends Actor {
validateObject(mech_guid, ImplantMechCheck, "implant terminal mech") validateObject(mech_guid, ImplantMechCheck, "implant terminal mech")
validateObject(interface_guid, TerminalCheck, "implant terminal interface") validateObject(interface_guid, TerminalCheck, "implant terminal interface")
}) })
errors.intValue()
} }
} }
object ZoneActor { object ZoneActor {
/** /**
* Recover an object from a collection and perform any number of validating tests upon it. * Recover an object from a collection and perform any number of validating tests upon it.
* If the object fails any tests, log an error. * If the object fails any tests, log an error.
@ -73,11 +156,12 @@ object ZoneActor {
* @return `true` if the object was discovered and validates correctly; * @return `true` if the object was discovered and validates correctly;
* `false` if the object failed any tests * `false` if the object failed any tests
*/ */
def ValidateObject(guid : (Int)=>Option[PlanetSideGameObject], elog : Logger) def ValidateObject(guid : (Int)=>Option[PlanetSideGameObject], elog : Logger, errorCounter : AtomicInteger)
(object_guid : Int, test : (PlanetSideGameObject)=>Boolean, description : String) : Boolean = { (object_guid : Int, test : (PlanetSideGameObject)=>Boolean, description : String) : Boolean = {
try { try {
if(!test(guid(object_guid).get)) { if(!test(guid(object_guid).get)) {
elog.error(s"expected id $object_guid to be a $description, but it was not") elog.error(s"expected id $object_guid to be a $description, but it was not")
errorCounter.incrementAndGet()
false false
} }
else { else {
@ -85,8 +169,9 @@ object ZoneActor {
} }
} }
catch { catch {
case _ : Exception => case e : Exception =>
elog.error(s"expected a $description at id $object_guid but no object is initialized") elog.error(s"expected a $description at id $object_guid but no object is initialized - $e")
errorCounter.incrementAndGet()
false false
} }
} }

View file

@ -2,7 +2,7 @@
package net.psforever.objects.zones package net.psforever.objects.zones
import net.psforever.objects.serverobject.structures.FoundationBuilder import net.psforever.objects.serverobject.structures.FoundationBuilder
import net.psforever.objects.serverobject.ServerObjectBuilder import net.psforever.objects.serverobject.{PlanetSideServerObject, ServerObjectBuilder}
/** /**
* The fixed instantiation and relation of a series of server objects.<br> * The fixed instantiation and relation of a series of server objects.<br>
@ -44,10 +44,15 @@ class ZoneMap(private val name : String) {
/** /**
* Append the builder for a server object to the list of builders known to this `ZoneMap`. * Append the builder for a server object to the list of builders known to this `ZoneMap`.
* @param obj the builder for a server object * @param id the unique id that will be assigned to this entity
* @param constructor the logic that initializes the emitted entity
* @return the current number of builders
*/ */
def LocalObject(obj : ServerObjectBuilder[_]) : Unit = { def LocalObject[A <: PlanetSideServerObject](id : Int, constructor : ServerObjectBuilder.ConstructorType[A]) : Int = {
localObjects = localObjects :+ obj if(id > 0) {
localObjects = localObjects :+ ServerObjectBuilder[A](id, constructor)
}
localObjects.size
} }
def LocalBuildings : Map[Int, FoundationBuilder] = buildings def LocalBuildings : Map[Int, FoundationBuilder] = buildings

View file

@ -0,0 +1,184 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.zones
import akka.actor.Actor
import net.psforever.objects.{Avatar, Player}
import scala.annotation.tailrec
import scala.collection.concurrent.TrieMap
import scala.collection.mutable.ListBuffer
/**
* A support `Actor` that sequences adding and removing `Avatar` and `Player` objects to mappings and lists.
* The former mapping is considered to represent every user connect to the `zone` (`as Avatar` objects)
* and their current representation (as `Player` objects).
* The latter list keeps track of a group of former user representations.
* @param zone the `Zone` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @param corpseList a list of `Player` objects
*/
class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player]], corpseList : ListBuffer[Player]) extends Actor {
import ZonePopulationActor._
def receive : Receive = {
case Zone.Population.Join(avatar) =>
PopulationJoin(avatar, playerMap)
case Zone.Population.Leave(avatar) =>
PopulationLeave(avatar, playerMap) match {
case None => ;
case player @ Some(_) =>
sender ! Zone.Population.PlayerHasLeft(zone, player)
}
case Zone.Population.Spawn(avatar, player) =>
PopulationSpawn(avatar, player, playerMap) match {
case Some(tplayer) =>
if(tplayer ne player) {
sender ! Zone.Population.PlayerAlreadySpawned(zone, player)
}
case None =>
sender ! Zone.Population.PlayerCanNotSpawn(zone, player)
}
case Zone.Population.Release(avatar) =>
PopulationRelease(avatar, playerMap) match {
case Some(_) => ;
case None =>
sender ! Zone.Population.PlayerHasLeft(zone, None)
}
case Zone.Corpse.Add(player) =>
CorpseAdd(player, corpseList)
case Zone.Corpse.Remove(player) =>
CorpseRemove(player, corpseList)
case _ => ;
}
}
object ZonePopulationActor {
/**
* Add an `avatar` as the key of an `Avatar` to `Player` object pair in the given collection.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return true, if the mapping is for a new key;
* false, if the key already exists
*/
def PopulationJoin(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Boolean = {
playerMap.get(avatar) match {
case Some(_) =>
false
case None =>
playerMap += avatar -> None
true
}
}
/**
* Remove an `avatar` from the key of an `Avatar` to `Player` object pair in the given collection.
* If a `Player` object is associated at the time, return it safely.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return any `Player` object that was associated at the time the `avatar` was removed
*/
def PopulationLeave(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.remove(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer
}
}
/**
* Associate a `Player` object as a value to an existing `Avatar` object that will be its key.
* Do not overwrite players that are already associated.
* @param avatar an `Avatar` object
* @param player a `Player` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return the `Player` object that is associated with the `Avatar` key
*/
def PopulationSpawn(avatar : Avatar, player : Player, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.get(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer match {
case Some(aplayer) =>
Some(aplayer)
case None =>
playerMap(avatar) = Some(player)
Some(player)
}
}
}
/**
* Disassociate a `Player` object from an existing `Avatar` object that was be its key.
* @param avatar an `Avatar` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
* @return any `Player` object that is associated at the time
*/
def PopulationRelease(avatar : Avatar, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
playerMap.get(avatar) match {
case None =>
None
case Some(tplayer) =>
playerMap(avatar) = None
tplayer
}
}
/**
* If the given `player` passes a condition check, add it to the list.
* @param player a `Player` object
* @param corpseList a list of `Player` objects
* @return true, if the `player` was added to the list;
* false, otherwise
*/
def CorpseAdd(player : Player, corpseList : ListBuffer[Player]) : Boolean = {
if(player.isBackpack) {
corpseList += player
true
}
else {
false
}
}
/**
* Remove the given `player` from the list.
* @param player a `Player` object
* @param corpseList a list of `Player` objects
*/
def CorpseRemove(player : Player, corpseList : ListBuffer[Player]) : Unit = {
recursiveFindCorpse(corpseList.iterator, player) match {
case None => ;
case Some(index) =>
corpseList.remove(index)
}
}
/**
* A recursive function that finds and removes a specific player from a list of players.
* @param iter an `Iterator` of `Player` objects
* @param player the target `Player`
* @param index the index of the discovered `Player` object
* @return the index of the `Player` object in the list to be removed;
* `None`, otherwise
*/
@tailrec final def recursiveFindCorpse(iter : Iterator[Player], player : Player, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
if(iter.next == player) {
Some(index)
}
else {
recursiveFindCorpse(iter, player, index + 1)
}
}
}
}

View file

@ -2,15 +2,19 @@
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.Vector3 import net.psforever.types.{PlanetSideEmpire, Vector3}
import scodec.Codec import scodec.Attempt.{Failure, Successful}
import scodec.{Codec, Err}
import scodec.codecs._ import scodec.codecs._
/**
* An `Enumeration` of the various states a `Player` may possess in the cycle of nanite life and death.
*/
object DeadState extends Enumeration { object DeadState extends Enumeration {
type Type = Value type Type = Value
val val
Nothing, Alive,
Dead, Dead,
Release, Release,
RespawnTime RespawnTime
@ -20,19 +24,41 @@ object DeadState extends Enumeration {
} }
/** /**
* na * Dispatched by the server to manipulate the client's management of the `Player` object owned by the user as his "avatar."<br>
* @param state avatar's relationship with the world * <br>
* The cycle of a player is generally `Alive` to `Dead` and `Dead` to `Release` and `Release` to `RespawnTimer` to `Alive`.
* When deconstructing oneself, the user makes a jump between `Alive` and `Release`;
* and, he may make a further jump from `Release` to `Alive` depending on spawning choices.
* Being `Alive` is the most common state.
* (Despite what anyone says.)
* Being `Dead` is just a technical requirement to initialize the revive timer.
* The player should be sufficiently "dead" by having his health points decreased to zero.
* If the timer is reduced to zero, the player is sent back to their faction-appropriate sanctuary continent.<br>
* <br>
* `Release` causes a "dead" player to have its character model converted into a backpack or a form of pastry.
* This cancels the revival timer - the player may no longer be revived - and brings the user to the deployment map.
* From the deployment map, the user may select a place where they may respawn a new character.
* The options available form this spawn are not only related to the faction affinity of the bases compared to the user's player(s)
* but also to the field `faction` as is provided in the packet.
* If the player is converted to a state of `Release` while being alive, the deployment map is still displayed.
* Their character model is not replaced by a backpack or pastry.<br>
* <br>
* `RespawnTimer` is like `Dead` as it is just a formal distinction to cause the client to display a timer.
* The state indicates that the player is being resurrected at a previously-selected location in the state `Alive`.
* @param state avatar's mortal relationship with the world;
* the following timers are applicable during `Death` and `RespawnTimer`;
* `faction` is applicable mainly during `Release`
* @param timer_max total length of respawn countdown, in milliseconds * @param timer_max total length of respawn countdown, in milliseconds
* @param timer initial length of the respawn timer, in milliseconds * @param timer initial length of the respawn timer, in milliseconds
* @param pos last position * @param pos player's last position
* @param unk4 na * @param faction spawn points available for this faction on redeployment map
* @param unk5 na * @param unk5 na
*/ */
final case class AvatarDeadStateMessage(state : DeadState.Value, final case class AvatarDeadStateMessage(state : DeadState.Value,
timer_max : Long, timer_max : Long,
timer : Long, timer : Long,
pos : Vector3, pos : Vector3,
unk4 : Long, faction : PlanetSideEmpire.Value,
unk5 : Boolean) unk5 : Boolean)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = AvatarDeadStateMessage type Packet = AvatarDeadStateMessage
@ -41,12 +67,32 @@ final case class AvatarDeadStateMessage(state : DeadState.Value,
} }
object AvatarDeadStateMessage extends Marshallable[AvatarDeadStateMessage] { object AvatarDeadStateMessage extends Marshallable[AvatarDeadStateMessage] {
/**
* allocate all values from the `PlanetSideEmpire` `Enumeration`
*/
private val factionLongValues = PlanetSideEmpire.values map { _.id.toLong }
/**
* `Codec` for converting between the limited `PlanetSideEmpire` `Enumeration` and a `Long` value.
*/
private val factionLongCodec = uint32L.exmap[PlanetSideEmpire.Value] (
fv =>
if(factionLongValues.contains(fv)) {
Successful(PlanetSideEmpire(fv.toInt))
}
else {
Failure(Err(s"$fv is not mapped to a PlanetSideEmpire value"))
},
f =>
Successful(f.id.toLong)
)
implicit val codec : Codec[AvatarDeadStateMessage] = ( implicit val codec : Codec[AvatarDeadStateMessage] = (
("state" | DeadState.codec) :: ("state" | DeadState.codec) ::
("timer_max" | uint32L) :: ("timer_max" | uint32L) ::
("timer" | uint32L) :: ("timer" | uint32L) ::
("pos" | Vector3.codec_pos) :: ("pos" | Vector3.codec_pos) ::
("unk4" | uint32L) :: ("unk4" | factionLongCodec) ::
("unk5" | bool) ("unk5" | bool)
).as[AvatarDeadStateMessage] ).as[AvatarDeadStateMessage]
} }

View file

@ -20,8 +20,8 @@ import scodec.codecs._
* @param unk3 na * @param unk3 na
*/ */
final case class DisconnectMessage(msg : String, final case class DisconnectMessage(msg : String,
unk2 : String = "", unk2 : String,
unk3 : String = "") unk3 : String)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = DisconnectMessage type Packet = DisconnectMessage
def opcode = GamePacketOpcode.DisconnectMessage def opcode = GamePacketOpcode.DisconnectMessage
@ -29,6 +29,15 @@ final case class DisconnectMessage(msg : String,
} }
object DisconnectMessage extends Marshallable[DisconnectMessage] { object DisconnectMessage extends Marshallable[DisconnectMessage] {
/**
* Overloaded constructor that focuses only on the visible disconnection message
* @param msg the displayed message
* @return a `DisconnectMessage` object
*/
def apply(msg : String) : DisconnectMessage = {
new DisconnectMessage(msg, "", "")
}
implicit val codec : Codec[DisconnectMessage] = ( implicit val codec : Codec[DisconnectMessage] = (
("msg" | PacketHelpers.encodedString) :: ("msg" | PacketHelpers.encodedString) ::
("unk2" | PacketHelpers.encodedString) :: ("unk2" | PacketHelpers.encodedString) ::

View file

@ -5,7 +5,18 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacke
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
/**
* na
* @param unk1 when defined, na;
* non-zero when selecting the sanctuary option from a non-sanctuary continent deployment map
* @param unk2 when defined, indicates type of spawn point by destination;
* 0 is unknown (may refer to all available spawns regardless of last position);
* 6 is towers;
* 7 is facilities
* @param unk3 na
* @param unk4 na
* @param unk5 when defined, the continent number
*/
final case class SpawnRequestMessage(unk1 : Int, final case class SpawnRequestMessage(unk1 : Int,
unk2 : Long, unk2 : Long,
unk3 : Int, unk3 : Int,

View file

@ -9,8 +9,8 @@ final case class Vector3(x : Float,
y : Float, y : Float,
z : Float) { z : Float) {
/** /**
* Operator override for vector addition, treating `Vector3` objects as actual mathematical vectors. * Operator for vector addition, treating `Vector3` objects as actual mathematical vectors.
* The application of this overload is "vector1 + vector2." * The application of this definition is "vector1 + vector2."
* @param vec the other `Vector3` object * @param vec the other `Vector3` object
* @return a new `Vector3` object with the summed values * @return a new `Vector3` object with the summed values
*/ */
@ -19,8 +19,8 @@ final case class Vector3(x : Float,
} }
/** /**
* Operator override for vector subtraction, treating `Vector3` objects as actual mathematical vectors. * Operator for vector subtraction, treating `Vector3` objects as actual mathematical vectors.
* The application of this overload is "vector1 - vector2." * The application of this definition is "vector1 - vector2."
* @param vec the other `Vector3` object * @param vec the other `Vector3` object
* @return a new `Vector3` object with the difference values * @return a new `Vector3` object with the difference values
*/ */
@ -29,7 +29,7 @@ final case class Vector3(x : Float,
} }
/** /**
* Operator override for vector scaling, treating `Vector3` objects as actual mathematical vectors. * Operator for vector scaling, treating `Vector3` objects as actual mathematical vectors.
* The application of this overload is "vector * scalar" exclusively. * The application of this overload is "vector * scalar" exclusively.
* "scalar * vector" is invalid. * "scalar * vector" is invalid.
* @param scalar the value to multiply this vector * @param scalar the value to multiply this vector
@ -38,6 +38,14 @@ final case class Vector3(x : Float,
def *(scalar : Float) : Vector3 = { def *(scalar : Float) : Vector3 = {
Vector3(x*scalar, y*scalar, z*scalar) Vector3(x*scalar, y*scalar, z*scalar)
} }
/**
* Operator for returning the ground-planar coordinates
* and ignoring the perpendicular distance from the world floor.
* The application of this definition is "vector.xy" or "vector xy."
* @return a new `Vector3` object with only two of the components of the original
*/
def xy : Vector3 = Vector3(x, y, 0)
} }
object Vector3 { object Vector3 {

View file

@ -64,6 +64,11 @@ class Vector3Test extends Specification {
vec * 3f mustEqual Vector3(3.8999999f, -7.7999997f, 11.700001f) vec * 3f mustEqual Vector3(3.8999999f, -7.7999997f, 11.700001f)
} }
"separate into x-component and y-component only" in {
val obj = Vector3(1.1f, 2.2f, 3.3f)
obj.xy mustEqual Vector3(1.1f, 2.2f, 0f)
}
"calculate the unit vector (zero)" in { "calculate the unit vector (zero)" in {
Vector3.Unit(Vector3.Zero) mustEqual Vector3(0,0,0) Vector3.Unit(Vector3.Zero) mustEqual Vector3(0,0,0)
} }

View file

@ -4,11 +4,12 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.Vector3 import net.psforever.types.{PlanetSideEmpire, Vector3}
import scodec.bits._ import scodec.bits._
class AvatarDeadStateMessageTest extends Specification { class AvatarDeadStateMessageTest extends Specification {
val string = hex"ad3c1260801c12608009f99861fb0741e040000010" val string = hex"ad3c1260801c12608009f99861fb0741e040000010"
val string_invalid = hex"ad3c1260801c12608009f99861fb0741e0400000F0"
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
@ -17,15 +18,19 @@ class AvatarDeadStateMessageTest extends Specification {
unk2 mustEqual 300000 unk2 mustEqual 300000
unk3 mustEqual 300000 unk3 mustEqual 300000
pos mustEqual Vector3(6552.617f,4602.375f,60.90625f) pos mustEqual Vector3(6552.617f,4602.375f,60.90625f)
unk4 mustEqual 2 unk4 mustEqual PlanetSideEmpire.VS
unk5 mustEqual true unk5 mustEqual true
case _ => case _ =>
ko ko
} }
} }
"decode (failure)" in {
PacketCoding.DecodePacket(string_invalid).isFailure mustEqual true
}
"encode" in { "encode" in {
val msg = AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3(6552.617f,4602.375f,60.90625f), 2, true) val msg = AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, Vector3(6552.617f,4602.375f,60.90625f), PlanetSideEmpire.VS, true)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -26,4 +26,8 @@ class DisconnectMessageTest extends Specification {
pkt mustEqual string pkt mustEqual string
} }
"comparison" in {
DisconnectMessage("First") mustEqual DisconnectMessage("First", "", "")
}
} }

View file

@ -0,0 +1,397 @@
// Copyright (c) 2017 PSForever
package objects
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects._
import net.psforever.objects.definition.ImplantDefinition
import net.psforever.types.{CharacterGender, ImplantType, PlanetSideEmpire}
import org.specs2.mutable._
class AvatarTest extends Specification {
def CreatePlayer() : (Player, Avatar) = {
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
val
player = Player(avatar)
player.Slot(0).Equipment = Tool(beamer)
player.Slot(2).Equipment = Tool(suppressor)
player.Slot(4).Equipment = Tool(forceblade)
player.Slot(6).Equipment = AmmoBox(bullet_9mm)
player.Slot(9).Equipment = AmmoBox(bullet_9mm)
player.Slot(12).Equipment = AmmoBox(bullet_9mm)
player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP)
player.Slot(36).Equipment = AmmoBox(energy_cell)
player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
(player, avatar)
}
"construct" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.name mustEqual "Chord"
av.faction mustEqual PlanetSideEmpire.TR
av.sex mustEqual CharacterGender.Male
av.head mustEqual 0
av.voice mustEqual 5
av.BEP mustEqual 0
av.CEP mustEqual 0
av.Certifications mustEqual Set.empty
av.Definition.ObjectId mustEqual 121
}
"can maintain cumulative battle experience point values" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.BEP mustEqual 0
av.BEP = 100
av.BEP mustEqual 100
av.BEP = 700
av.BEP mustEqual 700
}
"can maintain battle experience point values up to a maximum (Long.MaxValue)" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.BEP mustEqual 0
av.BEP = 4294967295L
av.BEP mustEqual 4294967295L
}
"can not maintain battle experience point values below zero" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.BEP mustEqual 0
av.BEP = -1
av.BEP mustEqual 0
av.BEP = 100
av.BEP mustEqual 100
av.BEP = -1
av.BEP mustEqual 0
}
"can maintain cumulative command experience point values" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.CEP mustEqual 0
av.CEP = 100
av.CEP mustEqual 100
av.CEP = 700
av.CEP mustEqual 700
}
"can maintain command experience point values up to a maximum (Long.MaxValue)" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.CEP mustEqual 0
av.CEP = 4294967295L
av.CEP mustEqual 4294967295L
}
"can not maintain command experience point values below zero" in {
val av = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
av.CEP mustEqual 0
av.CEP = -1
av.CEP mustEqual 0
av.CEP = 100
av.CEP mustEqual 100
av.CEP = -1
av.CEP mustEqual 0
}
"can tell the difference between avatars" in {
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
}
//refer to ImplantTest.scala for more tests
"maximum of three implant slots" in {
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants.length mustEqual 3
obj.Implants(0).Unlocked mustEqual false
obj.Implants(0).Initialized mustEqual false
obj.Implants(0).Active mustEqual false
obj.Implants(0).Implant mustEqual ImplantType.None
obj.Implant(0) mustEqual ImplantType.None
obj.Implants(0).Installed mustEqual None
obj.Implants(1).Unlocked mustEqual false
obj.Implants(1).Initialized mustEqual false
obj.Implants(1).Active mustEqual false
obj.Implants(1).Implant mustEqual ImplantType.None
obj.Implant(1) mustEqual ImplantType.None
obj.Implants(1).Installed mustEqual None
obj.Implants(2).Unlocked mustEqual false
obj.Implants(2).Initialized mustEqual false
obj.Implants(2).Active mustEqual false
obj.Implants(2).Implant mustEqual ImplantType.None
obj.Implant(2) mustEqual ImplantType.None
obj.Implants(2).Installed mustEqual None
obj.Implant(3) mustEqual ImplantType.None //invalid slots beyond the third always reports as ImplantType.None
}
"can install an implant" in {
val testplant : ImplantDefinition = ImplantDefinition(1)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.InstallImplant(testplant) mustEqual Some(0)
obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant
case Some(slot) =>
slot.Installed mustEqual Some(testplant)
case _ =>
ko
}
ok
}
"can install implants in sequential slots" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
}
"can not install the same type of implant twice" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(1)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual None
}
"can not install more implants than slots available (two unlocked)" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.InstallImplant(testplant3) mustEqual None
}
"can not install more implants than slots available (four implants)" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val testplant4 : ImplantDefinition = ImplantDefinition(4)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.InstallImplant(testplant3) mustEqual Some(2)
obj.InstallImplant(testplant4) mustEqual None
}
"can uninstall an implant" in {
val testplant : ImplantDefinition = ImplantDefinition(1)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.InstallImplant(testplant) mustEqual Some(0)
obj.Implants(0).Installed mustEqual Some(testplant)
obj.UninstallImplant(testplant.Type) mustEqual Some(0)
obj.Implants(0).Installed mustEqual None
}
"can uninstall just a specific implant" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.InstallImplant(testplant3) mustEqual Some(2)
obj.Implant(0) mustEqual testplant1.Type
obj.Implant(1) mustEqual testplant2.Type
obj.Implant(2) mustEqual testplant3.Type
obj.UninstallImplant(testplant2.Type) mustEqual Some(1)
obj.Implant(0) mustEqual testplant1.Type
obj.Implant(1) mustEqual ImplantType.None
obj.Implant(2) mustEqual testplant3.Type
}
"can install implants to any available slot" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val testplant3 : ImplantDefinition = ImplantDefinition(3)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.Implants(2).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.InstallImplant(testplant3) mustEqual Some(2)
obj.UninstallImplant(testplant2.Type) mustEqual Some(1)
obj.Implant(0) mustEqual testplant1.Type
obj.Implant(1) mustEqual ImplantType.None
obj.Implant(2) mustEqual testplant3.Type
val testplant4 : ImplantDefinition = ImplantDefinition(4)
obj.InstallImplant(testplant4) mustEqual Some(1)
obj.Implant(0) mustEqual testplant1.Type
obj.Implant(1) mustEqual testplant4.Type
obj.Implant(2) mustEqual testplant3.Type
}
"can reset implants to uninitialized state" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1)
val testplant2 : ImplantDefinition = ImplantDefinition(2)
val obj = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true
obj.Implants(1).Unlocked = true
obj.InstallImplant(testplant1) mustEqual Some(0)
obj.InstallImplant(testplant2) mustEqual Some(1)
obj.Implants(0).Initialized = true
obj.Implants(0).Active = true
obj.Implants(1).Initialized = true
obj.Implants(0).Initialized mustEqual true
obj.Implants(0).Active mustEqual true
obj.Implants(1).Initialized mustEqual true
obj.ResetAllImplants()
obj.Implants(0).Initialized mustEqual false
obj.Implants(0).Active mustEqual false
obj.Implants(1).Initialized mustEqual false
}
"does not have any loadout specifications by default" in {
val (_, avatar) = CreatePlayer()
(0 to 9).foreach { avatar.LoadLoadout(_) mustEqual None }
ok
}
"save player's current inventory as a loadout" in {
val (obj, avatar) = CreatePlayer()
obj.Slot(0).Equipment.get.asInstanceOf[Tool].Magazine = 1 //non-standard but legal
obj.Slot(2).Equipment.get.asInstanceOf[Tool].AmmoSlot.Magazine = 100 //non-standard (and out of range, real=25)
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 3
val holsters = items.VisibleSlots.sortBy(_.index)
holsters.head.index mustEqual 0
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual beamer
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 1 //we changed this
holsters(1).index mustEqual 2
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual suppressor
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 100 //we changed this
holsters(2).index mustEqual 4
holsters(2).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual forceblade
items.Inventory.length mustEqual 6
val inventory = items.Inventory.sortBy(_.index)
inventory.head.index mustEqual 6
inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(1).index mustEqual 9
inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(2).index mustEqual 12
inventory(2).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(3).index mustEqual 33
inventory(3).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm_AP
inventory(4).index mustEqual 36
inventory(4).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual energy_cell
inventory(5).index mustEqual 39
inventory(5).item.asInstanceOf[Loadout.ShorthandSimpleItem].definition mustEqual remote_electronics_kit
case None =>
ko
}
}
"save player's current inventory as a loadout, only found in the called-out slot number" in {
val (obj, avatar) = CreatePlayer()
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(1).isDefined mustEqual false
avatar.LoadLoadout(0).isDefined mustEqual true
}
"try to save player's current inventory as a loadout, but will not save to an invalid slot" in {
val (obj, avatar) = CreatePlayer()
avatar.SaveLoadout(obj, "test", 10)
avatar.LoadLoadout(10) mustEqual None
}
"save player's current inventory as a loadout, without inventory contents" in {
val (obj, avatar) = CreatePlayer()
obj.Inventory.Clear()
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 3
items.Inventory.length mustEqual 0 //empty
case None =>
ko
}
}
"save player's current inventory as a loadout, without visible slot contents" in {
val (obj, avatar) = CreatePlayer()
obj.Slot(0).Equipment = None
obj.Slot(2).Equipment = None
obj.Slot(4).Equipment = None
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 0 //empty
items.Inventory.length mustEqual 6
case None =>
ko
}
}
"save, load, delete; rapidly" in {
val (obj, avatar) = CreatePlayer()
avatar.SaveLoadout(obj, "test", 0)
avatar.LoadLoadout(0).isDefined mustEqual true
avatar.DeleteLoadout(0)
avatar.LoadLoadout(0) mustEqual None
}
"the fifth slot is the locker wrapped in an EquipmentSlot" in {
val (_, avatar) = CreatePlayer()
avatar.FifthSlot.Equipment.contains(avatar.Locker)
}
"toString" in {
Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5).toString mustEqual "TR Chord"
}
}

View file

@ -6,7 +6,7 @@ import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.{Door, DoorControl} import net.psforever.objects.serverobject.doors.{Door, DoorControl}
import net.psforever.objects.serverobject.structures.{Amenity, Building, BuildingControl, WarpGate} import net.psforever.objects.serverobject.structures._
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
@ -27,7 +27,7 @@ class AmenityTest extends Specification {
"can be owned by a building" in { "can be owned by a building" in {
val ao = new AmenityObject() val ao = new AmenityObject()
val bldg = Building(10, Zone.Nowhere) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
ao.Owner = bldg ao.Owner = bldg
ao.Owner mustEqual bldg ao.Owner mustEqual bldg
@ -51,7 +51,7 @@ class AmenityTest extends Specification {
"confer faction allegiance through ownership" in { "confer faction allegiance through ownership" in {
//see FactionAffinityTest //see FactionAffinityTest
val ao = new AmenityObject() val ao = new AmenityObject()
val bldg = Building(10, Zone.Nowhere) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
ao.Owner = bldg ao.Owner = bldg
bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL
ao.Faction mustEqual PlanetSideEmpire.NEUTRAL ao.Faction mustEqual PlanetSideEmpire.NEUTRAL
@ -66,7 +66,7 @@ class AmenityTest extends Specification {
class BuildingTest extends Specification { class BuildingTest extends Specification {
"Building" should { "Building" should {
"construct" in { "construct" in {
val bldg = Building(10, Zone.Nowhere) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Id mustEqual 10 bldg.Id mustEqual 10
bldg.Actor mustEqual ActorRef.noSender bldg.Actor mustEqual ActorRef.noSender
bldg.Amenities mustEqual Nil bldg.Amenities mustEqual Nil
@ -75,7 +75,7 @@ class BuildingTest extends Specification {
} }
"change faction affinity" in { "change faction affinity" in {
val bldg = Building(10, Zone.Nowhere) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL
bldg.Faction = PlanetSideEmpire.TR bldg.Faction = PlanetSideEmpire.TR
@ -83,7 +83,7 @@ class BuildingTest extends Specification {
} }
"keep track of amenities" in { "keep track of amenities" in {
val bldg = Building(10, Zone.Nowhere) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
val door1 = Door(GlobalDefinitions.door) val door1 = Door(GlobalDefinitions.door)
val door2 = Door(GlobalDefinitions.door) val door2 = Door(GlobalDefinitions.door)
@ -114,7 +114,7 @@ class WarpGateTest extends Specification {
class BuildingControl1Test extends ActorTest { class BuildingControl1Test extends ActorTest {
"Building Control" should { "Building Control" should {
"construct" in { "construct" in {
val bldg = Building(10, Zone.Nowhere) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test") bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test")
assert(bldg.Actor != ActorRef.noSender) assert(bldg.Actor != ActorRef.noSender)
} }
@ -124,7 +124,7 @@ class BuildingControl1Test extends ActorTest {
class BuildingControl2Test extends ActorTest { class BuildingControl2Test extends ActorTest {
"Building Control" should { "Building Control" should {
"convert and assert faction affinity on convert request" in { "convert and assert faction affinity on convert request" in {
val bldg = Building(10, Zone.Nowhere) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test") bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test")
assert(bldg.Faction == PlanetSideEmpire.TR) assert(bldg.Faction == PlanetSideEmpire.TR)
@ -142,7 +142,7 @@ class BuildingControl2Test extends ActorTest {
class BuildingControl3Test extends ActorTest { class BuildingControl3Test extends ActorTest {
"Building Control" should { "Building Control" should {
"convert and assert faction affinity on convert request, and for each of its amenities" in { "convert and assert faction affinity on convert request, and for each of its amenities" in {
val bldg = Building(10, Zone.Nowhere) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "building-test") bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "building-test")
val door1 = Door(GlobalDefinitions.door) val door1 = Door(GlobalDefinitions.door)

View file

@ -151,6 +151,7 @@ class ConverterTest extends Specification {
} }
"Player" should { "Player" should {
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj : Player = { val obj : Player = {
/* /*
Create an AmmoBoxDefinition with which to build two AmmoBoxes Create an AmmoBoxDefinition with which to build two AmmoBoxes
@ -171,7 +172,7 @@ class ConverterTest extends Specification {
val tool = Tool(tdef) val tool = Tool(tdef)
tool.GUID = PlanetSideGUID(92) tool.GUID = PlanetSideGUID(92)
tool.AmmoSlot.Box.GUID = PlanetSideGUID(90) tool.AmmoSlot.Box.GUID = PlanetSideGUID(90)
val obj = Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = Player(avatar)
obj.GUID = PlanetSideGUID(93) obj.GUID = PlanetSideGUID(93)
obj.Slot(2).Equipment = tool obj.Slot(2).Equipment = tool
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(94) obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(94)
@ -182,7 +183,7 @@ class ConverterTest extends Specification {
val converter = new CharacterSelectConverter val converter = new CharacterSelectConverter
"convert to packet (BR < 24)" in { "convert to packet (BR < 24)" in {
obj.BEP = 0 avatar.BEP = 0
obj.Definition.Packet.DetailedConstructorData(obj) match { obj.Definition.Packet.DetailedConstructorData(obj) match {
case Success(_) => case Success(_) =>
ok ok
@ -198,7 +199,7 @@ class ConverterTest extends Specification {
} }
"convert to packet (BR >= 24)" in { "convert to packet (BR >= 24)" in {
obj.BEP = 10000000 avatar.BEP = 10000000
obj.Definition.Packet.DetailedConstructorData(obj) match { obj.Definition.Packet.DetailedConstructorData(obj) match {
case Success(_) => case Success(_) =>
ok ok
@ -214,7 +215,7 @@ class ConverterTest extends Specification {
} }
"convert to simple packet (BR < 24)" in { "convert to simple packet (BR < 24)" in {
obj.BEP = 0 avatar.BEP = 0
converter.DetailedConstructorData(obj) match { converter.DetailedConstructorData(obj) match {
case Success(_) => case Success(_) =>
ok ok
@ -226,7 +227,7 @@ class ConverterTest extends Specification {
} }
"convert to simple packet (BR >= 24)" in { "convert to simple packet (BR >= 24)" in {
obj.BEP = 10000000 avatar.BEP = 10000000
converter.DetailedConstructorData(obj) match { converter.DetailedConstructorData(obj) match {
case Success(_) => case Success(_) =>
ok ok

View file

@ -2,9 +2,9 @@
package objects package objects
import akka.actor.{ActorRef, ActorSystem, Props} import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.doors.{Door, DoorControl} import net.psforever.objects.serverobject.doors.{Door, DoorControl}
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage} import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage}
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3} import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
@ -13,7 +13,7 @@ import org.specs2.mutable.Specification
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
class DoorTest extends Specification { class DoorTest extends Specification {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
"Door" should { "Door" should {
"construct" in { "construct" in {
@ -121,8 +121,8 @@ object DoorControlTest {
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, Door) = { def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, Door) = {
val door = Door(GlobalDefinitions.door) val door = Door(GlobalDefinitions.door)
door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door") door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door")
door.Owner = new Building(0, Zone.Nowhere) door.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
door.Owner.Faction = faction door.Owner.Faction = faction
(Player("test", faction, CharacterGender.Male, 0, 0), door) (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), door)
} }
} }

View file

@ -5,7 +5,7 @@ import akka.actor.{Actor, ActorSystem, Props}
import net.psforever.objects.{GlobalDefinitions, Vehicle} import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
@ -42,7 +42,7 @@ class FactionAffinityTest extends Specification {
"inherits affinity from owner 2" in { "inherits affinity from owner 2" in {
val obj = new Door(GlobalDefinitions.door) val obj = new Door(GlobalDefinitions.door)
val bldg = new Building(1, Zone.Nowhere) val bldg = new Building(1, Zone.Nowhere, StructureType.Building)
obj.Owner = bldg obj.Owner = bldg
obj.Faction mustEqual PlanetSideEmpire.NEUTRAL obj.Faction mustEqual PlanetSideEmpire.NEUTRAL

View file

@ -3,10 +3,9 @@ package objects
import akka.actor.{ActorRef, ActorSystem, Props} import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl} import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl}
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, PlanetSideEmpire} import net.psforever.types.{CharacterGender, PlanetSideEmpire}
@ -68,8 +67,8 @@ object IFFLockControlTest {
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, IFFLock) = { def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, IFFLock) = {
val lock = IFFLock(GlobalDefinitions.lock_external) val lock = IFFLock(GlobalDefinitions.lock_external)
lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control") lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control")
lock.Owner = new Building(0, Zone.Nowhere) lock.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
lock.Owner.Faction = faction lock.Owner.Faction = faction
(Player("test", faction, CharacterGender.Male, 0, 0), lock) (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), lock)
} }
} }

View file

@ -4,193 +4,87 @@ package objects
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire} import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire}
import net.psforever.objects.GlobalDefinitions._ import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects.equipment.{Equipment, EquipmentSize}
import org.specs2.mutable._ import org.specs2.mutable._
class LoadoutTest extends Specification { class LoadoutTest extends Specification {
val avatar = Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1)
def CreatePlayer() : Player = { def CreatePlayer() : Player = {
val new Player(avatar) {
player = Player("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1) Slot(0).Equipment = Tool(beamer)
player.Slot(0).Equipment = Tool(beamer) Slot(2).Equipment = Tool(suppressor)
player.Slot(2).Equipment = Tool(suppressor) Slot(4).Equipment = Tool(forceblade)
player.Slot(4).Equipment = Tool(forceblade) Slot(6).Equipment = ConstructionItem(ace)
player.Slot(6).Equipment = AmmoBox(bullet_9mm) Slot(9).Equipment = AmmoBox(bullet_9mm)
player.Slot(9).Equipment = AmmoBox(bullet_9mm) Slot(12).Equipment = AmmoBox(bullet_9mm)
player.Slot(12).Equipment = AmmoBox(bullet_9mm) Slot(33).Equipment = Kit(medkit)
player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP) Slot(39).Equipment = SimpleItem(remote_electronics_kit)
player.Slot(36).Equipment = AmmoBox(energy_cell) }
player.Slot(39).Equipment = SimpleItem(remote_electronics_kit)
player
} }
"Player Loadout" should { "test sample player" in {
"test sample player" in { val player = CreatePlayer()
val obj : Player = CreatePlayer() player.Holsters()(0).Equipment.get.Definition mustEqual beamer
obj.Holsters()(0).Equipment.get.Definition mustEqual beamer player.Holsters()(2).Equipment.get.Definition mustEqual suppressor
obj.Holsters()(2).Equipment.get.Definition mustEqual suppressor player.Holsters()(4).Equipment.get.Definition mustEqual forceblade
obj.Holsters()(4).Equipment.get.Definition mustEqual forceblade player.Slot(6).Equipment.get.Definition mustEqual ace
obj.Slot(6).Equipment.get.Definition mustEqual bullet_9mm player.Slot(9).Equipment.get.Definition mustEqual bullet_9mm
obj.Slot(9).Equipment.get.Definition mustEqual bullet_9mm player.Slot(12).Equipment.get.Definition mustEqual bullet_9mm
obj.Slot(12).Equipment.get.Definition mustEqual bullet_9mm player.Slot(33).Equipment.get.Definition mustEqual medkit
obj.Slot(33).Equipment.get.Definition mustEqual bullet_9mm_AP player.Slot(39).Equipment.get.Definition mustEqual remote_electronics_kit
obj.Slot(36).Equipment.get.Definition mustEqual energy_cell }
obj.Slot(39).Equipment.get.Definition mustEqual remote_electronics_kit
}
"do not load, if never saved" in { "create a loadout that contains a player's inventory" in {
CreatePlayer().LoadLoadout(0) mustEqual None val player = CreatePlayer()
} val obj = Loadout.Create(player, "test")
"save but incorrect load" in { obj.Label mustEqual "test"
val obj : Player = CreatePlayer() obj.ExoSuit mustEqual obj.ExoSuit
obj.SaveLoadout("test", 0) obj.Subtype mustEqual 0
obj.LoadLoadout(1) mustEqual None obj.VisibleSlots.length mustEqual 3
} val holsters = obj.VisibleSlots.sortBy(_.index)
holsters.head.index mustEqual 0
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual beamer
holsters(1).index mustEqual 2
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual suppressor
holsters(2).index mustEqual 4
holsters(2).item.asInstanceOf[Loadout.ShorthandTool].definition mustEqual forceblade
"save and load" in { obj.Inventory.length mustEqual 5
val obj : Player = CreatePlayer() val inventory = obj.Inventory.sortBy(_.index)
obj.Slot(0).Equipment.get.asInstanceOf[Tool].Magazine = 1 //non-standard but legal inventory.head.index mustEqual 6
obj.Slot(2).Equipment.get.asInstanceOf[Tool].AmmoSlot.Magazine = 100 //non-standard (and out of range, real=25) inventory.head.item.asInstanceOf[Loadout.ShorthandConstructionItem].definition mustEqual ace
obj.SaveLoadout("test", 0) inventory(1).index mustEqual 9
inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(2).index mustEqual 12
inventory(2).item.asInstanceOf[Loadout.ShorthandAmmoBox].definition mustEqual bullet_9mm
inventory(3).index mustEqual 33
inventory(3).item.asInstanceOf[Loadout.ShorthandKit].definition mustEqual medkit
inventory(4).index mustEqual 39
inventory(4).item.asInstanceOf[Loadout.ShorthandSimpleItem].definition mustEqual remote_electronics_kit
}
obj.LoadLoadout(0) match { "distinguish MAX subtype information" in {
case Some(items) => val player = CreatePlayer()
items.Label mustEqual "test" val slot = player.Slot(0)
items.ExoSuit mustEqual obj.ExoSuit slot.Equipment = None //only an unequipped slot can have its Equipment Size changed (Rifle -> Max)
items.Subtype mustEqual 0 Player.SuitSetup(player, ExoSuitType.MAX)
items.VisibleSlots.length mustEqual 3 val ldout1 = Loadout.Create(player, "weaponless")
val holsters = items.VisibleSlots.sortBy(_.index) slot.Equipment = None
holsters.head.index mustEqual 0 slot.Equipment = Tool(trhev_dualcycler)
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual beamer val ldout2 = Loadout.Create(player, "cycler")
holsters.head.item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 1 slot.Equipment = None
holsters(1).index mustEqual 2 slot.Equipment = Tool(trhev_pounder)
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual suppressor val ldout3 = Loadout.Create(player, "pounder")
holsters(1).item.asInstanceOf[Loadout.ShorthandTool].ammo.head.ammo.capacity mustEqual 100 slot.Equipment = None
holsters(2).index mustEqual 4 slot.Equipment = Tool(trhev_burster)
holsters(2).item.asInstanceOf[Loadout.ShorthandTool].tdef mustEqual forceblade val ldout4 = Loadout.Create(player, "burster")
items.Inventory.length mustEqual 6 ldout1.Subtype mustEqual 0
val inventory = items.Inventory.sortBy(_.index) ldout2.Subtype mustEqual 1
inventory.head.index mustEqual 6 ldout3.Subtype mustEqual 2
inventory.head.item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm ldout4.Subtype mustEqual 3
inventory(1).index mustEqual 9
inventory(1).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm
inventory(2).index mustEqual 12
inventory(2).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm
inventory(3).index mustEqual 33
inventory(3).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual bullet_9mm_AP
inventory(4).index mustEqual 36
inventory(4).item.asInstanceOf[Loadout.ShorthandAmmoBox].adef mustEqual energy_cell
inventory(5).index mustEqual 39
inventory(5).item.asInstanceOf[Loadout.ShorthandSimpleItem].sdef mustEqual remote_electronics_kit
case None =>
ko
}
}
"save without inventory contents" in {
val obj : Player = CreatePlayer()
obj.Inventory.Clear()
obj.SaveLoadout("test", 0)
obj.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 3
items.Inventory.length mustEqual 0 //empty
case None =>
ko
}
}
"save without visible slot contents" in {
val obj : Player = CreatePlayer()
obj.Slot(0).Equipment = None
obj.Slot(2).Equipment = None
obj.Slot(4).Equipment = None
obj.SaveLoadout("test", 0)
obj.LoadLoadout(0) match {
case Some(items) =>
items.Label mustEqual "test"
items.ExoSuit mustEqual obj.ExoSuit
items.Subtype mustEqual 0
items.VisibleSlots.length mustEqual 0 //empty
items.Inventory.length mustEqual 6
case None =>
ko
}
}
"save (a construction item) and load" in {
val obj : Player = CreatePlayer()
obj.Inventory.Clear()
obj.Slot(6).Equipment = ConstructionItem(advanced_ace)
obj.SaveLoadout("test", 0)
obj.LoadLoadout(0) match {
case Some(items) =>
items.Inventory.length mustEqual 1
items.Inventory.head.index mustEqual 6
items.Inventory.head.item.asInstanceOf[Loadout.ShorthandConstructionItem].cdef mustEqual advanced_ace
case None =>
ko
}
}
"save (a kit) and load" in {
val obj : Player = CreatePlayer()
obj.Inventory.Clear()
obj.Slot(6).Equipment = Kit(medkit)
obj.SaveLoadout("test", 0)
obj.LoadLoadout(0) match {
case Some(items) =>
items.Inventory.length mustEqual 1
items.Inventory.head.index mustEqual 6
items.Inventory.head.item.asInstanceOf[Loadout.ShorthandKit].kdef mustEqual medkit
case None =>
ko
}
}
"save, load, delete" in {
val obj : Player = CreatePlayer()
obj.SaveLoadout("test", 0)
obj.LoadLoadout(0) match {
case None =>
ko
case Some(_) => ; //good; keep going
}
obj.DeleteLoadout(0)
obj.LoadLoadout(0) mustEqual None
}
"distinguish MAX subtype information" in {
val obj : Player = CreatePlayer()
val slot = obj.Slot(0)
slot.Equipment = None //only an unequipped slot can have its Equipment Size changed (Rifle -> Max)
Player.SuitSetup(obj, ExoSuitType.MAX)
obj.SaveLoadout("generic", 0) //weaponless
slot.Equipment = None
slot.Equipment = Tool(trhev_dualcycler)
obj.SaveLoadout("cycler", 1)
slot.Equipment = None
slot.Equipment = Tool(trhev_pounder)
obj.SaveLoadout("pounder", 2)
slot.Equipment = None
slot.Equipment = Tool(trhev_burster)
obj.SaveLoadout("burster", 3)
obj.LoadLoadout(0).get.Subtype mustEqual 0
obj.LoadLoadout(1).get.Subtype mustEqual 1
obj.LoadLoadout(2).get.Subtype mustEqual 2
obj.LoadLoadout(3).get.Subtype mustEqual 3
}
} }
} }

View file

@ -2,7 +2,7 @@
package objects package objects
import akka.actor.{Actor, ActorRef, Props} import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.Player import net.psforever.objects.{Avatar, Player}
import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition} import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
@ -25,7 +25,7 @@ class MountableControl1Test extends ActorTest() {
class MountableControl2Test extends ActorTest() { class MountableControl2Test extends ActorTest() {
"MountableControl" should { "MountableControl" should {
"let a player mount" in { "let a player mount" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj = new MountableTest.MountableTestObject val obj = new MountableTest.MountableTestObject
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable") obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
val msg = Mountable.TryMount(player, 0) val msg = Mountable.TryMount(player, 0)
@ -46,8 +46,8 @@ class MountableControl2Test extends ActorTest() {
class MountableControl3Test extends ActorTest() { class MountableControl3Test extends ActorTest() {
"MountableControl" should { "MountableControl" should {
"block a player from mounting" in { "block a player from mounting" in {
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj = new MountableTest.MountableTestObject val obj = new MountableTest.MountableTestObject
obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable") obj.Actor = system.actorOf(Props(classOf[MountableTest.MountableTestControl], obj), "mountable")
obj.Actor ! Mountable.TryMount(player1, 0) obj.Actor ! Mountable.TryMount(player1, 0)

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects package objects
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.definition.{ImplantDefinition, SimpleItemDefinition} import net.psforever.objects.definition.{ImplantDefinition, SimpleItemDefinition}
import net.psforever.objects.equipment.EquipmentSize import net.psforever.objects.equipment.EquipmentSize
@ -8,38 +9,53 @@ import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, ExoSuitType, ImplantType, PlanetSideEmpire} import net.psforever.types.{CharacterGender, ExoSuitType, ImplantType, PlanetSideEmpire}
import org.specs2.mutable._ import org.specs2.mutable._
import scala.util.Success
class PlayerTest extends Specification { class PlayerTest extends Specification {
def TestPlayer(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : Int) : Player = {
new Player(Avatar(name, faction, sex, head, voice))
}
"construct" in { "construct" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.isAlive mustEqual false obj.isAlive mustEqual false
obj.FacingYawUpper mustEqual 0
obj.Jumping mustEqual false
obj.Crouching mustEqual false
obj.Cloaked mustEqual false
obj.FacingYawUpper = 1.3f
obj.Jumping = true
obj.Crouching = true
obj.Cloaked = true
obj.FacingYawUpper mustEqual 1.3f
obj.Jumping mustEqual true
obj.Crouching mustEqual true
obj.Cloaked mustEqual true
} }
"different players" in { "different players" in {
(Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) == (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual true
(Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
(Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
(Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
(Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
(Player("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
Player("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
}
"become a backpack" in { (TestPlayer("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) TestPlayer("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) mustEqual false
obj.isAlive mustEqual false
obj.isBackpack mustEqual false (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
obj.Release TestPlayer("Chord", PlanetSideEmpire.NC, CharacterGender.Male, 0, 5)) mustEqual false
obj.isAlive mustEqual false
obj.isBackpack mustEqual true (TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Female, 0, 5)) mustEqual false
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 1, 5)) mustEqual false
(TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) ==
TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 6)) mustEqual false
} }
"(re)spawn" in { "(re)spawn" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.isAlive mustEqual false obj.isAlive mustEqual false
obj.Health mustEqual 0 obj.Health mustEqual 0
obj.Stamina mustEqual 0 obj.Stamina mustEqual 0
@ -54,32 +70,24 @@ class PlayerTest extends Specification {
obj.Armor mustEqual 50 obj.Armor mustEqual 50
} }
"set new maximum values (health, stamina)" in { "will not (re)spawn if not dead" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.MaxHealth mustEqual 100
obj.MaxStamina mustEqual 100
obj.MaxHealth = 123
obj.MaxStamina = 456
obj.Spawn obj.Spawn
obj.Health mustEqual 123 obj.Health mustEqual 100
obj.Stamina mustEqual 456 obj.Armor mustEqual 50
obj.isAlive mustEqual true
obj.Health = 10
obj.Armor = 10
obj.Health mustEqual 10
obj.Armor mustEqual 10
obj.Spawn
obj.Health mustEqual 10
obj.Armor mustEqual 10
} }
"init (Standard Exo-Suit)" in { "can die" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.ExoSuit mustEqual ExoSuitType.Standard
obj.Slot(0).Size mustEqual EquipmentSize.Pistol
obj.Slot(1).Size mustEqual EquipmentSize.Blocked
obj.Slot(2).Size mustEqual EquipmentSize.Rifle
obj.Slot(3).Size mustEqual EquipmentSize.Blocked
obj.Slot(4).Size mustEqual EquipmentSize.Melee
obj.Inventory.Width mustEqual 9
obj.Inventory.Height mustEqual 6
obj.Inventory.Offset mustEqual 6
}
"die" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Spawn obj.Spawn
obj.Armor = 35 //50 -> 35 obj.Armor = 35 //50 -> 35
obj.isAlive mustEqual true obj.isAlive mustEqual true
@ -93,9 +101,80 @@ class PlayerTest extends Specification {
obj.Armor mustEqual 35 obj.Armor mustEqual 35
} }
"can not become a backpack if alive" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Spawn
obj.isAlive mustEqual true
obj.isBackpack mustEqual false
obj.Release
obj.isAlive mustEqual true
obj.isBackpack mustEqual false
}
"can become a backpack" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.isAlive mustEqual false
obj.isBackpack mustEqual false
obj.Release
obj.isAlive mustEqual false
obj.isBackpack mustEqual true
}
"set new maximum values (health, stamina)" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.MaxHealth mustEqual 100
obj.MaxStamina mustEqual 100
obj.MaxHealth = 123
obj.MaxStamina = 456
obj.Spawn
obj.Health mustEqual 123
obj.Stamina mustEqual 456
}
"set new values (health, armor, stamina) but only when alive" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Health = 23
obj.Armor = 34
obj.Stamina = 45
obj.Health mustEqual 0
obj.Armor mustEqual 0
obj.Stamina mustEqual 0
obj.Spawn
obj.Health mustEqual obj.MaxHealth
obj.Armor mustEqual obj.MaxArmor
obj.Stamina mustEqual obj.MaxStamina
obj.Health = 23
obj.Armor = 34
obj.Stamina = 45
obj.Health mustEqual 23
obj.Armor mustEqual 34
obj.Stamina mustEqual 45
}
"has visible slots" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.VisibleSlots mustEqual Set(0,1,2,3,4)
obj.ExoSuit = ExoSuitType.MAX
obj.VisibleSlots mustEqual Set(0)
}
"init (Standard Exo-Suit)" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.ExoSuit mustEqual ExoSuitType.Standard
obj.Slot(0).Size mustEqual EquipmentSize.Pistol
obj.Slot(1).Size mustEqual EquipmentSize.Blocked
obj.Slot(2).Size mustEqual EquipmentSize.Rifle
obj.Slot(3).Size mustEqual EquipmentSize.Blocked
obj.Slot(4).Size mustEqual EquipmentSize.Melee
obj.Inventory.Width mustEqual 9
obj.Inventory.Height mustEqual 6
obj.Inventory.Offset mustEqual 6
}
"draw equipped holsters only" in { "draw equipped holsters only" in {
val wep = SimpleItem(SimpleItemDefinition(149)) val wep = SimpleItem(SimpleItemDefinition(149))
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Slot(1).Size = EquipmentSize.Pistol obj.Slot(1).Size = EquipmentSize.Pistol
obj.Slot(1).Equipment = wep obj.Slot(1).Equipment = wep
obj.DrawnSlot mustEqual Player.HandsDownSlot obj.DrawnSlot mustEqual Player.HandsDownSlot
@ -108,7 +187,7 @@ class PlayerTest extends Specification {
"remember the last drawn holster" in { "remember the last drawn holster" in {
val wep1 = SimpleItem(SimpleItemDefinition(149)) val wep1 = SimpleItem(SimpleItemDefinition(149))
val wep2 = SimpleItem(SimpleItemDefinition(149)) val wep2 = SimpleItem(SimpleItemDefinition(149))
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Slot(0).Size = EquipmentSize.Pistol obj.Slot(0).Size = EquipmentSize.Pistol
obj.Slot(0).Equipment = wep1 obj.Slot(0).Equipment = wep1
obj.Slot(1).Size = EquipmentSize.Pistol obj.Slot(1).Size = EquipmentSize.Pistol
@ -147,7 +226,7 @@ class PlayerTest extends Specification {
"hold something in their free hand" in { "hold something in their free hand" in {
val wep = SimpleItem(SimpleItemDefinition(149)) val wep = SimpleItem(SimpleItemDefinition(149))
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Slot(Player.FreeHandSlot).Equipment = wep obj.Slot(Player.FreeHandSlot).Equipment = wep
obj.Slot(Player.FreeHandSlot).Equipment.get.Definition.ObjectId mustEqual 149 obj.Slot(Player.FreeHandSlot).Equipment.get.Definition.ObjectId mustEqual 149
@ -155,15 +234,15 @@ class PlayerTest extends Specification {
"provide an invalid hand that can not hold anything" in { "provide an invalid hand that can not hold anything" in {
val wep = SimpleItem(SimpleItemDefinition(149)) val wep = SimpleItem(SimpleItemDefinition(149))
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Slot(-1).Equipment = wep obj.Slot(-1).Equipment = wep
obj.Slot(-1).Equipment mustEqual None obj.Slot(-1).Equipment mustEqual None
} }
"search for the smallest available slot in which to satore equipment" in { "search for the smallest available slot in which to store equipment" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Inventory.Resize(3,3) obj.Inventory.Resize(3,3) //fits one item
obj.Fit(Tool(GlobalDefinitions.beamer)) mustEqual Some(0) obj.Fit(Tool(GlobalDefinitions.beamer)) mustEqual Some(0)
@ -176,46 +255,180 @@ class PlayerTest extends Specification {
obj.Slot(6).Equipment = ammo obj.Slot(6).Equipment = ammo
obj.Fit(ammo2) mustEqual Some(Player.FreeHandSlot) obj.Fit(ammo2) mustEqual Some(Player.FreeHandSlot)
obj.Slot(Player.FreeHandSlot).Equipment = ammo2 obj.Slot(Player.FreeHandSlot).Equipment = ammo2
obj.Fit(ammo2) mustEqual None obj.Fit(ammo3) mustEqual None
} }
"install an implant" in { "can use their free hand to hold things" in {
val testplant : ImplantDefinition = ImplantDefinition(1) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val ammo = AmmoBox(GlobalDefinitions.bullet_9mm)
obj.Implants(0).Unlocked = true obj.FreeHand.Equipment mustEqual None
obj.InstallImplant(testplant) mustEqual Some(0)
obj.Implants.find({p => p.Implant == ImplantType(1)}) match { //find the installed implant obj.FreeHand.Equipment = ammo
case Some(slot) => obj.FreeHand.Equipment mustEqual Some(ammo)
slot.Installed mustEqual Some(testplant) }
"can access the player's locker-space" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Slot(5).Equipment.get.isInstanceOf[LockerContainer] mustEqual true
}
"can find equipment" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Slot(0).Equipment = {
val item = Tool(beamer)
item.GUID = PlanetSideGUID(1)
item
}
obj.Slot(4).Equipment = {
val item = Tool(forceblade)
item.GUID = PlanetSideGUID(2)
item
}
obj.Slot(6).Equipment = {
val item = ConstructionItem(ace)
item.GUID = PlanetSideGUID(3)
item
}
obj.Locker.Slot(6).Equipment = {
val item = Kit(medkit)
item.GUID = PlanetSideGUID(4)
item
}
obj.FreeHand.Equipment = {
val item = SimpleItem(remote_electronics_kit)
item.GUID = PlanetSideGUID(5)
item
}
obj.Find(PlanetSideGUID(1)) mustEqual Some(0) //holsters
obj.Find(PlanetSideGUID(2)) mustEqual Some(4) //holsters, melee
obj.Find(PlanetSideGUID(3)) mustEqual Some(6) //inventory
obj.Find(PlanetSideGUID(4)) mustEqual Some(Player.LockerSlot) //locker-space
obj.Find(PlanetSideGUID(5)) mustEqual Some(Player.FreeHandSlot) //free hand
obj.Find(PlanetSideGUID(6)) mustEqual None //not here
}
"does equipment collision checking (are we already holding something there?)" in {
val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val item1 = Tool(beamer)
val item2 = Kit(medkit)
val item3 = AmmoBox(GlobalDefinitions.bullet_9mm)
obj.Slot(0).Equipment = item1
obj.Slot(6).Equipment = item2
obj.FreeHand.Equipment = item3
obj.Collisions(0, 1, 1) match {
case Success(List(item)) =>
item.obj mustEqual item1
item.start mustEqual 0
case _ => case _ =>
ko ko
} } //holsters
ok
obj.Collisions(1, 1, 1) match {
case Success(List()) => ;
case _ =>
ko
} //holsters, nothing
obj.Collisions(6, 1, 1)match {
case Success(List(item)) =>
item.obj mustEqual item2
item.start mustEqual 6
case _ =>
ko
} //inventory
obj.Collisions(Player.FreeHandSlot, 1, 1)match {
case Success(List(item)) =>
item.obj mustEqual item3
item.start mustEqual Player.FreeHandSlot
case _ =>
ko
} //free hand
} }
"can not install the same type of implant twice" in { "battle experience point values of the avatar" in {
val testplant1 : ImplantDefinition = ImplantDefinition(1) val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val testplant2 : ImplantDefinition = ImplantDefinition(1) val player = Player(avatar)
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Implants(0).Unlocked = true player.BEP mustEqual avatar.BEP
obj.Implants(1).Unlocked = true avatar.BEP = 1002
obj.InstallImplant(testplant1) mustEqual Some(0) player.BEP mustEqual avatar.BEP
obj.InstallImplant(testplant2) mustEqual Some(1)
} }
"uninstall implants" in { "command experience point values of the avatar" in {
val testplant : ImplantDefinition = ImplantDefinition(1) val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val player = Player(avatar)
obj.Implants(0).Unlocked = true
obj.InstallImplant(testplant) mustEqual Some(0)
obj.Implants(0).Installed mustEqual Some(testplant)
obj.UninstallImplant(testplant.Type) player.CEP mustEqual avatar.CEP
obj.Implants(0).Installed mustEqual None avatar.CEP = 1002
player.CEP mustEqual avatar.CEP
}
"can get a quick summary of implant slots (default)" in {
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
player.Implants mustEqual Array.empty
}
"can get a quick summary of implant slots (two unlocked, one installed)" in {
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
val temp = new ImplantDefinition(1)
avatar.Implants(0).Unlocked = true
avatar.InstallImplant(new ImplantDefinition(1))
avatar.Implants(1).Unlocked = true
avatar.InstallImplant(new ImplantDefinition(2))
avatar.UninstallImplant(temp.Type)
val list = player.Implants
//slot 0
val (implant1, init1, active1) = list(0)
implant1 mustEqual ImplantType.None
init1 mustEqual -1
active1 mustEqual false
//slot 1
val (implant2, init2, active2) = list(1)
implant2 mustEqual ImplantType(2)
init2 mustEqual 0
active2 mustEqual false
}
"can get a quick summary of implant slots (all unlocked, first two installed)" in {
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
avatar.Implants(0).Unlocked = true
avatar.InstallImplant(new ImplantDefinition(1))
avatar.Implants(0).Initialized = true
avatar.Implants(0).Active = true
avatar.Implants(1).Unlocked = true
avatar.InstallImplant(new ImplantDefinition(2))
avatar.Implants(1).Initialized = true
avatar.Implants(1).Active = false
avatar.Implants(2).Unlocked = true
val list = player.Implants
//slot 0
val (implant1, init1, active1) = list(0)
implant1 mustEqual ImplantType(1)
init1 mustEqual 0
active1 mustEqual true
//slot 1
val (implant2, init2, active2) = list(1)
implant2 mustEqual ImplantType(2)
init2 mustEqual 0
active2 mustEqual false
//slot 2
val (implant3, init3, active3) = list(2)
implant3 mustEqual ImplantType.None
init3 mustEqual -1
active3 mustEqual false
} }
"seat in a vehicle" in { "seat in a vehicle" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.VehicleSeated mustEqual None obj.VehicleSeated mustEqual None
obj.VehicleSeated = PlanetSideGUID(65) obj.VehicleSeated = PlanetSideGUID(65)
obj.VehicleSeated mustEqual Some(PlanetSideGUID(65)) obj.VehicleSeated mustEqual Some(PlanetSideGUID(65))
@ -224,7 +437,7 @@ class PlayerTest extends Specification {
} }
"own in a vehicle" in { "own in a vehicle" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.VehicleOwned mustEqual None obj.VehicleOwned mustEqual None
obj.VehicleOwned = PlanetSideGUID(65) obj.VehicleOwned = PlanetSideGUID(65)
obj.VehicleOwned mustEqual Some(PlanetSideGUID(65)) obj.VehicleOwned mustEqual Some(PlanetSideGUID(65))
@ -233,27 +446,18 @@ class PlayerTest extends Specification {
} }
"remember what zone he is in" in { "remember what zone he is in" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Continent mustEqual "home2" obj.Continent mustEqual "home2"
obj.Continent = "ugd01" obj.Continent = "ugd01"
obj.Continent mustEqual "ugd01" obj.Continent mustEqual "ugd01"
} }
"administrate" in { "toString" in {
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) val obj = TestPlayer("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
obj.Admin mustEqual false obj.toString mustEqual "TR Chord 0/100 0/50"
Player.Administrate(obj, true)
obj.Admin mustEqual true
Player.Administrate(obj, false)
obj.Admin mustEqual false
}
"spectate" in { obj.GUID = PlanetSideGUID(455)
val obj = new Player("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5) obj.Continent = "z3"
obj.Spectator mustEqual false obj.toString mustEqual "TR Chord z3-455 0/100 0/50"
Player.Spectate(obj, true)
obj.Spectator mustEqual true
Player.Spectate(obj, false)
obj.Spectator mustEqual false
} }
} }

View file

@ -5,7 +5,7 @@ import akka.actor.{Actor, ActorContext, Props}
import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.NumberPoolHub
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.objects.serverobject.ServerObjectBuilder import net.psforever.objects.serverobject.ServerObjectBuilder
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate} import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3 import net.psforever.types.Vector3
@ -14,7 +14,7 @@ import scala.concurrent.duration.Duration
class BuildingBuilderTest extends ActorTest { class BuildingBuilderTest extends ActorTest {
"Building object" should { "Building object" should {
"build" in { "build" in {
val structure : (Int,Zone,ActorContext)=>Building = Building.Structure val structure : (Int,Zone,ActorContext)=>Building = Building.Structure(StructureType.Building)
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, 10, Zone.Nowhere), "building") val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, 10, Zone.Nowhere), "building")
actor ! "!" actor ! "!"
@ -169,6 +169,26 @@ class LockerObjectBuilderTest extends ActorTest {
} }
} }
class SpawnTubeObjectBuilderTest extends ActorTest {
import net.psforever.objects.serverobject.tube.SpawnTube
"LockerObjectBuilder" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90))), hub), "spawn-tube")
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[SpawnTube])
assert(reply.asInstanceOf[SpawnTube].HasGUID)
assert(reply.asInstanceOf[SpawnTube].GUID == PlanetSideGUID(1))
assert(reply.asInstanceOf[SpawnTube].Position == Vector3(3980.4062f, 4267.3047f, 257.5625f))
assert(reply.asInstanceOf[SpawnTube].Orientation == Vector3(0, 0, 90))
assert(reply == hub(1).get)
}
}
}
object ServerObjectBuilderTest { object ServerObjectBuilderTest {
import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.guid.source.LimitedNumberSource
def NumberPoolHub : NumberPoolHub = { def NumberPoolHub : NumberPoolHub = {

View file

@ -0,0 +1,59 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{ActorRef, Props}
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.tube.{SpawnTube, SpawnTubeControl, SpawnTubeDefinition}
import org.specs2.mutable.Specification
class SpawnTubeTest extends Specification {
"SpawnTubeDefinition" should {
"define (ams_respawn_tube)" in {
val obj = new SpawnTubeDefinition(49)
obj.ObjectId mustEqual 49
obj.Name mustEqual "ams_respawn_tube"
}
"define (respawn_tube)" in {
val obj = new SpawnTubeDefinition(732)
obj.ObjectId mustEqual 732
obj.Name mustEqual "respawn_tube"
}
"define (respawn_tube_tower)" in {
val obj = new SpawnTubeDefinition(733)
obj.ObjectId mustEqual 733
obj.Name mustEqual "respawn_tube_tower"
}
"define (invalid)" in {
var id : Int = (math.random * Int.MaxValue).toInt
if(id == 49 || id == 733) {
id += 1
}
else if(id == 732) {
id += 2
}
new SpawnTubeDefinition(id) must throwA[IllegalArgumentException]
}
}
"SpawnTube" should {
"construct" in {
val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube)
obj.Actor mustEqual ActorRef.noSender
obj.Definition mustEqual GlobalDefinitions.ams_respawn_tube
}
}
}
class SpawnTubeControlTest extends ActorTest() {
"SpawnTubeControl" should {
"construct" in {
val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube)
obj.Actor = system.actorOf(Props(classOf[SpawnTubeControl], obj), "spawn-tube")
assert(obj.Actor != ActorRef.noSender)
}
}
}

View file

@ -3,10 +3,10 @@ package objects
import akka.actor.{ActorRef, ActorSystem, Props} import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.vehicles.VehicleControl import net.psforever.objects.vehicles.VehicleControl
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle} import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3} import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
@ -109,8 +109,8 @@ object VehicleSpawnPadControl {
def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, VehicleSpawnPad) = { def SetUpAgents(faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, VehicleSpawnPad) = {
val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad) val pad = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), "test-pad") pad.Actor = system.actorOf(Props(classOf[VehicleSpawnControl], pad), "test-pad")
pad.Owner = new Building(0, Zone.Nowhere) pad.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
pad.Owner.Faction = faction pad.Owner.Faction = faction
(Player("test", faction, CharacterGender.Male, 0, 0), pad) (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), pad)
} }
} }

View file

@ -2,17 +2,18 @@
package objects package objects
import akka.actor.Props import akka.actor.Props
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle} import net.psforever.objects._
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition} import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles._ import net.psforever.objects.vehicles._
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire} import net.psforever.types.ExoSuitType
import org.specs2.mutable._ import org.specs2.mutable._
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
class VehicleTest extends Specification { class VehicleTest extends Specification {
import VehicleTest._
"SeatDefinition" should { "SeatDefinition" should {
val seat = new SeatDefinition val seat = new SeatDefinition
@ -73,7 +74,7 @@ class VehicleTest extends Specification {
val seat = new Seat(seat_def) val seat = new Seat(seat_def)
seat.Occupant.isDefined mustEqual false seat.Occupant.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(avatar1)
player1.ExoSuit = ExoSuitType.MAX player1.ExoSuit = ExoSuitType.MAX
seat.Occupant = player1 seat.Occupant = player1
seat.Occupant.isDefined mustEqual true seat.Occupant.isDefined mustEqual true
@ -84,13 +85,13 @@ class VehicleTest extends Specification {
val seat = new Seat(seat_def) val seat = new Seat(seat_def)
seat.Occupant.isDefined mustEqual false seat.Occupant.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(avatar1)
player1.ExoSuit = ExoSuitType.MAX player1.ExoSuit = ExoSuitType.MAX
seat.Occupant = player1 seat.Occupant = player1
seat.Occupant.isDefined mustEqual true seat.Occupant.isDefined mustEqual true
seat.Occupant.contains(player1) mustEqual true seat.Occupant.contains(player1) mustEqual true
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player2 = Player(avatar1)
player2.ExoSuit = ExoSuitType.MAX player2.ExoSuit = ExoSuitType.MAX
seat.Occupant = player2 seat.Occupant = player2
seat.Occupant.isDefined mustEqual true seat.Occupant.isDefined mustEqual true
@ -101,13 +102,13 @@ class VehicleTest extends Specification {
val seat = new Seat(seat_def) val seat = new Seat(seat_def)
seat.Occupant.isDefined mustEqual false seat.Occupant.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(avatar1)
player1.ExoSuit = ExoSuitType.MAX player1.ExoSuit = ExoSuitType.MAX
seat.Occupant = player1 seat.Occupant = player1
seat.Occupant.isDefined mustEqual true seat.Occupant.isDefined mustEqual true
seat.Occupant.contains(player1) mustEqual true seat.Occupant.contains(player1) mustEqual true
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player2 = Player(avatar2)
player2.ExoSuit = ExoSuitType.MAX player2.ExoSuit = ExoSuitType.MAX
seat.Occupant = player2 seat.Occupant = player2
seat.Occupant.isDefined mustEqual true seat.Occupant.isDefined mustEqual true
@ -160,7 +161,7 @@ class VehicleTest extends Specification {
val fury_vehicle = Vehicle(GlobalDefinitions.fury) val fury_vehicle = Vehicle(GlobalDefinitions.fury)
fury_vehicle.Owner.isDefined mustEqual false fury_vehicle.Owner.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(avatar1)
player1.GUID = PlanetSideGUID(1) player1.GUID = PlanetSideGUID(1)
fury_vehicle.Owner = player1 fury_vehicle.Owner = player1
fury_vehicle.Owner.isDefined mustEqual true fury_vehicle.Owner.isDefined mustEqual true
@ -171,13 +172,13 @@ class VehicleTest extends Specification {
val fury_vehicle = Vehicle(GlobalDefinitions.fury) val fury_vehicle = Vehicle(GlobalDefinitions.fury)
fury_vehicle.Owner.isDefined mustEqual false fury_vehicle.Owner.isDefined mustEqual false
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(avatar1)
player1.GUID = PlanetSideGUID(1) player1.GUID = PlanetSideGUID(1)
fury_vehicle.Owner = player1 fury_vehicle.Owner = player1
fury_vehicle.Owner.isDefined mustEqual true fury_vehicle.Owner.isDefined mustEqual true
fury_vehicle.Owner.contains(PlanetSideGUID(1)) mustEqual true fury_vehicle.Owner.contains(PlanetSideGUID(1)) mustEqual true
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player2 = Player(avatar2)
player2.GUID = PlanetSideGUID(2) player2.GUID = PlanetSideGUID(2)
fury_vehicle.Owner = player2 fury_vehicle.Owner = player2
fury_vehicle.Owner.isDefined mustEqual true fury_vehicle.Owner.isDefined mustEqual true
@ -234,9 +235,9 @@ class VehicleTest extends Specification {
"can find a passenger in a seat" in { "can find a passenger in a seat" in {
val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(avatar1)
player1.GUID = PlanetSideGUID(1) player1.GUID = PlanetSideGUID(1)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player2 = Player(avatar2)
player2.GUID = PlanetSideGUID(2) player2.GUID = PlanetSideGUID(2)
harasser_vehicle.Seat(0).get.Occupant = player1 //don't worry about ownership for now harasser_vehicle.Seat(0).get.Occupant = player1 //don't worry about ownership for now
harasser_vehicle.Seat(1).get.Occupant = player2 harasser_vehicle.Seat(1).get.Occupant = player2
@ -282,9 +283,9 @@ class VehicleTest extends Specification {
class VehicleControl1Test extends ActorTest { class VehicleControl1Test extends ActorTest {
"Vehicle Control" should { "Vehicle Control" should {
"deactivate and stop handling mount messages" in { "deactivate and stop handling mount messages" in {
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(VehicleTest.avatar1)
player1.GUID = PlanetSideGUID(1) player1.GUID = PlanetSideGUID(1)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player2 = Player(VehicleTest.avatar2)
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
vehicle.GUID = PlanetSideGUID(3) vehicle.GUID = PlanetSideGUID(3)
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test") vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
@ -303,9 +304,9 @@ class VehicleControl1Test extends ActorTest {
class VehicleControl2Test extends ActorTest { class VehicleControl2Test extends ActorTest {
"Vehicle Control" should { "Vehicle Control" should {
"reactivate and resume handling mount messages" in { "reactivate and resume handling mount messages" in {
val player1 = Player("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player1 = Player(VehicleTest.avatar1)
player1.GUID = PlanetSideGUID(1) player1.GUID = PlanetSideGUID(1)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player2 = Player(VehicleTest.avatar2)
player2.GUID = PlanetSideGUID(2) player2.GUID = PlanetSideGUID(2)
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy) val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
vehicle.GUID = PlanetSideGUID(3) vehicle.GUID = PlanetSideGUID(3)
@ -324,3 +325,10 @@ class VehicleControl2Test extends ActorTest {
} }
} }
} }
object VehicleTest {
import net.psforever.objects.Avatar
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
val avatar1 = Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
val avatar2 = Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
}

View file

@ -1,16 +1,24 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects package objects
import akka.actor.{ActorContext, ActorRef} import java.util.concurrent.atomic.AtomicInteger
import akka.actor.{Actor, ActorContext, ActorRef, Props}
import net.psforever.objects.entity.IdentifiableEntity import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder} import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.{GlobalDefinitions, Vehicle} import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
import net.psforever.objects._
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
import scala.concurrent.duration.Duration
class ZoneTest extends Specification { class ZoneTest extends Specification {
def test(a: Int, b : Zone, c : ActorContext) : Building = { Building.NoBuilding } def test(a: Int, b : Zone, c : ActorContext) : Building = { Building.NoBuilding }
@ -82,6 +90,8 @@ class ZoneTest extends Specification {
//zone also has a unique identifier system but it can't be accessed without its the Actor GUID being initialized //zone also has a unique identifier system but it can't be accessed without its the Actor GUID being initialized
zone.EquipmentOnGround mustEqual List.empty[Equipment] zone.EquipmentOnGround mustEqual List.empty[Equipment]
zone.Vehicles mustEqual List.empty[Vehicle] zone.Vehicles mustEqual List.empty[Vehicle]
zone.Players mustEqual List.empty[Player]
zone.Corpses mustEqual List.empty[Player]
} }
"can have its unique identifier system changed if no objects were added to it" in { "can have its unique identifier system changed if no objects were added to it" in {
@ -124,3 +134,422 @@ class ZoneTest extends Specification {
} }
} }
} }
class ZoneActorTest extends ActorTest {
"Zone" should {
"have an Actor" in {
val zone = new Zone("test", new ZoneMap("map6"), 1)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-actor")
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Actor != ActorRef.noSender)
}
"set up spawn groups based on buildings" in {
val map6 = new ZoneMap("map6") {
LocalBuilding(1, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
LocalObject(1, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
LocalObject(2, Terminal.Constructor(GlobalDefinitions.dropship_vehicle_terminal))
LocalObject(3, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(1, 1)
ObjectToBuilding(2, 1)
ObjectToBuilding(3, 1)
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building)))
LocalObject(7, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(7, 2)
LocalBuilding(3, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
LocalObject(4, Terminal.Constructor(GlobalDefinitions.dropship_vehicle_terminal))
LocalObject(5, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
LocalObject(6, Terminal.Constructor(GlobalDefinitions.dropship_vehicle_terminal))
ObjectToBuilding(4, 3)
ObjectToBuilding(5, 3)
ObjectToBuilding(6, 3)
}
val zone = new Zone("test", map6, 1)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-init")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
val groups = zone.SpawnGroups()
assert(groups.size == 2)
zone.SpawnGroups().foreach({ case(building, tubes) =>
if(building.Id == 1) {
val building1 = zone.SpawnGroups(building)
assert(tubes.length == 2)
assert(tubes.head == building1.head)
assert(tubes.head.GUID == PlanetSideGUID(1))
assert(tubes(1) == building1(1))
assert(tubes(1).GUID == PlanetSideGUID(3))
}
else if(building.Id == 3) {
val building2 = zone.SpawnGroups(building)
assert(tubes.length == 1)
assert(tubes.head == building2.head)
assert(tubes.head.GUID == PlanetSideGUID(5))
}
else {
assert(false)
}
})
}
"select spawn points based on the position of the player in reference to buildings" in {
val map6 = new ZoneMap("map6") {
LocalBuilding(1, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
LocalObject(1, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(1, 1)
LocalBuilding(3, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(4,4,4))))
LocalObject(5, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(5, 3)
}
val zone = new Zone("test", map6, 1)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-spawn")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, 5))
val bldg1 = zone.Building(1).get
val bldg3 = zone.Building(3).get
player.Position = Vector3(1,1,1) //closer to bldg1
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
val reply1 = receiveOne(Duration.create(200, "ms"))
assert(reply1.isInstanceOf[Zone.Lattice.SpawnPoint])
assert(reply1.asInstanceOf[Zone.Lattice.SpawnPoint].zone_id == "test")
assert(reply1.asInstanceOf[Zone.Lattice.SpawnPoint].building == bldg1)
assert(reply1.asInstanceOf[Zone.Lattice.SpawnPoint].spawn_tube.Owner == bldg1)
player.Position = Vector3(3,3,3) //closer to bldg3
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
val reply3 = receiveOne(Duration.create(200, "ms"))
assert(reply3.isInstanceOf[Zone.Lattice.SpawnPoint])
assert(reply3.asInstanceOf[Zone.Lattice.SpawnPoint].zone_id == "test")
assert(reply3.asInstanceOf[Zone.Lattice.SpawnPoint].building == bldg3)
assert(reply3.asInstanceOf[Zone.Lattice.SpawnPoint].spawn_tube.Owner == bldg3)
}
"will report if no spawn points have been found in a zone" in {
val map6 = new ZoneMap("map6") {
LocalBuilding(1, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
LocalBuilding(3, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(4,4,4))))
LocalObject(5, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(5, 3)
}
val zone = new Zone("test", map6, 1)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-no-spawn")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
val player = Player(Avatar("Chord", PlanetSideEmpire.NEUTRAL, CharacterGender.Male, 0, 5))
zone.Actor ! Zone.Lattice.RequestSpawnPoint(1, player, 7)
val reply = receiveOne(Duration.create(200, "ms"))
assert(reply.isInstanceOf[Zone.Lattice.NoValidSpawnPoint])
assert(reply.asInstanceOf[Zone.Lattice.NoValidSpawnPoint].zone_number == 1)
assert(reply.asInstanceOf[Zone.Lattice.NoValidSpawnPoint].spawn_group.contains(7))
}
}
}
class ZonePopulationTest extends ActorTest {
val testNum = new AtomicInteger(1)
def TestName : String = s"test${testNum.getAndIncrement()}"
"ZonePopulationActor" should {
"add new user to zones" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
}
"remove user from zones" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
zone.Population ! Zone.Population.Leave(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.isEmpty)
}
"associate user with a character" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Spawn(avatar, player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player)
}
"disassociate character from a user" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player)
zone.Population ! Zone.Population.Release(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
}
"user tries to Leave, but still has an associated character" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(500, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player)
zone.Population ! Zone.Population.Leave(avatar)
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
assert(reply.isInstanceOf[Zone.Population.PlayerHasLeft])
assert(reply.asInstanceOf[Zone.Population.PlayerHasLeft].zone == zone)
assert(reply.asInstanceOf[Zone.Population.PlayerHasLeft].player.contains(player))
}
"user tries to Spawn a character, but an associated character already exists" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player1 = Player(avatar)
val player2 = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player1)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player1)
zone.Population ! Zone.Population.Spawn(avatar, player2)
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.size == 1)
assert(zone.LivePlayers.head == player1)
assert(reply.isInstanceOf[Zone.Population.PlayerAlreadySpawned])
assert(reply.asInstanceOf[Zone.Population.PlayerAlreadySpawned].player == player1)
}
"user tries to Spawn a character, but did not Join first" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Spawn(avatar, player)
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
assert(reply.isInstanceOf[Zone.Population.PlayerCanNotSpawn])
assert(reply.asInstanceOf[Zone.Population.PlayerCanNotSpawn].zone == zone)
assert(reply.asInstanceOf[Zone.Population.PlayerCanNotSpawn].player == player)
}
"user tries to Release a character, but did not Spawn a character first" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
zone.Population ! Zone.Population.Release(avatar)
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.Players.size == 1)
assert(zone.Players.head == avatar)
assert(zone.LivePlayers.isEmpty)
assert(reply.isInstanceOf[Zone.Population.PlayerHasLeft])
assert(reply.asInstanceOf[Zone.Population.PlayerHasLeft].zone == zone)
assert(reply.asInstanceOf[Zone.Population.PlayerHasLeft].player.isEmpty)
}
"user adds character to list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.size == 1)
assert(zone.Corpses.head == player)
}
"user removes character from the list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Corpse.Add(player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.size == 1)
assert(zone.Corpses.head == player)
zone.Population ! Zone.Corpse.Remove(player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.isEmpty)
}
"user removes THE CORRECT character from the list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player1 = Player(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player1.Release
val player2 = Player(Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player2.Release
val player3 = Player(Avatar("Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
player3.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Corpse.Add(player1)
zone.Population ! Zone.Corpse.Add(player2)
zone.Population ! Zone.Corpse.Add(player3)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.size == 3)
assert(zone.Corpses.head == player1)
assert(zone.Corpses(1) == player2)
assert(zone.Corpses(2) == player3)
zone.Population ! Zone.Corpse.Remove(player2)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.size == 2)
assert(zone.Corpses.head == player1)
assert(zone.Corpses(1) == player3)
}
"user tries to add character to list of retired characters, but is not in correct state" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
//player.Release !!important
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "testC") ! "!"
receiveOne(Duration.create(500, "ms")) //consume
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(player)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Corpses.isEmpty)
}
}
}
class ZoneGroundTest extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
item.GUID = PlanetSideGUID(10)
"ZoneGroundActor" should {
"drop item on ground" in {
val zone = new Zone("test", new ZoneMap(""), 0)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-item-test") ! "!"
receiveOne(Duration.create(200, "ms")) //consume
assert(zone.EquipmentOnGround.isEmpty)
assert(item.Position == Vector3.Zero)
assert(item.Orientation == Vector3.Zero)
zone.Ground ! Zone.DropItemOnGround(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
assert(item.Position == Vector3(1.1f, 2.2f, 3.3f))
assert(item.Orientation == Vector3(4.4f, 5.5f, 6.6f))
}
"get item from ground (success)" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-good") ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(10))
val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround.isEmpty)
assert(reply.isInstanceOf[Zone.ItemFromGround])
assert(reply.asInstanceOf[Zone.ItemFromGround].player == player)
assert(reply.asInstanceOf[Zone.ItemFromGround].item == item)
}
"get item from ground (failure)" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-fail") ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item))
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(11)) //wrong guid
expectNoMsg(Duration.create(500, "ms"))
}
}
}
object ZoneTest {
class ZoneInitActor(zone : Zone) extends Actor {
def receive : Receive = {
case "!" =>
zone.Init(context)
sender ! "!"
case _ => ;
}
}
}

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, PlanetSideEmpire} import net.psforever.types.{CharacterGender, PlanetSideEmpire}
@ -10,7 +9,7 @@ import objects.ActorTest
class GUIDTaskRegister5Test extends ActorTest() { class GUIDTaskRegister5Test extends ActorTest() {
"RegisterAvatar" in { "RegisterAvatar" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj_wep = Tool(GlobalDefinitions.beamer) val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell) val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)

View file

@ -0,0 +1,38 @@
// Copyright (c) 2017 PSForever
package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskRegister6Test extends ActorTest() {
"RegisterPlayer" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj.Slot(6).Equipment = obj_inv_ammo
val obj_locker = obj.Slot(5).Equipment.get
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_wep_ammo.HasGUID)
assert(!obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(!obj_locker_ammo.HasGUID)
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterPlayer(obj)(uns)))
probe.expectMsg(scala.util.Success)
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_wep_ammo.HasGUID)
assert(obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(!obj_locker_ammo.HasGUID)
}
}

View file

@ -9,7 +9,7 @@ import objects.ActorTest
class GUIDTaskUnregister5Test extends ActorTest() { class GUIDTaskUnregister5Test extends ActorTest() {
"UnregisterAvatar" in { "UnregisterAvatar" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj_wep = Tool(GlobalDefinitions.beamer) val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell) val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)

View file

@ -0,0 +1,44 @@
// Copyright (c) 2017 PSForever
package objects.guidtask
import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskUnregister6Test extends ActorTest() {
"UnregisterPlayer" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj.Slot(6).Equipment = obj_inv_ammo
val obj_locker = obj.Slot(5).Equipment.get
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo
guid.register(obj, "dynamic")
guid.register(obj_wep, "dynamic")
guid.register(obj_wep_ammo, "dynamic")
guid.register(obj_inv_ammo, "dynamic")
guid.register(obj_locker, "dynamic")
guid.register(obj_locker_ammo, "dynamic")
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_wep_ammo.HasGUID)
assert(obj_inv_ammo.HasGUID)
assert(obj_locker.HasGUID)
assert(obj_locker_ammo.HasGUID)
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterPlayer(obj)(uns)))
probe.expectMsg(scala.util.Success)
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_wep_ammo.HasGUID)
assert(!obj_inv_ammo.HasGUID)
assert(obj_locker.HasGUID)
assert(obj_locker_ammo.HasGUID)
}
}

View file

@ -275,6 +275,40 @@ class UniqueNumberSystemTest8 extends ActorTest() {
} }
} }
class UniqueNumberSystemTest9 extends ActorTest() {
class EntityTestClass extends IdentifiableEntity
"UniqueNumberSystem" should {
"Failures (manually walking the failure cases)" in {
val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
val uns = system.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)), "uns")
val excp = new Exception("EXCEPTION MESSAGE")
expectNoMsg(Duration.create(200, "ms"))
//GiveNumber
uns ! NumberPoolActor.GiveNumber(1001, Some("test")) //no task associated with id="test"
uns ! NumberPoolActor.GiveNumber(1000, Some("test")) //no task associated with id="test" and number is not pooled
uns ! NumberPoolActor.GiveNumber(1000, Some(1)) //the task could theoretically exist, but does not
//NoNumber
uns ! NumberPoolActor.NoNumber(excp, Some(1))
uns ! NumberPoolActor.NoNumber(excp, None)
uns ! NumberPoolActor.NoNumber(excp, Some("test"))
//ReturnNumberResult A
uns ! NumberPoolActor.ReturnNumberResult(1001, None, Some("test"))
uns ! NumberPoolActor.ReturnNumberResult(1000, None, Some("test"))
uns ! NumberPoolActor.ReturnNumberResult(1001, None, Some(1))
uns ! NumberPoolActor.ReturnNumberResult(1000, None, Some(1))
//ReturnNumberResult B
uns ! NumberPoolActor.ReturnNumberResult(1001, Some(excp), Some("test"))
uns ! NumberPoolActor.ReturnNumberResult(1001, Some(excp), Some(1))
}
}
}
object UniqueNumberSystemTest { object UniqueNumberSystemTest {
/** /**
* @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)` * @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`

View file

@ -2,8 +2,8 @@
package objects.terminal package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class AirVehicleTerminalTest extends Specification { class AirVehicleTerminalTest extends Specification {
"Air_Vehicle_Terminal" should { "Air_Vehicle_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal) val terminal = Terminal(GlobalDefinitions.air_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in { "construct" in {

View file

@ -2,19 +2,19 @@
package objects.terminal package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._ import net.psforever.types._
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
class CertTerminalTest extends Specification { class CertTerminalTest extends Specification {
"Cert_Terminal" should { "Cert_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.cert_terminal) val terminal = Terminal(GlobalDefinitions.cert_terminal)
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in { "construct" in {

View file

@ -2,8 +2,8 @@
package objects.terminal package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class DropshipVehicleTerminalTest extends Specification { class DropshipVehicleTerminalTest extends Specification {
"Dropship_Vehicle_Terminal" should { "Dropship_Vehicle_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal) val terminal = Terminal(GlobalDefinitions.dropship_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in { "construct" in {

View file

@ -2,8 +2,8 @@
package objects.terminal package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class GroundVehicleTerminalTest extends Specification { class GroundVehicleTerminalTest extends Specification {
"Ground_Vehicle_Terminal" should { "Ground_Vehicle_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal) val terminal = Terminal(GlobalDefinitions.ground_vehicle_terminal)
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in { "construct" in {

View file

@ -2,8 +2,8 @@
package objects.terminal package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class ImplantTerminalInterfaceTest extends Specification { class ImplantTerminalInterfaceTest extends Specification {
"Implant_Terminal_Interface" should { "Implant_Terminal_Interface" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.implant_terminal_interface) val terminal = Terminal(GlobalDefinitions.implant_terminal_interface)
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in { "construct" in {

View file

@ -5,8 +5,9 @@ import akka.actor.{ActorRef, ActorSystem, Props}
import net.psforever.objects.definition.SeatDefinition import net.psforever.objects.definition.SeatDefinition
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl} import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.objects.vehicles.Seat import net.psforever.objects.vehicles.Seat
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3} import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
import objects.ActorTest import objects.ActorTest
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
@ -44,7 +45,7 @@ class ImplantTerminalMechTest extends Specification {
} }
"get passenger in a seat" in { "get passenger in a seat" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
obj.PassengerInSeat(player) mustEqual None obj.PassengerInSeat(player) mustEqual None
obj.Seats(0).Occupant = player obj.Seats(0).Occupant = player
@ -89,7 +90,7 @@ class ImplantTerminalMechControl3Test extends ActorTest() {
"ImplantTerminalMechControl" should { "ImplantTerminalMechControl" should {
"block a player from mounting" in { "block a player from mounting" in {
val (player1, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR) val (player1, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)
val player2 = Player("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player2 = Player(Avatar("test2", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
mech.Actor ! Mountable.TryMount(player1, 0) mech.Actor ! Mountable.TryMount(player1, 0)
receiveOne(Duration.create(100, "ms")) //consume reply receiveOne(Duration.create(100, "ms")) //consume reply
@ -160,9 +161,9 @@ object ImplantTerminalMechTest {
val terminal = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) val terminal = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
terminal.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], terminal), "mech") terminal.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], terminal), "mech")
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = faction terminal.Owner.Faction = faction
terminal.GUID = PlanetSideGUID(1) terminal.GUID = PlanetSideGUID(1)
(Player("test", faction, CharacterGender.Male, 0, 0), terminal) (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
} }
} }

View file

@ -3,7 +3,7 @@ package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal} import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle} import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._ import net.psforever.types._
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
@ -22,12 +22,18 @@ class MatrixTerminalTest extends Specification {
b.Name mustEqual "matrix_terminalb" b.Name mustEqual "matrix_terminalb"
} }
"define (b)" in { "define (c)" in {
val b = new MatrixTerminalDefinition(519) val b = new MatrixTerminalDefinition(519)
b.ObjectId mustEqual 519 b.ObjectId mustEqual 519
b.Name mustEqual "matrix_terminalc" b.Name mustEqual "matrix_terminalc"
} }
"define (d)" in {
val b = new MatrixTerminalDefinition(812)
b.ObjectId mustEqual 812
b.Name mustEqual "spawn_terminal"
}
"define (invalid)" in { "define (invalid)" in {
var id : Int = (math.random * Int.MaxValue).toInt var id : Int = (math.random * Int.MaxValue).toInt
if(id == 517) { if(id == 517) {
@ -36,7 +42,7 @@ class MatrixTerminalTest extends Specification {
else if(id == 518) { else if(id == 518) {
id += 2 id += 2
} }
else if(id == 519) { else if(id == 519 | id == 812) {
id += 1 id += 1
} }
@ -54,7 +60,7 @@ class MatrixTerminalTest extends Specification {
} }
"player can not buy (anything)" in { "player can not buy (anything)" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0)) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.NoDeal() terminal.Request(player, msg) mustEqual Terminal.NoDeal()

View file

@ -2,10 +2,10 @@
package objects.terminal package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.{OrderTerminalABDefinition, Terminal} import net.psforever.objects.serverobject.terminals.{OrderTerminalABDefinition, Terminal}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._ import net.psforever.types._
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
@ -39,7 +39,7 @@ class OrderTerminalABTest extends Specification {
"Order_Terminal" should { "Order_Terminal" should {
val terminal = Terminal(GlobalDefinitions.order_terminala) val terminal = Terminal(GlobalDefinitions.order_terminala)
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in { "construct" in {
@ -47,14 +47,14 @@ class OrderTerminalABTest extends Specification {
} }
"player can buy different armor ('lite_armor')" in { "player can buy different armor ('lite_armor')" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0)) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "lite_armor", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile) terminal.Request(player, msg) mustEqual Terminal.BuyExosuit(ExoSuitType.Agile)
} }
"player can buy max armor ('trhev_antiaircraft')" in { "player can buy max armor ('trhev_antiaircraft')" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "trhev_antiaircraft", 0, PlanetSideGUID(0)) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 1, "trhev_antiaircraft", 0, PlanetSideGUID(0))
terminal.Request(player, msg) mustEqual Terminal.NoDeal() terminal.Request(player, msg) mustEqual Terminal.NoDeal()
@ -62,10 +62,11 @@ class OrderTerminalABTest extends Specification {
//TODO loudout tests //TODO loudout tests
"player can not load max loadout" in { "player can not load max loadout" in {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val avatar = Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0)
player.SaveLoadout("test1", 0) val player = Player(avatar)
avatar.SaveLoadout(player, "test1", 0)
player.ExoSuit = ExoSuitType.MAX player.ExoSuit = ExoSuitType.MAX
player.SaveLoadout("test2", 1) avatar.SaveLoadout(player, "test2", 1)
val msg1 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.InfantryLoadout, 4, "", 0, PlanetSideGUID(0)) val msg1 = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.InfantryLoadout, 4, "", 0, PlanetSideGUID(0))
terminal.Request(player, msg1) mustEqual Terminal.InfantryLoadout(ExoSuitType.Standard, 0, Nil, Nil) terminal.Request(player, msg1) mustEqual Terminal.InfantryLoadout(ExoSuitType.Standard, 0, Nil, Nil)

View file

@ -2,19 +2,19 @@
package objects.terminal package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.{AmmoBox, GlobalDefinitions, Player, Tool} import net.psforever.objects.{AmmoBox, Avatar, GlobalDefinitions, Player, Tool}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._ import net.psforever.types._
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
class OrderTerminalTest extends Specification { class OrderTerminalTest extends Specification {
"Order_Terminal" should { "Order_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.order_terminal) val terminal = Terminal(GlobalDefinitions.order_terminal)
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in { "construct" in {

View file

@ -2,10 +2,10 @@
package objects.terminal package objects.terminal
import akka.actor.{ActorSystem, Props} import akka.actor.{ActorSystem, Props}
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition} import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._ import net.psforever.types._
import objects.ActorTest import objects.ActorTest
@ -122,8 +122,8 @@ object TerminalControlTest {
def SetUpAgents(tdef : TerminalDefinition, faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, Terminal) = { def SetUpAgents(tdef : TerminalDefinition, faction : PlanetSideEmpire.Value)(implicit system : ActorSystem) : (Player, Terminal) = {
val terminal = Terminal(tdef) val terminal = Terminal(tdef)
terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-term") terminal.Actor = system.actorOf(Props(classOf[TerminalControl], terminal), "test-term")
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = faction terminal.Owner.Faction = faction
(Player("test", faction, CharacterGender.Male, 0, 0), terminal) (Player(Avatar("test", faction, CharacterGender.Male, 0, 0)), terminal)
} }
} }

View file

@ -2,8 +2,8 @@
package objects.terminal package objects.terminal
import akka.actor.ActorRef import akka.actor.ActorRef
import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
@ -12,9 +12,9 @@ import org.specs2.mutable.Specification
class VehicleTerminalCombinedTest extends Specification { class VehicleTerminalCombinedTest extends Specification {
"Ground_Vehicle_Terminal" should { "Ground_Vehicle_Terminal" should {
val player = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0))
val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined) val terminal = Terminal(GlobalDefinitions.vehicle_terminal_combined)
terminal.Owner = new Building(0, Zone.Nowhere) terminal.Owner = new Building(0, Zone.Nowhere, StructureType.Building)
terminal.Owner.Faction = PlanetSideEmpire.TR terminal.Owner.Faction = PlanetSideEmpire.TR
"construct" in { "construct" in {

View file

@ -1,14 +1,14 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
import net.psforever.objects.zones.ZoneMap import net.psforever.objects.zones.ZoneMap
import net.psforever.objects.GlobalDefinitions._ import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects.serverobject.ServerObjectBuilder
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate} import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.types.Vector3 import net.psforever.types.Vector3
object Maps { object Maps {
@ -23,16 +23,362 @@ object Maps {
val map5 = new ZoneMap("map05") val map5 = new ZoneMap("map05")
val map6 = new ZoneMap("map06") { val map6 = new ZoneMap("map06") {
//TODO TEST ceryshen Building2()
LocalObject(ServerObjectBuilder(3353, Terminal.Constructor(ground_vehicle_terminal))) Building38()
LocalObject(ServerObjectBuilder(500, Building42()
VehicleSpawnPad.Constructor(Vector3(3962.0f, 4334.0f, 268.0f), Vector3(0f, 0f, 180.0f)) Building48()
)) //TODO guid not correct Building49()
LocalBuilding(2, FoundationBuilder(Building.Structure)) def Building2() : Unit = {
ObjectToBuilding(3353, 2) //Anguta
ObjectToBuilding(500, 2) LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Facility, Vector3(3974.2344f, 4287.914f, 0))))
TerminalToSpawnPad(3353, 500) LocalObject(222, Door.Constructor) //air term building, bay door
LocalObject(370, Door.Constructor) //courtyard
LocalObject(371, Door.Constructor) //courtyard
LocalObject(372, Door.Constructor) //courtyard
LocalObject(373, Door.Constructor) //courtyard
LocalObject(375, Door.Constructor(Vector3(3924.0f, 4231.2656f, 271.82812f), Vector3(0, 0, 180))) //2nd level door, south
LocalObject(376, Door.Constructor(Vector3(3924.0f, 4240.2656f, 271.82812f), Vector3(0, 0, 0))) //2nd level door, north
LocalObject(383, Door.Constructor) //courtyard
LocalObject(384, Door.Constructor(Vector3(3939.6328f, 4232.547f, 279.26562f), Vector3(0, 0, 270))) //3rd floor door
LocalObject(385, Door.Constructor) //courtyard
LocalObject(387, Door.Constructor(Vector3(3951.9531f, 4260.008f, 271.82812f), Vector3(0, 0, 270))) //2nd level door, stairwell
LocalObject(391, Door.Constructor) //courtyard
LocalObject(393, Door.Constructor(Vector3(3997.8984f, 4344.3203f, 271.8125f), Vector3(0, 0, 0))) //air term building, upstairs door
LocalObject(394, Door.Constructor(Vector3(3999.9766f, 4314.3203f, 266.82812f), Vector3(0, 0, 270))) //air term building, f.door
LocalObject(396, Door.Constructor) //courtyard
LocalObject(398, Door.Constructor) //courtyard
LocalObject(399, Door.Constructor) //courtyard
LocalObject(402, Door.Constructor) //courtyard
LocalObject(403, Door.Constructor) //courtyard
LocalObject(404, Door.Constructor(Vector3(4060.0078f, 4319.9766f, 266.8125f), Vector3(0, 0, 0))) //b.door
LocalObject(603, Door.Constructor)
LocalObject(604, Door.Constructor)
LocalObject(605, Door.Constructor)
LocalObject(606, Door.Constructor)
LocalObject(607, Door.Constructor)
LocalObject(610, Door.Constructor)
LocalObject(611, Door.Constructor)
LocalObject(614, Door.Constructor)
LocalObject(619, Door.Constructor)
LocalObject(620, Door.Constructor(Vector3(3983.9531f, 4299.992f, 249.29688f), Vector3(0, 0, 90))) //generator room door
LocalObject(621, Door.Constructor)
LocalObject(622, Door.Constructor(Vector3(3988.0078f, 4248.0156f, 256.82812f), Vector3(0, 0, 180))) //spawn room door
LocalObject(623, Door.Constructor(Vector3(3988.0078f, 4271.9766f, 256.79688f), Vector3(0, 0, 0))) //spawn room door
LocalObject(630, Door.Constructor(Vector3(4000.0078f, 4252.0f, 249.29688f), Vector3(0, 0, 270))) //spawn room door
LocalObject(631, Door.Constructor) //spawn room door, kitchen
LocalObject(634, Door.Constructor) //air term building, interior
LocalObject(638, Door.Constructor(Vector3(4016.0078f, 4212.008f, 249.29688f), Vector3(0, 0, 270))) //cc door
LocalObject(642, Door.Constructor(Vector3(4023.9844f, 4212.008f, 249.32812f), Vector3(0, 0, 90))) //cc door, interior
LocalObject(643, Door.Constructor) //cc door, exterior
LocalObject(645, Door.Constructor) //b.door, interior
LocalObject(646, Door.Constructor) //b.door, interior
LocalObject(715, Door.Constructor(Vector3(3961.5938f ,4235.8125f, 266.84375f), Vector3(0, 0, 90))) //f.door
LocalObject(751, IFFLock.Constructor)
LocalObject(860, IFFLock.Constructor)
LocalObject(863, IFFLock.Constructor)
LocalObject(866, IFFLock.Constructor)
LocalObject(868, IFFLock.Constructor)
LocalObject(873, IFFLock.Constructor)
LocalObject(874, IFFLock.Constructor)
LocalObject(875, IFFLock.Constructor)
LocalObject(876, IFFLock.Constructor)
LocalObject(878, IFFLock.Constructor)
LocalObject(879, IFFLock.Constructor)
LocalObject(882, IFFLock.Constructor)
LocalObject(884, IFFLock.Constructor)
LocalObject(885, IFFLock.Constructor)
LocalObject(1177, Locker.Constructor)
LocalObject(1178, Locker.Constructor)
LocalObject(1179, Locker.Constructor)
LocalObject(1180, Locker.Constructor)
LocalObject(1181, Locker.Constructor)
LocalObject(1182, Locker.Constructor)
LocalObject(1183, Locker.Constructor)
LocalObject(1184, Locker.Constructor)
LocalObject(1185, Locker.Constructor)
LocalObject(1186, Locker.Constructor)
LocalObject(1187, Locker.Constructor)
LocalObject(1188, Locker.Constructor)
LocalObject(1564, Terminal.Constructor(order_terminal))
LocalObject(1568, Terminal.Constructor(order_terminal))
LocalObject(1569, Terminal.Constructor(order_terminal))
LocalObject(1570, Terminal.Constructor(order_terminal))
LocalObject(1571, Terminal.Constructor(order_terminal))
LocalObject(1576, Terminal.Constructor(order_terminal))
LocalObject(1577, Terminal.Constructor(order_terminal))
LocalObject(1578, Terminal.Constructor(order_terminal))
LocalObject(2145, SpawnTube.Constructor(Vector3(3980.4062f, 4252.7656f, 257.5625f), Vector3(0, 0, 90)))
LocalObject(2146, SpawnTube.Constructor(Vector3(3980.4062f, 4259.992f, 257.5625f), Vector3(0, 0, 90)))
LocalObject(2147, SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90)))
LocalObject(2239, Terminal.Constructor(spawn_terminal))
LocalObject(2244, Terminal.Constructor(spawn_terminal))
LocalObject(2245, Terminal.Constructor(spawn_terminal))
LocalObject(2246, Terminal.Constructor(spawn_terminal))
LocalObject(2248, Terminal.Constructor(spawn_terminal))
LocalObject(2250, Terminal.Constructor(spawn_terminal))
LocalObject(2251, Terminal.Constructor(spawn_terminal))
LocalObject(2253, Terminal.Constructor(spawn_terminal))
LocalObject(2254, Terminal.Constructor(spawn_terminal))
LocalObject(2322, Door.Constructor) //spawn tube door
LocalObject(2323, Door.Constructor) //spawn tube door
LocalObject(2324, Door.Constructor) //spawn tube door
LocalObject(2419, Terminal.Constructor(ground_vehicle_terminal))
LocalObject(500,
VehicleSpawnPad.Constructor(Vector3(3962.0f, 4334.0f, 267.75f), Vector3(0f, 0f, 180.0f))
) //TODO guid not correct
LocalObject(224, Terminal.Constructor(dropship_vehicle_terminal))
LocalObject(501,
VehicleSpawnPad.Constructor(Vector3(4012.3594f, 4364.8047f, 271.90625f), Vector3(0f, 0f, 180.0f))
) //TODO guid not correct
ObjectToBuilding(222, 2)
ObjectToBuilding(224, 2)
ObjectToBuilding(370, 2)
ObjectToBuilding(371, 2)
ObjectToBuilding(372, 2)
ObjectToBuilding(373, 2)
ObjectToBuilding(375, 2)
ObjectToBuilding(376, 2)
ObjectToBuilding(383, 2)
ObjectToBuilding(384, 2)
ObjectToBuilding(385, 2)
ObjectToBuilding(387, 2)
ObjectToBuilding(391, 2)
ObjectToBuilding(393, 2)
ObjectToBuilding(394, 2)
ObjectToBuilding(396, 2)
ObjectToBuilding(398, 2)
ObjectToBuilding(399, 2)
ObjectToBuilding(402, 2)
ObjectToBuilding(403, 2)
ObjectToBuilding(404, 2)
ObjectToBuilding(603, 2)
ObjectToBuilding(604, 2)
ObjectToBuilding(605, 2)
ObjectToBuilding(606, 2)
ObjectToBuilding(607, 2)
ObjectToBuilding(610, 2)
ObjectToBuilding(611, 2)
ObjectToBuilding(614, 2)
ObjectToBuilding(619, 2)
ObjectToBuilding(620, 2)
ObjectToBuilding(621, 2)
ObjectToBuilding(622, 2)
ObjectToBuilding(623, 2)
ObjectToBuilding(630, 2)
ObjectToBuilding(631, 2)
ObjectToBuilding(634, 2)
ObjectToBuilding(638, 2)
ObjectToBuilding(642, 2)
ObjectToBuilding(643, 2)
ObjectToBuilding(645, 2)
ObjectToBuilding(646, 2)
ObjectToBuilding(715, 2)
ObjectToBuilding(751, 2)
ObjectToBuilding(860, 2)
ObjectToBuilding(863, 2)
ObjectToBuilding(866, 2)
ObjectToBuilding(868, 2)
ObjectToBuilding(873, 2)
ObjectToBuilding(874, 2)
ObjectToBuilding(875, 2)
ObjectToBuilding(876, 2)
ObjectToBuilding(878, 2)
ObjectToBuilding(879, 2)
ObjectToBuilding(882, 2)
ObjectToBuilding(884, 2)
ObjectToBuilding(885, 2)
ObjectToBuilding(1177, 2)
ObjectToBuilding(1178, 2)
ObjectToBuilding(1179, 2)
ObjectToBuilding(1180, 2)
ObjectToBuilding(1181, 2)
ObjectToBuilding(1182, 2)
ObjectToBuilding(1183, 2)
ObjectToBuilding(1184, 2)
ObjectToBuilding(1185, 2)
ObjectToBuilding(1186, 2)
ObjectToBuilding(1187, 2)
ObjectToBuilding(1188, 2)
ObjectToBuilding(1564, 2)
ObjectToBuilding(1568, 2)
ObjectToBuilding(1569, 2)
ObjectToBuilding(1570, 2)
ObjectToBuilding(1571, 2)
ObjectToBuilding(1576, 2)
ObjectToBuilding(1577, 2)
ObjectToBuilding(1578, 2)
ObjectToBuilding(2145, 2)
ObjectToBuilding(2146, 2)
ObjectToBuilding(2147, 2)
ObjectToBuilding(2239, 2)
ObjectToBuilding(2244, 2)
ObjectToBuilding(2245, 2)
ObjectToBuilding(2246, 2)
ObjectToBuilding(2248, 2)
ObjectToBuilding(2250, 2)
ObjectToBuilding(2251, 2)
ObjectToBuilding(2253, 2)
ObjectToBuilding(2254, 2)
ObjectToBuilding(2322, 2)
ObjectToBuilding(2323, 2)
ObjectToBuilding(2324, 2)
ObjectToBuilding(2419, 2)
ObjectToBuilding(500, 2)
ObjectToBuilding(501, 2)
DoorToLock(375, 863)
DoorToLock(376, 860)
DoorToLock(384, 866)
DoorToLock(387, 868)
DoorToLock(393, 876)
DoorToLock(394, 879)
DoorToLock(404, 885)
DoorToLock(620, 873)
DoorToLock(622, 876)
DoorToLock(623, 874)
DoorToLock(630, 878)
DoorToLock(638, 882)
DoorToLock(642, 884)
DoorToLock(715, 751)
TerminalToSpawnPad(224, 501)
TerminalToSpawnPad(2419, 500)
}
def Building38() : Unit = {
//Anguta, West Bunker
LocalBuilding(38, FoundationBuilder(Building.Structure(StructureType.Bunker)))
LocalObject(362, Door.Constructor)
ObjectToBuilding(362, 38)
}
def Building42() : Unit = {
//Anguta, East Bunker(s)
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Bunker)))
LocalObject(407, Door.Constructor)
LocalObject(408, Door.Constructor)
ObjectToBuilding(407, 42)
ObjectToBuilding(408, 42)
}
def Building48() : Unit = {
//North Anguta Watchtower
LocalBuilding(48, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(3864.2266f, 4518.0234f, 0))))
LocalObject(364, Door.Constructor(Vector3(3871.9688f, 4509.992f, 269.65625f), Vector3(0f, 0f, 180f))) //s1
LocalObject(365, Door.Constructor(Vector3(3871.9688f, 4509.992f, 279.57812f), Vector3(0f, 0f, 180f))) //s2
LocalObject(366, Door.Constructor(Vector3(3871.9688f, 4509.992f, 299.57812f), Vector3(0f, 0f, 180f))) //s3
LocalObject(367, Door.Constructor(Vector3(3871.9688f, 4525.9844f, 269.65625f), Vector3(0f, 0f, 0f))) //n1
LocalObject(368, Door.Constructor(Vector3(3871.9688f, 4525.9844f, 279.57812f), Vector3(0f, 0f, 0f))) //n2
LocalObject(369, Door.Constructor(Vector3(3871.9688f, 4525.9844f, 299.57812f), Vector3(0f, 0f, 0f))) //n3
LocalObject(854, IFFLock.Constructor)
LocalObject(855, IFFLock.Constructor)
LocalObject(856, IFFLock.Constructor)
LocalObject(857, IFFLock.Constructor)
LocalObject(858, IFFLock.Constructor)
LocalObject(859, IFFLock.Constructor)
LocalObject(1140, Locker.Constructor)
LocalObject(1141, Locker.Constructor)
LocalObject(1142, Locker.Constructor)
LocalObject(1143, Locker.Constructor)
LocalObject(1144, Locker.Constructor)
LocalObject(1145, Locker.Constructor)
LocalObject(1146, Locker.Constructor)
LocalObject(1147, Locker.Constructor)
LocalObject(1561, Terminal.Constructor(order_terminal))
LocalObject(1562, Terminal.Constructor(order_terminal))
LocalObject(1563, Terminal.Constructor(order_terminal))
LocalObject(2138, SpawnTube.Constructor(respawn_tube_tower, Vector3(3870.9688f, 4505.7266f, 259.875f), Vector3(0, 0, 90)))
LocalObject(2139, SpawnTube.Constructor(respawn_tube_tower, Vector3(3870.9688f, 4522.1562f, 259.875f), Vector3(0, 0, 90)))
LocalObject(2315, Door.Constructor) //spawn tube door
LocalObject(2316, Door.Constructor) //spawn tube door
ObjectToBuilding(364, 48)
ObjectToBuilding(365, 48)
ObjectToBuilding(366, 48)
ObjectToBuilding(367, 48)
ObjectToBuilding(368, 48)
ObjectToBuilding(369, 48)
ObjectToBuilding(854, 48)
ObjectToBuilding(855, 48)
ObjectToBuilding(856, 48)
ObjectToBuilding(857, 48)
ObjectToBuilding(858, 48)
ObjectToBuilding(859, 48)
ObjectToBuilding(1140, 48)
ObjectToBuilding(1141, 48)
ObjectToBuilding(1142, 48)
ObjectToBuilding(1143, 48)
ObjectToBuilding(1144, 48)
ObjectToBuilding(1145, 48)
ObjectToBuilding(1146, 48)
ObjectToBuilding(1147, 48)
ObjectToBuilding(1561, 48)
ObjectToBuilding(1562, 48)
ObjectToBuilding(1563, 48)
ObjectToBuilding(2138, 48)
ObjectToBuilding(2139, 48)
ObjectToBuilding(2315, 48)
ObjectToBuilding(2316, 48)
DoorToLock(364, 857)
DoorToLock(365, 858)
DoorToLock(366, 859)
DoorToLock(367, 854)
DoorToLock(368, 855)
DoorToLock(369, 856)
}
def Building49() : Unit = {
//North Akna Air Tower
LocalBuilding(49, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(4358.3203f, 3989.5625f, 0))))
LocalObject(430, Door.Constructor(Vector3(4366.0156f, 3981.9922f, 237.96875f), Vector3(0f, 0f, 180f))) //s1
LocalObject(431, Door.Constructor(Vector3(4366.0156f, 3981.9922f, 257.89062f), Vector3(0f, 0f, 180f))) //s2
LocalObject(432, Door.Constructor(Vector3(4366.0156f, 3997.9297f, 237.96875f), Vector3(0f, 0f, 0f))) //n1
LocalObject(433, Door.Constructor(Vector3(4366.0156f, 3997.9297f, 257.89062f), Vector3(0f, 0f, 0f))) //n2
LocalObject(902, IFFLock.Constructor)
LocalObject(903, IFFLock.Constructor)
LocalObject(906, IFFLock.Constructor)
LocalObject(907, IFFLock.Constructor)
LocalObject(1217, Locker.Constructor)
LocalObject(1218, Locker.Constructor)
LocalObject(1219, Locker.Constructor)
LocalObject(1220, Locker.Constructor)
LocalObject(1225, Locker.Constructor)
LocalObject(1226, Locker.Constructor)
LocalObject(1227, Locker.Constructor)
LocalObject(1228, Locker.Constructor)
LocalObject(1591, Terminal.Constructor(order_terminal))
LocalObject(1592, Terminal.Constructor(order_terminal))
LocalObject(1593, Terminal.Constructor(order_terminal))
LocalObject(2156, SpawnTube.Constructor(respawn_tube_tower, Vector3(4364.633f, 3994.125f, 228.1875f), Vector3(0, 0, 90)))
LocalObject(2157, SpawnTube.Constructor(respawn_tube_tower, Vector3(4364.633f, 3977.7266f, 228.1875f), Vector3(0, 0, 90)))
LocalObject(2333, Door.Constructor) //spawn tube door
LocalObject(2334, Door.Constructor) //spawn tube door
ObjectToBuilding(430, 49)
ObjectToBuilding(431, 49)
ObjectToBuilding(432, 49)
ObjectToBuilding(433, 49)
ObjectToBuilding(902, 49)
ObjectToBuilding(903, 49)
ObjectToBuilding(906, 49)
ObjectToBuilding(907, 49)
ObjectToBuilding(1217, 49)
ObjectToBuilding(1218, 49)
ObjectToBuilding(1219, 49)
ObjectToBuilding(1220, 49)
ObjectToBuilding(1225, 49)
ObjectToBuilding(1226, 49)
ObjectToBuilding(1227, 49)
ObjectToBuilding(1228, 49)
ObjectToBuilding(1591, 49)
ObjectToBuilding(1592, 49)
ObjectToBuilding(1593, 49)
ObjectToBuilding(2156, 49)
ObjectToBuilding(2157, 49)
ObjectToBuilding(2333, 49)
ObjectToBuilding(2334, 49)
DoorToLock(430, 906)
DoorToLock(431, 907)
DoorToLock(432, 902)
DoorToLock(433, 903)
}
} }
val map7 = new ZoneMap("map07") val map7 = new ZoneMap("map07")
@ -48,154 +394,241 @@ object Maps {
val map12 = new ZoneMap("map12") val map12 = new ZoneMap("map12")
val map13 = new ZoneMap("map13") { val map13 = new ZoneMap("map13") {
LocalBuilding(1, FoundationBuilder(WarpGate.Structure)) Building1()
LocalBuilding(2, FoundationBuilder(WarpGate.Structure)) Building2()
LocalBuilding(3, FoundationBuilder(WarpGate.Structure)) Building3()
Building29()
Building42()
Building51()
Building77()
LocalObject(ServerObjectBuilder(372, Door.Constructor)) def Building1() : Unit = {
LocalObject(ServerObjectBuilder(373, Door.Constructor)) //warpgate?
LocalBuilding(1, FoundationBuilder(WarpGate.Structure))
}
LocalObject(ServerObjectBuilder(520, ImplantTerminalMech.Constructor)) //Hart B def Building3() : Unit = {
LocalObject(ServerObjectBuilder(853, Terminal.Constructor(order_terminal))) //warpgate?
LocalObject(ServerObjectBuilder(855, Terminal.Constructor(order_terminal))) LocalBuilding(3, FoundationBuilder(WarpGate.Structure))
LocalObject(ServerObjectBuilder(860, Terminal.Constructor(order_terminal))) }
LocalObject(ServerObjectBuilder(1081, Terminal.Constructor(implant_terminal_interface))) //tube 520
TerminalToInterface(520, 1081)
LocalBuilding(2, FoundationBuilder(Building.Structure)) //HART building C // LocalBuilding(2, FoundationBuilder(WarpGate.Structure)) //TODO might be wrong?
LocalObject(ServerObjectBuilder(186, Terminal.Constructor(cert_terminal)))
LocalObject(ServerObjectBuilder(187, Terminal.Constructor(cert_terminal)))
LocalObject(ServerObjectBuilder(188, Terminal.Constructor(cert_terminal)))
LocalObject(ServerObjectBuilder(362, Door.Constructor))
LocalObject(ServerObjectBuilder(370, Door.Constructor))
LocalObject(ServerObjectBuilder(371, Door.Constructor))
LocalObject(ServerObjectBuilder(374, Door.Constructor))
LocalObject(ServerObjectBuilder(375, Door.Constructor))
LocalObject(ServerObjectBuilder(394, Door.Constructor))
LocalObject(ServerObjectBuilder(395, Door.Constructor))
LocalObject(ServerObjectBuilder(396, Door.Constructor))
LocalObject(ServerObjectBuilder(397, Door.Constructor))
LocalObject(ServerObjectBuilder(398, Door.Constructor))
LocalObject(ServerObjectBuilder(462, Door.Constructor))
LocalObject(ServerObjectBuilder(463, Door.Constructor))
LocalObject(ServerObjectBuilder(522, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(523, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(524, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(525, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(526, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(527, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(528, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(529, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(686, Locker.Constructor))
LocalObject(ServerObjectBuilder(687, Locker.Constructor))
LocalObject(ServerObjectBuilder(688, Locker.Constructor))
LocalObject(ServerObjectBuilder(689, Locker.Constructor))
LocalObject(ServerObjectBuilder(690, Locker.Constructor))
LocalObject(ServerObjectBuilder(691, Locker.Constructor))
LocalObject(ServerObjectBuilder(692, Locker.Constructor))
LocalObject(ServerObjectBuilder(693, Locker.Constructor))
LocalObject(ServerObjectBuilder(842, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(843, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(844, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(845, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(1082, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1083, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1084, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1085, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1086, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1087, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1088, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1089, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
ObjectToBuilding(186, 2)
ObjectToBuilding(187, 2)
ObjectToBuilding(188, 2)
ObjectToBuilding(522, 2)
ObjectToBuilding(523, 2)
ObjectToBuilding(524, 2)
ObjectToBuilding(525, 2)
ObjectToBuilding(526, 2)
ObjectToBuilding(527, 2)
ObjectToBuilding(528, 2)
ObjectToBuilding(529, 2)
ObjectToBuilding(686, 2)
ObjectToBuilding(687, 2)
ObjectToBuilding(688, 2)
ObjectToBuilding(689, 2)
ObjectToBuilding(690, 2)
ObjectToBuilding(691, 2)
ObjectToBuilding(692, 2)
ObjectToBuilding(693, 2)
ObjectToBuilding(842, 2)
ObjectToBuilding(843, 2)
ObjectToBuilding(844, 2)
ObjectToBuilding(845, 2)
ObjectToBuilding(1082, 2)
ObjectToBuilding(1083, 2)
ObjectToBuilding(1084, 2)
ObjectToBuilding(1085, 2)
ObjectToBuilding(1086, 2)
ObjectToBuilding(1087, 2)
ObjectToBuilding(1088, 2)
ObjectToBuilding(1089, 2)
TerminalToInterface(522, 1082)
TerminalToInterface(523, 1083)
TerminalToInterface(524, 1084)
TerminalToInterface(525, 1085)
TerminalToInterface(526, 1086)
TerminalToInterface(527, 1087)
TerminalToInterface(528, 1088)
TerminalToInterface(529, 1089)
LocalBuilding(29, FoundationBuilder(Building.Structure)) //South Villa Gun Tower // LocalObject(520, ImplantTerminalMech.Constructor) //Hart B
LocalObject(ServerObjectBuilder(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180)))) // LocalObject(1081, Terminal.Constructor(implant_terminal_interface)) //tube 520
LocalObject(ServerObjectBuilder(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180)))) // TerminalToInterface(520, 1081)
LocalObject(ServerObjectBuilder(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0))))
LocalObject(ServerObjectBuilder(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0))))
LocalObject(ServerObjectBuilder(556, IFFLock.Constructor))
LocalObject(ServerObjectBuilder(557, IFFLock.Constructor))
LocalObject(ServerObjectBuilder(558, IFFLock.Constructor))
LocalObject(ServerObjectBuilder(559, IFFLock.Constructor))
ObjectToBuilding(330, 29)
ObjectToBuilding(331, 29)
ObjectToBuilding(332, 29)
ObjectToBuilding(333, 29)
ObjectToBuilding(556, 29)
ObjectToBuilding(557, 29)
ObjectToBuilding(558, 29)
ObjectToBuilding(559, 29)
DoorToLock(330, 558)
DoorToLock(331, 559)
DoorToLock(332, 556)
DoorToLock(333, 557)
LocalBuilding(51, FoundationBuilder(Building.Structure)) def Building2() : Unit = {
LocalObject(ServerObjectBuilder(304, Terminal.Constructor(dropship_vehicle_terminal))) //HART building C
LocalObject(ServerObjectBuilder(292, LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building)))
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f)) LocalObject(186, Terminal.Constructor(cert_terminal))
)) LocalObject(187, Terminal.Constructor(cert_terminal))
ObjectToBuilding(304, 51) LocalObject(188, Terminal.Constructor(cert_terminal))
ObjectToBuilding(292, 51) LocalObject(362, Door.Constructor)
TerminalToSpawnPad(304, 292) LocalObject(370, Door.Constructor)
LocalObject(371, Door.Constructor)
LocalObject(374, Door.Constructor)
LocalObject(375, Door.Constructor)
LocalObject(394, Door.Constructor)
LocalObject(395, Door.Constructor)
LocalObject(396, Door.Constructor)
LocalObject(397, Door.Constructor)
LocalObject(398, Door.Constructor)
LocalObject(462, Door.Constructor)
LocalObject(463, Door.Constructor)
LocalObject(522, ImplantTerminalMech.Constructor)
LocalObject(523, ImplantTerminalMech.Constructor)
LocalObject(524, ImplantTerminalMech.Constructor)
LocalObject(525, ImplantTerminalMech.Constructor)
LocalObject(526, ImplantTerminalMech.Constructor)
LocalObject(527, ImplantTerminalMech.Constructor)
LocalObject(528, ImplantTerminalMech.Constructor)
LocalObject(529, ImplantTerminalMech.Constructor)
LocalObject(686, Locker.Constructor)
LocalObject(687, Locker.Constructor)
LocalObject(688, Locker.Constructor)
LocalObject(689, Locker.Constructor)
LocalObject(690, Locker.Constructor)
LocalObject(691, Locker.Constructor)
LocalObject(692, Locker.Constructor)
LocalObject(693, Locker.Constructor)
LocalObject(842, Terminal.Constructor(order_terminal))
LocalObject(843, Terminal.Constructor(order_terminal))
LocalObject(844, Terminal.Constructor(order_terminal))
LocalObject(845, Terminal.Constructor(order_terminal))
LocalObject(1082, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1083, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1084, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1085, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1086, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1087, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1088, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
LocalObject(1089, Terminal.Constructor(implant_terminal_interface)) //TODO guid not correct
ObjectToBuilding(186, 2)
ObjectToBuilding(187, 2)
ObjectToBuilding(188, 2)
ObjectToBuilding(362, 2)
ObjectToBuilding(370, 2)
ObjectToBuilding(371, 2)
ObjectToBuilding(374, 2)
ObjectToBuilding(375, 2)
ObjectToBuilding(394, 2)
ObjectToBuilding(395, 2)
ObjectToBuilding(396, 2)
ObjectToBuilding(397, 2)
ObjectToBuilding(398, 2)
ObjectToBuilding(462, 2)
ObjectToBuilding(463, 2)
ObjectToBuilding(522, 2)
ObjectToBuilding(523, 2)
ObjectToBuilding(524, 2)
ObjectToBuilding(525, 2)
ObjectToBuilding(526, 2)
ObjectToBuilding(527, 2)
ObjectToBuilding(528, 2)
ObjectToBuilding(529, 2)
ObjectToBuilding(686, 2)
ObjectToBuilding(687, 2)
ObjectToBuilding(688, 2)
ObjectToBuilding(689, 2)
ObjectToBuilding(690, 2)
ObjectToBuilding(691, 2)
ObjectToBuilding(692, 2)
ObjectToBuilding(693, 2)
ObjectToBuilding(842, 2)
ObjectToBuilding(843, 2)
ObjectToBuilding(844, 2)
ObjectToBuilding(845, 2)
ObjectToBuilding(1082, 2)
ObjectToBuilding(1083, 2)
ObjectToBuilding(1084, 2)
ObjectToBuilding(1085, 2)
ObjectToBuilding(1086, 2)
ObjectToBuilding(1087, 2)
ObjectToBuilding(1088, 2)
ObjectToBuilding(1089, 2)
TerminalToInterface(522, 1082)
TerminalToInterface(523, 1083)
TerminalToInterface(524, 1084)
TerminalToInterface(525, 1085)
TerminalToInterface(526, 1086)
TerminalToInterface(527, 1087)
TerminalToInterface(528, 1088)
TerminalToInterface(529, 1089)
}
LocalBuilding(77, FoundationBuilder(Building.Structure)) def Building29() : Unit = {
LocalObject(ServerObjectBuilder(1063, Terminal.Constructor(ground_vehicle_terminal))) //South Villa Gun Tower
LocalObject(ServerObjectBuilder(706, LocalBuilding(29, FoundationBuilder(Building.Structure(StructureType.Tower)))
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f)) LocalObject(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180)))
)) LocalObject(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180)))
ObjectToBuilding(1063, 77) LocalObject(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0)))
ObjectToBuilding(706, 77) LocalObject(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0)))
TerminalToSpawnPad(1063, 706) LocalObject(556, IFFLock.Constructor)
LocalObject(557, IFFLock.Constructor)
LocalObject(558, IFFLock.Constructor)
LocalObject(559, IFFLock.Constructor)
ObjectToBuilding(330, 29)
ObjectToBuilding(331, 29)
ObjectToBuilding(332, 29)
ObjectToBuilding(333, 29)
ObjectToBuilding(556, 29)
ObjectToBuilding(557, 29)
ObjectToBuilding(558, 29)
ObjectToBuilding(559, 29)
DoorToLock(330, 558)
DoorToLock(331, 559)
DoorToLock(332, 556)
DoorToLock(333, 557)
}
ObjectToBuilding(853, 2) //TODO check building_id def Building42() : Unit = {
ObjectToBuilding(855, 2) //TODO check building_id //spawn building south of HART C
ObjectToBuilding(860, 2) //TODO check building_id LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1, 0, 0))))
LocalObject(258, Door.Constructor) //spawn tube door
LocalObject(259, Door.Constructor) //spawn tube door
LocalObject(260, Door.Constructor) //spawn tube door
LocalObject(261, Door.Constructor) //spawn tube door
LocalObject(262, Door.Constructor) //spawn tube door
LocalObject(263, Door.Constructor) //spawn tube door
LocalObject(372, Door.Constructor) //entrance
LocalObject(373, Door.Constructor) //entrance
LocalObject(430, Door.Constructor) //vr door
LocalObject(431, Door.Constructor) //vr door
LocalObject(432, Door.Constructor) //vr door
LocalObject(433, Door.Constructor) //vr door
LocalObject(434, Door.Constructor) //vr door
LocalObject(435, Door.Constructor) //vr door
LocalObject(744, SpawnTube.Constructor(Vector3(3684.336f, 2709.0469f, 91.9f), Vector3(0, 0, 180)))
LocalObject(745, SpawnTube.Constructor(Vector3(3684.336f, 2713.75f, 91.9f), Vector3(0, 0, 0)))
LocalObject(746, SpawnTube.Constructor(Vector3(3690.9062f, 2708.4219f, 91.9f), Vector3(0, 0, 180)))
LocalObject(747, SpawnTube.Constructor(Vector3(3691.0703f, 2713.8672f, 91.9f), Vector3(0, 0, 0)))
LocalObject(748, SpawnTube.Constructor(Vector3(3697.664f, 2708.3984f, 91.9f), Vector3(0, 0, 180)))
LocalObject(749, SpawnTube.Constructor(Vector3(3697.711f, 2713.2344f, 91.9f), Vector3(0, 0, 0)))
LocalObject(852, Terminal.Constructor(order_terminal)) //s. wall
LocalObject(853, Terminal.Constructor(order_terminal)) //n. wall
LocalObject(854, Terminal.Constructor(order_terminal)) //s. wall
LocalObject(855, Terminal.Constructor(order_terminal)) //n. wall
LocalObject(859, Terminal.Constructor(order_terminal)) //s. wall
LocalObject(860, Terminal.Constructor(order_terminal)) //n. wall
ObjectToBuilding(258, 42)
ObjectToBuilding(259, 42)
ObjectToBuilding(260, 42)
ObjectToBuilding(261, 42)
ObjectToBuilding(262, 42)
ObjectToBuilding(263, 42)
ObjectToBuilding(372, 42)
ObjectToBuilding(373, 42)
ObjectToBuilding(430, 42)
ObjectToBuilding(431, 42)
ObjectToBuilding(432, 42)
ObjectToBuilding(433, 42)
ObjectToBuilding(434, 42)
ObjectToBuilding(435, 42)
ObjectToBuilding(744, 42)
ObjectToBuilding(745, 42)
ObjectToBuilding(746, 42)
ObjectToBuilding(747, 42)
ObjectToBuilding(748, 42)
ObjectToBuilding(749, 42)
ObjectToBuilding(852, 42)
ObjectToBuilding(853, 42)
ObjectToBuilding(854, 42)
ObjectToBuilding(855, 42)
ObjectToBuilding(859, 42)
ObjectToBuilding(860, 42)
}
def Building51() : Unit = {
//air terminal west of HART C
LocalBuilding(51, FoundationBuilder(Building.Structure(StructureType.Platform)))
LocalObject(304, Terminal.Constructor(dropship_vehicle_terminal))
LocalObject(292,
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
)
ObjectToBuilding(304, 51)
ObjectToBuilding(292, 51)
TerminalToSpawnPad(304, 292)
}
def Building77() : Unit = {
//ground terminal west of HART C
LocalBuilding(77, FoundationBuilder(Building.Structure(StructureType.Platform)))
LocalObject(1063, Terminal.Constructor(ground_vehicle_terminal))
LocalObject(706,
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
)
ObjectToBuilding(1063, 77)
ObjectToBuilding(706, 77)
TerminalToSpawnPad(1063, 706)
}
} }
val map14 = new ZoneMap("map13") val map14 = new ZoneMap("map14")
val map15 = new ZoneMap("map13") val map15 = new ZoneMap("map15")
val map16 = new ZoneMap("map13") val map16 = new ZoneMap("map16")
val ugd01 = new ZoneMap("ugd01") val ugd01 = new ZoneMap("ugd01")
@ -209,7 +642,7 @@ object Maps {
val ugd06 = new ZoneMap("ugd06") val ugd06 = new ZoneMap("ugd06")
val map96 = new ZoneMap("ugd06") val map96 = new ZoneMap("map96")
val map97 = new ZoneMap("map97") val map97 = new ZoneMap("map97")

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
import akka.actor.ActorContext import akka.actor.ActorContext
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideEmpire
object Zones { object Zones {
val z1 = new Zone("z1", Maps.map1, 1) val z1 = new Zone("z1", Maps.map1, 1)
@ -19,6 +20,13 @@ object Zones {
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
Building(2).get.Faction = PlanetSideEmpire.VS Building(2).get.Faction = PlanetSideEmpire.VS
Building(2).get.ModelId = 20
Building(38).get.ModelId = 0
Building(42).get.ModelId = 0
Building(48).get.Faction = PlanetSideEmpire.VS
Building(48).get.ModelId = 59
Building(49).get.Faction = PlanetSideEmpire.VS
Building(49).get.ModelId = 69
} }
} }
@ -39,7 +47,7 @@ object Zones {
super.Init(context) super.Init(context)
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
Buildings.values.foreach(building => { building.Faction = PlanetSideEmpire.VS }) Buildings.values.foreach { _.Faction = PlanetSideEmpire.VS }
Building(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower Building(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower
} }
} }
@ -81,4 +89,32 @@ object Zones {
val i3 = new Zone("i3", Maps.map98, 31) val i3 = new Zone("i3", Maps.map98, 31)
val i4 = new Zone("i4", Maps.map99, 32) val i4 = new Zone("i4", Maps.map99, 32)
/**
* Get the zone identifier name for the sanctuary continent of a given empire.
* @param faction the empire
* @return the zone id, with a blank string as an invalidating result
*/
def SanctuaryZoneId(faction : PlanetSideEmpire.Value) : String = {
faction match {
case PlanetSideEmpire.NC => "home1"
case PlanetSideEmpire.TR => "home2"
case PlanetSideEmpire.VS => "home3"
case PlanetSideEmpire.NEUTRAL => "" //invalid, not black ops
}
}
/**
* Get the zone number for the sanctuary continent of a given empire.
* @param faction the empire
* @return the zone number, within the sequence 1-32, and with 0 as an invalidating result
*/
def SanctuaryZoneNumber(faction : PlanetSideEmpire.Value) : Int = {
faction match {
case PlanetSideEmpire.NC => 11
case PlanetSideEmpire.TR => 12
case PlanetSideEmpire.VS => 13
case PlanetSideEmpire.NEUTRAL => 0 //invalid, not black ops
}
}
} }

View file

@ -1,7 +1,9 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package services.avatar package services.avatar
import net.psforever.objects.Player
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream} import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.packet.game.objectcreate.ConstructorData import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.types.{ExoSuitType, Vector3} import net.psforever.types.{ExoSuitType, Vector3}
@ -24,6 +26,7 @@ object AvatarAction {
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
final case class Release(player : Player, zone : Zone, time : Option[Long] = None) extends Action
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action // final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package services.avatar package services.avatar
import net.psforever.objects.Player
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream} import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.packet.game.objectcreate.ConstructorData import net.psforever.packet.game.objectcreate.ConstructorData
@ -24,6 +25,7 @@ object AvatarResponse {
final case class ObjectHeld(slot : Int) extends Response final case class ObjectHeld(slot : Int) extends Response
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response
final case class Release(player : Player) extends Response
final case class Reload(weapon_guid : PlanetSideGUID) extends Response final case class Reload(weapon_guid : PlanetSideGUID) extends Response
final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response // final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response

View file

@ -1,11 +1,14 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package services.avatar package services.avatar
import akka.actor.Actor import akka.actor.{Actor, ActorRef, Props}
import services.avatar.support.CorpseRemovalActor
import services.{GenericEventBus, Service} import services.{GenericEventBus, Service}
class AvatarService extends Actor { class AvatarService extends Actor {
//import AvatarServiceResponse._ private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
undertaker ! "startup"
private [this] val log = org.log4s.getLogger private [this] val log = org.log4s.getLogger
override def preStart = { override def preStart = {
@ -87,6 +90,14 @@ class AvatarService extends Actor {
AvatarEvents.publish( AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon)) AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
) )
case AvatarAction.Release(player, zone, time) =>
undertaker ! (time match {
case Some(t) => CorpseRemovalActor.AddCorpse(player, zone, t)
case None => CorpseRemovalActor.AddCorpse(player, zone)
})
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player.GUID, AvatarResponse.Release(player))
)
case AvatarAction.Reload(player_guid, weapon_guid) => case AvatarAction.Reload(player_guid, weapon_guid) =>
AvatarEvents.publish( AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid)) AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid))
@ -95,9 +106,14 @@ class AvatarService extends Actor {
AvatarEvents.publish( AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.WeaponDryFire(weapon_guid)) AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.WeaponDryFire(weapon_guid))
) )
case _ => ; case _ => ;
} }
//message to Undertaker
case AvatarServiceMessage.RemoveSpecificCorpse(corpses) =>
undertaker ! AvatarServiceMessage.RemoveSpecificCorpse( corpses.filter(corpse => {corpse.HasGUID && corpse.isBackpack}) )
/* /*
case AvatarService.PlayerStateMessage(msg) => case AvatarService.PlayerStateMessage(msg) =>
// log.info(s"NEW: ${m}") // log.info(s"NEW: ${m}")

View file

@ -1,4 +1,10 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package services.avatar package services.avatar
import net.psforever.objects.Player
final case class AvatarServiceMessage(forChannel : String, actionMessage : AvatarAction.Action) final case class AvatarServiceMessage(forChannel : String, actionMessage : AvatarAction.Action)
object AvatarServiceMessage {
final case class RemoveSpecificCorpse(corpse : List[Player])
}

View file

@ -0,0 +1,199 @@
// Copyright (c) 2017 PSForever
package services.avatar.support
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.{DefaultCancellable, Player}
import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3
import services.{Service, ServiceManager}
import services.ServiceManager.Lookup
import services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.annotation.tailrec
import scala.concurrent.duration._
class CorpseRemovalActor extends Actor {
private var burial : Cancellable = DefaultCancellable.obj
private var corpses : List[CorpseRemovalActor.Entry] = List()
private var taskResolver : ActorRef = Actor.noSender
private[this] val log = org.log4s.getLogger
override def postStop() = {
//Cart Master: See you on Thursday.
corpses.foreach { BurialTask }
corpses = Nil
}
def receive : Receive = {
case "startup" =>
ServiceManager.serviceManager ! Lookup("taskResolver") //ask for a resolver to deal with the GUID system
case ServiceManager.LookupResult("taskResolver", endpoint) =>
//Cart Master: Bring out your dead!
taskResolver = endpoint
context.become(Processing)
case _ => ;
}
def Processing : Receive = {
case CorpseRemovalActor.AddCorpse(corpse, zone, time) =>
if(corpse.isBackpack) {
if(corpses.isEmpty) {
//we were the only entry so the event must be started from scratch
corpses = List(CorpseRemovalActor.Entry(corpse, zone, time))
RetimeFirstTask()
}
else {
//unknown number of entries; append, sort, then re-time tasking
val oldHead = corpses.head
corpses = (corpses :+ CorpseRemovalActor.Entry(corpse, zone, time)).sortBy(_.timeAlive)
if(oldHead != corpses.head) {
RetimeFirstTask()
}
}
}
else {
//Cart Master: 'Ere. He says he's not dead!
log.warn(s"$corpse does not qualify as a corpse; ignored queueing request")
}
case AvatarServiceMessage.RemoveSpecificCorpse(targets) =>
if(targets.nonEmpty) {
//Cart Master: No, I've got to go to the Robinsons'. They've lost nine today.
burial.cancel
if(targets.size == 1) {
log.debug(s"a target corpse submitted for early cleanup: ${targets.head}")
//simple selection
CorpseRemovalActor.recursiveFindCorpse(corpses.iterator, targets.head) match {
case None => ;
case Some(index) =>
BurialTask(corpses(index))
corpses = corpses.take(index) ++ corpses.drop(index+1)
}
}
else {
log.debug(s"multiple target corpses submitted for early cleanup: $targets")
//cumbersome partition
//a - find targets from corpses
(for {
a <- targets
b <- corpses
if b.corpse == a &&
b.corpse.Continent.equals(a.Continent) &&
b.corpse.HasGUID && a.HasGUID && b.corpse.GUID == a.GUID
} yield b).foreach { BurialTask }
//b - corpses after the found targets are
//removed (note: cull any non-GUID entries while at it)
corpses = (for {
a <- targets
b <- corpses
if b.corpse.HasGUID && a.HasGUID &&
(b.corpse != a ||
!b.corpse.Continent.equals(a.Continent) ||
!b.corpse.HasGUID || !a.HasGUID || b.corpse.GUID != a.GUID)
} yield b).sortBy(_.timeAlive)
}
RetimeFirstTask()
}
case CorpseRemovalActor.Dispose() =>
burial.cancel
val now : Long = System.nanoTime
val (buried, rotting) = corpses.partition(entry => { now - entry.time >= entry.timeAlive })
corpses = rotting
buried.foreach { BurialTask }
RetimeFirstTask()
case CorpseRemovalActor.FailureToWork(target, zone, ex) =>
//Cart Master: Oh, I can't take him like that. It's against regulations.
log.error(s"corpse $target from $zone not properly unregistered - $ex")
case _ => ;
}
def RetimeFirstTask(now : Long = System.nanoTime) : Unit = {
//Cart Master: Thursday.
burial.cancel
if(corpses.nonEmpty) {
val short_timeout : FiniteDuration = math.max(1, corpses.head.timeAlive - (now - corpses.head.time)) nanoseconds
import scala.concurrent.ExecutionContext.Implicits.global
burial = context.system.scheduler.scheduleOnce(short_timeout, self, CorpseRemovalActor.Dispose())
}
}
def BurialTask(entry : CorpseRemovalActor.Entry) : Unit = {
//Cart master: Nine pence.
val target = entry.corpse
val zone = entry.zone
target.Position = Vector3.Zero //somewhere it will not disturb anything
entry.zone.Population ! Zone.Corpse.Remove(target)
context.parent ! AvatarServiceMessage(zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID))
taskResolver ! BurialTask(target, zone)
}
def BurialTask(corpse : Player, zone : Zone) : TaskResolver.GiveTask = {
import net.psforever.objects.guid.{GUIDTask, Task}
TaskResolver.GiveTask (
new Task() {
private val localCorpse = corpse
private val localZone = zone
private val localAnnounce = self
override def isComplete : Task.Resolution.Value = if(!localCorpse.HasGUID) {
Task.Resolution.Success
}
else {
Task.Resolution.Incomplete
}
def Execute(resolver : ActorRef) : Unit = {
resolver ! scala.util.Success(this)
}
override def onFailure(ex : Throwable): Unit = {
localAnnounce ! CorpseRemovalActor.FailureToWork(localCorpse, localZone, ex)
}
}, List(GUIDTask.UnregisterPlayer(corpse)(zone.GUID))
)
}
}
object CorpseRemovalActor {
final val time : Long = 180000000000L //3 min (180s)
final case class AddCorpse(corpse : Player, zone : Zone, time : Long = CorpseRemovalActor.time)
final case class Entry(corpse : Player, zone : Zone, timeAlive : Long = CorpseRemovalActor.time, time : Long = System.nanoTime())
final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable)
final case class Dispose()
/**
* A recursive function that finds and removes a specific player from a list of players.
* @param iter an `Iterator` of `CorpseRemovalActor.Entry` objects
* @param player the target `Player`
* @param index the index of the discovered `Player` object
* @return the index of the `Player` object in the list to be removed;
* `None`, otherwise
*/
@tailrec final def recursiveFindCorpse(iter : Iterator[CorpseRemovalActor.Entry], player : Player, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val corpse = iter.next.corpse
if(corpse == player && corpse.Continent.equals(player.Continent) && corpse.GUID == player.GUID) {
Some(index)
}
else {
recursiveFindCorpse(iter, player, index + 1)
}
}
}
}

View file

@ -7,6 +7,7 @@ import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.vehicles.Seat import net.psforever.objects.vehicles.Seat
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.Vector3
import services.ServiceManager import services.ServiceManager
import services.ServiceManager.Lookup import services.ServiceManager.Lookup
import services.vehicle.{VehicleAction, VehicleServiceMessage} import services.vehicle.{VehicleAction, VehicleServiceMessage}
@ -80,6 +81,7 @@ class DeconstructionActor extends Actor {
vehiclesToScrap.foreach(entry => { vehiclesToScrap.foreach(entry => {
val vehicle = entry.vehicle val vehicle = entry.vehicle
val zone = entry.zone val zone = entry.zone
vehicle.Position = Vector3.Zero //somewhere it will not disturb anything
entry.zone.Transport ! Zone.DespawnVehicle(vehicle) entry.zone.Transport ! Zone.DespawnVehicle(vehicle)
context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system
context.parent ! VehicleServiceMessage.RevokeActorControl(vehicle) //call up to a sibling manager context.parent ! VehicleServiceMessage.RevokeActorControl(vehicle) //call up to a sibling manager

View file

@ -1,15 +1,21 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
import akka.actor.Props import akka.actor.Props
import akka.routing.RandomPool
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream} import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vector3} import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vector3}
import services.Service import services.{Service, ServiceManager}
import services.avatar._ import services.avatar._
import scala.concurrent.duration._
class AvatarService1Test extends ActorTest { class AvatarService1Test extends ActorTest {
"AvatarService" should { "AvatarService" should {
"construct" in { "construct" in {
system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
assert(true) assert(true)
} }
} }
@ -18,7 +24,8 @@ class AvatarService1Test extends ActorTest {
class AvatarService2Test extends ActorTest { class AvatarService2Test extends ActorTest {
"AvatarService" should { "AvatarService" should {
"subscribe" in { "subscribe" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
assert(true) assert(true)
} }
@ -27,8 +34,9 @@ class AvatarService2Test extends ActorTest {
class AvatarService3Test extends ActorTest { class AvatarService3Test extends ActorTest {
"AvatarService" should { "AvatarService" should {
ServiceManager.boot(system)
"subscribe to a specific channel" in { "subscribe to a specific channel" in {
val service = system.actorOf(Props[AvatarService], "service") val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! Service.Leave() service ! Service.Leave()
assert(true) assert(true)
@ -39,7 +47,8 @@ class AvatarService3Test extends ActorTest {
class AvatarService4Test extends ActorTest { class AvatarService4Test extends ActorTest {
"AvatarService" should { "AvatarService" should {
"subscribe" in { "subscribe" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! Service.LeaveAll() service ! Service.LeaveAll()
assert(true) assert(true)
@ -50,7 +59,8 @@ class AvatarService4Test extends ActorTest {
class AvatarService5Test extends ActorTest { class AvatarService5Test extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass an unhandled message" in { "pass an unhandled message" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! "hello" service ! "hello"
expectNoMsg() expectNoMsg()
@ -61,7 +71,8 @@ class AvatarService5Test extends ActorTest {
class ArmorChangedTest extends ActorTest { class ArmorChangedTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass ArmorChanged" in { "pass ArmorChanged" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ArmorChanged(PlanetSideGUID(10), ExoSuitType.Reinforced, 0)) service ! AvatarServiceMessage("test", AvatarAction.ArmorChanged(PlanetSideGUID(10), ExoSuitType.Reinforced, 0))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ArmorChanged(ExoSuitType.Reinforced, 0))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ArmorChanged(ExoSuitType.Reinforced, 0)))
@ -72,7 +83,8 @@ class ArmorChangedTest extends ActorTest {
class ConcealPlayerTest extends ActorTest { class ConcealPlayerTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass ConcealPlayer" in { "pass ConcealPlayer" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ConcealPlayer(PlanetSideGUID(10))) service ! AvatarServiceMessage("test", AvatarAction.ConcealPlayer(PlanetSideGUID(10)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ConcealPlayer())) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ConcealPlayer()))
@ -85,7 +97,8 @@ class EquipmentInHandTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass EquipmentInHand" in { "pass EquipmentInHand" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.EquipmentInHand(PlanetSideGUID(10), 2, tool)) service ! AvatarServiceMessage("test", AvatarAction.EquipmentInHand(PlanetSideGUID(10), 2, tool))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(2, tool))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(2, tool)))
@ -101,7 +114,8 @@ class EquipmentOnGroundTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass EquipmentOnGround" in { "pass EquipmentOnGround" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.EquipmentOnGround(PlanetSideGUID(10), Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata)) service ! AvatarServiceMessage("test", AvatarAction.EquipmentOnGround(PlanetSideGUID(10), Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentOnGround(Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentOnGround(Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata)))
@ -110,14 +124,15 @@ class EquipmentOnGroundTest extends ActorTest {
} }
class LoadPlayerTest extends ActorTest { class LoadPlayerTest extends ActorTest {
val obj = Player("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1) val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.GUID = PlanetSideGUID(10) obj.GUID = PlanetSideGUID(10)
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11) obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
val pdata = obj.Definition.Packet.DetailedConstructorData(obj).get val pdata = obj.Definition.Packet.DetailedConstructorData(obj).get
"AvatarService" should { "AvatarService" should {
"pass LoadPlayer" in { "pass LoadPlayer" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(PlanetSideGUID(10), pdata)) service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(PlanetSideGUID(10), pdata))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.LoadPlayer(pdata))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.LoadPlayer(pdata)))
@ -128,7 +143,8 @@ class LoadPlayerTest extends ActorTest {
class ObjectDeleteTest extends ActorTest { class ObjectDeleteTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass ObjectDelete" in { "pass ObjectDelete" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(10), PlanetSideGUID(11))) service ! AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(10), PlanetSideGUID(11)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(PlanetSideGUID(11), 0))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(PlanetSideGUID(11), 0)))
@ -142,7 +158,8 @@ class ObjectDeleteTest extends ActorTest {
class ObjectHeldTest extends ActorTest { class ObjectHeldTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass ObjectHeld" in { "pass ObjectHeld" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ObjectHeld(PlanetSideGUID(10), 1)) service ! AvatarServiceMessage("test", AvatarAction.ObjectHeld(PlanetSideGUID(10), 1))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectHeld(1))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectHeld(1)))
@ -153,7 +170,8 @@ class ObjectHeldTest extends ActorTest {
class PlanetsideAttributeTest extends ActorTest { class PlanetsideAttributeTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass PlanetsideAttribute" in { "pass PlanetsideAttribute" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.PlanetsideAttribute(PlanetSideGUID(10), 5, 1200L)) service ! AvatarServiceMessage("test", AvatarAction.PlanetsideAttribute(PlanetSideGUID(10), 5, 1200L))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlanetsideAttribute(5, 1200L))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlanetsideAttribute(5, 1200L)))
@ -166,7 +184,8 @@ class PlayerStateTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass PlayerState" in { "pass PlayerState" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.PlayerState(PlanetSideGUID(10), msg, false, false)) service ! AvatarServiceMessage("test", AvatarAction.PlayerState(PlanetSideGUID(10), msg, false, false))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlayerState(msg, false, false))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlayerState(msg, false, false)))
@ -177,7 +196,8 @@ class PlayerStateTest extends ActorTest {
class ReloadTest extends ActorTest { class ReloadTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass Reload" in { "pass Reload" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.Reload(PlanetSideGUID(10), PlanetSideGUID(40))) service ! AvatarServiceMessage("test", AvatarAction.Reload(PlanetSideGUID(10), PlanetSideGUID(40)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.Reload(PlanetSideGUID(40)))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.Reload(PlanetSideGUID(40))))
@ -191,7 +211,8 @@ class ChangeAmmoTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass ChangeAmmo" in { "pass ChangeAmmo" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ChangeAmmo(PlanetSideGUID(10), PlanetSideGUID(40), 0, PlanetSideGUID(40), ammoDef.ObjectId, PlanetSideGUID(41), ammoDef.Packet.ConstructorData(ammoBox).get)) service ! AvatarServiceMessage("test", AvatarAction.ChangeAmmo(PlanetSideGUID(10), PlanetSideGUID(40), 0, PlanetSideGUID(40), ammoDef.ObjectId, PlanetSideGUID(41), ammoDef.Packet.ConstructorData(ammoBox).get))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeAmmo(PlanetSideGUID(40), 0, PlanetSideGUID(40), ammoDef.ObjectId, PlanetSideGUID(41), ammoDef.Packet.ConstructorData(ammoBox).get))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeAmmo(PlanetSideGUID(40), 0, PlanetSideGUID(40), ammoDef.ObjectId, PlanetSideGUID(41), ammoDef.Packet.ConstructorData(ammoBox).get)))
@ -205,7 +226,8 @@ class ChangeFireModeTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass ChangeFireMode" in { "pass ChangeFireMode" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ChangeFireMode(PlanetSideGUID(10), PlanetSideGUID(40), 0)) service ! AvatarServiceMessage("test", AvatarAction.ChangeFireMode(PlanetSideGUID(10), PlanetSideGUID(40), 0))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireMode(PlanetSideGUID(40), 0))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireMode(PlanetSideGUID(40), 0)))
@ -216,7 +238,8 @@ class ChangeFireModeTest extends ActorTest {
class ChangeFireStateStartTest extends ActorTest { class ChangeFireStateStartTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass ChangeFireState_Start" in { "pass ChangeFireState_Start" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ChangeFireState_Start(PlanetSideGUID(10), PlanetSideGUID(40))) service ! AvatarServiceMessage("test", AvatarAction.ChangeFireState_Start(PlanetSideGUID(10), PlanetSideGUID(40)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireState_Start(PlanetSideGUID(40)))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireState_Start(PlanetSideGUID(40))))
@ -227,7 +250,8 @@ class ChangeFireStateStartTest extends ActorTest {
class ChangeFireStateStopTest extends ActorTest { class ChangeFireStateStopTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass ChangeFireState_Stop" in { "pass ChangeFireState_Stop" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.ChangeFireState_Stop(PlanetSideGUID(10), PlanetSideGUID(40))) service ! AvatarServiceMessage("test", AvatarAction.ChangeFireState_Stop(PlanetSideGUID(10), PlanetSideGUID(40)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireState_Stop(PlanetSideGUID(40)))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireState_Stop(PlanetSideGUID(40))))
@ -238,7 +262,8 @@ class ChangeFireStateStopTest extends ActorTest {
class WeaponDryFireTest extends ActorTest { class WeaponDryFireTest extends ActorTest {
"AvatarService" should { "AvatarService" should {
"pass WeaponDryFire" in { "pass WeaponDryFire" in {
val service = system.actorOf(Props[AvatarService], "service") ServiceManager.boot(system)
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
service ! Service.Join("test") service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.WeaponDryFire(PlanetSideGUID(10), PlanetSideGUID(40))) service ! AvatarServiceMessage("test", AvatarAction.WeaponDryFire(PlanetSideGUID(10), PlanetSideGUID(40)))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.WeaponDryFire(PlanetSideGUID(40)))) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.WeaponDryFire(PlanetSideGUID(40))))
@ -246,6 +271,177 @@ class WeaponDryFireTest extends ActorTest {
} }
} }
object AvatarServiceTest { /*
//decoy Preparation for these three Release tests is involved.
The ServiceManager must not only be set up correctly, but must be given a TaskResolver.
The AvatarService is started and that starts CorpseRemovalActor, an essential part of this test.
The CorpseRemovalActor needs that TaskResolver created by the ServiceManager;
but, another independent TaskResolver will be needed for manual parts of the test.
(The ServiceManager's TaskResolver can be "borrowed" but that requires writing code to intercept it.)
The Zone needs to be set up and initialized properly with a ZoneActor.
The ZoneActor builds the GUID Actor and the ZonePopulationActor.
ALL of these Actors will talk to each other.
The lines of communication can short circuit if the next Actor does not have the correct information.
Putting Actor startup in the main class, outside of the body of the test, helps.
Frequent pauses to allow everything to sort their messages also helps.
Even with all this work, the tests have a high chance of failure just due to being asynchronous.
*/
class AvatarReleaseTest extends ActorTest {
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val service = system.actorOf(Props[AvatarService], "release-test-service")
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.Continent = "test"
obj.Release
"AvatarService" should {
"pass Release" in {
expectNoMsg(100 milliseconds) //spacer
service ! Service.Join("test")
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMsg(100 milliseconds) //spacer
assert(zone.Corpses.size == 1)
assert(obj.HasGUID)
val guid = obj.GUID
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone, Some(1000000000))) //alive for one second
val reply1 = receiveOne(100 milliseconds)
assert(reply1.isInstanceOf[AvatarServiceResponse])
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
assert(reply1msg.toChannel == "/test/Avatar")
assert(reply1msg.avatar_guid == guid)
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
val reply2 = receiveOne(2 seconds)
assert(reply2.isInstanceOf[AvatarServiceResponse])
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
assert(reply2msg.toChannel.equals("/test/Avatar"))
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
expectNoMsg(200 milliseconds)
assert(zone.Corpses.isEmpty)
assert(!obj.HasGUID)
}
}
}
class AvatarReleaseEarly1Test extends ActorTest {
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val service = system.actorOf(Props[AvatarService], "release-test-service")
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.Continent = "test"
obj.Release
"AvatarService" should {
"pass Release" in {
expectNoMsg(100 milliseconds) //spacer
service ! Service.Join("test")
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMsg(100 milliseconds) //spacer
assert(zone.Corpses.size == 1)
assert(obj.HasGUID)
val guid = obj.GUID
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes!
val reply1 = receiveOne(100 milliseconds)
assert(reply1.isInstanceOf[AvatarServiceResponse])
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
assert(reply1msg.toChannel == "/test/Avatar")
assert(reply1msg.avatar_guid == guid)
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(obj)) //IMPORTANT: ONE ENTRY
val reply2 = receiveOne(100 milliseconds)
assert(reply2.isInstanceOf[AvatarServiceResponse])
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
assert(reply2msg.toChannel.equals("/test/Avatar"))
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
expectNoMsg(200 milliseconds)
assert(zone.Corpses.isEmpty)
assert(!obj.HasGUID)
}
}
}
class AvatarReleaseEarly2Test extends ActorTest {
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val service = system.actorOf(Props[AvatarService], "release-test-service")
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
val objAlt = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 1, 1)) //necessary clutter
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
obj.Continent = "test"
obj.Release
"AvatarService" should {
"pass Release" in {
expectNoMsg(100 milliseconds) //spacer
service ! Service.Join("test")
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(obj)
expectNoMsg(100 milliseconds) //spacer
assert(zone.Corpses.size == 1)
assert(obj.HasGUID)
val guid = obj.GUID
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes!
val reply1 = receiveOne(100 milliseconds)
assert(reply1.isInstanceOf[AvatarServiceResponse])
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
assert(reply1msg.toChannel == "/test/Avatar")
assert(reply1msg.avatar_guid == guid)
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(objAlt, obj)) //IMPORTANT: TWO ENTRIES
val reply2 = receiveOne(100 milliseconds)
assert(reply2.isInstanceOf[AvatarServiceResponse])
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
assert(reply2msg.toChannel.equals("/test/Avatar"))
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
expectNoMsg(200 milliseconds)
assert(zone.Corpses.isEmpty)
assert(!obj.HasGUID)
}
}
}
object AvatarServiceTest {
import java.util.concurrent.atomic.AtomicInteger
private val number = new AtomicInteger(1)
def TestName : String = {
s"service${number.getAndIncrement()}"
}
} }