Merge pull request #221 from Fate-JH/projectiles

Projectiles
This commit is contained in:
Fate-JH 2018-06-23 23:11:25 -04:00 committed by GitHub
commit cfeb9afb6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 3249 additions and 194 deletions

View file

@ -0,0 +1,36 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import akka.actor.ActorContext
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.types.PlanetSideEmpire
/**
* A `LocalProjectile` is a server-side object designed to populate a fake shared space.
* It is a placeholder intended to block out the existence of projectiles communicated from clients.
* All clients reserve the same internal range of user-generated GUID's from 40100 to 40124, inclusive.
* All clients recognize this same range independent of each other as "their own featureless projectiles."
* @see `Zone.MakeReservedObjects`<br>
* `Projectile.BaseUID`<br>
* `Projectile.RangeUID`
*/
class LocalProjectile extends PlanetSideServerObject {
def Faction = PlanetSideEmpire.NEUTRAL
def Definition = LocalProjectile.local
}
object LocalProjectile {
import net.psforever.objects.definition.ObjectDefinition
def local = new ObjectDefinition(0) { Name = "projectile" }
/**
* Instantiate and configure a `LocalProjectile` object.
* @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 `LocalProjectile` object
*/
def Constructor(id : Int, context : ActorContext) : LocalProjectile = {
new LocalProjectile()
}
}

View file

@ -1,8 +1,9 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import net.psforever.objects.definition.{AmmoBoxDefinition, ToolDefinition}
import net.psforever.objects.equipment.{Ammo, Equipment, FireModeDefinition, FireModeSwitch}
import net.psforever.objects.definition.{AmmoBoxDefinition, ProjectileDefinition, ToolDefinition}
import net.psforever.objects.equipment._
import net.psforever.objects.ballistics.Projectiles
import scala.annotation.tailrec
@ -59,6 +60,20 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
AmmoType
}
def Projectile : ProjectileDefinition = {
toolDef.ProjectileTypes({
val projIndices = FireMode.ProjectileTypeIndices
if(projIndices.isEmpty) {
AmmoTypeIndex //e.g., bullet_9mm -> bullet_9mm_projectile, bullet_9mm_AP -> bullet_9mm_AP_projectile
}
else {
projIndices(AmmoSlot.AmmoTypeIndex) //e.g., pulsar: f.mode1 -> pulsar_projectile, f.mode2 = pulsar_ap_projectile
}
})
}
def ProjectileType : Projectiles.Value = Projectile.ProjectileType
def Magazine : Int = AmmoSlot.Magazine
def Magazine_=(mag : Int) : Int = {

View file

@ -323,13 +323,12 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
Some(AccessPermissionGroup.Passenger)
}
case None =>
None
}
CargoHold(seatNumber) match {
case Some(_) =>
Some(AccessPermissionGroup.Passenger)
case None =>
None
CargoHold(seatNumber) match {
case Some(_) =>
Some(AccessPermissionGroup.Passenger)
case None =>
None
}
}
}
}

View file

@ -0,0 +1,28 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.ballistics
/**
* The different values for five common types of damage that can be dealt, based on target and application.
* In the same way, the five damage modifiers that are applied to the same kind of damage.
*/
trait DamageProfile {
def Damage0 : Int
def Damage0_=(damage : Int) : Int
def Damage1 : Int
def Damage1_=(damage : Int) : Int
def Damage2 : Int
def Damage2_=(damage : Int) : Int
def Damage3 : Int
def Damage3_=(damage : Int) : Int
def Damage4 : Int
def Damage4_=(damage : Int) : Int
}

View file

@ -0,0 +1,19 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.ballistics
/**
* An `Enumeration` of the damage type.
* These types are not necessarily representative of the kind of projectile being used;
* for example, the bolt driver's `bolt` is considered "splash."
*/
object DamageType extends Enumeration(1) {
type Type = Value
final val Direct,
Splash,
Radiation,
Aggravated,
Plasma,
Comet,
None = Value
}

View file

@ -0,0 +1,78 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.ballistics
import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
import net.psforever.objects.entity.SimpleWorldEntity
import net.psforever.types.Vector3
/**
* A summation of weapon (`Tool`) discharge.
* @param profile an explanation of the damage that can be performed by this discharge
* @param tool_def the weapon that caused this discharge
* @param shot_origin where the projectile started
* @param shot_angle in which direction the projectile was aimed when it was discharged
* @param resolution whether this projectile has encountered a target or wall;
* defaults to `Unresolved`
* @param fire_time when the weapon discharged was recorded;
* defaults to `System.nanoTime`
* @param hit_time when the discharge had its resolution status updated
*/
final case class Projectile(profile : ProjectileDefinition,
tool_def : ToolDefinition,
shot_origin : Vector3,
shot_angle : Vector3,
resolution : ProjectileResolution.Value,
fire_time : Long = System.nanoTime,
hit_time : Long = 0) {
/** Information about the current world coordinates and orientation of the projectile */
val current : SimpleWorldEntity = new SimpleWorldEntity()
/**
* Give the projectile the suggested resolution status.
* Update the world coordinates and orientation.
* @param pos the current position
* @param ang the current orientation
* @param resolution the resolution status
* @return a new projectile with the suggested resolution status, or the original projectile
*/
def Resolve(pos : Vector3, ang : Vector3, resolution : ProjectileResolution.Value) : Projectile = {
val obj = Resolve(resolution)
obj.current.Position = pos
obj.current.Orientation = ang
obj
}
/**
* Give the projectile the suggested resolution status.
* @param resolution the resolution status
* @return a new projectile with the suggested resolution status, or the original projectile
*/
def Resolve(resolution : ProjectileResolution.Value) : Projectile = {
resolution match {
case ProjectileResolution.Unresolved =>
this
case _ =>
Projectile(profile, tool_def, shot_origin, shot_angle, resolution, fire_time, System.nanoTime)
}
}
}
object Projectile {
/** the first projectile GUID used by all clients internally */
final val BaseUID : Int = 40100
/** all clients progress through 40100 to 40124 normally, skipping only for long-lived projectiles
* 40125 to 40149 are being reserved as a guard against undetected overflow */
final val RangeUID : Int = 40150
/**
* Overloaded constructor for an `Unresolved` projectile.
* @param profile an explanation of the damage that can be performed by this discharge
* @param tool_def the weapon that caused this discharge
* @param shot_origin where the projectile started
* @param shot_angle in which direction the projectile was aimed when it was discharged
* @return the `Projectile` object
*/
def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, shot_origin : Vector3, shot_angle : Vector3) : Projectile = {
Projectile(profile, tool_def, shot_origin, shot_angle, ProjectileResolution.Unresolved)
}
}

View file

@ -0,0 +1,18 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.ballistics
/**
* An `Enumeration` of outcomes regarding what actually happened to the projectile.
*/
object ProjectileResolution extends Enumeration {
type Type = Value
val
Unresolved, //original basic non-resolution
MissedShot, //projectile did not encounter any collision object and was despawned
Resolved, //a general "projectile encountered something" status with a more specific resolution
Hit, //direct hit, one target
Splash, //area of effect damage, potentially multiple targets
Lash //lashing damage, potentially multiple targets
= Value
}

View file

