mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-04-04 11:35:05 +00:00
documentation and tests; added projectile names; changed logging level for ActorTests to OFF to remove the majority of log clutter; issue with number pool hub re-assignment and the existence of fixed objects (LocalProjectile) that requires unrelated tests be modified
This commit is contained in:
parent
c57999d676
commit
000652c969
12 changed files with 556 additions and 191 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
|
||||
/**
|
||||
* 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 PlanetSideGameObject {
|
||||
def Definition = LocalProjectile.local
|
||||
}
|
||||
|
||||
object LocalProjectile {
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
def local = new ObjectDefinition(0) { Name = "projectile" }
|
||||
}
|
||||
|
|
@ -1,25 +1,23 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects
|
||||
package net.psforever.objects.ballistics
|
||||
|
||||
import net.psforever.objects.definition.{ProjectileDefinition, ToolDefinition}
|
||||
import net.psforever.objects.entity.SimpleWorldEntity
|
||||
import net.psforever.objects.ballistics.ProjectileResolution
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
final case class Projectile(profile : ProjectileDefinition,
|
||||
tool_def : ToolDefinition,
|
||||
pos : Vector3,
|
||||
orient : Vector3,
|
||||
shot_origin : Vector3,
|
||||
shot_angle : Vector3,
|
||||
resolution : ProjectileResolution.Value,
|
||||
fire_time : Long = System.nanoTime,
|
||||
hit_time : Long = 0) {
|
||||
val current : SimpleWorldEntity = new SimpleWorldEntity()
|
||||
|
||||
def Resolve(hitPos : Vector3, hitAng : Vector3, hitVel : Vector3, resolution : ProjectileResolution.Value) : Projectile = {
|
||||
def Resolve(hitPos : Vector3, hitAng : Vector3, resolution : ProjectileResolution.Value) : Projectile = {
|
||||
val obj = Resolve(resolution)
|
||||
obj.current.Position = hitPos
|
||||
obj.current.Orientation = hitAng
|
||||
obj.current.Velocity = hitVel
|
||||
obj
|
||||
}
|
||||
|
||||
|
|
@ -28,15 +26,19 @@ final case class Projectile(profile : ProjectileDefinition,
|
|||
case ProjectileResolution.Unresolved =>
|
||||
this
|
||||
case _ =>
|
||||
Projectile(profile, tool_def, pos, orient, resolution, fire_time, System.nanoTime)
|
||||
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
|
||||
|
||||
def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, pos : Vector3, orient : Vector3) : Projectile = {
|
||||
Projectile(profile, tool_def, pos, orient, ProjectileResolution.Unresolved)
|
||||
def apply(profile : ProjectileDefinition, tool_def : ToolDefinition, shot_origin : Vector3, shot_angle : Vector3) : Projectile = {
|
||||
Projectile(profile, tool_def, shot_origin, shot_angle, ProjectileResolution.Unresolved)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,10 @@ object ProjectileResolution extends Enumeration {
|
|||
|
||||
val
|
||||
Unresolved,
|
||||
Resolved,
|
||||
MissedShot,
|
||||
Target,
|
||||
Obstacle
|
||||
Hit,
|
||||
Splash,
|
||||
Lash
|
||||
= Value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -47,6 +48,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
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
|
||||
guid.AddPool("projectiles", (Projectile.BaseUID until Projectile.RangeUID).toList) //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]()
|
||||
/** */
|
||||
|
|
@ -97,6 +99,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
MakeBuildings(context)
|
||||
AssignAmenities()
|
||||
CreateSpawnGroups()
|
||||
MakeLocalProjectiles()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,10 +153,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 +170,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
|
||||
|
|
@ -258,6 +303,10 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
entry
|
||||
}
|
||||
|
||||
def MakeLocalProjectiles() : Unit = {
|
||||
(Projectile.BaseUID until Projectile.RangeUID) foreach { guid.register(new LocalProjectile(), _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,21 @@ 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.loglevel" -> ConfigValueFactory.fromAnyRef("OFF"),
|
||||
"akka.stdout-loglevel" -> ConfigValueFactory.fromAnyRef("OFF")
|
||||
).asJava
|
||||
}
|
||||
|
|
|
|||
198
common/src/test/scala/objects/ProjectileTest.scala
Normal file
198
common/src/test/scala/objects/ProjectileTest.scala
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -326,7 +327,7 @@ object VehicleSpawnPadControlTest {
|
|||
val zone = new Zone("test-zone", map, 0)
|
||||
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(40150))
|
||||
guid.AddPool("test-pool", (0 to 2).toList)
|
||||
guid.register(vehicle, "test-pool")
|
||||
guid.register(weapon, "test-pool")
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, V
|
|||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType}
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.ballistics.Projectile
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -96,18 +97,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -121,6 +119,48 @@ class ZoneActorTest extends ActorTest {
|
|||
assert(zone.Actor != ActorRef.noSender)
|
||||
}
|
||||
|
||||
"create new number pools before the Actor is started" in {
|
||||
val zone = new Zone("test", new ZoneMap("map6"), 1)
|
||||
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)
|
||||
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)
|
||||
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(300, "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)
|
||||
|
||||
zone.GUID(new NumberPoolHub(new LimitedNumberSource(40150)))
|
||||
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))))
|
||||
|
|
@ -225,6 +265,20 @@ class ZoneActorTest extends ActorTest {
|
|||
assert(reply.asInstanceOf[Zone.Lattice.NoValidSpawnPoint].spawn_group.contains(7))
|
||||
}
|
||||
}
|
||||
|
||||
"populate a series of reference projectiles" in {
|
||||
val zone = new Zone("test", new ZoneMap("map6"), 1)
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-projectiles")
|
||||
zone.Actor ! Zone.Init()
|
||||
expectNoMsg(Duration.create(300, "ms"))
|
||||
|
||||
(Projectile.BaseUID until Projectile.RangeUID)
|
||||
.map { zone.GUID }
|
||||
.collect {
|
||||
case Some(_ : LocalProjectile) => ; //pass
|
||||
case _ => assert(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ZonePopulationTest extends ActorTest {
|
||||
|
|
@ -466,7 +520,7 @@ class ZonePopulationTest extends ActorTest {
|
|||
|
||||
class ZoneGroundDropItemTest extends ActorTest {
|
||||
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(40150))
|
||||
hub.register(item, 10)
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
zone.GUID(hub)
|
||||
|
|
@ -490,7 +544,7 @@ class ZoneGroundDropItemTest extends ActorTest {
|
|||
|
||||
class ZoneGroundCanNotDropItem1Test extends ActorTest {
|
||||
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(40150))
|
||||
//hub.register(item, 10) //!important
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
zone.GUID(hub)
|
||||
|
|
@ -538,7 +592,7 @@ class ZoneGroundCanNotDropItem2Test extends ActorTest {
|
|||
|
||||
class ZoneGroundCanNotDropItem3Test extends ActorTest {
|
||||
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(40150))
|
||||
hub.register(item, 10) //!important
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
zone.GUID(hub) //!important
|
||||
|
|
@ -570,7 +624,7 @@ class ZoneGroundCanNotDropItem3Test extends ActorTest {
|
|||
|
||||
class ZoneGroundPickupItemTest extends ActorTest {
|
||||
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(40150))
|
||||
hub.register(item, 10)
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
zone.GUID(hub)
|
||||
|
|
@ -597,7 +651,7 @@ class ZoneGroundPickupItemTest extends ActorTest {
|
|||
|
||||
class ZoneGroundCanNotPickupItemTest extends ActorTest {
|
||||
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(40150))
|
||||
hub.register(item, 10)
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
zone.GUID(hub) //still registered to this zone
|
||||
|
|
@ -620,7 +674,7 @@ class ZoneGroundCanNotPickupItemTest extends ActorTest {
|
|||
|
||||
class ZoneGroundRemoveItemTest extends ActorTest {
|
||||
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(20))
|
||||
val hub = new NumberPoolHub(new LimitedNumberSource(40150))
|
||||
hub.register(item, 10)
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
zone.GUID(hub) //still registered to this zone
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ import scala.concurrent.Future
|
|||
import scala.concurrent.duration._
|
||||
import scala.util.Success
|
||||
import akka.pattern.ask
|
||||
import net.psforever.objects.ballistics.ProjectileResolution
|
||||
import net.psforever.objects.ballistics.{Projectile, ProjectileResolution}
|
||||
|
||||
class WorldSessionActor extends Actor with MDCContextAware {
|
||||
import WorldSessionActor._
|
||||
|
|
@ -85,9 +85,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
var traveler : Traveler = null
|
||||
var deadState : DeadState.Value = DeadState.Dead
|
||||
var whenUsedLastKit : Long = 0
|
||||
/** the client rotates between 40100 -- 40124 for projectiles normally, skipping only for long-lived projectiles
|
||||
* 40125 -- 40149 is being reserved as a (potential) guard against overflow */
|
||||
val projectiles : Array[Option[Projectile]] = Array.fill[Option[Projectile]](50)(None)
|
||||
val projectiles : Array[Option[Projectile]] = Array.fill[Option[Projectile]](Projectile.RangeUID - Projectile.BaseUID)(None)
|
||||
|
||||
var amsSpawnPoint : Option[SpawnTube] = None
|
||||
|
||||
|
|
@ -2686,13 +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 =>
|
||||
if(ResolveProjectileEntry(object_guid.guid - Projectile.BaseUID, ProjectileResolution.MissedShot).isEmpty) {
|
||||
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) =>
|
||||
|
|
@ -3173,15 +3180,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ HitMessage(seq_time, projectile_guid, unk1, hit_info, unk2, unk3, unk4) =>
|
||||
log.info(s"Hit: $msg")
|
||||
ResolveProjectileEntry(projectile_guid.guid - Projectile.BaseUID, ProjectileResolution.Target)
|
||||
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Hit)
|
||||
|
||||
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.guid - Projectile.BaseUID, ProjectileResolution.Target)
|
||||
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.guid - Projectile.BaseUID, ProjectileResolution.Target)
|
||||
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Lash)
|
||||
|
||||
case msg @ AvatarFirstTimeEventMessage(avatar_guid, object_guid, unk1, event_name) =>
|
||||
log.info("AvatarFirstTimeEvent: " + msg)
|
||||
|
|
@ -5249,22 +5256,32 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
}
|
||||
|
||||
def ResolveProjectileEntry(index : Int, resolution : ProjectileResolution.Value) : Option[Projectile] = {
|
||||
def FindProjectileEntry(projectile_guid : PlanetSideGUID) : Option[Projectile] = {
|
||||
val index = projectile_guid.guid - Projectile.BaseUID
|
||||
if(0 <= index && index < projectiles.length) {
|
||||
val entry = projectiles(index)
|
||||
entry match {
|
||||
case None =>
|
||||
log.warn(s"ResolveProjectile: expected projectile, but ${Projectile.BaseUID + index} not found")
|
||||
None
|
||||
case Some(projectile) =>
|
||||
projectile.resolution match {
|
||||
case ProjectileResolution.Unresolved => ;
|
||||
case _ =>
|
||||
log.warn(s"ResolveProjectileEntry: ${projectile.profile.ProjectileType} projectile found but reports to already be resolved")
|
||||
}
|
||||
projectiles(index) = Some(projectile.Resolve(resolution))
|
||||
projectiles(index)
|
||||
}
|
||||
projectiles(index)
|
||||
}
|
||||
else {
|
||||
log.warn(s"ResolveProjectile: expected projectile, but ${projectile_guid.guid} not found")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,17 +2,25 @@
|
|||
|
||||
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.loglevel" -> ConfigValueFactory.fromAnyRef("OFF"),
|
||||
"akka.stdout-loglevel" -> ConfigValueFactory.fromAnyRef("OFF")
|
||||
).asJava
|
||||
|
||||
final case class MDCGamePacket(packet : GamePacket)
|
||||
|
||||
final case class MDCControlPacket(packet : ControlPacket)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue