Lightweight Locker (#578)

* object registration adjustments for players and avatars and lockers (especially lockers)

* refactored locker container

* modifications to fields and method names for guid-related files; SpecificNumberSource created

* locker item display; proper item insertion into locker-space and searchability of that locker-space

* proper item removal from locker-space, including swap-items on other insertion tasks

* comments and tests; adjusted avatar/player registrations; allowed for restriction in the SpecificNumberSource; renamed CataloguedInventory to LocallyRegisteredInventory, and made internal object registration work

* accommodations for RequestDestroy, to allow the locker's Clear button to work; modification of expectation for resolving projectiles through ValidObject
This commit is contained in:
Fate-JH 2020-09-15 19:46:56 -04:00 committed by GitHub
parent 8245d3ff1e
commit 7626c8e6c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 1743 additions and 948 deletions

View file

@ -213,7 +213,7 @@ object VehicleSpawnPadControlTest {
faction: PlanetSideEmpire.Value
)(implicit system: ActorSystem): (Vehicle, Player, VehicleSpawnPad, Zone) = {
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.vehicles.VehicleControl
import net.psforever.objects.Tool
@ -221,7 +221,7 @@ object VehicleSpawnPadControlTest {
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
val weapon = vehicle.WeaponControlledFromSeat(1).get.asInstanceOf[Tool]
val guid: NumberPoolHub = new NumberPoolHub(LimitedNumberSource(5))
val guid: NumberPoolHub = new NumberPoolHub(MaxNumberSource(5))
guid.AddPool("test-pool", (0 to 5).toList)
guid.register(vehicle, "test-pool")
guid.register(weapon, "test-pool")

View file

@ -17,7 +17,7 @@ import scala.concurrent.duration._
import akka.actor.typed.scaladsl.adapter._
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
class AvatarService1Test extends ActorTest {
"AvatarService" should {
@ -512,7 +512,7 @@ class AvatarReleaseTest extends ActorTest {
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) }
}
val guid1: NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(100))
val guid1: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(100))
zone.GUID(guid1)
val service = system.actorOf(Props(classOf[AvatarService], zone), "release-test-service")
zone.actor = system.spawn(ZoneActor(zone), "release-test-zone")
@ -564,7 +564,7 @@ class AvatarReleaseEarly1Test extends ActorTest {
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) }
}
val guid1: NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(100))
val guid1: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(100))
zone.GUID(guid1)
val service = system.actorOf(Props(classOf[AvatarService], zone), "release-test-service")
zone.actor = system.spawn(ZoneActor(zone), "release-test-zone")
@ -617,7 +617,7 @@ class AvatarReleaseEarly2Test extends ActorTest {
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
override def SetupNumberPools() = { AddPool("dynamic", 1 to 10) }
}
val guid1: NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(100))
val guid1: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(100))
zone.GUID(guid1)
val service = system.actorOf(Props(classOf[AvatarService], zone), "release-test-service")
zone.actor = system.spawn(ZoneActor(zone), "release-test-zone")

View file

@ -8,8 +8,11 @@ import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware}
import akka.pattern.ask
import akka.util.Timeout
import java.util.concurrent.TimeUnit
import MDCContextAware.Implicits._
import net.psforever.objects.locker.LockerContainer
import org.log4s.MDC
import scala.collection.mutable
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
@ -359,13 +362,29 @@ class SessionActor extends Actor with MDCContextAware {
def ValidObject(id: Option[PlanetSideGUID]): Option[PlanetSideGameObject] =
continent.GUID(id) match {
case Some(obj: LocalProjectile) =>
FindProjectileEntry(id.get)
case Some(_: LocalLockerItem) =>
player.avatar.locker.Inventory.hasItem(id.get) match {
case out @ Some(_) =>
out
case None =>
//delete stale entity reference from client
log.warn(s"${player.Name} has an invalid reference to GUID ${id.get} in zone ${continent.id}")
sendResponse(ObjectDeleteMessage(id.get, 0))
None
}
case out @ Some(obj) if obj.HasGUID =>
out
case None if id.nonEmpty && id.get != PlanetSideGUID(0) =>
//delete stale entity reference from client
log.warn(s"Player ${player.Name} has an invalid reference to GUID ${id.get} in zone ${continent.id}.")
log.warn(s"${player.Name} has an invalid reference to GUID ${id.get} in zone ${continent.id}")
sendResponse(ObjectDeleteMessage(id.get, 0))
None
case _ =>
None
}
@ -1893,7 +1912,7 @@ class SessionActor extends Actor with MDCContextAware {
continent.GUID(mount) match {
case Some(obj: Vehicle) =>
TotalDriverVehicleControl(obj)
UnAccessContents(obj)
UnaccessContainer(obj)
case _ => ;
}
PlayerActionsToCancel()
@ -2480,7 +2499,7 @@ class SessionActor extends Actor with MDCContextAware {
// the player will receive no messages consistently except the KeepAliveMessage echo
keepAliveFunc = KeepAlivePersistence
}
AccessContents(obj)
AccessContainer(obj)
UpdateWeaponAtSeatPosition(obj, seat_num)
MountingAction(tplayer, obj, seat_num)
@ -2516,7 +2535,7 @@ class SessionActor extends Actor with MDCContextAware {
DismountAction(tplayer, obj, seat_num)
case Mountable.CanDismount(obj: Vehicle, seat_num) if obj.Definition == GlobalDefinitions.droppod =>
UnAccessContents(obj)
UnaccessContainer(obj)
DismountAction(tplayer, obj, seat_num)
case Mountable.CanDismount(obj: Vehicle, seat_num) =>
@ -2524,7 +2543,7 @@ class SessionActor extends Actor with MDCContextAware {
if (player_guid == player.GUID) {
//disembarking self
TotalDriverVehicleControl(obj)
UnAccessContents(obj)
UnaccessContainer(obj)
DismountAction(tplayer, obj, seat_num)
} else {
continent.VehicleEvents ! VehicleServiceMessage(
@ -2735,7 +2754,7 @@ class SessionActor extends Actor with MDCContextAware {
if (tplayer_guid == guid) {
continent.GUID(vehicle_guid) match {
case Some(obj: Vehicle) =>
UnAccessContents(obj)
UnaccessContainer(obj)
case _ => ;
}
}
@ -3779,9 +3798,7 @@ class SessionActor extends Actor with MDCContextAware {
val guid = player.GUID
sendResponse(UnuseItemMessage(guid, veh.GUID))
sendResponse(UnuseItemMessage(guid, guid))
veh.AccessingTrunk = None
UnAccessContents(veh)
accessedContainer = None
UnaccessContainer(veh)
}
case Some(container) => //just in case
if (isMovingPlus) {
@ -3791,7 +3808,7 @@ class SessionActor extends Actor with MDCContextAware {
sendResponse(UnuseItemMessage(guid, container.GUID))
}
sendResponse(UnuseItemMessage(guid, guid))
accessedContainer = None
UnaccessContainer(container)
}
case None => ;
}
@ -4384,25 +4401,20 @@ class SessionActor extends Actor with MDCContextAware {
case Some(obj: Equipment) =>
FindEquipmentToDelete(object_guid, obj)
case Some(_: LocalProjectile) =>
FindProjectileEntry(object_guid) match {
case Some(projectile) =>
if (projectile.isResolved) {
log.warn(
s"RequestDestroy: tried to clean up projectile ${object_guid.guid} but it was already resolved"
)
} else {
projectile.Miss()
if (projectile.profile.ExistsOnRemoteClients && projectile.HasGUID) {
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ProjectileExplodes(player.GUID, projectile.GUID, projectile)
)
taskResolver ! UnregisterProjectile(projectile)
}
}
case None =>
log.warn(s"RequestDestroy: projectile ${object_guid.guid} has never been fired")
case Some(obj: Projectile) =>
if (obj.isResolved) {
log.warn(
s"RequestDestroy: tried to clean up projectile ${object_guid.guid} but it was already resolved"
)
} else {
obj.Miss()
if (obj.profile.ExistsOnRemoteClients && obj.HasGUID) {
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ProjectileExplodes(player.GUID, obj.GUID, obj)
)
taskResolver ! UnregisterProjectile(obj)
}
}
case Some(obj: BoomerDeployable) =>
@ -4463,7 +4475,7 @@ class SessionActor extends Actor with MDCContextAware {
Some(destination: PlanetSideServerObject with Container),
Some(item: Equipment)
) =>
source.Actor ! Containable.MoveItem(destination, item, dest)
ContainableMoveItem(taskResolver, player.Name, source, destination, item, dest)
case (None, _, _) =>
log.error(s"MoveItem: wanted to move $item_guid from $source_guid, but could not find source object")
case (_, None, _) =>
@ -4500,7 +4512,7 @@ class SessionActor extends Actor with MDCContextAware {
destination.Fit(item)
) match {
case (Some((source, Some(_))), Some(dest)) =>
source.Actor ! Containable.MoveItem(destination, item, dest)
ContainableMoveItem(taskResolver, player.Name, source, destination, item, dest)
case (None, _) =>
log.error(s"LootItem: can not find where $item is put currently")
case (_, None) =>
@ -4610,7 +4622,7 @@ class SessionActor extends Actor with MDCContextAware {
itemType
)
)
accessedContainer = Some(obj)
AccessContainer(obj)
}
} else if (!unk3 && player.isAlive) { //potential kit use
ValidObject(item_used_guid) match {
@ -4759,13 +4771,12 @@ class SessionActor extends Actor with MDCContextAware {
case None if locker.Faction == player.Faction || !locker.HackedBy.isEmpty =>
log.trace(s"UseItem: ${player.Name} accessing a locker")
CancelZoningProcessWithDescriptiveReason("cancel_use")
val container = player.avatar.locker
accessedContainer = Some(container)
val playerLocker = player.avatar.locker
sendResponse(
UseItemMessage(
avatar_guid,
item_used_guid,
container.GUID,
playerLocker.GUID,
unk2,
unk3,
unk4,
@ -4776,6 +4787,7 @@ class SessionActor extends Actor with MDCContextAware {
456
)
)
AccessContainer(playerLocker)
case _ => ;
}
@ -4827,8 +4839,7 @@ class SessionActor extends Actor with MDCContextAware {
) {
CancelZoningProcessWithDescriptiveReason("cancel_use")
obj.AccessingTrunk = player.GUID
accessedContainer = Some(obj)
AccessContents(obj)
AccessContainer(obj)
sendResponse(
UseItemMessage(
avatar_guid,
@ -5044,19 +5055,16 @@ class SessionActor extends Actor with MDCContextAware {
case msg @ UnuseItemMessage(player_guid, object_guid) =>
log.info(s"UnuseItem: $msg")
//TODO check for existing accessedContainer value?
ValidObject(object_guid) match {
case Some(obj: Vehicle) =>
if (obj.AccessingTrunk.contains(player.GUID)) {
obj.AccessingTrunk = None
UnAccessContents(obj)
}
case Some(obj: Player) =>
UnaccessContainer(obj)
TryDisposeOfLootedCorpse(obj)
case Some(obj: Container) =>
UnaccessContainer(obj)
case _ => ;
}
accessedContainer = None
case msg @ DeployObjectMessage(guid, unk1, pos, orient, unk2) =>
log.info(s"DeployObject: $msg")
@ -5757,7 +5765,8 @@ class SessionActor extends Actor with MDCContextAware {
}
/**
* Construct tasking that registers all aspects of a `Player` avatar.
* Construct tasking that registers all aspects of a `Player` avatar
* as if that player is only just being introduced.
* `Players` are complex objects that contain a variety of other register-able objects and each of these objects much be handled.
* @param tplayer the avatar `Player`
* @return a `TaskResolver.GiveTask` message
@ -5785,7 +5794,7 @@ class SessionActor extends Actor with MDCContextAware {
}
override def onFailure(ex: Throwable): Unit = {
localAnnounce ! PlayerFailedToLoad(localPlayer) //alerts WorldSessionActor
localAnnounce ! PlayerFailedToLoad(localPlayer) //alerts SessionActor
}
},
List(GUIDTask.RegisterAvatar(tplayer)(continent.GUID))
@ -5793,7 +5802,8 @@ class SessionActor extends Actor with MDCContextAware {
}
/**
* Construct tasking that registers all aspects of a `Player` avatar.
* Construct tasking that registers all aspects of a `Player` avatar
* as if that player was already introduced and is just being renewed.
* `Players` are complex objects that contain a variety of other register-able objects and each of these objects much be handled.
* @param tplayer the avatar `Player`
* @return a `TaskResolver.GiveTask` message
@ -5872,7 +5882,7 @@ class SessionActor extends Actor with MDCContextAware {
* Additionally, the driver is only partially associated with the vehicle at this time.
* `interstellarFerry` is properly keeping track of the vehicle during the transition
* and the user who is the driver (second param) is properly seated
* but the said driver does not know about the vehicle through his usual convention - VehicleSeated` - yet.
* but the said driver does not know about the vehicle through his usual convention - `VehicleSeated` - yet.
* @see `GlobalDefinitions.droppod`
* @see `GUIDTask.RegisterObjectTask`
* @see `interstellarFerry`
@ -5975,10 +5985,10 @@ class SessionActor extends Actor with MDCContextAware {
}
override def onFailure(ex: Throwable): Unit = {
localAnnounce ! PlayerFailedToLoad(localDriver) //alerts WorldSessionActor
localAnnounce ! PlayerFailedToLoad(localDriver) //alerts SessionActor
}
},
List(GUIDTask.RegisterAvatar(driver)(continent.GUID), GUIDTask.RegisterVehicle(obj)(continent.GUID))
List(RegisterNewAvatar(driver), GUIDTask.RegisterVehicle(obj)(continent.GUID))
)
}
@ -6115,50 +6125,106 @@ class SessionActor extends Actor with MDCContextAware {
)
}
def AccessContainer(container: Container): Unit = {
container match {
case v: Vehicle =>
AccessVehicleContents(v)
case o: LockerContainer =>
AccessGenericContainer(o)
case p: PlanetSideServerObject with Container =>
accessedContainer = Some(p)
case _ => ;
}
}
def AccessGenericContainer(container: PlanetSideServerObject with Container): Unit = {
accessedContainer = Some(container)
DisplayContainerContents(container.GUID, container)
}
/**
* Common preparation for interfacing with a vehicle.
* Join a vehicle-specific group for shared updates.
* Construct every object in the vehicle's inventory for shared manipulation updates.
* @param vehicle the vehicle
*/
def AccessContents(vehicle: Vehicle): Unit = {
AccessContentsChannel(vehicle)
val parent_guid = vehicle.GUID
vehicle.Trunk.Items.foreach(entry => {
val obj = entry.obj
val objDef = obj.Definition
sendResponse(
ObjectCreateDetailedMessage(
objDef.ObjectId,
obj.GUID,
ObjectCreateMessageParent(parent_guid, entry.start),
objDef.Packet.DetailedConstructorData(obj).get
)
)
})
def AccessVehicleContents(vehicle: Vehicle): Unit = {
accessedContainer = Some(vehicle)
if(vehicle.AccessingTrunk.isEmpty) {
vehicle.AccessingTrunk = Some(player.GUID)
}
AccessVehicleContainerChannel(vehicle)
DisplayContainerContents(vehicle.GUID, vehicle)
}
def AccessContentsChannel(container: PlanetSideServerObject): Unit = {
def AccessVehicleContainerChannel(container: PlanetSideServerObject): Unit = {
continent.VehicleEvents ! Service.Join(s"${container.Actor}")
}
def DisplayContainerContents(containerId: PlanetSideGUID, container: Container): Unit = {
container.Inventory.Items.foreach(entry => {
val obj = entry.obj
val objDef = obj.Definition
sendResponse(
ObjectCreateDetailedMessage(
objDef.ObjectId,
obj.GUID,
ObjectCreateMessageParent(containerId, entry.start),
objDef.Packet.DetailedConstructorData(obj).get
)
)
})
}
def UnaccessContainer(): Unit = {
accessedContainer match {
case Some(container) => UnaccessContainer(container)
case _ => ;
}
}
def UnaccessContainer(container: Container): Unit = {
container match {
case v: Vehicle =>
UnaccessVehicleContainer(v)
case o: LockerContainer =>
UnaccessGenericContainer(o)
case _: PlanetSideServerObject with Container =>
accessedContainer = None
case _ => ;
}
}
def UnaccessGenericContainer(container: Container): Unit = {
accessedContainer = None
HideContainerContents(container)
}
/**
* Common preparation for disengaging from a vehicle.
* Leave the vehicle-specific group that was used for shared updates.
* Deconstruct every object in the vehicle's inventory.
* @param vehicle the vehicle
*/
def UnAccessContents(vehicle: Vehicle): Unit = {
continent.VehicleEvents ! Service.Leave(Some(s"${vehicle.Actor}"))
vehicle.Trunk.Items.foreach(entry => {
sendResponse(ObjectDeleteMessage(entry.obj.GUID, 0))
})
def UnaccessVehicleContainer(vehicle: Vehicle): Unit = {
accessedContainer = None
if(vehicle.AccessingTrunk.contains(player.GUID)) {
vehicle.AccessingTrunk = None
}
UnaccessVehicleContainerChannel(vehicle)
HideContainerContents(vehicle)
}
def UnAccessContentsChannel(container: PlanetSideServerObject): Unit = {
def UnaccessVehicleContainerChannel(container: PlanetSideServerObject): Unit = {
continent.VehicleEvents ! Service.Leave(Some(s"${container.Actor}"))
}
def HideContainerContents(container: Container): Unit = {
container.Inventory.Items.foreach(entry => {
sendResponse(ObjectDeleteMessage(entry.obj.GUID, 0))
})
}
/**
* Check two locations for a controlled piece of equipment that is associated with the `player`.<br>
* <br>
@ -6813,19 +6879,7 @@ class SessionActor extends Actor with MDCContextAware {
progressBarUpdate.cancel()
progressBarValue = None
lastTerminalOrderFulfillment = true
accessedContainer match {
case Some(obj: Vehicle) =>
if (obj.AccessingTrunk.contains(player.GUID)) {
obj.AccessingTrunk = None
UnAccessContents(obj)
}
accessedContainer = None
case Some(_) =>
accessedContainer = None
case None => ;
}
UnaccessContainer()
prefire.orElse(shooting) match {
case Some(guid) =>
sendResponse(ChangeFireStateMessage_Stop(guid))
@ -7030,7 +7084,7 @@ class SessionActor extends Actor with MDCContextAware {
* and is permitted to introduce the avatar to the vehicle's internal settings in a similar way.
* Neither the player avatar nor the vehicle should be reconstructed before the next zone load operation
* to avoid damaging the critical setup of this function.
* @see `AccessContents`
* @see `AccessContainer`
* @see `UpdateWeaponAtSeatPosition`
* @param tplayer the player avatar seated in the vehicle's seat
* @param vehicle the vehicle the player is riding
@ -7046,7 +7100,7 @@ class SessionActor extends Actor with MDCContextAware {
sendResponse(ObjectCreateDetailedMessage(pdef.ObjectId, pguid, pdata))
if (seat == 0 || vehicle.Seats(seat).ControlledWeapon.nonEmpty) {
sendResponse(ObjectAttachMessage(vguid, pguid, seat))
AccessContents(vehicle)
AccessContainer(vehicle)
UpdateWeaponAtSeatPosition(vehicle, seat)
} else {
interimUngunnedVehicle = Some(vguid)
@ -7120,7 +7174,7 @@ class SessionActor extends Actor with MDCContextAware {
//log.info(s"AvatarRejoin: $vguid -> $vdata")
if (seat == 0 || vehicle.Seats(seat).ControlledWeapon.nonEmpty) {
sendResponse(ObjectAttachMessage(vguid, pguid, seat))
AccessContents(vehicle)
AccessContainer(vehicle)
UpdateWeaponAtSeatPosition(vehicle, seat)
} else {
interimUngunnedVehicle = Some(vguid)
@ -8203,6 +8257,7 @@ class SessionActor extends Actor with MDCContextAware {
/**
* A simple object searching algorithm that is limited to containers currently known and accessible by the player.
* If all relatively local containers are checked and the object is not found,
* the player's locker inventory will be checked, and then
* the game environment (items on the ground) will be checked too.
* If the target object is discovered, it is removed from its current location and is completely destroyed.
* @see `RequestDestroyMessage`<br>
@ -8218,8 +8273,7 @@ class SessionActor extends Actor with MDCContextAware {
: PlanetSideServerObject with Container => Option[(PlanetSideServerObject with Container, Option[Int])] =
FindInLocalContainer(object_guid)
findFunc(player.avatar.locker)
.orElse(findFunc(player))
findFunc(player)
.orElse(accessedContainer match {
case Some(parent: PlanetSideServerObject) =>
findFunc(parent)
@ -8239,7 +8293,11 @@ class SessionActor extends Actor with MDCContextAware {
true
case _ =>
if (continent.EquipmentOnGround.contains(obj)) {
if (player.avatar.locker.Inventory.Remove(object_guid)) {
sendResponse(ObjectDeleteMessage(object_guid, 0))
log.info(s"RequestDestroy: equipment $obj")
true
} else if (continent.EquipmentOnGround.contains(obj)) {
obj.Position = Vector3.Zero
continent.Ground ! Zone.Ground.RemoveItem(object_guid)
continent.AvatarEvents ! AvatarServiceMessage.Ground(RemoverActor.ClearSpecific(List(obj), continent))
@ -8440,7 +8498,7 @@ class SessionActor extends Actor with MDCContextAware {
if (player.isBackpack) {
session = session.copy(player = targetPlayer)
taskThenZoneChange(
GUIDTask.UnregisterLocker(original.avatar.locker)(continent.GUID),
GUIDTask.UnregisterObjectTask(original.avatar.locker)(continent.GUID),
InterstellarClusterService.FindZone(_.id == zoneId, context.self)
)
} else if (player.HasGUID) {
@ -8545,12 +8603,12 @@ class SessionActor extends Actor with MDCContextAware {
InterstellarClusterService.FindZone(_.id == zoneId, context.self)
)
} else {
UnAccessContents(vehicle)
UnaccessContainer(vehicle)
LoadZoneCommonTransferActivity()
player.VehicleSeated = vehicle.GUID
player.Continent = zoneId //forward-set the continent id to perform a test
interstellarFerryTopLevelGUID =
(if (
if (
manifest.passengers.isEmpty && manifest.cargo.count { case (name, _) => !name.equals("MISSING_DRIVER") } == 0
) {
//do not delete if vehicle has passengers or cargo
@ -8561,7 +8619,7 @@ class SessionActor extends Actor with MDCContextAware {
None
} else {
Some(topLevel)
})
}
//unregister vehicle and driver whole + GiveWorld
continent.Transport ! Zone.Vehicle.Despawn(vehicle)
taskThenZoneChange(
@ -8586,12 +8644,12 @@ class SessionActor extends Actor with MDCContextAware {
* A reference to the top-level ferrying vehicle's former globally unique identifier has been retained for this purpose.
* This vehicle can be deleted for everyone if no more work can be detected.
*
* @see `GUIDTask.UnregisterAvatar`
* @see `GUIDTask.UnregisterPlayer`
* @see `LoadZoneCommonTransferActivity`
* @see `Vehicles.AllGatedOccupantsInSameZone`
* @see `PlayerLoaded`
* @see `TaskBeforeZoneChange`
* @see `UnAccessContents`
* @see `UnaccessContainer`
* @param vehicle the target vehicle being moved around
* @param zoneId the zone in which the vehicle and driver will be placed
* @return a tuple composed of an `ActorRef` destination and a message to send to that destination
@ -9209,7 +9267,7 @@ class SessionActor extends Actor with MDCContextAware {
}
/**
* The upstream counter accumulates when the server receives sp[ecific messages from the client.
* The upstream counter accumulates when the server receives specific messages from the client.
* It counts upwards until it reach maximum value, and then starts over.
* When it starts over, which should take an exceptionally long time to achieve,
* it starts counting at one rather than zero.
@ -9265,7 +9323,7 @@ class SessionActor extends Actor with MDCContextAware {
case (Some(vehicle: Vehicle), Some(vguid), Some(seat)) =>
//sit down
sendResponse(ObjectAttachMessage(vguid, pguid, seat))
AccessContents(vehicle)
AccessContainer(vehicle)
keepAliveFunc = KeepAlivePersistence
case _ => ;
//we can't find a vehicle? and we're still here? that's bad

View file

@ -6,6 +6,7 @@ import akka.util.Timeout
import net.psforever.objects.equipment.{Ammo, Equipment}
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{Container, InventoryItem}
import net.psforever.objects.locker.LockerContainer
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.zones.Zone
@ -504,6 +505,215 @@ object WorldSession {
result
}
/**
* Move an item from one container to another.
* If the source or if the destination is a kind of container called a `LockerContainer`,
* then a special procedure for the movement of the item must be respected.
* If the source and the destination are both `LockerContainer` objects, however,
* the normal operations for moving an item may be executed.
* @see `ActorRef`
* @see `Containable.MoveItem`
* @see `Container`
* @see `Equipment`
* @see `LockerContainer`
* @see `RemoveEquipmentFromLockerContainer`
* @see `StowEquipmentInLockerContainer`
* @see `TaskResolver`
* @param taskResolver na
* @param toChannel broadcast channel name for a manual packet callback
* @param source the container in which the item is to be removed
* @param destination the container into which the item is to be placed
* @param item the item
* @param dest where in the destination container the item is being placed
*/
def ContainableMoveItem(
taskResolver: ActorRef,
toChannel: String,
source: PlanetSideServerObject with Container,
destination: PlanetSideServerObject with Container,
item: Equipment,
dest: Int
) : Unit = {
(source, destination) match {
case (locker: LockerContainer, _) if !destination.isInstanceOf[LockerContainer] =>
RemoveEquipmentFromLockerContainer(taskResolver, toChannel, locker, destination, item, dest)
case (_, locker: LockerContainer) =>
StowEquipmentInLockerContainer(taskResolver, toChannel, source, locker, item, dest)
case _ =>
source.Actor ! Containable.MoveItem(destination, item, dest)
}
}
/**
* Move an item into a player's locker inventory.
* Handle any swap item that might become involved in the transfer.
* Failure of this process is not supported and may lead to irregular behavior.
* @see `ActorRef`
* @see `AvatarAction.ObjectDelete`
* @see `AvatarServiceMessage`
* @see `Containable.MoveItem`
* @see `Container`
* @see `Equipment`
* @see `GridInventory.CheckCollisionsVar`
* @see `GUIDTask.RegisterEquipment`
* @see `GUIDTask.UnregisterEquipment`
* @see `IdentifiableEntity.Invalidate`
* @see `LockerContainer`
* @see `Service`
* @see `Task`
* @see `TaskResolver`
* @see `TaskResolver.GiveTask`
* @see `Zone.AvatarEvents`
* @param taskResolver na
* @param toChannel broadcast channel name for a manual packet callback
* @param source the container in which the item is to be removed
* @param destination the container into which the item is to be placed
* @param item the item
* @param dest where in the destination container the item is being placed
*/
def StowEquipmentInLockerContainer(
taskResolver: ActorRef,
toChannel: String,
source: PlanetSideServerObject with Container,
destination: PlanetSideServerObject with Container,
item: Equipment,
dest: Int
): Unit = {
val registrationTask = GUIDTask.UnregisterEquipment(item)(source.Zone.GUID)
//check for the existence of a swap item - account for that in advance
val (subtasks, swapItemGUID): (List[TaskResolver.GiveTask], Option[PlanetSideGUID]) = {
val tile = item.Definition.Tile
destination.Inventory.CheckCollisionsVar(dest, tile.Width, tile.Height)
} match {
case Success(Nil) =>
//no swap item
(List(registrationTask), None)
case Success(List(swapEntry: InventoryItem)) =>
//the swap item is to be registered to the source's zone
/*
destination is a locker container that has its own internal unique number system
the swap item is currently registered to this system
the swap item will be moved into the system in which the source operates
to facilitate the transfer, the item needs to be partially unregistered from the destination's system
to facilitate the transfer, the item needs to be preemptively registered to the source's system
invalidating the current unique number is sufficient for both of these steps
*/
val swapItem = swapEntry.obj
swapItem.Invalidate()
(List(GUIDTask.RegisterEquipment(swapItem)(source.Zone.GUID), registrationTask), Some(swapItem.GUID))
case _ =>
//too many swap items or other error; this attempt will probably fail
(Nil, None)
}
taskResolver ! TaskResolver.GiveTask(
new Task() {
val localGUID = swapItemGUID //the swap item's original GUID, if any swap item
val localChannel = toChannel
val localSource = source
val localDestination = destination
val localItem = item
val localSlot = dest
override def Description: String = s"unregistering $localItem before stowing in $localDestination"
override def isComplete: Task.Resolution.Value = {
if (localItem.HasGUID && localDestination.Find(localItem).contains(localSlot)) {
Task.Resolution.Success
} else {
Task.Resolution.Incomplete
}
}
def Execute(resolver: ActorRef): Unit = {
localGUID match {
case Some(guid) =>
//see LockerContainerControl.RemoveItemFromSlotCallback
val zone = localSource.Zone
zone.AvatarEvents ! AvatarServiceMessage(localChannel, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, guid))
case None => ;
}
localSource.Actor ! Containable.MoveItem(localDestination, localItem, localSlot)
resolver ! Success(this)
}
},
subtasks
)
}
/**
* Remove an item from a player's locker inventory.
* Failure of this process is not supported and may lead to irregular behavior.
* @see `ActorRef`
* @see `AvatarAction.ObjectDelete`
* @see `AvatarServiceMessage`
* @see `Containable.MoveItem`
* @see `Container`
* @see `Equipment`
* @see `GridInventory.CheckCollisionsVar`
* @see `GUIDTask.RegisterEquipment`
* @see `GUIDTask.UnregisterEquipment`
* @see `IdentifiableEntity.Invalidate`
* @see `LockerContainer`
* @see `Service`
* @see `Task`
* @see `TaskResolver`
* @see `TaskResolver.GiveTask`
* @see `Zone.AvatarEvents`
* @param taskResolver na
* @param toChannel broadcast channel name for a manual packet callback
* @param source the container in which the item is to be removed
* @param destination the container into which the item is to be placed
* @param item the item
* @param dest where in the destination container the item is being placed
*/
def RemoveEquipmentFromLockerContainer(
taskResolver: ActorRef,
toChannel: String,
source: PlanetSideServerObject with Container,
destination: PlanetSideServerObject with Container,
item: Equipment,
dest: Int
): Unit = {
taskResolver ! TaskResolver.GiveTask(
new Task() {
val localGUID = item.GUID //original GUID
val localChannel = toChannel
val localSource = source
val localDestination = destination
val localItem = item
val localSlot = dest
/*
source is a locker container that has its own internal unique number system
the item is currently registered to this system
the item will be moved into the system in which the destination operates
to facilitate the transfer, the item needs to be partially unregistered from the source's system
to facilitate the transfer, the item needs to be preemptively registered to the destination's system
invalidating the current unique number is sufficient for both of these steps
*/
localItem.Invalidate()
override def Description: String = s"registering $localItem in ${localDestination.Zone.id} before removing from $localSource"
override def isComplete: Task.Resolution.Value = {
if (localItem.HasGUID && localDestination.Find(localItem).isEmpty) {
Task.Resolution.Success
} else {
Task.Resolution.Incomplete
}
}
def Execute(resolver: ActorRef): Unit = {
val zone = localSource.Zone
//see LockerContainerControl.RemoveItemFromSlotCallback
zone.AvatarEvents ! AvatarServiceMessage(localChannel, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, localGUID))
localSource.Actor ! Containable.MoveItem(localDestination, localItem, localSlot)
resolver ! Success(this)
}
},
List(GUIDTask.RegisterEquipment(item)(destination.Zone.GUID))
)
}
/**
* If a timeout occurs on the manipulation, declare a terminal transaction failure.
* @see `AskTimeoutException`

View file

@ -0,0 +1,37 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects
import akka.actor.ActorContext
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.types.PlanetSideEmpire
/**
* A `LocalLockerItem` is a server-side object designed to populate a fake unshared space within a shared space.
* It is a placeholder intended to block out the existence of locker objects that may or may not exist.
* All clients reserve the same internal range of user-generated GUID's from 40150 to 40449, inclusive.
* All clients recognize this same range independent of each other as "equipment in their own locker."
* The GUID's in this locker-space can be reflected onto the zone GUID.
* The item of that GUID may exist to the client.
* The item of that GUID does not formally exist to the server, but it can be searched.
* @see `Zone.MakeReservedObjects`
*/
class LocalLockerItem extends PlanetSideServerObject {
def Faction = PlanetSideEmpire.NEUTRAL
def Definition = LocalLockerItem.local
}
object LocalLockerItem {
import net.psforever.objects.definition.ObjectDefinition
def local = new ObjectDefinition(0) { Name = "locker-equipment" }
/**
* Instantiate and configure a `LocalProjectile` object.
* @param id the unique id that will be assigned to this entity
* @param context a context to allow the object to properly set up `ActorSystem` functionality
* @return the `LocalProjectile` object
*/
def Constructor(id: Int, context: ActorContext): LocalLockerItem = {
new LocalLockerItem()
}
}

View file

@ -2,8 +2,10 @@ package net.psforever.objects.avatar
import net.psforever.objects.definition.{AvatarDefinition, BasicDefinition}
import net.psforever.objects.equipment.{EquipmentSize, EquipmentSlot}
import net.psforever.objects.inventory.LocallyRegisteredInventory
import net.psforever.objects.loadouts.{Loadout, SquadLoadout}
import net.psforever.objects.{GlobalDefinitions, LockerContainer, LockerEquipment, OffhandEquipmentSlot}
import net.psforever.objects.locker.{LockerContainer, LockerEquipment}
import net.psforever.objects.{GlobalDefinitions, OffhandEquipmentSlot}
import net.psforever.types._
import org.joda.time.{Duration, LocalDateTime, Seconds}
@ -82,7 +84,11 @@ case class Avatar(
loadouts: Seq[Option[Loadout]] = Seq.fill(15)(None),
squadLoadouts: Seq[Option[SquadLoadout]] = Seq.fill(10)(None),
implants: Seq[Option[Implant]] = Seq(None, None, None),
locker: LockerContainer = new LockerContainer(), // TODO var bad
locker: LockerContainer = new LockerContainer({
val inv = new LocallyRegisteredInventory(numbers = 40150 until 40450) // TODO var bad
inv.Resize(30,20)
inv
}),
deployables: DeployableToolbox = new DeployableToolbox(), // TODO var bad
lookingForSquad: Boolean = false,
var vehicle: Option[PlanetSideGUID] = None, // TODO var bad

View file

@ -27,6 +27,8 @@ import net.psforever.services.{RemoverActor, Service}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import akka.actor.typed
import net.psforever.objects.locker.LockerContainerControl
import scala.concurrent.duration._
class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Command])

View file

@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.Player
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
import net.psforever.packet.game.objectcreate._
import net.psforever.types.{ExoSuitType, GrenadeState, PlanetSideGUID}
import net.psforever.types.{ExoSuitType, GrenadeState, PlanetSideEmpire, PlanetSideGUID}
import scala.annotation.tailrec
import scala.util.{Success, Try}
@ -175,7 +175,9 @@ object AvatarConverter {
def MakeDetailedInventoryData(obj: Player): InventoryData = {
InventoryData(
(MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)
(MakeHolsters(obj, BuildDetailedEquipment) ++
MakeFifthSlot(obj) ++
MakeInventory(obj)).sortBy(_.parentSlot)
)
}
@ -222,7 +224,16 @@ object AvatarConverter {
private def MakeFifthSlot(obj: Player): List[InternalSlot] = {
obj.Slot(5).Equipment match {
case Some(equip) =>
BuildDetailedEquipment(5, equip) :: Nil
//List(BuildDetailedEquipment(5, equip))
List(InternalSlot(
equip.Definition.ObjectId,
equip.GUID,
5,
DetailedLockerContainerData(
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
None
)
))
case _ =>
Nil
}

View file

@ -1,9 +1,9 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.definition.converter
import net.psforever.objects.LockerEquipment
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.GridInventory
import net.psforever.objects.locker.LockerEquipment
import net.psforever.packet.game.objectcreate._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}

View file

@ -6,6 +6,7 @@ import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
import net.psforever.objects._
import net.psforever.objects.inventory.Container
import net.psforever.objects.locker.{LockerContainer, LockerEquipment}
import net.psforever.objects.serverobject.turret.WeaponTurret
import scala.annotation.tailrec
@ -153,7 +154,7 @@ object GUIDTask {
*/
def RegisterAvatar(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
val lockerTask = List(RegisterLocker(tplayer.avatar.locker))
val lockerTask = List(RegisterObjectTask(tplayer.avatar.locker))
val inventoryTasks = RegisterInventory(tplayer)
TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
}
@ -311,7 +312,7 @@ object GUIDTask {
*/
def UnregisterAvatar(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
val lockerTask = List(UnregisterLocker(tplayer.avatar.locker))
val lockerTask = List(UnregisterObjectTask(tplayer.avatar.locker))
val inventoryTasks = UnregisterInventory(tplayer)
TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
}
@ -368,7 +369,7 @@ object GUIDTask {
* @param guid implicit reference to a unique number system
* @return a list of `TaskResolver.GiveTask` messages
*/
def VisibleSlotTaskBuilding(list: Iterable[EquipmentSlot], func: ((Equipment) => TaskResolver.GiveTask))(implicit
def VisibleSlotTaskBuilding(list: Iterable[EquipmentSlot], func: Equipment => TaskResolver.GiveTask)(implicit
guid: ActorRef
): List[TaskResolver.GiveTask] = {
recursiveVisibleSlotTaskBuilding(list.iterator, func)
@ -386,7 +387,7 @@ object GUIDTask {
*/
@tailrec private def recursiveVisibleSlotTaskBuilding(
iter: Iterator[EquipmentSlot],
func: ((Equipment) => TaskResolver.GiveTask),
func: Equipment => TaskResolver.GiveTask,
list: List[TaskResolver.GiveTask] = Nil
)(implicit guid: ActorRef): List[TaskResolver.GiveTask] = {
if (!iter.hasNext) {

View file

@ -24,8 +24,8 @@ class NumberPoolHub(private val source: NumberSource) {
import scala.collection.mutable
private val hash: mutable.HashMap[String, NumberPool] = mutable.HashMap[String, NumberPool]()
private val bigpool: mutable.LongMap[String] = mutable.LongMap[String]()
hash += "generic" -> new GenericPool(bigpool, source.Size)
source.FinalizeRestrictions.foreach(i =>
hash += "generic" -> new GenericPool(bigpool, source.size)
source.finalizeRestrictions.foreach(i =>
bigpool += i.toLong -> ""
) //these numbers can never be pooled; the source can no longer restrict numbers
@ -47,7 +47,7 @@ class NumberPoolHub(private val source: NumberSource) {
* @param number the unique number to attempt to retrieve from the `source`
* @return the object that is assigned to the number
*/
def apply(number: Int): Option[IdentifiableEntity] = source.Get(number).orElse(return None).get.Object
def apply(number: Int): Option[IdentifiableEntity] = source.get(number).orElse(return None).get.Object
def Numbers: List[Int] = bigpool.keys.map(key => key.toInt).toList
@ -74,14 +74,15 @@ class NumberPoolHub(private val source: NumberSource) {
if (pool.isEmpty) {
throw new IllegalArgumentException(s"can not add empty pool $name")
}
if (source.Size <= pool.max) {
throw new IllegalArgumentException(s"can not add pool $name - max(pool) is greater than source.size")
if (source.max < pool.max) {
throw new IllegalArgumentException(s"can not add pool $name - pool.max is greater than source.max")
}
val collision = bigpool.keys.map(n => n.toInt).toSet.intersect(pool.toSet)
if (collision.nonEmpty) {
throw new IllegalArgumentException(
s"can not add pool $name - it contains the following redundant numbers: ${collision.toString}"
)
bigpool.keys.map(n => n.toInt).toSet.intersect(pool.toSet).toSeq match {
case Nil => ;
case collisions =>
throw new IllegalArgumentException(
s"can not add pool $name - it contains the following redundant numbers: ${collisions.mkString(",")}"
)
}
pool.foreach(i => bigpool += i.toLong -> name)
hash += name -> new ExclusivePool(pool)
@ -162,7 +163,7 @@ class NumberPoolHub(private val source: NumberSource) {
def WhichPool(obj: IdentifiableEntity): Option[String] = {
try {
val number: Int = obj.GUID.guid
val entry = source.Get(number)
val entry = source.get(number)
if (entry.isDefined && entry.get.Object.contains(obj)) { WhichPool(number) }
else { None }
} catch {
@ -228,7 +229,7 @@ class NumberPoolHub(private val source: NumberSource) {
}
private def register_GetAvailableNumberFromSource(number: Int): Try[LoanedKey] = {
source.Available(number) match {
source.getAvailable(number) match {
case Some(key) =>
Success(key)
case None =>
@ -243,22 +244,21 @@ class NumberPoolHub(private val source: NumberSource) {
* @return the number the was given to the object
*/
def register(obj: IdentifiableEntity, name: String): Try[Int] = {
try {
if (obj.HasGUID) {
register_CheckNumberAgainstDesiredPool(obj, name, obj.GUID.guid)
} catch {
case _: Exception =>
register_GetPool(name) match {
case Success(key) =>
key.Object = obj
Success(obj.GUID.guid)
case Failure(ex) =>
Failure(new Exception(s"trying to register an object but, ${ex.getMessage}"))
}
} else {
register_GetPool(name) match {
case Success(key) =>
key.Object = obj
Success(obj.GUID.guid)
case Failure(ex) =>
Failure(new Exception(s"trying to register an object but, ${ex.getMessage}"))
}
}
}
private def register_CheckNumberAgainstDesiredPool(obj: IdentifiableEntity, name: String, number: Int): Try[Int] = {
val directKey = source.Get(number)
val directKey = source.get(number)
if (directKey.isEmpty || !directKey.get.Object.contains(obj)) {
Failure(new Exception("object already registered, but not to this source"))
} else if (!WhichPool(number).contains(name)) {
@ -347,11 +347,34 @@ class NumberPoolHub(private val source: NumberSource) {
case Success(pool) =>
val number = obj.GUID.guid
pool.Return(number)
source.Return(number)
source.returnNumber(number)
obj.Invalidate()
Success(number)
case Failure(ex) =>
Failure(new Exception(s"can not unregister this object: ${ex.getMessage}"))
unregister_GetMonitorFromObject(obj, ex.getMessage)
}
}
/**
* Unregister a specific object
* by actually finding the object itself, if it exists.
* @param obj an object being unregistered
* @param msg custom error message;
* has a vague default
* @return the number associated with this object
*/
def unregister_GetMonitorFromObject(
obj: IdentifiableEntity,
msg: String = "can not find this object"
): Try[Int] = {
source.get(obj) match {
case Some(key) =>
val number = key.GUID
GetPool(WhichPool(number).get).get.Return(number)
source.returnNumber(number)
Success(number)
case _ =>
Failure(new Exception(s"can not unregister this $obj - $msg"))
}
}
@ -360,7 +383,7 @@ class NumberPoolHub(private val source: NumberSource) {
case Some(name) =>
unregister_GetPool(name)
case None =>
Failure(throw new Exception("can not find a pool for this object"))
Failure(new Exception(s"can not find a pool for this $obj"))
}
}
@ -379,7 +402,7 @@ class NumberPoolHub(private val source: NumberSource) {
* @return the object, if any, previous associated with the number
*/
def unregister(number: Int): Try[Option[IdentifiableEntity]] = {
if (source.Test(number)) {
if (source.test(number)) {
unregister_GetObjectFromSource(number)
} else {
Failure(new Exception(s"can not unregister a number $number that this source does not own"))
@ -387,7 +410,7 @@ class NumberPoolHub(private val source: NumberSource) {
}
private def unregister_GetObjectFromSource(number: Int): Try[Option[IdentifiableEntity]] = {
source.Return(number) match {
source.returnNumber(number) match {
case Some(obj) =>
unregister_ReturnObjectToPool(obj)
case None =>
@ -403,7 +426,7 @@ class NumberPoolHub(private val source: NumberSource) {
obj.Invalidate()
Success(Some(obj))
case Failure(ex) =>
source.Available(number) //undo
source.getAvailable(number) //undo
Failure(new Exception(s"started unregistering, but ${ex.getMessage}"))
}
}
@ -432,11 +455,11 @@ class NumberPoolHub(private val source: NumberSource) {
}
/**
* For accessing the `Return` function of the contained `NumberSource` directly.
* For accessing the `returnNumber` function of the contained `NumberSource` directly.
* @param number the number to return.
* @return any object previously using this number
*/
def latterPartUnregister(number: Int): Option[IdentifiableEntity] = source.Return(number)
def latterPartUnregister(number: Int): Option[IdentifiableEntity] = source.returnNumber(number)
/**
* Determines if the object is registered.<br>
@ -451,7 +474,7 @@ class NumberPoolHub(private val source: NumberSource) {
*/
def isRegistered(obj: IdentifiableEntity): Boolean = {
try {
source.Get(obj.GUID.guid) match {
source.get(obj.GUID.guid) match {
case Some(monitor) =>
monitor.Object.contains(obj)
case None =>
@ -474,7 +497,7 @@ class NumberPoolHub(private val source: NumberSource) {
* @see `isRegistered(IdentifiableEntity)`
*/
def isRegistered(number: Int): Boolean = {
source.Get(number) match {
source.get(number) match {
case Some(monitor) =>
monitor.Policy == AvailabilityPolicy.Leased
case None =>

View file

@ -73,7 +73,7 @@ object NumberPoolActor {
final case class NoNumber(ex: Throwable, id: Option[Any] = None)
/**
* A message to invoke the `Return` functionality of the current `NumberSelector`.
* A message to invoke the `returnNumber` functionality of the current `NumberSelector`.
* @param number the number
*/
final case class ReturnNumber(number: Int, id: Option[Any] = None)

View file

@ -12,9 +12,9 @@ import net.psforever.objects.guid.AvailabilityPolicy
class LoanedKey(private val guid: Int, private val key: Monitor) {
def GUID: Int = guid
def Policy: AvailabilityPolicy.Value = key.Policy
def Policy: AvailabilityPolicy.Value = key.policy
def Object: Option[IdentifiableEntity] = key.Object
def Object: Option[IdentifiableEntity] = key.obj
/**
* na
@ -30,18 +30,18 @@ class LoanedKey(private val guid: Int, private val key: Monitor) {
*/
def Object_=(obj: Option[IdentifiableEntity]): Option[IdentifiableEntity] = {
if (
key.Policy == AvailabilityPolicy.Leased || (key.Policy == AvailabilityPolicy.Restricted && key.Object.isEmpty)
key.policy == AvailabilityPolicy.Leased || (key.policy == AvailabilityPolicy.Restricted && key.obj.isEmpty)
) {
if (key.Object.isDefined) {
key.Object.get.Invalidate()
key.Object = None
if (key.obj.isDefined) {
key.obj.get.Invalidate()
key.obj = None
}
key.Object = obj
key.obj = obj
if (obj.isDefined) {
import net.psforever.types.PlanetSideGUID
obj.get.GUID = PlanetSideGUID(guid)
}
}
key.Object
key.obj
}
}

View file

@ -5,9 +5,7 @@ import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.AvailabilityPolicy
trait Monitor {
def Policy: AvailabilityPolicy.Value
var policy: AvailabilityPolicy.Value
def Object: Option[IdentifiableEntity]
def Object_=(objct: Option[IdentifiableEntity]): Option[IdentifiableEntity]
var obj: Option[IdentifiableEntity]
}

View file

@ -11,8 +11,8 @@ import net.psforever.objects.guid.AvailabilityPolicy
final class SecureKey(private val guid: Int, private val key: Monitor) {
def GUID: Int = guid
def Policy: AvailabilityPolicy.Value = key.Policy
def Policy: AvailabilityPolicy.Value = key.policy
import net.psforever.objects.entity.IdentifiableEntity
def Object: Option[IdentifiableEntity] = key.Object
def Object: Option[IdentifiableEntity] = key.obj
}

View file

@ -46,7 +46,7 @@ abstract class NumberSelector {
* By default, a simple policy for returning numbers has been provided.
* This will not be sufficient for all selection actions that can be implemented so `override` where necessary.
* <br>
* `Return` is under no obligation to leave its parameter `Array` unmodified.
* `returnNumber` is under no obligation to leave its parameter `Array` unmodified.
* In fact, it should modify it by default to provide additional feedback of its process.
* Pass a copy if data mutation is a concern.
* @param number the number to be returned

View file

@ -6,20 +6,7 @@ import net.psforever.objects.guid.AvailabilityPolicy
import net.psforever.objects.guid.key.Monitor
private class Key extends Monitor {
private var policy: AvailabilityPolicy.Value = AvailabilityPolicy.Available
private var obj: Option[IdentifiableEntity] = None
var policy: AvailabilityPolicy.Value = AvailabilityPolicy.Available
def Policy: AvailabilityPolicy.Value = policy
def Policy_=(pol: AvailabilityPolicy.Value): AvailabilityPolicy.Value = {
policy = pol
Policy
}
def Object: Option[IdentifiableEntity] = obj
def Object_=(objct: Option[IdentifiableEntity]): Option[IdentifiableEntity] = {
obj = objct
Object
}
var obj: Option[IdentifiableEntity] = None
}

View file

@ -1,111 +0,0 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.guid.source
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
import net.psforever.objects.guid.AvailabilityPolicy
import scala.collection.mutable
/**
* A `NumberSource` is considered a master "pool" of numbers from which all numbers are available to be drawn.
* The numbers are considered to be exclusive.<br>
* <br>
* Produce a series of numbers from 0 to a maximum number (inclusive) to be used as globally unique identifiers (GUIDs).
* @param max the highest number to be generated by this source;
* must be a positive integer or zero
* @throws IllegalArgumentException if `max` is less than zero (therefore the count of generated numbers is at most zero)
* @throws java.lang.NegativeArraySizeException if the count of numbers generated due to max is negative
*/
class LimitedNumberSource(max: Int) extends NumberSource {
if (max < 0) {
throw new IllegalArgumentException(s"non-negative integers only, not $max")
}
private val ary: Array[Key] = Array.ofDim[Key](max + 1)
(0 to max).foreach(x => { ary(x) = new Key })
private var allowRestrictions: Boolean = true
def Size: Int = ary.length
def CountAvailable: Int = ary.count(key => key.Policy == AvailabilityPolicy.Available)
def CountUsed: Int = ary.count(key => key.Policy != AvailabilityPolicy.Available)
def Get(number: Int): Option[SecureKey] = {
if (Test(number)) {
Some(new SecureKey(number, ary(number)))
} else {
None
}
}
def Available(number: Int): Option[LoanedKey] = {
var out: Option[LoanedKey] = None
if (Test(number)) {
val key: Key = ary(number)
if (key.Policy == AvailabilityPolicy.Available) {
key.Policy = AvailabilityPolicy.Leased
out = Some(new LoanedKey(number, key))
}
}
out
}
/**
* Consume the number of a `Monitor` and release that number from its previous assignment/use.
* @param number the number
* @return any object previously using this number
*/
def Return(number: Int): Option[IdentifiableEntity] = {
var out: Option[IdentifiableEntity] = None
if (Test(number)) {
val existing: Key = ary(number)
if (existing.Policy == AvailabilityPolicy.Leased) {
out = existing.Object
existing.Policy = AvailabilityPolicy.Available
existing.Object = None
}
}
out
}
/**
* Produce a modifiable wrapper for the `Monitor` for this number, only if the number has not been used.
* This wrapped `Monitor` can only be assigned once and the number may not be `Return`ed to this source.
* @param number the number
* @return the wrapped `Monitor`
* @throws ArrayIndexOutOfBoundsException if the requested number is above or below the range
*/
def Restrict(number: Int): Option[LoanedKey] = {
if (allowRestrictions && Test(number)) {
val key: Key = ary(number)
key.Policy = AvailabilityPolicy.Restricted
Some(new LoanedKey(number, key))
} else {
None
}
}
def FinalizeRestrictions: List[Int] = {
allowRestrictions = false
ary.zipWithIndex.filter(entry => entry._1.Policy == AvailabilityPolicy.Restricted).map(entry => entry._2).toList
}
def Clear(): List[IdentifiableEntity] = {
val outList: mutable.ListBuffer[IdentifiableEntity] = mutable.ListBuffer[IdentifiableEntity]()
for (x <- ary.indices) {
ary(x).Policy = AvailabilityPolicy.Available
if (ary(x).Object.isDefined) {
outList += ary(x).Object.get
ary(x).Object = None
}
}
outList.toList
}
}
object LimitedNumberSource {
def apply(max: Int): LimitedNumberSource = {
new LimitedNumberSource(max)
}
}

View file

@ -0,0 +1,121 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.guid.source
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
import net.psforever.objects.guid.AvailabilityPolicy
/**
* A `NumberSource` is considered a master "pool" of numbers from which all numbers are available to be drawn.
* Produce a series of numbers from 0 to a maximum number (inclusive) to be used as globally unique identifiers (GUID's).
* @param max the highest number to be generated by this source;
* must be a positive integer or zero
* @throws IllegalArgumentException if `max` is less than zero (therefore the count of generated numbers is at most zero)
*/
class MaxNumberSource(val max: Int) extends NumberSource {
if (max < 0) {
throw new IllegalArgumentException(s"non-negative integers only, not $max")
}
private val ary: Array[Key] = Array.ofDim[Key](max + 1)
(0 to max).foreach(x => { ary(x) = new Key })
private var allowRestrictions: Boolean = true
def size: Int = ary.length
def countAvailable: Int = ary.count(key => key.policy == AvailabilityPolicy.Available)
def countUsed: Int = ary.count(_.policy != AvailabilityPolicy.Available)
def test(number: Int): Boolean = -1 < number && number < size
def get(number: Int): Option[SecureKey] = {
if (test(number)) {
Some(new SecureKey(number, ary(number)))
} else {
None
}
}
def get(obj: IdentifiableEntity) : Option[SecureKey] = {
ary.zipWithIndex.find { case (key, _) =>
key.obj match {
case Some(o) => o eq obj
case _ => false
}
} match {
case Some((key, number)) => Some(new SecureKey(number, key))
case _=> None
}
}
def getAvailable(number: Int): Option[LoanedKey] = {
var out: Option[LoanedKey] = None
if (test(number)) {
val key: Key = ary(number)
if (key.policy == AvailabilityPolicy.Available) {
key.policy = AvailabilityPolicy.Leased
out = Some(new LoanedKey(number, key))
}
}
out
}
/**
* Consume the number of a `Monitor` and release that number from its previous assignment/use.
* @param number the number
* @return any object previously using this number
*/
def returnNumber(number: Int): Option[IdentifiableEntity] = {
var out: Option[IdentifiableEntity] = None
if (test(number)) {
val existing: Key = ary(number)
if (existing.policy == AvailabilityPolicy.Leased) {
out = existing.obj
existing.policy = AvailabilityPolicy.Available
existing.obj = None
}
}
out
}
/**
* Produce a modifiable wrapper for the `Monitor` for this number, only if the number has not been used.
* This wrapped `Monitor` can only be assigned once and the number may not be `returnNumber`ed to this source.
* @param number the number
* @return the wrapped `Monitor`
* @throws ArrayIndexOutOfBoundsException if the requested number is above or below the range
*/
def restrictNumber(number: Int): Option[LoanedKey] = {
ary.lift(number) match {
case Some(key: Key) if allowRestrictions && key.policy != AvailabilityPolicy.Restricted =>
key.policy = AvailabilityPolicy.Restricted
Some(new LoanedKey(number, key))
case None =>
None
}
}
def finalizeRestrictions: List[Int] = {
allowRestrictions = false
ary.zipWithIndex.filter(entry => entry._1.policy == AvailabilityPolicy.Restricted).map(entry => entry._2).toList
}
def clear(): List[IdentifiableEntity] = {
val leased = ary.filter(_.policy != AvailabilityPolicy.Available)
leased collect { case key if key.obj.isEmpty =>
key.policy = AvailabilityPolicy.Available
}
leased.toList collect { case key if key.obj.nonEmpty =>
key.policy = AvailabilityPolicy.Available
val out = key.obj.get
key.obj = None
out
}
}
}
object MaxNumberSource {
def apply(max: Int): MaxNumberSource = {
new MaxNumberSource(max)
}
}

View file

@ -18,42 +18,51 @@ import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
* The purpose of a `NumberSource` is to help facilitate globally unique identifiers (GUID, pl. GUIDs).
*/
trait NumberSource {
/**
* The maximum number that can be produced by this source.
* @return the max
*/
def max: Int
/**
* The count of numbers allocated to this source.
* @return the count
*/
def Size: Int
def size: Int
/**
* The count of numbers that can still be drawn.
* @return the count
*/
def CountAvailable: Int
def countAvailable: Int
/**
* The count of numbers that can not be drawn.
* @return the count
*/
def CountUsed: Int
def countUsed: Int
/**
* Is this number a member of this number source?
* @param number the number
* @return `true`, if it is a member; `false`, otherwise
*/
def Test(number: Int): Boolean = -1 < number && number < Size
def test(number: Int): Boolean
/**
* Produce an un-modifiable wrapper for the `Monitor` for this number.
* @param number the number
* @return the wrapped `Monitor`
*/
def Get(number: Int): Option[SecureKey]
def get(number: Int): Option[SecureKey]
//def GetAll(list : List[Int]) : List[SecureKey]
//def GetAll(p : Key => Boolean) : List[SecureKey]
/**
* Produce an un-modifiable wrapper for the `Monitor` for this entity,
* if the entity is discovered being represented in this source.
* @param obj the entity
* @return the wrapped `Monitor`
*/
def get(obj: IdentifiableEntity) : Option[SecureKey]
/**
* Produce a modifiable wrapper for the `Monitor` for this number, only if the number has not been used.
@ -61,15 +70,15 @@ trait NumberSource {
* @param number the number
* @return the wrapped `Monitor`, or `None`
*/
def Available(number: Int): Option[LoanedKey]
def getAvailable(number: Int): Option[LoanedKey]
/**
* Consume a wrapped `Monitor` and release its number from its previous assignment/use.
* @param monitor the `Monitor`
* @return any object previously using this `Monitor`
*/
def Return(monitor: SecureKey): Option[IdentifiableEntity] = {
Return(monitor.GUID)
def returnNumber(monitor: SecureKey): Option[IdentifiableEntity] = {
returnNumber(monitor.GUID)
}
/**
@ -77,8 +86,8 @@ trait NumberSource {
* @param monitor the `Monitor`
* @return any object previously using this `Monitor`
*/
def Return(monitor: LoanedKey): Option[IdentifiableEntity] = {
Return(monitor.GUID)
def returnNumber(monitor: LoanedKey): Option[IdentifiableEntity] = {
returnNumber(monitor.GUID)
}
/**
@ -86,21 +95,21 @@ trait NumberSource {
* @param number the number
* @return any object previously using this number
*/
def Return(number: Int): Option[IdentifiableEntity]
def returnNumber(number: Int): Option[IdentifiableEntity]
/**
* Produce a modifiable wrapper for the `Monitor` for this number, only if the number has not been used.
* This wrapped `Monitor` can only be assigned once and the number may not be `Return`ed to this source.
* This wrapped `Monitor` can only be assigned once and the number may not be `returnNumber`ed to this source.
* @param number the number
* @return the wrapped `Monitor`
*/
def Restrict(number: Int): Option[LoanedKey]
def restrictNumber(number: Int): Option[LoanedKey]
/**
* Numbers from this source may not longer be marked as `Restricted`.
* @return the `List` of all numbers that have been restricted
*/
def FinalizeRestrictions: List[Int]
def finalizeRestrictions: List[Int]
import net.psforever.objects.entity.IdentifiableEntity
@ -110,5 +119,5 @@ trait NumberSource {
* This is the only way to free `Monitors` that are marked as `Restricted`.
* @return a `List` of assignments maintained by all the currently-used number `Monitors`
*/
def Clear(): List[IdentifiableEntity]
def clear(): List[IdentifiableEntity]
}

View file

@ -0,0 +1,112 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.guid.source
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.AvailabilityPolicy
import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
/**
* A `NumberSource` is considered a master "pool" of numbers from which all numbers are available to be drawn.
* Produce a series of numbers from 0 to a maximum number (inclusive) to be used as globally unique identifiers (GUID's).
* @param values the domain of numbers to be used by this source;
* must only be positive integers or zero
* @throws IllegalArgumentException if no numbers are provided
* @throws IllegalArgumentException if any of the numbers provided are negative
*/
class SpecificNumberSource(values: Iterable[Int]) extends NumberSource {
if (values.isEmpty) {
throw new IllegalArgumentException(s"must provide one or more positive integers (or zero)")
}
values.filter(_ < 0) match {
case Nil => ;
case list => throw new IllegalArgumentException(s"non-negative integers only, not ${list.mkString(" ")}")
}
private val ary : Map[Int, Key] = values.map(index => (index, new Key)).toMap
def max : Int = ary.keys.max
def size : Int = ary.size
def countAvailable : Int = ary.values.count(key => key.policy == AvailabilityPolicy.Available)
def countUsed : Int = ary.values.count(_.policy != AvailabilityPolicy.Available)
def test(number : Int) : Boolean = ary.get(number).nonEmpty
def get(number : Int) : Option[SecureKey] = {
ary.get(number) match {
case Some(key) => Some(new SecureKey(number, key))
case _ => None
}
}
def get(obj : IdentifiableEntity) : Option[SecureKey] = {
ary.find { case (_, key) =>
key.obj match {
case Some(o) => o eq obj
case _ => false
}
} match {
case Some((number, key)) => Some(new SecureKey(number, key))
case _ => None
}
}
def getAvailable(number : Int) : Option[LoanedKey] = {
ary.get(number) match {
case Some(key) if key.policy == AvailabilityPolicy.Available =>
key.policy = AvailabilityPolicy.Leased
Some(new LoanedKey(number, key))
case _ =>
None
}
}
def returnNumber(number : Int) : Option[IdentifiableEntity] = {
ary.get(number) match {
case Some(key) if key.policy == AvailabilityPolicy.Leased =>
val out = key.obj
key.policy = AvailabilityPolicy.Available
key.obj = None
out
case _ =>
None
}
}
def restrictNumber(number : Int) : Option[LoanedKey] = {
ary.get(number) match {
case Some(key) if key.policy != AvailabilityPolicy.Restricted =>
key.policy = AvailabilityPolicy.Restricted
Some(new LoanedKey(number, key))
case _ =>
None
}
}
def finalizeRestrictions : List[Int] = {
ary
.filter { case (_, key : Key) => key.policy == AvailabilityPolicy.Restricted }
.keys
.toList
}
def clear(): List[IdentifiableEntity] = {
val leased = ary.values.filter(_.policy != AvailabilityPolicy.Available)
leased collect { case key if key.obj.isEmpty =>
key.policy = AvailabilityPolicy.Available
}
leased.toList collect { case key if key.obj.nonEmpty =>
key.policy = AvailabilityPolicy.Available
val out = key.obj.get
key.obj = None
out
}
}
}
object SpecificNumberSource {
def apply(values: Iterable[Int]): SpecificNumberSource = {
new SpecificNumberSource(values)
}
}

View file

@ -98,94 +98,3 @@ trait Container {
def Collisions(index: Int, width: Int, height: Int): Try[List[InventoryItem]] =
Inventory.CheckCollisionsVar(index, width, height)
}
//object Container {
// type ValidContainer = PlanetSideServerObject with Container
//
// final case class GetMoveItem(where_src : Int, other : ValidContainer, where_other : Int, other_item : Option[Equipment])
//
// final case class GiveMoveItem(cont1 : ValidContainer, where_cont1 : Int, item : Option[Equipment], cont2 : ValidContainer, where_cont2 : Int, other_item : Option[Equipment])
//
//
// final case class TakeMoveItem(source_index : Int, destination : ValidContainer, destination_index : Int)
//
// final case class TakeMoveItem2(item_guid : PlanetSideGUID, destination : ValidContainer, destination_index : Int)
//
// final case class GivingMoveItem(item : Equipment, source : ValidContainer, source_index : Int, destination : ValidContainer, destination_index : Int)
//
// final case class PutMoveItem(item : Equipment, target_index : Int, source : ValidContainer, source_index : Int)
//
// final case class DropMoveItem(item : Equipment, source : ValidContainer, source_index : Int)
//
// final case class ItemMoved(item : Equipment, location : ValidContainer, location_index : Int)
//
// final case class NoMoveItem(source : ValidContainer, source_index : Int)
//}
//
//trait ContainerBehavior {
// this : Actor =>
//
// def ContainableObject : Container.ValidContainer
//
// val containerBehavior : Receive = {
// case Container.GetMoveItem(where_src, destination, where_dest, other_item) =>
// val slot : EquipmentSlot = ContainableObject.Slot(where_src)
// val equipment = slot.Equipment
// slot.Equipment = None
// sender ! Container.GiveMoveItem(ContainableObject, where_src, equipment, destination, where_dest, other_item)
//
// case Container.TakeMoveItem(source_index, destination, destination_index) =>
// val slot : EquipmentSlot = ContainableObject.Slot(source_index)
// val equipment : Option[Equipment] = slot.Equipment
// slot.Equipment = None
// sender ! (equipment match {
// case Some(item) =>
// Container.GivingMoveItem(item, ContainableObject, source_index, destination, destination_index)
// case None =>
// Container.NoMoveItem(ContainableObject, source_index)
// })
//
// case Container.TakeMoveItem2(item_guid, destination, destination_index) =>
// ContainableObject.Find(item_guid) match {
// case Some(source_index) =>
// val slot : EquipmentSlot = ContainableObject.Slot(source_index)
// val equipment : Option[Equipment] = slot.Equipment
// slot.Equipment = None
// sender ! (equipment match {
// case Some(item) =>
// Container.GivingMoveItem(item, ContainableObject, source_index, destination, destination_index)
// case None => ;
// })
//
// case None =>
// sender ! Container.NoMoveItem(ContainableObject, 65535)
// }
//
// case Container.PutMoveItem(item, target_index, source, source_index) =>
// val slot : EquipmentSlot = ContainableObject.Slot(target_index)
// val equipment : Option[Equipment] = slot.Equipment
// if( {
// val tile = item.Definition.Tile
// ContainableObject.Collisions(target_index, tile.Width, tile.Height) match {
// case Success(Nil) => //no item swap
// true
// case Success(_ :: Nil) => //one item to swap
// true
// case Success(_) | Failure(_) =>
// false //abort when too many items at destination or other failure case
// }
// }) {
// slot.Equipment = None
// slot.Equipment = item
// equipment match {
// case Some(swapItem) =>
// sender ! Container.GivingMoveItem(swapItem, ContainableObject, target_index, source, source_index)
// case None => ;
// }
// sender ! Container.ItemMoved(item, ContainableObject, target_index)
// }
// else {
// sender ! Container.DropMoveItem(item, source, source_index)
// }
// }
//}

View file

@ -191,8 +191,7 @@ class GridInventory extends Container {
} else {
val collisions: mutable.Set[InventoryItem] = mutable.Set[InventoryItem]()
items
.map { case (_, item: InventoryItem) => item }
.foreach { item: InventoryItem =>
.foreach { case (_, item: InventoryItem) =>
val actualItemStart: Int = item.start - offset
val itemx: Int = actualItemStart % width
val itemy: Int = actualItemStart / width
@ -542,8 +541,8 @@ class GridInventory extends Container {
//lists with multiple entries represent a group of items that collide
//perform a specific distinct on the list of lists of numeric id overlaps
//THEN map each list of list's item id to an item
val out = testGrid.collect {
case (_, list) if list.size > 1 => list.sortWith(_ < _)
val out = testGrid.values.collect {
case list if list.size > 1 => list.sortWith(_ < _)
}
recursiveRelatedListCollisions(out.iterator, out.toList).map { list => list.map { items } }
}

View file

@ -0,0 +1,132 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.inventory
import net.psforever.objects.Tool
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.SpecificNumberSource
import net.psforever.types.PlanetSideGUID
import scala.util.{Failure, Success}
/**
* An inventory that contains its own internal unique number system bound by a domain of numbers.
* When equipment is inserted into this inventory,
* the equipment is registered to it, assigned one of its internal unique numbers.
* The equipment must not already be registered to another unique number system for that reason.
* Upon being removed, the removed equipment is unregistered.
* The registration system adds another unspoken layer to `Capacity`
* as it imposes a total object count to the inventory.
* @see `NumberSourceHub`
* @see `RandomSelector`
* @see `SpecificNumberSource`
* @param numbers the numbers used as unique identifiers
*/
class LocallyRegisteredInventory(numbers: Iterable[Int])
extends GridInventory {
private val hub: NumberPoolHub = {
val numHub = new NumberPoolHub(SpecificNumberSource(numbers))
//only one pool composed of all of the numbers; randomized selection
numHub.AddPool("internal", numbers.toList).Selector = new RandomSelector
numHub
}
override def Insert(start : Int, obj : Equipment) : Boolean = {
if(!obj.HasGUID) {
registerEquipment(obj) match {
case true if super.Insert(start, obj) =>
true
case true =>
unregisterEquipment(obj) //the item failed to be inserted; undo the previous registration
false
case _ =>
false
}
}
else {
false
}
}
override def InsertQuickly(start : Int, obj : Equipment) : Boolean = {
if(!obj.HasGUID) {
registerEquipment(obj) match {
case true if super.InsertQuickly(start, obj) =>
true
case true =>
unregisterEquipment(obj) //the item failed to be inserted; undo the previous registration
false
case _ =>
false
}
}
else {
false
}
}
override def Remove(guid : PlanetSideGUID) : Boolean = {
hub(guid) match {
case Some(obj: Equipment) if super.Remove(guid) =>
unregisterEquipment(obj)
case _ =>
false
}
}
override def Remove(index : Int) : Boolean = {
Slot(index).Equipment match {
case Some(obj: Equipment) if super.Remove(obj.GUID) =>
unregisterEquipment(obj)
case _ =>
false
}
}
override def Clear() : List[InventoryItem] = {
val items = super.Clear()
items.foreach { item => unregisterEquipment(item.obj) }
items
}
private def registerEquipment(obj: Equipment): Boolean = {
obj match {
case tool: Tool => registerTool(tool)
case _ => registerObject(obj)
}
}
private def registerTool(obj: Tool): Boolean = {
val parts = obj +: obj.AmmoSlots.map { _.Box }
val tasks = parts.map { part => hub.register(part, "internal") }
if(tasks.exists(o => o.isInstanceOf[Failure[Int]])) {
tasks.zipWithIndex.collect { case (Success(_), index) =>
unregisterEquipment(parts(index))
}
false
} else {
true
}
}
private def registerObject(obj: Equipment): Boolean = {
hub.register(obj, "internal").isSuccess
}
private def unregisterEquipment(obj: Equipment): Boolean = {
obj match {
case tool: Tool => unregisterTool(tool)
case _ => unregisterObject(obj)
}
}
private def unregisterTool(obj: Tool): Boolean = {
val parts = obj +: obj.AmmoSlots.map { _.Box }
parts.map { part => hub.unregister(part) }.forall(o => o.isInstanceOf[Success[Int]])
}
private def unregisterObject(obj: Equipment): Boolean = {
hub.unregister(obj).isSuccess
}
}

View file

@ -0,0 +1,44 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.locker
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.EquipmentDefinition
import net.psforever.objects.inventory.{Container, GridInventory}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.types.PlanetSideEmpire
/**
* The companion of a `Locker` that is carried with a player
* masquerading as their sixth `EquipmentSlot` object and a sub-inventory item.
* The inventory of this object is accessed indirectly using a game world `Locker` object (`mb_locker`) as a proxy.
* The `Player` class refers to it as the "fifth slot".
*/
class LockerContainer(inventory: GridInventory)
extends PlanetSideServerObject
with Container {
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private val inv: GridInventory = inventory
def Faction: PlanetSideEmpire.Value = faction
override def Faction_=(fact: PlanetSideEmpire.Value): PlanetSideEmpire.Value = {
faction = fact
Faction
}
def Inventory: GridInventory = inv
def VisibleSlots: Set[Int] = Set.empty[Int]
def Definition: EquipmentDefinition = GlobalDefinitions.locker_container
}
object LockerContainer {
/**
* Overloaded constructor for the standard Infantry locker container of size 30x20.
* @return a `LockerContainer` object
*/
def apply(): LockerContainer = {
new LockerContainer(GridInventory(30, 20))
}
}

View file

@ -1,69 +1,24 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
// Copyright (c) 2020 PSForever
package net.psforever.objects.locker
import akka.actor.Actor
import net.psforever.objects.definition.EquipmentDefinition
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.{Container, GridInventory}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
import net.psforever.packet.game.{ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectDetachMessage}
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
import net.psforever.packet.game.{ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectDetachMessage}
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.{PlanetSideEmpire, Vector3}
/**
* The companion of a `Locker` that is carried with a player
* masquerading as their sixth `EquipmentSlot` object and a sub-inventory item.
* The `Player` class refers to it as the "fifth slot" as its permanent slot number is encoded as `0x85`.
* The inventory of this object is accessed using a game world `Locker` object (`mb_locker`).
* A control agency mainly for manipulating the equipment stowed by a player in a `LockerContainer`
* and reporting back to a specific xchannel in the event system about these changes.
* @param locker the governed player-facing locker component
* @param toChannel the channel to which to publish events, typically the owning player's name
*/
class LockerContainer extends PlanetSideServerObject with Container {
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private val inventory = GridInventory(30, 20)
def Faction: PlanetSideEmpire.Value = faction
override def Faction_=(fact: PlanetSideEmpire.Value): PlanetSideEmpire.Value = {
faction = fact
Faction
}
def Inventory: GridInventory = inventory
def VisibleSlots: Set[Int] = Set.empty[Int]
def Definition: EquipmentDefinition = GlobalDefinitions.locker_container
}
object LockerContainer {
def apply(): LockerContainer = {
new LockerContainer()
}
}
class LockerEquipment(locker: LockerContainer) extends Equipment with Container {
private val obj = locker
override def GUID: PlanetSideGUID = obj.GUID
override def GUID_=(guid: PlanetSideGUID): PlanetSideGUID = obj.GUID_=(guid)
override def HasGUID: Boolean = obj.HasGUID
override def Invalidate(): Unit = obj.Invalidate()
override def Faction: PlanetSideEmpire.Value = obj.Faction
def Inventory: GridInventory = obj.Inventory
def VisibleSlots: Set[Int] = Set.empty[Int]
def Definition: EquipmentDefinition = obj.Definition
}
class LockerContainerControl(locker: LockerContainer, toChannel: String) extends Actor with ContainableBehavior {
class LockerContainerControl(locker: LockerContainer, toChannel: String)
extends Actor
with ContainableBehavior {
def ContainerObject = locker
def receive: Receive =

View file

@ -0,0 +1,41 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.locker
import net.psforever.objects.definition.EquipmentDefinition
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.{Container, GridInventory}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
/**
* A wrapper class that allows a player-facing locker component
* to be treated standard `Equipment` object in the player's fifth (sixth) slot.
* (The opposite is not true - the equipment does not get treated as a locker component.)
* During packet conversion and registration and general access in terms of holsters or "equipment slots",
* the component may be be treated the same as other existing objects at the same level.
* The entity's ability to be utilized like an inventory-stowable entity is not compromised.
* @see `EquipmentSlot`
* @see `IdentifiableEntity`
* @see `LockerContainer`
* @param locker the player-facing locker
*/
class LockerEquipment(locker: LockerContainer)
extends Equipment
with Container {
private val obj = locker
override def GUID: PlanetSideGUID = obj.GUID
override def GUID_=(guid: PlanetSideGUID): PlanetSideGUID = obj.GUID_=(guid)
override def HasGUID: Boolean = obj.HasGUID
override def Invalidate(): Unit = obj.Invalidate()
override def Faction: PlanetSideEmpire.Value = obj.Faction
def Inventory: GridInventory = obj.Inventory
def VisibleSlots: Set[Int] = Set.empty[Int]
def Definition: EquipmentDefinition = obj.Definition
}

View file

@ -12,7 +12,7 @@ import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.UniqueNumberSystem
import net.psforever.objects.guid.key.LoanedKey
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition}
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
@ -73,7 +73,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
private var accessor: ActorRef = ActorRef.noSender
/** The basic support structure for the globally unique number system used by this `Zone`. */
private var guid: NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536))
private var guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(65536))
/** A synchronized `List` of items (`Equipment`) dropped by players on the ground and can be collected again. */
private val equipmentOnGround: ListBuffer[Equipment] = ListBuffer[Equipment]()
@ -322,6 +322,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
guid.AddPool("f", (30001 to 35000).toList).Selector = new RandomSelector
guid.AddPool("g", (35001 until 40100).toList).Selector = new RandomSelector
guid.AddPool("projectiles", (Projectile.baseUID until Projectile.rangeUID).toList)
guid.AddPool("locker-contents", (40150 until 40450).toList).Selector = new RandomSelector
//TODO disabled temporarily to lighten load times
//guid.AddPool("h", (40150 to 45000).toList).Selector = new RandomSelector
//guid.AddPool("i", (45001 to 50000).toList).Selector = new RandomSelector
@ -358,12 +359,12 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
building match {
case warpGate: WarpGate =>
warpGate.Faction == faction || warpGate.Faction == PlanetSideEmpire.NEUTRAL || warpGate.Broadcast
case building =>
case _ =>
building.Faction == faction
}
}
.map {
case (building, spawns) =>
case (building, spawns: List[SpawnPoint]) =>
(building, spawns.filter(!_.Offline))
}
.concat(

View file

@ -364,13 +364,12 @@ class PersistenceMonitor(name: String, squadService: ActorRef, taskResolver: Act
//player has released
//our last body was turned into a corpse; just the avatar remains
//TODO perform any last minute saving now ...
AvatarLogout(avatar)
inZone.GUID(avatar.vehicle) match {
case Some(obj: Vehicle) if obj.OwnerName.contains(avatar.name) =>
obj.Actor ! Vehicle.Ownership(None)
case _ => ;
}
taskResolver.tell(GUIDTask.UnregisterLocker(avatar.locker)(inZone.GUID), context.parent)
AvatarLogout(avatar)
case _ =>
//user stalled during initial session, or was caught in between zone transfer
@ -386,7 +385,7 @@ class PersistenceMonitor(name: String, squadService: ActorRef, taskResolver: Act
* @see `Avatar`
* @see `AvatarAction.ObjectDelete`
* @see `AvatarServiceMessage`
* @see `GUIDTask.UnregisterAvatar`
* @see `GUIDTask.UnregisterPlayer`
* @see `Player`
* @see `Zone.AvatarEvents`
* @see `Zone.Population.Release`
@ -405,8 +404,8 @@ class PersistenceMonitor(name: String, squadService: ActorRef, taskResolver: Act
}
inZone.Population.tell(Zone.Population.Release(avatar), parent)
inZone.AvatarEvents.tell(AvatarServiceMessage(inZone.id, AvatarAction.ObjectDelete(pguid, pguid)), parent)
taskResolver.tell(GUIDTask.UnregisterPlayer(player)(inZone.GUID), parent)
AvatarLogout(avatar)
taskResolver.tell(GUIDTask.UnregisterAvatar(player)(inZone.GUID), parent)
}
/**
@ -424,6 +423,7 @@ class PersistenceMonitor(name: String, squadService: ActorRef, taskResolver: Act
squadService.tell(Service.Leave(Some(avatar.id.toString)), context.parent)
Deployables.Disown(inZone, avatar, context.parent)
inZone.Population.tell(Zone.Population.Leave(avatar), context.parent)
taskResolver.tell(GUIDTask.UnregisterObjectTask(avatar.locker)(inZone.GUID), context.parent)
log.info(s"logout of ${avatar.name}")
}
}

View file

@ -2,21 +2,14 @@ package net.psforever.zones
import java.io.FileNotFoundException
import net.psforever.objects.serverobject.terminals.{
CaptureTerminal,
CaptureTerminalDefinition,
ProximityTerminal,
ProximityTerminalDefinition,
Terminal,
TerminalDefinition
}
import net.psforever.objects.serverobject.terminals.{CaptureTerminal, CaptureTerminalDefinition, ProximityTerminal, ProximityTerminalDefinition, Terminal, TerminalDefinition}
import net.psforever.objects.serverobject.mblocker.Locker
import java.util.concurrent.atomic.AtomicInteger
import akka.actor.ActorContext
import io.circe._
import io.circe.parser._
import net.psforever.objects.{GlobalDefinitions, LocalProjectile}
import net.psforever.objects.{GlobalDefinitions, LocalLockerItem, LocalProjectile}
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.definition.BasicDefinition
import net.psforever.objects.serverobject.doors.Door
@ -25,18 +18,13 @@ import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.pad.{VehicleSpawnPad, VehicleSpawnPadDefinition}
import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition}
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.structures.{
Building,
BuildingDefinition,
FoundationBuilder,
StructureType,
WarpGate
}
import net.psforever.objects.serverobject.structures.{Building, BuildingDefinition, FoundationBuilder, StructureType, WarpGate}
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretDefinition}
import net.psforever.objects.zones.{MapInfo, Zone, ZoneInfo, ZoneMap}
import net.psforever.types.{PlanetSideEmpire, Vector3}
import net.psforever.util.DefinitionUtil
import scala.io.Source
import scala.collection.parallel.CollectionConverters._
@ -281,6 +269,9 @@ object Zones {
(Projectile.baseUID until Projectile.rangeUID) foreach {
zoneMap.addLocalObject(_, LocalProjectile.Constructor)
}
40150 until 40450 foreach {
zoneMap.addLocalObject(_, LocalLockerItem.Constructor)
}
lattice.asObject.get(info.value).foreach { obj =>
obj.asArray.get.foreach { entry =>

View file

@ -5,6 +5,7 @@ import net.psforever.objects.GlobalDefinitions._
import net.psforever.objects._
import net.psforever.objects.avatar.{Avatar, BattleRank, Implant}
import net.psforever.objects.definition.ImplantDefinition
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterGender, CharacterVoice, ImplantType, PlanetSideEmpire}
import org.specs2.mutable._

View file

@ -7,6 +7,7 @@ import net.psforever.objects.avatar.Avatar
import net.psforever.objects.definition._
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
import net.psforever.objects.locker.{LockerContainer, LockerEquipment}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.vehicles.UtilityType

View file

@ -8,7 +8,7 @@ import net.psforever.objects._
import net.psforever.objects.ballistics._
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
@ -242,7 +242,7 @@ essentially, treat them more as generic entities whose object types are damageab
see specific object type tests in relation to what those object types does above and beyond that during damage
*/
class DamageableEntityDamageTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -313,7 +313,7 @@ class DamageableEntityDamageTest extends ActorTest {
}
class DamageableEntityDestroyedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -387,7 +387,7 @@ class DamageableEntityDestroyedTest extends ActorTest {
}
class DamageableEntityNotDestroyTwice extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -458,7 +458,7 @@ class DamageableEntityNotDestroyTwice extends ActorTest {
}
class DamageableAmenityTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -546,7 +546,7 @@ class DamageableAmenityTest extends ActorTest {
class DamageableMountableDamageTest extends ActorTest {
//TODO this test with not send HitHint packets because LivePlayers is not being allocated for the players in the zone
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -638,7 +638,7 @@ class DamageableMountableDamageTest extends ActorTest {
class DamageableMountableDestroyTest extends ActorTest {
//TODO this test with not send HitHint packets because LivePlayers is not being allocated for the players in the zone
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -730,7 +730,7 @@ class DamageableMountableDestroyTest extends ActorTest {
}
class DamageableWeaponTurretDamageTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -823,7 +823,7 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
}
class DamageableWeaponTurretJammerTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -918,7 +918,7 @@ class DamageableWeaponTurretJammerTest extends ActorTest {
}
class DamageableWeaponTurretDestructionTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -1069,7 +1069,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
}
class DamageableVehicleDamageTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -1174,7 +1174,7 @@ class DamageableVehicleDamageTest extends ActorTest {
}
class DamageableVehicleDamageMountedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -1315,7 +1315,7 @@ class DamageableVehicleDamageMountedTest extends ActorTest {
}
class DamageableVehicleJammeringMountedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -1428,7 +1428,7 @@ class DamageableVehicleJammeringMountedTest extends ActorTest {
}
class DamageableVehicleDestroyTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -1531,7 +1531,7 @@ class DamageableVehicleDestroyTest extends ActorTest {
}
class DamageableVehicleDestroyMountedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)

View file

@ -7,7 +7,7 @@ import base.ActorTest
import net.psforever.objects.ballistics._
import net.psforever.objects.ce.DeployedItem
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{Zone, ZoneMap}
@ -300,7 +300,7 @@ class ShieldGeneratorDeployableTest extends Specification {
}
class ExplosiveDeployableJammerTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -398,7 +398,7 @@ class ExplosiveDeployableJammerTest extends ActorTest {
}
class ExplosiveDeployableJammerExplodeTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -508,7 +508,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
}
class ExplosiveDeployableDestructionTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)

View file

@ -8,7 +8,7 @@ import net.psforever.objects.avatar.Avatar
import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool}
import net.psforever.objects.definition.ToolDefinition
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.structures.{Building, StructureType}
@ -182,7 +182,7 @@ class FacilityTurretControl4Test extends ActorTest {
}
class FacilityTurretControlRestorationTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)

View file

@ -8,7 +8,7 @@ import net.psforever.objects.avatar.Avatar
import net.psforever.objects.ballistics._
import net.psforever.objects.{GlobalDefinitions, Player, Tool}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
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.structures.{Building, StructureType}
@ -46,7 +46,7 @@ class GeneratorControlConstructTest extends ActorTest {
}
class GeneratorControlDamageTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -128,7 +128,7 @@ class GeneratorControlDamageTest extends ActorTest {
}
class GeneratorControlCriticalTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -218,7 +218,7 @@ class GeneratorControlCriticalTest extends ActorTest {
}
class GeneratorControlDestroyedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -341,7 +341,7 @@ class GeneratorControlKillsTest extends ActorTest {
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 guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -478,7 +478,7 @@ class GeneratorControlKillsTest extends ActorTest {
}
class GeneratorControlNotDestroyTwice extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -561,7 +561,7 @@ class GeneratorControlNotDestroyTwice extends ActorTest {
}
class GeneratorControlNotDamageIfExplodingTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -655,7 +655,7 @@ class GeneratorControlNotDamageIfExplodingTest extends ActorTest {
}
class GeneratorControlNotRepairIfExplodingTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -753,7 +753,7 @@ class GeneratorControlNotRepairIfExplodingTest extends ActorTest {
}
class GeneratorControlRepairPastRestorePoint extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(5))
val guid = new NumberPoolHub(new MaxNumberSource(5))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)

View file

@ -3,13 +3,13 @@ package objects
import net.psforever.objects.{AmmoBox, SimpleItem, Tool}
import net.psforever.objects.definition.SimpleItemDefinition
import net.psforever.objects.inventory.{GridInventory, InventoryDisarrayException, InventoryItem, InventoryTile}
import net.psforever.objects.inventory._
import net.psforever.objects.GlobalDefinitions.{bullet_9mm, suppressor}
import net.psforever.types.PlanetSideGUID
import org.specs2.mutable._
import scala.collection.mutable.ListBuffer
import scala.util.{Success, Failure}
import scala.util.{Failure, Success}
class InventoryTest extends Specification {
val bullet9mmBox1 = AmmoBox(bullet_9mm)
@ -522,6 +522,168 @@ class InventoryTest extends Specification {
}
}
"LocallyRegisteredInventory" should {
"construct" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15))
obj.TotalCapacity mustEqual 1
obj.Capacity mustEqual 1
}
"insert (and register) item" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15))
obj.Resize(9, 6)
obj.Size mustEqual 0
val item = AmmoBox(bullet_9mm)
item.HasGUID mustEqual false
obj.CheckCollisions(2, item) mustEqual Success(Nil)
obj += 2 -> item
obj.Size mustEqual 1
obj.hasItem(PlanetSideGUID(15)).contains(item) mustEqual true
item.HasGUID mustEqual true
item.GUID mustEqual PlanetSideGUID(15)
}
"insert (and register) complex item" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15, 16))
obj.Resize(9, 6)
obj.Size mustEqual 0
val item = Tool(suppressor)
item.HasGUID mustEqual false
item.AmmoSlot.Box.HasGUID mustEqual false
obj.CheckCollisions(2, item) mustEqual Success(Nil)
obj += 2 -> item
obj.Size mustEqual 1
item.HasGUID mustEqual true
item.AmmoSlot.Box.HasGUID mustEqual true
}
"not insert item if already registered" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15))
obj.Resize(9, 6)
obj.Size mustEqual 0
val item = AmmoBox(bullet_9mm)
item.GUID = PlanetSideGUID(10) //artificially register
item.HasGUID mustEqual true
obj.CheckCollisions(2, item) mustEqual Success(Nil) //insert should be possible
obj += 2 -> item
obj.Size mustEqual 0
obj.hasItem(PlanetSideGUID(15)).isEmpty mustEqual true
}
"not insert (or register) item if no (more) GUID's are available" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15))
obj.Resize(9, 6)
val item1 = AmmoBox(bullet_9mm)
val item2 = AmmoBox(bullet_9mm)
item1.HasGUID mustEqual false
item2.HasGUID mustEqual false
obj.CheckCollisions(2, item1) mustEqual Success(Nil) //insert should be possible
obj += 2 -> item1
obj.Size mustEqual 1
obj.hasItem(PlanetSideGUID(15)).contains(item1) mustEqual true
item1.HasGUID mustEqual true
item1.GUID mustEqual PlanetSideGUID(15)
obj.CheckCollisions(5, item2) mustEqual Success(Nil) //insert should be possible
obj += 5 -> item2
obj.Size mustEqual 1
item2.HasGUID mustEqual false
}
"not insert (or register) complex item if no (more) GUID's are available" in {
val item = Tool(suppressor)
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15))
obj.Resize(9, 6)
obj.Size mustEqual 0
item.HasGUID mustEqual false
item.AmmoSlot.Box.HasGUID mustEqual false
obj.CheckCollisions(2, item) mustEqual Success(Nil) //insert should be possible
obj += 2 -> item
obj.Size mustEqual 0
item.HasGUID mustEqual false
item.AmmoSlot.Box.HasGUID mustEqual false
}
"insert (and register) multiple items" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15, 17))
obj.Resize(9, 6)
val item1 = AmmoBox(bullet_9mm)
val item2 = AmmoBox(bullet_9mm)
item1.HasGUID mustEqual false
item2.HasGUID mustEqual false
obj.CheckCollisions(2, item1) mustEqual Success(Nil) //insert should be possible
obj += 2 -> item1
obj.Size mustEqual 1
item1.HasGUID mustEqual true
obj.CheckCollisions(5, item2) mustEqual Success(Nil) //insert should be possible
obj += 5 -> item2
obj.Size mustEqual 2
item2.HasGUID mustEqual true
}
"clear (and unregister) all items" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15, 17))
obj.Resize(9, 6)
val item1 = AmmoBox(bullet_9mm)
val item2 = AmmoBox(bullet_9mm)
obj += 2 -> item1
obj += 5 -> item2
obj.Size mustEqual 2
item1.HasGUID mustEqual true
item2.HasGUID mustEqual true
obj.Clear()
obj.Size mustEqual 0
item1.HasGUID mustEqual false
item2.HasGUID mustEqual false
}
"remove (and unregister) item" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15, 17))
obj.Resize(9, 6)
val item1 = AmmoBox(bullet_9mm)
val item2 = AmmoBox(bullet_9mm)
obj += 2 -> item1
obj += 5 -> item2
obj.Size mustEqual 2
item1.HasGUID mustEqual true
item2.HasGUID mustEqual true
obj -= 2
obj.Size mustEqual 1
item1.HasGUID mustEqual false
item2.HasGUID mustEqual true
obj -= 5
obj.Size mustEqual 0
item1.HasGUID mustEqual false
item2.HasGUID mustEqual false
}
}
"remove (and unregister) complex item" in {
val obj: LocallyRegisteredInventory = new LocallyRegisteredInventory(List(15, 17))
obj.Resize(9, 6)
val item = Tool(suppressor)
item.HasGUID mustEqual false
item.AmmoSlot.Box.HasGUID mustEqual false
obj.CheckCollisions(2, item) mustEqual Success(Nil)
obj += 2 -> item
obj.Size mustEqual 1
item.HasGUID mustEqual true
item.AmmoSlot.Box.HasGUID mustEqual true
obj -= 2
obj.Size mustEqual 0
item.HasGUID mustEqual false
item.AmmoSlot.Box.HasGUID mustEqual false
}
"InventoryEquiupmentSlot" should {
"insert, collide, insert" in {
val obj: GridInventory = GridInventory(7, 7)

View file

@ -0,0 +1,24 @@
package objects
import net.psforever.objects.{LocalLockerItem, LocalProjectile}
import net.psforever.types.PlanetSideEmpire
import org.specs2.mutable.Specification
class LocalTest 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"
}
}
"LocalLockerItem" should {
"construct" in {
val obj = new LocalLockerItem() //since they're just placeholders, they only need to construct
obj.Faction mustEqual PlanetSideEmpire.NEUTRAL
obj.Definition.ObjectId mustEqual 0
obj.Definition.Name mustEqual "locker-equipment"
}
}
}

View file

@ -7,7 +7,7 @@ import base.ActorTest
import net.psforever.objects.avatar.{Avatar, PlayerControl}
import net.psforever.objects.ballistics._
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects._
@ -20,7 +20,7 @@ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.duration._
class PlayerControlHealTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -106,7 +106,7 @@ class PlayerControlHealTest extends ActorTest {
}
class PlayerControlHealSelfTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -181,7 +181,7 @@ class PlayerControlHealSelfTest extends ActorTest {
}
class PlayerControlRepairTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -277,7 +277,7 @@ class PlayerControlRepairTest extends ActorTest {
}
class PlayerControlRepairSelfTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -352,7 +352,7 @@ class PlayerControlRepairSelfTest extends ActorTest {
}
class PlayerControlDamageTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -449,7 +449,7 @@ class PlayerControlDamageTest extends ActorTest {
}
class PlayerControlDeathStandingTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -575,7 +575,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
}
class PlayerControlDeathSeatedTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(15))
val guid = new NumberPoolHub(new MaxNumberSource(15))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)

View file

@ -6,6 +6,7 @@ import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.definition.{SimpleItemDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.equipment.EquipmentSize
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{PlanetSideGUID, _}
import org.specs2.mutable._

View file

@ -14,13 +14,7 @@ class ProjectileTest extends Specification {
val player = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val fury = Vehicle(GlobalDefinitions.fury)
"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"
}
"Range" should {
"local projectile range" in {
Projectile.baseUID < Projectile.rangeUID mustEqual true
}

View file

@ -7,7 +7,7 @@ import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
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.structures.{Building, StructureType}
@ -28,7 +28,7 @@ essentially, treat it more as a generic entity whose object type is repairable
see GeneratorTest in relation to what the generator does above and beyond that during repair
*/
class RepairableEntityRepairTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -100,7 +100,7 @@ class RepairableEntityRepairTest extends ActorTest {
}
class RepairableEntityNotRepairTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -141,7 +141,7 @@ class RepairableEntityNotRepairTest extends ActorTest {
}
class RepairableAmenityTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
@ -229,7 +229,7 @@ class RepairableAmenityTest extends ActorTest {
}
class RepairableTurretWeapon extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -323,7 +323,7 @@ class RepairableTurretWeapon extends ActorTest {
}
class RepairableVehicleRepair extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
@ -396,7 +396,7 @@ class RepairableVehicleRestoration extends ActorTest {
/*
no messages are dispatched, in this case, because most vehicles are flagged to not be repairable if destroyed
*/
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)

View file

@ -7,7 +7,7 @@ import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.actors.zone.{BuildingActor, ZoneActor}
import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.{GlobalDefinitions, Ntu, Player, Vehicle}
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl, ResourceSiloDefinition}
@ -94,7 +94,7 @@ class ResourceSiloControlStartupTest extends ActorTest {
}
class ResourceSiloControlUseTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val map = new ZoneMap("test")
val zone = new Zone("test", map, 0) {
override def SetupNumberPools() = {}

View file

@ -1,8 +1,8 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{Actor, ActorContext, Props}
import base.ActorTest
import akka.actor.ActorContext
import base.FreedContextActorTest
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.serverobject.ServerObjectBuilder
@ -11,361 +11,223 @@ import net.psforever.objects.serverobject.terminals.ProximityTerminal
import net.psforever.objects.zones.Zone
import net.psforever.types.{PlanetSideGUID, Vector3}
import scala.concurrent.duration.Duration
class BuildingBuilderTest extends ActorTest {
class BuildingBuilderTest extends FreedContextActorTest {
"Building object" should {
"build" in {
val structure: (String, Int, Int, Zone, ActorContext) => Building = Building.Structure(StructureType.Building)
val actor = system.actorOf(
Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, "Building", 10, 10, Zone.Nowhere),
"building"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[Building])
assert(reply.asInstanceOf[Building].MapId == 10)
assert(reply.asInstanceOf[Building].Zone == Zone.Nowhere)
val building = FoundationBuilder(structure).Build("building", 10, 10, Zone.Nowhere)(context)
assert(building ne null)
assert(building.isInstanceOf[Building])
assert(building.MapId == 10)
assert(building.Zone == Zone.Nowhere)
}
}
}
class WarpGateBuilderTest extends ActorTest {
class WarpGateBuilderTest extends FreedContextActorTest {
"WarpGate object" should {
"build" in {
val structure: (String, Int, Int, Zone, ActorContext) => Building = WarpGate.Structure
val actor = system.actorOf(
Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, "wgate", 10, 10, Zone.Nowhere),
"wgate"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[Building])
assert(reply.asInstanceOf[Building].MapId == 10)
assert(reply.asInstanceOf[Building].Zone == Zone.Nowhere)
val building = FoundationBuilder(structure).Build("wgate", 10, 10, Zone.Nowhere)(context)
assert(building ne null)
assert(building.isInstanceOf[WarpGate])
assert(building.MapId == 10)
assert(building.Zone == Zone.Nowhere)
}
}
}
class DoorObjectBuilderTest1 extends ActorTest {
class DoorObjectBuilderTest1 extends FreedContextActorTest {
import net.psforever.objects.serverobject.doors.Door
"Door object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, Door.Constructor), hub),
"door"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[Door])
assert(reply.asInstanceOf[Door].HasGUID)
assert(reply.asInstanceOf[Door].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, Door.Constructor).Build(context, hub)
assert(obj.isInstanceOf[Door])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj == hub(1).get)
}
}
}
class DoorObjectBuilderTest2 extends ActorTest {
class DoorObjectBuilderTest2 extends FreedContextActorTest {
import net.psforever.objects.serverobject.doors.Door
"Door object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, Door.Constructor(Vector3(1, 2, 3))),
hub
),
"door"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[Door])
assert(reply.asInstanceOf[Door].Position == Vector3(1, 2, 3))
assert(reply.asInstanceOf[Door].HasGUID)
assert(reply.asInstanceOf[Door].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, Door.Constructor(Vector3(1, 2, 3))).Build(context, hub)
assert(obj.isInstanceOf[Door])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj == hub(1).get)
assert(obj.Position == Vector3(1, 2, 3))
}
}
}
class IFFLockObjectBuilderTest extends ActorTest {
class IFFLockObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.serverobject.locks.IFFLock
"IFFLock object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, IFFLock.Constructor(Vector3(0f, 0f, 0f), Vector3(0f, 0f, 0f))),
hub
),
"lock"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[IFFLock])
assert(reply.asInstanceOf[IFFLock].HasGUID)
assert(reply.asInstanceOf[IFFLock].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, IFFLock.Constructor(Vector3(1f, 1f, 1f), Vector3(2f, 2f, 2f))).Build(context, hub)
assert(obj.isInstanceOf[IFFLock])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj.Position == Vector3(1,1,1))
assert(obj.Outwards == Vector3(0.034899496f, 0.99939084f, 0.0f))
assert(obj == hub(1).get)
}
}
}
class ImplantTerminalMechObjectBuilderTest extends ActorTest {
class ImplantTerminalMechObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
"Implant terminal mech object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, ImplantTerminalMech.Constructor(Vector3.Zero)),
hub
),
"mech"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[ImplantTerminalMech])
assert(reply.asInstanceOf[ImplantTerminalMech].HasGUID)
assert(reply.asInstanceOf[ImplantTerminalMech].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, ImplantTerminalMech.Constructor(Vector3.Zero)).Build(context, hub)
assert(obj.isInstanceOf[ImplantTerminalMech])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj == hub(1).get)
}
}
}
class TerminalObjectBuilderTest extends ActorTest {
class TerminalObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.GlobalDefinitions.order_terminal
import net.psforever.objects.serverobject.terminals.Terminal
"Terminal object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, Terminal.Constructor(Vector3(1.1f, 2.2f, 3.3f), order_terminal)),
hub
),
"term"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[Terminal])
assert(reply.asInstanceOf[Terminal].HasGUID)
assert(reply.asInstanceOf[Terminal].GUID == PlanetSideGUID(1))
assert(reply.asInstanceOf[Terminal].Position == Vector3(1.1f, 2.2f, 3.3f))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, Terminal.Constructor(Vector3(1.1f, 2.2f, 3.3f), order_terminal)).Build(context, hub)
assert(obj.isInstanceOf[Terminal])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj.Position == Vector3(1.1f, 2.2f, 3.3f))
assert(obj == hub(1).get)
}
}
}
class ProximityTerminalObjectBuilderTest extends ActorTest {
class ProximityTerminalObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.GlobalDefinitions.medical_terminal
import net.psforever.objects.serverobject.terminals.Terminal
"Terminal object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, ProximityTerminal.Constructor(medical_terminal)),
hub
),
"term"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[Terminal])
assert(reply.asInstanceOf[Terminal].HasGUID)
assert(reply.asInstanceOf[Terminal].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, ProximityTerminal.Constructor(medical_terminal)).Build(context, hub)
assert(obj.isInstanceOf[Terminal])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj == hub(1).get)
}
}
}
class VehicleSpawnPadObjectBuilderTest extends ActorTest {
class VehicleSpawnPadObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
"Vehicle spawn pad object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(
1,
VehicleSpawnPad
.Constructor(Vector3(1.1f, 2.2f, 3.3f), GlobalDefinitions.mb_pad_creation, Vector3(4.4f, 5.5f, 6.6f))
),
hub
),
"pad"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[VehicleSpawnPad])
assert(reply.asInstanceOf[VehicleSpawnPad].HasGUID)
assert(reply.asInstanceOf[VehicleSpawnPad].GUID == PlanetSideGUID(1))
assert(reply.asInstanceOf[VehicleSpawnPad].Position == Vector3(1.1f, 2.2f, 3.3f))
assert(reply.asInstanceOf[VehicleSpawnPad].Orientation == Vector3(4.4f, 5.5f, 6.6f))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1,
VehicleSpawnPad.Constructor(
Vector3(1.1f, 2.2f, 3.3f), GlobalDefinitions.mb_pad_creation, Vector3(4.4f, 5.5f, 6.6f)
)
).Build(context, hub)
assert(obj.isInstanceOf[VehicleSpawnPad])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj.Position == Vector3(1.1f, 2.2f, 3.3f))
assert(obj.Orientation == Vector3(4.4f, 5.5f, 6.6f))
assert(obj == hub(1).get)
}
}
}
class LocalProjectileBuilderTest extends ActorTest {
class LocalProjectileBuilderTest extends FreedContextActorTest {
import net.psforever.objects.LocalProjectile
"Local projectile object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, LocalProjectile.Constructor),
hub
),
"locker"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[LocalProjectile])
assert(reply.asInstanceOf[LocalProjectile].HasGUID)
assert(reply.asInstanceOf[LocalProjectile].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, LocalProjectile.Constructor).Build(context, hub)
assert(obj.isInstanceOf[LocalProjectile])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj == hub(1).get)
}
}
}
class LockerObjectBuilderTest extends ActorTest {
class LockerObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.serverobject.mblocker.Locker
"Locker object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, Locker.Constructor), hub),
"locker"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[Locker])
assert(reply.asInstanceOf[Locker].HasGUID)
assert(reply.asInstanceOf[Locker].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, Locker.Constructor).Build(context, hub)
assert(obj.isInstanceOf[Locker])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj == hub(1).get)
}
}
}
class ResourceSiloObjectBuilderTest extends ActorTest {
class ResourceSiloObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
"Resource silo object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, ResourceSilo.Constructor(Vector3(0f, 0f, 0f))),
hub
),
"resource-silo"
)
actor ! "startup"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[ResourceSilo])
assert(reply.asInstanceOf[ResourceSilo].HasGUID)
assert(reply.asInstanceOf[ResourceSilo].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, ResourceSilo.Constructor(Vector3(1f, 1f, 1f))).Build(context, hub)
assert(obj.isInstanceOf[ResourceSilo])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj.Position == Vector3(1,1,1))
assert(obj == hub(1).get)
}
}
}
class SpawnTubeObjectBuilderTest extends ActorTest {
class SpawnTubeObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.serverobject.tube.SpawnTube
"Spawn tube object" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90))),
hub
),
"spawn-tube"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[SpawnTube])
assert(reply.asInstanceOf[SpawnTube].HasGUID)
assert(reply.asInstanceOf[SpawnTube].GUID == PlanetSideGUID(1))
assert(reply.asInstanceOf[SpawnTube].Position == Vector3(3980.4062f, 4267.3047f, 257.5625f))
assert(reply.asInstanceOf[SpawnTube].Orientation == Vector3(0, 0, 90))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(
1,
SpawnTube.Constructor(Vector3(3980.4062f, 4267.3047f, 257.5625f), Vector3(0, 0, 90))
).Build(context, hub)
assert(obj.isInstanceOf[SpawnTube])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj.Position == Vector3(3980.4062f, 4267.3047f, 257.5625f))
assert(obj.Orientation == Vector3(0, 0, 90))
assert(obj == hub(1).get)
}
}
}
class FacilityTurretObjectBuilderTest extends ActorTest {
class FacilityTurretObjectBuilderTest extends FreedContextActorTest {
import net.psforever.objects.GlobalDefinitions.manned_turret
import net.psforever.objects.serverobject.turret.FacilityTurret
"FacilityTurretObjectBuilder" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(
Props(
classOf[ServerObjectBuilderTest.BuilderTestActor],
ServerObjectBuilder(1, FacilityTurret.Constructor(manned_turret)),
hub
),
"manned-turret"
)
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[FacilityTurret])
assert(reply.asInstanceOf[FacilityTurret].HasGUID)
assert(reply.asInstanceOf[FacilityTurret].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
val obj = ServerObjectBuilder(1, FacilityTurret.Constructor(manned_turret)).Build(context, hub)
assert(obj.isInstanceOf[FacilityTurret])
assert(obj.HasGUID)
assert(obj.GUID == PlanetSideGUID(1))
assert(obj == hub(1).get)
}
}
}
object ServerObjectBuilderTest {
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
def NumberPoolHub: NumberPoolHub = {
val obj = new NumberPoolHub(new LimitedNumberSource(2))
val obj = new NumberPoolHub(new MaxNumberSource(2))
obj
}
class BuilderTestActor(builder: ServerObjectBuilder[_], hub: NumberPoolHub) extends Actor {
def receive: Receive = {
case _ =>
sender() ! builder.Build(context, hub)
}
}
class BuildingTestActor(
structure_con: (String, Int, Int, Zone, ActorContext) => Building,
name: String,
building_guid: Int,
map_id: Int,
zone: Zone
) extends Actor {
def receive: Receive = {
case _ =>
sender() ! FoundationBuilder(structure_con).Build(name, building_guid, map_id, zone)(context)
}
}
}

