diff --git a/common/src/main/scala/net/psforever/objects/entity/IdentifiableEntity.scala b/common/src/main/scala/net/psforever/objects/entity/IdentifiableEntity.scala index 3531f390..dc5e283b 100644 --- a/common/src/main/scala/net/psforever/objects/entity/IdentifiableEntity.scala +++ b/common/src/main/scala/net/psforever/objects/entity/IdentifiableEntity.scala @@ -9,25 +9,42 @@ import net.psforever.packet.game.PlanetSideGUID * "Testing" the object refers to the act of acquiring a reference to the GUID the object is using. * This object starts with a container class that represents a unprepared GUID state and raises an `Exception` when tested. * Setting a proper `PlanetSideGUID` replaces that container class with a container class that returns the GUID when tested. - * The object can be invalidated, restoring the previous `Exception`-raising condition. - * @throws `NoGUIDException` if there is no GUID to give + * The object can be invalidated, retaining the previous identifier number, but marking that object as being "stale." + * "Staleness" is a property indicating whether or not the number can be used as a valid representation of the object. + * @throws `NoGUIDException` if a GUID has not yet been assigned */ abstract class IdentifiableEntity extends Identifiable { + /** indicate the validity of the current GUID */ + private var stale : Boolean = true + /** storage for the active GUID */ private val container : GUIDContainable = GUIDContainer() + /** the handle for the active GUID; starts as exception-throwing */ private var current : GUIDContainable = IdentifiableEntity.noGUIDContainer - def HasGUID : Boolean = current ne IdentifiableEntity.noGUIDContainer + /** + * The object will not originally having a valid GUID, + * so "stale" will be used to expressed "not initialized." + * After being set and then properly invalidated, then it will indicate proper staleness. + * @return whether the value of the GUID is a valid representation for this object + */ + def HasGUID : Boolean = !stale def GUID : PlanetSideGUID = current.GUID def GUID_=(guid : PlanetSideGUID) : PlanetSideGUID = { + stale = false current = container current.GUID = guid GUID } + /** + * Set the staleness to indicate whether the GUID has ever been set + * or that the set GUID is not a proper representation of the object. + * It is always set to `true`. + */ def Invalidate() : Unit = { - current = IdentifiableEntity.noGUIDContainer + stale = true } } @@ -50,7 +67,7 @@ private case class NoGUIDContainer() extends GUIDContainable { * @return never returns */ def GUID : PlanetSideGUID = { - throw NoGUIDException("object has not initialized a global identifier") + throw NoGUIDException(s"object $this has not initialized a global identifier") } /** diff --git a/common/src/test/scala/objects/EntityTest.scala b/common/src/test/scala/objects/EntityTest.scala index 9ac85bfd..9eb089fc 100644 --- a/common/src/test/scala/objects/EntityTest.scala +++ b/common/src/test/scala/objects/EntityTest.scala @@ -106,14 +106,16 @@ class EntityTest extends Specification { ok } - "error while unset" in { + "error while not set" in { val obj : EntityTestClass = new EntityTestClass + obj.HasGUID mustEqual false obj.GUID must throwA[NoGUIDException] } "work after mutation" in { val obj : EntityTestClass = new EntityTestClass obj.GUID = PlanetSideGUID(1051) + obj.HasGUID mustEqual true obj.GUID mustEqual PlanetSideGUID(1051) } @@ -127,12 +129,43 @@ class EntityTest extends Specification { obj.GUID mustEqual PlanetSideGUID(62) } - "invalidate and resume error" in { + "invalidate and report as not having a GUID, but continue to work" in { val obj : EntityTestClass = new EntityTestClass obj.GUID = PlanetSideGUID(1051) + obj.HasGUID mustEqual true obj.GUID mustEqual PlanetSideGUID(1051) obj.Invalidate() - obj.GUID must throwA[NoGUIDException] + obj.HasGUID mustEqual false + obj.GUID mustEqual PlanetSideGUID(1051) + } + + "assign a new GUID after invalidation and continue to work" in { + val obj : EntityTestClass = new EntityTestClass + obj.GUID = PlanetSideGUID(1051) + obj.HasGUID mustEqual true + obj.GUID mustEqual PlanetSideGUID(1051) + obj.Invalidate() + obj.GUID mustEqual PlanetSideGUID(1051) + obj.HasGUID mustEqual false + + obj.GUID = PlanetSideGUID(1052) + obj.HasGUID mustEqual true + obj.GUID mustEqual PlanetSideGUID(1052) + } + + "assignthe same GUID after invalidation and continue to work" in { + val obj : EntityTestClass = new EntityTestClass + val guid = new PlanetSideGUID(1051) + obj.GUID = guid + obj.HasGUID mustEqual true + obj.GUID mustEqual guid + obj.Invalidate() + obj.GUID mustEqual guid + obj.HasGUID mustEqual false + + obj.GUID = guid + obj.HasGUID mustEqual true + obj.GUID mustEqual guid } } } diff --git a/common/src/test/scala/objects/number/NumberPoolActorTest.scala b/common/src/test/scala/objects/number/NumberPoolActorTest.scala index 61b181ec..a3357b36 100644 --- a/common/src/test/scala/objects/number/NumberPoolActorTest.scala +++ b/common/src/test/scala/objects/number/NumberPoolActorTest.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package objects.number -import akka.actor.{ActorSystem, Props} +import akka.actor.Props import base.ActorTest import net.psforever.objects.guid.actor.NumberPoolActor import net.psforever.objects.guid.pool.ExclusivePool diff --git a/common/src/test/scala/objects/number/NumberPoolHubTest.scala b/common/src/test/scala/objects/number/NumberPoolHubTest.scala index eb5546bc..966b547a 100644 --- a/common/src/test/scala/objects/number/NumberPoolHubTest.scala +++ b/common/src/test/scala/objects/number/NumberPoolHubTest.scala @@ -55,7 +55,7 @@ class NumberPoolHubTest extends Specification { obj.Numbers.toSet.equals(numberList.toSet) mustEqual true obj.RemovePool("fibonacci").toSet.equals(numberList.toSet) mustEqual true obj.Numbers.isEmpty mustEqual true - obj.GetPool("fibonacci") mustEqual None + obj.GetPool("fibonacci").isEmpty mustEqual true } "block removing the default 'generic' pool" in { @@ -101,7 +101,7 @@ class NumberPoolHubTest extends Specification { hub.register(obj, "fibonacci") match { case Success(number) => val objFromNumber = hub(number) - objFromNumber mustEqual Some(obj) + objFromNumber.contains(obj) mustEqual true case _ => ko } @@ -111,7 +111,7 @@ class NumberPoolHubTest extends Specification { val hub = new NumberPoolHub(new LimitedNumberSource(51)) hub.AddPool("fibonacci1", numberList1) hub.AddPool("fibonacci2", numberList2) - hub.WhichPool(13) mustEqual Some("fibonacci2") + hub.WhichPool(13).contains("fibonacci2") mustEqual true } "lookup the pool of a registered object" in { @@ -119,7 +119,7 @@ class NumberPoolHubTest extends Specification { hub.AddPool("fibonacci", numberList1) val obj = new EntityTestClass() hub.register(obj, "fibonacci") - hub.WhichPool(obj) mustEqual Some("fibonacci") + hub.WhichPool(obj).contains("fibonacci") mustEqual true } "register an object to a specific, unused number; it is assigned to pool 'generic'" in { @@ -130,7 +130,7 @@ class NumberPoolHubTest extends Specification { hub.register(obj, 44) match { case Success(number) => obj.GUID mustEqual PlanetSideGUID(number) - hub.WhichPool(obj) mustEqual Some("generic") + hub.WhichPool(obj).contains("generic") mustEqual true case _ => ko } @@ -145,8 +145,8 @@ class NumberPoolHubTest extends Specification { hub.register(obj, 5) match { case Success(number) => obj.GUID mustEqual PlanetSideGUID(number) - hub.WhichPool(obj) mustEqual Some("fibonacci") - src.Available(5) mustEqual None + hub.WhichPool(obj).contains("fibonacci") mustEqual true + src.Available(5).isEmpty mustEqual true case _ => ko } @@ -161,8 +161,8 @@ class NumberPoolHubTest extends Specification { hub.register(obj, 13) match { case Success(number) => obj.GUID mustEqual PlanetSideGUID(number) - hub.WhichPool(obj) mustEqual Some("fibonacci") - src.Available(13) mustEqual None + hub.WhichPool(obj).contains("fibonacci") mustEqual true + src.Available(13).isEmpty mustEqual true case _ => ko } @@ -172,20 +172,21 @@ class NumberPoolHubTest extends Specification { val hub = new NumberPoolHub(new LimitedNumberSource(51)) val obj = new EntityTestClass() hub.register(obj) - hub.WhichPool(obj) mustEqual Some("generic") + hub.WhichPool(obj).contains("generic") mustEqual true } "unregister an object" in { val hub = new NumberPoolHub(new LimitedNumberSource(51)) hub.AddPool("fibonacci", numberList) val obj = new EntityTestClass() + obj.HasGUID mustEqual false hub.register(obj, "fibonacci") - hub.WhichPool(obj) mustEqual Some("fibonacci") - try { obj.GUID } catch { case _ : Exception => ko } //passes + hub.WhichPool(obj).contains("fibonacci") mustEqual true + obj.HasGUID mustEqual true hub.unregister(obj) - hub.WhichPool(obj) mustEqual None - obj.GUID must throwA[Exception] //fails + obj.HasGUID mustEqual false + hub.WhichPool(obj).isEmpty mustEqual true } "not register an object to a different pool" in { @@ -247,14 +248,15 @@ class NumberPoolHubTest extends Specification { hub.register(13) match { case Success(key) => key.Object = obj + obj.HasGUID mustEqual true case _ => ko } - hub.WhichPool(obj) mustEqual Some("fibonacci") + hub.WhichPool(obj).contains("fibonacci") mustEqual true hub.unregister(13) match { case Success(thing) => - thing mustEqual Some(obj) - thing.get.GUID must throwA[Exception] + thing.contains(obj) mustEqual true + thing.get.HasGUID mustEqual false case _ => ko } diff --git a/common/src/test/scala/objects/number/NumberPoolTest.scala b/common/src/test/scala/objects/number/NumberPoolTest.scala index a27c5783..6c51f3db 100644 --- a/common/src/test/scala/objects/number/NumberPoolTest.scala +++ b/common/src/test/scala/objects/number/NumberPoolTest.scala @@ -158,10 +158,10 @@ class NumberPoolTest extends Specification { val obj = new GenericPool(map, 11) obj.Selector.asInstanceOf[SpecificSelector].SelectionIndex = 5 obj.Get() - map.get(5) mustEqual Some("generic") + map.get(5).contains("generic") mustEqual true obj.Numbers.contains(5) mustEqual true obj.Return(5) mustEqual true - map.get(5) mustEqual None + map.get(5).isEmpty mustEqual true obj.Numbers.isEmpty mustEqual true } diff --git a/common/src/test/scala/objects/number/NumberSourceTest.scala b/common/src/test/scala/objects/number/NumberSourceTest.scala index f60e6ec9..132baf2e 100644 --- a/common/src/test/scala/objects/number/NumberSourceTest.scala +++ b/common/src/test/scala/objects/number/NumberSourceTest.scala @@ -25,7 +25,7 @@ class NumberSourceTest extends Specification { result.isDefined mustEqual true result.get.GUID mustEqual 5 result.get.Policy mustEqual AvailabilityPolicy.Leased - result.get.Object mustEqual None + result.get.Object.isEmpty mustEqual true obj.Size mustEqual 26 obj.CountAvailable mustEqual 25 obj.CountUsed mustEqual 1 @@ -46,7 +46,7 @@ class NumberSourceTest extends Specification { result.get.GUID mustEqual 5 obj.CountUsed mustEqual 1 val ret = obj.Return(result.get) - ret mustEqual None + ret.isEmpty mustEqual true obj.CountUsed mustEqual 0 } @@ -59,7 +59,7 @@ class NumberSourceTest extends Specification { result.get.Object = test obj.CountUsed mustEqual 1 val ret = obj.Return(result.get) - ret mustEqual Some(test) + ret.contains(test) mustEqual true obj.CountUsed mustEqual 0 } @@ -69,7 +69,7 @@ class NumberSourceTest extends Specification { result.isDefined mustEqual true result.get.GUID mustEqual 5 result.get.Policy mustEqual AvailabilityPolicy.Restricted - result.get.Object mustEqual None + result.get.Object.isEmpty mustEqual true } "restrict a number (assigned + multiple assignments)" in { @@ -79,13 +79,13 @@ class NumberSourceTest extends Specification { val result : Option[LoanedKey] = obj.Restrict(5) result.get.GUID mustEqual 5 result.get.Policy mustEqual AvailabilityPolicy.Restricted - result.get.Object mustEqual None + result.get.Object.isEmpty mustEqual true result.get.Object = None //assignment 1 - result.get.Object mustEqual None //still unassigned + result.get.Object.isEmpty mustEqual true //still unassigned result.get.Object = test1 //assignment 2 - result.get.Object mustEqual Some(test1) + result.get.Object.contains(test1) mustEqual true result.get.Object = test2 //assignment 3 - result.get.Object mustEqual Some(test1) //same as above + result.get.Object.contains(test1) mustEqual true //same as above } "return a restricted number (correctly fail)" in { @@ -100,7 +100,7 @@ class NumberSourceTest extends Specification { val result2 : Option[SecureKey] = obj.Get(5) result2.get.GUID mustEqual 5 result2.get.Policy mustEqual AvailabilityPolicy.Restricted - result2.get.Object mustEqual Some(test) + result2.get.Object.contains(test) mustEqual true } "return a secure key" in { @@ -111,7 +111,7 @@ class NumberSourceTest extends Specification { test.GUID = PlanetSideGUID(5) val result2 : Option[SecureKey] = obj.Get(5) - obj.Return(result2.get) mustEqual Some(test) + obj.Return(result2.get).contains(test) mustEqual true } "restrict a previously-assigned number" in { @@ -124,7 +124,7 @@ class NumberSourceTest extends Specification { val result2 : Option[LoanedKey] = obj.Restrict(5) result2.isDefined mustEqual true result2.get.Policy mustEqual AvailabilityPolicy.Restricted - result2.get.Object mustEqual Some(test) + result2.get.Object.contains(test) mustEqual true } "check a number (not previously gotten)" in { @@ -132,7 +132,7 @@ class NumberSourceTest extends Specification { val result2 : Option[SecureKey] = obj.Get(5) result2.get.GUID mustEqual 5 result2.get.Policy mustEqual AvailabilityPolicy.Available - result2.get.Object mustEqual None + result2.get.Object.isEmpty mustEqual true } "check a number (previously gotten)" in { @@ -141,11 +141,11 @@ class NumberSourceTest extends Specification { result.isDefined mustEqual true result.get.GUID mustEqual 5 result.get.Policy mustEqual AvailabilityPolicy.Leased - result.get.Object mustEqual None + result.get.Object.isEmpty mustEqual true val result2 : Option[SecureKey] = obj.Get(5) result2.get.GUID mustEqual 5 result2.get.Policy mustEqual AvailabilityPolicy.Leased - result2.get.Object mustEqual None + result2.get.Object.isEmpty mustEqual true } "check a number (assigned)" in { @@ -170,10 +170,10 @@ class NumberSourceTest extends Specification { val result2 : Option[SecureKey] = obj.Get(5) result2.get.Policy mustEqual AvailabilityPolicy.Leased result2.get.Object.get mustEqual test - obj.Return(5) mustEqual Some(test) + obj.Return(5).contains(test) mustEqual true val result3 : Option[SecureKey] = obj.Get(5) result3.get.Policy mustEqual AvailabilityPolicy.Available - result3.get.Object mustEqual None + result3.get.Object.isEmpty mustEqual true } "clear" in { diff --git a/common/src/test/scala/objects/number/RegisterTest.scala b/common/src/test/scala/objects/number/RegisterTest.scala index 5bde0af0..ef9314cb 100644 --- a/common/src/test/scala/objects/number/RegisterTest.scala +++ b/common/src/test/scala/objects/number/RegisterTest.scala @@ -12,49 +12,49 @@ class RegisterTest extends Specification { "construct (object)" in { val reg = Register(obj) reg.obj mustEqual obj - reg.number mustEqual None - reg.name mustEqual None - reg.callback mustEqual None + reg.number.isEmpty mustEqual true + reg.name.isEmpty mustEqual true + reg.callback.isEmpty mustEqual true } "construct (object, callback)" in { val reg = Register(obj, Actor.noSender) reg.obj mustEqual obj - reg.number mustEqual None - reg.name mustEqual None - reg.callback mustEqual Some(Actor.noSender) + reg.number.isEmpty mustEqual true + reg.name.isEmpty mustEqual true + reg.callback.contains(Actor.noSender) mustEqual true } "construct (object, suggested number)" in { val reg = Register(obj, 5) reg.obj mustEqual obj - reg.number mustEqual Some(5) - reg.name mustEqual None - reg.callback mustEqual None + reg.number.contains(5) mustEqual true + reg.name.isEmpty mustEqual true + reg.callback.isEmpty mustEqual true } "construct (object, suggested number, callback)" in { val reg = Register(obj, 5, Actor.noSender) reg.obj mustEqual obj - reg.number mustEqual Some(5) - reg.name mustEqual None - reg.callback mustEqual Some(Actor.noSender) + reg.number.contains(5) mustEqual true + reg.name.isEmpty mustEqual true + reg.callback.contains(Actor.noSender) mustEqual true } "construct (object, pool name)" in { val reg = Register(obj, "pool") reg.obj mustEqual obj - reg.number mustEqual None - reg.name mustEqual Some("pool") - reg.callback mustEqual None + reg.number.isEmpty mustEqual true + reg.name.contains("pool") mustEqual true + reg.callback.isEmpty mustEqual true } "construct (object, pool name, callback)" in { val reg = Register(obj, "pool", Actor.noSender) reg.obj mustEqual obj - reg.number mustEqual None - reg.name mustEqual Some("pool") - reg.callback mustEqual Some(Actor.noSender) + reg.number.isEmpty mustEqual true + reg.name.contains("pool") mustEqual true + reg.callback.contains(Actor.noSender) mustEqual true } } } diff --git a/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala b/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala index 17e7efe6..97203814 100644 --- a/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala +++ b/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala @@ -341,7 +341,7 @@ object UniqueNumberSystemTest { * @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)` */ def AllocateNumberPoolActors(poolSource : NumberPoolHub)(implicit system : ActorSystem) : Map[String, ActorRef] = { - poolSource.Pools.map({ case ((pname, pool)) => + poolSource.Pools.map({ case (pname, pool) => pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname) }).toMap } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 2eb3f95a..620aa1ba 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -170,7 +170,6 @@ class WorldSessionActor extends Actor var antChargingTick : Cancellable = DefaultCancellable.obj var antDischargingTick : Cancellable = DefaultCancellable.obj - /** * Convert a boolean value into an integer value. * Use: `true:Int` or `false:Int` @@ -1131,7 +1130,7 @@ class WorldSessionActor extends Actor log.warn(s"Vital target ${target.Definition.Name} damage resolution not supported using this method") case ResponseToSelf(pkt) => - log.info(s"Received a direct message: $pkt") + //log.info(s"Received a direct message: $pkt") sendResponse(pkt) case LoadedRemoteProjectile(projectile_guid, Some(projectile)) =>