diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/WarpGate.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/WarpGate.scala
new file mode 100644
index 000000000..784685377
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/WarpGate.scala
@@ -0,0 +1,22 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.serverobject.structures
+
+import akka.actor.ActorContext
+import net.psforever.objects.zones.Zone
+
+class WarpGate(id : Int, zone : Zone) extends Building(id, zone) {
+ //TODO stuff later
+}
+
+object WarpGate {
+ def apply(id : Int, zone : Zone) : WarpGate = {
+ new WarpGate(id, zone)
+ }
+
+ def Structure(id : Int, zone : Zone, context : ActorContext) : WarpGate = {
+ import akka.actor.Props
+ val obj = new WarpGate(id, zone)
+ obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-gate")
+ obj
+ }
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/BroadcastWarpgateUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/BroadcastWarpgateUpdateMessage.scala
index d786560b3..89dac67f2 100644
--- a/common/src/main/scala/net/psforever/packet/game/BroadcastWarpgateUpdateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/BroadcastWarpgateUpdateMessage.scala
@@ -17,17 +17,17 @@ import scodec.codecs._
* I believe these `Boolean` values actually indicate some measure of warpgate operation.
* Geowarps, for example, though their appearance does not change, recieve this packet.
* Moreover, they can operate as a receiving-end broadcast gate.
- * @param continent_guid identifies the zone (continent)
- * @param building_guid identifies the warpgate (see `BuildingInfoUpdateMessage`)
+ * @param continent_id the zone
+ * @param building_id the warp gate (see `BuildingInfoUpdateMessage`)
* @param unk1 na
* @param unk2 na
- * @param is_broadcast if true, the gate replaces its destination text with "Broadcast"
+ * @param broadcast if true, the gate replaces its destination text with "Broadcast"
*/
-final case class BroadcastWarpgateUpdateMessage(continent_guid : PlanetSideGUID,
- building_guid : PlanetSideGUID,
+final case class BroadcastWarpgateUpdateMessage(continent_id : Int,
+ building_id : Int,
unk1 : Boolean,
unk2 : Boolean,
- is_broadcast : Boolean)
+ broadcast : Boolean)
extends PlanetSideGamePacket {
type Packet = BroadcastWarpgateUpdateMessage
def opcode = GamePacketOpcode.BroadcastWarpgateUpdateMessage
@@ -36,10 +36,10 @@ final case class BroadcastWarpgateUpdateMessage(continent_guid : PlanetSideGUID,
object BroadcastWarpgateUpdateMessage extends Marshallable[BroadcastWarpgateUpdateMessage] {
implicit val codec : Codec[BroadcastWarpgateUpdateMessage] = (
- ("continent_guid" | PlanetSideGUID.codec) ::
- ("building_guid" | PlanetSideGUID.codec) ::
+ ("continent_id" | uint16L) ::
+ ("building_id" | uint16L) ::
("unk1" | bool) ::
("unk2" | bool) ::
- ("is_broadcast" | bool)
+ ("broadcast" | bool)
).as[BroadcastWarpgateUpdateMessage]
}
diff --git a/common/src/main/scala/net/psforever/packet/game/BuildingInfoUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/BuildingInfoUpdateMessage.scala
index 9887e4093..753a0a72a 100644
--- a/common/src/main/scala/net/psforever/packet/game/BuildingInfoUpdateMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/BuildingInfoUpdateMessage.scala
@@ -87,8 +87,8 @@ final case class Additional3(unk1 : Boolean,
* 064 - Health Module
* 128 - Pain Module
* `
- * @param continent_guid the continent (zone)
- * @param building_guid the building
+ * @param continent_id the continent (zone)
+ * @param building_id the building
* @param ntu_level if the building has a silo, the amount of NTU in that silo;
* NTU is reported in multiples of 10%;
* valid for 0 (0%) to 10 (100%)
@@ -117,8 +117,8 @@ final case class Additional3(unk1 : Boolean,
* @param boost_spawn_pain if the building has spawn tubes, the (boosted) strength of its enemy pain field
* @param boost_generator_pain if the building has a generator, the (boosted) strength of its enemy pain field
*/
-final case class BuildingInfoUpdateMessage(continent_guid : PlanetSideGUID,
- building_guid : PlanetSideGUID,
+final case class BuildingInfoUpdateMessage(continent_id : Int,
+ building_id : Int,
ntu_level : Int,
is_hacked : Boolean,
empire_hack : PlanetSideEmpire.Value,
@@ -171,8 +171,8 @@ object BuildingInfoUpdateMessage extends Marshallable[BuildingInfoUpdateMessage]
).as[Additional3]
implicit val codec : Codec[BuildingInfoUpdateMessage] = (
- ("continent_guid" | PlanetSideGUID.codec) ::
- ("building_guid" | PlanetSideGUID.codec) ::
+ ("continent_id" | uint16L) ::
+ ("building_id" | uint16L) ::
("ntu_level" | uint4L) ::
("is_hacked" | bool ) ::
("empire_hack" | PlanetSideEmpire.codec) ::
diff --git a/common/src/test/scala/game/BroadcastWarpgateUpdateMessageTest.scala b/common/src/test/scala/game/BroadcastWarpgateUpdateMessageTest.scala
index 49bf1221c..eaeed5e8b 100644
--- a/common/src/test/scala/game/BroadcastWarpgateUpdateMessageTest.scala
+++ b/common/src/test/scala/game/BroadcastWarpgateUpdateMessageTest.scala
@@ -12,8 +12,8 @@ class BroadcastWarpgateUpdateMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
case BroadcastWarpgateUpdateMessage(continent_guid, building_guid, state1, state2, state3) =>
- continent_guid mustEqual PlanetSideGUID(13)
- building_guid mustEqual PlanetSideGUID(1)
+ continent_guid mustEqual 13
+ building_guid mustEqual 1
state1 mustEqual false
state2 mustEqual false
state3 mustEqual true
@@ -23,7 +23,7 @@ class BroadcastWarpgateUpdateMessageTest extends Specification {
}
"encode" in {
- val msg = BroadcastWarpgateUpdateMessage(PlanetSideGUID(13), PlanetSideGUID(1), false, false, true)
+ val msg = BroadcastWarpgateUpdateMessage(13, 1, false, false, true)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
diff --git a/common/src/test/scala/game/BuildingInfoUpdateMessageTest.scala b/common/src/test/scala/game/BuildingInfoUpdateMessageTest.scala
index edbd476f5..983f25beb 100644
--- a/common/src/test/scala/game/BuildingInfoUpdateMessageTest.scala
+++ b/common/src/test/scala/game/BuildingInfoUpdateMessageTest.scala
@@ -12,29 +12,29 @@ class BuildingInfoUpdateMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
- case BuildingInfoUpdateMessage(continent_guid : PlanetSideGUID,
- building_guid : PlanetSideGUID,
- ntu_level : Int,
- is_hacked : Boolean,
- empire_hack : PlanetSideEmpire.Value,
- hack_time_remaining : Long,
- empire_own : PlanetSideEmpire.Value,
- unk1 : Long,
- unk1x : Option[Additional1],
- generator_state : PlanetSideGeneratorState.Value,
- spawn_tubes_normal : Boolean,
- force_dome_active : Boolean,
- lattice_benefit : Int,
- unk3 : Int,
- unk4 : List[Additional2],
- unk5 : Long,
- unk6 : Boolean,
- unk7 : Int,
- unk7x : Option[Additional3],
- boost_spawn_pain : Boolean,
- boost_generator_pain : Boolean) =>
- continent_guid mustEqual PlanetSideGUID(4)
- building_guid mustEqual PlanetSideGUID(9)
+ case BuildingInfoUpdateMessage(continent_guid,
+ building_guid,
+ ntu_level,
+ is_hacked,
+ empire_hack,
+ hack_time_remaining,
+ empire_own,
+ unk1,
+ unk1x,
+ generator_state,
+ spawn_tubes_normal,
+ force_dome_active,
+ lattice_benefit,
+ unk3,
+ unk4,
+ unk5,
+ unk6,
+ unk7,
+ unk7x,
+ boost_spawn_pain,
+ boost_generator_pain) =>
+ continent_guid mustEqual 4
+ building_guid mustEqual 9
ntu_level mustEqual 1
is_hacked mustEqual false
empire_hack mustEqual PlanetSideEmpire.NEUTRAL
@@ -61,8 +61,8 @@ class BuildingInfoUpdateMessageTest extends Specification {
}
"encode" in {
- val msg = BuildingInfoUpdateMessage(PlanetSideGUID(4),
- PlanetSideGUID(9),
+ val msg = BuildingInfoUpdateMessage(4,
+ 9,
1,
false,
PlanetSideEmpire.NEUTRAL,
diff --git a/common/src/test/scala/objects/BuildingTest.scala b/common/src/test/scala/objects/BuildingTest.scala
index 122f3d993..dc533b1b8 100644
--- a/common/src/test/scala/objects/BuildingTest.scala
+++ b/common/src/test/scala/objects/BuildingTest.scala
@@ -6,7 +6,7 @@ import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.{Door, DoorControl}
-import net.psforever.objects.serverobject.structures.{Amenity, Building, BuildingControl}
+import net.psforever.objects.serverobject.structures.{Amenity, Building, BuildingControl, WarpGate}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire
@@ -98,6 +98,19 @@ class BuildingTest extends Specification {
}
}
+class WarpGateTest extends Specification {
+ "WarpGate" should {
+ "construct" in {
+ val bldg = WarpGate(10, Zone.Nowhere)
+ bldg.Id mustEqual 10
+ bldg.Actor mustEqual ActorRef.noSender
+ bldg.Amenities mustEqual Nil
+ bldg.Zone mustEqual Zone.Nowhere
+ bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL
+ }
+ }
+}
+
class BuildingControl1Test extends ActorTest {
"Building Control" should {
"construct" in {
diff --git a/common/src/test/scala/objects/ServerObjectBuilderTest.scala b/common/src/test/scala/objects/ServerObjectBuilderTest.scala
index 73f802031..903e2e50d 100644
--- a/common/src/test/scala/objects/ServerObjectBuilderTest.scala
+++ b/common/src/test/scala/objects/ServerObjectBuilderTest.scala
@@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever
package objects
-import akka.actor.{Actor, Props}
+import akka.actor.{Actor, ActorContext, Props}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.objects.serverobject.ServerObjectBuilder
-import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder}
+import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate}
import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3
@@ -14,7 +14,23 @@ import scala.concurrent.duration.Duration
class BuildingBuilderTest extends ActorTest {
"Building object" should {
"build" in {
- val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], 10, Zone.Nowhere), "building")
+ val structure : (Int,Zone,ActorContext)=>Building = Building.Structure
+ val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, 10, Zone.Nowhere), "building")
+ actor ! "!"
+
+ val reply = receiveOne(Duration.create(1000, "ms"))
+ assert(reply.isInstanceOf[Building])
+ assert(reply.asInstanceOf[Building].Id == 10)
+ assert(reply.asInstanceOf[Building].Zone == Zone.Nowhere)
+ }
+ }
+}
+
+class WarpGateBuilderTest extends ActorTest {
+ "WarpGate object" should {
+ "build" in {
+ val structure : (Int,Zone,ActorContext)=>Building = WarpGate.Structure
+ val actor = system.actorOf(Props(classOf[ServerObjectBuilderTest.BuildingTestActor], structure, 10, Zone.Nowhere), "wgate")
actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms"))
@@ -167,10 +183,10 @@ object ServerObjectBuilderTest {
}
}
- class BuildingTestActor(building_id : Int, zone : Zone) extends Actor {
+ class BuildingTestActor(structure_con : (Int,Zone,ActorContext)=>Building, building_id : Int, zone : Zone) extends Actor {
def receive : Receive = {
case _ =>
- sender ! FoundationBuilder(Building.Structure).Build(building_id, zone)(context)
+ sender ! FoundationBuilder(structure_con).Build(building_id, zone)(context)
}
}
}
diff --git a/pslogin/src/main/scala/Maps.scala b/pslogin/src/main/scala/Maps.scala
index 0a67c6c56..e764c0bc7 100644
--- a/pslogin/src/main/scala/Maps.scala
+++ b/pslogin/src/main/scala/Maps.scala
@@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
-import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder}
+import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.types.Vector3
@@ -48,6 +48,10 @@ object Maps {
val map12 = new ZoneMap("map12")
val map13 = new ZoneMap("map13") {
+ LocalBuilding(1, FoundationBuilder(WarpGate.Structure))
+ LocalBuilding(2, FoundationBuilder(WarpGate.Structure))
+ LocalBuilding(3, FoundationBuilder(WarpGate.Structure))
+
LocalObject(ServerObjectBuilder(372, Door.Constructor))
LocalObject(ServerObjectBuilder(373, Door.Constructor))
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index aa0393a56..9eb970b85 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -26,7 +26,7 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
-import net.psforever.objects.serverobject.structures.Building
+import net.psforever.objects.serverobject.structures.{Building, WarpGate}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState}
import net.psforever.objects.zones.{InterstellarCluster, Zone}
@@ -1000,7 +1000,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
case InterstellarCluster.ClientInitializationComplete(tplayer)=>
//custom
sendResponse(ContinentalLockUpdateMessage(13, PlanetSideEmpire.VS)) // "The VS have captured the VS Sanctuary."
- sendResponse(BroadcastWarpgateUpdateMessage(PlanetSideGUID(13), PlanetSideGUID(1), false, false, true)) // VS Sanctuary: Inactive Warpgate -> Broadcast Warpgate
//this will cause the client to send back a BeginZoningMessage packet (see below)
sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L)) //VS Sanctuary
@@ -1241,10 +1240,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(TimeOfDayMessage(1191182336))
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
- sendResponse(ZonePopulationUpdateMessage(PlanetSideGUID(6), 414, 138, 0, 138, 0, 138, 0, 138, 0))
+ sendResponse(ZonePopulationUpdateMessage(6, 414, 138, 0, 138, 0, 138, 0, 138, 0))
(1 to 255).foreach(i => { sendResponse(SetEmpireMessage(PlanetSideGUID(i), PlanetSideEmpire.VS)) })
- //render Equipment that was dropped into zone before the player arrived
+ //render Equipment that was dropped into zone before the player arrived
continent.EquipmentOnGround.foreach(item => {
val definition = item.Definition
sendResponse(
@@ -3041,11 +3040,75 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.error(s"DeployRequest: $obj can not transition to $state - $reason$mobileShift")
}
+ /**
+ * For a given continental structure, determine the method of generating server-join client configuration packets.
+ * @param continentNumber the zone id
+ * @param buildingNumber the building id
+ * @param building the building object
+ */
def initBuilding(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
+ building match {
+ case _ : WarpGate =>
+ initGate(continentNumber, buildingNumber, building)
+ case _ : Building =>
+ initFacility(continentNumber, buildingNumber, building)
+ }
+ }
+
+ /**
+ * For a given facility structure, configure a client by dispatching the appropriate packets.
+ * Pay special attention to the details of `BuildingInfoUpdateMessage` when preparing this packet.
+ * @see `BuildingInfoUpdateMessage`
+ * @see `DensityLevelUpdateMessage`
+ * @param continentNumber the zone id
+ * @param buildingNumber the building id
+ * @param building the building object
+ */
+ def initFacility(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
sendResponse(
BuildingInfoUpdateMessage(
- PlanetSideGUID(continentNumber), PlanetSideGUID(buildingNumber),
- 10,
+ continentNumber, //Zone
+ buildingNumber, //Facility
+ 8, //NTU%
+ false, //Hacked
+ PlanetSideEmpire.NEUTRAL, //Base hacked by
+ 0, //Time remaining for hack (ms)
+ building.Faction, //Base owned by
+ 0, //!! Field != 0 will cause malformed packet. See class def.
+ None,
+ PlanetSideGeneratorState.Normal, //Generator state
+ true, //Respawn tubes operating state
+ false, //Force dome state
+ 0, //Lattice benefits
+ 0, //!! Field > 0 will cause malformed packet. See class def.
+ Nil,
+ 0,
+ false,
+ 8, //!! Field != 8 will cause malformed packet. See class def.
+ None,
+ false, //Boosted spawn room pain field
+ false //Boosted generator room pain field
+ )
+ )
+ sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0,0, 0,0, 0,0, 0,0)))
+ }
+
+ /**
+ * For a given lattice warp gate structure, configure a client by dispatching the appropriate packets.
+ * Unlike other facilities, gates do not have complicated `BuildingInfoUpdateMessage` packets.
+ * Also unlike facilities, gates have an additional packet.
+ * @see `BuildingInfoUpdateMessage`
+ * @see `DensityLevelUpdateMessage`
+ * @see `BroadcastWarpgateUpdateMessage`
+ * @param continentNumber the zone id
+ * @param buildingNumber the building id
+ * @param building the building object
+ */
+ def initGate(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
+ sendResponse(
+ BuildingInfoUpdateMessage(
+ continentNumber, buildingNumber,
+ 0,
false,
PlanetSideEmpire.NEUTRAL,
0,
@@ -3066,33 +3129,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
false
)
)
- sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0,0, 0,0, 0,0, 0,0))) //TODO what is density?
- //TODO BroadcastWarpgateUpdateMessage() for warp gates
- // sendResponse(
- // BuildingInfoUpdateMessage(
- // PlanetSideGUID(6), //Ceryshen
- // PlanetSideGUID(2), //Anguta
- // 8, //80% NTU
- // true, //Base hacked
- // PlanetSideEmpire.NC, //Base hacked by NC
- // 600000, //10 minutes remaining for hack
- // PlanetSideEmpire.VS, //Base owned by VS
- // 0, //!! Field != 0 will cause malformed packet. See class def.
- // None,
- // PlanetSideGeneratorState.Critical, //Generator critical
- // true, //Respawn tubes destroyed
- // true, //Force dome active
- // 16, //Tech plant lattice benefit
- // 0,
- // Nil, //!! Field > 0 will cause malformed packet. See class def.
- // 0,
- // false,
- // 8, //!! Field != 8 will cause malformed packet. See class def.
- // None,
- // true, //Boosted spawn room pain field
- // true //Boosted generator room pain field
- // )
- // )
+ sendResponse(DensityLevelUpdateMessage(continentNumber, buildingNumber, List(0,0, 0,0, 0,0, 0,0)))
+ sendResponse(BroadcastWarpgateUpdateMessage(continentNumber, buildingNumber, false, false, true))
}
def configZone(zone : Zone) : Unit = {