View file

@ -7,7 +7,7 @@ import base.{ActorTest, FreedContextActorTest}
import net.psforever.objects._
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles._
import net.psforever.objects.vital.VehicleShieldCharge
@ -408,7 +408,7 @@ class VehicleControlPrepareForDeletionPassengerTest extends ActorTest {
class VehicleControlPrepareForDeletionMountedInTest extends FreedContextActorTest {
ServiceManager.boot
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
GUID(guid)
@ -534,7 +534,7 @@ class VehicleControlPrepareForDeletionMountedInTest extends FreedContextActorTes
}
class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
ServiceManager.boot
val zone = new Zone("test", new ZoneMap("test"), 0) {
GUID(guid)

View file

@ -7,7 +7,7 @@ import base.ActorTest
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects._
@ -104,7 +104,7 @@ 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))
val guid1: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(100))
zone.GUID(guid1) mustEqual true
zone.AddPool("pool1", (0 to 50).toList)
zone.AddPool("pool2", (51 to 75).toList)
@ -114,7 +114,7 @@ class ZoneTest extends Specification {
registration.isSuccess mustEqual true
guid1.WhichPool(obj).contains("pool2") mustEqual true
zone.GUID(new NumberPoolHub(new LimitedNumberSource(150))) mustEqual false
zone.GUID(new NumberPoolHub(new MaxNumberSource(150))) mustEqual false
}
}
}
@ -123,7 +123,7 @@ class ZoneActorTest extends ActorTest {
"Zone" should {
"refuse new number pools after the Actor is started" in {
val zone = new Zone("test", new ZoneMap("map6"), 1) { override def SetupNumberPools() = {} }
zone.GUID(new NumberPoolHub(new LimitedNumberSource(40150)))
zone.GUID(new NumberPoolHub(new MaxNumberSource(40150)))
zone.actor = system.spawn(ZoneActor(zone), "test-add-pool-actor-init")
expectNoMessage(Duration.create(500, "ms"))
@ -133,7 +133,7 @@ class ZoneActorTest extends ActorTest {
"refuse to remove number pools after the Actor is started" in {
val zone = new Zone("test", new ZoneMap("map6"), 1) { override def SetupNumberPools() = {} }
zone.GUID(new NumberPoolHub(new LimitedNumberSource(10)))
zone.GUID(new NumberPoolHub(new MaxNumberSource(10)))
zone.AddPool("test", 1 to 2)
zone.actor = system.spawn(ZoneActor(zone), "test-remove-pool-actor-init")
expectNoMessage(Duration.create(300, "ms"))
@ -482,7 +482,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 MaxNumberSource(20))
hub.register(item, 10)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} }
zone.GUID(hub)
@ -507,7 +507,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 MaxNumberSource(20))
//hub.register(item, 10) //!important
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} }
zone.GUID(hub)
@ -532,7 +532,7 @@ class ZoneGroundCanNotDropItem1Test extends ActorTest {
class ZoneGroundCanNotDropItem2Test extends ActorTest {
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
val hub = new NumberPoolHub(new LimitedNumberSource(20))
val hub = new NumberPoolHub(new MaxNumberSource(20))
hub.register(item, 10) //!important
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} }
//zone.GUID(hub) //!important
@ -557,7 +557,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 MaxNumberSource(20))
hub.register(item, 10) //!important
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} }
zone.GUID(hub) //!important
@ -590,7 +590,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 MaxNumberSource(20))
hub.register(item, 10)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} }
zone.GUID(hub)
@ -618,7 +618,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 MaxNumberSource(20))
hub.register(item, 10)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} }
zone.GUID(hub) //still registered to this zone
@ -642,7 +642,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 MaxNumberSource(20))
hub.register(item, 10)
val zone = new Zone("test", new ZoneMap("test-map"), 0) { override def SetupNumberPools() = {} }
zone.GUID(hub) //still registered to this zone

