From 54f6588439163de15c4ef2dc1a872b388496e9b8 Mon Sep 17 00:00:00 2001 From: "Jason_DiDonato@yahoo.com" Date: Wed, 21 Oct 2020 12:35:13 -0400 Subject: [PATCH] comments and tests; generator now passes state information to owner, owner composes messaging; can not set faction information if building does not have ntu --- .../actors/session/SessionActor.scala | 6 - .../psforever/actors/zone/BuildingActor.scala | 178 +++++++++++------- .../generator/GeneratorControl.scala | 136 ++++++------- .../objects/AutoRepairIntegrationTest.scala | 2 + src/test/scala/objects/DoorTest.scala | 36 ++-- src/test/scala/objects/GeneratorTest.scala | 168 +++++++++-------- src/test/scala/objects/ResourceSiloTest.scala | 6 - 7 files changed, 296 insertions(+), 236 deletions(-) diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index b2a31c009..42a4f2a23 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -3646,12 +3646,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con if (isMovingPlus) { CancelZoningProcessWithDescriptiveReason("cancel_motion") } - if (!player.Crouching && is_crouching) { - continent.Buildings.values.find { b => Vector3.Distance(player.Position, b.Position) <= b.Definition.SOIRadius} match { - case Some(b) => ; - case _ => ; - } - } player.Position = pos player.Velocity = vel player.Orientation = Vector3(player.Orientation.x, pitch, yaw) diff --git a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala index fc7b699a7..cf1383f53 100644 --- a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala +++ b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala @@ -7,7 +7,7 @@ import akka.{actor => classic} import net.psforever.actors.commands.NtuCommand import net.psforever.objects.{CommonNtuContainer, NtuContainer} import net.psforever.objects.serverobject.PlanetSideServerObject -import net.psforever.objects.serverobject.generator.Generator +import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl} import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate} import net.psforever.objects.zones.Zone import net.psforever.persistence @@ -16,7 +16,7 @@ import net.psforever.types.PlanetSideEmpire import net.psforever.util.Database._ import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.{InterstellarClusterService, ServiceManager} +import net.psforever.services.{InterstellarClusterService, Service, ServiceManager} import scala.concurrent.ExecutionContext.Implicits.global import scala.util.{Failure, Success} @@ -73,6 +73,7 @@ class BuildingActor( private[this] val log = org.log4s.getLogger var galaxyService: Option[classic.ActorRef] = None var interstellarCluster: Option[ActorRef[InterstellarClusterService.Command]] = None + var hasNtuSupply: Boolean = true context.system.receptionist ! Receptionist.Find( InterstellarClusterService.InterstellarClusterServiceKey, @@ -124,28 +125,11 @@ class BuildingActor( galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) Behaviors.same - case AmenityStateChange(_: Generator, data) => - //TODO when parameter object is finally immutable, perform analysis on it to determine specific actions - data match { - case Some("overloaded") => - powerLost() - val zone = building.Zone - val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 2) - building.PlayersInSOI.foreach { player => - zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg) - } //??? - case Some("restored") => - // Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal. - powerRestored() - val zone = building.Zone - building.PlayersInSOI.foreach { player => - val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 0) - zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg) - } //reset ??? - case _ => ; + case AmenityStateChange(gen: Generator, data) => + if (generatorStateChange(gen, data)) { + //update the map + galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) } - //update the map - galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) Behaviors.same case AmenityStateChange(_, _) => @@ -163,15 +147,17 @@ class BuildingActor( Behaviors.same case msg @ NtuDepleted() => - // Oops, someone let the base run out of power. Shut it all down. + // Someone let the base run out of nanites. No one gets anything. building.Amenities.foreach { amenity => amenity.Actor ! msg } setFactionTo(PlanetSideEmpire.NEUTRAL, galaxyService) + hasNtuSupply = false Behaviors.same case msg @ SuppliedWithNtu() => - // Auto-repair, mainly. If the Generator works, power is restored too. + // Auto-repair restart, mainly. If the Generator works, power should be restored too. + hasNtuSupply = true building.Amenities.foreach { amenity => amenity.Actor ! msg } @@ -182,49 +168,107 @@ class BuildingActor( } } + def generatorStateChange(generator: Generator, event: Any): Boolean = { + event match { + case Some(GeneratorControl.Event.UnderAttack) => + val events = zone.AvatarEvents + val guid = building.GUID + val msg = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 15) + building.PlayersInSOI.foreach { player => + events ! AvatarServiceMessage(player.Name, msg) + } + false + case Some(GeneratorControl.Event.Critical) => + val events = zone.AvatarEvents + val guid = building.GUID + val msg = AvatarAction.PlanetsideAttributeToAll(guid, 46, 1) + building.PlayersInSOI.foreach { player => + events ! AvatarServiceMessage(player.Name, msg) + } + true + case Some(GeneratorControl.Event.Destabilized) => + val events = zone.AvatarEvents + val guid = building.GUID + val msg = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 16) + building.PlayersInSOI.foreach { player => + events ! AvatarServiceMessage(player.Name, msg) + } + false + case Some(GeneratorControl.Event.Destroyed) => + true + case Some(GeneratorControl.Event.Offline) => + powerLost() + val zone = building.Zone + val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 2) + building.PlayersInSOI.foreach { player => + zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg) + } //??? + false + case Some(GeneratorControl.Event.Normal) => + true + case Some(GeneratorControl.Event.Online) => + // Power restored. Reactor Online. Sensors Online. Weapons Online. All systems nominal. + powerRestored() + val events = zone.AvatarEvents + val guid = building.GUID + val msg1 = AvatarAction.PlanetsideAttributeToAll(guid, 46, 0) + val msg2 = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 17) + building.PlayersInSOI.foreach { player => + val name = player.Name + events ! AvatarServiceMessage(name, msg1) //reset ???; might be global? + events ! AvatarServiceMessage(name, msg2) //This facility's generator is back on line + } + true + case _ => + false + } + } + def setFactionTo(faction: PlanetSideEmpire.Value, galaxy: classic.ActorRef): Unit = { - import ctx._ - ctx - .run( - query[persistence.Building] - .filter(_.localId == lift(building.MapId)) - .filter(_.zoneId == lift(zone.Number)) - ) - .onComplete { - case Success(res) => - res.headOption match { - case Some(_) => - ctx - .run( - query[persistence.Building] - .filter(_.localId == lift(building.MapId)) - .filter(_.zoneId == lift(zone.Number)) - .update(_.factionId -> lift(building.Faction.id)) - ) - .onComplete { - case Success(_) => - case Failure(e) => log.error(e.getMessage) - } - case _ => - ctx - .run( - query[persistence.Building] - .insert( - _.localId -> lift(building.MapId), - _.factionId -> lift(building.Faction.id), - _.zoneId -> lift(zone.Number) - ) - ) - .onComplete { - case Success(_) => - case Failure(e) => log.error(e.getMessage) - } - } - case Failure(e) => log.error(e.getMessage) - } - building.Faction = faction - galaxy ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction)) + if (hasNtuSupply) { + import ctx._ + ctx + .run( + query[persistence.Building] + .filter(_.localId == lift(building.MapId)) + .filter(_.zoneId == lift(zone.Number)) + ) + .onComplete { + case Success(res) => + res.headOption match { + case Some(_) => + ctx + .run( + query[persistence.Building] + .filter(_.localId == lift(building.MapId)) + .filter(_.zoneId == lift(zone.Number)) + .update(_.factionId -> lift(building.Faction.id)) + ) + .onComplete { + case Success(_) => + case Failure(e) => log.error(e.getMessage) + } + case _ => + ctx + .run( + query[persistence.Building] + .insert( + _.localId -> lift(building.MapId), + _.factionId -> lift(building.Faction.id), + _.zoneId -> lift(zone.Number) + ) + ) + .onComplete { + case Success(_) => + case Failure(e) => log.error(e.getMessage) + } + } + case Failure(e) => log.error(e.getMessage) + } + building.Faction = faction + galaxy ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) + zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction)) + } } def powerLost(): Unit = { diff --git a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala index 12795d97b..693bde06b 100644 --- a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala @@ -34,13 +34,23 @@ class GeneratorControl(gen: Generator) def DamageableObject = gen def RepairableObject = gen def AutoRepairObject = gen + /** flagged to explode after some time */ var imminentExplosion: Boolean = false + /** explode when this timer completes */ var queuedExplosion: Cancellable = Default.Cancellable - var alarmCooldownPeriod: Boolean = false - private[this] val log = org.log4s.getLogger + /** when damaged, announce that damage was dealt on a schedule */ + var alarmCooldown: Cancellable = Default.Cancellable + + /* + behavior of the generator piggybacks from the logic used in `AmenityAutoRepair` + AAR splits its logic based on whether or not it has detected a source of nanite transfer units (NTU) + this amenity is the bridge between NTU and facility power so it leverages that logic + it is split between when detecting ntu and when starved for ntu + */ def receive: Receive = withNtu + /** behavior that is valid for both "with-ntu" and "without-ntu" */ val commonBehavior: Receive = checkBehavior .orElse(takesDamage) @@ -48,22 +58,29 @@ class GeneratorControl(gen: Generator) .orElse(autoRepairBehavior) .orElse { case GeneratorControl.UnderThreatAlarm() => - if (!alarmCooldownPeriod) { - alarmCooldownPeriod = true - GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.UnderAttack) - queuedExplosion = context.system.scheduler.scheduleOnce(delay = 5 seconds, self, GeneratorControl.AlarmReset()) + //alert to damage and block other damage alerts for a time + if (alarmCooldown.isCancelled) { + GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.UnderAttack)) + alarmCooldown.cancel() + alarmCooldown = context.system.scheduler.scheduleOnce(delay = 5 seconds, self, GeneratorControl.AlarmReset()) } case GeneratorControl.AlarmReset() => - alarmCooldownPeriod = false + //clear the blocker for alerting to damage + alarmCooldown = Default.Cancellable } + /* + when NTU is detected, + the generator can be properly destabilized and explode + the generator can be repaired to operational status and power the facility in which it is installed + */ def withNtu: Receive = commonBehavior .orElse { case GeneratorControl.Destabilized() => imminentExplosion = true - GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Overloaded) + GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destabilized)) queuedExplosion.cancel() queuedExplosion = context.system.scheduler.scheduleOnce(10 seconds, self, GeneratorControl.GeneratorExplodes()) @@ -73,7 +90,7 @@ class GeneratorControl(gen: Generator) gen.Health = 0 super.DestructionAwareness(gen, gen.LastShot.get) gen.Condition = PlanetSideGeneratorState.Destroyed - GeneratorControl.UpdateOwner(gen, Some("destroyed")) + GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destroyed)) //kaboom zone.AvatarEvents ! AvatarServiceMessage( zone.id, @@ -99,39 +116,45 @@ class GeneratorControl(gen: Generator) gen.ClearHistory() case GeneratorControl.Restored() => - GeneratorControl.UpdateOwner(gen, Some("restored")) - GeneratorControl.BroadcastGeneratorEvent(gen, GeneratorControl.Event.Online) + GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Online)) case _ => ; } + /* + when ntu is not expected, + the generator can still be destroyed but will not explode + handles the possibility that ntu was lost during an ongoing destabilization and cancels the explosion + */ def withoutNtu: Receive = - commonBehavior - .orElse { - case GeneratorControl.GeneratorExplodes() => - queuedExplosion.cancel() - queuedExplosion = Default.Cancellable - imminentExplosion = false + commonBehavior + .orElse { + case GeneratorControl.GeneratorExplodes() => + queuedExplosion.cancel() + queuedExplosion = Default.Cancellable + imminentExplosion = false - case GeneratorControl.Destabilized() => - //if the generator is destabilized but has no ntu, it will not explode - gen.Health = 0 - super.DestructionAwareness(gen, gen.LastShot.get) - queuedExplosion.cancel() - queuedExplosion = Default.Cancellable - imminentExplosion = false - gen.Condition = PlanetSideGeneratorState.Destroyed - GeneratorControl.UpdateOwner(gen, Some("destroyed")) - gen.ClearHistory() + case GeneratorControl.Destabilized() => + //if the generator is destabilized but has no ntu, it will not explode + gen.Health = 0 + super.DestructionAwareness(gen, gen.LastShot.get) + queuedExplosion.cancel() + queuedExplosion = Default.Cancellable + imminentExplosion = false + gen.Condition = PlanetSideGeneratorState.Destroyed + GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destroyed)) + gen.ClearHistory() - case _ => - } + case _ => + } override protected def CanPerformRepairs(obj: Target, player: Player, item: Tool): Boolean = { + //if an explosion is queued, disallow repairs !imminentExplosion && super.CanPerformRepairs(obj, player, item) } override protected def WillAffectTarget(target: Target, damage: Int, cause: ResolvedProjectile): Boolean = { + //if an explosion is queued, disallow further damage !imminentExplosion && super.WillAffectTarget(target, damage, cause) } @@ -147,9 +170,10 @@ class GeneratorControl(gen: Generator) override protected def DestructionAwareness(target: Target, cause: ResolvedProjectile): Unit = { tryAutoRepair() + //if the target is already destroyed, do not let it be destroyed again if (!target.Destroyed) { target.Health = 1 //temporary - GeneratorControl.UpdateOwner(gen, Some("overloaded")) + GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Offline)) self ! GeneratorControl.Destabilized() } } @@ -165,12 +189,14 @@ class GeneratorControl(gen: Generator) override def Restoration(obj: Repairable.Target): Unit = { super.Restoration(obj) gen.Condition = PlanetSideGeneratorState.Normal + GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Normal)) self ! GeneratorControl.Restored() } override def withNtuSupplyCallback() : Unit = { context.become(withNtu) super.withNtuSupplyCallback() + //if not destroyed when a source of ntu is detected, restore facility power if(!gen.Destroyed) { self ! GeneratorControl.Restored() } @@ -180,9 +206,11 @@ class GeneratorControl(gen: Generator) //auto-repair must stop naturally context.become(withoutNtu) super.noNtuSupplyCallback() + //if not destroyed when cutoff from a source of ntu, stop facility power generation if(!gen.Destroyed) { - GeneratorControl.UpdateOwner(gen, Some("overloaded")) + GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Offline)) } + //can any explosion (see withoutNtu->GenweratorControl.Destabilized) if(!queuedExplosion.isCancelled) { queuedExplosion.cancel() self ! GeneratorControl.Destabilized() @@ -191,7 +219,6 @@ class GeneratorControl(gen: Generator) } object GeneratorControl { - /** * na */ @@ -221,41 +248,24 @@ object GeneratorControl { * na */ object Event extends Enumeration { - val UnderAttack = Value(15) - val Critical = Value(0) - val Overloaded = Value(16) - val Online = Value(17) + val + Critical, //PlanetSideGeneratorState.Critical + UnderAttack, + Destabilized, + Destroyed, //PlanetSideGeneratorState.Destroyed + Offline, + Normal, //PlanetSideGeneratorState.Normal + Online + = Value } /** - * na - * @param obj na + * Send a message back to the owner for which this `Amenity` entity is installed. + * @param obj the entity doing the self-reporting + * @param data optional information that indicates the nature of the state change */ private def UpdateOwner(obj: Generator, data: Option[Any] = None): Unit = { - obj.Owner match { - case b: Building => b.Actor ! BuildingActor.AmenityStateChange(obj, data) - case _ => ; - } - } - - /** - * na - * @param target the generator - * @param event the action code for the event - */ - private def BroadcastGeneratorEvent(target: Generator, event: Event.Value): Unit = { - target.Owner match { - case b: Building => - val events = target.Zone.AvatarEvents - val msg = event match { - case Event.Critical => AvatarAction.PlanetsideAttributeToAll(b.GUID, 46, 1) //critical status warning - case _ => AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, b.GUID, event.id) - } - b.PlayersInSOI.foreach { player => - events ! AvatarServiceMessage(player.Name, msg) - } - case _ => ; - } + obj.Owner.Actor ! BuildingActor.AmenityStateChange(obj, data) } /** @@ -270,7 +280,7 @@ object GeneratorControl { val max: Float = target.MaxHealth.toFloat if (target.Condition != PlanetSideGeneratorState.Critical && health / max < 0.51f) { //becoming critical target.Condition = PlanetSideGeneratorState.Critical - GeneratorControl.UpdateOwner(target, Some("critical")) + GeneratorControl.UpdateOwner(target, Some(GeneratorControl.Event.Critical)) } //the generator is under attack target.Actor ! UnderThreatAlarm() diff --git a/src/test/scala/objects/AutoRepairIntegrationTest.scala b/src/test/scala/objects/AutoRepairIntegrationTest.scala index 581943081..3ce685028 100644 --- a/src/test/scala/objects/AutoRepairIntegrationTest.scala +++ b/src/test/scala/objects/AutoRepairIntegrationTest.scala @@ -4,6 +4,7 @@ package objects import akka.actor.Props import akka.testkit.TestProbe import base.FreedContextActorTest +import net.psforever.actors.zone.BuildingActor import net.psforever.objects.avatar.Avatar import net.psforever.objects.ballistics.{Projectile, ProjectileResolution, ResolvedProjectile, SourceEntry} import net.psforever.objects.guid.NumberPoolHub @@ -57,6 +58,7 @@ class AutoRepairFacilityIntegrationTest extends FreedContextActorTest { silo.NtuCapacitor = 1000 silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo") silo.Actor ! "startup" + building.Actor ! BuildingActor.PowerOn() //artificial val wep_fmode = weapon.FireMode val wep_prof = wep_fmode.Add diff --git a/src/test/scala/objects/DoorTest.scala b/src/test/scala/objects/DoorTest.scala index 742af526e..bc908e66e 100644 --- a/src/test/scala/objects/DoorTest.scala +++ b/src/test/scala/objects/DoorTest.scala @@ -2,17 +2,22 @@ package objects import akka.actor.{ActorSystem, Props} +import akka.testkit.TestProbe import base.ActorTest import net.psforever.objects.avatar.Avatar +import net.psforever.objects.guid.NumberPoolHub +import net.psforever.objects.guid.source.MaxNumberSource +import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.{Default, GlobalDefinitions, Player} import net.psforever.objects.serverobject.doors.{Door, DoorControl} import net.psforever.objects.serverobject.structures.{Building, StructureType} -import net.psforever.objects.zones.Zone +import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.packet.game.UseItemMessage +import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.types._ import org.specs2.mutable.Specification -import scala.concurrent.duration.Duration +import scala.concurrent.duration._ class DoorTest extends Specification { val player = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) @@ -83,6 +88,8 @@ class DoorControl2Test extends ActorTest { "DoorControl" should { "open on use" in { val (player, door) = DoorControlTest.SetUpAgents(PlanetSideEmpire.TR) + val probe = new TestProbe(system) + door.Zone.LocalEvents = probe.ref val msg = UseItemMessage( PlanetSideGUID(1), PlanetSideGUID(0), @@ -98,13 +105,12 @@ class DoorControl2Test extends ActorTest { ) //faked assert(door.Open.isEmpty) - door.Actor ! Door.Use(player, msg) - val reply = receiveOne(Duration.create(500, "ms")) - assert(reply.isInstanceOf[Door.DoorMessage]) - val reply2 = reply.asInstanceOf[Door.DoorMessage] - assert(reply2.player == player) - assert(reply2.msg == msg) - assert(reply2.response == Door.OpenEvent()) + door.Actor ! CommonMessages.Use(player, Some(msg)) + val reply = probe.receiveOne(1000 milliseconds) + assert(reply match { + case LocalServiceMessage("test", LocalAction.DoorOpens(PlanetSideGUID(0), _, d)) => d eq door + case _ => false + }) assert(door.Open.isDefined) } } @@ -126,16 +132,24 @@ class DoorControl3Test extends ActorTest { object DoorControlTest { def SetUpAgents(faction: PlanetSideEmpire.Value)(implicit system: ActorSystem): (Player, Door) = { val door = Door(GlobalDefinitions.door) + val guid = new NumberPoolHub(new MaxNumberSource(5)) + val zone = new Zone("test", new ZoneMap("test"), 0) { + override def SetupNumberPools() = {} + GUID(guid) + } + guid.register(door, 1) door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door") door.Owner = new Building( "Building", building_guid = 0, map_id = 0, - Zone.Nowhere, + zone, StructureType.Building, GlobalDefinitions.building ) door.Owner.Faction = faction - (Player(Avatar(0, "test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)), door) + val player = Player(Avatar(0, "test", faction, CharacterGender.Male, 0, CharacterVoice.Mute)) + guid.register(player, 2) + (player, door) } } diff --git a/src/test/scala/objects/GeneratorTest.scala b/src/test/scala/objects/GeneratorTest.scala index 91ea0926f..10807bf02 100644 --- a/src/test/scala/objects/GeneratorTest.scala +++ b/src/test/scala/objects/GeneratorTest.scala @@ -11,7 +11,7 @@ import net.psforever.objects.{GlobalDefinitions, Player, Tool} import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.source.MaxNumberSource import net.psforever.objects.serverobject.CommonMessages -import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl} +import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl, GeneratorDefinition} import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.vital.Vitality import net.psforever.objects.zones.{Zone, ZoneMap} @@ -25,12 +25,12 @@ import scala.concurrent.duration._ class GeneratorTest extends Specification { "Generator" should { "construct" in { - Generator(GlobalDefinitions.generator) + Generator(GeneratorTest.generator_definition) ok } "start in 'Normal' condition" in { - val obj = Generator(GlobalDefinitions.generator) + val obj = Generator(GeneratorTest.generator_definition) obj.Condition mustEqual PlanetSideGeneratorState.Normal } } @@ -39,7 +39,7 @@ class GeneratorTest extends Specification { class GeneratorControlConstructTest extends ActorTest { "GeneratorControl" should { "construct" in { - val gen = Generator(GlobalDefinitions.generator) + val gen = Generator(GeneratorTest.generator_definition) gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "gen-control") assert(gen.Actor != ActorRef.noSender) } @@ -57,7 +57,7 @@ class GeneratorControlDamageTest extends ActorTest { val activityProbe = TestProbe() zone.Activity = activityProbe.ref - val gen = Generator(GlobalDefinitions.generator) //guid=2 + val gen = Generator(GeneratorTest.generator_definition) //guid=2 gen.Position = Vector3(1, 0, 0) gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control") @@ -106,19 +106,18 @@ class GeneratorControlDamageTest extends ActorTest { assert(gen.Condition == PlanetSideGeneratorState.Normal) gen.Actor ! Vitality.Damage(applyDamageTo) - val msg_avatar = avatarProbe.receiveN(2, 500 milliseconds) - buildingProbe.expectNoMessage(200 milliseconds) + val msg_avatar = avatarProbe.receiveOne(500 milliseconds) + val msg_building = buildingProbe.receiveOne(500 milliseconds) assert( - msg_avatar.head match { + msg_avatar 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 + msg_building match { + case BuildingActor.AmenityStateChange(_, Some(GeneratorControl.Event.UnderAttack)) => true + case _ => false } ) assert(gen.Health < gen.Definition.MaxHealth) @@ -139,7 +138,7 @@ class GeneratorControlCriticalTest extends ActorTest { val activityProbe = TestProbe() zone.Activity = activityProbe.ref - val gen = Generator(GlobalDefinitions.generator) //guid=2 + val gen = Generator(GeneratorTest.generator_definition) //guid=2 gen.Position = Vector3(1, 0, 0) gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control") @@ -190,25 +189,18 @@ class GeneratorControlCriticalTest extends ActorTest { assert(gen.Condition == PlanetSideGeneratorState.Normal) gen.Actor ! Vitality.Damage(applyDamageTo) - val msg_avatar = avatarProbe.receiveN(2, 500 milliseconds) + val msg_avatar = avatarProbe.receiveOne(500 milliseconds) val msg_building = buildingProbe.receiveOne(500 milliseconds) assert( - msg_avatar.head match { + msg_avatar 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 BuildingActor.AmenityStateChange(o) => o eq gen - case _ => false + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Critical)) => o eq gen + case _ => false } ) assert(gen.Health < halfHealth) @@ -229,7 +221,7 @@ class GeneratorControlDestroyedTest extends ActorTest { val activityProbe = TestProbe() zone.Activity = activityProbe.ref - val gen = Generator(GlobalDefinitions.generator) //guid=2 + val gen = Generator(GeneratorTest.generator_definition) //guid=2 gen.Position = Vector3(1, 0, 0) gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control") @@ -269,7 +261,6 @@ class GeneratorControlDestroyedTest extends ActorTest { Vector3(1, 0, 0) ) val applyDamageTo = resolved.damage_model.Calculate(resolved) - gen.Actor ! BuildingActor.NtuDepleted() //no auto-repair expectNoMessage(200 milliseconds) //we're not testing that the math is correct @@ -281,12 +272,16 @@ class GeneratorControlDestroyedTest extends ActorTest { 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) + val msg_building12 = buildingProbe.receiveN(2,500 milliseconds) assert( - msg_avatar1 match { - case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => - true + msg_building12.head match { + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Offline)) => o eq gen + case _ => false + } + ) + assert( + msg_building12(1) match { + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Destabilized)) => o eq gen case _ => false } ) @@ -294,13 +289,13 @@ class GeneratorControlDestroyedTest extends ActorTest { assert(!gen.Destroyed) assert(gen.Condition == PlanetSideGeneratorState.Normal) - avatarProbe.expectNoMessage(9 seconds) + avatarProbe.expectNoMessage(9500 milliseconds) val msg_avatar2 = avatarProbe.receiveN(3, 1000 milliseconds) //see DamageableEntity test file val msg_building = buildingProbe.receiveOne(200 milliseconds) assert( msg_building match { - case BuildingActor.AmenityStateChange(o) => o eq gen - case _ => false + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Destroyed)) => o eq gen + case _ => false } ) assert( @@ -352,7 +347,7 @@ class GeneratorControlKillsTest extends ActorTest { val activityProbe = TestProbe() zone.Activity = activityProbe.ref - val gen = Generator(GlobalDefinitions.generator) //guid=2 + val gen = Generator(GeneratorTest.generator_definition) //guid=2 gen.Position = Vector3(1, 0, 0) gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control") @@ -400,7 +395,6 @@ class GeneratorControlKillsTest extends ActorTest { Vector3(1, 0, 0) ) val applyDamageTo = resolved.damage_model.Calculate(resolved) - gen.Actor ! BuildingActor.NtuDepleted() //no auto-repair expectNoMessage(200 milliseconds) //we're not testing that the math is correct @@ -412,21 +406,16 @@ class GeneratorControlKillsTest extends ActorTest { 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) + val msg_building12 = buildingProbe.receiveN(2,500 milliseconds) assert( - msg_avatar1.head match { - case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => - true + msg_building12.head match { + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Offline)) => o eq gen case _ => false } ) assert( - msg_avatar1(1) match { - case AvatarServiceMessage("TestCharacter2", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => - true + msg_building12(1) match { + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Destabilized)) => o eq gen case _ => false } ) @@ -434,14 +423,13 @@ class GeneratorControlKillsTest extends ActorTest { 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) + avatarProbe.expectNoMessage(9500 milliseconds) + val msg_avatar2 = avatarProbe.receiveN(3, 1000 milliseconds) //see DamageableEntity test file + val msg_building = buildingProbe.receiveOne(200 milliseconds) assert( msg_building match { - case BuildingActor.AmenityStateChange(o) => o eq gen - case _ => false + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Destroyed)) => o eq gen + case _ => false } ) assert( @@ -459,22 +447,25 @@ class GeneratorControlKillsTest extends ActorTest { assert( msg_avatar2(2) match { case AvatarServiceMessage( - "test", - AvatarAction.SendResponse(_, TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None)) - ) => + "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) + + val msg_player1 = player1Probe.receiveOne(100 milliseconds) + player2Probe.expectNoMessage(200 milliseconds) assert( msg_player1 match { case _ @Player.Die() => true case _ => false } ) - assert(gen.Health == 0) - assert(gen.Destroyed) - assert(gen.Condition == PlanetSideGeneratorState.Destroyed) } } } @@ -487,7 +478,7 @@ class GeneratorControlNotDestroyTwice extends ActorTest { GUID(guid) } val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1 - val gen = Generator(GlobalDefinitions.generator) //guid=2 + val gen = Generator(GeneratorTest.generator_definition) //guid=2 val player1 = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3 player1.Spawn() @@ -573,7 +564,7 @@ class GeneratorControlNotDamageIfExplodingTest extends ActorTest { val activityProbe = TestProbe() zone.Activity = activityProbe.ref - val gen = Generator(GlobalDefinitions.generator) //guid=2 + val gen = Generator(GeneratorTest.generator_definition) //guid=2 gen.Position = Vector3(1, 0, 0) gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control") @@ -625,13 +616,16 @@ class GeneratorControlNotDamageIfExplodingTest extends ActorTest { 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) + val msg_building12 = buildingProbe.receiveN(2,500 milliseconds) assert( - msg_avatar match { - case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => - true + msg_building12.head match { + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Offline)) => o eq gen + case _ => false + } + ) + assert( + msg_building12(1) match { + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Destabilized)) => o eq gen case _ => false } ) @@ -667,7 +661,7 @@ class GeneratorControlNotRepairIfExplodingTest extends ActorTest { val activityProbe = TestProbe() zone.Activity = activityProbe.ref - val gen = Generator(GlobalDefinitions.generator) //guid=2 + val gen = Generator(GeneratorTest.generator_definition) //guid=2 gen.Position = Vector3(1, 0, 0) gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control") @@ -723,13 +717,16 @@ class GeneratorControlNotRepairIfExplodingTest extends ActorTest { 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) + val msg_building12 = buildingProbe.receiveN(2,500 milliseconds) assert( - msg_avatar1 match { - case AvatarServiceMessage("TestCharacter1", AvatarAction.GenericObjectAction(_, PlanetSideGUID(1), 16)) => - true + msg_building12.head match { + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Offline)) => o eq gen + case _ => false + } + ) + assert( + msg_building12(1) match { + case BuildingActor.AmenityStateChange(o, Some(GeneratorControl.Event.Destabilized)) => o eq gen case _ => false } ) @@ -765,7 +762,7 @@ class GeneratorControlRepairPastRestorePoint extends ActorTest { val activityProbe = TestProbe() zone.Activity = activityProbe.ref - val gen = Generator(GlobalDefinitions.generator) //guid=2 + val gen = Generator(GeneratorTest.generator_definition) //guid=2 gen.Position = Vector3(1, 0, 0) gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control") @@ -804,7 +801,7 @@ class GeneratorControlRepairPastRestorePoint extends ActorTest { assert(gen.Destroyed) gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair - val msg_avatar = avatarProbe.receiveN(4, 500 milliseconds) //expected + val msg_avatar = avatarProbe.receiveN(3, 500 milliseconds) //expected val msg_building = buildingProbe.receiveOne(200 milliseconds) assert( msg_avatar.head match { @@ -825,13 +822,6 @@ class GeneratorControlRepairPastRestorePoint extends ActorTest { ) 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), _)) @@ -842,7 +832,7 @@ class GeneratorControlRepairPastRestorePoint extends ActorTest { ) assert( msg_building match { - case BuildingActor.AmenityStateChange(o) => o eq gen + case BuildingActor.AmenityStateChange(o, _) => o eq gen case _ => false } ) @@ -852,3 +842,15 @@ class GeneratorControlRepairPastRestorePoint extends ActorTest { } } } + +object GeneratorTest { + final val generator_definition = new GeneratorDefinition(352) { + MaxHealth = 4000 + Damageable = true + DamageableByFriendlyFire = false + Repairable = true + RepairDistance = 13.5f + RepairIfDestroyed = true + //note: no auto-repair + } +} diff --git a/src/test/scala/objects/ResourceSiloTest.scala b/src/test/scala/objects/ResourceSiloTest.scala index ba1fe6432..36dab9b95 100644 --- a/src/test/scala/objects/ResourceSiloTest.scala +++ b/src/test/scala/objects/ResourceSiloTest.scala @@ -289,12 +289,6 @@ class ResourceSiloControlUpdate1Test extends ActorTest { case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => true case _ => false }) - - val reply4 = zoneEvents.receiveOne(500 milliseconds) - assert(reply4 match { - case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 48, 0)) => true - case _ => false - }) } } }