Facility Turrets (#223)

* object class, actor class, and definitions for base turrets; untested

* wired base turrets into existence, with hoop jumping; created interface for objects with mounted weapons (vehicles and turrets); working example phalanx_sgl_hevgatcan in Anguta, Ceryshen

* re-wiring manned turrets so that the turreted weapon itself never changes externally but merely identifies different and changes internally; workflow for upgrading wall turrets in place (30s); clarifications and documentation for HackMessage and UseItemMessage; getting rid of orphaned packages from previous location of services

* added a simple task that reverts upgraded manned turrets to their None state after a certain amount of time has passed; it works but need improvement

* turret weapon upgrades now last for a duration of 30 minutes before reverting; created a service support actor base actor that underlies all current support actors; nano-dispenser now properly loads 1 unit of upgrade canister, rather than 100 units; all canister types have appropriate 2x3 inventory size

* forgot to hurry; moved over the Services tests from main/test folder into the common/test folder and needed to change the location of ActorTest to accommodate it; test and documentation for MannedTurret; codecov ignore update

* wired facility turrets in Anguta, Ceryshen; Akna tower, Ceryshen; and S.Villa tower, home3 (Anguta tower is a watchtower); attempted workaround for Travis CI issues with receiveN; re-introduced RemoveActorTest, at least the first test; expanded how ZoneActor performs tests on MannedTurret setup

* getting rid of useless commented-out code; making common operations for mounting and dismounting

* removed outdated comment; added ResourceSilo tests; added extra test for Zone
This commit is contained in:
Fate-JH 2018-07-14 21:25:44 -04:00 committed by GitHub
parent 61a51c1dd1
commit b81ff2bbf4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 2246 additions and 680 deletions

View file

@ -9,8 +9,11 @@ ignore:
- "common/src/main/scala/net/psforever/objects/equipment/Kits.scala" - "common/src/main/scala/net/psforever/objects/equipment/Kits.scala"
- "common/src/main/scala/net/psforever/objects/equipment/SItem.scala" - "common/src/main/scala/net/psforever/objects/equipment/SItem.scala"
- "common/src/main/scala/net/psforever/objects/guid/AvailabilityPolicy.scala" - "common/src/main/scala/net/psforever/objects/guid/AvailabilityPolicy.scala"
- "common/src/main/scala/net/psforever/objects/serverobject/turret/TurretUpgrade.scala"
- "common/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/AccessPermissionGroup.scala" - "common/src/main/scala/net/psforever/objects/vehicles/AccessPermissionGroup.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/SeatArmoRestriction.scala" - "common/src/main/scala/net/psforever/objects/vehicles/SeatArmoRestriction.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/Turrets.scala"
- "common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala" - "common/src/main/scala/net/psforever/objects/vehicles/VehicleLockState.scala"
- "common/src/main/scala/net/psforever/packet/crypto" - "common/src/main/scala/net/psforever/packet/crypto"
- "common/src/main/scala/net/psforever/packet/game/objectcreate/DrawnSlot.scala" - "common/src/main/scala/net/psforever/packet/game/objectcreate/DrawnSlot.scala"
@ -33,12 +36,12 @@ ignore:
- "common/src/main/scala/net/psforever/types/MeritCommendation.scala" - "common/src/main/scala/net/psforever/types/MeritCommendation.scala"
- "common/src/main/scala/net/psforever/types/PlanetSideEmpire.scala" - "common/src/main/scala/net/psforever/types/PlanetSideEmpire.scala"
- "common/src/main/scala/net/psforever/types/TransactionType.scala" - "common/src/main/scala/net/psforever/types/TransactionType.scala"
- "pslogin/src/main/scala/services/avatar/AvatarAction.scala" - "common/src/main/scala/services/avatar/AvatarAction.scala"
- "pslogin/src/main/scala/services/avatar/AvatarResponse.scala" - "common/src/main/scala/services/avatar/AvatarResponse.scala"
- "pslogin/src/main/scala/services/local/LocalAction.scala" - "common/src/main/scala/services/local/LocalAction.scala"
- "pslogin/src/main/scala/services/local/LocalResponse.scala" - "common/src/main/scala/services/local/LocalResponse.scala"
- "pslogin/src/main/scala/services/vehicle/VehicleAction.scala" - "common/src/main/scala/services/vehicle/VehicleAction.scala"
- "pslogin/src/main/scala/services/vehicle/VehicleResponse.scala" - "common/src/main/scala/services/vehicle/VehicleResponse.scala"
- "pslogin/src/main/scala/CryptoSessionActor.scala" - "pslogin/src/main/scala/CryptoSessionActor.scala"
- "pslogin/src/main/scala/DatabaseConnector.scala" - "pslogin/src/main/scala/DatabaseConnector.scala"
- "pslogin/src/main/scala/LoginConfig.scala" - "pslogin/src/main/scala/LoginConfig.scala"

View file

@ -15,9 +15,11 @@ import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.tube.SpawnTubeDefinition import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
import net.psforever.objects.ballistics.{DamageType, Projectiles} import net.psforever.objects.ballistics.{DamageType, Projectiles}
import net.psforever.objects.serverobject.turret.{MannedTurretDefinition, TurretUpgrade}
import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType} import net.psforever.objects.vehicles.{SeatArmorRestriction, UtilityType}
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
import scala.collection.mutable
import scala.concurrent.duration._ import scala.concurrent.duration._
object GlobalDefinitions { object GlobalDefinitions {
@ -414,11 +416,11 @@ object GlobalDefinitions {
val upgrade_canister = AmmoBoxDefinition(Ammo.upgrade_canister) val upgrade_canister = AmmoBoxDefinition(Ammo.upgrade_canister)
val trek_ammo = AmmoBoxDefinition(Ammo.trek_ammo) val trek_ammo = AmmoBoxDefinition(Ammo.trek_ammo)
//
val bullet_35mm = AmmoBoxDefinition(Ammo.bullet_35mm) //liberator nosegun val bullet_35mm = AmmoBoxDefinition(Ammo.bullet_35mm) //liberator nosegun
val ancient_ammo_vehicle = AmmoBoxDefinition(Ammo.ancient_ammo_vehicle) val ancient_ammo_vehicle = AmmoBoxDefinition(Ammo.ancient_ammo_vehicle)
//
val aphelion_laser_ammo = AmmoBoxDefinition(Ammo.aphelion_laser_ammo) val aphelion_laser_ammo = AmmoBoxDefinition(Ammo.aphelion_laser_ammo)
val aphelion_immolation_cannon_ammo = AmmoBoxDefinition(Ammo.aphelion_immolation_cannon_ammo) val aphelion_immolation_cannon_ammo = AmmoBoxDefinition(Ammo.aphelion_immolation_cannon_ammo)
@ -488,6 +490,8 @@ object GlobalDefinitions {
val peregrine_sparrow_ammo = AmmoBoxDefinition(Ammo.peregrine_sparrow_ammo) val peregrine_sparrow_ammo = AmmoBoxDefinition(Ammo.peregrine_sparrow_ammo)
val bullet_150mm = AmmoBoxDefinition(Ammo.bullet_150mm) val bullet_150mm = AmmoBoxDefinition(Ammo.bullet_150mm)
val phalanx_ammo = AmmoBoxDefinition(Ammo.phalanx_ammo)
init_ammo() init_ammo()
val chainblade = ToolDefinition(ObjectClass.chainblade) val chainblade = ToolDefinition(ObjectClass.chainblade)
@ -722,6 +726,12 @@ object GlobalDefinitions {
val galaxy_gunship_tailgun = ToolDefinition(ObjectClass.galaxy_gunship_tailgun) val galaxy_gunship_tailgun = ToolDefinition(ObjectClass.galaxy_gunship_tailgun)
val galaxy_gunship_gun = ToolDefinition(ObjectClass.galaxy_gunship_gun) val galaxy_gunship_gun = ToolDefinition(ObjectClass.galaxy_gunship_gun)
val phalanx_sgl_hevgatcan = ToolDefinition(ObjectClass.phalanx_sgl_hevgatcan)
val phalanx_avcombo = ToolDefinition(ObjectClass.phalanx_avcombo)
val phalanx_flakcombo = ToolDefinition(ObjectClass.phalanx_flakcombo)
init_tools() init_tools()
/* /*
@ -849,6 +859,18 @@ object GlobalDefinitions {
val resource_silo = new ResourceSiloDefinition val resource_silo = new ResourceSiloDefinition
val manned_turret = new MannedTurretDefinition(480) {
Name = "manned_turret"
MaxHealth = 3600
Weapons += 1 -> new mutable.HashMap()
Weapons(1) += TurretUpgrade.None -> phalanx_sgl_hevgatcan
Weapons(1) += TurretUpgrade.AVCombo -> phalanx_avcombo
Weapons(1) += TurretUpgrade.FlakCombo -> phalanx_flakcombo
MountPoints += 1 -> 0
FactionLocked = true
ReserveAmmunition = false
}
/** /**
* Given a faction, provide the standard assault melee weapon. * Given a faction, provide the standard assault melee weapon.
* @param faction the faction * @param faction the faction
@ -1352,15 +1374,15 @@ object GlobalDefinitions {
health_canister.Name = "health_canister" health_canister.Name = "health_canister"
health_canister.Capacity = 100 health_canister.Capacity = 100
health_canister.Tile = InventoryTile.Tile33 health_canister.Tile = InventoryTile.Tile23
armor_canister.Name = "armor_canister" armor_canister.Name = "armor_canister"
armor_canister.Capacity = 100 armor_canister.Capacity = 100
armor_canister.Tile = InventoryTile.Tile33 armor_canister.Tile = InventoryTile.Tile23
upgrade_canister.Name = "upgrade_canister" upgrade_canister.Name = "upgrade_canister"
upgrade_canister.Capacity = 100 upgrade_canister.Capacity = 1
upgrade_canister.Tile = InventoryTile.Tile33 upgrade_canister.Tile = InventoryTile.Tile23
trek_ammo.Name = "trek_ammo" trek_ammo.Name = "trek_ammo"
trek_ammo.Size = EquipmentSize.Blocked trek_ammo.Size = EquipmentSize.Blocked
@ -1508,6 +1530,10 @@ object GlobalDefinitions {
bullet_150mm.Name = "bullet_150mm" bullet_150mm.Name = "bullet_150mm"
bullet_150mm.Capacity = 50 bullet_150mm.Capacity = 50
bullet_150mm.Tile = InventoryTile.Tile44 bullet_150mm.Tile = InventoryTile.Tile44
phalanx_ammo.Name = "phalanx_ammo"
phalanx_ammo.Capacity = 4000 //sufficient for a reload
phalanx_ammo.Size = EquipmentSize.Inventory
} }
/** /**
@ -3743,6 +3769,7 @@ object GlobalDefinitions {
nano_dispenser.FireModes.head.AmmoTypeIndices += 1 nano_dispenser.FireModes.head.AmmoTypeIndices += 1
nano_dispenser.FireModes.head.AmmoSlotIndex = 0 nano_dispenser.FireModes.head.AmmoSlotIndex = 0
nano_dispenser.FireModes.head.Magazine = 100 nano_dispenser.FireModes.head.Magazine = 100
nano_dispenser.FireModes.head.CustomMagazine = Ammo.upgrade_canister -> 1
nano_dispenser.FireModes.head.Modifiers.Damage1 = 20 nano_dispenser.FireModes.head.Modifiers.Damage1 = 20
nano_dispenser.FireModes.head.Modifiers.Damage4 = 20 nano_dispenser.FireModes.head.Modifiers.Damage4 = 20
nano_dispenser.Tile = InventoryTile.Tile63 nano_dispenser.Tile = InventoryTile.Tile63
@ -4297,6 +4324,45 @@ object GlobalDefinitions {
galaxy_gunship_gun.FireModes.head.AmmoTypeIndices += 0 galaxy_gunship_gun.FireModes.head.AmmoTypeIndices += 0
galaxy_gunship_gun.FireModes.head.AmmoSlotIndex = 0 galaxy_gunship_gun.FireModes.head.AmmoSlotIndex = 0
galaxy_gunship_gun.FireModes.head.Magazine = 200 galaxy_gunship_gun.FireModes.head.Magazine = 200
phalanx_sgl_hevgatcan.Name = "phalanx_sgl_hevgatcan"
phalanx_sgl_hevgatcan.Size = EquipmentSize.BaseTurretWeapon
phalanx_sgl_hevgatcan.AmmoTypes += phalanx_ammo
phalanx_sgl_hevgatcan.ProjectileTypes += phalanx_projectile
phalanx_sgl_hevgatcan.FireModes += new InfiniteFireModeDefinition
phalanx_sgl_hevgatcan.FireModes.head.AmmoTypeIndices += 0
phalanx_sgl_hevgatcan.FireModes.head.AmmoSlotIndex = 0
phalanx_sgl_hevgatcan.FireModes.head.Magazine = 4000
phalanx_avcombo.Name = "phalanx_avcombo"
phalanx_avcombo.Size = EquipmentSize.BaseTurretWeapon
phalanx_avcombo.AmmoTypes += phalanx_ammo
phalanx_avcombo.ProjectileTypes += phalanx_projectile
phalanx_avcombo.ProjectileTypes += phalanx_av_projectile
phalanx_avcombo.FireModes += new InfiniteFireModeDefinition
phalanx_avcombo.FireModes.head.AmmoTypeIndices += 0
phalanx_avcombo.FireModes.head.AmmoSlotIndex = 0
phalanx_avcombo.FireModes.head.Magazine = 4000
phalanx_avcombo.FireModes += new InfiniteFireModeDefinition
phalanx_avcombo.FireModes(1).AmmoTypeIndices += 0
phalanx_avcombo.FireModes(1).ProjectileTypeIndices += 1
phalanx_avcombo.FireModes(1).AmmoSlotIndex = 0
phalanx_avcombo.FireModes(1).Magazine = 4000
phalanx_flakcombo.Name = "phalanx_flakcombo"
phalanx_flakcombo.Size = EquipmentSize.BaseTurretWeapon
phalanx_flakcombo.AmmoTypes += phalanx_ammo
phalanx_flakcombo.ProjectileTypes += phalanx_projectile
phalanx_flakcombo.ProjectileTypes += phalanx_flak_projectile
phalanx_flakcombo.FireModes += new InfiniteFireModeDefinition
phalanx_flakcombo.FireModes.head.AmmoTypeIndices += 0
phalanx_flakcombo.FireModes.head.AmmoSlotIndex = 0
phalanx_flakcombo.FireModes.head.Magazine = 4000
phalanx_flakcombo.FireModes += new InfiniteFireModeDefinition
phalanx_flakcombo.FireModes(1).AmmoTypeIndices += 0
phalanx_flakcombo.FireModes(1).ProjectileTypeIndices += 1
phalanx_flakcombo.FireModes(1).AmmoSlotIndex = 0
phalanx_flakcombo.FireModes(1).Magazine = 4000
} }
/** /**

View file

@ -21,24 +21,26 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
/** index of the current fire mode on the `ToolDefinition`'s list of fire modes */ /** index of the current fire mode on the `ToolDefinition`'s list of fire modes */
private var fireModeIndex : Int = 0 private var fireModeIndex : Int = 0
/** current ammunition slot being used by this fire mode */ /** current ammunition slot being used by this fire mode */
private val ammoSlots : List[Tool.FireModeSlot] = Tool.LoadDefinition(this) private var ammoSlots : List[Tool.FireModeSlot] = List.empty
Tool.LoadDefinition(this)
def FireModeIndex : Int = fireModeIndex def FireModeIndex : Int = fireModeIndex
def FireModeIndex_=(index : Int) : Int = { def FireModeIndex_=(index : Int) : Int = {
fireModeIndex = index % toolDef.FireModes.length fireModeIndex = index % Definition.FireModes.length
FireModeIndex FireModeIndex
} }
def FireMode : FireModeDefinition = toolDef.FireModes(fireModeIndex) def FireMode : FireModeDefinition = Definition.FireModes(fireModeIndex)
def NextFireMode : FireModeDefinition = { def NextFireMode : FireModeDefinition = {
FireModeIndex = toolDef.NextFireModeIndex(FireModeIndex) FireModeIndex = Definition.NextFireModeIndex(FireModeIndex)
AmmoSlot.Chamber = FireMode.Chamber AmmoSlot.Chamber = FireMode.Chamber
FireMode FireMode
} }
def ToFireMode : Int = toolDef.NextFireModeIndex(FireModeIndex) def ToFireMode : Int = Definition.NextFireModeIndex(FireModeIndex)
def ToFireMode_=(index : Int) : FireModeDefinition = { def ToFireMode_=(index : Int) : FireModeDefinition = {
FireModeIndex = index FireModeIndex = index
@ -53,7 +55,7 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
AmmoTypeIndex AmmoTypeIndex
} }
def AmmoType : Ammo.Value = toolDef.AmmoTypes(AmmoTypeIndex).AmmoType def AmmoType : Ammo.Value = Definition.AmmoTypes(AmmoTypeIndex).AmmoType
def NextAmmoType : Ammo.Value = { def NextAmmoType : Ammo.Value = {
AmmoSlot.AmmoTypeIndex = AmmoSlot.AmmoTypeIndex + 1 AmmoSlot.AmmoTypeIndex = AmmoSlot.AmmoTypeIndex + 1
@ -61,7 +63,7 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
} }
def Projectile : ProjectileDefinition = { def Projectile : ProjectileDefinition = {
toolDef.ProjectileTypes({ Definition.ProjectileTypes({
val projIndices = FireMode.ProjectileTypeIndices val projIndices = FireMode.ProjectileTypeIndices
if(projIndices.isEmpty) { if(projIndices.isEmpty) {
AmmoTypeIndex //e.g., bullet_9mm -> bullet_9mm_projectile, bullet_9mm_AP -> bullet_9mm_AP_projectile AmmoTypeIndex //e.g., bullet_9mm -> bullet_9mm_projectile, bullet_9mm_AP -> bullet_9mm_AP_projectile
@ -77,11 +79,20 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
def Magazine : Int = AmmoSlot.Magazine def Magazine : Int = AmmoSlot.Magazine
def Magazine_=(mag : Int) : Int = { def Magazine_=(mag : Int) : Int = {
AmmoSlot.Magazine = Math.min(Math.max(0, mag), MaxMagazine) //AmmoSlot.Magazine = Math.min(Math.max(0, mag), MaxMagazine)
AmmoSlot.Magazine = Math.max(0, mag)
Magazine Magazine
} }
def MaxMagazine : Int = FireMode.Magazine def MaxMagazine : Int = {
val fmode = FireMode
fmode.CustomMagazine.get(AmmoType) match {
case Some(magSize) =>
magSize
case None =>
fmode.Magazine
}
}
def Discharge : Int = { def Discharge : Int = {
Magazine = FireMode.Discharge(this) Magazine = FireMode.Discharge(this)
@ -98,6 +109,8 @@ class Tool(private val toolDef : ToolDefinition) extends Equipment with FireMode
override def toString : String = Tool.toString(this) override def toString : String = Tool.toString(this)
} }
//AmmoType = Definition.AmmoTypes( (Definition.FireModes(fireModeIndex)).AmmoTypeIndices( (ammoSlots((Definition.FireModes(fireModeIndex)).AmmoSlotIndex)).AmmoTypeIndex) ).AmmoType
object Tool { object Tool {
def apply(toolDef : ToolDefinition) : Tool = { def apply(toolDef : ToolDefinition) : Tool = {
new Tool(toolDef) new Tool(toolDef)
@ -107,10 +120,10 @@ object Tool {
* Use the `*Definition` that was provided to this object to initialize its fields and settings. * Use the `*Definition` that was provided to this object to initialize its fields and settings.
* @param tool the `Tool` being initialized * @param tool the `Tool` being initialized
*/ */
def LoadDefinition(tool : Tool) : List[FireModeSlot] = { def LoadDefinition(tool : Tool) : Unit = {
val tdef : ToolDefinition = tool.Definition val tdef : ToolDefinition = tool.Definition
val maxSlot = tdef.FireModes.maxBy(fmode => fmode.AmmoSlotIndex).AmmoSlotIndex val maxSlot = tdef.FireModes.maxBy(fmode => fmode.AmmoSlotIndex).AmmoSlotIndex
buildFireModes(tdef, (0 to maxSlot).iterator, tdef.FireModes.toList) tool.ammoSlots = buildFireModes(tdef, (0 to maxSlot).iterator, tdef.FireModes.toList)
} }
@tailrec private def buildFireModes(tdef : ToolDefinition, iter : Iterator[Int], fmodes : List[FireModeDefinition], list : List[FireModeSlot] = Nil) : List[FireModeSlot] = { @tailrec private def buildFireModes(tdef : ToolDefinition, iter : Iterator[Int], fmodes : List[FireModeDefinition], list : List[FireModeSlot] = Nil) : List[FireModeSlot] = {

View file

@ -65,6 +65,7 @@ import scala.annotation.tailrec
class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject
with FactionAffinity with FactionAffinity
with Mountable with Mountable
with MountedWeapons
with Deployment with Deployment
with Container { with Container {
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
@ -371,38 +372,6 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
} }
} }
/**
* Given a valid seat number, retrieve an index where the weapon controlled from this seat is mounted.
* @param seatNumber the seat number
* @return a mounted weapon by index, or `None` if either the seat doesn't exist or there is no controlled weapon
*/
def WeaponControlledFromSeat(seatNumber : Int) : Option[Equipment] = {
Seat(seatNumber) match {
case Some(seat) =>
wepFromSeat(seat)
case None =>
None
}
}
private def wepFromSeat(seat : Seat) : Option[Equipment] = {
seat.ControlledWeapon match {
case Some(index) =>
wepFromSeat(index)
case None =>
None
}
}
private def wepFromSeat(wepIndex : Int) : Option[Equipment] = {
weapons.get(wepIndex) match {
case Some(wep) =>
wep.Equipment
case None =>
None
}
}
def Utilities : Map[Int, Utility] = utilities def Utilities : Map[Int, Utility] = utilities
/** /**

View file

@ -9,6 +9,7 @@ object EquipmentSize extends Enumeration {
Rifle, //6x3 and 9x3 Rifle, //6x3 and 9x3
Max, //max weapon only Max, //max weapon only
VehicleWeapon, //vehicle-mounted weapons VehicleWeapon, //vehicle-mounted weapons
BaseTurretWeapon, //common phalanx cannons, and cavern turrets
BFRArmWeapon, //duel arm weapons for bfr BFRArmWeapon, //duel arm weapons for bfr
BFRGunnerWeapon, //gunner seat for bfr BFRGunnerWeapon, //gunner seat for bfr
Inventory //reserved Inventory //reserved

View file

@ -18,6 +18,9 @@ class FireModeDefinition {
private var ammoSlotIndex : Int = 0 private var ammoSlotIndex : Int = 0
/** how many rounds are replenished each reload cycle */ /** how many rounds are replenished each reload cycle */
private var magazine : Int = 1 private var magazine : Int = 1
/** how many rounds are replenished each reload cycle, per type of ammunition loaded
* key - ammo type index, value - magazine capacity*/
private val customAmmoMagazine : mutable.HashMap[Ammo.Value, Int] = mutable.HashMap[Ammo.Value, Int]()
/** how much is subtracted from the magazine each fire cycle; /** how much is subtracted from the magazine each fire cycle;
* most weapons will only fire 1 round per fire cycle; the flamethrower in fire mode 1 fires 50 */ * most weapons will only fire 1 round per fire cycle; the flamethrower in fire mode 1 fires 50 */
private var rounds : Int = 1 private var rounds : Int = 1
@ -53,6 +56,14 @@ class FireModeDefinition {
Magazine Magazine
} }
def CustomMagazine : mutable.HashMap[Ammo.Value, Int] = customAmmoMagazine
def CustomMagazine_=(kv : (Ammo.Value, Int)) : mutable.HashMap[Ammo.Value, Int] = {
val (ammoTypeIndex, cap) = kv
customAmmoMagazine += ammoTypeIndex -> cap
CustomMagazine
}
def Rounds : Int = rounds def Rounds : Int = rounds
def Rounds_=(round : Int) : Int = { def Rounds_=(round : Int) : Int = {

View file

@ -82,4 +82,4 @@ object ResourceSilo {
obj.Actor ! "startup" obj.Actor ! "startup"
obj obj
} }
} }

View file

@ -0,0 +1,224 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.turret
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.definition.SeatDefinition
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.{Container, GridInventory}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.objects.serverobject.turret.MannedTurret.MannedTurretWeapon
import net.psforever.objects.vehicles.{MountedWeapons, Seat => Chair}
class MannedTurret(tDef : MannedTurretDefinition) extends Amenity
with FactionAffinity
with Mountable
with MountedWeapons
with Container {
private var health : Int = 1
private var jammered : Boolean = false
/** manned turrets have just one seat; this is just standard interface */
private val seats : Map[Int, Chair] = Map(0 -> Chair(new SeatDefinition() { ControlledWeapon = Some(1) }))
/** manned turrets have just one weapon; this is just standard interface */
private var weapons : Map[Int, EquipmentSlot] = Map.empty
/** may or may not have inaccessible inventory space
* see `ReserveAmmunition` in the definition */
private val inventory : GridInventory = new GridInventory() {
import net.psforever.packet.game.PlanetSideGUID
override def Remove(index : Int) : Boolean = false
override def Remove(guid : PlanetSideGUID) : Boolean = false
}
/** some turrets can be updated; they all start without updates */
private var upgradePath : TurretUpgrade.Value = TurretUpgrade.None
MannedTurret.LoadDefinition(this)
def Health : Int = {
health
}
def Health_=(toHealth : Int) : Int = {
health = toHealth
health
}
def MaxHealth : Int = {
Definition.MaxHealth
}
def Seats : Map[Int, Chair] = seats
def Seat(seatNum : Int) : Option[Chair] = seats.get(seatNum)
/**
* Given the index of an entry mounting point, return the infantry-accessible `Seat` associated with it.
* @param mountPoint an index representing the seat position / mounting point
* @return a seat number, or `None`
*/
def GetSeatFromMountPoint(mountPoint : Int) : Option[Int] = {
Definition.MountPoints.get(mountPoint)
}
def MountPoints : Map[Int, Int] = Definition.MountPoints.toMap
def PassengerInSeat(user : Player) : Option[Int] = {
if(seats(0).Occupant.contains(user)) {
Some(0)
}
else {
None
}
}
def Weapons : Map[Int, EquipmentSlot] = weapons.filter({ case(index, _) => index < 2 })
def ControlledWeapon(wepNumber : Int) : Option[Equipment] = {
if(VisibleSlots.contains(wepNumber)) {
weapons(wepNumber).Equipment
}
else {
None
}
}
def Inventory : GridInventory = inventory
def VisibleSlots : Set[Int] = Set(1)
def Upgrade : TurretUpgrade.Value = upgradePath
def Upgrade_=(upgrade : TurretUpgrade.Value) : TurretUpgrade.Value = {
upgradePath = upgrade
//upgrade each weapon as long as that weapon has a valid option for that upgrade
Definition.Weapons.foreach({ case(index, upgradePaths) =>
if(upgradePaths.contains(upgrade)) {
weapons(index).Equipment.get.asInstanceOf[MannedTurretWeapon].Upgrade = upgrade
}
})
Upgrade
}
def Jammered : Boolean = jammered
def Jammered_=(jamState : Boolean) : Boolean = {
jammered = jamState
Jammered
}
def Definition : MannedTurretDefinition = tDef
}
object MannedTurret {
/**
* Overloaded constructor.
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
* @return a `MannedTurret` object
*/
def apply(tDef : MannedTurretDefinition) : MannedTurret = {
new MannedTurret(tDef)
}
/**
* Use the `*Definition` that was provided to this object to initialize its fields and settings.
* @param turret the `MannedTurret` being initialized
* @see `{object}.LoadDefinition`
*/
def LoadDefinition(turret : MannedTurret) : MannedTurret = {
import net.psforever.objects.equipment.EquipmentSize.BaseTurretWeapon
val tdef : MannedTurretDefinition = turret.Definition
//general stuff
turret.Health = tdef.MaxHealth
//create weapons; note the class
turret.weapons = tdef.Weapons.map({case (num, upgradePaths) =>
val slot = EquipmentSlot(BaseTurretWeapon)
slot.Equipment = new MannedTurretWeapon(tdef, upgradePaths.toMap)
num -> slot
}).toMap
//special inventory ammunition object(s)
if(tdef.ReserveAmmunition) {
val allAmmunitionTypes = tdef.Weapons.values.flatMap{ _.values.flatMap { _.AmmoTypes } }.toSet
if(allAmmunitionTypes.nonEmpty) {
turret.inventory.Resize(allAmmunitionTypes.size, 1)
var i : Int = 0
allAmmunitionTypes.foreach(ammotype => {
turret.inventory.InsertQuickly(i, new TurretAmmoBox(ammotype))
i += 1
})
}
}
turret
}
import net.psforever.objects.definition.ToolDefinition
import net.psforever.objects.Tool
/**
* A stateful weapon that is mounted in `MannedTurrets`
* and may maintains a group of upgraded forms that can by swapped
* without reconstructing the weapon object itself or managing object registration.
* @param mdef the turret's definition
* @param udefs a map of turret upgrades to tool definitions that would be constructed by this weapon
* @param default the default upgrade state;
* defaults to `None`
*/
private class MannedTurretWeapon(mdef : MannedTurretDefinition, udefs : Map[TurretUpgrade.Value, ToolDefinition], default : TurretUpgrade.Value = TurretUpgrade.None)
extends Tool(udefs(default)) {
private var upgradePath : TurretUpgrade.Value = default
def Upgrade : TurretUpgrade.Value = {
/*
Must check `not null` due to how this object's `Definition` will be called during `Tool`'s constructor
before the internal value can be set to default value `None`
*/
Option(upgradePath) match {
case Some(value) =>
value
case None =>
default
}
}
def Upgrade_=(upgrade : TurretUpgrade.Value) : TurretUpgrade.Value = {
if(udefs.contains(upgrade)) {
val beforeUpgrade = upgradePath
upgradePath = upgrade
if(beforeUpgrade != upgradePath) {
Tool.LoadDefinition(this) //rebuild weapon internal structure
FireModeIndex = 0 //reset fire mode; this option is always valid
}
}
Upgrade
}
override def Definition = udefs(Upgrade)
}
import net.psforever.objects.definition.AmmoBoxDefinition
import net.psforever.objects.AmmoBox
/**
* A special type of ammunition box contained within a `MannedTurret` for the purposes of infinite reloads.
* The original quantity of ammunition does not change.
* @param adef ammunition definition
*/
private class TurretAmmoBox(private val adef : AmmoBoxDefinition) extends AmmoBox(adef, Some(65535)) {
import net.psforever.objects.inventory.InventoryTile
override def Tile = InventoryTile.Tile11
override def Capacity_=(toCapacity : Int) = Capacity
}
import akka.actor.ActorContext
/**
* Instantiate an configure a `MannedTurret` 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 `MannedTurret` object
*/
def Constructor(tdef : MannedTurretDefinition)(id : Int, context : ActorContext) : MannedTurret = {
import akka.actor.Props
val obj = MannedTurret(tdef)
obj.Actor = context.actorOf(Props(classOf[MannedTurretControl], obj), s"${tdef.Name}_$id")
obj
}
}

View file

@ -0,0 +1,43 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.turret
import akka.actor.Actor
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
/**
* An `Actor` that handles messages being dispatched to a specific `MannedTurret`.<br>
* <br>
* Mounted turrets have only slightly different entry requirements than a normal vehicle
* because they encompass both faction-specific facility turrets
* and faction-blind cavern sentry turrets.
* @param turret the `MannedTurret` object being governed
*/
class MannedTurretControl(turret : MannedTurret) extends Actor
with FactionAffinityBehavior.Check
with MountableBehavior.Dismount {
def MountableObject = turret //do not add type!
def FactionObject : FactionAffinity = turret
def receive : Receive = checkBehavior
.orElse(dismountBehavior)
.orElse {
case Mountable.TryMount(user, seat_num) =>
turret.Seat(seat_num) match {
case Some(seat) =>
if((!turret.Definition.FactionLocked || user.Faction == turret.Faction) &&
(seat.Occupant = user).contains(user)) {
user.VehicleSeated = turret.GUID
sender ! Mountable.MountMessages(user, Mountable.CanMount(turret, seat_num))
}
else {
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(turret, seat_num))
}
case None =>
sender ! Mountable.MountMessages(user, Mountable.CanNotMount(turret, seat_num))
}
case _ => ;
}
}

View file

@ -0,0 +1,53 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.turret
import net.psforever.objects.definition.{ObjectDefinition, ToolDefinition}
import net.psforever.objects.vehicles.Turrets
import scala.collection.mutable
/**
* The definition for any `MannedTurret`.
* @param objectId the object's identifier number
*/
class MannedTurretDefinition(private val objectId : Int) extends ObjectDefinition(objectId) {
Turrets(objectId) //let throw NoSuchElementException
private var maxHealth : Int = 100
/* key - entry point index, value - seat index */
private val mountPoints : mutable.HashMap[Int, Int] = mutable.HashMap()
/* key - seat number, value - hash map (below) */
/* key - upgrade, value - weapon definition */
private val weapons : mutable.HashMap[Int, mutable.HashMap[TurretUpgrade.Value, ToolDefinition]] =
mutable.HashMap[Int, mutable.HashMap[TurretUpgrade.Value, ToolDefinition]]()
/** can only be mounted by owning faction when `true` */
private var factionLocked : Boolean = true
/** creates internal ammunition reserves that can not become depleted
* see `MannedTurret.TurretAmmoBox` for details */
private var hasReserveAmmunition : Boolean = false
def MaxHealth : Int = maxHealth
def MaxHealth_=(health : Int) : Int = {
maxHealth = health
MaxHealth
}
def MountPoints : mutable.HashMap[Int, Int] = mountPoints
def Weapons : mutable.HashMap[Int, mutable.HashMap[TurretUpgrade.Value, ToolDefinition]] = weapons
def FactionLocked : Boolean = factionLocked
def FactionLocked_=(ownable : Boolean) : Boolean = {
factionLocked = ownable
FactionLocked
}
def ReserveAmmunition : Boolean = hasReserveAmmunition
def ReserveAmmunition_=(reserved : Boolean) : Boolean = {
hasReserveAmmunition = reserved
ReserveAmmunition
}
}

View file

@ -0,0 +1,13 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.turret
/**
* An `Enumeration` of the available turret upgrade states.
*/
object TurretUpgrade extends Enumeration {
val
None, //default, always
AVCombo, //phalanx_avcombo
FlakCombo //phalanx_flakcombo
= Value
}

View file

@ -0,0 +1,39 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vehicles
import net.psforever.objects.{EquipmentSlot, PlanetSideGameObject}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles.{Seat => Chair}
trait MountedWeapons {
this : PlanetSideGameObject with Mountable with Container =>
def Weapons : Map[Int, EquipmentSlot]
/**
* Given a valid seat number, retrieve an index where the weapon controlled from this seat is mounted.
* @param seatNumber the seat number
* @return a mounted weapon by index, or `None` if either the seat doesn't exist or there is no controlled weapon
*/
def WeaponControlledFromSeat(seatNumber : Int) : Option[Equipment] = {
Seat(seatNumber) match {
case Some(seat) =>
wepFromSeat(seat)
case None =>
None
}
}
private def wepFromSeat(seat : Chair) : Option[Equipment] = {
seat.ControlledWeapon match {
case Some(index) =>
ControlledWeapon(index)
case None =>
None
}
}
def ControlledWeapon(wepNumber : Int) : Option[Equipment]
}

View file

@ -0,0 +1,10 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.vehicles
/**
* An `Enumeration` of all the turret type objectss in the game, paired with their object id as the `Value`.
*/
object Turrets extends Enumeration {
val manned_turret = Value(480)
val vanu_sentry_turret = Value(943)
}

View file

@ -12,6 +12,7 @@ import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.structures.{Amenity, Building}
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.MannedTurret
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.Vector3 import net.psforever.types.Vector3
@ -93,7 +94,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles") transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"$Id-vehicles")
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players") population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"$Id-players")
Map.LocalObjects.foreach({ builderObject => builderObject.Build }) BuildLocalObjects(context, guid)
BuildSupportObjects()
MakeBuildings(context) MakeBuildings(context)
AssignAmenities() AssignAmenities()
CreateSpawnGroups() CreateSpawnGroups()
@ -279,6 +281,39 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
buildings.get(id) buildings.get(id)
} }
private def BuildLocalObjects(implicit context : ActorContext, guid : NumberPoolHub) : Unit = {
Map.LocalObjects.foreach({ builderObject => builderObject.Build })
}
private def BuildSupportObjects() : Unit = {
//guard against errors here, but don't worry about specifics; let ZoneActor.ZoneSetupCheck complain about problems
//turret to weapon
Map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
((GUID(turret_guid) match {
case Some(obj : MannedTurret) =>
Some(obj)
case _ => ;
None
}) match {
case Some(obj) =>
obj.Weapons.get(1) match {
case Some(slot) =>
Some(obj, slot.Equipment)
case None =>
None
}
case None =>
None
}) match {
case Some((obj, Some(weapon : Tool))) =>
guid.register(weapon, weapon_guid)
weapon.AmmoSlots.foreach(slot => guid.register(slot.Box, "dynamic"))
obj.Inventory.Items.foreach(item => guid.register(item.obj, "dynamic")) //internal ammunition reserves, if any
case _ => ;
}
})
}
private def MakeBuildings(implicit context : ActorContext) : PairMap[Int, Building] = { private def MakeBuildings(implicit context : ActorContext) : PairMap[Int, Building] = {
val buildingList = Map.LocalBuildings val buildingList = Map.LocalBuildings
buildings = buildingList.map({case(building_id, constructor) => building_id -> constructor.Build(building_id, this) }) buildings = buildingList.map({case(building_id, constructor) => building_id -> constructor.Build(building_id, this) })

View file

@ -4,7 +4,7 @@ package net.psforever.objects.zones
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject} import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Tool}
import net.psforever.objects.serverobject.structures.StructureType import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.vehicles.UtilityType import net.psforever.objects.vehicles.UtilityType
@ -171,73 +171,24 @@ class ZoneActor(zone : Zone) extends Actor {
validateObject(mech_guid, ImplantMechCheck, "implant terminal mech") validateObject(mech_guid, ImplantMechCheck, "implant terminal mech")
validateObject(interface_guid, TerminalCheck, "implant terminal interface") validateObject(interface_guid, TerminalCheck, "implant terminal interface")
}) })
//check manned turret to weapon association
map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
validateObject(turret_guid, MannedTurretCheck, "manned turret mount")
if(validateObject(weapon_guid, WeaponCheck, "manned turret weapon")) {
if(guid(weapon_guid).get.asInstanceOf[Tool].AmmoSlots.count(!_.Box.HasGUID) > 0) {
slog.error(s"expected weapon $weapon_guid has an unregistered ammunition unit")
errors.incrementAndGet()
}
}
})
//output number of errors
errors.intValue() errors.intValue()
} }
} }
object ZoneActor { object ZoneActor {
// import net.psforever.types.PlanetSideEmpire
// import net.psforever.objects.Vehicle
// import net.psforever.objects.serverobject.structures.Building
// def AllSpawnGroup(zone : Zone, targetPosition : Vector3, targetFaction : PlanetSideEmpire.Value) : Option[List[SpawnTube]] = {
// ClosestOwnedSpawnTube(AmsSpawnGroup(zone) ++ BuildingSpawnGroup(zone, 0), targetPosition, targetFaction)
// }
//
// def AmsSpawnGroup(vehicles : List[Vehicle]) : Iterable[(Vector3, PlanetSideEmpire.Value, Iterable[SpawnTube])] = {
// vehicles
// .filter(veh => veh.DeploymentState == DriveState.Deployed && veh.Definition == GlobalDefinitions.ams)
// .map(veh =>
// (veh.Position, veh.Faction,
// veh.Utilities
// .values
// .filter(util => util.UtilType == UtilityType.ams_respawn_tube)
// .map { _().asInstanceOf[SpawnTube] }
// )
// )
// }
//
// def AmsSpawnGroup(zone : Zone, spawn_group : Int = 2) : Iterable[(Vector3, PlanetSideEmpire.Value, Iterable[SpawnTube])] = {
// if(spawn_group == 2) {
// AmsSpawnGroup(zone.Vehicles)
// }
// else {
// Nil
// }
// }
//
// def BuildingSpawnGroup(spawnGroups : Map[Building, List[SpawnTube]]) : Iterable[(Vector3, PlanetSideEmpire.Value, Iterable[SpawnTube])] = {
// spawnGroups
// .map({ case ((building, tubes)) => (building.Position.xy, building.Faction, tubes) })
// }
//
// def BuildingSpawnGroup(zone : Zone, spawn_group : Int) : Iterable[(Vector3, PlanetSideEmpire.Value, Iterable[SpawnTube])] = {
// val buildingTypeSet = if(spawn_group == 0) {
// Set(StructureType.Facility, StructureType.Tower, StructureType.Building)
// }
// else if(spawn_group == 6) {
// Set(StructureType.Tower)
// }
// else if(spawn_group == 7) {
// Set(StructureType.Facility, StructureType.Building)
// }
// else {
// Set.empty[StructureType.Value]
// }
// BuildingSpawnGroup(
// zone.SpawnGroups().filter({ case((building, _)) => buildingTypeSet.contains(building.BuildingType) })
// )
// }
//
// def ClosestOwnedSpawnTube(tubes : Iterable[(Vector3, PlanetSideEmpire.Value, Iterable[SpawnTube])], targetPosition : Vector3, targetFaction : PlanetSideEmpire.Value) : Option[List[SpawnTube]] = {
// tubes
// .toSeq
// .filter({ case (_, faction, _) => faction == targetFaction })
// .sortBy({ case (pos, _, _) => Vector3.DistanceSquared(pos, targetPosition) })
// .take(1)
// .map({ case (_, _, tubes : List[SpawnTube]) => tubes })
// .headOption
// }
/** /**
* Recover an object from a collection and perform any number of validating tests upon it. * Recover an object from a collection and perform any number of validating tests upon it.
* If the object fails any tests, log an error. * If the object fails any tests, log an error.
@ -295,4 +246,14 @@ object ZoneActor {
import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.pad.VehicleSpawnPad
obj.isInstanceOf[VehicleSpawnPad] obj.isInstanceOf[VehicleSpawnPad]
} }
def MannedTurretCheck(obj : PlanetSideGameObject) : Boolean = {
import net.psforever.objects.serverobject.turret.MannedTurret
obj.isInstanceOf[MannedTurret]
}
def WeaponCheck(obj : PlanetSideGameObject) : Boolean = {
import net.psforever.objects.Tool
obj.isInstanceOf[Tool]
}
} }

View file

@ -26,6 +26,7 @@ import net.psforever.objects.serverobject.{PlanetSideServerObject, ServerObjectB
*/ */
class ZoneMap(private val name : String) { class ZoneMap(private val name : String) {
private var localObjects : List[ServerObjectBuilder[_]] = List() private var localObjects : List[ServerObjectBuilder[_]] = List()
private var linkTurretWeapon : Map[Int, Int] = Map()
private var linkTerminalPad : Map[Int, Int] = Map() private var linkTerminalPad : Map[Int, Int] = Map()
private var linkTerminalInterface : Map[Int, Int] = Map() private var linkTerminalInterface : Map[Int, Int] = Map()
private var linkDoorLock : Map[Int, Int] = Map() private var linkDoorLock : Map[Int, Int] = Map()
@ -87,4 +88,10 @@ class ZoneMap(private val name : String) {
def TerminalToInterface(interface_guid : Int, terminal_guid : Int) : Unit = { def TerminalToInterface(interface_guid : Int, terminal_guid : Int) : Unit = {
linkTerminalInterface = linkTerminalInterface ++ Map(interface_guid -> terminal_guid) linkTerminalInterface = linkTerminalInterface ++ Map(interface_guid -> terminal_guid)
} }
def TurretToWeapon : Map[Int, Int] = linkTurretWeapon
def TurretToWeapon(turret_guid : Int, weapon_guid : Int) : Unit = {
linkTurretWeapon = linkTurretWeapon ++ Map(turret_guid -> weapon_guid)
}
} }

View file

@ -33,21 +33,27 @@ object HackState extends Enumeration {
} }
/** /**
* Dispatched by the server to control the process of hacking.<br> * Dispatched by the server to control the progress of hacking.
* While "hacking" is typically performed against enemy targets,
* some actions that involve ally on ally hacking can occur.
* In this sense, hacking can be consider change progress.<br>
* <br> * <br>
* Part of the hacking process is regulated by the server while another part of it is automatically reset by the client. * In general, the act of hacking is maintained by the server but the conclusion is managed by the client.
* The visibility, update, and closing of the hacking progress bar must be handled manually, for each tick. * Hacking typically locks the player into a cancellable firing animation and works as all-or-nothing.
* When hacking is complete, using the appropriate `HackState` will cue the target to be affected by the hack. * The progress bar window is displayed and updated each tick by the server; but, the client can cancel it on its own.
* Terminals and door IFF panels will temporarily expose their functionality; * When hacking is complete as indicated by the appropriate `HackState`,
* the faction association of vehicles will be converted permanently; * the client performs the intended action upon the target.
* a protracted process of a base conversion will be enacted; etc.. * Facility amenities will temporarily ignore IFF requirements;
* This transfer of faction association occurs to align the target with the faction of the hacking player (as indicated). * vehicles will permanently transfer control over to the hack-starter's empire;
* The client will select the faction without needing to be explicitly told * facility turret weapons will temporarily convert to their anti-vehicle or anti-aircraft configurations;
* and will select the appropriate action to enact upon the target. * facilities will be compromised and begin the long process of converting to the hack-starter's empire;
* Upon the hack's completion, the target on the client will automatically revert back to its original state, if possible. * and, so forth.<br>
* (It will still be necessary to alert this change from the server's perspective.) * <br>
* As mentioned, one of the unexpected uses of this message
* will assist the conversion of allied facility turreted weapons to their upgraded armaments.
* @param unk1 na; * @param unk1 na;
* 0 commonly; * 0 commonly;
* 2 when performing (phalanx) upgrades;
* 3 for building objects during login phase; * 3 for building objects during login phase;
* hack type? * hack type?
* @param target_guid the target of the hack * @param target_guid the target of the hack

View file

@ -7,21 +7,28 @@ import scodec.Codec
import scodec.codecs._ import scodec.codecs._
/** /**
* (Where the child object was before it was moved is not specified or important.)<br> * (Where the child object was before it was moved is not specified or important.)
* @param avatar_guid the player. * @see `Definition.ObjectId`<br>
* @param item_used_guid The "item" GUID used e.g. a rek to hack or a medkit to heal. * `TurretUpgrade`
* @param object_guid can be : Door, Terminal, Avatar (medkit). * @param avatar_guid the player
* @param unk2 ??? * @param item_used_guid the item used;
* @param unk3 ??? true when use a rek (false when door, medkit or open equip term) * e.g., a REK to hack, or a medkit to heal
* @param unk4 ??? * @param object_guid the object affected;
* @param unk5 ??? * e.g., door when opened, terminal when accessed, avatar when medkit used
* @param unk6 ??? * @param unk2 na;
* @param unk7 ??? 25 when door 223 when terminal * when upgrading phalanx turrets, 1 for `AVCombo` and 2 for `FlakCombo`
* @param unk8 ??? 0 when door 1 when use rek (252 then equipment term) * @param unk3 using tools, e.g., a REK or nano-dispenser
* @param itemType object ID from game_objects.adb (ex 612 is an equipment terminal, for medkit we have 121 (avatar)) * @param unk4 na
* @param unk5 na
* @param unk6 na
* @param unk7 na;
* 25 when door 223 when terminal
* @param unk8 na;
* 0 when door 1 when use rek (252 then equipment term)
* @param object_id he object id for `object_guid`'s object
*/ */
final case class UseItemMessage(avatar_guid : PlanetSideGUID, final case class UseItemMessage(avatar_guid : PlanetSideGUID,
item_used_guid : Int, item_used_guid : PlanetSideGUID,
object_guid : PlanetSideGUID, object_guid : PlanetSideGUID,
unk2 : Long, unk2 : Long,
unk3 : Boolean, unk3 : Boolean,
@ -30,7 +37,7 @@ final case class UseItemMessage(avatar_guid : PlanetSideGUID,
unk6 : Int, unk6 : Int,
unk7 : Int, unk7 : Int,
unk8 : Int, unk8 : Int,
itemType : Long) object_id : Long)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = UseItemMessage type Packet = UseItemMessage
def opcode = GamePacketOpcode.UseItemMessage def opcode = GamePacketOpcode.UseItemMessage
@ -40,7 +47,7 @@ final case class UseItemMessage(avatar_guid : PlanetSideGUID,
object UseItemMessage extends Marshallable[UseItemMessage] { object UseItemMessage extends Marshallable[UseItemMessage] {
implicit val codec : Codec[UseItemMessage] = ( implicit val codec : Codec[UseItemMessage] = (
("avatar_guid" | PlanetSideGUID.codec) :: ("avatar_guid" | PlanetSideGUID.codec) ::
("item_used_guid" | uint16L) :: ("item_used_guid" | PlanetSideGUID.codec) ::
("object_guid" | PlanetSideGUID.codec) :: ("object_guid" | PlanetSideGUID.codec) ::
("unk2" | uint32L) :: ("unk2" | uint32L) ::
("unk3" | bool) :: ("unk3" | bool) ::
@ -49,6 +56,6 @@ object UseItemMessage extends Marshallable[UseItemMessage] {
("unk6" | uint8L) :: ("unk6" | uint8L) ::
("unk7" | uint8L) :: ("unk7" | uint8L) ::
("unk8" | uint8L) :: ("unk8" | uint8L) ::
("itemType" | uint32L) ("object_id" | uint32L)
).as[UseItemMessage] ).as[UseItemMessage]
} }

View file

@ -373,6 +373,7 @@ object ObjectClass {
final val implant_terminal_interface = 409 final val implant_terminal_interface = 409
final val locker_container = 456 final val locker_container = 456
final val lodestar_repair_terminal = 461 final val lodestar_repair_terminal = 461
final val manned_turret = 480
final val matrix_terminala = 517 final val matrix_terminala = 517
final val matrix_terminalb = 518 final val matrix_terminalb = 518
final val matrix_terminalc = 519 final val matrix_terminalc = 519

View file

@ -6,8 +6,8 @@ import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.{DefaultCancellable, PlanetSideGameObject} import net.psforever.objects.{DefaultCancellable, PlanetSideGameObject}
import net.psforever.types.Vector3 import net.psforever.types.Vector3
import services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions}
import scala.annotation.tailrec
import scala.concurrent.duration._ import scala.concurrent.duration._
/** /**
@ -29,7 +29,7 @@ import scala.concurrent.duration._
* and finally unregistering it. * and finally unregistering it.
* Some types of object have (de-)implementation variations which should be made explicit through the overrides. * Some types of object have (de-)implementation variations which should be made explicit through the overrides.
*/ */
abstract class RemoverActor extends Actor { abstract class RemoverActor extends SupportActor[RemoverActor.Entry] {
/** /**
* The timer that checks whether entries in the first pool are still eligible for that pool. * The timer that checks whether entries in the first pool are still eligible for that pool.
*/ */
@ -50,16 +50,18 @@ abstract class RemoverActor extends Actor {
private var taskResolver : ActorRef = Actor.noSender private var taskResolver : ActorRef = Actor.noSender
private[this] val log = org.log4s.getLogger val sameEntryComparator = new SimilarityComparator[RemoverActor.Entry]() {
def trace(msg : String) : Unit = log.trace(msg) def Test(entry1 : RemoverActor.Entry, entry2 : RemoverActor.Entry) : Boolean = {
def debug(msg : String) : Unit = log.debug(msg) entry1.obj == entry2.obj && entry1.zone == entry2.zone && entry1.obj.GUID == entry2.obj.GUID
}
}
/** /**
* Send the initial message that requests a task resolver for assisting in the removal process. * Send the initial message that requests a task resolver for assisting in the removal process.
*/ */
override def preStart() : Unit = { override def preStart() : Unit = {
super.preStart() super.preStart()
self ! RemoverActor.Startup() self ! Service.Startup()
} }
/** /**
@ -82,7 +84,7 @@ abstract class RemoverActor extends Actor {
} }
def receive : Receive = { def receive : Receive = {
case RemoverActor.Startup() => case Service.Startup() =>
ServiceManager.serviceManager ! ServiceManager.Lookup("taskResolver") //ask for a resolver to deal with the GUID system ServiceManager.serviceManager ! ServiceManager.Lookup("taskResolver") //ask for a resolver to deal with the GUID system
case ServiceManager.LookupResult("taskResolver", endpoint) => case ServiceManager.LookupResult("taskResolver", endpoint) =>
@ -90,83 +92,72 @@ abstract class RemoverActor extends Actor {
context.become(Processing) context.become(Processing)
case msg => case msg =>
log.error(s"received message $msg before being properly initialized") debug(s"received message $msg before being properly initialized")
} }
def Processing : Receive = { def Processing : Receive = entryManagementBehaviors
case RemoverActor.AddTask(obj, zone, duration) => .orElse {
val entry = RemoverActor.Entry(obj, zone, duration.getOrElse(FirstStandardDuration).toNanos) case RemoverActor.AddTask(obj, zone, duration) =>
if(InclusionTest(entry) && !secondHeap.exists(test => RemoverActor.Similarity(test, entry) )) { val entry = RemoverActor.Entry(obj, zone, duration.getOrElse(FirstStandardDuration).toNanos)
InitialJob(entry) if(InclusionTest(entry) && !secondHeap.exists(test => sameEntryComparator.Test(test, entry) )) {
if(firstHeap.isEmpty) { InitialJob(entry)
//we were the only entry so the event must be started from scratch if(firstHeap.isEmpty) {
firstHeap = List(entry) //we were the only entry so the event must be started from scratch
trace(s"a remover task has been added: $entry") firstHeap = List(entry)
RetimeFirstTask()
}
else {
//unknown number of entries; append, sort, then re-time tasking
val oldHead = firstHeap.head
if(!firstHeap.exists(test => RemoverActor.Similarity(test, entry))) {
firstHeap = (firstHeap :+ entry).sortBy(_.duration)
trace(s"a remover task has been added: $entry") trace(s"a remover task has been added: $entry")
if(oldHead != firstHeap.head) { RetimeFirstTask()
RetimeFirstTask()
}
} }
else { else {
trace(s"$obj is already queued for removal") //unknown number of entries; append, sort, then re-time tasking
val oldHead = firstHeap.head
if(!firstHeap.exists(test => sameEntryComparator.Test(test, entry))) {
firstHeap = (firstHeap :+ entry).sortBy(entry => entry.time + entry.duration)
trace(s"a remover task has been added: $entry")
if(oldHead != firstHeap.head) {
RetimeFirstTask()
}
}
else {
trace(s"$obj is already queued for removal")
}
} }
} }
} else {
else { trace(s"$obj either does not qualify for this Remover or is already queued")
trace(s"$obj either does not qualify for this Remover or is already queued") }
}
case RemoverActor.HurrySpecific(targets, zone) => //private messages from RemoverActor to RemoverActor
HurrySpecific(targets, zone) case RemoverActor.StartDelete() =>
firstTask.cancel
secondTask.cancel
val now : Long = System.nanoTime
val (in, out) = firstHeap.partition(entry => { now - entry.time >= entry.duration })
firstHeap = out
secondHeap = secondHeap ++ in.map { RepackageEntry }
in.foreach { FirstJob }
RetimeFirstTask()
if(secondHeap.nonEmpty) {
import scala.concurrent.ExecutionContext.Implicits.global
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
}
trace(s"item removal task has found ${in.size} items to remove")
case RemoverActor.HurryAll() => case RemoverActor.TryDelete() =>
HurryAll() secondTask.cancel
val (in, out) = secondHeap.partition { ClearanceTest }
secondHeap = out
in.foreach { SecondJob }
if(out.nonEmpty) {
import scala.concurrent.ExecutionContext.Implicits.global
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
}
trace(s"item removal task has removed ${in.size} items")
case RemoverActor.ClearSpecific(targets, zone) => case RemoverActor.FailureToWork(entry, ex) =>
ClearSpecific(targets, zone) debug(s"${entry.obj} from ${entry.zone} not properly deleted - $ex")
case RemoverActor.ClearAll() => case _ => ;
ClearAll() }
//private messages from RemoverActor to RemoverActor
case RemoverActor.StartDelete() =>
firstTask.cancel
secondTask.cancel
val now : Long = System.nanoTime
val (in, out) = firstHeap.partition(entry => { now - entry.time >= entry.duration })
firstHeap = out
secondHeap = secondHeap ++ in.map { RepackageEntry }
in.foreach { FirstJob }
RetimeFirstTask()
if(secondHeap.nonEmpty) {
import scala.concurrent.ExecutionContext.Implicits.global
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
}
trace(s"item removal task has found ${in.size} items to remove")
case RemoverActor.TryDelete() =>
secondTask.cancel
val (in, out) = secondHeap.partition { ClearanceTest }
secondHeap = out
in.foreach { SecondJob }
if(out.nonEmpty) {
import scala.concurrent.ExecutionContext.Implicits.global
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
}
trace(s"item removal task has removed ${in.size} items")
case RemoverActor.FailureToWork(entry, ex) =>
log.error(s"${entry.obj} from ${entry.zone} not properly deleted - $ex")
case _ => ;
}
/** /**
* Expedite some entries from the first pool into the second. * Expedite some entries from the first pool into the second.
@ -175,14 +166,18 @@ abstract class RemoverActor extends Actor {
* all targets must be in this zone, with the assumption that this is the zone where they were registered * all targets must be in this zone, with the assumption that this is the zone where they were registered
*/ */
def HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = { def HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = {
CullTargetsFromFirstHeap(targets, zone) match { PartitionTargetsFromList(firstHeap, targets.map { RemoverActor.Entry(_, zone, 0) }, zone) match {
case Nil => case (Nil, _) =>
debug(s"no tasks matching the targets $targets have been hurried") debug(s"no tasks matching the targets $targets have been hurried")
case list => case (in, out) =>
debug(s"the following tasks have been hurried: $list") debug(s"the following tasks have been hurried: $in")
firstHeap = out //.sortBy(entry => entry.time + entry.duration)
if(out.nonEmpty) {
RetimeFirstTask()
}
secondTask.cancel secondTask.cancel
list.foreach { FirstJob } in.foreach { FirstJob }
secondHeap = secondHeap ++ list.map { RepackageEntry } secondHeap = secondHeap ++ in.map { RepackageEntry }
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete()) secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
} }
@ -206,11 +201,15 @@ abstract class RemoverActor extends Actor {
* Remove specific entries from the first pool. * Remove specific entries from the first pool.
*/ */
def ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = { def ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = {
CullTargetsFromFirstHeap(targets, zone) match { PartitionTargetsFromList(firstHeap, targets.map { RemoverActor.Entry(_, zone, 0) }, zone) match {
case Nil => case (Nil, _) =>
debug(s"no tasks matching the targets $targets have been cleared") debug(s"no tasks matching the targets $targets have been cleared")
case list => case (in, out) =>
debug(s"the following tasks have been cleared: $list") debug(s"the following tasks have been cleared: $in")
firstHeap = out //.sortBy(entry => entry.time + entry.duration)
if(out.nonEmpty) {
RetimeFirstTask()
}
} }
} }
@ -232,68 +231,6 @@ abstract class RemoverActor extends Actor {
RemoverActor.Entry(entry.obj, entry.zone, SecondStandardDuration.toNanos) RemoverActor.Entry(entry.obj, entry.zone, SecondStandardDuration.toNanos)
} }
/**
* Search the first pool of entries awaiting removal processing.
* If any entry has the same object as one of the targets and belongs to the same zone, remove it from the first pool.
* If no targets are selected (an empty list), all discovered targets within the appropriate zone are removed.
* @param targets a list of objects to pick
* @param zone the zone in which these objects must be discovered;
* all targets must be in this zone, with the assumption that this is the zone where they were registered
* @return all of the discovered entries
*/
private def CullTargetsFromFirstHeap(targets : List[PlanetSideGameObject], zone : Zone) : List[RemoverActor.Entry] = {
val culledEntries = if(targets.nonEmpty) {
if(targets.size == 1) {
debug(s"a target submitted: ${targets.head}")
//simple selection
RemoverActor.recursiveFind(firstHeap.iterator, RemoverActor.Entry(targets.head, zone, 0)) match {
case None => ;
Nil
case Some(index) =>
val entry = firstHeap(index)
firstHeap = (firstHeap.take(index) ++ firstHeap.drop(index + 1)).sortBy(_.duration)
List(entry)
}
}
else {
debug(s"multiple targets submitted: $targets")
//cumbersome partition
//a - find targets from entries
val locatedTargets = for {
a <- targets.map(RemoverActor.Entry(_, zone, 0))
b <- firstHeap//.filter(entry => entry.zone == zone)
if b.obj.HasGUID && a.obj.HasGUID && RemoverActor.Similarity(b, a)
} yield b
if(locatedTargets.nonEmpty) {
//b - entries, after the found targets are removed (cull any non-GUID entries while at it)
firstHeap = (for {
a <- locatedTargets
b <- firstHeap
if b.obj.HasGUID && a.obj.HasGUID && !RemoverActor.Similarity(b, a)
} yield b).sortBy(_.duration)
locatedTargets
}
else {
Nil
}
}
}
else {
debug(s"all targets within the specified zone $zone will be submitted")
//no specific targets; split on all targets in the given zone instead
val (in, out) = firstHeap.partition(entry => entry.zone == zone)
firstHeap = out.sortBy(_.duration)
in
}
if(culledEntries.nonEmpty) {
RetimeFirstTask()
culledEntries
}
else {
Nil
}
}
/** /**
* Common function to reset the first task's delayed execution. * Common function to reset the first task's delayed execution.
* Cancels the scheduled timer and will only restart the timer if there is at least one entry in the first pool. * Cancels the scheduled timer and will only restart the timer if there is at least one entry in the first pool.
@ -353,15 +290,6 @@ abstract class RemoverActor extends Actor {
*/ */
def SecondStandardDuration : FiniteDuration def SecondStandardDuration : FiniteDuration
/**
* Determine whether or not the resulting entry is valid for this removal process.
* The primary purpose of this function should be to determine if the appropriate type of object is being submitted.
* Override.
* @param entry the entry
* @return `true`, if it can be processed; `false`, otherwise
*/
def InclusionTest(entry : RemoverActor.Entry) : Boolean
/** /**
* Performed when the entry is initially added to the first list. * Performed when the entry is initially added to the first list.
* Override. * Override.
@ -392,23 +320,15 @@ abstract class RemoverActor extends Actor {
def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask
} }
object RemoverActor { object RemoverActor extends SupportActorCaseConversions {
/** /**
* All information necessary to apply to the removal process to produce an effect. * All information necessary to apply to the removal process to produce an effect.
* Internally, all entries have a "time created" field. * Internally, all entries have a "time created" field.
* @param obj the target * @param _obj the target
* @param zone the zone in which this target is registered * @param _zone the zone in which this target is registered
* @param duration how much longer the target will exist in its current state (in nanoseconds) * @param _duration how much longer the target will exist in its current state (in nanoseconds)
*/ */
case class Entry(obj : PlanetSideGameObject, zone : Zone, duration : Long) { case class Entry(_obj : PlanetSideGameObject, _zone : Zone, _duration : Long) extends SupportActor.Entry(_obj, _zone, _duration)
/** The time when this entry was created (in nanoseconds) */
val time : Long = System.nanoTime
}
/**
* A message that prompts the retrieval of a `TaskResolver` for us in the removal process.
*/
case class Startup()
/** /**
* Message to submit an object to the removal process. * Message to submit an object to the removal process.
@ -420,36 +340,6 @@ object RemoverActor {
*/ */
case class AddTask(obj : PlanetSideGameObject, zone : Zone, duration : Option[FiniteDuration] = None) case class AddTask(obj : PlanetSideGameObject, zone : Zone, duration : Option[FiniteDuration] = None)
/**
* "Hurrying" shifts entries with the discovered objects (in the same `zone`)
* through their first task and into the second pool.
* If the list of targets is empty, all discovered objects in the given zone will be considered targets.
* @param targets a list of objects to match
* @param zone the zone in which these objects exist;
* the assumption is that all these target objects are registered to this zone
*/
case class HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone)
/**
* "Hurrying" shifts all entries through their first task and into the second pool.
*/
case class HurryAll()
/**
* "Clearing" cancels entries with the discovered objects (in the same `zone`)
* if they are discovered in the first pool of objects.
* Those entries will no longer be affected by any actions performed by the removal process until re-submitted.
* If the list of targets is empty, all discovered objects in the given zone will be considered targets.
* @param targets a list of objects to match
* @param zone the zone in which these objects exist;
* the assumption is that all these target objects are registered to this zone
*/
case class ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone)
/**
* "Clearing" cancels all entries if they are discovered in the first pool of objects.
* Those entries will no longer be affected by any actions performed by the removal process until re-submitted.
*/
case class ClearAll()
/** /**
* Message that indicates that the final stage of the remover process has failed. * Message that indicates that the final stage of the remover process has failed.
* Since the last step is generally unregistering the object, it could be a critical error. * Since the last step is generally unregistering the object, it could be a critical error.
@ -467,36 +357,4 @@ object RemoverActor {
* Internal message to flag operations by data in the second list if it has been in that list long enough. * Internal message to flag operations by data in the second list if it has been in that list long enough.
*/ */
private final case class TryDelete() private final case class TryDelete()
/**
* Match two entries by object and by zone information.
* @param entry1 the first entry
* @param entry2 the second entry
* @return if they match
*/
private def Similarity(entry1 : RemoverActor.Entry, entry2 : RemoverActor.Entry) : Boolean = {
entry1.obj == entry2.obj && entry1.zone == entry2.zone && entry1.obj.GUID == entry2.obj.GUID
}
/**
* Get the index of an entry in the list of entries.
* @param iter an `Iterator` of entries
* @param target the specific entry to be found
* @param index the incrementing index value
* @return the index of the entry in the list, if a match to the target is found
*/
@tailrec private def recursiveFind(iter : Iterator[RemoverActor.Entry], target : RemoverActor.Entry, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val entry = iter.next
if(entry.obj.HasGUID && target.obj.HasGUID && Similarity(entry, target)) {
Some(index)
}
else {
recursiveFind(iter, target, index + 1)
}
}
}
} }

View file

@ -8,6 +8,7 @@ import net.psforever.packet.game.PlanetSideGUID
object Service { object Service {
final val defaultPlayerGUID : PlanetSideGUID = PlanetSideGUID(0) final val defaultPlayerGUID : PlanetSideGUID = PlanetSideGUID(0)
final case class Startup()
final case class Join(channel : String) final case class Join(channel : String)
final case class Leave(channel : Option[String] = None) final case class Leave(channel : Option[String] = None)
final case class LeaveAll() final case class LeaveAll()

View file

@ -0,0 +1,12 @@
// Copyright (c) 2017 PSForever
package services.support
abstract class SimilarityComparator[A <: SupportActor.Entry] {
/**
* Match two entries by object and by zone information.
* @param entry1 the first entry
* @param entry2 the second entry
* @return if they match
*/
def Test(entry1 : A, entry2 : A) : Boolean
}

View file

@ -0,0 +1,155 @@
// Copyright (c) 2017 PSForever
package services.support
import akka.actor.Actor
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.zones.Zone
import scala.annotation.tailrec
abstract class SupportActor[A <: SupportActor.Entry] extends Actor {
private[this] val log = org.log4s.getLogger
def info(msg : String) : Unit = log.info(msg)
def trace(msg : String) : Unit = log.trace(msg)
def debug(msg : String) : Unit = log.debug(msg)
def sameEntryComparator : SimilarityComparator[A]
/**
* Determine whether or not the resulting entry is valid for this process.
* The primary purpose of this function should be to determine if the appropriate type of object is being submitted.
* Override.
* @param entry the entry
* @return `true`, if it can be processed; `false`, otherwise
*/
def InclusionTest(entry : A) : Boolean
def entryManagementBehaviors : Receive = {
case SupportActor.HurrySpecific(targets, zone) =>
HurrySpecific(targets, zone)
case SupportActor.HurryAll() =>
HurryAll()
case SupportActor.ClearSpecific(targets, zone) =>
ClearSpecific(targets, zone)
case SupportActor.ClearAll() =>
ClearAll()
}
def HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit
def HurryAll()
def ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit
def ClearAll() : Unit
/*
* Search the first pool of entries awaiting removal processing.
* If any entry has the same object as one of the targets and belongs to the same zone, remove it from the first pool.
* If no targets are selected (an empty list), all discovered targets within the appropriate zone are removed.
* @param targets a list of objects to pick
* @param zone the zone in which these objects must be discovered;
* all targets must be in this zone, with the assumption that this is the zone where they were registered
* @return all of the discovered entries
*/
def PartitionTargetsFromList(list : List[A], targets : List[A], zone : Zone, comparator : SimilarityComparator[A] = sameEntryComparator) : (List[A], List[A]) = {
if(targets.nonEmpty) {
if(targets.size == 1) {
debug(s"a target submitted: ${targets.head}")
//simple selection
SupportActor.recursiveFind(comparator)(list.iterator, targets.head) match {
case None => ;
(Nil, list)
case Some(index) =>
(List(list(index)), list.take(index) ++ list.drop(index + 1))
}
}
else {
debug(s"multiple targets submitted: $targets")
//cumbersome partition
//a - find targets from entries
val locatedTargets = for {
a <- targets
b <- list//.filter(entry => entry.zone == zone)
if b.obj.HasGUID && a.obj.HasGUID && comparator.Test(b, a)
} yield b
if(locatedTargets.nonEmpty) {
//b - entries, after the found targets are removed (cull any non-GUID entries while at it)
val retained = for {
a <- locatedTargets
b <- list
if b.obj.HasGUID && a.obj.HasGUID && !comparator.Test(b, a)
} yield b
(locatedTargets, retained)
}
else {
(Nil, list)
}
}
}
else {
list.partition(entry => entry.zone == zone)
}
}
}
object SupportActor {
class Entry(val obj : PlanetSideGameObject, val zone : Zone, val duration : Long) {
val time : Long = System.nanoTime
}
/**
* "Hurrying" shifts entries with the discovered objects (in the same `zone`)
* through their first task and into the second pool.
* If the list of targets is empty, all discovered objects in the given zone will be considered targets.
* @param targets a list of objects to match
* @param zone the zone in which these objects exist;
* the assumption is that all these target objects are registered to this zone
*/
case class HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone)
/**
* "Hurrying" shifts all entries through their first task and into the second pool.
*/
case class HurryAll()
/**
* "Clearing" cancels entries with the discovered objects (in the same `zone`)
* if they are discovered in the first pool of objects.
* Those entries will no longer be affected by any actions performed by the removal process until re-submitted.
* If the list of targets is empty, all discovered objects in the given zone will be considered targets.
* @param targets a list of objects to match
* @param zone the zone in which these objects exist;
* the assumption is that all these target objects are registered to this zone
*/
case class ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone)
/**
* "Clearing" cancels all entries if they are discovered in the first pool of objects.
* Those entries will no longer be affected by any actions performed by the removal process until re-submitted.
*/
case class ClearAll()
/**
* Get the index of an entry in the list of entries.
* @param iter an `Iterator` of entries
* @param target the specific entry to be found
* @param index the incrementing index value
* @return the index of the entry in the list, if a match to the target is found
*/
@tailrec private def recursiveFind[A <: SupportActor.Entry](comparator : SimilarityComparator[A])(iter : Iterator[A], target : A, index : Int = 0) : Option[Int] = {
if(!iter.hasNext) {
None
}
else {
val entry = iter.next
if(entry.obj.HasGUID && target.obj.HasGUID && comparator.Test(entry, target)) {
Some(index)
}
else {
recursiveFind(comparator)(iter, target, index + 1)
}
}
}
}

View file

@ -0,0 +1,37 @@
// Copyright (c) 2017 PSForever
package services.support
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.zones.Zone
trait SupportActorCaseConversions {
/**
* A mask for converting between a class local and `SupportActor.HurrySpecific`.
* @param targets a list of objects to match
* @param zone the zone in which these objects exist
* @return a `SupportActor.HurrySpecific` object
*/
def HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone) : SupportActor.HurrySpecific =
SupportActor.HurrySpecific(targets, zone)
/**
* A mask for converting between a class local and `SupportActor.HurryAll`.
* @return a `SupportActor.HurryAll` object
*/
def HurryAll() : SupportActor.HurryAll = SupportActor.HurryAll()
/**
* A mask for converting between a class local and `SupportActor.ClearSpecific`.
* @param targets a list of objects to match
* @param zone the zone in which these objects exist
* @return a `SupportActor.ClearSpecific` object
*/
def ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone) : SupportActor.ClearSpecific =
SupportActor.ClearSpecific(targets, zone)
/**
* A mask for converting between a class local and `SupportActor.ClearAll`.
* @return a `SupportActor.ClearAll` object
*/
def ClearAll() : SupportActor.ClearAll = SupportActor.ClearAll()
}

View file

@ -15,6 +15,7 @@ object VehicleAction {
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, bailType : BailType.Value, unk2 : Boolean) extends Action final case class DismountVehicle(player_guid : PlanetSideGUID, bailType : BailType.Value, unk2 : Boolean) extends Action
final case class EquipmentInSlot(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, equipment : Equipment) extends Action
final case class InventoryState(player_guid : PlanetSideGUID, obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Action final case class InventoryState(player_guid : PlanetSideGUID, obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Action
final case class InventoryState2(player_guid : PlanetSideGUID, obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Action final case class InventoryState2(player_guid : PlanetSideGUID, obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Action
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action

View file

@ -1,10 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package services.vehicle package services.vehicle
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.{PlanetSideGameObject, Vehicle} import net.psforever.objects.{PlanetSideGameObject, Vehicle}
import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID}
import net.psforever.packet.game.objectcreate.ConstructorData import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.types.{BailType, DriveState, Vector3} import net.psforever.types.{BailType, DriveState, Vector3}
@ -17,6 +18,7 @@ object VehicleResponse {
final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response
final case class DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response final case class DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response
final case class DismountVehicle(bailType : BailType.Value , unk2 : Boolean) extends Response final case class DismountVehicle(bailType : BailType.Value , unk2 : Boolean) extends Response
final case class EquipmentInSlot(pkt : ObjectCreateMessage) extends Response
final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response
final case class InventoryState2(obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Response final case class InventoryState2(obj_guid : PlanetSideGUID, parent_guid : PlanetSideGUID, value : Int) extends Response
final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response final case class KickPassenger(seat_num : Int, kickedByDriver : Boolean, vehicle_guid : PlanetSideGUID) extends Response

View file

@ -4,7 +4,9 @@ package services.vehicle
import akka.actor.{Actor, ActorRef, Props} import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import services.vehicle.support.VehicleRemover import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import services.vehicle.support.{TurretUpgrader, VehicleRemover}
import net.psforever.types.DriveState import net.psforever.types.DriveState
import services.{GenericEventBus, RemoverActor, Service} import services.{GenericEventBus, RemoverActor, Service}
@ -12,6 +14,7 @@ import scala.concurrent.duration._
class VehicleService extends Actor { class VehicleService extends Actor {
private val vehicleDecon : ActorRef = context.actorOf(Props[VehicleRemover], "vehicle-decon-agent") private val vehicleDecon : ActorRef = context.actorOf(Props[VehicleRemover], "vehicle-decon-agent")
private val turretUpgrade : ActorRef = context.actorOf(Props[TurretUpgrader], "turret-upgrade-agent")
private [this] val log = org.log4s.getLogger private [this] val log = org.log4s.getLogger
override def preStart = { override def preStart = {
@ -53,6 +56,18 @@ class VehicleService extends Actor {
VehicleEvents.publish( VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(bailType, unk2)) VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(bailType, unk2))
) )
case VehicleAction.EquipmentInSlot(player_guid, target_guid, slot, equipment) =>
val definition = equipment.Definition
val pkt = ObjectCreateMessage(
definition.ObjectId,
equipment.GUID,
ObjectCreateMessageParent(target_guid, slot),
definition.Packet.ConstructorData(equipment).get
)
ObjectCreateMessageParent(target_guid, slot)
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.EquipmentInSlot(pkt))
)
case VehicleAction.InventoryState(player_guid, obj, parent_guid, start, con_data) => case VehicleAction.InventoryState(player_guid, obj, parent_guid, start, con_data) =>
VehicleEvents.publish( VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data)) VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data))
@ -114,6 +129,10 @@ class VehicleService extends Actor {
case VehicleServiceMessage.Decon(msg) => case VehicleServiceMessage.Decon(msg) =>
vehicleDecon forward msg vehicleDecon forward msg
//message to TurretUpgrader
case VehicleServiceMessage.TurretUpgrade(msg) =>
turretUpgrade forward msg
//from VehicleSpawnControl //from VehicleSpawnControl
case VehicleSpawnPad.ConcealPlayer(player_guid, zone_id) => case VehicleSpawnPad.ConcealPlayer(player_guid, zone_id) =>
VehicleEvents.publish( VehicleEvents.publish(

View file

@ -12,5 +12,7 @@ object VehicleServiceMessage {
final case class Decon(msg : Any) final case class Decon(msg : Any)
final case class TurretUpgrade(msg : Any)
final case class AMSDeploymentChange(zone : Zone) final case class AMSDeploymentChange(zone : Zone)
} }

View file

@ -0,0 +1,260 @@
// Copyright (c) 2017 PSForever
package services.vehicle.support
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects.{AmmoBox, DefaultCancellable, PlanetSideGameObject, Tool}
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.serverobject.turret.{MannedTurret, TurretUpgrade}
import net.psforever.objects.vehicles.MountedWeapons
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import services.{Service, ServiceManager}
import scala.concurrent.duration._
class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] {
var task : Cancellable = DefaultCancellable.obj
var list : List[TurretUpgrader.Entry] = List()
private var taskResolver : ActorRef = Actor.noSender
val sameEntryComparator = new SimilarityComparator[TurretUpgrader.Entry]() {
def Test(entry1 : TurretUpgrader.Entry, entry2 : TurretUpgrader.Entry) : Boolean = {
entry1.obj == entry2.obj && entry1.zone == entry2.zone && entry1.obj.GUID == entry2.obj.GUID
}
}
/**
* Send the initial message that requests a task resolver for assisting in the removal process.
*/
override def preStart() : Unit = {
super.preStart()
self ! Service.Startup()
}
/**
* Sufficiently clean up the current contents of these waiting removal jobs.
* Cancel all timers, rush all entries in the lists through their individual steps, then empty the lists.
* This is an improved `HurryAll`.
*/
override def postStop() : Unit = {
super.postStop()
task.cancel
list.foreach { UpgradeTurretAmmo }
list = Nil
taskResolver = ActorRef.noSender
}
def CreateEntry(obj : PlanetSideGameObject, zone : Zone, upgrade : TurretUpgrade.Value, duration : Long) = TurretUpgrader.Entry(obj, zone, upgrade, duration)
def InclusionTest(entry : TurretUpgrader.Entry) : Boolean = entry.obj.isInstanceOf[MannedTurret]
def receive : Receive = {
case Service.Startup() =>
ServiceManager.serviceManager ! ServiceManager.Lookup("taskResolver") //ask for a resolver to deal with the GUID system
case ServiceManager.LookupResult("taskResolver", endpoint) =>
taskResolver = endpoint
context.become(Processing)
case msg =>
debug(s"received message $msg before being properly initialized")
}
def Processing : Receive = entryManagementBehaviors
.orElse {
case TurretUpgrader.AddTask(turret, zone, upgrade, duration) =>
val lengthOfTime = duration.getOrElse(TurretUpgrader.StandardUpgradeLifetime).toNanos
if(lengthOfTime > (1 second).toNanos) { //don't even bother if it's too short; it'll revert near instantly
val entry = CreateEntry(turret, zone, TurretUpgrade.None, lengthOfTime)
UpgradeTurretAmmo(CreateEntry(turret, zone, upgrade, lengthOfTime))
if(list.isEmpty) {
//we were the only entry so the event must be started from scratch
list = List(entry)
trace(s"a task has been added: $entry")
RetimeFirstTask()
}
else{
val oldHead = list.head
if(!list.exists(test => TurretUpgrader.Similarity(test, entry))) {
list = (list :+ entry).sortBy(entry => entry.time + entry.duration)
trace(s"a task has been added: $entry")
if(oldHead != list.head) {
RetimeFirstTask()
}
}
}
}
case TurretUpgrader.Downgrade() =>
task.cancel
val now : Long = System.nanoTime
val (in, out) = list.partition(entry => { now - entry.time >= entry.duration }) //&& entry.obj.Seats.values.count(_.isOccupied) == 0
list = out
in.foreach { UpgradeTurretAmmo }
RetimeFirstTask()
case _ => ;
}
def RetimeFirstTask(now : Long = System.nanoTime) : Unit = {
task.cancel
if(list.nonEmpty) {
val short_timeout : FiniteDuration = math.max(1, list.head.duration - (now - list.head.time)) nanoseconds
import scala.concurrent.ExecutionContext.Implicits.global
task = context.system.scheduler.scheduleOnce(short_timeout, self, TurretUpgrader.Downgrade())
}
}
def HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = {
PartitionTargetsFromList(list, targets.map { TurretUpgrader.Entry(_, zone, TurretUpgrade.None, 0) }, zone) match {
case (Nil, _) =>
debug(s"no tasks matching the targets $targets have been hurried")
case (in, out) =>
debug(s"the following tasks have been hurried: $in")
in.foreach { UpgradeTurretAmmo }
list = out //.sortBy(entry => entry.time + entry.duration)
if(out.nonEmpty) {
RetimeFirstTask()
}
}
}
def HurryAll() : Unit = {
trace("all tasks have been hurried")
task.cancel
list.foreach { UpgradeTurretAmmo }
list = Nil
}
def ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = {
PartitionTargetsFromList(list, targets.map { TurretUpgrader.Entry(_, zone, TurretUpgrade.None, 0) }, zone) match {
case (Nil, _) =>
debug(s"no tasks matching the targets $targets have been cleared")
case (in, out) =>
debug(s"the following tasks have been cleared: $in")
list = out //.sortBy(entry => entry.time + entry.duration)
if(out.nonEmpty) {
RetimeFirstTask()
}
}
}
def ClearAll() : Unit = {
task.cancel
list = Nil
}
/**
* The process of upgrading a turret is nearly complete.
* After the upgrade status is changed, the internal structure of the turret's weapons change to suit the configuration.
* Of special importance are internal ammo supplies of the changing weapon,
* the original ammunition that must be un-registered,
* and the new boxes that must be registered so the weapon may be introduced into the game world properly.
* @param entry na
*/
def UpgradeTurretAmmo(entry : TurretUpgrader.Entry) : Unit = {
val target = entry.obj.asInstanceOf[MannedTurret]
val zone = entry.zone
val zoneId = zone.Id
val upgrade = entry.upgrade
val guid = zone.GUID
val turretGUID = target.GUID
//kick all occupying players for duration of conversion
target.Seats.values
.filter { _.isOccupied }
.foreach({seat =>
val tplayer = seat.Occupant.get
seat.Occupant = None
tplayer.VehicleSeated = None
if(tplayer.HasGUID) {
context.parent ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(tplayer.GUID, 4, false, turretGUID))
}
})
info(s"Converting manned wall turret weapon to $upgrade")
val oldBoxesTask = AllMountedWeaponMagazines(target)
.map(box => GUIDTask.UnregisterEquipment(box)(guid))
.toList
target.Upgrade = upgrade //perform upgrade
val newBoxesTask = TaskResolver.GiveTask(
new Task() {
private val localFunc : ()=>Unit = FinishUpgradingTurret(entry)
override def isComplete = Task.Resolution.Success
def Execute(resolver : ActorRef) : Unit = {
localFunc()
resolver ! scala.util.Success(this)
}
}, AllMountedWeaponMagazines(target).map(box => GUIDTask.RegisterEquipment(box)(guid)).toList
)
taskResolver ! TaskResolver.GiveTask(
new Task() {
def Execute(resolver : ActorRef) : Unit = {
resolver ! scala.util.Success(this)
}
}, oldBoxesTask :+ newBoxesTask
)
}
/**
* From an object that has mounted weapons, parse all of the internal ammunition loaded into all of the weapons.
* @param target the object with mounted weaponry
* @return all of the internal ammunition objects
*/
def AllMountedWeaponMagazines(target : MountedWeapons) : Iterable[AmmoBox] = {
target.Weapons
.values
.map { _.Equipment }
.collect { case Some(tool : Tool) => tool.AmmoSlots }
.flatMap { _.map { _.Box } }
}
/**
* Finish upgrading the turret by announcing to other players that the weapon type has changed.
* By this point, a prior required action that required that new ammunition objects had to be registered.
* It is now safe to announce that clients can update to the new weapon.
* @param entry na
*/
def FinishUpgradingTurret(entry : TurretUpgrader.Entry)() : Unit = {
val target = entry.obj.asInstanceOf[MannedTurret]
val zone = entry.zone
info(s"Wall turret finished ${target.Upgrade} upgrade")
val targetGUID = target.GUID
target.Weapons
.map({ case (index, slot) => (index, slot.Equipment) })
.collect { case (index, Some(tool : Tool)) =>
context.parent ! VehicleServiceMessage(
zone.Id,
VehicleAction.EquipmentInSlot(PlanetSideGUID(0), targetGUID, index, tool)
)
}
}
}
object TurretUpgrader extends SupportActorCaseConversions {
private val StandardUpgradeLifetime : FiniteDuration = 30 minutes
/**
* All information necessary to apply to the removal process to produce an effect.
* Internally, all entries have a "time created" field.
* @param _obj the target
* @param _zone the zone in which this target is registered
* @param upgrade the next upgrade state for this turret
* @param _duration how much longer the target will exist in its current state (in nanoseconds)
*/
case class Entry(_obj : PlanetSideGameObject, _zone : Zone, upgrade : TurretUpgrade.Value, _duration : Long) extends SupportActor.Entry(_obj, _zone, _duration)
final case class AddTask(turret : MannedTurret, zone : Zone, upgrade : TurretUpgrade.Value, duration : Option[FiniteDuration] = None)
final case class Downgrade()
private def Similarity(entry1 : TurretUpgrader.Entry, entry2 : TurretUpgrader.Entry) : Boolean = {
entry1.obj == entry2.obj && entry1.zone == entry2.zone && entry1.obj.GUID == entry2.obj.GUID
}
}

View file

@ -0,0 +1,51 @@
package base
// Copyright (c) 2017 PSForever
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import com.typesafe.config.ConfigFactory
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import org.specs2.specification.Scope
import scala.collection.mutable
import scala.concurrent.duration.FiniteDuration
abstract class ActorTest(sys : ActorSystem = ActorSystem("system", ConfigFactory.parseMap(ActorTest.LoggingConfig)))
extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
}
object ActorTest {
import scala.collection.JavaConverters._
private val LoggingConfig = Map(
"akka.loggers" -> List("akka.testkit.TestEventListener").asJava,
"akka.loglevel" -> "OFF",
"akka.stdout-loglevel" -> "OFF",
"akka.log-dead-letters" -> "OFF"
).asJava
/**
* A (potential) workaround to a Travis CI issue involving polling a series of messages over a period of time.
* Running the test in isolation works every time.
* Running the test as part of a series produces mixed results.
* Travis CI fails the test every time by not getting any messages.
* @see TestKit.receiveN
* @param n the number of messages to poll
* @param timeout how long to wait for each message
* @param sys what to poll
* @return a list of messages
*/
def receiveMultiple(n : Int, timeout : FiniteDuration, sys : TestKit) : List[Any] = {
assert(0 < n, s"number of expected messages must be positive non-zero integer - $n")
val out = {
val msgs = mutable.ListBuffer[Any]()
(0 until n).foreach(_ => {
msgs += sys.receiveOne(timeout)
})
msgs.toList
}
out
}
}

View file

@ -14,7 +14,7 @@ class UseItemMessageTest extends Specification {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType) => case UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType) =>
avatar_guid mustEqual PlanetSideGUID(75) avatar_guid mustEqual PlanetSideGUID(75)
unk1 mustEqual 0 unk1 mustEqual PlanetSideGUID(0)
object_guid mustEqual PlanetSideGUID(372) object_guid mustEqual PlanetSideGUID(372)
unk2 mustEqual 0xFFFFFFFFL unk2 mustEqual 0xFFFFFFFFL
unk3 mustEqual false unk3 mustEqual false
@ -30,7 +30,7 @@ class UseItemMessageTest extends Specification {
} }
"encode" in { "encode" in {
val msg = UseItemMessage(PlanetSideGUID(75), 0, PlanetSideGUID(372), 0xFFFFFFFFL, false, Vector3(5.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), 11, 25, 0, 364) val msg = UseItemMessage(PlanetSideGUID(75), PlanetSideGUID(0), PlanetSideGUID(372), 0xFFFFFFFFL, false, Vector3(5.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), 11, 25, 0, 364)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -1,25 +0,0 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import com.typesafe.config.{ConfigFactory, ConfigValueFactory}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import org.specs2.specification.Scope
abstract class ActorTest(sys : ActorSystem = ActorSystem("system", ConfigFactory.parseMap(ActorTest.LoggingConfig)))
extends TestKit(sys) with Scope with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
}
object ActorTest {
import scala.collection.JavaConverters._
private val LoggingConfig = Map(
"akka.loggers" -> List("akka.testkit.TestEventListener").asJava,
"akka.loglevel" -> "OFF",
"akka.stdout-loglevel" -> "OFF",
"akka.log-dead-letters" -> "OFF"
).asJava
}

View file

@ -3,6 +3,7 @@ package objects
import akka.actor.Props import akka.actor.Props
import akka.testkit.TestProbe import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle} import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided} import net.psforever.objects.serverobject.pad.process.{AutoDriveControls, VehicleSpawnControlGuided}

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{ActorRef, Props} import akka.actor.{ActorRef, Props}
import base.ActorTest
import net.psforever.objects.GlobalDefinitions import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
@ -128,12 +129,11 @@ class BuildingControl2Test extends ActorTest {
val bldg = Building(10, Zone.Nowhere, StructureType.Building) val bldg = Building(10, Zone.Nowhere, StructureType.Building)
bldg.Faction = PlanetSideEmpire.TR bldg.Faction = PlanetSideEmpire.TR
bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test") bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test")
bldg.Actor ! "startup"
"Building Control" should { "Building Control" should {
"convert and assert faction affinity on convert request" in { "convert and assert faction affinity on convert request" in {
expectNoMsg(250 milliseconds) expectNoMsg(500 milliseconds)
bldg.Actor ! "startup"
expectNoMsg(250 milliseconds)
assert(bldg.Faction == PlanetSideEmpire.TR) assert(bldg.Faction == PlanetSideEmpire.TR)
bldg.Actor ! FactionAffinity.ConvertFactionAffinity(PlanetSideEmpire.VS) bldg.Actor ! FactionAffinity.ConvertFactionAffinity(PlanetSideEmpire.VS)
@ -159,12 +159,11 @@ class BuildingControl3Test extends ActorTest {
door2.Actor = system.actorOf(Props(classOf[DoorControl], door2), "door2-test") door2.Actor = system.actorOf(Props(classOf[DoorControl], door2), "door2-test")
bldg.Amenities = door2 bldg.Amenities = door2
bldg.Amenities = door1 bldg.Amenities = door1
bldg.Actor ! "startup"
"Building Control" should { "Building Control" should {
"convert and assert faction affinity on convert request, and for each of its amenities" in { "convert and assert faction affinity on convert request, and for each of its amenities" in {
expectNoMsg(250 milliseconds) expectNoMsg(500 milliseconds)
bldg.Actor ! "startup"
expectNoMsg(250 milliseconds)
assert(bldg.Faction == PlanetSideEmpire.TR) assert(bldg.Faction == PlanetSideEmpire.TR)
assert(bldg.Amenities.length == 2) assert(bldg.Amenities.length == 2)
@ -172,7 +171,8 @@ class BuildingControl3Test extends ActorTest {
assert(bldg.Amenities(1) == door1) assert(bldg.Amenities(1) == door1)
bldg.Actor ! FactionAffinity.ConvertFactionAffinity(PlanetSideEmpire.VS) bldg.Actor ! FactionAffinity.ConvertFactionAffinity(PlanetSideEmpire.VS)
val reply = receiveN(3, Duration.create(500, "ms")) val reply = ActorTest.receiveMultiple(3, 500 milliseconds, this)
//val reply = receiveN(3, Duration.create(5000, "ms"))
assert(reply.length == 3) assert(reply.length == 3)
var building_count = 0 var building_count = 0
var door_count = 0 var door_count = 0

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{Actor, ActorSystem, Props} import akka.actor.{Actor, ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.{GlobalDefinitions, Vehicle} import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior} import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{ActorRef, ActorSystem, Props} import akka.actor.{ActorRef, ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.{Avatar, GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.doors.{Door, DoorControl} import net.psforever.objects.serverobject.doors.{Door, DoorControl}
import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.serverobject.structures.{Building, StructureType}
@ -42,7 +43,7 @@ class DoorTest extends Specification {
} }
"be opened and closed (2; toggle)" in { "be opened and closed (2; toggle)" in {
val msg = UseItemMessage(PlanetSideGUID(6585), 0, PlanetSideGUID(372), 4294967295L, false, Vector3(5.0f,0.0f,0.0f), Vector3(0.0f,0.0f,0.0f), 11, 25, 0, 364) val msg = UseItemMessage(PlanetSideGUID(6585), PlanetSideGUID(0), PlanetSideGUID(372), 4294967295L, false, Vector3(5.0f,0.0f,0.0f), Vector3(0.0f,0.0f,0.0f), 11, 25, 0, 364)
val door = Door(GlobalDefinitions.door) val door = Door(GlobalDefinitions.door)
door.Open mustEqual None door.Open mustEqual None
door.Use(player, msg) door.Use(player, msg)
@ -74,7 +75,7 @@ class DoorTest extends Specification {
} }
} }
class DoorControl1Test extends ActorTest() { class DoorControl1Test extends ActorTest {
"DoorControl" should { "DoorControl" should {
"construct" in { "construct" in {
val door = Door(GlobalDefinitions.door) val door = Door(GlobalDefinitions.door)
@ -84,11 +85,11 @@ class DoorControl1Test extends ActorTest() {
} }
} }
class DoorControl2Test extends ActorTest() { class DoorControl2Test extends ActorTest {
"DoorControl" should { "DoorControl" should {
"open on use" in { "open on use" in {
val (player, door) = DoorControlTest.SetUpAgents(PlanetSideEmpire.TR) val (player, door) = DoorControlTest.SetUpAgents(PlanetSideEmpire.TR)
val msg = UseItemMessage(PlanetSideGUID(1), 0, PlanetSideGUID(2), 0L, false, Vector3(0f,0f,0f),Vector3(0f,0f,0f),0,0,0,0L) //faked val msg = UseItemMessage(PlanetSideGUID(1), PlanetSideGUID(0), PlanetSideGUID(2), 0L, false, Vector3(0f,0f,0f),Vector3(0f,0f,0f),0,0,0,0L) //faked
assert(door.Open.isEmpty) assert(door.Open.isEmpty)
door.Actor ! Door.Use(player, msg) door.Actor ! Door.Use(player, msg)
@ -103,7 +104,7 @@ class DoorControl2Test extends ActorTest() {
} }
} }
class DoorControl3Test extends ActorTest() { class DoorControl3Test extends ActorTest {
"DoorControl" should { "DoorControl" should {
"do nothing if given garbage" in { "do nothing if given garbage" in {
val (_, door) = DoorControlTest.SetUpAgents(PlanetSideEmpire.TR) val (_, door) = DoorControlTest.SetUpAgents(PlanetSideEmpire.TR)

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{Actor, ActorSystem, Props} import akka.actor.{Actor, ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.{GlobalDefinitions, Vehicle} import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
@ -55,7 +56,7 @@ class FactionAffinityTest extends Specification {
} }
} }
class FactionAffinity1Test extends ActorTest() { class FactionAffinity1Test extends ActorTest {
"FactionAffinity" should { "FactionAffinity" should {
"assert affinity on confirm request" in { "assert affinity on confirm request" in {
val obj = FactionAffinityTest.SetUpAgent val obj = FactionAffinityTest.SetUpAgent
@ -71,7 +72,7 @@ class FactionAffinity1Test extends ActorTest() {
} }
} }
class FactionAffinity2Test extends ActorTest() { class FactionAffinity2Test extends ActorTest {
"FactionAffinity" should { "FactionAffinity" should {
"assert affinity on assert request" in { "assert affinity on assert request" in {
val obj = FactionAffinityTest.SetUpAgent val obj = FactionAffinityTest.SetUpAgent
@ -87,7 +88,7 @@ class FactionAffinity2Test extends ActorTest() {
} }
} }
class FactionAffinity3Test extends ActorTest() { class FactionAffinity3Test extends ActorTest {
"FactionAffinity" should { "FactionAffinity" should {
"convert and assert affinity on convert request" in { "convert and assert affinity on convert request" in {
val obj = FactionAffinityTest.SetUpAgent val obj = FactionAffinityTest.SetUpAgent

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{ActorRef, ActorSystem, Props} import akka.actor.{ActorRef, ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.{Avatar, GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl} import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl}
@ -22,7 +23,7 @@ class IFFLockTest extends Specification {
} }
} }
class IFFLockControl1Test extends ActorTest() { class IFFLockControl1Test extends ActorTest {
"IFFLockControl" should { "IFFLockControl" should {
"construct" in { "construct" in {
val lock = IFFLock(GlobalDefinitions.lock_external) val lock = IFFLock(GlobalDefinitions.lock_external)
@ -32,7 +33,7 @@ class IFFLockControl1Test extends ActorTest() {
} }
} }
class IFFLockControl2Test extends ActorTest() { class IFFLockControl2Test extends ActorTest {
"IFFLockControl" should { "IFFLockControl" should {
"can hack" in { "can hack" in {
val (player, lock) = IFFLockControlTest.SetUpAgents(PlanetSideEmpire.TR) val (player, lock) = IFFLockControlTest.SetUpAgents(PlanetSideEmpire.TR)
@ -46,7 +47,7 @@ class IFFLockControl2Test extends ActorTest() {
} }
} }
class IFFLockControl3Test extends ActorTest() { class IFFLockControl3Test extends ActorTest {
"IFFLockControl" should { "IFFLockControl" should {
"can hack" in { "can hack" in {
val (player, lock) = IFFLockControlTest.SetUpAgents(PlanetSideEmpire.TR) val (player, lock) = IFFLockControlTest.SetUpAgents(PlanetSideEmpire.TR)

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{ActorRef, Props} import akka.actor.{ActorRef, Props}
import base.ActorTest
import net.psforever.objects.GlobalDefinitions import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.mblocker.{Locker, LockerControl} import net.psforever.objects.serverobject.mblocker.{Locker, LockerControl}

View file

@ -0,0 +1,179 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{ActorRef, Props}
import base.ActorTest
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Tool}
import net.psforever.objects.definition.ToolDefinition
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.turret.{MannedTurret, MannedTurretControl, MannedTurretDefinition, TurretUpgrade}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import org.specs2.mutable.Specification
import scala.collection.mutable
import scala.concurrent.duration._
class MannedTurretTest extends Specification {
"MannedTurretTest" should {
"define" in {
val obj = new MannedTurretDefinition(480)
obj.Weapons mustEqual mutable.HashMap.empty[TurretUpgrade.Value, ToolDefinition]
obj.ReserveAmmunition mustEqual false
obj.FactionLocked mustEqual true
obj.MaxHealth mustEqual 100
obj.MountPoints mustEqual mutable.HashMap.empty[Int,Int]
}
"construct" in {
val obj = MannedTurret(GlobalDefinitions.manned_turret)
obj.Weapons.size mustEqual 1
obj.Weapons(1).Equipment match {
case Some(tool : Tool) =>
tool.Definition mustEqual GlobalDefinitions.phalanx_sgl_hevgatcan
case _ =>
ko
}
obj.Seats.size mustEqual 1
obj.Seats(0).ControlledWeapon mustEqual Some(1)
obj.MountPoints.size mustEqual 1
obj.MountPoints(1) mustEqual 0
obj.Health mustEqual 3600
obj.Upgrade mustEqual TurretUpgrade.None
obj.Jammered mustEqual false
obj.Health = 360
obj.Health mustEqual 360
obj.Jammered = true
obj.Jammered mustEqual true
}
"upgrade to a different weapon" in {
val obj = MannedTurret(GlobalDefinitions.manned_turret)
obj.Upgrade = TurretUpgrade.None
obj.Weapons(1).Equipment match {
case Some(tool : Tool) =>
tool.Definition mustEqual GlobalDefinitions.phalanx_sgl_hevgatcan
tool.FireModeIndex mustEqual 0
tool.NextFireMode
tool.FireModeIndex mustEqual 0 //one fire mode
case _ =>
ko
}
//upgrade
obj.Upgrade = TurretUpgrade.AVCombo
obj.Weapons(1).Equipment match {
case Some(tool : Tool) =>
tool.Definition mustEqual GlobalDefinitions.phalanx_avcombo
tool.FireModeIndex mustEqual 0
tool.ProjectileType mustEqual GlobalDefinitions.phalanx_projectile.ProjectileType
tool.NextFireMode
tool.FireModeIndex mustEqual 1
tool.ProjectileType mustEqual GlobalDefinitions.phalanx_av_projectile.ProjectileType
case _ =>
ko
}
//revert
obj.Upgrade = TurretUpgrade.None
obj.Weapons(1).Equipment match {
case Some(tool : Tool) =>
tool.Definition mustEqual GlobalDefinitions.phalanx_sgl_hevgatcan
case _ =>
ko
}
}
}
}
class MannedTurretControl1Test extends ActorTest {
"MannedTurretControl" should {
"construct" in {
val obj = MannedTurret(GlobalDefinitions.manned_turret)
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
assert(obj.Actor != ActorRef.noSender)
}
}
}
class MannedTurretControl2Test extends ActorTest {
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj = MannedTurret(GlobalDefinitions.manned_turret)
obj.GUID = PlanetSideGUID(1)
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
bldg.Amenities = obj
bldg.Faction = PlanetSideEmpire.TR
"MannedTurretControl" should {
"seat on faction affiliation when FactionLock is true" in {
assert(player.Faction == PlanetSideEmpire.TR)
assert(obj.Faction == PlanetSideEmpire.TR)
assert(obj.Definition.FactionLocked)
obj.Actor ! Mountable.TryMount(player, 0)
val reply = receiveOne(300 milliseconds)
reply match {
case msg : Mountable.MountMessages =>
assert(msg.response.isInstanceOf[Mountable.CanMount])
case _ =>
assert(false)
}
}
}
}
class MannedTurretControl3Test extends ActorTest {
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val obj = MannedTurret(GlobalDefinitions.manned_turret)
obj.GUID = PlanetSideGUID(1)
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
bldg.Amenities = obj
"MannedTurretControl" should {
"block seating on mismatched faction affiliation when FactionLock is true" in {
assert(player.Faction == PlanetSideEmpire.TR)
assert(obj.Faction == PlanetSideEmpire.NEUTRAL)
assert(obj.Definition.FactionLocked)
obj.Actor ! Mountable.TryMount(player, 0)
val reply = receiveOne(300 milliseconds)
reply match {
case msg : Mountable.MountMessages =>
assert(msg.response.isInstanceOf[Mountable.CanNotMount])
case _ =>
assert(false)
}
}
}
}
class MannedTurretControl4Test extends ActorTest {
val player = Player(Avatar("", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
val objDef = new MannedTurretDefinition(480)
objDef.FactionLocked = false
val obj = MannedTurret(objDef)
obj.GUID = PlanetSideGUID(1)
obj.Actor = system.actorOf(Props(classOf[MannedTurretControl], obj), "turret-control")
val bldg = Building(0, Zone.Nowhere, StructureType.Building)
bldg.Amenities = obj
"MannedTurretControl" should {
"seating even with mismatched faction affiliation when FactionLock is false" in {
assert(player.Faction == PlanetSideEmpire.TR)
assert(obj.Faction == PlanetSideEmpire.NEUTRAL)
assert(!obj.Definition.FactionLocked)
obj.Actor ! Mountable.TryMount(player, 0)
val reply = receiveOne(300 milliseconds)
reply match {
case msg : Mountable.MountMessages =>
assert(msg.response.isInstanceOf[Mountable.CanMount])
case _ =>
assert(false)
}
}
}
}

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{Actor, ActorRef, Props} import akka.actor.{Actor, ActorRef, Props}
import base.ActorTest
import net.psforever.objects.{Avatar, Player} import net.psforever.objects.{Avatar, Player}
import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition} import net.psforever.objects.definition.{ObjectDefinition, SeatDefinition}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior} import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
@ -12,7 +13,7 @@ import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
class MountableControl1Test extends ActorTest() { class MountableControl1Test extends ActorTest {
"MountableControl" should { "MountableControl" should {
"construct" in { "construct" in {
val obj = new MountableTest.MountableTestObject val obj = new MountableTest.MountableTestObject
@ -22,7 +23,7 @@ class MountableControl1Test extends ActorTest() {
} }
} }
class MountableControl2Test extends ActorTest() { class MountableControl2Test extends ActorTest {
"MountableControl" should { "MountableControl" should {
"let a player mount" in { "let a player mount" in {
val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) val player = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
@ -43,7 +44,7 @@ class MountableControl2Test extends ActorTest() {
} }
} }
class MountableControl3Test extends ActorTest() { class MountableControl3Test extends ActorTest {
"MountableControl" should { "MountableControl" should {
"block a player from mounting" in { "block a player from mounting" in {
val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) val player1 = Player(Avatar("test1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))

View file

@ -0,0 +1,332 @@
// Copyright (c) 2017 PSForever
package objects
import akka.actor.{Actor, Props}
import akka.routing.RandomPool
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.resourcesilo.{ResourceSilo, ResourceSiloControl, ResourceSiloDefinition}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{PlanetSideGUID, UseItemMessage}
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
import org.specs2.mutable.Specification
import services.ServiceManager
import services.avatar.{AvatarAction, AvatarService, AvatarServiceMessage}
import scala.concurrent.duration._
class ResourceSiloTest extends Specification {
"Resource Silo" should {
"define" in {
val obj = new ResourceSiloDefinition
obj.ObjectId mustEqual 731
}
"construct" in {
val obj = ResourceSilo()
obj.Definition mustEqual GlobalDefinitions.resource_silo
obj.MaximumCharge mustEqual 1000
obj.ChargeLevel mustEqual 0
obj.LowNtuWarningOn mustEqual 0
obj.CapacitorDisplay mustEqual 0
//
obj.ChargeLevel = 50
obj.LowNtuWarningOn = 25
obj.CapacitorDisplay = 75
obj.ChargeLevel mustEqual 50
obj.LowNtuWarningOn mustEqual 25
obj.CapacitorDisplay mustEqual 75
}
"charge level can not exceed limits(0 to maximum)" in {
val obj = ResourceSilo()
obj.ChargeLevel mustEqual 0
obj.ChargeLevel = -5
obj.ChargeLevel mustEqual 0
obj.ChargeLevel = 1250
obj.ChargeLevel mustEqual 1000
obj.ChargeLevel mustEqual obj.MaximumCharge
}
"using the silo generates a charge event" in {
val msg = UseItemMessage(PlanetSideGUID(1), PlanetSideGUID(0), PlanetSideGUID(2), 0L, false, Vector3(0f,0f,0f),Vector3(0f,0f,0f),0,0,0,0L) //faked
ResourceSilo().Use(ResourceSiloTest.player, msg) mustEqual ResourceSilo.ChargeEvent()
}
}
}
class ResourceSiloControlStartupTest extends ActorTest {
val serviceManager = ServiceManager.boot(system)
serviceManager ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val obj = ResourceSilo()
obj.GUID = PlanetSideGUID(1)
val probe = TestProbe()
serviceManager ! ServiceManager.Register(Props(classOf[ResourceSiloTest.ProbedAvatarService], probe), "avatar")
"Resource silo" should {
"startup properly" in {
expectNoMsg(500 milliseconds)
system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
expectNoMsg(1 seconds)
assert(true)
}
}
}
class ResourceSiloControlUseTest extends ActorTest {
val serviceManager = ServiceManager.boot(system)
serviceManager ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val probe = TestProbe()
serviceManager ! ServiceManager.Register(Props(classOf[ResourceSiloTest.ProbedAvatarService], probe), "avatar")
val msg = UseItemMessage(PlanetSideGUID(1), PlanetSideGUID(0), PlanetSideGUID(2), 0L, false, Vector3(0f,0f,0f),Vector3(0f,0f,0f),0,0,0,0L) //faked
val obj = ResourceSilo()
obj.GUID = PlanetSideGUID(1)
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
obj.Actor ! "startup"
"Resource silo" should {
"respond when being used" in {
expectNoMsg(1 seconds)
obj.Actor ! ResourceSilo.Use(ResourceSiloTest.player, msg)
val reply = receiveOne(500 milliseconds)
assert(reply.isInstanceOf[ResourceSilo.ResourceSiloMessage])
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].player == ResourceSiloTest.player)
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].msg == msg)
assert(reply.asInstanceOf[ResourceSilo.ResourceSiloMessage].response == ResourceSilo.ChargeEvent())
}
}
}
class ResourceSiloControlNtuWarningTest extends ActorTest {
val serviceManager = ServiceManager.boot(system)
serviceManager ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val probe = TestProbe()
serviceManager ! ServiceManager.Register(Props(classOf[ResourceSiloTest.ProbedAvatarService], probe), "avatar")
val obj = ResourceSilo()
obj.GUID = PlanetSideGUID(1)
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
obj.Actor ! "startup"
obj.Owner = new Building(0, Zone.Nowhere, StructureType.Building) {
ModelId = 6
}
"Resource silo" should {
"announce low ntu" in {
expectNoMsg(1 seconds)
assert(obj.LowNtuWarningOn == 0)
obj.Actor ! ResourceSilo.LowNtuWarning(10)
val reply = probe.receiveOne(500 milliseconds)
assert(obj.LowNtuWarningOn == 10)
assert(reply.isInstanceOf[AvatarServiceMessage])
assert(reply.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
assert(reply.asInstanceOf[AvatarServiceMessage]
.actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
assert(reply.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].player_guid == PlanetSideGUID(6))
assert(reply.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_type == 47)
assert(reply.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 10)
}
}
}
class ResourceSiloControlUpdate1Test extends ActorTest {
val serviceManager = ServiceManager.boot(system)
serviceManager ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val probe1 = TestProbe()
serviceManager ! ServiceManager.Register(Props(classOf[ResourceSiloTest.ProbedAvatarService], probe1), "avatar")
val obj = ResourceSilo()
obj.GUID = PlanetSideGUID(1)
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
obj.Actor ! "startup"
val bldg = new Building(0, Zone.Nowhere, StructureType.Building) {
ModelId = 6
}
val probe2 = TestProbe()
bldg.Actor = system.actorOf(Props(classOf[ResourceSiloTest.ProbedBuildingControl], probe2), "test-bldg")
obj.Owner = bldg
"Resource silo" should {
"update the charge level and capacitor display (report low ntu, power restored)" in {
expectNoMsg(1 seconds)
assert(obj.ChargeLevel == 0)
assert(obj.CapacitorDisplay == 0)
obj.Actor ! ResourceSilo.UpdateChargeLevel(105)
val reply1 = probe1.receiveOne(500 milliseconds)
val reply2 = probe2.receiveOne(500 milliseconds)
assert(obj.ChargeLevel == 105)
assert(obj.CapacitorDisplay == 1)
assert(reply1.isInstanceOf[AvatarServiceMessage])
assert(reply1.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
assert(reply1.asInstanceOf[AvatarServiceMessage]
.actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
assert(reply1.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].player_guid == PlanetSideGUID(1))
assert(reply1.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_type == 45)
assert(reply1.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 1)
assert(reply2.isInstanceOf[Building.SendMapUpdateToAllClients])
val reply3 = probe1.receiveOne(500 milliseconds)
assert(reply3.isInstanceOf[AvatarServiceMessage])
assert(reply3.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
assert(reply3.asInstanceOf[AvatarServiceMessage]
.actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
assert(reply3.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].player_guid == PlanetSideGUID(6))
assert(reply3.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_type == 48)
assert(reply3.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 0)
val reply4 = probe1.receiveOne(500 milliseconds)
assert(obj.LowNtuWarningOn == 1)
assert(reply4.isInstanceOf[AvatarServiceMessage])
assert(reply4.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
assert(reply4.asInstanceOf[AvatarServiceMessage]
.actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
assert(reply4.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].player_guid == PlanetSideGUID(6))
assert(reply4.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_type == 47)
assert(reply4.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 1)
}
}
}
class ResourceSiloControlUpdate2Test extends ActorTest {
val serviceManager = ServiceManager.boot(system)
serviceManager ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val probe1 = TestProbe()
serviceManager ! ServiceManager.Register(Props(classOf[ResourceSiloTest.ProbedAvatarService], probe1), "avatar")
val obj = ResourceSilo()
obj.GUID = PlanetSideGUID(1)
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
obj.Actor ! "startup"
val bldg = new Building(0, Zone.Nowhere, StructureType.Building) {
ModelId = 6
}
val probe2 = TestProbe()
bldg.Actor = system.actorOf(Props(classOf[ResourceSiloTest.ProbedBuildingControl], probe2), "test-bldg")
obj.Owner = bldg
"Resource silo" should {
"update the charge level and capacitor display (report good ntu)" in {
expectNoMsg(1 seconds)
obj.ChargeLevel = 100
obj.CapacitorDisplay = 1
obj.LowNtuWarningOn = 1
assert(obj.ChargeLevel == 100)
assert(obj.CapacitorDisplay == 1)
assert(obj.LowNtuWarningOn == 1)
obj.Actor ! ResourceSilo.UpdateChargeLevel(105)
val reply1 = probe1.receiveOne(500 milliseconds)
val reply2 = probe2.receiveOne(500 milliseconds)
assert(obj.ChargeLevel == 205)
assert(obj.CapacitorDisplay == 2)
assert(reply1.isInstanceOf[AvatarServiceMessage])
assert(reply1.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
assert(reply1.asInstanceOf[AvatarServiceMessage]
.actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
assert(reply1.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].player_guid == PlanetSideGUID(1))
assert(reply1.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_type == 45)
assert(reply1.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 2)
assert(reply2.isInstanceOf[Building.SendMapUpdateToAllClients])
val reply3 = probe1.receiveOne(500 milliseconds)
assert(obj.LowNtuWarningOn == 0)
assert(reply3.isInstanceOf[AvatarServiceMessage])
assert(reply3.asInstanceOf[AvatarServiceMessage].forChannel == "nowhere")
assert(reply3.asInstanceOf[AvatarServiceMessage]
.actionMessage.isInstanceOf[AvatarAction.PlanetsideAttribute])
assert(reply3.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].player_guid == PlanetSideGUID(6))
assert(reply3.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_type == 47)
assert(reply3.asInstanceOf[AvatarServiceMessage]
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 0)
}
}
}
class ResourceSiloControlNoUpdateTest extends ActorTest {
val serviceManager = ServiceManager.boot(system)
serviceManager ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
val probe1 = TestProbe()
serviceManager ! ServiceManager.Register(Props(classOf[ResourceSiloTest.ProbedAvatarService], probe1), "avatar")
val obj = ResourceSilo()
obj.GUID = PlanetSideGUID(1)
obj.Actor = system.actorOf(Props(classOf[ResourceSiloControl], obj), "test-silo")
obj.Actor ! "startup"
val bldg = new Building(0, Zone.Nowhere, StructureType.Building) {
ModelId = 6
}
val probe2 = TestProbe()
bldg.Actor = system.actorOf(Props(classOf[ResourceSiloTest.ProbedBuildingControl], probe2), "test-bldg")
obj.Owner = bldg
"Resource silo" should {
"update, but not sufficiently to change the capacitor display" in {
expectNoMsg(1 seconds)
obj.ChargeLevel = 250
obj.CapacitorDisplay = 3
obj.LowNtuWarningOn = 0
assert(obj.ChargeLevel == 250)
assert(obj.CapacitorDisplay == 3)
assert(obj.LowNtuWarningOn == 0)
obj.Actor ! ResourceSilo.UpdateChargeLevel(50)
expectNoMsg(500 milliseconds)
probe1.expectNoMsg(500 milliseconds)
probe2.expectNoMsg(500 milliseconds)
assert(obj.ChargeLevel == 300)
assert(obj.CapacitorDisplay == 3)
assert(obj.LowNtuWarningOn == 0)
}
}
}
object ResourceSiloTest {
val player = Player(Avatar("TestCharacter", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))
class ProbedAvatarService(probe : TestProbe) extends Actor {
override def receive : Receive = {
case msg =>
probe.ref ! msg
}
}
class ProbedBuildingControl(probe : TestProbe) extends Actor {
override def receive : Receive = {
case msg =>
probe.ref ! msg
}
}
class ProbedResourceSiloControl(silo : ResourceSilo, probe : TestProbe) extends ResourceSiloControl(silo) {
override def receive : Receive = {
case msg =>
super.receive.apply(msg)
probe.ref ! msg
}
}
}

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{Actor, ActorContext, Props} import akka.actor.{Actor, ActorContext, Props}
import base.ActorTest
import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.NumberPoolHub
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.objects.serverobject.ServerObjectBuilder import net.psforever.objects.serverobject.ServerObjectBuilder
@ -137,7 +138,8 @@ class ProximityTerminalObjectBuilderTest extends ActorTest {
"Terminal object" should { "Terminal object" should {
"build" in { "build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, ProximityTerminal.Constructor(medical_terminal)), hub), "term") val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
ProximityTerminal.Constructor(medical_terminal)), hub), "term")
actor ! "!" actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms")) val reply = receiveOne(Duration.create(1000, "ms"))
@ -172,7 +174,7 @@ class VehicleSpawnPadObjectBuilderTest extends ActorTest {
class LocalProjectileBuilderTest extends ActorTest { class LocalProjectileBuilderTest extends ActorTest {
import net.psforever.objects.LocalProjectile import net.psforever.objects.LocalProjectile
"Local ProjectileBuilder" should { "Local projectile object" should {
"build" in { "build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
@ -190,7 +192,7 @@ class LocalProjectileBuilderTest extends ActorTest {
class LockerObjectBuilderTest extends ActorTest { class LockerObjectBuilderTest extends ActorTest {
import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.mblocker.Locker
"LockerObjectBuilder" should { "Locker object" should {
"build" in { "build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
@ -206,9 +208,27 @@ class LockerObjectBuilderTest extends ActorTest {
} }
} }
class ResourceSiloObjectBuilderTest extends ActorTest {
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), hub), "spawn-tube")
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)
}
}
}
class SpawnTubeObjectBuilderTest extends ActorTest { class SpawnTubeObjectBuilderTest extends ActorTest {
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
"LockerObjectBuilder" should { "Spawn tube object" should {
"build" in { "build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1, val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
@ -226,6 +246,25 @@ class SpawnTubeObjectBuilderTest extends ActorTest {
} }
} }
class MannedTurretObjectBuilderTest extends ActorTest {
import net.psforever.objects.GlobalDefinitions.manned_turret
import net.psforever.objects.serverobject.turret.MannedTurret
"MannedTurretObjectBuilder" should {
"build" in {
val hub = ServerObjectBuilderTest.NumberPoolHub
val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuilderTestActor], ServerObjectBuilder(1,
MannedTurret.Constructor(manned_turret)), hub), "spawn-tube")
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
assert(reply.isInstanceOf[MannedTurret])
assert(reply.asInstanceOf[MannedTurret].HasGUID)
assert(reply.asInstanceOf[MannedTurret].GUID == PlanetSideGUID(1))
assert(reply == hub(1).get)
}
}
}
object ServerObjectBuilderTest { object ServerObjectBuilderTest {
import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.guid.source.LimitedNumberSource
def NumberPoolHub : NumberPoolHub = { def NumberPoolHub : NumberPoolHub = {

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{ActorRef, Props} import akka.actor.{ActorRef, Props}
import base.ActorTest
import net.psforever.objects.GlobalDefinitions import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.tube.{SpawnTube, SpawnTubeControl, SpawnTubeDefinition} import net.psforever.objects.serverobject.tube.{SpawnTube, SpawnTubeControl, SpawnTubeDefinition}
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
@ -48,7 +49,7 @@ class SpawnTubeTest extends Specification {
} }
} }
class SpawnTubeControlTest extends ActorTest() { class SpawnTubeControlTest extends ActorTest {
"SpawnTubeControl" should { "SpawnTubeControl" should {
"construct" in { "construct" in {
val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube) val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube)

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.{Actor, ActorRef, Props} import akka.actor.{Actor, ActorRef, Props}
import base.ActorTest
import net.psforever.objects.{GlobalDefinitions, Vehicle} import net.psforever.objects.{GlobalDefinitions, Vehicle}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vehicles._ import net.psforever.objects.vehicles._
@ -73,7 +74,7 @@ class UtilityTest extends Specification {
} }
} }
class Utility1Test extends ActorTest() { class Utility1Test extends ActorTest {
"Utility" should { "Utility" should {
"wire an order_terminala Actor" in { "wire an order_terminala Actor" in {
val obj = Utility(UtilityType.order_terminala, UtilityTest.vehicle) val obj = Utility(UtilityType.order_terminala, UtilityTest.vehicle)
@ -87,7 +88,7 @@ class Utility1Test extends ActorTest() {
} }
} }
class Utility2Test extends ActorTest() { class Utility2Test extends ActorTest {
"Utility" should { "Utility" should {
"wire an order_terminalb Actor" in { "wire an order_terminalb Actor" in {
val obj = Utility(UtilityType.order_terminalb, UtilityTest.vehicle) val obj = Utility(UtilityType.order_terminalb, UtilityTest.vehicle)
@ -101,7 +102,7 @@ class Utility2Test extends ActorTest() {
} }
} }
class Utility3Test extends ActorTest() { class Utility3Test extends ActorTest {
"Utility" should { "Utility" should {
"wire a matrix_terminalc Actor" in { "wire a matrix_terminalc Actor" in {
val obj = Utility(UtilityType.matrix_terminalc, UtilityTest.vehicle) val obj = Utility(UtilityType.matrix_terminalc, UtilityTest.vehicle)
@ -115,7 +116,7 @@ class Utility3Test extends ActorTest() {
} }
} }
class Utility4Test extends ActorTest() { class Utility4Test extends ActorTest {
"Utility" should { "Utility" should {
"wire an ams_respawn_tube Actor" in { "wire an ams_respawn_tube Actor" in {
val obj = Utility(UtilityType.ams_respawn_tube, UtilityTest.vehicle) val obj = Utility(UtilityType.ams_respawn_tube, UtilityTest.vehicle)

View file

@ -3,7 +3,7 @@ package objects
import akka.actor.{ActorRef, ActorSystem, Props} import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.TestProbe import akka.testkit.TestProbe
import net.psforever.objects.ballistics.Projectile import base.ActorTest
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.serverobject.structures.StructureType import net.psforever.objects.serverobject.structures.StructureType
@ -39,7 +39,7 @@ class VehicleSpawnPadTest extends Specification {
} }
} }
class VehicleSpawnControl1Test extends ActorTest() { class VehicleSpawnControl1Test extends ActorTest {
"VehicleSpawnControl" should { "VehicleSpawnControl" should {
"construct" in { "construct" in {
val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad) val obj = VehicleSpawnPad(GlobalDefinitions.spawn_pad)
@ -49,7 +49,7 @@ class VehicleSpawnControl1Test extends ActorTest() {
} }
} }
class VehicleSpawnControl2aTest extends ActorTest() { class VehicleSpawnControl2aTest extends ActorTest {
// This runs for a long time. // This runs for a long time.
"VehicleSpawnControl" should { "VehicleSpawnControl" should {
"complete on a vehicle order (block a second one until the first is done and the spawn pad is cleared)" in { "complete on a vehicle order (block a second one until the first is done and the spawn pad is cleared)" in {
@ -113,7 +113,7 @@ class VehicleSpawnControl2aTest extends ActorTest() {
} }
} }
class VehicleSpawnControl2bTest extends ActorTest() { class VehicleSpawnControl2bTest extends ActorTest {
// This runs for a long time. // This runs for a long time.
"VehicleSpawnControl" should { "VehicleSpawnControl" should {
"complete on a vehicle order (railless)" in { "complete on a vehicle order (railless)" in {
@ -170,7 +170,7 @@ class VehicleSpawnControl2bTest extends ActorTest() {
} }
} }
class VehicleSpawnControl3Test extends ActorTest() { class VehicleSpawnControl3Test extends ActorTest {
"VehicleSpawnControl" should { "VehicleSpawnControl" should {
"player is on wrong continent before vehicle can partially load; vehicle is cleaned up" in { "player is on wrong continent before vehicle can partially load; vehicle is cleaned up" in {
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
@ -241,7 +241,7 @@ class VehicleSpawnControl4Test extends ActorTest() {
// } // }
//} //}
class VehicleSpawnControl5Test extends ActorTest() { class VehicleSpawnControl5Test extends ActorTest {
"VehicleSpawnControl" should { "VehicleSpawnControl" should {
"player dies right after vehicle partially loads; the vehicle spawns and blocks the pad" in { "player dies right after vehicle partially loads; the vehicle spawns and blocks the pad" in {
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)
@ -274,7 +274,7 @@ class VehicleSpawnControl5Test extends ActorTest() {
} }
} }
class VehicleSpawnControl6Test extends ActorTest() { class VehicleSpawnControl6Test extends ActorTest {
"VehicleSpawnControl" should { "VehicleSpawnControl" should {
"the player can not sit in vehicle; vehicle spawns and blocks the pad" in { "the player can not sit in vehicle; vehicle spawns and blocks the pad" in {
val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR) val (vehicle, player, pad, zone) = VehicleSpawnPadControlTest.SetUpAgents(PlanetSideEmpire.TR)

View file

@ -2,6 +2,7 @@
package objects package objects
import akka.actor.Props import akka.actor.Props
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition} import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable

View file

@ -4,6 +4,7 @@ package objects
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import akka.actor.{ActorContext, ActorRef, Props} import akka.actor.{ActorContext, ActorRef, Props}
import base.ActorTest
import net.psforever.objects.entity.IdentifiableEntity import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.NumberPoolHub
@ -74,6 +75,15 @@ class ZoneTest extends Specification {
map.TerminalToInterface(3, 4) map.TerminalToInterface(3, 4)
map.TerminalToInterface mustEqual Map(1 -> 2, 3 -> 4) map.TerminalToInterface mustEqual Map(1 -> 2, 3 -> 4)
} }
"associate turrets to weapons" in {
val map = new ZoneMap("map13")
map.TurretToWeapon mustEqual Map.empty
map.TurretToWeapon(1, 2)
map.TurretToWeapon mustEqual Map(1 -> 2)
map.TurretToWeapon(3, 4)
map.TurretToWeapon mustEqual Map(1 -> 2, 3 -> 4)
}
} }
val map13 = new ZoneMap("map13") val map13 = new ZoneMap("map13")

View file

@ -1,8 +1,8 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import objects.ActorTest
class GUIDTaskRegister1Test extends ActorTest { class GUIDTaskRegister1Test extends ActorTest {
"RegisterObjectTask" in { "RegisterObjectTask" in {

View file

@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import objects.ActorTest
class GUIDTaskRegister2Test extends ActorTest() { class GUIDTaskRegister2Test extends ActorTest {
"RegisterEquipment -> RegisterObjectTask" in { "RegisterEquipment -> RegisterObjectTask" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = AmmoBox(GlobalDefinitions.energy_cell) val obj = AmmoBox(GlobalDefinitions.energy_cell)

View file

@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import objects.ActorTest
class GUIDTaskRegister3Test extends ActorTest() { class GUIDTaskRegister3Test extends ActorTest {
"RegisterEquipment -> RegisterTool" in { "RegisterEquipment -> RegisterTool" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Tool(GlobalDefinitions.beamer) val obj = Tool(GlobalDefinitions.beamer)

View file

@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import objects.ActorTest
class GUIDTaskRegister4Test extends ActorTest() { class GUIDTaskRegister4Test extends ActorTest {
"RegisterVehicle" in { "RegisterVehicle" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Vehicle(GlobalDefinitions.fury) val obj = Vehicle(GlobalDefinitions.fury)

View file

@ -1,12 +1,12 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire} import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskRegister5Test extends ActorTest() { class GUIDTaskRegister5Test extends ActorTest {
"RegisterAvatar" in { "RegisterAvatar" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))

View file

@ -1,12 +1,12 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire} import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskRegister6Test extends ActorTest() { class GUIDTaskRegister6Test extends ActorTest {
"RegisterPlayer" in { "RegisterPlayer" in {
val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))

View file

@ -1,10 +1,10 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import objects.ActorTest
class GUIDTaskUnregister1Test extends ActorTest() { class GUIDTaskUnregister1Test extends ActorTest {
"UnregisterObjectTask" in { "UnregisterObjectTask" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = new GUIDTaskTest.TestObject val obj = new GUIDTaskTest.TestObject

View file

@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import objects.ActorTest
class GUIDTaskUnregister2Test extends ActorTest() { class GUIDTaskUnregister2Test extends ActorTest {
"UnregisterEquipment -> UnregisterObjectTask" in { "UnregisterEquipment -> UnregisterObjectTask" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = AmmoBox(GlobalDefinitions.energy_cell) val obj = AmmoBox(GlobalDefinitions.energy_cell)

View file

@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import objects.ActorTest
class GUIDTaskUnregister3Test extends ActorTest() { class GUIDTaskUnregister3Test extends ActorTest {
"UnregisterEquipment -> UnregisterTool" in { "UnregisterEquipment -> UnregisterTool" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Tool(GlobalDefinitions.beamer) val obj = Tool(GlobalDefinitions.beamer)

View file

@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import objects.ActorTest
class GUIDTaskUnregister4Test extends ActorTest() { class GUIDTaskUnregister4Test extends ActorTest {
"RegisterVehicle" in { "RegisterVehicle" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Vehicle(GlobalDefinitions.fury) val obj = Vehicle(GlobalDefinitions.fury)

View file

@ -1,12 +1,12 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire} import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskUnregister5Test extends ActorTest() { class GUIDTaskUnregister5Test extends ActorTest {
"UnregisterAvatar" in { "UnregisterAvatar" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))

View file

@ -1,12 +1,12 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects.guidtask package objects.guidtask
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire} import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
class GUIDTaskUnregister6Test extends ActorTest() { class GUIDTaskUnregister6Test extends ActorTest {
"UnregisterPlayer" in { "UnregisterPlayer" in {
val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) val obj = Player(Avatar("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute))

View file

@ -2,14 +2,14 @@
package objects.number package objects.number
import akka.actor.{ActorSystem, Props} import akka.actor.{ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.guid.actor.NumberPoolActor import net.psforever.objects.guid.actor.NumberPoolActor
import net.psforever.objects.guid.pool.ExclusivePool import net.psforever.objects.guid.pool.ExclusivePool
import net.psforever.objects.guid.selector.RandomSelector import net.psforever.objects.guid.selector.RandomSelector
import objects.ActorTest
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
class NumberPoolActorTest extends ActorTest() { class NumberPoolActorTest extends ActorTest {
"NumberPoolActor" should { "NumberPoolActor" should {
"GetAnyNumber" in { "GetAnyNumber" in {
val pool = new ExclusivePool((25 to 50).toList) val pool = new ExclusivePool((25 to 50).toList)
@ -22,7 +22,7 @@ class NumberPoolActorTest extends ActorTest() {
} }
} }
class NumberPoolActorTest1 extends ActorTest() { class NumberPoolActorTest1 extends ActorTest {
"NumberPoolActor" should { "NumberPoolActor" should {
"GetSpecificNumber" in { "GetSpecificNumber" in {
val pool = new ExclusivePool((25 to 50).toList) val pool = new ExclusivePool((25 to 50).toList)
@ -34,7 +34,7 @@ class NumberPoolActorTest1 extends ActorTest() {
} }
} }
class NumberPoolActorTest2 extends ActorTest() { class NumberPoolActorTest2 extends ActorTest {
"NumberPoolActor" should { "NumberPoolActor" should {
"NoNumber" in { "NoNumber" in {
val pool = new ExclusivePool((25 to 25).toList) //pool only has one number - 25 val pool = new ExclusivePool((25 to 25).toList) //pool only has one number - 25

View file

@ -2,17 +2,17 @@
package objects.number package objects.number
import akka.actor.{ActorRef, ActorSystem, Props} import akka.actor.{ActorRef, ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.entity.IdentifiableEntity import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.{NumberPoolActor, Register, UniqueNumberSystem, Unregister} import net.psforever.objects.guid.actor.{NumberPoolActor, Register, UniqueNumberSystem, Unregister}
import net.psforever.objects.guid.selector.RandomSelector import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.guid.source.LimitedNumberSource
import objects.ActorTest
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
import scala.util.{Failure, Success} import scala.util.{Failure, Success}
class AllocateNumberPoolActors extends ActorTest() { class AllocateNumberPoolActors extends ActorTest {
"AllocateNumberPoolActors" in { "AllocateNumberPoolActors" in {
val src : LimitedNumberSource = LimitedNumberSource(6000) val src : LimitedNumberSource = LimitedNumberSource(6000)
val guid : NumberPoolHub = new NumberPoolHub(src) val guid : NumberPoolHub = new NumberPoolHub(src)

View file

@ -2,6 +2,7 @@
package objects.terminal package objects.terminal
import akka.actor.{ActorRef, ActorSystem, Props} import akka.actor.{ActorRef, ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.definition.SeatDefinition import net.psforever.objects.definition.SeatDefinition
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl} import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
@ -9,7 +10,6 @@ import net.psforever.objects.serverobject.structures.StructureType
import net.psforever.objects.vehicles.Seat import net.psforever.objects.vehicles.Seat
import net.psforever.objects.{Avatar, GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3} import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
import objects.ActorTest
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
@ -56,7 +56,7 @@ class ImplantTerminalMechTest extends Specification {
} }
} }
class ImplantTerminalMechControl1Test extends ActorTest() { class ImplantTerminalMechControl1Test extends ActorTest {
"ImplantTerminalMechControl" should { "ImplantTerminalMechControl" should {
"construct" in { "construct" in {
val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech)
@ -66,7 +66,7 @@ class ImplantTerminalMechControl1Test extends ActorTest() {
} }
} }
class ImplantTerminalMechControl2Test extends ActorTest() { class ImplantTerminalMechControl2Test extends ActorTest {
"ImplantTerminalMechControl" should { "ImplantTerminalMechControl" should {
"let a player mount" in { "let a player mount" in {
val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR) val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)
@ -85,7 +85,7 @@ class ImplantTerminalMechControl2Test extends ActorTest() {
} }
} }
class ImplantTerminalMechControl3Test extends ActorTest() { class ImplantTerminalMechControl3Test extends ActorTest {
import net.psforever.types.CharacterGender import net.psforever.types.CharacterGender
"ImplantTerminalMechControl" should { "ImplantTerminalMechControl" should {
"block a player from mounting" in { "block a player from mounting" in {
@ -108,7 +108,7 @@ class ImplantTerminalMechControl3Test extends ActorTest() {
} }
} }
class ImplantTerminalMechControl4Test extends ActorTest() { class ImplantTerminalMechControl4Test extends ActorTest {
"ImplantTerminalMechControl" should { "ImplantTerminalMechControl" should {
"dismount player after mounting" in { "dismount player after mounting" in {
val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR) val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)
@ -130,7 +130,7 @@ class ImplantTerminalMechControl4Test extends ActorTest() {
} }
} }
class ImplantTerminalMechControl5Test extends ActorTest() { class ImplantTerminalMechControl5Test extends ActorTest {
"ImplantTerminalMechControl" should { "ImplantTerminalMechControl" should {
"block a player from dismounting" in { "block a player from dismounting" in {
val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR) val (player, mech) = ImplantTerminalMechTest.SetUpAgents(PlanetSideEmpire.TR)

View file

@ -2,16 +2,16 @@
package objects.terminal package objects.terminal
import akka.actor.{ActorSystem, Props} import akka.actor.{ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.{Avatar, GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.objects.serverobject.terminals._ import net.psforever.objects.serverobject.terminals._
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire} import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
class ProximityTerminalControl1Test extends ActorTest() { class ProximityTerminalControl1Test extends ActorTest {
"ProximityTerminalControl" should { "ProximityTerminalControl" should {
"construct (medical terminal)" in { "construct (medical terminal)" in {
val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal) val terminal = ProximityTerminal(GlobalDefinitions.medical_terminal)
@ -20,7 +20,7 @@ class ProximityTerminalControl1Test extends ActorTest() {
} }
} }
class ProximityTerminalControl2Test extends ActorTest() { class ProximityTerminalControl2Test extends ActorTest {
"ProximityTerminalControl can not process wrong messages" in { "ProximityTerminalControl can not process wrong messages" in {
val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR) val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
@ -30,7 +30,7 @@ class ProximityTerminalControl2Test extends ActorTest() {
} }
//terminal control is mostly a pass-through actor for Terminal.Exchange messages, wrapped in Terminal.TerminalMessage protocol //terminal control is mostly a pass-through actor for Terminal.Exchange messages, wrapped in Terminal.TerminalMessage protocol
class MedicalTerminalControl1Test extends ActorTest() { class MedicalTerminalControl1Test extends ActorTest {
"ProximityTerminalControl sends a message to the first new user only" in { "ProximityTerminalControl sends a message to the first new user only" in {
val (player, terminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR) val (player, terminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
player.GUID = PlanetSideGUID(10) player.GUID = PlanetSideGUID(10)
@ -53,7 +53,7 @@ class MedicalTerminalControl1Test extends ActorTest() {
} }
} }
class MedicalTerminalControl2Test extends ActorTest() { class MedicalTerminalControl2Test extends ActorTest {
"ProximityTerminalControl sends a message to the last user only" in { "ProximityTerminalControl sends a message to the last user only" in {
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR) val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
player.GUID = PlanetSideGUID(10) player.GUID = PlanetSideGUID(10)
@ -82,7 +82,7 @@ class MedicalTerminalControl2Test extends ActorTest() {
} }
} }
class MedicalTerminalControl3Test extends ActorTest() { class MedicalTerminalControl3Test extends ActorTest {
"ProximityTerminalControl sends a message to the last user only (confirmation of test #2)" in { "ProximityTerminalControl sends a message to the last user only (confirmation of test #2)" in {
val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR) val (player, terminal) : (Player, ProximityTerminal) = ProximityTerminalControlTest.SetUpAgents(GlobalDefinitions.medical_terminal, PlanetSideEmpire.TR)
player.GUID = PlanetSideGUID(10) player.GUID = PlanetSideGUID(10)

View file

@ -2,13 +2,13 @@
package objects.terminal package objects.terminal
import akka.actor.Props import akka.actor.Props
import base.ActorTest
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalControl, ProximityUnit, Terminal} import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalControl, ProximityUnit, Terminal}
import net.psforever.objects.{Avatar, GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire} import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire}
import objects.ActorTest
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
import scala.concurrent.duration._ import scala.concurrent.duration._

View file

@ -2,17 +2,17 @@
package objects.terminal package objects.terminal
import akka.actor.{ActorSystem, Props} import akka.actor.{ActorSystem, Props}
import base.ActorTest
import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition} import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.{Avatar, GlobalDefinitions, Player} import net.psforever.objects.{Avatar, GlobalDefinitions, Player}
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
import net.psforever.types._ import net.psforever.types._
import objects.ActorTest
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
class TerminalControl1Test extends ActorTest() { class TerminalControl1Test extends ActorTest {
"TerminalControl" should { "TerminalControl" should {
"construct (cert terminal)" in { "construct (cert terminal)" in {
val terminal = Terminal(GlobalDefinitions.cert_terminal) val terminal = Terminal(GlobalDefinitions.cert_terminal)
@ -21,7 +21,7 @@ class TerminalControl1Test extends ActorTest() {
} }
} }
class TerminalControl2Test extends ActorTest() { class TerminalControl2Test extends ActorTest {
"TerminalControl can not process wrong messages" in { "TerminalControl can not process wrong messages" in {
val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) val (_, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR)
@ -32,7 +32,7 @@ class TerminalControl2Test extends ActorTest() {
//terminal control is mostly a pass-through actor for Terminal.Exchange messages, wrapped in Terminal.TerminalMessage protocol //terminal control is mostly a pass-through actor for Terminal.Exchange messages, wrapped in Terminal.TerminalMessage protocol
//test for Cert_Terminal messages (see CertTerminalTest) //test for Cert_Terminal messages (see CertTerminalTest)
class CertTerminalControl1Test extends ActorTest() { class CertTerminalControl1Test extends ActorTest {
"TerminalControl can be used to learn a certification ('medium_assault')" in { "TerminalControl can be used to learn a certification ('medium_assault')" in {
val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR)
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "medium_assault", 0, PlanetSideGUID(0)) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "medium_assault", 0, PlanetSideGUID(0))
@ -47,7 +47,7 @@ class CertTerminalControl1Test extends ActorTest() {
} }
} }
class CertTerminalControl2Test extends ActorTest() { class CertTerminalControl2Test extends ActorTest {
"TerminalControl can be used to warn about not learning a fake certification ('juggling')" in { "TerminalControl can be used to warn about not learning a fake certification ('juggling')" in {
val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR)
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "juggling", 0, PlanetSideGUID(0)) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Learn, 0, "juggling", 0, PlanetSideGUID(0))
@ -62,7 +62,7 @@ class CertTerminalControl2Test extends ActorTest() {
} }
} }
class CertTerminalControl3Test extends ActorTest() { class CertTerminalControl3Test extends ActorTest {
"TerminalControl can be used to forget a certification ('medium_assault')" in { "TerminalControl can be used to forget a certification ('medium_assault')" in {
val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR) val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.cert_terminal, PlanetSideEmpire.TR)
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "medium_assault", 0, PlanetSideGUID(0)) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Sell, 0, "medium_assault", 0, PlanetSideGUID(0))
@ -77,7 +77,7 @@ class CertTerminalControl3Test extends ActorTest() {
} }
} }
class VehicleTerminalControl1Test extends ActorTest() { class VehicleTerminalControl1Test extends ActorTest {
"TerminalControl can be used to buy a vehicle ('two_man_assault_buggy')" in { "TerminalControl can be used to buy a vehicle ('two_man_assault_buggy')" in {
val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.ground_vehicle_terminal, PlanetSideEmpire.TR) val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.ground_vehicle_terminal, PlanetSideEmpire.TR)
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0)) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "two_man_assault_buggy", 0, PlanetSideGUID(0))
@ -102,7 +102,7 @@ class VehicleTerminalControl1Test extends ActorTest() {
} }
} }
class VehicleTerminalControl2Test extends ActorTest() { class VehicleTerminalControl2Test extends ActorTest {
"TerminalControl can be used to warn about not buy a vehicle ('harasser')" in { "TerminalControl can be used to warn about not buy a vehicle ('harasser')" in {
val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.ground_vehicle_terminal, PlanetSideEmpire.TR) val (player, terminal) = TerminalControlTest.SetUpAgents(GlobalDefinitions.ground_vehicle_terminal, PlanetSideEmpire.TR)
val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0)) val msg = ItemTransactionMessage(PlanetSideGUID(1), TransactionType.Buy, 0, "harasser", 0, PlanetSideGUID(0))

View file

@ -1,6 +1,9 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package service
import akka.actor.Props import akka.actor.Props
import akka.routing.RandomPool import akka.routing.RandomPool
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap} import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}

View file

@ -1,5 +1,8 @@
package service
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
import akka.actor.Props import akka.actor.Props
import base.ActorTest
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.types.{PlanetSideEmpire, Vector3}

View file

@ -1,46 +1,49 @@
//// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
//import akka.actor.{ActorRef, Props} package service
//import akka.routing.RandomPool
//import akka.testkit.TestProbe import akka.actor.{ActorRef, Props}
//import net.psforever.objects.PlanetSideGameObject import akka.routing.RandomPool
//import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition} import akka.testkit.TestProbe
//import net.psforever.objects.equipment.Equipment import base.ActorTest
//import net.psforever.objects.guid.TaskResolver import net.psforever.objects.PlanetSideGameObject
//import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
//import net.psforever.packet.game.PlanetSideGUID import net.psforever.objects.equipment.Equipment
//import services.{RemoverActor, ServiceManager} import net.psforever.objects.guid.TaskResolver
// import net.psforever.objects.zones.{Zone, ZoneMap}
//import scala.concurrent.duration._ import net.psforever.packet.game.PlanetSideGUID
// import services.{RemoverActor, ServiceManager}
//class StandardRemoverActorTest extends ActorTest {
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver") import scala.concurrent.duration._
//
// "RemoverActor" should { class StandardRemoverActorTest extends ActorTest {
// "handle a simple task" in { ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
// expectNoMsg(500 milliseconds)
// val probe = TestProbe() "RemoverActor" should {
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover") "handle a simple task" in {
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere) expectNoMsg(500 milliseconds)
// val probe = TestProbe()
// val reply1 = probe.receiveOne(200 milliseconds) val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert]) remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
// val reply2 = probe.receiveOne(200 milliseconds)
// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert]) val reply1 = probe.receiveOne(200 milliseconds)
// probe.expectNoMsg(1 seconds) //delay assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
// val reply3 = probe.receiveOne(300 milliseconds) val reply2 = probe.receiveOne(200 milliseconds)
// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert]) assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
// val reply4 = probe.receiveOne(300 milliseconds) probe.expectNoMsg(1 seconds) //delay
// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert]) val reply3 = probe.receiveOne(300 milliseconds)
// val reply5 = probe.receiveOne(300 milliseconds) assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert]) val reply4 = probe.receiveOne(300 milliseconds)
// val reply6 = probe.receiveOne(500 milliseconds) assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert]) val reply5 = probe.receiveOne(300 milliseconds)
// val reply7 = probe.receiveOne(500 milliseconds) assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert]) val reply6 = probe.receiveOne(500 milliseconds)
// } assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
// } val reply7 = probe.receiveOne(500 milliseconds)
//} assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
// }
}
}
//class DelayedRemoverActorTest extends ActorTest { //class DelayedRemoverActorTest extends ActorTest {
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver") // ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
// //
@ -473,65 +476,65 @@
// } // }
// } // }
//} //}
//
//object RemoverActorTest { object RemoverActorTest {
// final val TestObject = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(1) } } final val TestObject = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(1) } }
//
// final case class InclusionTestAlert() final case class InclusionTestAlert()
//
// final case class InitialJobAlert() final case class InitialJobAlert()
//
// final case class FirstJobAlert() final case class FirstJobAlert()
//
// final case class SecondJobAlert() final case class SecondJobAlert()
//
// final case class ClearanceTestAlert() final case class ClearanceTestAlert()
//
// final case class DeletionTaskAlert() final case class DeletionTaskAlert()
//
// final case class DeletionTaskRunAlert() final case class DeletionTaskRunAlert()
//
// class TestRemover(probe : TestProbe) extends RemoverActor { class TestRemover(probe : TestProbe) extends RemoverActor {
// import net.psforever.objects.guid.{Task, TaskResolver} import net.psforever.objects.guid.{Task, TaskResolver}
// val FirstStandardDuration = 1 seconds val FirstStandardDuration = 1 seconds
//
// val SecondStandardDuration = 100 milliseconds val SecondStandardDuration = 100 milliseconds
//
// def InclusionTest(entry : RemoverActor.Entry) : Boolean = { def InclusionTest(entry : RemoverActor.Entry) : Boolean = {
// probe.ref ! InclusionTestAlert() probe.ref ! InclusionTestAlert()
// entry.obj.isInstanceOf[Equipment] entry.obj.isInstanceOf[Equipment]
// } }
//
// def InitialJob(entry : RemoverActor.Entry) : Unit = { def InitialJob(entry : RemoverActor.Entry) : Unit = {
// probe.ref ! InitialJobAlert() probe.ref ! InitialJobAlert()
// } }
//
// def FirstJob(entry : RemoverActor.Entry) : Unit = { def FirstJob(entry : RemoverActor.Entry) : Unit = {
// probe.ref ! FirstJobAlert() probe.ref ! FirstJobAlert()
// } }
//
// override def SecondJob(entry : RemoverActor.Entry) : Unit = { override def SecondJob(entry : RemoverActor.Entry) : Unit = {
// probe.ref ! SecondJobAlert() probe.ref ! SecondJobAlert()
// super.SecondJob(entry) super.SecondJob(entry)
// } }
//
// def ClearanceTest(entry : RemoverActor.Entry) : Boolean = { def ClearanceTest(entry : RemoverActor.Entry) : Boolean = {
// probe.ref ! ClearanceTestAlert() probe.ref ! ClearanceTestAlert()
// true true
// } }
//
// def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = { def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
// probe.ref ! DeletionTaskAlert() probe.ref ! DeletionTaskAlert()
// TaskResolver.GiveTask(new Task() { TaskResolver.GiveTask(new Task() {
// private val localProbe = probe private val localProbe = probe
//
// override def isComplete = Task.Resolution.Success override def isComplete = Task.Resolution.Success
//
// def Execute(resolver : ActorRef) : Unit = { def Execute(resolver : ActorRef) : Unit = {
// localProbe.ref ! DeletionTaskRunAlert() localProbe.ref ! DeletionTaskRunAlert()
// resolver ! scala.util.Success(this) resolver ! scala.util.Success(this)
// } }
// }) })
// } }
// } }
//} }

View file

@ -1,5 +1,8 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package service
import akka.actor.Props import akka.actor.Props
import base.ActorTest
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types._ import net.psforever.types._

View file

@ -11,6 +11,8 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate} import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal} import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal}
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.turret.MannedTurret
import net.psforever.types.Vector3 import net.psforever.types.Vector3
object Maps { object Maps {
@ -1215,6 +1217,14 @@ object Maps {
LocalObject(1186, Locker.Constructor) LocalObject(1186, Locker.Constructor)
LocalObject(1187, Locker.Constructor) LocalObject(1187, Locker.Constructor)
LocalObject(1188, Locker.Constructor) LocalObject(1188, Locker.Constructor)
LocalObject(1418, MannedTurret.Constructor(manned_turret))
LocalObject(1419, MannedTurret.Constructor(manned_turret))
LocalObject(1421, MannedTurret.Constructor(manned_turret))
LocalObject(1426, MannedTurret.Constructor(manned_turret))
LocalObject(1427, MannedTurret.Constructor(manned_turret))
LocalObject(1428, MannedTurret.Constructor(manned_turret))
LocalObject(1431, MannedTurret.Constructor(manned_turret))
LocalObject(1432, MannedTurret.Constructor(manned_turret))
LocalObject(1492, ProximityTerminal.Constructor(medical_terminal)) //lobby LocalObject(1492, ProximityTerminal.Constructor(medical_terminal)) //lobby
LocalObject(1494, ProximityTerminal.Constructor(medical_terminal)) //kitchen LocalObject(1494, ProximityTerminal.Constructor(medical_terminal)) //kitchen
LocalObject(1564, Terminal.Constructor(order_terminal)) LocalObject(1564, Terminal.Constructor(order_terminal))
@ -1330,6 +1340,14 @@ object Maps {
ObjectToBuilding(1186, 2) ObjectToBuilding(1186, 2)
ObjectToBuilding(1187, 2) ObjectToBuilding(1187, 2)
ObjectToBuilding(1188, 2) ObjectToBuilding(1188, 2)
ObjectToBuilding(1418, 2)
ObjectToBuilding(1419, 2)
ObjectToBuilding(1421, 2)
ObjectToBuilding(1426, 2)
ObjectToBuilding(1427, 2)
ObjectToBuilding(1428, 2)
ObjectToBuilding(1431, 2)
ObjectToBuilding(1432, 2)
ObjectToBuilding(1492, 2) ObjectToBuilding(1492, 2)
ObjectToBuilding(1494, 2) ObjectToBuilding(1494, 2)
ObjectToBuilding(1479, 2) ObjectToBuilding(1479, 2)
@ -1385,6 +1403,14 @@ object Maps {
DoorToLock(715, 751) DoorToLock(715, 751)
TerminalToSpawnPad(224, 223) TerminalToSpawnPad(224, 223)
TerminalToSpawnPad(2419, 1479) TerminalToSpawnPad(2419, 1479)
TurretToWeapon(1418, 5000)
TurretToWeapon(1419, 5001)
TurretToWeapon(1421, 5002)
TurretToWeapon(1426, 5003)
TurretToWeapon(1427, 5004)
TurretToWeapon(1428, 5005)
TurretToWeapon(1431, 5006)
TurretToWeapon(1432, 5007)
} }
def Building38() : Unit = { def Building38() : Unit = {
@ -1487,6 +1513,8 @@ object Maps {
LocalObject(1226, Locker.Constructor) LocalObject(1226, Locker.Constructor)
LocalObject(1227, Locker.Constructor) LocalObject(1227, Locker.Constructor)
LocalObject(1228, Locker.Constructor) LocalObject(1228, Locker.Constructor)
LocalObject(1440, MannedTurret.Constructor(manned_turret))
LocalObject(1442, MannedTurret.Constructor(manned_turret))
LocalObject(1591, Terminal.Constructor(order_terminal)) LocalObject(1591, Terminal.Constructor(order_terminal))
LocalObject(1592, Terminal.Constructor(order_terminal)) LocalObject(1592, Terminal.Constructor(order_terminal))
LocalObject(1593, Terminal.Constructor(order_terminal)) LocalObject(1593, Terminal.Constructor(order_terminal))
@ -1514,6 +1542,8 @@ object Maps {
ObjectToBuilding(1226, 49) ObjectToBuilding(1226, 49)
ObjectToBuilding(1227, 49) ObjectToBuilding(1227, 49)
ObjectToBuilding(1228, 49) ObjectToBuilding(1228, 49)
ObjectToBuilding(1440, 49)
ObjectToBuilding(1442, 49)
ObjectToBuilding(1591, 49) ObjectToBuilding(1591, 49)
ObjectToBuilding(1592, 49) ObjectToBuilding(1592, 49)
ObjectToBuilding(1593, 49) ObjectToBuilding(1593, 49)
@ -1529,6 +1559,8 @@ object Maps {
DoorToLock(431, 907) DoorToLock(431, 907)
DoorToLock(432, 902) DoorToLock(432, 902)
DoorToLock(433, 903) DoorToLock(433, 903)
TurretToWeapon(1440, 5008)
TurretToWeapon(1442, 5009)
} }
} }
@ -1615,10 +1647,6 @@ object Maps {
Projectiles(this) Projectiles(this)
} }
val map12 = new ZoneMap("map12") {
Projectiles(this)
}
val map13 = new ZoneMap("map13") { val map13 = new ZoneMap("map13") {
Building1() Building1()
Building2() Building2()
@ -1766,6 +1794,8 @@ object Maps {
LocalObject(557, IFFLock.Constructor) LocalObject(557, IFFLock.Constructor)
LocalObject(558, IFFLock.Constructor) LocalObject(558, IFFLock.Constructor)
LocalObject(559, IFFLock.Constructor) LocalObject(559, IFFLock.Constructor)
LocalObject(670, MannedTurret.Constructor(manned_turret))
LocalObject(671, MannedTurret.Constructor(manned_turret))
ObjectToBuilding(330, 29) ObjectToBuilding(330, 29)
ObjectToBuilding(331, 29) ObjectToBuilding(331, 29)
ObjectToBuilding(332, 29) ObjectToBuilding(332, 29)
@ -1774,10 +1804,14 @@ object Maps {
ObjectToBuilding(557, 29) ObjectToBuilding(557, 29)
ObjectToBuilding(558, 29) ObjectToBuilding(558, 29)
ObjectToBuilding(559, 29) ObjectToBuilding(559, 29)
ObjectToBuilding(670, 29)
ObjectToBuilding(671, 29)
DoorToLock(330, 558) DoorToLock(330, 558)
DoorToLock(331, 559) DoorToLock(331, 559)
DoorToLock(332, 556) DoorToLock(332, 556)
DoorToLock(333, 557) DoorToLock(333, 557)
TurretToWeapon(670, 5000)
TurretToWeapon(671, 5001)
} }
def Building42() : Unit = { def Building42() : Unit = {

View file

@ -219,7 +219,7 @@ object PsLogin {
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future import scala.concurrent.Future
import scala.util.{Failure, Success} import scala.util.{Failure, Success}
implicit val timeout = Timeout(500 milliseconds) implicit val timeout = Timeout(5 seconds)
val requestVehicleEventBus : Future[ServiceManager.LookupResult] = val requestVehicleEventBus : Future[ServiceManager.LookupResult] =
(ServiceManager.serviceManager ask ServiceManager.Lookup("vehicle")).mapTo[ServiceManager.LookupResult] (ServiceManager.serviceManager ask ServiceManager.Lookup("vehicle")).mapTo[ServiceManager.LookupResult]
requestVehicleEventBus.onComplete { requestVehicleEventBus.onComplete {

View file

@ -35,7 +35,7 @@ import net.psforever.objects.serverobject.structures.{Building, StructureType, W
import net.psforever.objects.serverobject.terminals._ import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.vehicles.{AccessPermissionGroup, Cargo, Utility, VehicleLockState} import net.psforever.objects.vehicles._
import net.psforever.objects.zones.{InterstellarCluster, Zone} import net.psforever.objects.zones.{InterstellarCluster, Zone}
import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game.objectcreate._
import net.psforever.types._ import net.psforever.types._
@ -43,7 +43,7 @@ import services.{RemoverActor, _}
import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse} import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
import services.galaxy.{GalaxyResponse, GalaxyServiceResponse} import services.galaxy.{GalaxyResponse, GalaxyServiceResponse}
import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse} import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
import services.vehicle.VehicleAction.UnstowEquipment import services.vehicle.support.TurretUpgrader
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse} import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -54,6 +54,7 @@ import scala.concurrent.duration._
import scala.util.Success import scala.util.Success
import akka.pattern.ask import akka.pattern.ask
import net.psforever.objects.ballistics.{Projectile, ProjectileResolution} import net.psforever.objects.ballistics.{Projectile, ProjectileResolution}
import net.psforever.objects.serverobject.turret.{MannedTurret, TurretUpgrade}
class WorldSessionActor extends Actor with MDCContextAware { class WorldSessionActor extends Actor with MDCContextAware {
import WorldSessionActor._ import WorldSessionActor._
@ -179,7 +180,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
* Vehicle cleanup that is specific to log out behavior. * Vehicle cleanup that is specific to log out behavior.
*/ */
def DismountVehicleOnLogOut() : Unit = { def DismountVehicleOnLogOut() : Unit = {
//TODO Will base guns implement Vehicle type? Don't want those to deconstruct
(player.VehicleSeated match { (player.VehicleSeated match {
case Some(vehicle_guid) => case Some(vehicle_guid) =>
continent.GUID(vehicle_guid) continent.GUID(vehicle_guid)
@ -572,6 +572,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
case VehicleResponse.DetachFromRails(vehicle_guid, pad_guid, pad_position, pad_orientation_z) => case VehicleResponse.DetachFromRails(vehicle_guid, pad_guid, pad_position, pad_orientation_z) =>
sendResponse(ObjectDetachMessage(pad_guid, vehicle_guid, pad_position + Vector3(0,0,0.5f), pad_orientation_z)) sendResponse(ObjectDetachMessage(pad_guid, vehicle_guid, pad_position + Vector3(0,0,0.5f), pad_orientation_z))
case VehicleResponse.EquipmentInSlot(pkt) =>
if(tplayer_guid != guid) {
sendResponse(pkt)
}
case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) => case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) =>
if(tplayer_guid != guid) { if(tplayer_guid != guid) {
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
@ -764,20 +769,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Mountable.MountMessages(tplayer, reply) => case Mountable.MountMessages(tplayer, reply) =>
reply match { reply match {
case Mountable.CanMount(obj : ImplantTerminalMech, seat_num) => case Mountable.CanMount(obj : ImplantTerminalMech, seat_num) =>
val player_guid : PlanetSideGUID = tplayer.GUID MountingAction(tplayer, obj, seat_num)
val obj_guid : PlanetSideGUID = obj.GUID sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, 1000L)) //health of mech
log.info(s"MountVehicleMsg: $player_guid mounts $obj @ $seat_num")
PlayerActionsToCancel() case Mountable.CanMount(obj : MannedTurret, seat_num) =>
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, 1000L)) //health of mech obj.WeaponControlledFromSeat(seat_num) match {
sendResponse(ObjectAttachMessage(obj_guid, player_guid, seat_num)) case Some(weapon : Tool) =>
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seat_num)) //update mounted weapon belonging to seat
weapon.AmmoSlots.foreach(slot => { //update the magazine(s) in the weapon, specifically
val magazine = slot.Box
sendResponse(InventoryStateMessage(magazine.GUID, weapon.GUID, magazine.Capacity.toLong))
})
case _ => ; //no weapons to update
}
sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health))
MountingAction(tplayer, obj, seat_num)
case Mountable.CanMount(obj : Vehicle, seat_num) => case Mountable.CanMount(obj : Vehicle, seat_num) =>
val obj_guid : PlanetSideGUID = obj.GUID val obj_guid : PlanetSideGUID = obj.GUID
val player_guid : PlanetSideGUID = tplayer.GUID
log.info(s"MountVehicleMsg: $player_guid mounts $obj_guid @ $seat_num")
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent)) //clear timer
PlayerActionsToCancel()
if(seat_num == 0) { //simplistic vehicle ownership management if(seat_num == 0) { //simplistic vehicle ownership management
obj.Owner match { obj.Owner match {
case Some(owner_guid) => case Some(owner_guid) =>
@ -791,7 +800,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case None => ; case None => ;
} }
tplayer.VehicleOwned = Some(obj_guid) tplayer.VehicleOwned = Some(obj_guid)
obj.Owner = Some(player_guid) obj.Owner = Some(tplayer.GUID)
} }
obj.WeaponControlledFromSeat(seat_num) match { obj.WeaponControlledFromSeat(seat_num) match {
case Some(weapon : Tool) => case Some(weapon : Tool) =>
@ -802,29 +811,27 @@ class WorldSessionActor extends Actor with MDCContextAware {
}) })
case _ => ; //no weapons to update case _ => ; //no weapons to update
} }
sendResponse(ObjectAttachMessage(obj_guid, player_guid, seat_num)) //sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health)) //TODO vehicle max health in definition
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent)) //clear timer
AccessContents(obj) AccessContents(obj)
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seat_num)) MountingAction(tplayer, obj, seat_num)
case Mountable.CanMount(obj : Mountable, _) => case Mountable.CanMount(obj : Mountable, _) =>
log.warn(s"MountVehicleMsg: $obj is some generic mountable object and nothing will happen") log.warn(s"MountVehicleMsg: $obj is some generic mountable object and nothing will happen")
case Mountable.CanDismount(obj : ImplantTerminalMech, seat_num) => case Mountable.CanDismount(obj : ImplantTerminalMech, seat_num) =>
val obj_guid : PlanetSideGUID = obj.GUID DismountAction(tplayer, obj, seat_num)
val player_guid : PlanetSideGUID = tplayer.GUID
log.info(s"DismountVehicleMsg: $player_guid dismounts $obj @ $seat_num") case Mountable.CanDismount(obj : MannedTurret, seat_num) =>
sendResponse(DismountVehicleMsg(player_guid, BailType.Normal, false)) DismountAction(tplayer, obj, seat_num)
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, BailType.Normal, false))
case Mountable.CanDismount(obj : Vehicle, seat_num) => case Mountable.CanDismount(obj : Vehicle, seat_num) =>
val player_guid : PlanetSideGUID = tplayer.GUID val player_guid : PlanetSideGUID = tplayer.GUID
if(player_guid == player.GUID) { if(player_guid == player.GUID) {
//disembarking self //disembarking self
log.info(s"DismountVehicleMsg: $player_guid dismounts $obj @ $seat_num")
TotalDriverVehicleControl(obj) TotalDriverVehicleControl(obj)
sendResponse(DismountVehicleMsg(player_guid, BailType.Normal, false))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, BailType.Normal, false))
UnAccessContents(obj) UnAccessContents(obj)
DismountAction(tplayer, obj, seat_num)
} }
else { else {
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, seat_num, true, obj.GUID)) vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, seat_num, true, obj.GUID))
@ -1713,7 +1720,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
antDischargingTick.cancel() antDischargingTick.cancel()
} }
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) => case HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction, tickAction) =>
progressBarUpdate.cancel progressBarUpdate.cancel
if(progressBarValue.isDefined) { if(progressBarValue.isDefined) {
val progressBarVal : Float = progressBarValue.get + delta val progressBarVal : Float = progressBarValue.get + delta
@ -1726,10 +1733,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
else { else {
HackState.Ongoing HackState.Ongoing
} }
sendResponse(HackMessage(1, target.GUID, player.GUID, progressBarVal.toInt, 0L, vis, 8L)) sendResponse(HackMessage(progressType, target.GUID, player.GUID, progressBarVal.toInt, 0L, vis, 8L))
if(progressBarVal > 100) { //done if(progressBarVal > 100) { //done
progressBarValue = None progressBarValue = None
log.info(s"Hacked a $target")
// sendResponse(HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L)) // sendResponse(HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L))
completeAction() completeAction()
} }
@ -1737,7 +1743,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
tickAction.getOrElse(() => Unit)() tickAction.getOrElse(() => Unit)()
progressBarValue = Some(progressBarVal) progressBarValue = Some(progressBarVal)
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
progressBarUpdate = context.system.scheduler.scheduleOnce(250 milliseconds, self, ItemHacking(tplayer, target, tool_guid, delta, completeAction)) progressBarUpdate = context.system.scheduler.scheduleOnce(250 milliseconds, self, HackingProgress(progressType, tplayer, target, tool_guid, delta, completeAction))
} }
} }
@ -1803,6 +1809,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatar.Certifications += GalaxyGunship avatar.Certifications += GalaxyGunship
avatar.Certifications += Phantasm avatar.Certifications += Phantasm
avatar.Certifications += UniMAX avatar.Certifications += UniMAX
avatar.Certifications += Engineering
avatar.Certifications += CombatEngineering
avatar.Certifications += AdvancedEngineering
avatar.Certifications += FortificationEngineering
avatar.Certifications += AssaultEngineering
AwardBattleExperiencePoints(avatar, 1000000L) AwardBattleExperiencePoints(avatar, 1000000L)
player = new Player(avatar) player = new Player(avatar)
//player.Position = Vector3(3561.0f, 2854.0f, 90.859375f) //home3, HART C //player.Position = Vector3(3561.0f, 2854.0f, 90.859375f) //home3, HART C
@ -1812,9 +1823,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
//player.Orientation = Vector3(0f, 0f, 132.1875f) //player.Orientation = Vector3(0f, 0f, 132.1875f)
// player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting // player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting
player.Slot(0).Equipment = SimpleItem(remote_electronics_kit) //Tool(GlobalDefinitions.StandardPistol(player.Faction)) player.Slot(0).Equipment = SimpleItem(remote_electronics_kit) //Tool(GlobalDefinitions.StandardPistol(player.Faction))
player.Slot(2).Equipment = Tool(mini_chaingun) //punisher //suppressor player.Slot(2).Equipment = Tool(nano_dispenser) //punisher //suppressor
player.Slot(4).Equipment = Tool(GlobalDefinitions.StandardMelee(player.Faction)) player.Slot(4).Equipment = Tool(GlobalDefinitions.StandardMelee(player.Faction))
player.Slot(6).Equipment = AmmoBox(bullet_9mm, 20) //bullet_9mm player.Slot(6).Equipment = AmmoBox(upgrade_canister) //bullet_9mm
player.Slot(9).Equipment = AmmoBox(rocket, 11) //bullet_9mm player.Slot(9).Equipment = AmmoBox(rocket, 11) //bullet_9mm
player.Slot(12).Equipment = AmmoBox(frag_cartridge) //bullet_9mm player.Slot(12).Equipment = AmmoBox(frag_cartridge) //bullet_9mm
player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP) player.Slot(33).Equipment = AmmoBox(bullet_9mm_AP)
@ -2056,6 +2067,45 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => ; case _ => ;
} }
}) })
//base turrets
continent.Map.TurretToWeapon.foreach({ case((turret_guid, weapon_guid)) =>
val parent_guid = PlanetSideGUID(turret_guid)
continent.GUID(turret_guid) match {
case Some(turret : MannedTurret) =>
//attached weapon
turret.ControlledWeapon(1) match {
case Some(obj : Tool) =>
val objDef = obj.Definition
sendResponse(
ObjectCreateMessage(
objDef.ObjectId,
obj.GUID,
ObjectCreateMessageParent(parent_guid, 1),
objDef.Packet.ConstructorData(obj).get
)
)
case _ => ;
}
//reserved ammunition?
//TODO need to register if it exists
//seat turret occupant
turret.Seats(0).Occupant match {
case Some(tplayer) =>
val tdefintion = tplayer.Definition
sendResponse(
ObjectCreateMessage(
tdefintion.ObjectId,
tplayer.GUID,
ObjectCreateMessageParent(parent_guid, 0),
tdefintion.Packet.ConstructorData(tplayer).get
)
)
case None => ;
}
case _ => ;
}
})
StopBundlingPackets() StopBundlingPackets()
self ! SetCurrentAvatar(player) self ! SetCurrentAvatar(player)
@ -2079,33 +2129,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ ChildObjectStateMessage(object_guid, pitch, yaw) => case msg @ ChildObjectStateMessage(object_guid, pitch, yaw) =>
//the majority of the following check retrieves information to determine if we are in control of the child //the majority of the following check retrieves information to determine if we are in control of the child
player.VehicleSeated match { FindContainedWeapon match {
case Some(vehicle_guid) => case (Some(_), Some(tool)) =>
continent.GUID(vehicle_guid) match { if(tool.GUID == object_guid) {
case Some(obj : Vehicle) => //TODO set tool orientation?
obj.PassengerInSeat(player) match { player.Orientation = Vector3(0f, pitch, yaw)
case Some(seat_num) => vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw))
obj.WeaponControlledFromSeat(seat_num) match {
case Some(tool) =>
if(tool.GUID == object_guid) {
//TODO set tool orientation?
player.Orientation = Vector3(0f, pitch, yaw)
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw))
}
case None =>
log.warn(s"ChildObjectState: player $player is not using stated controllable agent")
}
case None =>
log.warn(s"ChildObjectState: player ${player.GUID} is not in a position to use controllable agent")
}
case _ =>
log.warn(s"ChildObjectState: player $player's controllable agent not available in scope")
} }
case None => else {
//TODO status condition of "playing getting out of vehicle to allow for late packets without warning log.warn(s"ChildObjectState: ${player.Name} is using a different controllable agent than #${object_guid.guid}")
//log.warn(s"ChildObjectState: player $player not related to anything with a controllable agent") }
case (Some(obj), None) =>
log.warn(s"ChildObjectState: ${player.Name} can not find any controllable agent, let alone #${object_guid.guid}")
case (None, _) => ;
//TODO status condition of "playing getting out of vehicle to allow for late packets without warning
//log.warn(s"ChildObjectState: player $player not related to anything with a controllable agent")
} }
//log.info("ChildObjectState: " + msg)
case msg @ VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk5, unk6, unk7, wheels, unk9, unkA) => case msg @ VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk5, unk6, unk7, wheels, unk9, unkA) =>
continent.GUID(vehicle_guid) match { continent.GUID(vehicle_guid) match {
@ -2299,9 +2338,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
FindContainedWeapon match { FindContainedWeapon match {
case (Some(obj), Some(tool : Tool)) => case (Some(obj), Some(tool : Tool)) =>
val originalAmmoType = tool.AmmoType val originalAmmoType = tool.AmmoType
val fullMagazine = tool.MaxMagazine
do { do {
val requestedAmmoType = tool.NextAmmoType val requestedAmmoType = tool.NextAmmoType
val fullMagazine = tool.MaxMagazine
if(requestedAmmoType != tool.AmmoSlot.Box.AmmoType) { if(requestedAmmoType != tool.AmmoSlot.Box.AmmoType) {
FindEquipmentStock(obj, FindAmmoBoxThatUses(requestedAmmoType), fullMagazine, CountAmmunition).reverse match { FindEquipmentStock(obj, FindAmmoBoxThatUses(requestedAmmoType), fullMagazine, CountAmmunition).reverse match {
case Nil => ; case Nil => ;
@ -2823,14 +2862,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.warn(s"Player ${player.GUID} - ${player.Faction} tried to refill silo ${resourceSilo.GUID} - ${resourceSilo.Faction} belonging to another empire") log.warn(s"Player ${player.GUID} - ${player.Faction} tried to refill silo ${resourceSilo.GUID} - ${resourceSilo.Faction} belonging to another empire")
} }
case Some(panel : IFFLock) => case Some(panel : IFFLock) =>
if(panel.Faction != player.Faction && panel.HackedBy.isEmpty) { if(panel.Faction != player.Faction && panel.HackedBy.isEmpty) {
player.Slot(player.DrawnSlot).Equipment match { player.Slot(player.DrawnSlot).Equipment match {
case Some(tool : SimpleItem) => case Some(tool : SimpleItem) =>
if(tool.Definition == GlobalDefinitions.remote_electronics_kit) { if(tool.Definition == GlobalDefinitions.remote_electronics_kit) {
progressBarValue = Some(-GetPlayerHackSpeed()) progressBarValue = Some(-GetPlayerHackSpeed())
self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, GetPlayerHackSpeed(), FinishHacking(panel, 1114636288L)) self ! WorldSessionActor.HackingProgress(1, player, panel, tool.GUID, GetPlayerHackSpeed(), FinishHacking(panel, 1114636288L))
log.info("Hacking a door~") log.info("Hacking a door~")
} }
case _ => ; case _ => ;
@ -2892,7 +2930,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(tool: SimpleItem) => case Some(tool: SimpleItem) =>
if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
progressBarValue = Some(-GetPlayerHackSpeed()) progressBarValue = Some(-GetPlayerHackSpeed())
self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L)) self ! WorldSessionActor.HackingProgress(1, player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L))
log.info("Hacking a locker") log.info("Hacking a locker")
} }
case _ => ; case _ => ;
@ -2907,6 +2945,32 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info(s"UseItem: not $player's locker") log.info(s"UseItem: not $player's locker")
} }
case Some(obj : MannedTurret) =>
player.Slot(player.DrawnSlot).Equipment match {
case Some(tool : Tool) =>
if(tool.Definition == GlobalDefinitions.nano_dispenser && tool.Magazine > 0) {
val ammo = tool.AmmoType
if(ammo == Ammo.upgrade_canister && obj.Seats.values.count(_.isOccupied) == 0) {
progressBarValue = Some(-1.25f)
self ! WorldSessionActor.HackingProgress(
2,
player,
obj,
tool.GUID,
1.25f,
FinishUpgradingMannedTurret(obj, tool, TurretUpgrade(unk2.toInt))
)
}
else if(ammo == Ammo.armor_canister && obj.Health < obj.MaxHealth) {
//repair turret
}
}
else if(tool.Definition == GlobalDefinitions.trek) {
//infect turret with virus
}
case _ => ;
}
case Some(obj : Vehicle) => case Some(obj : Vehicle) =>
val equipment = player.Slot(player.DrawnSlot).Equipment val equipment = player.Slot(player.DrawnSlot).Equipment
if(player.Faction == obj.Faction) { if(player.Faction == obj.Faction) {
@ -2969,7 +3033,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(tool: SimpleItem) => case Some(tool: SimpleItem) =>
if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
progressBarValue = Some(-GetPlayerHackSpeed()) progressBarValue = Some(-GetPlayerHackSpeed())
self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L)) self ! WorldSessionActor.HackingProgress(1, player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L))
log.info("Hacking a terminal") log.info("Hacking a terminal")
} }
case _ => ; case _ => ;
@ -2979,7 +3043,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
// Otherwise allow the faction that owns the terminal to use it // Otherwise allow the faction that owns the terminal to use it
sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
} }
} }
case Some(obj : SpawnTube) => case Some(obj : SpawnTube) =>
@ -3853,7 +3916,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
/** /**
* The process of hacking an object is completed * The process of hacking an object is completed.
* Pass the message onto the hackable object and onto the local events system. * Pass the message onto the hackable object and onto the local events system.
* @param target the `Hackable` object that has been hacked * @param target the `Hackable` object that has been hacked
* @param unk na; * @param unk na;
@ -3862,16 +3925,33 @@ class WorldSessionActor extends Actor with MDCContextAware {
*/ */
//TODO add params here depending on which params in HackMessage are important //TODO add params here depending on which params in HackMessage are important
private def FinishHacking(target : PlanetSideServerObject with Hackable, unk : Long)() : Unit = { private def FinishHacking(target : PlanetSideServerObject with Hackable, unk : Long)() : Unit = {
log.info(s"Hacked a $target")
// Wait for the target actor to set the HackedBy property, otherwise LocalAction.HackTemporarily will not complete properly // Wait for the target actor to set the HackedBy property, otherwise LocalAction.HackTemporarily will not complete properly
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
ask(target.Actor, CommonMessages.Hack(player))(1 second).mapTo[Boolean].onComplete { ask(target.Actor, CommonMessages.Hack(player))(1 second).mapTo[Boolean].onComplete {
case Success(_) => case Success(_) =>
localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, target.HackSound, player.Position, 30, 0.49803925f)) localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, target.HackSound, player.Position, 30, 0.49803925f))
localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk)) localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk))
case scala.util.Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}") case scala.util.Failure(_) =>
} log.warn(s"Hack message failed on target guid: ${target.GUID}")
}
} }
/**
* The process of upgrading a turret's weapon(s) is completed.
* Pass the message onto the turret and onto the vehicle events system.
* Additionally, force-deplete the ammunition count of the nano-dispenser used to perform the upgrade.
* @param target the turret
* @param tool the nano-dispenser that was used to perform this upgrade
* @param upgrade the new upgrade state
*/
private def FinishUpgradingMannedTurret(target : MannedTurret, tool : Tool, upgrade : TurretUpgrade.Value)() : Unit = {
log.info(s"Converting manned wall turret weapon to $upgrade")
tool.Magazine = 0
sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, tool.GUID, 0))
vehicleService ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.ClearSpecific(List(target), continent))
vehicleService ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.AddTask(target, continent, upgrade))
}
/** /**
* Temporary function that iterates over vehicle permissions and turns them into `PlanetsideAttributeMessage` packets.<br> * Temporary function that iterates over vehicle permissions and turns them into `PlanetsideAttributeMessage` packets.<br>
@ -3887,7 +3967,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
* Occasionally, during deployment, local(?) vehicle seat access permissions may change. * Occasionally, during deployment, local(?) vehicle seat access permissions may change.
* This results in players being locked into their own vehicle. * This results in players being locked into their own vehicle.
* Reloading vehicle permissions supposedly ensures the seats will be properly available. * Reloading vehicle permissions supposedly ensures the seats will be properly available.
* This is considered a client issue; but, somehow, it also impacts server operation somehow. * This is considered a client issue; but, somehow, it also impacts server operation somehow.<br>
* <br>
* 22 June 2018:<br>
* I think vehicle ownership works properly now.
* @param vehicle the `Vehicle` * @param vehicle the `Vehicle`
*/ */
def ReloadVehicleAccessPermissions(vehicle : Vehicle) : Unit = { def ReloadVehicleAccessPermissions(vehicle : Vehicle) : Unit = {
@ -4025,7 +4108,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.VehicleSeated match { player.VehicleSeated match {
case Some(vehicle_guid) => //weapon is vehicle turret? case Some(vehicle_guid) => //weapon is vehicle turret?
continent.GUID(vehicle_guid) match { continent.GUID(vehicle_guid) match {
case Some(vehicle : Vehicle) => case Some(vehicle : Mountable with MountedWeapons with Container) =>
vehicle.PassengerInSeat(player) match { vehicle.PassengerInSeat(player) match {
case Some(seat_num) => case Some(seat_num) =>
(Some(vehicle), vehicle.WeaponControlledFromSeat(seat_num)) (Some(vehicle), vehicle.WeaponControlledFromSeat(seat_num))
@ -5312,6 +5395,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
} }
/**
* Common activities/procedure when a player mounts a valid object.
* @param tplayer the player
* @param obj the mountable object
* @param seatNum the seat into which the player is mounting
*/
def MountingAction(tplayer : Player, obj : PlanetSideGameObject with Mountable, seatNum : Int) : Unit = {
val player_guid : PlanetSideGUID = tplayer.GUID
val obj_guid : PlanetSideGUID = obj.GUID
PlayerActionsToCancel()
log.info(s"MountVehicleMsg: $player_guid mounts $obj @ $seatNum")
sendResponse(ObjectAttachMessage(obj_guid, player_guid, seatNum))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seatNum))
}
/**
* Common activities/procedure when a player dismounts a valid object.
* @param tplayer the player
* @param obj the mountable object
* @param seatNum the seat out of which which the player is disembarking
*/
def DismountAction(tplayer : Player, obj : PlanetSideGameObject with Mountable, seatNum : Int) : Unit = {
val player_guid : PlanetSideGUID = tplayer.GUID
log.info(s"DismountVehicleMsg: ${tplayer.Name} dismounts $obj from $seatNum")
sendResponse(DismountVehicleMsg(player_guid, BailType.Normal, false))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, BailType.Normal, false))
}
def failWithError(error : String) = { def failWithError(error : String) = {
log.error(error) log.error(error)
sendResponse(ConnectionClose()) sendResponse(ConnectionClose())
@ -5470,9 +5581,12 @@ object WorldSessionActor {
/** /**
* A message that indicates the user is using a remote electronics kit to hack some server object. * A message that indicates the user is using a remote electronics kit to hack some server object.<br>
* <br>
* Each time this message is sent for a given hack attempt counts as a single "tick" of progress. * Each time this message is sent for a given hack attempt counts as a single "tick" of progress.
* The process of "making progress" with a hack involves sending this message repeatedly until the progress is 100 or more. * The process of "making progress" with a hack involves sending this message repeatedly until the progress is 100 or more.
* To calculate the actual amount of change in the progress `delta`,
* start with 100, divide by the length of time in seconds, then divide once more by 4.
* @param tplayer the player * @param tplayer the player
* @param target the object being hacked * @param target the object being hacked
* @param tool_guid the REK * @param tool_guid the REK
@ -5480,12 +5594,13 @@ object WorldSessionActor {
* @param completeAction a custom action performed once the hack is completed * @param completeAction a custom action performed once the hack is completed
* @param tickAction an optional action is is performed for each tick of progress * @param tickAction an optional action is is performed for each tick of progress
*/ */
private final case class ItemHacking(tplayer : Player, private final case class HackingProgress(progressType : Int,
target : PlanetSideServerObject, tplayer : Player,
tool_guid : PlanetSideGUID, target : PlanetSideServerObject,
delta : Float, tool_guid : PlanetSideGUID,
completeAction : () => Unit, delta : Float,
tickAction : Option[() => Unit] = None) completeAction : () => Unit,
tickAction : Option[() => Unit] = None)
private final case class NtuCharging(tplayer: Player, private final case class NtuCharging(tplayer: Player,
vehicle: Vehicle) vehicle: Vehicle)

View file

@ -2,7 +2,7 @@
import akka.actor.{ActorRef, ActorSystem, MDCContextAware} import akka.actor.{ActorRef, ActorSystem, MDCContextAware}
import akka.testkit.{ImplicitSender, TestKit, TestProbe} import akka.testkit.{ImplicitSender, TestKit, TestProbe}
import com.typesafe.config.{ConfigFactory, ConfigValueFactory} import com.typesafe.config.ConfigFactory
import net.psforever.packet.{ControlPacket, GamePacket} import net.psforever.packet.{ControlPacket, GamePacket}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import org.specs2.specification.Scope import org.specs2.specification.Scope
@ -39,7 +39,7 @@ object ActorTest {
Normally inaccessible from the outside, the payload is unwrapped within the standard receive PartialFunction. Normally inaccessible from the outside, the payload is unwrapped within the standard receive PartialFunction.
By interacting with a TestProbe constructor param, information that would be concealed by MdcMsg can be polled. By interacting with a TestProbe constructor param, information that would be concealed by MdcMsg can be polled.
The l-neighbor of the MDCContextAware is the system of the ActorTest TestKit. The l-neighbor of the MDCContextAware is the system of the base.ActorTest TestKit.
The r-neighbor of the MDCContextAware is this MDCTestProbe and, indirectly, the TestProbe that was interjected. The r-neighbor of the MDCContextAware is this MDCTestProbe and, indirectly, the TestProbe that was interjected.
Pass l-input into the MDCContextAware itself. Pass l-input into the MDCContextAware itself.
The r-output is a normal message that can be polled on that TestProbe. The r-output is a normal message that can be polled on that TestProbe.