@ -0,0 +1,146 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.ballistics
/**
* An `Enumeration` of all the projectile types in the game, paired with their object id as the `Value`.
*/
object Projectiles extends Enumeration {
final val bullet_105mm_projectile = Value(1)
final val bullet_12mm_projectile = Value(4)
final val bullet_12mm_projectileb = Value(5)
final val bullet_150mm_projectile = Value(7)
final val bullet_15mm_apc_projectile = Value(10)
final val bullet_15mm_projectile = Value(11)
final val bullet_20mm_apc_projectile = Value(17)
final val bullet_20mm_projectile = Value(18)
final val bullet_25mm_projectile = Value(20)
final val bullet_35mm_projectile = Value(22)
final val bullet_75mm_apc_projectile = Value(26)
final val bullet_75mm_projectile = Value(27)
final val bullet_9mm_AP_projectile = Value(30)
final val bullet_9mm_projectile = Value(31)
final val anniversary_projectilea = Value(58)
final val anniversary_projectileb = Value(59)
final val aphelion_immolation_cannon_projectile = Value(87)
final val aphelion_laser_projectile = Value(91)
final val aphelion_plasma_rocket_projectile = Value(99)
final val aphelion_ppa_projectile = Value(103)
final val aphelion_starfire_projectile = Value(108)
final val bolt_projectile = Value(147)
final val burster_projectile = Value(155)
final val chainblade_projectile = Value(176)
final val colossus_100mm_projectile = Value(181)
final val colossus_burster_projectile = Value(188)
final val colossus_chaingun_projectile = Value(193)
final val colossus_cluster_bomb_projectile = Value(197)
final val colossus_tank_cannon_projectile = Value(207)
final val comet_projectile = Value(210)
final val dualcycler_projectile = Value(266)
final val dynomite_projectile = Value(268)
final val energy_cell_projectile = Value(273)
final val energy_gun_nc_projectile = Value(277)
final val energy_gun_tr_projectile = Value(279)
final val energy_gun_vs_projectile = Value(281)
final val enhanced_energy_cell_projectile = Value(282)
final val enhanced_quasar_projectile = Value(283)
final val falcon_projectile = Value(286)
final val firebird_missile_projectile = Value(288)
final val flail_projectile = Value(296)
final val flamethrower_fireball = Value(302)
final val flamethrower_projectile = Value(303)
final val flux_cannon_apc_projectile = Value(305)
final val flux_cannon_thresher_projectile = Value(308)
final val fluxpod_projectile = Value(311)
final val forceblade_projectile = Value(325)
final val frag_cartridge_projectile = Value(328)
final val frag_cartridge_projectile_b = Value(329)
final val frag_grenade_projectile = Value(332)
final val frag_grenade_projectile_enh = Value(333)
final val galaxy_gunship_gun_projectile = Value(341)
final val gauss_cannon_projectile = Value(348)
final val grenade_projectile = Value(372)
final val heavy_grenade_projectile = Value(392)
final val heavy_rail_beam_projectile = Value(395)
final val heavy_sniper_projectile = Value(397)
final val hellfire_projectile = Value(400)
final val hunter_seeker_missile_dumbfire = Value(404)
final val hunter_seeker_missile_projectile = Value(405)
final val jammer_cartridge_projectile = Value(414)
final val jammer_cartridge_projectile_b = Value(415)
final val jammer_grenade_projectile = Value(418)
final val jammer_grenade_projectile_enh = Value(419)
final val katana_projectile = Value(422)
final val katana_projectileb = Value(423)
final val lancer_projectile = Value(427)
final val lasher_projectile = Value(430)
final val lasher_projectile_ap = Value(431)
final val liberator_bomb_cluster_bomblet_projectile = Value(436)
final val liberator_bomb_cluster_projectile = Value(437)
final val liberator_bomb_projectile = Value(438)
final val maelstrom_grenade_projectile = Value(465)
final val maelstrom_grenade_projectile_contact = Value(466)
final val maelstrom_stream_projectile = Value(467)
final val magcutter_projectile = Value(469)
final val melee_ammo_projectile = Value(541)
final val meteor_common = Value(543)
final val meteor_projectile_b_large = Value(544)
final val meteor_projectile_b_medium = Value(545)
final val meteor_projectile_b_small = Value(546)
final val meteor_projectile_large = Value(547)
final val meteor_projectile_medium = Value(548)
final val meteor_projectile_small = Value(549)
final val mine_projectile = Value(551)
final val mine_sweeper_projectile = Value(554)
final val mine_sweeper_projectile_enh = Value(555)
final val oicw_projectile = Value(602)
final val pellet_gun_projectile = Value(631)
final val peregrine_dual_machine_gun_projectile = Value(639)
final val peregrine_mechhammer_projectile = Value(647)
final val peregrine_particle_cannon_projectile = Value(654)
final val peregrine_rocket_pod_projectile = Value(657)
final val peregrine_sparrow_projectile = Value(661)
final val phalanx_av_projectile = Value(665)
final val phalanx_flak_projectile = Value(667)
final val phalanx_projectile = Value(669)
final val phoenix_missile_guided_projectile = Value(675)
final val phoenix_missile_projectile = Value(676)
final val plasma_cartridge_projectile = Value(678)
final val plasma_cartridge_projectile_b = Value(679)
final val plasma_grenade_projectile = Value(682)
final val plasma_grenade_projectile_B = Value(683)
final val pounder_projectile = Value(694)
final val pounder_projectile_enh = Value(695)
final val ppa_projectile = Value(696)
final val pulsar_ap_projectile = Value(702)
final val pulsar_projectile = Value(703)
final val quasar_projectile = Value(713)
final val radiator_grenade_projectile = Value(718)
final val radiator_sticky_projectile = Value(719)
final val reaver_rocket_projectile = Value(723)
final val rocket_projectile = Value(735)
final val rocklet_flak_projectile = Value(738)
final val rocklet_jammer_projectile = Value(739)
final val scattercannon_projectile = Value(746)
final val scythe_projectile = Value(748)
final val scythe_projectile_slave = Value(749)
final val shotgun_shell_AP_projectile = Value(757)
final val shotgun_shell_projectile = Value(758)
final val six_shooter_projectile = Value(763)
final val skyguard_flak_cannon_projectile = Value(787)
final val sparrow_projectile = Value(792)
final val sparrow_secondary_projectile = Value(793)
final val spiker_projectile = Value(818)
final val spitfire_aa_ammo_projectile = Value(821)
final val spitfire_ammo_projectile = Value(824)
final val starfire_projectile = Value(831)
final val striker_missile_projectile = Value(840)
final val striker_missile_targeting_projectile = Value(841)
final val trek_projectile = Value(878)
final val vanu_sentry_turret_projectile = Value(944)
final val vulture_bomb_projectile = Value(988)
final val vulture_nose_bullet_projectile = Value(989)
final val vulture_tail_bullet_projectile = Value(991)
final val wasp_gun_projectile = Value(999)
final val wasp_rocket_projectile = Value(1001)
final val winchester_projectile = Value(1005)
}

View file

@ -0,0 +1,166 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition
import net.psforever.objects.ballistics.{DamageProfile, DamageType, Projectiles}
/**
* The definition that outlines the damage-dealing characteristics of any projectile.
* `Tool` objects emit `ProjectileDefinition` objects and that is later wrapped into a `Projectile` object.
* @param objectId the object's identifier number
*/
class ProjectileDefinition(objectId : Int) extends ObjectDefinition(objectId) with DamageProfile {
private val projectileType : Projectiles.Value = Projectiles(objectId) //let throw NoSuchElementException
private var damage0 : Int = 0
private var damage1 : Option[Int] = None
private var damage2 : Option[Int] = None
private var damage3 : Option[Int] = None
private var damage4 : Option[Int] = None
private var acceleration : Int = 0
private var accelerationUntil : Float = 0f
private var damageType : DamageType.Value = DamageType.None
private var damageTypeSecondary : DamageType.Value = DamageType.None
private var degradeDelay : Float = 1f
private var degradeMultiplier : Float = 1f
private var initialVelocity : Int = 1
private var lifespan : Float = 1f
private var damageAtEdge : Float = 1f
private var damageRadius : Float = 1f
private var useDamage1Subtract : Boolean = false
Name = "projectile"
def ProjectileType : Projectiles.Value = projectileType
def UseDamage1Subtract : Boolean = useDamage1Subtract
def UseDamage1Subtract_=(useDamage1Subtract : Boolean) : Boolean = {
this.useDamage1Subtract = useDamage1Subtract
UseDamage1Subtract
}
def Damage0 : Int = damage0
def Damage0_=(damage : Int) : Int = {
damage0 = damage
damage0
}
def Damage0_=(damage : Option[Int]) : Int = {
damage0 = damage match {
case Some(value) => value
case None => 0 //can not be set to None
}
Damage0
}
def Damage1 : Int = damage1.getOrElse(Damage0)
def Damage1_=(damage : Int) : Int = Damage1_=(Some(damage))
def Damage1_=(damage : Option[Int]) : Int = {
this.damage1 = damage
Damage1
}
def Damage2 : Int = damage2.getOrElse(Damage1)
def Damage2_=(damage : Int) : Int = Damage2_=(Some(damage))
def Damage2_=(damage : Option[Int]) : Int = {
this.damage2 = damage
Damage2
}
def Damage3 : Int = damage3.getOrElse(Damage2)
def Damage3_=(damage : Int) : Int = Damage3_=(Some(damage))
def Damage3_=(damage : Option[Int]) : Int = {
this.damage3 = damage
Damage3
}
def Damage4 : Int = damage4.getOrElse(Damage3)
def Damage4_=(damage : Int) : Int = Damage4_=(Some(damage))
def Damage4_=(damage : Option[Int]) : Int = {
this.damage4 = damage
Damage4
}
def Acceleration : Int = acceleration
def Acceleration_=(accel : Int) : Int = {
acceleration = accel
Acceleration
}
def AccelerationUntil : Float = accelerationUntil
def AccelerationUntil_=(accelUntil : Float) : Float = {
accelerationUntil = accelUntil
AccelerationUntil
}
def ProjectileDamageType : DamageType.Value = damageType
def ProjectileDamageType_=(damageType1 : DamageType.Value) : DamageType.Value = {
damageType = damageType1
ProjectileDamageType
}
def ProjectileDamageTypeSecondary : DamageType.Value = damageTypeSecondary
def ProjectileDamageTypeSecondary_=(damageTypeSecondary1 : DamageType.Value) : DamageType.Value = {
damageTypeSecondary = damageTypeSecondary1
ProjectileDamageTypeSecondary
}
def DegradeDelay : Float = degradeDelay
def DegradeDelay_=(degradeDelay : Float) : Float = {
this.degradeDelay = degradeDelay
DegradeDelay
}
def DegradeMultiplier : Float = degradeMultiplier
def DegradeMultiplier_=(degradeMultiplier : Float) : Float = {
this.degradeMultiplier = degradeMultiplier
DegradeMultiplier
}
def InitialVelocity : Int = initialVelocity
def InitialVelocity_=(initialVelocity : Int) : Int = {
this.initialVelocity = initialVelocity
InitialVelocity
}
def Lifespan : Float = lifespan
def Lifespan_=(lifespan : Float) : Float = {
this.lifespan = lifespan
Lifespan
}
def DamageAtEdge : Float = damageAtEdge
def DamageAtEdge_=(damageAtEdge : Float) : Float = {
this.damageAtEdge = damageAtEdge
DamageAtEdge
}
def DamageRadius : Float = damageRadius
def DamageRadius_=(damageRadius : Float) : Float = {
this.damageRadius = damageRadius
DamageRadius
}
}
object ProjectileDefinition {
def apply(projectileType : Projectiles.Value) : ProjectileDefinition = {
new ProjectileDefinition(projectileType.id)
}
}

