mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-15 00:14:40 +00:00
Zone Setup (#1208)
* zone entity setup code has been moved from an instance method to a class function * port address bind to socket and request to join game world address are different
This commit is contained in:
parent
00a6f2abe1
commit
6d1530c311
4 changed files with 354 additions and 303 deletions
|
|
@ -27,6 +27,8 @@ import scala.concurrent.duration._
|
||||||
import scala.util.matching.Regex
|
import scala.util.matching.Regex
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
object LoginActor {
|
object LoginActor {
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
|
|
||||||
|
|
@ -56,7 +58,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
||||||
var port: Int = 0
|
var port: Int = 0
|
||||||
|
|
||||||
val serverName: String = Config.app.world.serverName
|
val serverName: String = Config.app.world.serverName
|
||||||
val publicAddress = new InetSocketAddress(InetAddress.getByName(Config.app.public), Config.app.world.port)
|
val gameTestServerAddress = new InetSocketAddress(InetAddress.getByName(Config.app.public), Config.app.world.port)
|
||||||
|
|
||||||
private val bcryptRounds = 12
|
private val bcryptRounds = 12
|
||||||
|
|
||||||
|
|
@ -87,8 +89,10 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
||||||
case packet: PlanetSideGamePacket =>
|
case packet: PlanetSideGamePacket =>
|
||||||
handleGamePkt(packet)
|
handleGamePkt(packet)
|
||||||
|
|
||||||
case SocketPane.NextPort(_, address, portNum) =>
|
case SocketPane.NextPort(_, _, portNum) =>
|
||||||
val response = ConnectToWorldMessage(serverName, address.getHostAddress, portNum)
|
val address = gameTestServerAddress.getAddress.getHostAddress
|
||||||
|
log.info(s"Connecting to ${address.toLowerCase}: $portNum ...")
|
||||||
|
val response = ConnectToWorldMessage(serverName, address, portNum)
|
||||||
middlewareActor ! MiddlewareActor.Send(response)
|
middlewareActor ! MiddlewareActor.Send(response)
|
||||||
middlewareActor ! MiddlewareActor.Close()
|
middlewareActor ! MiddlewareActor.Close()
|
||||||
|
|
||||||
|
|
@ -109,7 +113,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
||||||
requestAccountLogin(username, password, token)
|
requestAccountLogin(username, password, token)
|
||||||
|
|
||||||
case ConnectToWorldRequestMessage(name, _, _, _, _, _, _, _) =>
|
case ConnectToWorldRequestMessage(name, _, _, _, _, _, _, _) =>
|
||||||
log.info(s"Connect to world request for '$name'")
|
log.info(s"Request to connect to world '$name' ...")
|
||||||
sockets ! SocketPane.GetNextPort("world", context.self)
|
sockets ! SocketPane.GetNextPort("world", context.self)
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|
@ -440,7 +444,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
||||||
serverName,
|
serverName,
|
||||||
WorldStatus.Up,
|
WorldStatus.Up,
|
||||||
Config.app.world.serverType,
|
Config.app.world.serverType,
|
||||||
Vector(WorldConnectionInfo(publicAddress)), //todo ideally, ask for info from SocketPane
|
Vector(WorldConnectionInfo(gameTestServerAddress)), //todo ideally, ask for info from SocketPane
|
||||||
PlanetSideEmpire.VS
|
PlanetSideEmpire.VS
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package net.psforever.actors.zone
|
package net.psforever.actors.zone
|
||||||
|
|
||||||
import akka.actor.typed.{ActorRef, Behavior, SupervisorStrategy}
|
import akka.actor.typed.{ActorRef, Behavior, PostStop, SupervisorStrategy}
|
||||||
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
|
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
|
||||||
import net.psforever.objects.ce.Deployable
|
import net.psforever.objects.ce.Deployable
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
import net.psforever.objects.serverobject.structures.{StructureType, WarpGate}
|
import net.psforever.objects.serverobject.structures.{StructureType, WarpGate}
|
||||||
|
|
@ -29,7 +29,7 @@ object ZoneActor {
|
||||||
def apply(zone: Zone): Behavior[Command] =
|
def apply(zone: Zone): Behavior[Command] =
|
||||||
Behaviors
|
Behaviors
|
||||||
.supervise[Command] {
|
.supervise[Command] {
|
||||||
Behaviors.setup(context => new ZoneActor(context, zone))
|
Behaviors.setup(context => new ZoneActor(context, zone).onMessage())
|
||||||
}
|
}
|
||||||
.onFailure[Exception](SupervisorStrategy.resume)
|
.onFailure[Exception](SupervisorStrategy.resume)
|
||||||
|
|
||||||
|
|
@ -80,8 +80,10 @@ object ZoneActor {
|
||||||
final case class RewardOurSupporters(target: SourceEntry, history: Iterable[InGameActivity], kill: Kill, bep: Long) extends Command
|
final case class RewardOurSupporters(target: SourceEntry, history: Iterable[InGameActivity], kill: Kill, bep: Long) extends Command
|
||||||
}
|
}
|
||||||
|
|
||||||
class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
|
class ZoneActor(
|
||||||
extends AbstractBehavior[ZoneActor.Command](context) {
|
context: ActorContext[ZoneActor.Command],
|
||||||
|
zone: Zone
|
||||||
|
) {
|
||||||
|
|
||||||
import ZoneActor._
|
import ZoneActor._
|
||||||
import ctx._
|
import ctx._
|
||||||
|
|
@ -116,59 +118,78 @@ class ZoneActor(context: ActorContext[ZoneActor.Command], zone: Zone)
|
||||||
case Failure(e) => log.error(e.getMessage)
|
case Failure(e) => log.error(e.getMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
def onMessage(msg: Command): Behavior[Command] = {
|
def onMessage(): Behavior[Command] = {
|
||||||
msg match {
|
Behaviors.receiveMessagePartial[Command] {
|
||||||
case GetZone(replyTo) =>
|
case GetZone(replyTo) =>
|
||||||
replyTo ! ZoneResponse(zone)
|
replyTo ! ZoneResponse(zone)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case AddPlayer(player) =>
|
case AddPlayer(player) =>
|
||||||
players.addOne(player)
|
players.addOne(player)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case RemovePlayer(player) =>
|
case RemovePlayer(player) =>
|
||||||
players.filterInPlace(p => p.CharId == player.CharId)
|
players.filterInPlace(p => p.CharId == player.CharId)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case DropItem(item, position, orientation) =>
|
case DropItem(item, position, orientation) =>
|
||||||
zone.Ground ! Zone.Ground.DropItem(item, position, orientation)
|
zone.Ground ! Zone.Ground.DropItem(item, position, orientation)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case PickupItem(guid) =>
|
case PickupItem(guid) =>
|
||||||
zone.Ground ! Zone.Ground.PickupItem(guid)
|
zone.Ground ! Zone.Ground.PickupItem(guid)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case BuildDeployable(obj, _) =>
|
case BuildDeployable(obj, _) =>
|
||||||
zone.Deployables ! Zone.Deployable.Build(obj)
|
zone.Deployables ! Zone.Deployable.Build(obj)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case DismissDeployable(obj) =>
|
case DismissDeployable(obj) =>
|
||||||
zone.Deployables ! Zone.Deployable.Dismiss(obj)
|
zone.Deployables ! Zone.Deployable.Dismiss(obj)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case SpawnVehicle(vehicle) =>
|
case SpawnVehicle(vehicle) =>
|
||||||
zone.Transport ! Zone.Vehicle.Spawn(vehicle)
|
zone.Transport ! Zone.Vehicle.Spawn(vehicle)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case DespawnVehicle(vehicle) =>
|
case DespawnVehicle(vehicle) =>
|
||||||
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case AddToBlockMap(target, toPosition) =>
|
case AddToBlockMap(target, toPosition) =>
|
||||||
zone.blockMap.addTo(target, toPosition)
|
zone.blockMap.addTo(target, toPosition)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case UpdateBlockMap(target, toPosition) =>
|
case UpdateBlockMap(target, toPosition) =>
|
||||||
zone.blockMap.move(target, toPosition)
|
zone.blockMap.move(target, toPosition)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case RemoveFromBlockMap(target) =>
|
case RemoveFromBlockMap(target) =>
|
||||||
zone.blockMap.removeFrom(target)
|
zone.blockMap.removeFrom(target)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case HotSpotActivity(defender, attacker, location) =>
|
case HotSpotActivity(defender, attacker, location) =>
|
||||||
zone.Activity ! Zone.HotSpot.Activity(defender, attacker, location)
|
zone.Activity ! Zone.HotSpot.Activity(defender, attacker, location)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case RewardThisDeath(entity) =>
|
case RewardThisDeath(entity) =>
|
||||||
experience ! ExperienceCalculator.RewardThisDeath(entity)
|
experience ! ExperienceCalculator.RewardThisDeath(entity)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case RewardOurSupporters(target, history, kill, bep) =>
|
case RewardOurSupporters(target, history, kill, bep) =>
|
||||||
supportExperience ! SupportExperienceCalculator.RewardOurSupporters(target, history, kill, bep)
|
supportExperience ! SupportExperienceCalculator.RewardOurSupporters(target, history, kill, bep)
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
case ZoneMapUpdate() =>
|
case ZoneMapUpdate() =>
|
||||||
zone.Buildings
|
zone.Buildings
|
||||||
.filter(_._2.BuildingType == StructureType.Facility)
|
.filter(_._2.BuildingType == StructureType.Facility)
|
||||||
.values
|
.values
|
||||||
.foreach(_.Actor ! BuildingActor.MapUpdate())
|
.foreach(_.Actor ! BuildingActor.MapUpdate())
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
Behaviors.same
|
||||||
}
|
}
|
||||||
this
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ import net.psforever.objects.zones.blockmap.{BlockMap, SectorPopulation}
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.zones.Zones
|
import net.psforever.zones.Zones
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.{Future, Promise}
|
import scala.concurrent.{Future, Promise}
|
||||||
|
|
@ -84,15 +85,14 @@ import scala.concurrent.{Future, Promise}
|
||||||
* `LivePlayerList`
|
* `LivePlayerList`
|
||||||
*/
|
*/
|
||||||
class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
||||||
|
|
||||||
/** Governs general synchronized external requests. */
|
/** Governs general synchronized external requests. */
|
||||||
var actor: typed.ActorRef[ZoneActor.Command] = _
|
var actor: typed.ActorRef[ZoneActor.Command] = Default.typed.Actor
|
||||||
|
|
||||||
/** Actor that handles SOI related functionality, for example if a player is in an SOI */
|
/** Actor that handles SOI related functionality, for example if a player is in an SOI */
|
||||||
private var soi = Default.Actor
|
private var soi = Default.Actor
|
||||||
|
|
||||||
/** The basic support structure for the globally unique number system used by this `Zone`. */
|
/** The basic support structure for the globally unique number system used by this `Zone`. */
|
||||||
private var guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(65536))
|
private var guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(max = 65536))
|
||||||
/** The core of the unique number system, to which requests may be submitted.
|
/** The core of the unique number system, to which requests may be submitted.
|
||||||
* @see `UniqueNumberSys`
|
* @see `UniqueNumberSys`
|
||||||
* @see `Zone.Init(ActorContext)`
|
* @see `Zone.Init(ActorContext)`
|
||||||
|
|
@ -195,167 +195,16 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
||||||
def ZoneInitialized(): Future[Boolean] = zoneInitialized.future
|
def ZoneInitialized(): Future[Boolean] = zoneInitialized.future
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establish the basic accessible conditions necessary for a functional `Zone`.<br>
|
* Establish the basic accessible conditions necessary for a functional `Zone`.
|
||||||
* <br>
|
* @see `Zone.init`
|
||||||
* Called from the `Actor` that governs this `Zone` when it is passed a constructor reference to the `Zone`.
|
|
||||||
* Specifically, the order of calling follows: `InterstellarCluster.preStart -> ZoneActor.receive(Zone.Init()) -> Zone.Init`.
|
|
||||||
* The basic method performs three main operations.
|
|
||||||
* First, the `Actor`-driven aspect of the globally unique identifier system for this `Zone` is finalized.
|
|
||||||
* Second, all supporting `Actor` agents are created, e.g., `ground`.
|
|
||||||
* Third, the `ZoneMap` server objects are loaded and constructed within that aforementioned system.
|
|
||||||
* To avoid being called more than once, there is a test whether the globally unique identifier system has been changed.<br>
|
|
||||||
* <br>
|
|
||||||
* Execution of this operation should be fail-safe.
|
|
||||||
* The chances of failure should be mitigated or skipped.
|
|
||||||
* A testing routine should be run after the fact on the results of the process.
|
|
||||||
*
|
|
||||||
* @see `ZoneActor.ZoneSetupCheck`
|
|
||||||
* @param context a reference to an `ActorContext` necessary for `Props`
|
* @param context a reference to an `ActorContext` necessary for `Props`
|
||||||
*/
|
*/
|
||||||
def init(implicit context: ActorContext): Unit = {
|
def init(implicit context: ActorContext): Unit = {
|
||||||
if (unops == null) {
|
if(Zone.Setup.init(zone = this)) {
|
||||||
SetupNumberPools()
|
|
||||||
context.actorOf(Props(classOf[UniqueNumberSys], this, this.guid), s"zone-$id-uns")
|
|
||||||
ground = context.actorOf(Props(classOf[ZoneGroundActor], this, equipmentOnGround), s"zone-$id-ground")
|
|
||||||
deployables = context.actorOf(Props(classOf[ZoneDeployableActor], this, constructions, linkDynamicTurretWeapon), s"zone-$id-deployables")
|
|
||||||
projectiles = context.actorOf(Props(classOf[ZoneProjectileActor], this, projectileList), s"zone-$id-projectiles")
|
|
||||||
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles, linkDynamicTurretWeapon), s"zone-$id-vehicles")
|
|
||||||
population = context.actorOf(Props(classOf[ZonePopulationActor], this, players, corpses), s"zone-$id-players")
|
|
||||||
projector = context.actorOf(
|
|
||||||
Props(classOf[ZoneHotSpotDisplay], this, hotspots, 15 seconds, hotspotHistory, 60 seconds),
|
|
||||||
s"zone-$id-hotspots"
|
|
||||||
)
|
|
||||||
soi = context.actorOf(Props(classOf[SphereOfInfluenceActor], this), s"zone-$id-soi")
|
|
||||||
|
|
||||||
avatarEvents = context.actorOf(Props(classOf[AvatarService], this), s"zone-$id-avatar-events")
|
|
||||||
localEvents = context.actorOf(Props(classOf[LocalService], this), s"zone-$id-local-events")
|
|
||||||
vehicleEvents = context.actorOf(Props(classOf[VehicleService], this), s"zone-$id-vehicle-events")
|
|
||||||
|
|
||||||
implicit val guid: NumberPoolHub = this.guid //passed into builderObject.Build implicitly
|
|
||||||
BuildLocalObjects(context, guid)
|
|
||||||
BuildSupportObjects()
|
|
||||||
MakeBuildings(context)
|
|
||||||
MakeLattice()
|
|
||||||
AssignAmenities()
|
|
||||||
CreateSpawnGroups()
|
|
||||||
PopulateBlockMap()
|
|
||||||
validate()
|
|
||||||
|
|
||||||
zoneInitialized.success(true)
|
zoneInitialized.success(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def validate(): Unit = {
|
|
||||||
implicit val log: Logger = org.log4s.getLogger(s"zone/$id/sanity")
|
|
||||||
|
|
||||||
//check bases
|
|
||||||
map.objectToBuilding.values
|
|
||||||
.toSet[Int]
|
|
||||||
.foreach(building_id => {
|
|
||||||
val target = Building(building_id)
|
|
||||||
if (target.isEmpty) {
|
|
||||||
log.error(s"expected a building for id #$building_id")
|
|
||||||
} else if (!target.get.HasGUID) {
|
|
||||||
log.error(s"building #$building_id was not registered")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
//check base to object associations
|
|
||||||
map.objectToBuilding.keys.foreach(object_guid =>
|
|
||||||
if (guid(object_guid).isEmpty) {
|
|
||||||
log.error(s"expected object id $object_guid to exist, but it did not")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
//check door to lock association
|
|
||||||
map.doorToLock.foreach({
|
|
||||||
case (doorGuid, lockGuid) =>
|
|
||||||
validateObject(doorGuid, (x: PlanetSideGameObject) => x.isInstanceOf[serverobject.doors.Door], "door")
|
|
||||||
validateObject(lockGuid, (x: PlanetSideGameObject) => x.isInstanceOf[serverobject.locks.IFFLock], "IFF lock")
|
|
||||||
})
|
|
||||||
|
|
||||||
//check vehicle terminal to spawn pad association
|
|
||||||
map.terminalToSpawnPad.foreach({
|
|
||||||
case (termGuid, padGuid) =>
|
|
||||||
validateObject(
|
|
||||||
termGuid,
|
|
||||||
(x: PlanetSideGameObject) => x.isInstanceOf[serverobject.terminals.Terminal],
|
|
||||||
"vehicle terminal"
|
|
||||||
)
|
|
||||||
validateObject(
|
|
||||||
padGuid,
|
|
||||||
(x: PlanetSideGameObject) => x.isInstanceOf[serverobject.pad.VehicleSpawnPad],
|
|
||||||
"vehicle spawn pad"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
//check implant terminal mech to implant terminal interface association
|
|
||||||
map.terminalToInterface.foreach({
|
|
||||||
case (mechGuid, interfaceGuid) =>
|
|
||||||
validateObject(
|
|
||||||
mechGuid,
|
|
||||||
(x: PlanetSideGameObject) => x.isInstanceOf[ImplantTerminalMech],
|
|
||||||
"implant terminal mech"
|
|
||||||
)
|
|
||||||
validateObject(
|
|
||||||
interfaceGuid,
|
|
||||||
(o: PlanetSideGameObject) => o.isInstanceOf[serverobject.terminals.Terminal],
|
|
||||||
"implant terminal interface"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
//check manned turret to weapon association
|
|
||||||
map.turretToWeapon.foreach({
|
|
||||||
case (turretGuid, weaponGuid) =>
|
|
||||||
validateObject(
|
|
||||||
turretGuid,
|
|
||||||
(o: PlanetSideGameObject) => o.isInstanceOf[serverobject.turret.FacilityTurret],
|
|
||||||
"facility turret mount"
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
validateObject(
|
|
||||||
weaponGuid,
|
|
||||||
(o: PlanetSideGameObject) => o.isInstanceOf[net.psforever.objects.Tool],
|
|
||||||
"facility turret weapon"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (GUID(weaponGuid).get.asInstanceOf[Tool].AmmoSlots.count(!_.Box.HasGUID) > 0) {
|
|
||||||
log.error(s"expected weapon $weaponGuid has an unregistered ammunition unit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recover an object from a collection and perform any number of validating tests upon it.
|
|
||||||
* If the object fails any tests, log an error.
|
|
||||||
*
|
|
||||||
* @param objectGuid the unique indentifier being checked against the `guid` access point
|
|
||||||
* @param test a test for the discovered object;
|
|
||||||
* expects at least `Type` checking
|
|
||||||
* @param description an explanation of how the object, if not discovered, should be identified
|
|
||||||
* @return `true` if the object was discovered and validates correctly;
|
|
||||||
* `false` if the object failed any tests
|
|
||||||
*/
|
|
||||||
def validateObject(
|
|
||||||
objectGuid: Int,
|
|
||||||
test: PlanetSideGameObject => Boolean,
|
|
||||||
description: String
|
|
||||||
)(implicit log: Logger): Boolean = {
|
|
||||||
try {
|
|
||||||
if (!test(GUID(objectGuid).get)) {
|
|
||||||
log.error(s"expected id $objectGuid to be a $description, but it was not")
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
case e: Exception =>
|
|
||||||
log.error(s"expected a $description at id $objectGuid but no object is initialized - $e")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def SetupNumberPools(): Unit = { /* override to tailor to suit requirements of zone */ }
|
def SetupNumberPools(): Unit = { /* override to tailor to suit requirements of zone */ }
|
||||||
|
|
||||||
def findSpawns(
|
def findSpawns(
|
||||||
|
|
@ -632,129 +481,6 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
||||||
map.zipLinePaths
|
map.zipLinePaths
|
||||||
}
|
}
|
||||||
|
|
||||||
private def BuildLocalObjects(implicit context: ActorContext, guid: NumberPoolHub): Unit = {
|
|
||||||
map.localObjects.foreach({ builderObject =>
|
|
||||||
builderObject.Build
|
|
||||||
|
|
||||||
val obj = guid(builderObject.Id)
|
|
||||||
obj collect {
|
|
||||||
case el: ZoneAware => el.Zone = this
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private def BuildSupportObjects(): Unit = {
|
|
||||||
//guard against errors here, but don't worry about specifics; let ZoneActor.ZoneSetupCheck complain about problems
|
|
||||||
val other: ListBuffer[PlanetSideGameObject] = new ListBuffer[PlanetSideGameObject]()
|
|
||||||
//turret to weapon
|
|
||||||
map.turretToWeapon.foreach({
|
|
||||||
case (turret_guid, weapon_guid) =>
|
|
||||||
((GUID(turret_guid) match {
|
|
||||||
case Some(obj: FacilityTurret) =>
|
|
||||||
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)
|
|
||||||
other ++= weapon.AmmoSlots.map(slot => slot.Box)
|
|
||||||
other ++= obj.Inventory.Items.map(item => item.obj) //internal ammunition reserves, if any
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
//after all fixed GUID's are defined ...
|
|
||||||
other.foreach(obj => guid.register(obj, obj.Definition.registerAs))
|
|
||||||
}
|
|
||||||
|
|
||||||
private def MakeBuildings(implicit context: ActorContext): PairMap[Int, Building] = {
|
|
||||||
val buildingList = map.localBuildings
|
|
||||||
val registrationKeys: Map[Int, Try[LoanedKey]] = buildingList.map {
|
|
||||||
case ((_, building_guid: Int, _), _) =>
|
|
||||||
(building_guid, guid.register(building_guid))
|
|
||||||
}
|
|
||||||
buildings = buildingList.map({
|
|
||||||
case ((name, building_guid, map_id), constructor) if registrationKeys(building_guid).isSuccess =>
|
|
||||||
val building = constructor.Build(name, building_guid, map_id, this)
|
|
||||||
registrationKeys(building_guid).get.Object = building
|
|
||||||
building_guid -> building
|
|
||||||
})
|
|
||||||
buildings
|
|
||||||
}
|
|
||||||
|
|
||||||
private def AssignAmenities(): Unit = {
|
|
||||||
map.objectToBuilding.foreach({
|
|
||||||
case (object_guid, building_id) =>
|
|
||||||
(buildings.get(building_id), guid(object_guid)) match {
|
|
||||||
case (Some(building), Some(amenity: Amenity)) =>
|
|
||||||
building.Amenities = amenity
|
|
||||||
case (Some(_), _) | (None, _) | (_, None) => () //let ZoneActor's sanity check catch this error
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Zone.AssignOutwardSideToDoors(zone = this)
|
|
||||||
Zone.AssignSidednessToAmenities(zone = this)
|
|
||||||
//ntu management (eventually move to a generic building startup function)
|
|
||||||
buildings.values
|
|
||||||
.flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo))
|
|
||||||
.collect {
|
|
||||||
case silo: ResourceSilo =>
|
|
||||||
silo.Actor ! Service.Startup()
|
|
||||||
}
|
|
||||||
//some painfields need to look for their closest door
|
|
||||||
buildings.values
|
|
||||||
.flatMap(_.Amenities.filter(_.Definition.isInstanceOf[PainboxDefinition]))
|
|
||||||
.collect {
|
|
||||||
case painbox: Painbox =>
|
|
||||||
painbox.Actor ! Service.Startup()
|
|
||||||
}
|
|
||||||
//the orbital_buildings in sanctuary zones have to establish their shuttle routes
|
|
||||||
map.shuttleBays
|
|
||||||
.map { guid(_) }
|
|
||||||
.collect { case Some(obj: OrbitalShuttlePad) =>
|
|
||||||
obj.Actor ! Service.Startup()
|
|
||||||
}
|
|
||||||
//allocate soi information
|
|
||||||
soi ! SOI.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private def MakeLattice(): Unit = {
|
|
||||||
lattice ++= map.latticeLink
|
|
||||||
.filterNot {
|
|
||||||
case (a, _) => a.contains("/") //ignore intercontinental lattice connections
|
|
||||||
}
|
|
||||||
.map {
|
|
||||||
case (source, target) =>
|
|
||||||
val (sourceBuilding, targetBuilding) = (Building(source), Building(target)) match {
|
|
||||||
case (Some(sBuilding), Some(tBuilding)) => (sBuilding, tBuilding)
|
|
||||||
case _ =>
|
|
||||||
throw new NoSuchElementException(s"Zone $id - can't create lattice link between $source and $target.")
|
|
||||||
}
|
|
||||||
sourceBuilding ~ targetBuilding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def CreateSpawnGroups(): Unit = {
|
|
||||||
buildings.values
|
|
||||||
.filterNot { _.Position == Vector3.Zero }
|
|
||||||
.map(building => { building -> building.Amenities.collect { case obj: SpawnPoint => obj } })
|
|
||||||
.filter({ case (_, spawns) => spawns.nonEmpty })
|
|
||||||
.foreach { SpawnGroups }
|
|
||||||
|
|
||||||
buildings.values
|
|
||||||
.filterNot { _.Position == Vector3.Zero }
|
|
||||||
.collect { case building: WarpGate => building -> List(building.asInstanceOf[SpawnPoint]) }
|
|
||||||
.foreach { SpawnGroups }
|
|
||||||
}
|
|
||||||
|
|
||||||
def SpawnGroups(): Map[Building, List[SpawnPoint]] = spawnGroups
|
def SpawnGroups(): Map[Building, List[SpawnPoint]] = spawnGroups
|
||||||
|
|
||||||
def SpawnGroups(building: Building): List[SpawnPoint] = SpawnGroups(building.MapId)
|
def SpawnGroups(building: Building): List[SpawnPoint] = SpawnGroups(building.MapId)
|
||||||
|
|
@ -775,15 +501,6 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
||||||
entry
|
entry
|
||||||
}
|
}
|
||||||
|
|
||||||
def PopulateBlockMap(): Unit = {
|
|
||||||
vehicles.foreach { vehicle => blockMap.addTo(vehicle) }
|
|
||||||
buildings.values.foreach { building =>
|
|
||||||
blockMap.addTo(building)
|
|
||||||
building.Amenities.foreach { amenity => blockMap.addTo(amenity) }
|
|
||||||
}
|
|
||||||
map.environment.foreach { env => blockMap.addTo(env) }
|
|
||||||
}
|
|
||||||
|
|
||||||
def StartPlayerManagementSystems(): Unit = {
|
def StartPlayerManagementSystems(): Unit = {
|
||||||
soi ! SOI.Start()
|
soi ! SOI.Start()
|
||||||
}
|
}
|
||||||
|
|
@ -1589,6 +1306,315 @@ object Zone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Setup {
|
||||||
|
/* zone setup code */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establish the basic accessible conditions necessary for a functional `Zone`.<br>
|
||||||
|
* <br>
|
||||||
|
* Called from the `Actor` that governs this `Zone` when it is passed a constructor reference to the `Zone`.
|
||||||
|
* Specifically, the order of calling follows: `InterstellarCluster.preStart -> ZoneActor.receive(Zone.Init()) -> Zone.Init`.
|
||||||
|
* The basic method performs three main operations.
|
||||||
|
* First, the `Actor`-driven aspect of the globally unique identifier system for this `Zone` is finalized.
|
||||||
|
* Second, all supporting `Actor` agents are created, e.g., `ground`.
|
||||||
|
* Third, the `ZoneMap` server objects are loaded and constructed within that aforementioned system.
|
||||||
|
* To avoid being called more than once, there is a test whether the globally unique identifier system has been changed.<br>
|
||||||
|
* <br>
|
||||||
|
* Execution of this operation should be fail-safe.
|
||||||
|
* The chances of failure should be mitigated or skipped.
|
||||||
|
* A testing routine should be run after the fact on the results of the process.
|
||||||
|
*
|
||||||
|
* @see `ZoneActor.ZoneSetupCheck`
|
||||||
|
* @param context a reference to an `ActorContext` necessary for `Props`
|
||||||
|
*/
|
||||||
|
def init(zone: Zone)(implicit context: ActorContext): Boolean = {
|
||||||
|
implicit val guid: NumberPoolHub = zone.guid //passed into builderObject.Build implicitly
|
||||||
|
if (zone.unops == null) {
|
||||||
|
val id = s"zone-${zone.id}"
|
||||||
|
zone.SetupNumberPools()
|
||||||
|
context.actorOf(Props(classOf[UniqueNumberSys], zone, zone.guid), s"$id-uns")
|
||||||
|
zone.ground = context.actorOf(Props(classOf[ZoneGroundActor], zone, zone.equipmentOnGround), s"$id-ground")
|
||||||
|
zone.deployables = context.actorOf(Props(classOf[ZoneDeployableActor], zone, zone.constructions, zone.linkDynamicTurretWeapon), s"$id-deployables")
|
||||||
|
zone.projectiles = context.actorOf(Props(classOf[ZoneProjectileActor], zone, zone.projectileList), s"$id-projectiles")
|
||||||
|
zone.transport = context.actorOf(Props(classOf[ZoneVehicleActor], zone, zone.vehicles, zone.linkDynamicTurretWeapon), s"$id-vehicles")
|
||||||
|
zone.population = context.actorOf(Props(classOf[ZonePopulationActor], zone, zone.players, zone.corpses), s"$id-players")
|
||||||
|
zone.projector = context.actorOf(
|
||||||
|
Props(classOf[ZoneHotSpotDisplay], zone, zone.hotspots, 15 seconds, zone.hotspotHistory, 60 seconds),
|
||||||
|
s"$id-hotspots"
|
||||||
|
)
|
||||||
|
zone.soi = context.actorOf(Props(classOf[SphereOfInfluenceActor], zone), s"$id-soi")
|
||||||
|
|
||||||
|
zone.avatarEvents = context.actorOf(Props(classOf[AvatarService], zone), s"$id-avatar-events")
|
||||||
|
zone.localEvents = context.actorOf(Props(classOf[LocalService], zone), s"$id-local-events")
|
||||||
|
zone.vehicleEvents = context.actorOf(Props(classOf[VehicleService], zone), s"$id-vehicle-events")
|
||||||
|
|
||||||
|
BuildLocalObjects(zone)(context, guid)
|
||||||
|
BuildSupportObjects(zone)
|
||||||
|
MakeBuildings(zone)(context, guid)
|
||||||
|
MakeLattice(zone)
|
||||||
|
AssignAmenities(zone)
|
||||||
|
CreateSpawnGroups(zone)
|
||||||
|
PopulateBlockMap(zone)
|
||||||
|
}
|
||||||
|
Validate(zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def BuildLocalObjects(zone: Zone)(implicit context: ActorContext, guid: NumberPoolHub): Unit = {
|
||||||
|
zone.map.localObjects.foreach { builderObject =>
|
||||||
|
builderObject.Build
|
||||||
|
val obj = guid(builderObject.Id)
|
||||||
|
obj collect {
|
||||||
|
case el: ZoneAware => el.Zone = zone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def BuildSupportObjects(zone: Zone)(implicit guid: NumberPoolHub): Unit = {
|
||||||
|
//guard against errors here, but don't worry about specifics; let ZoneActor.ZoneSetupCheck complain about problems
|
||||||
|
val other: ListBuffer[PlanetSideGameObject] = new ListBuffer[PlanetSideGameObject]()
|
||||||
|
//turret to weapon
|
||||||
|
zone.map.turretToWeapon.foreach {
|
||||||
|
case (turret_guid, weapon_guid) =>
|
||||||
|
((zone.GUID(turret_guid) match {
|
||||||
|
case Some(obj: FacilityTurret) =>
|
||||||
|
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)
|
||||||
|
other ++= weapon.AmmoSlots.map(slot => slot.Box)
|
||||||
|
other ++= obj.Inventory.Items.map(item => item.obj) //internal ammunition reserves, if any
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//after all fixed GUID's are defined ...
|
||||||
|
other.foreach(obj => guid.register(obj, obj.Definition.registerAs))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def MakeBuildings(zone: Zone)(implicit context: ActorContext, guid: NumberPoolHub): PairMap[Int, Building] = {
|
||||||
|
val buildingList = zone.map.localBuildings
|
||||||
|
val registrationKeys: Map[Int, Try[LoanedKey]] = buildingList.map {
|
||||||
|
case ((_, building_guid: Int, _), _) =>
|
||||||
|
(building_guid, guid.register(building_guid))
|
||||||
|
}
|
||||||
|
zone.buildings = buildingList.map {
|
||||||
|
case ((name, building_guid, map_id), constructor) if registrationKeys(building_guid).isSuccess =>
|
||||||
|
val building = constructor.Build(name, building_guid, map_id, zone)
|
||||||
|
registrationKeys(building_guid).get.Object = building
|
||||||
|
building_guid -> building
|
||||||
|
}
|
||||||
|
zone.buildings
|
||||||
|
}
|
||||||
|
|
||||||
|
private def AssignAmenities(zone: Zone)(implicit guid: NumberPoolHub): Unit = {
|
||||||
|
val map = zone.map
|
||||||
|
val buildings = zone.buildings
|
||||||
|
map.objectToBuilding.foreach {
|
||||||
|
case (object_guid, building_id) =>
|
||||||
|
(buildings.get(building_id), guid(object_guid)) match {
|
||||||
|
case (Some(building), Some(amenity: Amenity)) =>
|
||||||
|
building.Amenities = amenity
|
||||||
|
case (Some(_), _) | (None, _) | (_, None) => () //let ZoneActor's sanity check catch this error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Zone.AssignOutwardSideToDoors(zone)
|
||||||
|
Zone.AssignSidednessToAmenities(zone)
|
||||||
|
//ntu management (eventually move to a generic building startup function)
|
||||||
|
buildings.values
|
||||||
|
.flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo))
|
||||||
|
.collect {
|
||||||
|
case silo: ResourceSilo =>
|
||||||
|
silo.Actor ! Service.Startup()
|
||||||
|
}
|
||||||
|
//some painfields need to look for their closest door
|
||||||
|
buildings.values
|
||||||
|
.flatMap(_.Amenities.filter(_.Definition.isInstanceOf[PainboxDefinition]))
|
||||||
|
.collect {
|
||||||
|
case painbox: Painbox =>
|
||||||
|
painbox.Actor ! Service.Startup()
|
||||||
|
}
|
||||||
|
//the orbital_buildings in sanctuary zones have to establish their shuttle routes
|
||||||
|
map.shuttleBays
|
||||||
|
.map {
|
||||||
|
guid(_)
|
||||||
|
}
|
||||||
|
.collect { case Some(obj: OrbitalShuttlePad) =>
|
||||||
|
obj.Actor ! Service.Startup()
|
||||||
|
}
|
||||||
|
//allocate soi information
|
||||||
|
zone.soi ! SOI.Build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def MakeLattice(zone: Zone): Unit = {
|
||||||
|
zone.lattice ++= zone.map.latticeLink
|
||||||
|
.filterNot { case (a, _) => a.contains("/") } //ignore intercontinental lattice connections
|
||||||
|
.map {
|
||||||
|
case (source, target) =>
|
||||||
|
val (sourceBuilding, targetBuilding) = (zone.Building(source), zone.Building(target)) match {
|
||||||
|
case (Some(sBuilding), Some(tBuilding)) => (sBuilding, tBuilding)
|
||||||
|
case _ =>
|
||||||
|
throw new NoSuchElementException(s"Zone ${zone.id} - can't create lattice link between $source and $target.")
|
||||||
|
}
|
||||||
|
sourceBuilding ~ targetBuilding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def CreateSpawnGroups(zone: Zone): Unit = {
|
||||||
|
val allBuildings = zone.buildings.values.filterNot { _.Position == Vector3.Zero }
|
||||||
|
allBuildings
|
||||||
|
.map { building => { building -> building.Amenities.collect { case obj: SpawnPoint => obj } } }
|
||||||
|
.filter { case (_, spawns) => spawns.nonEmpty }
|
||||||
|
.foreach { zone.SpawnGroups }
|
||||||
|
allBuildings
|
||||||
|
.collect { case building: WarpGate => building -> List(building.asInstanceOf[SpawnPoint]) }
|
||||||
|
.foreach { zone.SpawnGroups }
|
||||||
|
}
|
||||||
|
|
||||||
|
private def PopulateBlockMap(zone: Zone): Unit = {
|
||||||
|
val blockMap = zone.blockMap
|
||||||
|
zone.vehicles.foreach { vehicle => blockMap.addTo(vehicle) }
|
||||||
|
zone.buildings.values.foreach { building =>
|
||||||
|
blockMap.addTo(building)
|
||||||
|
building.Amenities.foreach { amenity => blockMap.addTo(amenity) }
|
||||||
|
}
|
||||||
|
zone.map.environment.foreach { env => blockMap.addTo(env) }
|
||||||
|
}
|
||||||
|
|
||||||
|
def Validate(zone: Zone)(implicit guid: NumberPoolHub): Boolean = {
|
||||||
|
implicit val log: Logger = org.log4s.getLogger(s"zone/${zone.id}/sanity")
|
||||||
|
val map = zone.map
|
||||||
|
val counter: AtomicInteger = new AtomicInteger()
|
||||||
|
val validateUsing: (Int, PlanetSideGameObject => Boolean, String) => Boolean = validateObject(zone, counter)
|
||||||
|
|
||||||
|
//check bases
|
||||||
|
map.objectToBuilding.values
|
||||||
|
.toSet[Int]
|
||||||
|
.foreach(building_id => {
|
||||||
|
val target = zone.Building(building_id)
|
||||||
|
if (target.isEmpty) {
|
||||||
|
log.error(s"expected a building for id #$building_id")
|
||||||
|
} else if (!target.get.HasGUID) {
|
||||||
|
log.error(s"building #$building_id was not registered")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//check base to object associations
|
||||||
|
map.objectToBuilding.keys.foreach(object_guid =>
|
||||||
|
if (guid(object_guid).isEmpty) {
|
||||||
|
log.error(s"expected object id $object_guid to exist, but it did not")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//check door to lock association
|
||||||
|
map.doorToLock.foreach({
|
||||||
|
case (doorGuid, lockGuid) =>
|
||||||
|
validateUsing(doorGuid, (x: PlanetSideGameObject) => x.isInstanceOf[serverobject.doors.Door], "door")
|
||||||
|
validateUsing(lockGuid, (x: PlanetSideGameObject) => x.isInstanceOf[serverobject.locks.IFFLock], "IFF lock")
|
||||||
|
})
|
||||||
|
|
||||||
|
//check vehicle terminal to spawn pad association
|
||||||
|
map.terminalToSpawnPad.foreach({
|
||||||
|
case (termGuid, padGuid) =>
|
||||||
|
validateUsing(
|
||||||
|
termGuid,
|
||||||
|
(x: PlanetSideGameObject) => x.isInstanceOf[serverobject.terminals.Terminal],
|
||||||
|
"vehicle terminal"
|
||||||
|
)
|
||||||
|
validateUsing(
|
||||||
|
padGuid,
|
||||||
|
(x: PlanetSideGameObject) => x.isInstanceOf[serverobject.pad.VehicleSpawnPad],
|
||||||
|
"vehicle spawn pad"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
//check implant terminal mech to implant terminal interface association
|
||||||
|
map.terminalToInterface.foreach({
|
||||||
|
case (mechGuid, interfaceGuid) =>
|
||||||
|
validateUsing(
|
||||||
|
mechGuid,
|
||||||
|
(x: PlanetSideGameObject) => x.isInstanceOf[ImplantTerminalMech],
|
||||||
|
"implant terminal mech"
|
||||||
|
)
|
||||||
|
validateUsing(
|
||||||
|
interfaceGuid,
|
||||||
|
(o: PlanetSideGameObject) => o.isInstanceOf[serverobject.terminals.Terminal],
|
||||||
|
"implant terminal interface"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
//check manned turret to weapon association
|
||||||
|
map.turretToWeapon.foreach({
|
||||||
|
case (turretGuid, weaponGuid) =>
|
||||||
|
validateUsing(
|
||||||
|
turretGuid,
|
||||||
|
(o: PlanetSideGameObject) => o.isInstanceOf[serverobject.turret.FacilityTurret],
|
||||||
|
"facility turret mount"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
validateUsing(
|
||||||
|
weaponGuid,
|
||||||
|
(o: PlanetSideGameObject) => o.isInstanceOf[net.psforever.objects.Tool],
|
||||||
|
"facility turret weapon"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
if (zone.GUID(weaponGuid).get.asInstanceOf[Tool].AmmoSlots.count(!_.Box.HasGUID) > 0) {
|
||||||
|
log.error(s"expected weapon $weaponGuid has an unregistered ammunition unit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
counter.get() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recover an object from a collection and perform any number of validating tests upon it.
|
||||||
|
* If the object fails any tests, log an error.
|
||||||
|
*
|
||||||
|
* @param objectGuid the unique indentifier being checked against the `guid` access point
|
||||||
|
* @param test a test for the discovered object;
|
||||||
|
* expects at least `Type` checking
|
||||||
|
* @param description an explanation of how the object, if not discovered, should be identified
|
||||||
|
* @return `true` if the object was discovered and validates correctly;
|
||||||
|
* `false` if the object failed any tests
|
||||||
|
*/
|
||||||
|
def validateObject(
|
||||||
|
zone: Zone,
|
||||||
|
counter: AtomicInteger
|
||||||
|
)
|
||||||
|
(
|
||||||
|
objectGuid: Int,
|
||||||
|
test: PlanetSideGameObject => Boolean,
|
||||||
|
description: String
|
||||||
|
)(implicit log: Logger): Boolean = {
|
||||||
|
try {
|
||||||
|
if (!test(zone.GUID(objectGuid).get)) {
|
||||||
|
log.error(s"expected id $objectGuid to be a $description, but it was not")
|
||||||
|
counter.incrementAndGet()
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case e: Exception =>
|
||||||
|
log.error(s"expected a $description at id $objectGuid but no object is initialized - $e")
|
||||||
|
counter.incrementAndGet()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* explosions */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates `Damageable` targets within the vicinity of server-prepared damage dealing
|
* Allocates `Damageable` targets within the vicinity of server-prepared damage dealing
|
||||||
* and informs those entities that they have affected by the aforementioned damage.
|
* and informs those entities that they have affected by the aforementioned damage.
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ class InterstellarClusterService(context: ActorContext[InterstellarClusterServic
|
||||||
zoneLoadedList.foreach {
|
zoneLoadedList.foreach {
|
||||||
_.onComplete({
|
_.onComplete({
|
||||||
case Success(true) => continentLinkFunc()
|
case Success(true) => continentLinkFunc()
|
||||||
case _ => //log.error("")
|
case _ => ()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
//construct the zones, resulting in the callback
|
//construct the zones, resulting in the callback
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue