PSF-LoginServer/common/src/test/scala/objects/GeneratorTest.scala
Jakob Gillich e0defe8240
Persistence #1 featuring quill (#508)
* Add .scalafmt.conf

* Adopt quill for database access

* Removed postgresql-async
* Refactored all instances of database access
* Creating duplicate characters of the same account is no longer possible
* Rewrote large parts of LoginSessionActor

* Implement migrations

* Move overrides into subdirectory

* Make usernames case insensitive

* Use LOWER(?) comparison instead of storing lowercased username

* import scala.util.{Success, Failure}

* Add config and joda-time dependencies

* Add sbt-scalafmt

* Use defaultWithAlign scalafmt preset

* Format all

* Add scalafix

* Remove unused imports

* Don't lowercase username when inserting

* Update readme

* Listen on worldserver.Hostname address

* Remove database test on startup

It could fail when the global thread pool is busy loading zone
maps. Migrations run on the main thread and also serve the
purpose of verifying the database configuration so it's fine to
remove the test altogether.

* Refactor chat message handlers, zones

What started as a small change to how zones are stored turned
into a pretty big effort of refactoring the chat message handler.
The !hack command was removed, the /capturebase commandwas added.

* Expose db ports in docker-compose.yml

* Silence property override log

* Rework configuration

* Unify configuration using the typesafe.config library
* Add configuration option for public address
* Configuration is now loaded from application.conf rather than worldserver.ini
* Refactor PsLogin and remove unnecessary logging
* Move pslogin into net.psforever.pslogin namespace

* Fix coverage
2020-07-13 23:54:05 -04:00

850 lines
30 KiB
Scala

// Copyright (c) 2020 PSForever
package objects
import akka.actor.{ActorRef, Props}
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.ballistics._
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Tool}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage, TriggerEffectMessage}
import net.psforever.types._
import org.specs2.mutable.Specification
import services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.duration._
class GeneratorTest extends Specification {
"Generator" should {
"construct" in {
Generator(GlobalDefinitions.generator)
ok
}
"start in 'Normal' condition" in {
val obj = Generator(GlobalDefinitions.generator)
obj.Condition mustEqual PlanetSideGeneratorState.Normal
}
}
}
class GeneratorControlConstructTest extends ActorTest {
"GeneratorControl" should {
"construct" in {
val gen = Generator(GlobalDefinitions.generator)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "gen-control")
assert(gen.Actor != ActorRef.noSender)
}
}
}
class GeneratorControlDamageTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(
projectile,
weapon.Definition,
weapon.FireMode,
PlayerSource(player1),
0,
Vector3(2, 0, 0),
Vector3(-1, 0, 0)
),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"handle damage" in {
assert(gen.Health == gen.Definition.MaxHealth)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(2, 500 milliseconds)
buildingProbe.expectNoMessage(200 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 15)) =>
true
case _ => false
}
)
assert(gen.Health < gen.Definition.MaxHealth)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
}
}
}
class GeneratorControlCriticalTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(
projectile,
weapon.Definition,
weapon.FireMode,
PlayerSource(player1),
0,
Vector3(2, 0, 0),
Vector3(-1, 0, 0)
),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
val halfHealth = gen.Definition.MaxHealth / 2
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"handle damage through the generator's critical state" in {
gen.Health = halfHealth + 1 //no matter what, the next shot pushes it to critical status
assert(gen.Health > halfHealth)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(2, 500 milliseconds)
val msg_building = buildingProbe.receiveOne(500 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 15)) =>
true
case _ => false
}
)
assert(
msg_building match {
case Building.AmenityStateChange(o) => o eq gen
case _ => false
}
)
assert(gen.Health < halfHealth)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Critical)
}
}
}
class GeneratorControlDestroyedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
player1.Actor = TestProbe().ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(
projectile,
weapon.Definition,
weapon.FireMode,
PlayerSource(player1),
0,
Vector3(2, 0, 0),
Vector3(-1, 0, 0)
),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"handle damage until destroyed" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal) //skipped critical state because didn't transition ~50%
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar1 = avatarProbe.receiveOne(500 milliseconds)
buildingProbe.expectNoMessage(200 milliseconds)
assert(
msg_avatar1 match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) =>
true
case _ => false
}
)
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
avatarProbe.expectNoMessage(9 seconds)
buildingProbe.expectNoMessage(50 milliseconds) //no prior messages
val msg_avatar2 = avatarProbe.receiveN(3, 1000 milliseconds) //see DamageableEntity test file
val msg_building = buildingProbe.receiveOne(200 milliseconds)
assert(
msg_building match {
case Building.AmenityStateChange(o) => o eq gen
case _ => false
}
)
assert(
msg_avatar2.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar2(1) match {
case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true
case _ => false
}
)
assert(
msg_avatar2(2) match {
case AvatarServiceMessage(
"test",
AvatarAction.SendResponse(_, TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None))
) =>
true
case _ => false
}
)
assert(gen.Health == 0)
assert(gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Destroyed)
}
}
}
class GeneratorControlKillsTest extends ActorTest {
/*
to perform this test, players need to be added to the SOI organization of the test base in proximity of the generator
under normal player scenario, this is an automatic process
extending from the act of players being in a zone
and players being within the SOI radius from the center of a facility on a periodic check
the test base being used has no established SOI region or automatic SOI check refresh,
but its SOI information can be loaded with the players manually
the players need something to catch the die message
*/
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
val player2 =
Player(Avatar("TestCharacter2", PlanetSideEmpire.TR, CharacterGender.Female, 1, CharacterVoice.Mute)) //guid=4
player2.Position = Vector3(15, 0, 0) //>14m from generator; lives
player2.Spawn
val player2Probe = TestProbe()
player2.Actor = player2Probe.ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1, player2)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
guid.register(player2, 4)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(
projectile,
weapon.Definition,
weapon.FireMode,
PlayerSource(player1),
0,
Vector3(2, 0, 0),
Vector3(-1, 0, 0)
),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"kill players when the generator is destroyed" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal) //skipped critical state because didn't transition ~50%
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar1 = avatarProbe.receiveN(2, 500 milliseconds)
buildingProbe.expectNoMessage(200 milliseconds)
player1Probe.expectNoMessage(200 milliseconds)
player2Probe.expectNoMessage(200 milliseconds)
assert(
msg_avatar1.head match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) =>
true
case _ => false
}
)
assert(
msg_avatar1(1) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) =>
true
case _ => false
}
)
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
val msg_building = buildingProbe.receiveOne(10500 milliseconds)
val msg_avatar2 = avatarProbe.receiveN(3, 200 milliseconds)
val msg_player1 = player1Probe.receiveOne(100 milliseconds)
player2Probe.expectNoMessage(200 milliseconds)
assert(
msg_building match {
case Building.AmenityStateChange(o) => o eq gen
case _ => false
}
)
assert(
msg_avatar2.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar2(1) match {
case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true
case _ => false
}
)
assert(
msg_avatar2(2) match {
case AvatarServiceMessage(
"test",
AvatarAction.SendResponse(_, TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None))
) =>
true
case _ => false
}
)
assert(
msg_player1 match {
case _ @Player.Die() => true
case _ => false
}
)
assert(gen.Health == 0)
assert(gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Destroyed)
}
}
}
class GeneratorControlNotDestroyTwice extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val gen = Generator(GlobalDefinitions.generator) //guid=2
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(
projectile,
weapon.Definition,
weapon.FireMode,
PlayerSource(player1),
0,
Vector3(2, 0, 0),
Vector3(-1, 0, 0)
),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"not send a status update if destroyed and partially repaired, but destroyed again" in {
//damaged, not yet restored, but will not be destroyed again within one shot
val originalHealth = gen.Health = gen.Definition.DamageDestroysAt + 1
gen.Condition = PlanetSideGeneratorState.Destroyed //initial state manip
gen.Destroyed = true
assert(gen.Destroyed)
assert(originalHealth < gen.Definition.DefaultHealth)
assert(originalHealth < gen.Definition.RepairRestoresAt)
assert(originalHealth > gen.Definition.DamageDestroysAt)
gen.Actor ! Vitality.Damage(applyDamageTo)
avatarProbe.expectNoMessage(500 milliseconds)
activityProbe.receiveOne(500 milliseconds)
buildingProbe.expectNoMessage(1000 milliseconds)
assert(gen.Health < originalHealth)
assert(gen.Destroyed)
assert(originalHealth < gen.Definition.DefaultHealth)
assert(originalHealth < gen.Definition.RepairRestoresAt)
assert(gen.Health <= gen.Definition.DamageDestroysAt)
//damaged, not yet restored, and would have been destroyed with next shot
gen.Health = 1
assert(gen.Health == 1)
assert(gen.Destroyed)
gen.Actor ! Vitality.Damage(applyDamageTo)
avatarProbe.expectNoMessage(500 milliseconds)
activityProbe.receiveOne(500 milliseconds) //activity alert occurs because this was not a kill shot
buildingProbe.expectNoMessage(1000 milliseconds)
assert(gen.Health == 0)
assert(gen.Destroyed)
}
}
}
class GeneratorControlNotDamageIfExplodingTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(
projectile,
weapon.Definition,
weapon.FireMode,
PlayerSource(player1),
0,
Vector3(2, 0, 0),
Vector3(-1, 0, 0)
),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"not damage if the generator is going to explode" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal) //skipped critical state because didn't transition ~50%
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveOne(500 milliseconds)
buildingProbe.expectNoMessage(200 milliseconds)
player1Probe.expectNoMessage(200 milliseconds)
assert(
msg_avatar match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) =>
true
case _ => false
}
)
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
//going to explode state
//once
gen.Actor ! Vitality.Damage(applyDamageTo)
avatarProbe.expectNoMessage(500 milliseconds)
buildingProbe.expectNoMessage(200 milliseconds)
player1Probe.expectNoMessage(200 milliseconds)
assert(gen.Health == 1)
//twice
gen.Actor ! Vitality.Damage(applyDamageTo)
avatarProbe.expectNoMessage(500 milliseconds)
buildingProbe.expectNoMessage(200 milliseconds)
player1Probe.expectNoMessage(200 milliseconds)
assert(gen.Health == 1)
}
}
}
class GeneratorControlNotRepairIfExplodingTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
val weapon = Tool(GlobalDefinitions.phoenix) //decimator
val projectile = weapon.Projectile
val resolved = ResolvedProjectile(
ProjectileResolution.Splash,
Projectile(
projectile,
weapon.Definition,
weapon.FireMode,
PlayerSource(player1),
0,
Vector3(2, 0, 0),
Vector3(-1, 0, 0)
),
SourceEntry(gen),
gen.DamageModel,
Vector3(1, 0, 0)
)
val applyDamageTo = resolved.damage_model.Calculate(resolved)
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"not repair if the generator is going to explode" in {
gen.Health = 1 //no matter what, the next shot destroys the generator
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal) //skipped critical state because didn't transition ~50%
gen.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar1 = avatarProbe.receiveOne(500 milliseconds)
buildingProbe.expectNoMessage(200 milliseconds)
player1Probe.expectNoMessage(200 milliseconds)
assert(
msg_avatar1 match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) =>
true
case _ => false
}
)
assert(gen.Health == 1)
assert(!gen.Destroyed)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
//going to explode state
//once
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair?
avatarProbe.expectNoMessage(1000 milliseconds) //no messages
buildingProbe.expectNoMessage(200 milliseconds)
player1Probe.expectNoMessage(200 milliseconds)
assert(gen.Health == 1)
//twice
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair?
avatarProbe.expectNoMessage(1000 milliseconds) //no messages
buildingProbe.expectNoMessage(200 milliseconds)
player1Probe.expectNoMessage(200 milliseconds)
assert(gen.Health == 1)
}
}
}
class GeneratorControlRepairPastRestorePoint extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val activityProbe = TestProbe()
zone.Activity = activityProbe.ref
val gen = Generator(GlobalDefinitions.generator) //guid=2
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
building.PlayersInSOI = List(player1)
val buildingProbe = TestProbe()
building.Actor = buildingProbe.ref
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"GeneratorControl" should {
"send a status update if destroyed and repairing past the restoration point" in {
val originalHealth = gen.Health = gen.Definition.RepairRestoresAt - 1 //damage
gen.Condition = PlanetSideGeneratorState.Destroyed //initial state manip
gen.Destroyed = true
assert(originalHealth < gen.Definition.DefaultHealth)
assert(originalHealth < gen.Definition.RepairRestoresAt)
assert(gen.Destroyed)
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair
val msg_avatar = avatarProbe.receiveN(4, 500 milliseconds) //expected
val msg_building = buildingProbe.receiveOne(200 milliseconds)
assert(
msg_avatar.head match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction
.SendResponse(_, InventoryStateMessage(ValidPlanetSideGUID(5), _, ValidPlanetSideGUID(4), _))
) =>
true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 17)) =>
true
case _ => false
}
)
assert(
msg_avatar(3) match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction.SendResponse(_, RepairMessage(ValidPlanetSideGUID(2), _))
) =>
true
case _ => false
}
)
assert(
msg_building match {
case Building.AmenityStateChange(o) => o eq gen
case _ => false
}
)
assert(gen.Condition == PlanetSideGeneratorState.Normal)
assert(gen.Health > gen.Definition.RepairRestoresAt)
assert(!gen.Destroyed)
}
}
}