View file

@ -8,12 +8,15 @@ import scala.collection.mutable
class ToolDefinition(objectId : Int) extends EquipmentDefinition(objectId) {
private val ammoTypes : mutable.ListBuffer[AmmoBoxDefinition] = new mutable.ListBuffer[AmmoBoxDefinition]
private val projectileTypes : mutable.ListBuffer[ProjectileDefinition] = new mutable.ListBuffer[ProjectileDefinition]
private val fireModes : mutable.ListBuffer[FireModeDefinition] = new mutable.ListBuffer[FireModeDefinition]
Name = "tool"
Packet = ToolDefinition.converter
def AmmoTypes : mutable.ListBuffer[AmmoBoxDefinition] = ammoTypes
def ProjectileTypes : mutable.ListBuffer[ProjectileDefinition] = projectileTypes
def FireModes : mutable.ListBuffer[FireModeDefinition] = fireModes
def NextFireModeIndex(index : Int) : Int = index + 1

View file

@ -4,8 +4,8 @@ package net.psforever.objects.entity
import net.psforever.types.Vector3
class SimpleWorldEntity extends WorldEntity {
private var coords : Vector3 = Vector3(0f, 0f, 0f)
private var orient : Vector3 = Vector3(0f, 0f, 0f)
private var coords : Vector3 = Vector3.Zero
private var orient : Vector3 = Vector3.Zero
private var vel : Option[Vector3] = None
def Position : Vector3 = coords

View file

@ -2,24 +2,30 @@
package net.psforever.objects.equipment
import net.psforever.objects.Tool
import net.psforever.objects.ballistics.DamageProfile
import scala.collection.mutable
class FireModeDefinition {
/** indices pointing to all ammo types used */
/** indices pointing to all ammo types used, in (an) order
* the ammo types list will be available from the `ToolDefinition` */
private val ammoTypeIndices : mutable.ListBuffer[Int] = mutable.ListBuffer[Int]()
/** custom indices pointing to the projectile type used for this mode's ammo types
* for most weapon fire modes, this list will be empty and the projectile will align with the `ammoTypeIndex`
* if at least one ammo type has a redirected projectile type, all projectiles must be defined for this mode */
private val projectileTypeIndices : mutable.ListBuffer[Int] = mutable.ListBuffer[Int]()
/** ammunition slot number this fire mode utilizes */
private var ammoSlotIndex : Int = 0
/** how many rounds are replenished each reload cycle */
private var magazine : Int = 1
/** how much is subtracted from the magazine each fire cycle;
* most weapons will only fire 1 round per fire cycle; the flamethrower, fire mode 1, fires 50 */
* most weapons will only fire 1 round per fire cycle; the flamethrower in fire mode 1 fires 50 */
private var rounds : Int = 1
/** how many sub-rounds are queued per round fired;
* the flechette fires 8 pellets per shell and generates 8 fire reports before the ammo count goes down */
private var chamber : Int = 1
//damage modifiers will follow here ... ?
/** modifiers for each damage type */
private val modifiers : DamageModifiers = new DamageModifiers
def AmmoSlotIndex : Int = ammoSlotIndex
@ -34,6 +40,12 @@ class FireModeDefinition {
ammoTypeIndices += index
}
def ProjectileTypeIndices : mutable.ListBuffer[Int] = projectileTypeIndices
def ProjectileTypeIndices_=(index : Int) : mutable.ListBuffer[Int] = {
projectileTypeIndices += index
}
def Magazine : Int = magazine
def Magazine_=(inMagazine : Int) : Int = {
@ -55,6 +67,8 @@ class FireModeDefinition {
Chamber
}
def Modifiers : DamageModifiers = modifiers
/**
* Shoot a weapon, remove an anticipated amount of ammunition.
* @param weapon the weapon
@ -105,3 +119,46 @@ class InfiniteFireModeDefinition extends FireModeDefinition {
*/
override def Discharge(weapon : Tool) : Int = 1
}
class DamageModifiers extends DamageProfile {
private var damage0 : Int = 0
private var damage1 : Int = 0
private var damage2 : Int = 0
private var damage3 : Int = 0
private var damage4 : Int = 0
def Damage0 : Int = damage0
def Damage0_=(damage : Int) : Int = {
damage0 = damage
Damage0
}
def Damage1 : Int = damage1
def Damage1_=(damage : Int) : Int = {
damage1 = damage
Damage1
}
def Damage2 : Int = damage2
def Damage2_=(damage : Int) : Int = {
damage2 = damage
Damage2
}
def Damage3 : Int = damage3
def Damage3_=(damage : Int) : Int = {
damage3 = damage
Damage3
}
def Damage4 : Int = damage4
def Damage4_=(damage : Int) : Int = {
damage4 = damage
Damage4
}
}

View file

@ -3,7 +3,8 @@ package net.psforever.objects.zones
import akka.actor.{ActorContext, ActorRef, Props}
import akka.routing.RandomPool
import net.psforever.objects.{Avatar, PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects._
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.UniqueNumberSystem
@ -45,8 +46,6 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
private var accessor : ActorRef = ActorRef.noSender
/** The basic support structure for the globally unique number system used by this `Zone`. */
private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536))
guid.AddPool("environment", (0 to 3000).toList) //TODO tailer ro suit requirements of zone
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. */
private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
/** */
@ -88,6 +87,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
def Init(implicit context : ActorContext) : Unit = {
if(accessor == ActorRef.noSender) {
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
SetupNumberPools()
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
ground = context.actorOf(Props(classOf[ZoneGroundActor], this, equipmentOnGround), s"$Id-ground")
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
@ -100,6 +100,25 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
}
}
def SetupNumberPools() : Unit = {
guid.AddPool("environment", (0 to 3000).toList) //TODO tailor to suit requirements of zone
//TODO unlump pools later; do not make any single pool too big
guid.AddPool("dynamic", (3001 to 10000).toList).Selector = new RandomSelector //TODO all things will be registered here, for now
guid.AddPool("b", (10001 to 15000).toList).Selector = new RandomSelector
guid.AddPool("c", (15001 to 20000).toList).Selector = new RandomSelector
guid.AddPool("d", (20001 to 25000).toList).Selector = new RandomSelector
guid.AddPool("e", (25001 to 30000).toList).Selector = new RandomSelector
guid.AddPool("f", (30001 to 35000).toList).Selector = new RandomSelector
guid.AddPool("g", (35001 until 40100).toList).Selector = new RandomSelector
guid.AddPool("projectiles", (Projectile.BaseUID until Projectile.RangeUID).toList)
//TODO disabled temporarily to lighten load times
//guid.AddPool("h", (40150 to 45000).toList).Selector = new RandomSelector
//guid.AddPool("i", (45001 to 50000).toList).Selector = new RandomSelector
//guid.AddPool("j", (50001 to 55000).toList).Selector = new RandomSelector
//guid.AddPool("k", (55001 to 60000).toList).Selector = new RandomSelector
//guid.AddPool("l", (60001 to 65535).toList).Selector = new RandomSelector
}
/**
* A reference to the primary `Actor` that governs this `Zone`.
* @return an `ActorRef`
@ -150,10 +169,15 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
/**
* Replace the current globally unique identifier system with a new one.
* The replacement will not occur if the current system is populated or if its synchronized reference has been created.
* The primary use of this function should be testing.
* A warning will be issued.
* @return synchronized reference to the globally unique identifier system
*/
def GUID(hub : NumberPoolHub) : Boolean = {
if(actor == ActorRef.noSender && guid.Pools.map({case ((_, pool)) => pool.Count}).sum == 0) {
import org.fusesource.jansi.Ansi.Color.RED
import org.fusesource.jansi.Ansi.ansi
println(ansi().fgBright(RED).a(s"""Caution: replacement of the number pool system for zone $Id; function is for testing purposes only""").reset())
guid = hub
true
}
@ -162,6 +186,43 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
}
}
/**
* Wraps around the globally unique identifier system to insert a new number pool.
* Throws exceptions for specific reasons if the pool can not be populated before the system has been started.
* @see `NumberPoolHub.AddPool`
* @param name the name of the pool
* @param pool the numbers that will belong to the pool
* @return `true`, if the new pool is created;
* `false`, if the new pool can not be created because the system has already been started
*/
def AddPool(name : String, pool : Seq[Int]) : Boolean = {
if(accessor == ActorRef.noSender) {
guid.AddPool(name, pool.toList)
true
}
else {
false
}
}
/**
* Wraps around the globally unique identifier system to remove an existing number pool.
* Throws exceptions for specific reasons if the pool can not be removed before the system has been started.
* @see `NumberPoolHub.RemovePool`
* @param name the name of the pool
* @return `true`, if the new pool is un-made;
* `false`, if the new pool can not be removed because the system has already been started
*/
def RemovePool(name : String) : Boolean = {
if(accessor == ActorRef.noSender) {
guid.RemovePool(name)
true
}
else {
false
}
}
/**
* Recover an object from the globally unique identifier system by the number that was assigned previously.
* @param object_guid the globally unique identifier requested

View file

@ -3,11 +3,23 @@ package objects
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import com.typesafe.config.{ConfigFactory, ConfigValueFactory}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import org.specs2.specification.Scope
abstract class ActorTest(sys : ActorSystem = ActorSystem("system")) extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
abstract class ActorTest(sys : ActorSystem = ActorSystem("system", ConfigFactory.parseMap(ActorTest.LoggingConfig)))
extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
}
object ActorTest {
import scala.collection.JavaConverters._
private val LoggingConfig = Map(
"akka.loggers" -> List("akka.testkit.TestEventListener").asJava,
"akka.loglevel" -> "OFF",
"akka.stdout-loglevel" -> "OFF",
"akka.log-dead-letters" -> "OFF"
).asJava
}

View file

@ -11,8 +11,10 @@ import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire
import org.specs2.mutable.Specification
import services.ServiceManager
import services.galaxy.GalaxyService
import scala.concurrent.duration.Duration
import scala.concurrent.duration._
class AmenityTest extends Specification {
class AmenityObject extends Amenity {
@ -122,15 +124,20 @@ class BuildingControl1Test extends ActorTest {
}
class BuildingControl2Test extends ActorTest {
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService], "galaxy")
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test")
"Building Control" should {
"convert and assert faction affinity on convert request" in {
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test")
assert(bldg.Faction == PlanetSideEmpire.TR)
expectNoMsg(250 milliseconds)
bldg.Actor ! "startup"
expectNoMsg(250 milliseconds)
assert(bldg.Faction == PlanetSideEmpire.TR)
bldg.Actor ! FactionAffinity.ConvertFactionAffinity(PlanetSideEmpire.VS)
val reply = receiveOne(Duration.create(100, "ms"))
val reply = receiveOne(500 milliseconds)
assert(reply.isInstanceOf[FactionAffinity.AssertFactionAffinity])
assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].obj == bldg)
assert(reply.asInstanceOf[FactionAffinity.AssertFactionAffinity].faction == PlanetSideEmpire.VS)
@ -140,19 +147,25 @@ class BuildingControl2Test extends ActorTest {
}
class BuildingControl3Test extends ActorTest {
ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService], "galaxy")
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test")
val door1 = Door(GlobalDefinitions.door)
door1.GUID = PlanetSideGUID(1)
door1.Actor = system.actorOf(Props(classOf[DoorControl], door1), "door1-test")
val door2 = Door(GlobalDefinitions.door)
door2.GUID = PlanetSideGUID(2)
door2.Actor = system.actorOf(Props(classOf[DoorControl], door2), "door2-test")
bldg.Amenities = door2
bldg.Amenities = door1
"Building Control" should {
"convert and assert faction affinity on convert request, and for each of its amenities" in {
val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "building-test")
val door1 = Door(GlobalDefinitions.door)
door1.GUID = PlanetSideGUID(1)
door1.Actor = system.actorOf(Props(classOf[DoorControl], door1), "door1-test")
val door2 = Door(GlobalDefinitions.door)
door2.GUID = PlanetSideGUID(2)
door2.Actor = system.actorOf(Props(classOf[DoorControl], door2), "door2-test")
bldg.Amenities = door2
bldg.Amenities = door1
expectNoMsg(250 milliseconds)
bldg.Actor ! "startup"
expectNoMsg(250 milliseconds)
assert(bldg.Faction == PlanetSideEmpire.TR)
assert(bldg.Amenities.length == 2)
assert(bldg.Amenities.head == door2)

View file

@ -236,6 +236,50 @@ class EquipmentTest extends Specification {
obj.AmmoType mustEqual Ammo.rocket
}
"projectile types and ammo types" in {
val suppressor_wep = Tool(suppressor)
suppressor_wep.ProjectileType mustEqual bullet_9mm_projectile.ProjectileType
suppressor_wep.NextAmmoType
suppressor_wep.ProjectileType mustEqual bullet_9mm_AP_projectile.ProjectileType
suppressor_wep.NextAmmoType
suppressor_wep.ProjectileType mustEqual bullet_9mm_projectile.ProjectileType
}
"projectile types and fire modes" in {
val pulsar_wep = Tool(pulsar)
pulsar_wep.ProjectileType mustEqual pulsar_projectile.ProjectileType
pulsar_wep.NextFireMode
pulsar_wep.ProjectileType mustEqual pulsar_ap_projectile.ProjectileType
pulsar_wep.NextFireMode
pulsar_wep.ProjectileType mustEqual pulsar_projectile.ProjectileType
}
"projectile types and fire modes / ammo types" in {
val punisher_wep = Tool(punisher)
punisher_wep.ProjectileType mustEqual bullet_9mm_projectile.ProjectileType
punisher_wep.NextAmmoType
punisher_wep.ProjectileType mustEqual bullet_9mm_AP_projectile.ProjectileType
punisher_wep.NextFireMode
punisher_wep.ProjectileType mustEqual rocket_projectile.ProjectileType
punisher_wep.NextAmmoType
punisher_wep.ProjectileType mustEqual frag_cartridge_projectile.ProjectileType
punisher_wep.NextAmmoType
punisher_wep.ProjectileType mustEqual jammer_cartridge_projectile.ProjectileType
punisher_wep.NextFireMode
punisher_wep.ProjectileType mustEqual bullet_9mm_AP_projectile.ProjectileType
punisher_wep.NextAmmoType
punisher_wep.ProjectileType mustEqual bullet_9mm_projectile.ProjectileType
punisher_wep.NextFireMode
punisher_wep.ProjectileType mustEqual jammer_cartridge_projectile.ProjectileType
punisher_wep.NextAmmoType
punisher_wep.ProjectileType mustEqual plasma_cartridge_projectile.ProjectileType
punisher_wep.NextAmmoType
punisher_wep.ProjectileType mustEqual rocket_projectile.ProjectileType
}
"discharge (1)" in {
val obj = Tool(GlobalDefinitions.punisher)
obj.Magazine mustEqual 30

View file

@ -0,0 +1,198 @@
// Copyright (c) 2017 PSForever
package objects
import net.psforever.objects.{GlobalDefinitions, LocalProjectile, Tool}
import net.psforever.objects.ballistics.{DamageType, Projectile, ProjectileResolution, Projectiles}
import net.psforever.objects.definition.ProjectileDefinition
import net.psforever.types.Vector3
import org.specs2.mutable.Specification
class ProjectileTest extends Specification {
"LocalProjectile" should {
"construct" in {
val obj = new LocalProjectile() //since they're just placeholders, they only need to construct
obj.Definition.ObjectId mustEqual 0
obj.Definition.Name mustEqual "projectile"
}
}
"ProjectileDefinition" should {
"define (default)" in {
val obj = new ProjectileDefinition(31) //9mmbullet_projectile
obj.ProjectileType mustEqual Projectiles.bullet_9mm_projectile
obj.ObjectId mustEqual 31
obj.Damage0 mustEqual 0
obj.Damage1 mustEqual 0
obj.Damage2 mustEqual 0
obj.Damage3 mustEqual 0
obj.Damage4 mustEqual 0
obj.Acceleration mustEqual 0
obj.AccelerationUntil mustEqual 0f
obj.ProjectileDamageType mustEqual DamageType.None
obj.ProjectileDamageTypeSecondary mustEqual DamageType.None
obj.DegradeDelay mustEqual 1f
obj.DegradeMultiplier mustEqual 1f
obj.InitialVelocity mustEqual 1
obj.Lifespan mustEqual 1f
obj.DamageAtEdge mustEqual 1f
obj.DamageRadius mustEqual 1f
obj.UseDamage1Subtract mustEqual false
}
"define (custom)" in {
val obj = new ProjectileDefinition(31) //9mmbullet_projectile
obj.Damage0 = 2
obj.Damage1 = 4
obj.Damage2 = 8
obj.Damage3 = 16
obj.Damage4 = 32
obj.Acceleration = 5
obj.AccelerationUntil = 5.5f
obj.ProjectileDamageType = DamageType.Splash
obj.ProjectileDamageTypeSecondary = DamageType.Radiation
obj.DegradeDelay = 11.1f
obj.DegradeMultiplier = 22.2f
obj.InitialVelocity = 50
obj.Lifespan = 11.2f
obj.DamageAtEdge = 3f
obj.DamageRadius = 3f
obj.UseDamage1Subtract = true
obj.Damage0 mustEqual 2
obj.Damage1 mustEqual 4
obj.Damage2 mustEqual 8
obj.Damage3 mustEqual 16
obj.Damage4 mustEqual 32
obj.Acceleration mustEqual 5
obj.AccelerationUntil mustEqual 5.5f
obj.ProjectileDamageType mustEqual DamageType.Splash
obj.ProjectileDamageTypeSecondary mustEqual DamageType.Radiation
obj.DegradeDelay mustEqual 11.1f
obj.DegradeMultiplier mustEqual 22.2f
obj.InitialVelocity mustEqual 50
obj.Lifespan mustEqual 11.2f
obj.DamageAtEdge mustEqual 3f
obj.DamageRadius mustEqual 3f
obj.UseDamage1Subtract mustEqual true
}
"define (failure)" in {
Projectiles(31) mustEqual Projectiles.bullet_9mm_projectile
try {
ProjectileDefinition(Projectiles.bullet_9mm_projectile) //passes
}
catch {
case _ : NoSuchElementException =>
ko
}
Projectiles(2) must throwA[NoSuchElementException]
new ProjectileDefinition(2) must throwA[NoSuchElementException]
}
"cascade damage values" in {
val obj = new ProjectileDefinition(31) //9mmbullet_projectile
obj.Damage4 = 32
obj.Damage3 = 16
obj.Damage2 = 8
obj.Damage1 = 4
obj.Damage0 = 2
//initial
obj.Damage4 mustEqual 32
obj.Damage3 mustEqual 16
obj.Damage2 mustEqual 8
obj.Damage1 mustEqual 4
obj.Damage0 mustEqual 2
//negate Damage4
obj.Damage4 = None
obj.Damage4 mustEqual 16
obj.Damage3 mustEqual 16
obj.Damage2 mustEqual 8
obj.Damage1 mustEqual 4
obj.Damage0 mustEqual 2
//negate Damage3
obj.Damage3 = None
obj.Damage4 mustEqual 8
obj.Damage3 mustEqual 8
obj.Damage2 mustEqual 8
obj.Damage1 mustEqual 4
obj.Damage0 mustEqual 2
//negate Damage2
obj.Damage2 = None
obj.Damage4 mustEqual 4
obj.Damage3 mustEqual 4
obj.Damage2 mustEqual 4
obj.Damage1 mustEqual 4
obj.Damage0 mustEqual 2
//negate Damage1
obj.Damage1 = None
obj.Damage4 mustEqual 2
obj.Damage3 mustEqual 2
obj.Damage2 mustEqual 2
obj.Damage1 mustEqual 2
obj.Damage0 mustEqual 2
//negate Damage0
obj.Damage0 = None
obj.Damage4 mustEqual 0
obj.Damage3 mustEqual 0
obj.Damage2 mustEqual 0
obj.Damage1 mustEqual 0
obj.Damage0 mustEqual 0
//set Damage3, set Damage0
obj.Damage3 = 13
obj.Damage0 = 7
obj.Damage4 mustEqual 13
obj.Damage3 mustEqual 13
obj.Damage2 mustEqual 7
obj.Damage1 mustEqual 7
obj.Damage0 mustEqual 7
}
}
"Projectile" should {
"construct" in {
val beamer_wep = Tool(GlobalDefinitions.beamer)
val projectile = beamer_wep.Projectile
val obj = Projectile(projectile, beamer_wep.Definition, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
obj.profile mustEqual beamer_wep.Projectile
obj.tool_def mustEqual GlobalDefinitions.beamer
obj.shot_origin mustEqual Vector3(1.2f, 3.4f, 5.6f)
obj.shot_angle mustEqual Vector3(0.2f, 0.4f, 0.6f)
obj.resolution mustEqual ProjectileResolution.Unresolved
obj.fire_time <= System.nanoTime mustEqual true
obj.hit_time mustEqual 0
}
"resolve" in {
val beamer_wep = Tool(GlobalDefinitions.beamer)
val projectile = beamer_wep.Projectile
val obj = Projectile(projectile, beamer_wep.Definition, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
val obj2 = obj.Resolve(ProjectileResolution.MissedShot)
obj.resolution mustEqual ProjectileResolution.Unresolved
obj.fire_time <= System.nanoTime mustEqual true
obj.hit_time mustEqual 0
obj2.resolution mustEqual ProjectileResolution.MissedShot
obj2.fire_time == obj.fire_time mustEqual true
obj2.hit_time <= System.nanoTime mustEqual true
obj2.fire_time <= obj2.hit_time mustEqual true
}
"resolve, with coordinates" in {
val beamer_wep = Tool(GlobalDefinitions.beamer)
val projectile = beamer_wep.Projectile
val obj = Projectile(projectile, beamer_wep.Definition, Vector3(1.2f, 3.4f, 5.6f), Vector3(0.2f, 0.4f, 0.6f))
val obj2 = obj.Resolve(Vector3(7.2f, 8.4f, 9.6f), Vector3(1.2f, 1.4f, 1.6f), ProjectileResolution.Resolved)
obj.resolution mustEqual ProjectileResolution.Unresolved
obj.current.Position mustEqual Vector3.Zero
obj.current.Orientation mustEqual Vector3.Zero
obj2.resolution mustEqual ProjectileResolution.Resolved
obj2.current.Position mustEqual Vector3(7.2f, 8.4f, 9.6f)
obj2.current.Orientation mustEqual Vector3(1.2f, 1.4f, 1.6f)
}
}
}

View file

@ -170,6 +170,24 @@ class VehicleSpawnPadObjectBuilderTest extends ActorTest {
}
}
class LocalProjectileBuilderTest extends ActorTest {
import net.psforever.objects.LocalProjectile
"Local ProjectileBuilder" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
LocalProjectile.Constructor), hub), "locker")
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[LocalProjectile])
assert(reply.asInstanceOf[LocalProjectile].HasGUID)
assert(reply.asInstanceOf[LocalProjectile].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
}
}
}
class LockerObjectBuilderTest extends ActorTest {
import net.psforever.objects.serverobject.mblocker.Locker
"LockerObjectBuilder" should {

View file

@ -3,6 +3,7 @@ package objects
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.TestProbe
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.serverobject.structures.StructureType
@ -323,10 +324,10 @@ object VehicleSpawnPadControlTest {
import net.psforever.objects.Tool
import net.psforever.types.CharacterGender
val zone = new Zone("test-zone", map, 0)
val zone = new Zone("test-zone", map, 0) { override def SetupNumberPools() = { } }
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
val weapon = vehicle.WeaponControlledFromSeat(1).get.asInstanceOf[Tool]
val guid : NumberPoolHub = new NumberPoolHub(LimitedNumberSource(3))
val guid : NumberPoolHub = new NumberPoolHub(LimitedNumberSource(5))
guid.AddPool("test-pool", (0 to 2).toList)
guid.register(vehicle, "test-pool")
guid.register(weapon, "test-pool")

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{ActorSystem, Props}
import akka.actor.Props
import net.psforever.objects._
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
import net.psforever.objects.serverobject.mount.Mountable

View file

@ -3,7 +3,7 @@ package objects
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.{Actor, ActorContext, ActorRef, Props}
import akka.actor.{ActorContext, ActorRef, Props}
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub
@ -96,18 +96,15 @@ class ZoneTest extends Specification {
"can have its unique identifier system changed if no objects were added to it" in {
val zone = new Zone("home3", map13, 13)
val guid1 : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(100))
guid1.AddPool("pool1", (0 to 50).toList)
guid1.AddPool("pool2", (51 to 75).toList)
zone.GUID(guid1) mustEqual true
zone.AddPool("pool1", (0 to 50).toList)
zone.AddPool("pool2", (51 to 75).toList)
val obj = new TestObject()
guid1.register(obj, "pool2").isSuccess mustEqual true
guid1.WhichPool(obj) mustEqual Some("pool2")
val guid2 : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(150))
guid2.AddPool("pool3", (0 to 50).toList)
guid2.AddPool("pool4", (51 to 75).toList)
zone.GUID(guid2) mustEqual false
zone.GUID(new NumberPoolHub(new LimitedNumberSource(150))) mustEqual false
}
}
}
@ -115,12 +112,54 @@ class ZoneTest extends Specification {
class ZoneActorTest extends ActorTest {
"Zone" should {
"have an Actor" in {
val zone = new Zone("test", new ZoneMap("map6"), 1)
val zone = new Zone("test", new ZoneMap("map6"), 1) { override def SetupNumberPools() = { } }
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-actor")
expectNoMsg(Duration.create(100, "ms"))
assert(zone.Actor != ActorRef.noSender)
}
"create new number pools before the Actor is started" in {
val zone = new Zone("test", new ZoneMap("map6"), 1) { override def SetupNumberPools() = { } }
zone.GUID(new NumberPoolHub(new LimitedNumberSource(10)))
assert( zone.AddPool("test1", 1 to 2) )
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-add-pool-actor") //note: not Init'd yet
assert( zone.AddPool("test2", 3 to 4) )
}
"remove existing number pools before the Actor is started" in {
val zone = new Zone("test", new ZoneMap("map6"), 1) { override def SetupNumberPools() = { } }
zone.GUID(new NumberPoolHub(new LimitedNumberSource(10)))
assert( zone.AddPool("test1", 1 to 2) )
assert( zone.RemovePool("test1") )
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-remove-pool-actor") //note: not Init'd yet
assert( zone.AddPool("test2", 3 to 4) )
assert( zone.RemovePool("test2") )
}
"refuse new number pools after the Actor is started" in {
val zone = new Zone("test", new ZoneMap("map6"), 1) { override def SetupNumberPools() = { } }
zone.GUID(new NumberPoolHub(new LimitedNumberSource(40150)))
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-add-pool-actor-init")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(500, "ms"))
assert( !zone.AddPool("test1", 1 to 2) )
}
"refuse to remove number pools after the Actor is started" in {
val zone = new Zone("test", new ZoneMap("map6"), 1) { override def SetupNumberPools() = { } }
zone.GUID(new NumberPoolHub(new LimitedNumberSource(10)))
zone.AddPool("test", 1 to 2)
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-remove-pool-actor-init")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
assert( !zone.RemovePool("test") )
}
"set up spawn groups based on buildings" in {
val map6 = new ZoneMap("map6") {
LocalBuilding(1, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1,1,1))))
@ -143,7 +182,7 @@ class ZoneActorTest extends ActorTest {
ObjectToBuilding(5, 3)
ObjectToBuilding(6, 3)
}
val zone = new Zone("test", map6, 1)
val zone = new Zone("test", map6, 1) { override def SetupNumberPools() = { } }
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-init")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
@ -181,7 +220,7 @@ class ZoneActorTest extends ActorTest {
LocalObject(5, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(5, 3)
}
val zone = new Zone("test", map6, 1)
val zone = new Zone("test", map6, 1) { override def SetupNumberPools() = { } }
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-spawn")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
@ -212,7 +251,7 @@ class ZoneActorTest extends ActorTest {
LocalObject(5, SpawnTube.Constructor(Vector3.Zero, Vector3.Zero))
ObjectToBuilding(5, 3)
}
val zone = new Zone("test", map6, 1)
val zone = new Zone("test", map6, 1) { override def SetupNumberPools() = { } }
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-no-spawn")
zone.Actor ! Zone.Init()
expectNoMsg(Duration.create(300, "ms"))
@ -228,15 +267,13 @@ class ZoneActorTest extends ActorTest {
}
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 zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
@ -248,9 +285,10 @@ class ZonePopulationTest extends ActorTest {
}
"remove user from zones" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
receiveOne(Duration.create(200, "ms")) //consume
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
@ -263,11 +301,12 @@ class ZonePopulationTest extends ActorTest {
}
"associate user with a character" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
@ -283,11 +322,12 @@ class ZonePopulationTest extends ActorTest {
}
"disassociate character from a user" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player)
@ -305,11 +345,12 @@ class ZonePopulationTest extends ActorTest {
}
"user tries to Leave, but still has an associated character" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(500, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player)
@ -329,12 +370,13 @@ class ZonePopulationTest extends ActorTest {
}
"user tries to Spawn a character, but an associated character already exists" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player1 = Player(avatar)
val player2 = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
zone.Population ! Zone.Population.Spawn(avatar, player1)
@ -355,11 +397,12 @@ class ZonePopulationTest extends ActorTest {
}
"user tries to Spawn a character, but did not Join first" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
val player = Player(avatar)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
assert(zone.Players.isEmpty)
assert(zone.LivePlayers.isEmpty)
@ -373,10 +416,11 @@ class ZonePopulationTest extends ActorTest {
}
"user tries to Release a character, but did not Spawn a character first" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val avatar = Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
zone.Population ! Zone.Population.Join(avatar)
expectNoMsg(Duration.create(100, "ms"))
@ -394,11 +438,12 @@ class ZonePopulationTest extends ActorTest {
}
"user adds character to list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(player)
@ -408,11 +453,12 @@ class ZonePopulationTest extends ActorTest {
}
"user removes character from the list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
zone.Population ! Zone.Corpse.Add(player)
expectNoMsg(Duration.create(100, "ms"))
@ -424,15 +470,16 @@ class ZonePopulationTest extends ActorTest {
}
"user removes THE CORRECT character from the list of retired characters" in {
val zone = new Zone("test", new ZoneMap(""), 0)
val zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val player1 = Player(Avatar("Chord1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player1.Release
val player2 = Player(Avatar("Chord2", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player2.Release
val player3 = Player(Avatar("Chord3", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
player3.Release
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), TestName) ! "!"
receiveOne(Duration.create(200, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
zone.Population ! Zone.Corpse.Add(player1)
zone.Population ! Zone.Corpse.Add(player2)
zone.Population ! Zone.Corpse.Add(player3)
@ -450,11 +497,12 @@ class ZonePopulationTest extends ActorTest {
}
"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 zone = new Zone("test", new ZoneMap(""), 0) { override def SetupNumberPools() = { } }
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Voice5))
//player.Release !!important
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "testC") ! "!"
receiveOne(Duration.create(500, "ms")) //consume
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
assert(zone.Corpses.isEmpty)
zone.Population ! Zone.Corpse.Add(player)
@ -468,9 +516,11 @@ class ZoneGroundDropItemTest extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
val hub = new NumberPoolHub(new LimitedNumberSource(20))
hub.register(item, 10)
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { } }
zone.GUID(hub)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
"DropItem" should {
"drop item on ground" in {
@ -492,9 +542,11 @@ class ZoneGroundCanNotDropItem1Test extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
val hub = new NumberPoolHub(new LimitedNumberSource(20))
//hub.register(item, 10) //!important
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { } }
zone.GUID(hub)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
"DropItem" should {
"not drop an item that is not registered" in {
@ -516,9 +568,11 @@ class ZoneGroundCanNotDropItem2Test extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
val hub = new NumberPoolHub(new LimitedNumberSource(20))
hub.register(item, 10) //!important
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { } }
//zone.GUID(hub) //!important
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
"DropItem" should {
"not drop an item that is not registered to the zone" in {
@ -540,9 +594,11 @@ class ZoneGroundCanNotDropItem3Test extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
val hub = new NumberPoolHub(new LimitedNumberSource(20))
hub.register(item, 10) //!important
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { } }
zone.GUID(hub) //!important
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
"DropItem" should {
"not drop an item that has already been dropped" in {
@ -572,9 +628,11 @@ class ZoneGroundPickupItemTest extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
val hub = new NumberPoolHub(new LimitedNumberSource(20))
hub.register(item, 10)
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { } }
zone.GUID(hub)
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
"PickupItem" should {
"pickup an item from ground" in {
@ -599,9 +657,11 @@ class ZoneGroundCanNotPickupItemTest extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
val hub = new NumberPoolHub(new LimitedNumberSource(20))
hub.register(item, 10)
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { } }
zone.GUID(hub) //still registered to this zone
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
"PickupItem" should {
"not pickup an item if it can not be found" in {
@ -622,9 +682,11 @@ class ZoneGroundRemoveItemTest extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
val hub = new NumberPoolHub(new LimitedNumberSource(20))
hub.register(item, 10)
val zone = new Zone("test", new ZoneMap("test-map"), 0)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { } }
zone.GUID(hub) //still registered to this zone
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-test-zone") ! "!"
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), ZoneTest.TestName)
zone.Actor ! Zone.Init()
expectNoMsg(200 milliseconds)
"RemoveItem" should {
"remove an item from the ground without callback (even if the item is not found)" in {
@ -646,12 +708,6 @@ class ZoneGroundRemoveItemTest extends ActorTest {
}
object ZoneTest {
class ZoneInitActor(zone : Zone) extends Actor {
def receive : Receive = {
case "!" =>
zone.Init(context)
sender ! "!"
case _ => ;
}
}
val testNum = new AtomicInteger(1)
def TestName : String = s"test${testNum.getAndIncrement()}"
}

View file

@ -1,52 +1,17 @@
// Copyright (c) 2017 PSForever
package objects.guidtask
import akka.actor.{Actor, ActorSystem, Props}
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import org.specs2.mutable.Specification
import objects.ActorTest
import scala.concurrent.Await
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
class GUIDTaskRegister1Test extends ActorTest {
"RegisterObjectTask" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = new GUIDTaskTest.TestObject
class GUIDTaskRegister1Test extends Specification {
"RegisterObjectTask" should {
"register (1)" in {
val system = ActorSystem("sys")
val test = system.actorOf(Props(classOf[GUIDTaskRegister1TestActor], system), "test")
implicit val timeout = Timeout(5 seconds)
val future = test ? "test"
val result = Await.result(future, timeout.duration).asInstanceOf[String]
result mustEqual "success"
}
assert(!obj.HasGUID)
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns)))
probe.expectMsg(scala.util.Success)
assert(obj.HasGUID)
}
}
private class GUIDTaskRegister1TestActor(implicit system : ActorSystem) extends Actor {
def receive : Receive = {
case "test" =>
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = new GUIDTaskTest.TestObject
assert(!obj.HasGUID)
taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns)))
probe.expectMsg(scala.util.Success)
assert(obj.HasGUID)
sender ! "success"
case _ => ;
}
}
//class GUIDTaskRegister1Test extends ActorTest() {
// "RegisterObjectTask" in {
// val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
// val obj = new GUIDTaskTest.TestObject
//
// assert(!obj.HasGUID)
// taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns)))
// probe.expectMsg(scala.util.Success)
// assert(obj.HasGUID)
// }
//}

View file

@ -9,7 +9,7 @@ import objects.ActorTest
import scala.concurrent.duration.Duration
class NumberPoolActorTest extends ActorTest(ActorSystem("test")) {
class NumberPoolActorTest extends ActorTest() {
"NumberPoolActor" should {
"GetAnyNumber" in {
val pool = new ExclusivePool((25 to 50).toList)
@ -22,7 +22,7 @@ class NumberPoolActorTest extends ActorTest(ActorSystem("test")) {
}
}
class NumberPoolActorTest1 extends ActorTest(ActorSystem("test")) {
class NumberPoolActorTest1 extends ActorTest() {
"NumberPoolActor" should {
"GetSpecificNumber" in {
val pool = new ExclusivePool((25 to 50).toList)
@ -34,7 +34,7 @@ class NumberPoolActorTest1 extends ActorTest(ActorSystem("test")) {
}
}
class NumberPoolActorTest2 extends ActorTest(ActorSystem("test")) {
class NumberPoolActorTest2 extends ActorTest() {
"NumberPoolActor" should {
"NoNumber" in {
val pool = new ExclusivePool((25 to 25).toList) //pool only has one number - 25

View file

@ -1,6 +1,8 @@
// Copyright (c) 2017 PSForever
import net.psforever.objects.zones.ZoneMap
import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects.LocalProjectile
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
import net.psforever.objects.serverobject.locks.IFFLock
@ -13,15 +15,25 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.types.Vector3
object Maps {
val map1 = new ZoneMap("map01")
val map1 = new ZoneMap("map01") {
Projectiles(this)
}
val map2 = new ZoneMap("map02")
val map2 = new ZoneMap("map02") {
Projectiles(this)
}
val map3 = new ZoneMap("map03")
val map3 = new ZoneMap("map03") {
Projectiles(this)
}
val map4 = new ZoneMap("map04")
val map4 = new ZoneMap("map04") {
Projectiles(this)
}
val map5 = new ZoneMap("map05")
val map5 = new ZoneMap("map05") {
Projectiles(this)
}
val map6 = new ZoneMap("map06") {
Building2()
@ -29,6 +41,7 @@ object Maps {
Building42()
Building48()
Building49()
Projectiles(this)
def Building2() : Unit = {
//Anguta
@ -420,17 +433,29 @@ object Maps {
}
}
val map7 = new ZoneMap("map07")
val map7 = new ZoneMap("map07") {
Projectiles(this)
}
val map8 = new ZoneMap("map08")
val map8 = new ZoneMap("map08") {
Projectiles(this)
}
val map9 = new ZoneMap("map09")
val map9 = new ZoneMap("map09") {
Projectiles(this)
}
val map10 = new ZoneMap("map10")
val map10 = new ZoneMap("map10") {
Projectiles(this)
}
val map11 = new ZoneMap("map11")
val map11 = new ZoneMap("map11") {
Projectiles(this)
}
val map12 = new ZoneMap("map12")
val map12 = new ZoneMap("map12") {
Projectiles(this)
}
val map13 = new ZoneMap("map13") {
Building1()
@ -443,6 +468,7 @@ object Maps {
Building77()
Building79()
Building81()
Projectiles(this)
def Building1() : Unit = {
//warpgate?
@ -716,29 +742,59 @@ object Maps {
}
}
val map14 = new ZoneMap("map14")
val map14 = new ZoneMap("map14") {
Projectiles(this)
}
val map15 = new ZoneMap("map15")
val map15 = new ZoneMap("map15") {
Projectiles(this)
}
val map16 = new ZoneMap("map16")
val map16 = new ZoneMap("map16") {
Projectiles(this)
}
val ugd01 = new ZoneMap("ugd01")
val ugd01 = new ZoneMap("ugd01") {
Projectiles(this)
}
val ugd02 = new ZoneMap("ugd02")
val ugd02 = new ZoneMap("ugd02") {
Projectiles(this)
}
val ugd03 = new ZoneMap("ugd03")
val ugd03 = new ZoneMap("ugd03") {
Projectiles(this)
}
val ugd04 = new ZoneMap("ugd04")
val ugd04 = new ZoneMap("ugd04") {
Projectiles(this)
}
val ugd05 = new ZoneMap("ugd05")
val ugd05 = new ZoneMap("ugd05") {
Projectiles(this)
}
val ugd06 = new ZoneMap("ugd06")
val ugd06 = new ZoneMap("ugd06") {
Projectiles(this)
}
val map96 = new ZoneMap("map96")
val map96 = new ZoneMap("map96") {
Projectiles(this)
}
val map97 = new ZoneMap("map97")
val map97 = new ZoneMap("map97") {
Projectiles(this)
}
val map98 = new ZoneMap("map98")
val map98 = new ZoneMap("map98") {
Projectiles(this)
}
val map99 = new ZoneMap("map99")
val map99 = new ZoneMap("map99") {
Projectiles(this)
}
private def Projectiles(zmap : ZoneMap) : Unit = {
(Projectile.BaseUID until Projectile.RangeUID) foreach { zmap.LocalObject(_, LocalProjectile.Constructor) }
}
}

View file

@ -53,6 +53,7 @@ import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Success
import akka.pattern.ask
import net.psforever.objects.ballistics.{Projectile, ProjectileResolution}
class WorldSessionActor extends Actor with MDCContextAware {
import WorldSessionActor._
@ -84,6 +85,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
var traveler : Traveler = null
var deadState : DeadState.Value = DeadState.Dead
var whenUsedLastKit : Long = 0
val projectiles : Array[Option[Projectile]] = Array.fill[Option[Projectile]](Projectile.RangeUID - Projectile.BaseUID)(None)
var amsSpawnPoint : Option[SpawnTube] = None
@ -2586,7 +2588,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
else if((player.DrawnSlot = held_holsters) != before) {
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
// Ignore non-equipment holsters
//todo: check current suit holster slots?
if(held_holsters >= 0 && held_holsters < 5) {
@ -2598,7 +2599,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case None => ;
}
}
// Stop using proximity terminals if player unholsters a weapon (which should re-trigger the proximity effect and re-holster the weapon)
@ -2684,11 +2684,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
case Some(_ : LocalProjectile) =>
FindProjectileEntry(object_guid) match {
case Some(projectile) =>
if(projectile.resolution != ProjectileResolution.Unresolved) {
log.warn(s"RequestDestroy: tried to clean up missed projectile ${object_guid.guid} but it was already resolved")
}
ResolveProjectileEntry(object_guid, ProjectileResolution.MissedShot)
case None =>
log.warn(s"RequestDestroy: projectile ${object_guid.guid} has never been fired")
}
case Some(thing) =>
log.warn(s"RequestDestroy: not allowed to delete object $thing")
case None =>
log.warn(s"RequestDestroy: object $object_guid not found")
log.warn(s"RequestDestroy: object ${object_guid.guid} not found")
}
case msg @ ObjectDeleteMessage(object_guid, unk1) =>
@ -3137,8 +3148,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
log.info("WeaponFire: " + msg)
FindWeapon match {
case Some(tool : Tool) =>
FindContainedWeapon match {
case (Some(obj), Some(tool : Tool)) =>
if(tool.Magazine <= 0) { //safety: enforce ammunition depletion
tool.Magazine = 0
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, weapon_guid, 0))
@ -3149,7 +3160,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
else { //shooting
tool.Discharge
//TODO other stuff?
val projectileIndex = projectile_guid.guid - Projectile.BaseUID
val ang = obj match {
case _ : Player =>
obj.Orientation //TODO upper body facing
case _ : Vehicle =>
tool.Orientation //TODO this is too simplistic
case _ =>
Vector3.Zero
}
projectiles(projectileIndex) =
Some(Projectile(tool.Projectile, tool.Definition, shot_origin, ang))
}
case _ => ;
}
@ -3158,10 +3179,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("Lazing position: " + pos2.toString)
case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) =>
log.info("Hit: " + msg)
log.info(s"Hit: $msg")
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Hit)
case msg @ SplashHitMessage(unk1, unk2, unk3, unk4, unk5, unk6, unk7, unk8) =>
log.info("SplashHitMessage: " + msg)
case msg @ SplashHitMessage(seq_time, projectile_guid, explosion_pos, direct_victim_uid, unk3, projectile_vel, unk4, targets) =>
log.info(s"Splash: $msg")
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Splash)
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
log.info(s"Lash: $msg")
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Lash)
case msg @ AvatarFirstTimeEventMessage(avatar_guid, object_guid, unk1, event_name) =>
log.info("AvatarFirstTimeEvent: " + msg)
@ -5229,6 +5256,62 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
/**
* Given a globally unique identifier in the 40100 to 40124 range
* (with an optional 25 as buffer),
* find a projectile.
* @param projectile_guid the projectile's GUID
* @return the discovered projectile
*/
def FindProjectileEntry(projectile_guid : PlanetSideGUID) : Option[Projectile] = {
val index = projectile_guid.guid - Projectile.BaseUID
if(0 <= index && index < projectiles.length) {
projectiles(index)
}
else {
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
None
}
}
/**
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
* @param projectile_guid the projectile GUID
* @param resolution the resolution status to promote the projectile
* @return the projectile
*/
def ResolveProjectileEntry(projectile_guid : PlanetSideGUID, resolution : ProjectileResolution.Value) : Option[Projectile] = {
FindProjectileEntry(projectile_guid) match {
case Some(projectile) =>
val index = projectile_guid.guid - Projectile.BaseUID
ResolveProjectileEntry(projectile, index, resolution)
case None =>
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
None
}
}
/**
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
* The internal copy of the projectile is retained as merely `Resolved`
* while the observed projectile is promoted to the suggested resolution status.
* @param projectile the projectile object
* @param index where the projectile was found
* @param resolution the resolution status to promote the projectile
* @return a copy of the projectile
*/
def ResolveProjectileEntry(projectile : Projectile, index : Int, resolution : ProjectileResolution.Value) : Option[Projectile] = {
if(projectiles(index).contains(projectile)) {
projectiles(index) = Some(projectile.Resolve(ProjectileResolution.Resolved))
Some(projectile.Resolve(resolution))
}
else {
None
}
}
def failWithError(error : String) = {
log.error(error)
sendResponse(ConnectionClose())

View file

@ -2,17 +2,27 @@
import akka.actor.{ActorRef, ActorSystem, MDCContextAware}
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
import com.typesafe.config.{ConfigFactory, ConfigValueFactory}
import net.psforever.packet.{ControlPacket, GamePacket}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import org.specs2.specification.Scope
abstract class ActorTest(sys : ActorSystem = ActorSystem("system")) extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
abstract class ActorTest(sys : ActorSystem = ActorSystem("system", ConfigFactory.parseMap(ActorTest.LoggingConfig)))
extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
}
object ActorTest {
import scala.collection.JavaConverters._
private val LoggingConfig = Map(
"akka.loggers" -> List("akka.testkit.TestEventListener").asJava,
"akka.loglevel" -> "OFF",
"akka.stdout-loglevel" -> "OFF",
"akka.log-dead-letters" -> "OFF"
).asJava
final case class MDCGamePacket(packet : GamePacket)
final case class MDCControlPacket(packet : ControlPacket)

View file

@ -384,7 +384,7 @@ Even with all this work, the tests have a high chance of failure just due to bei
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 zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) } }
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
@ -433,7 +433,7 @@ class AvatarReleaseTest extends ActorTest {
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 zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) } }
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()
@ -483,7 +483,7 @@ class AvatarReleaseEarly1Test extends ActorTest {
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 zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) } }
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
zone.Actor ! Zone.Init()