From 33a7636f5e72bed6578fc02215271d13ec13e830 Mon Sep 17 00:00:00 2001 From: FateJH Date: Mon, 5 Mar 2018 23:26:38 -0500 Subject: [PATCH] changing continent id and building id to normal data types in BroadcastWarpgateUpdate and BuildingInfoUpdate; implementing warp gates and branched logic for warp gate initialization in WSA; updating tests --- .../serverobject/structures/WarpGate.scala | 22 ++++ .../game/BroadcastWarpgateUpdateMessage.scala | 18 +-- .../game/BuildingInfoUpdateMessage.scala | 12 +- .../BroadcastWarpgateUpdateMessageTest.scala | 6 +- .../game/BuildingInfoUpdateMessageTest.scala | 50 ++++----- .../src/test/scala/objects/BuildingTest.scala | 15 ++- .../objects/ServerObjectBuilderTest.scala | 26 ++++- pslogin/src/main/scala/Maps.scala | 6 +- .../src/main/scala/WorldSessionActor.scala | 104 ++++++++++++------ 9 files changed, 176 insertions(+), 83 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/structures/WarpGate.scala 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 = {