View file

@ -5,6 +5,7 @@ import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
class GUIDTaskRegisterAvatarTest extends ActorTest {
@ -26,7 +27,7 @@ class GUIDTaskRegisterAvatarTest extends ActorTest {
assert(!obj_wep_ammo.HasGUID)
assert(!obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(!obj_locker_ammo.HasGUID)
assert(obj_locker_ammo.HasGUID)
taskResolver ! TaskResolver.GiveTask(
new GUIDTaskTest.RegisterTestTask(probe.ref),
List(GUIDTask.RegisterAvatar(obj)(uns))

View file

@ -5,6 +5,7 @@ import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
class GUIDTaskRegisterPlayerTest extends ActorTest {
@ -26,7 +27,7 @@ class GUIDTaskRegisterPlayerTest extends ActorTest {
assert(!obj_wep_ammo.HasGUID)
assert(!obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(!obj_locker_ammo.HasGUID)
assert(obj_locker_ammo.HasGUID)
taskResolver ! TaskResolver.GiveTask(
new GUIDTaskTest.RegisterTestTask(probe.ref),
List(GUIDTask.RegisterPlayer(obj)(uns))
@ -37,6 +38,6 @@ class GUIDTaskRegisterPlayerTest extends ActorTest {
assert(obj_wep_ammo.HasGUID)
assert(obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(!obj_locker_ammo.HasGUID)
assert(obj_locker_ammo.HasGUID)
}
}

View file

@ -8,7 +8,7 @@ import akka.testkit.TestProbe
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.actor.{NumberPoolActor, UniqueNumberSystem}
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.guid.{NumberPoolHub, Task, TaskResolver}
object GUIDTaskTest {
@ -26,7 +26,7 @@ object GUIDTaskTest {
import akka.routing.RandomPool
import akka.testkit.TestProbe
val guid: NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(110))
val guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(110))
guid.AddPool("dynamic", (1 to 100).toList).Selector = new RandomSelector //TODO name is hardcoded for now
val uns = system.actorOf(
RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, GUIDTaskTest.AllocateNumberPoolActors(guid))),

View file

@ -5,6 +5,7 @@ import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
class GUIDTaskUnregisterAvatarTest extends ActorTest {
@ -43,6 +44,6 @@ class GUIDTaskUnregisterAvatarTest extends ActorTest {
assert(!obj_wep_ammo.HasGUID)
assert(!obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(!obj_locker_ammo.HasGUID)
assert(obj_locker_ammo.HasGUID)
}
}

View file

@ -5,6 +5,7 @@ import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
class GUIDTaskUnregisterPlayerTest extends ActorTest {

View file

@ -4,7 +4,7 @@ package objects.number
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.types.PlanetSideGUID
import org.specs2.mutable.Specification
@ -20,17 +20,17 @@ class NumberPoolHubTest extends Specification {
"NumberPoolHub" should {
"construct" in {
new NumberPoolHub(new LimitedNumberSource(51))
new NumberPoolHub(new MaxNumberSource(51))
ok
}
"get a pool" in {
val obj = new NumberPoolHub(new LimitedNumberSource(51))
val obj = new NumberPoolHub(new MaxNumberSource(51))
obj.GetPool("generic").isDefined mustEqual true //default pool
}
"add a pool" in {
val obj = new NumberPoolHub(new LimitedNumberSource(51))
val obj = new NumberPoolHub(new MaxNumberSource(51))
obj.Numbers.isEmpty mustEqual true
obj.AddPool("fibonacci", numberList)
obj.Numbers.toSet.equals(numberList.toSet) mustEqual true
@ -40,7 +40,7 @@ class NumberPoolHubTest extends Specification {
}
"enumerate the content of all pools" in {
val obj = new NumberPoolHub(new LimitedNumberSource(51))
val obj = new NumberPoolHub(new MaxNumberSource(51))
obj.AddPool("fibonacci1", numberList1)
obj.AddPool("fibonacci2", numberList2)
numberSet1.intersect(obj.Numbers.toSet) mustEqual numberSet1
@ -49,7 +49,7 @@ class NumberPoolHubTest extends Specification {
}
"remove a pool" in {
val obj = new NumberPoolHub(new LimitedNumberSource(51))
val obj = new NumberPoolHub(new MaxNumberSource(51))
obj.Numbers.isEmpty mustEqual true
obj.AddPool("fibonacci", numberList)
obj.Numbers.toSet.equals(numberList.toSet) mustEqual true
@ -59,19 +59,19 @@ class NumberPoolHubTest extends Specification {
}
"block removing the default 'generic' pool" in {
val obj = new NumberPoolHub(new LimitedNumberSource(51))
val obj = new NumberPoolHub(new MaxNumberSource(51))
obj.RemovePool("generic") must throwA[IllegalArgumentException]
}
"block adding pools that use already-included numbers" in {
val obj = new NumberPoolHub(new LimitedNumberSource(51))
val obj = new NumberPoolHub(new MaxNumberSource(51))
obj.AddPool("fibonacci1", numberList)
val numberList4 = 3 :: 7 :: 21 :: 34 :: 45 :: Nil
obj.AddPool("fibonacci2", numberList4) must throwA[IllegalArgumentException]
}
"enumerate only the content of all current pools" in {
val obj = new NumberPoolHub(new LimitedNumberSource(51))
val obj = new NumberPoolHub(new MaxNumberSource(51))
obj.AddPool("fibonacci1", numberList1)
obj.AddPool("fibonacci2", numberList2)
numberSet1.intersect(obj.Numbers.toSet) mustEqual numberSet1
@ -82,7 +82,7 @@ class NumberPoolHubTest extends Specification {
}
"register an object to a pool" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci", numberList)
val obj = new EntityTestClass()
obj.GUID must throwA[Exception]
@ -95,7 +95,7 @@ class NumberPoolHubTest extends Specification {
}
"lookup a registered object" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci", numberList)
val obj = new EntityTestClass()
hub.register(obj, "fibonacci") match {
@ -108,14 +108,14 @@ class NumberPoolHubTest extends Specification {
}
"lookup the pool of a(n unassigned) number" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci1", numberList1)
hub.AddPool("fibonacci2", numberList2)
hub.WhichPool(13).contains("fibonacci2") mustEqual true
}
"lookup the pool of a registered object" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci", numberList1)
val obj = new EntityTestClass()
hub.register(obj, "fibonacci")
@ -123,7 +123,7 @@ class NumberPoolHubTest extends Specification {
}
"register an object to a specific, unused number; it is assigned to pool 'generic'" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci", numberList1)
val obj = new EntityTestClass()
obj.GUID must throwA[Exception]
@ -137,7 +137,7 @@ class NumberPoolHubTest extends Specification {
}
"register an object to a specific, pooled number (list 1)" in {
val src = new LimitedNumberSource(51)
val src = new MaxNumberSource(51)
val hub = new NumberPoolHub(src)
hub.AddPool("fibonacci", numberList)
val obj = new EntityTestClass()
@ -146,14 +146,14 @@ class NumberPoolHubTest extends Specification {
case Success(number) =>
obj.GUID mustEqual PlanetSideGUID(number)
hub.WhichPool(obj).contains("fibonacci") mustEqual true
src.Available(5).isEmpty mustEqual true
src.getAvailable(5).isEmpty mustEqual true
case _ =>
ko
}
}
"register an object to a specific, pooled number (list 2)" in {
val src = new LimitedNumberSource(51)
val src = new MaxNumberSource(51)
val hub = new NumberPoolHub(src)
hub.AddPool("fibonacci", numberList2)
val obj = new EntityTestClass()
@ -162,21 +162,21 @@ class NumberPoolHubTest extends Specification {
case Success(number) =>
obj.GUID mustEqual PlanetSideGUID(number)
hub.WhichPool(obj).contains("fibonacci") mustEqual true
src.Available(13).isEmpty mustEqual true
src.getAvailable(13).isEmpty mustEqual true
case _ =>
ko
}
}
"register an object without extra specifications; it is assigned to pool 'generic'" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
val obj = new EntityTestClass()
hub.register(obj)
hub.WhichPool(obj).contains("generic") mustEqual true
}
"unregister an object" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci", numberList)
val obj = new EntityTestClass()
obj.HasGUID mustEqual false
@ -190,7 +190,7 @@ class NumberPoolHubTest extends Specification {
}
"not register an object to a different pool" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci1", numberList1)
hub.AddPool("fibonacci2", numberList2)
val obj = new EntityTestClass()
@ -200,17 +200,17 @@ class NumberPoolHubTest extends Specification {
}
"fail to unregister an object that is not registered to this hub" in {
val hub1 = new NumberPoolHub(new LimitedNumberSource(51))
val hub2 = new NumberPoolHub(new LimitedNumberSource(51))
val hub1 = new NumberPoolHub(new MaxNumberSource(51))
val hub2 = new NumberPoolHub(new MaxNumberSource(51))
hub1.AddPool("fibonacci", numberList)
hub2.AddPool("fibonacci", numberList)
val obj = new EntityTestClass()
hub1.register(obj, "fibonacci")
hub2.unregister(obj) must throwA[Exception]
hub2.unregister(obj).isFailure mustEqual true
}
"pre-register a specific, unused number" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.register(13) match {
case Success(_) =>
ok
@ -220,7 +220,7 @@ class NumberPoolHubTest extends Specification {
}
"pre-register a specific, pooled number" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci", numberList)
hub.register(13) match {
case Success(key) =>
@ -231,7 +231,7 @@ class NumberPoolHubTest extends Specification {
}
"pre-register a number from a known pool" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci", numberList).Selector = new RandomSelector
hub.register("fibonacci") match {
case Success(key) =>
@ -242,7 +242,7 @@ class NumberPoolHubTest extends Specification {
}
"unregister a number" in {
val hub = new NumberPoolHub(new LimitedNumberSource(51))
val hub = new NumberPoolHub(new MaxNumberSource(51))
hub.AddPool("fibonacci", numberList).Selector = new RandomSelector //leave this tagged on
val obj = new EntityTestClass()
hub.register(13) match {
@ -263,48 +263,48 @@ class NumberPoolHubTest extends Specification {
}
"not affect the hidden restricted pool by adding a new pool" in {
val src = new LimitedNumberSource(51)
src.Restrict(4)
src.Restrict(8) //in fibonacci
src.Restrict(10)
src.Restrict(12)
val src = new MaxNumberSource(51)
src.restrictNumber(4)
src.restrictNumber(8) //in fibonacci
src.restrictNumber(10)
src.restrictNumber(12)
val hub = new NumberPoolHub(src)
hub.AddPool("fibonacci", numberList) must throwA[IllegalArgumentException]
}
"not register an object to a number belonging to the restricted pool" in {
val src = new LimitedNumberSource(51)
src.Restrict(4)
val src = new MaxNumberSource(51)
src.restrictNumber(4)
val hub = new NumberPoolHub(src)
val obj = new EntityTestClass()
hub.register(obj, 4).isFailure mustEqual true
}
"not register an object to the restricted pool directly" in {
val src = new LimitedNumberSource(51)
// src.Restrict(4)
val src = new MaxNumberSource(51)
// src.restrictNumber(4)
val hub = new NumberPoolHub(src)
val obj = new EntityTestClass()
hub.register(obj, "").isFailure mustEqual true //the empty string represents the restricted pool
}
"not register a number belonging to the restricted pool" in {
val src = new LimitedNumberSource(51)
src.Restrict(4)
val src = new MaxNumberSource(51)
src.restrictNumber(4)
val hub = new NumberPoolHub(src)
hub.register(4).isFailure mustEqual true
}
"not unregister a number belonging to the restricted pool" in {
val src = new LimitedNumberSource(51)
src.Restrict(4)
val src = new MaxNumberSource(51)
src.restrictNumber(4)
val hub = new NumberPoolHub(src)
hub.unregister(4).isFailure mustEqual true
}
"identity an object that is registered to it" in {
val hub1 = new NumberPoolHub(new LimitedNumberSource(10))
val hub2 = new NumberPoolHub(new LimitedNumberSource(10))
val hub1 = new NumberPoolHub(new MaxNumberSource(10))
val hub2 = new NumberPoolHub(new MaxNumberSource(10))
val obj1 = new EntityTestClass()
val obj2 = new EntityTestClass()
hub1.register(obj1)
@ -317,15 +317,15 @@ class NumberPoolHubTest extends Specification {
}
"identity a number that is registered to it" in {
val src1 = new LimitedNumberSource(5)
val src1 = new MaxNumberSource(5)
val hub1 = new NumberPoolHub(src1)
val src2 = new LimitedNumberSource(10)
src2.Restrict(0)
src2.Restrict(1)
src2.Restrict(2)
src2.Restrict(3)
src2.Restrict(4)
src2.Restrict(5)
val src2 = new MaxNumberSource(10)
src2.restrictNumber(0)
src2.restrictNumber(1)
src2.restrictNumber(2)
src2.restrictNumber(3)
src2.restrictNumber(4)
src2.restrictNumber(5)
val hub2 = new NumberPoolHub(src2)
val obj1 = new EntityTestClass()
val obj2 = new EntityTestClass()

View file

@ -10,62 +10,71 @@ class NumberSourceTest extends Specification {
import net.psforever.objects.entity.IdentifiableEntity
private class TestClass extends IdentifiableEntity
"LimitedNumberSource" should {
import net.psforever.objects.guid.source.LimitedNumberSource
"MaxNumberSource" should {
import net.psforever.objects.guid.source.MaxNumberSource
"construct" in {
val obj = LimitedNumberSource(25)
obj.Size mustEqual 26
obj.CountAvailable mustEqual 26
obj.CountUsed mustEqual 0
val obj = MaxNumberSource(25)
obj.size mustEqual 26
obj.countAvailable mustEqual 26
obj.countUsed mustEqual 0
}
"get a number" in {
val obj = LimitedNumberSource(25)
val result: Option[LoanedKey] = obj.Available(5)
"construct failure (negative max value)" in {
MaxNumberSource(-1) must throwA[IllegalArgumentException]
}
"get any number (failure)" in {
val obj = MaxNumberSource(25)
obj.getAvailable(number = 50).isDefined mustEqual false
}
"get a valid number" in {
val obj = MaxNumberSource(25)
val result: Option[LoanedKey] = obj.getAvailable(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object.isEmpty mustEqual true
obj.Size mustEqual 26
obj.CountAvailable mustEqual 25
obj.CountUsed mustEqual 1
obj.size mustEqual 26
obj.countAvailable mustEqual 25
obj.countUsed mustEqual 1
}
"assign the number" in {
val obj = LimitedNumberSource(25)
val result: Option[LoanedKey] = obj.Available(5)
val obj = MaxNumberSource(25)
val result: Option[LoanedKey] = obj.getAvailable(5)
result.isDefined mustEqual true
result.get.Object = new TestClass()
ok
}
"return a number (unused)" in {
val obj = LimitedNumberSource(25)
val result: Option[LoanedKey] = obj.Available(5)
val obj = MaxNumberSource(25)
val result: Option[LoanedKey] = obj.getAvailable(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
obj.CountUsed mustEqual 1
val ret = obj.Return(result.get)
obj.countUsed mustEqual 1
val ret = obj.returnNumber(result.get)
ret.isEmpty mustEqual true
obj.CountUsed mustEqual 0
obj.countUsed mustEqual 0
}
"return a number (assigned)" in {
val obj = LimitedNumberSource(25)
val obj = MaxNumberSource(25)
val test = new TestClass()
val result: Option[LoanedKey] = obj.Available(5)
val result: Option[LoanedKey] = obj.getAvailable(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Object = test
obj.CountUsed mustEqual 1
val ret = obj.Return(result.get)
obj.countUsed mustEqual 1
val ret = obj.returnNumber(result.get)
ret.contains(test) mustEqual true
obj.CountUsed mustEqual 0
obj.countUsed mustEqual 0
}
"restrict a number (unassigned)" in {
val obj = LimitedNumberSource(25)
val result: Option[LoanedKey] = obj.Restrict(5)
val obj = MaxNumberSource(25)
val result: Option[LoanedKey] = obj.restrictNumber(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Restricted
@ -73,10 +82,10 @@ class NumberSourceTest extends Specification {
}
"restrict a number (assigned + multiple assignments)" in {
val obj = LimitedNumberSource(25)
val obj = MaxNumberSource(25)
val test1 = new TestClass()
val test2 = new TestClass()
val result: Option[LoanedKey] = obj.Restrict(5)
val result: Option[LoanedKey] = obj.restrictNumber(5)
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Restricted
result.get.Object.isEmpty mustEqual true
@ -89,108 +98,309 @@ class NumberSourceTest extends Specification {
}
"return a restricted number (correctly fail)" in {
val obj = LimitedNumberSource(25)
val obj = MaxNumberSource(25)
val test = new TestClass()
val result: Option[LoanedKey] = obj.Restrict(5)
val result: Option[LoanedKey] = obj.restrictNumber(5)
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Restricted
result.get.Object = test
obj.Return(5)
val result2: Option[SecureKey] = obj.Get(5)
obj.returnNumber(5)
val result2: Option[SecureKey] = obj.get(5)
result2.get.GUID mustEqual 5
result2.get.Policy mustEqual AvailabilityPolicy.Restricted
result2.get.Object.contains(test) mustEqual true
}
"return a secure key" in {
val obj = LimitedNumberSource(25)
val obj = MaxNumberSource(25)
val test = new TestClass()
val result1: Option[LoanedKey] = obj.Available(5)
val result1: Option[LoanedKey] = obj.getAvailable(5)
result1.get.Object = test
test.GUID mustEqual PlanetSideGUID(5)
val result2: Option[SecureKey] = obj.Get(5)
obj.Return(result2.get).contains(test) mustEqual true
val result2: Option[SecureKey] = obj.get(5)
obj.returnNumber(result2.get).contains(test) mustEqual true
}
"restrict a previously-assigned number" in {
val obj = LimitedNumberSource(25)
val obj = MaxNumberSource(25)
val test = new TestClass()
val result1: Option[LoanedKey] = obj.Available(5)
val result1: Option[LoanedKey] = obj.getAvailable(5)
result1.isDefined mustEqual true
result1.get.Policy mustEqual AvailabilityPolicy.Leased
result1.get.Object = test
val result2: Option[LoanedKey] = obj.Restrict(5)
val result2: Option[LoanedKey] = obj.restrictNumber(5)
result2.isDefined mustEqual true
result2.get.Policy mustEqual AvailabilityPolicy.Restricted
result2.get.Object.contains(test) mustEqual true
}
"check a number (not previously gotten)" in {
val obj = LimitedNumberSource(25)
val result2: Option[SecureKey] = obj.Get(5)
val obj = MaxNumberSource(25)
val result2: Option[SecureKey] = obj.get(5)
result2.get.GUID mustEqual 5
result2.get.Policy mustEqual AvailabilityPolicy.Available
result2.get.Object.isEmpty mustEqual true
}
"check a number (previously gotten)" in {
val obj = LimitedNumberSource(25)
val result: Option[LoanedKey] = obj.Available(5)
val obj = MaxNumberSource(25)
val result: Option[LoanedKey] = obj.getAvailable(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object.isEmpty mustEqual true
val result2: Option[SecureKey] = obj.Get(5)
val result2: Option[SecureKey] = obj.get(5)
result2.get.GUID mustEqual 5
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object.isEmpty mustEqual true
}
"check a number (assigned)" in {
val obj = LimitedNumberSource(25)
val result: Option[LoanedKey] = obj.Available(5)
val obj = MaxNumberSource(25)
val result: Option[LoanedKey] = obj.getAvailable(5)
result.isDefined mustEqual true
result.get.GUID mustEqual 5
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object = new TestClass()
val result2: Option[SecureKey] = obj.Get(5)
val result2: Option[SecureKey] = obj.get(5)
result2.get.GUID mustEqual 5
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object mustEqual result.get.Object
}
"check a number (assigned and returned)" in {
val obj = LimitedNumberSource(25)
val obj = MaxNumberSource(25)
val test = new TestClass()
val result: Option[LoanedKey] = obj.Available(5)
val result: Option[LoanedKey] = obj.getAvailable(5)
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object = test
val result2: Option[SecureKey] = obj.Get(5)
val result2: Option[SecureKey] = obj.get(5)
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object.get mustEqual test
obj.Return(5).contains(test) mustEqual true
val result3: Option[SecureKey] = obj.Get(5)
obj.returnNumber(5).contains(test) mustEqual true
val result3: Option[SecureKey] = obj.get(5)
result3.get.Policy mustEqual AvailabilityPolicy.Available
result3.get.Object.isEmpty mustEqual true
}
"clear" in {
val obj = LimitedNumberSource(25)
val obj = MaxNumberSource(25)
val test1 = new TestClass()
val test2 = new TestClass()
val test3 = new TestClass()
obj.Available(5) //no assignment
obj.Available(10).get.Object = test1
obj.Available(15).get.Object = test2
obj.Restrict(15)
obj.Restrict(20).get.Object = test3
obj.CountUsed mustEqual 4
obj.getAvailable(5) //no assignment
obj.getAvailable(10).get.Object = test1
obj.getAvailable(15).get.Object = test2
obj.restrictNumber(15)
obj.restrictNumber(20).get.Object = test3
obj.countUsed mustEqual 4
val list: List[IdentifiableEntity] = obj.Clear()
obj.CountUsed mustEqual 0
val list: List[IdentifiableEntity] = obj.clear()
obj.countUsed mustEqual 0
list.size mustEqual 3
list.count(obj => obj == test1) mustEqual 1
list.count(obj => obj == test2) mustEqual 1
list.count(obj => obj == test3) mustEqual 1
}
}
"SpecificNumberSource" should {
import net.psforever.objects.guid.source.SpecificNumberSource
"construct" in {
val obj = SpecificNumberSource(List(25))
obj.size mustEqual 1
obj.countAvailable mustEqual 1
obj.countUsed mustEqual 0
}
"construct failure (no values)" in {
SpecificNumberSource(List()) must throwA[IllegalArgumentException]
}
"construct failure (at least one value is negative)" in {
SpecificNumberSource(List(0, 1, -1, 2, 3)) must throwA[IllegalArgumentException]
}
"get any number (failure)" in {
val obj = SpecificNumberSource(List(25))
obj.getAvailable(number = 5).isDefined mustEqual false
}
"get specific number (success)" in {
val obj = SpecificNumberSource(List(25))
val result: Option[LoanedKey] = obj.getAvailable(25)
result.isDefined mustEqual true
result.get.GUID mustEqual 25
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object.isEmpty mustEqual true
obj.size mustEqual 1
obj.countAvailable mustEqual 0
obj.countUsed mustEqual 1
}
"assign the number" in {
val obj = SpecificNumberSource(List(25))
val result: Option[LoanedKey] = obj.getAvailable(number = 25)
result.isDefined mustEqual true
result.get.Object = new TestClass()
ok
}
"return a number (unused)" in {
val obj = SpecificNumberSource(List(25))
val result: Option[LoanedKey] = obj.getAvailable(number = 25)
result.isDefined mustEqual true
result.get.GUID mustEqual 25
obj.countUsed mustEqual 1
val ret = obj.returnNumber(result.get)
ret.isEmpty mustEqual true
obj.countUsed mustEqual 0
}
"return a number (assigned)" in {
val obj = SpecificNumberSource(List(25))
val test = new TestClass()
val result: Option[LoanedKey] = obj.getAvailable(number = 25)
result.isDefined mustEqual true
result.get.GUID mustEqual 25
result.get.Object = test
obj.countUsed mustEqual 1
val ret = obj.returnNumber(result.get)
ret.contains(test) mustEqual true
obj.countUsed mustEqual 0
}
"restrict a number (unassigned)" in {
val obj = SpecificNumberSource(List(25))
val result: Option[LoanedKey] = obj.restrictNumber(number = 25)
result.isDefined mustEqual true
result.get.GUID mustEqual 25
result.get.Policy mustEqual AvailabilityPolicy.Restricted
result.get.Object.isEmpty mustEqual true
}
"restrict a number (assigned + multiple assignments)" in {
val obj = SpecificNumberSource(List(25, 26))
val test1 = new TestClass()
val test2 = new TestClass()
val result: Option[LoanedKey] = obj.restrictNumber(number = 25)
result.get.GUID mustEqual 25
result.get.Policy mustEqual AvailabilityPolicy.Restricted
result.get.Object.isEmpty mustEqual true
result.get.Object = None //assignment 1
result.get.Object.isEmpty mustEqual true //still unassigned
result.get.Object = test1 //assignment 2
result.get.Object.contains(test1) mustEqual true
result.get.Object = test2 //assignment 3
result.get.Object.contains(test1) mustEqual true //same as above
}
"return a restricted number (correctly fail)" in {
val obj = SpecificNumberSource(List(25))
val test = new TestClass()
val result: Option[LoanedKey] = obj.restrictNumber(number = 25)
result.get.GUID mustEqual 25
result.get.Policy mustEqual AvailabilityPolicy.Restricted
result.get.Object = test
obj.returnNumber(number = 25)
val result2: Option[SecureKey] = obj.get(25)
result2.get.GUID mustEqual 25
result2.get.Policy mustEqual AvailabilityPolicy.Restricted
result2.get.Object.contains(test) mustEqual true
}
"return a secure key" in {
val obj = SpecificNumberSource(List(25))
val test = new TestClass()
val result1: Option[LoanedKey] = obj.getAvailable(number = 25)
result1.get.Object = test
test.GUID mustEqual PlanetSideGUID(25)
val result2: Option[SecureKey] = obj.get(25)
obj.returnNumber(result2.get).contains(test) mustEqual true
}
"restrict a previously-assigned number" in {
val obj = SpecificNumberSource(List(25))
val test = new TestClass()
val result1: Option[LoanedKey] = obj.getAvailable(number = 25)
result1.isDefined mustEqual true
result1.get.Policy mustEqual AvailabilityPolicy.Leased
result1.get.Object = test
val result2: Option[LoanedKey] = obj.restrictNumber(number = 25)
result2.isDefined mustEqual true
result2.get.Policy mustEqual AvailabilityPolicy.Restricted
result2.get.Object.contains(test) mustEqual true
}
"check a number (not previously gotten)" in {
val obj = SpecificNumberSource(List(25))
val result2: Option[SecureKey] = obj.get(25)
result2.get.GUID mustEqual 25
result2.get.Policy mustEqual AvailabilityPolicy.Available
result2.get.Object.isEmpty mustEqual true
}
"check a number (previously gotten)" in {
val obj = SpecificNumberSource(List(25))
val result: Option[LoanedKey] = obj.getAvailable(number = 25)
result.isDefined mustEqual true
result.get.GUID mustEqual 25
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object.isEmpty mustEqual true
val result2: Option[SecureKey] = obj.get(25)
result2.get.GUID mustEqual 25
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object.isEmpty mustEqual true
}
"check a number (assigned)" in {
val obj = SpecificNumberSource(List(25))
val result: Option[LoanedKey] = obj.getAvailable(number = 25)
result.isDefined mustEqual true
result.get.GUID mustEqual 25
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object = new TestClass()
val result2: Option[SecureKey] = obj.get(25)
result2.get.GUID mustEqual 25
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object mustEqual result.get.Object
}
"check a number (assigned and returned)" in {
val obj = SpecificNumberSource(List(25))
val test = new TestClass()
val result: Option[LoanedKey] = obj.getAvailable(number = 25)
result.get.Policy mustEqual AvailabilityPolicy.Leased
result.get.Object = test
val result2: Option[SecureKey] = obj.get(25)
result2.get.Policy mustEqual AvailabilityPolicy.Leased
result2.get.Object.get mustEqual test
obj.returnNumber(number = 25).contains(test) mustEqual true
val result3: Option[SecureKey] = obj.get(25)
result3.get.Policy mustEqual AvailabilityPolicy.Available
result3.get.Object.isEmpty mustEqual true
}
"clear" in {
val obj = SpecificNumberSource(List(25, 26, 27, 28, 29, 30))
val test1 = new TestClass()
val test2 = new TestClass()
val test3 = new TestClass()
obj.getAvailable(25) //no assignment
obj.getAvailable(26).get.Object = test1
obj.getAvailable(28).get.Object = test2
obj.restrictNumber(28)
obj.restrictNumber(30).get.Object = test3
obj.countUsed mustEqual 4
val list: List[IdentifiableEntity] = obj.clear()
obj.countUsed mustEqual 0
list.size mustEqual 3
list.count(obj => obj == test1) mustEqual 1
list.count(obj => obj == test2) mustEqual 1

View file

@ -7,7 +7,7 @@ import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.{NumberPoolActor, Register, UniqueNumberSystem, Unregister}
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.types.PlanetSideGUID
import scala.concurrent.duration._
@ -15,7 +15,7 @@ import scala.util.{Failure, Success}
class AllocateNumberPoolActors extends ActorTest {
"AllocateNumberPoolActors" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList)
guid.AddPool("pool2", (3001 to 4000).toList)
@ -32,7 +32,7 @@ class AllocateNumberPoolActors extends ActorTest {
class UniqueNumberSystemTest extends ActorTest() {
"UniqueNumberSystem" should {
"constructor" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList)
guid.AddPool("pool2", (3001 to 4000).toList)
@ -51,7 +51,7 @@ class UniqueNumberSystemTest1 extends ActorTest() {
"UniqueNumberSystem" should {
"Register (success)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
val pool1 = (1001 to 2000).toList
val pool2 = (3001 to 4000).toList
@ -63,7 +63,7 @@ class UniqueNumberSystemTest1 extends ActorTest() {
Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
"uns"
)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
//pool1
for (_ <- 1 to 100) {
val testObj = new EntityTestClass()
@ -88,7 +88,7 @@ class UniqueNumberSystemTest1 extends ActorTest() {
assert(msg.isInstanceOf[Success[_]])
assert(pool3.contains(testObj.GUID.guid))
}
assert(src.CountUsed == 300)
assert(src.countUsed == 300)
}
}
}
@ -98,7 +98,7 @@ class UniqueNumberSystemTest2 extends ActorTest() {
"UniqueNumberSystem" should {
"Register (success; already registered)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
@ -109,20 +109,20 @@ class UniqueNumberSystemTest2 extends ActorTest() {
)
val testObj = new EntityTestClass()
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
uns ! Register(testObj, "pool1")
val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 1)
assert(src.countUsed == 1)
val id = testObj.GUID.guid
uns ! Register(testObj, "pool2") //different pool; makes no difference
val msg2 = receiveOne(Duration.create(500, "ms"))
assert(msg2.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 1)
assert(src.countUsed == 1)
assert(testObj.GUID.guid == id) //unchanged
}
}
@ -134,7 +134,7 @@ class UniqueNumberSystemTest3 extends ActorTest() {
"UniqueNumberSystem" should {
"Register (failure; no pool)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
@ -145,13 +145,13 @@ class UniqueNumberSystemTest3 extends ActorTest() {
)
val testObj = new EntityTestClass()
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
uns ! Register(testObj, "pool4")
val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
}
}
}
@ -161,7 +161,7 @@ class UniqueNumberSystemTest4 extends ActorTest() {
"UniqueNumberSystem" should {
"Register (failure; empty pool)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
@ -190,7 +190,7 @@ class UniqueNumberSystemTest5 extends ActorTest() {
"UniqueNumberSystem" should {
"Unregister (success)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
val pool2 = (3001 to 4000).toList
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
@ -202,20 +202,20 @@ class UniqueNumberSystemTest5 extends ActorTest() {
)
val testObj = new EntityTestClass()
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
uns ! Register(testObj, "pool2")
val msg1 = receiveOne(Duration.create(2000, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(pool2.contains(testObj.GUID.guid))
assert(src.CountUsed == 1)
assert(src.countUsed == 1)
uns ! Unregister(testObj)
val msg2 = receiveOne(Duration.create(2000, "ms"))
assert(msg2.isInstanceOf[Success[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
}
}
}
@ -225,7 +225,7 @@ class UniqueNumberSystemTest6 extends ActorTest() {
"UniqueNumberSystem" should {
"Unregister (success; object not registered at all)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
@ -236,13 +236,13 @@ class UniqueNumberSystemTest6 extends ActorTest() {
)
val testObj = new EntityTestClass()
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
uns ! Unregister(testObj)
val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
}
}
}
@ -252,7 +252,7 @@ class UniqueNumberSystemTest7 extends ActorTest() {
"UniqueNumberSystem" should {
"Unregister (failure; number not in system)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
@ -264,13 +264,13 @@ class UniqueNumberSystemTest7 extends ActorTest() {
val testObj = new EntityTestClass()
testObj.GUID = PlanetSideGUID(6001) //fake registering; number too high
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
uns ! Unregister(testObj)
val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
}
}
}
@ -280,7 +280,7 @@ class UniqueNumberSystemTest8 extends ActorTest() {
"UniqueNumberSystem" should {
"Unregister (failure; object is not registered to that number)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
@ -292,13 +292,13 @@ class UniqueNumberSystemTest8 extends ActorTest() {
val testObj = new EntityTestClass()
testObj.GUID = PlanetSideGUID(3500) //fake registering
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
uns ! Unregister(testObj)
val msg1 = receiveOne(Duration.create(500, "ms"))
assert(msg1.isInstanceOf[Failure[_]])
assert(testObj.HasGUID)
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
}
}
}
@ -308,7 +308,7 @@ class UniqueNumberSystemTest9 extends ActorTest() {
"UniqueNumberSystem" should {
"Failures (manually walking the failure cases)" in {
val src: LimitedNumberSource = LimitedNumberSource(6000)
val src: MaxNumberSource = MaxNumberSource(6000)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
@ -345,7 +345,7 @@ class UniqueNumberSystemTestA extends ActorTest {
"UniqueNumberSystem" should {
"remain consistent between registrations" in {
val src: LimitedNumberSource = LimitedNumberSource(10)
val src: MaxNumberSource = MaxNumberSource(10)
val guid: NumberPoolHub = new NumberPoolHub(src)
guid.AddPool("pool1", (0 until 10).toList).Selector = new RandomSelector
val uns = system.actorOf(
@ -354,9 +354,9 @@ class UniqueNumberSystemTestA extends ActorTest {
)
expectNoMessage(Duration.create(200, "ms"))
assert(src.CountUsed == 0)
assert(src.countUsed == 0)
(0 to 4).foreach(i => { assert(guid.register(new EntityTestClass(), i).isSuccess) })
assert(src.CountUsed == 5)
assert(src.countUsed == 5)
(0 to 5).foreach(_ => { uns ! Register(new EntityTestClass(), "pool1") })
assert(receiveOne(200 milliseconds).isInstanceOf[Success[_]]) //6th
@ -365,7 +365,7 @@ class UniqueNumberSystemTestA extends ActorTest {
assert(receiveOne(200 milliseconds).isInstanceOf[Success[_]]) //9th
assert(receiveOne(200 milliseconds).isInstanceOf[Success[_]]) //10th
assert(receiveOne(200 milliseconds).isInstanceOf[Failure[_]]) //no more
assert(src.CountUsed == 10)
assert(src.countUsed == 10)
}
}
}

View file

@ -7,7 +7,7 @@ import net.psforever.objects.avatar.Avatar
import net.psforever.objects.{Default, GlobalDefinitions, Player}
import net.psforever.objects.definition.SeatDefinition
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
@ -162,7 +162,7 @@ class ImplantTerminalMechControl5Test extends ActorTest {
object ImplantTerminalMechTest {
def SetUpAgents(faction: PlanetSideEmpire.Value)(implicit system: ActorSystem): (Player, ImplantTerminalMech) = {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val guid = new NumberPoolHub(new MaxNumberSource(10))
val map = new ZoneMap("test")
val zone = new Zone("test", map, 0) {
override def SetupNumberPools() = {}