mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-20 02:54:46 +00:00
Merge pull request #193 from Fate-JH/server-init
Packets, and a Warp Gate
This commit is contained in:
commit
bd76d28564
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ import net.psforever.packet.game.objectcreate.ObjectClass
|
|||
*/
|
||||
class ImplantTerminalInterfaceDefinition extends TerminalDefinition(ObjectClass.implant_terminal_interface) {
|
||||
Packet = new ImplantTerminalInterfaceConverter
|
||||
Name = "implante_terminal_interface"
|
||||
Name = "implant_terminal_interface"
|
||||
|
||||
private val implants : Map[String, ImplantDefinition] = Map (
|
||||
"advanced_regen" -> GlobalDefinitions.advanced_regen,
|
||||
|
|
|
|||
|
|
@ -50,9 +50,7 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
|
|||
}
|
||||
|
||||
case InterstellarCluster.RequestClientInitialization(tplayer) =>
|
||||
zones.foreach(zone => {
|
||||
sender ! Zone.ClientInitialization(zone.ClientInitialization()) //do this for each Zone
|
||||
})
|
||||
zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) })
|
||||
sender ! InterstellarCluster.ClientInitializationComplete(tplayer) //will be processed after all Zones
|
||||
|
||||
case _ => ;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import net.psforever.objects.guid.actor.UniqueNumberSystem
|
|||
import net.psforever.objects.guid.selector.RandomSelector
|
||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||
import net.psforever.packet.GamePacket
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
|
|
@ -218,6 +217,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
|
||||
def Transport : ActorRef = transport
|
||||
|
||||
def Buildings : Map[Int, Building] = buildings
|
||||
|
||||
def Building(id : Int) : Option[Building] = {
|
||||
buildings.get(id)
|
||||
}
|
||||
|
|
@ -250,25 +251,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
* - `ZonePopulationUpdateMessage`
|
||||
* @return a `List` of `GamePacket` messages
|
||||
*/
|
||||
def ClientInitialization() : List[GamePacket] = {
|
||||
//TODO unimplemented
|
||||
List.empty[GamePacket]
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide bulk correspondence on all server objects that can be composed into packet messages and reported to a client.
|
||||
* These messages are sent in this fashion at the time of joining a specific `Zone`:<br>
|
||||
* - `HackMessage`<br>
|
||||
* - `PlanetsideAttributeMessage`<br>
|
||||
* - `SetEmpireMessage`<br>
|
||||
* - `TimeOfDayMessage`<br>
|
||||
* - `WeatherMessage`
|
||||
* @return a `List` of `GamePacket` messages
|
||||
*/
|
||||
def ClientConfiguration() : List[GamePacket] = {
|
||||
//TODO unimplemented
|
||||
List.empty[GamePacket]
|
||||
}
|
||||
def ClientInitialization() : Zone = this
|
||||
}
|
||||
|
||||
object Zone {
|
||||
|
|
@ -308,11 +291,11 @@ object Zone {
|
|||
|
||||
/**
|
||||
* Message to report the packet messages that initialize the client.
|
||||
* @param list a `List` of `GamePacket` messages
|
||||
* @param zone a `Zone` to have its buildings and continental parameters turned into packet data
|
||||
* @see `Zone.ClientInitialization()`<br>
|
||||
* `InterstallarCluster`
|
||||
*/
|
||||
final case class ClientInitialization(list : List[GamePacket])
|
||||
final case class ClientInitialization(zone : Zone)
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
|
|
|
|||
|
|
@ -436,8 +436,8 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x60 => game.FavoritesMessage.decode
|
||||
case 0x61 => game.ObjectDetectedMessage.decode
|
||||
case 0x62 => game.SplashHitMessage.decode
|
||||
case 0x63 => noDecoder(SetChatFilterMessage)
|
||||
case 0x64 => noDecoder(AvatarSearchCriteriaMessage)
|
||||
case 0x63 => game.SetChatFilterMessage.decode
|
||||
case 0x64 => game.AvatarSearchCriteriaMessage.decode
|
||||
case 0x65 => noDecoder(AvatarSearchResponse)
|
||||
case 0x66 => game.WeaponJammedMessage.decode
|
||||
case 0x67 => noDecoder(LinkDeadAwarenessMsg)
|
||||
|
|
@ -468,7 +468,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x7c => game.DismountBuildingMsg.decode
|
||||
case 0x7d => noDecoder(UnknownMessage125)
|
||||
case 0x7e => noDecoder(UnknownMessage126)
|
||||
case 0x7f => noDecoder(AvatarStatisticsMessage)
|
||||
case 0x7f => game.AvatarStatisticsMessage.decode
|
||||
|
||||
// OPCODES 0x80-8f
|
||||
case 0x80 => noDecoder(GenericObjectAction2Message)
|
||||
|
|
@ -561,7 +561,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0xca => noDecoder(OutfitBenefitMessage)
|
||||
case 0xcb => noDecoder(EmpireChangeTimeMessage)
|
||||
case 0xcc => noDecoder(ClockCalibrationMessage)
|
||||
case 0xcd => noDecoder(DensityLevelUpdateMessage)
|
||||
case 0xcd => game.DensityLevelUpdateMessage.decode
|
||||
case 0xce => noDecoder(ActOfGodMessage)
|
||||
case 0xcf => noDecoder(AvatarAwardMessage)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +1,48 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import net.psforever.types.ImplantType
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
* Change the state of the implant.<br>
|
||||
* An `Enumeration` for all the actions that can be applied to implants and implant slots.
|
||||
*/
|
||||
object ImplantAction extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val
|
||||
Add,
|
||||
Remove,
|
||||
Initialization,
|
||||
Activation,
|
||||
UnlockMessage,
|
||||
OutOfStamina
|
||||
= Value
|
||||
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uintL(3))
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the state of the implant.
|
||||
* Spawn messages for certain implant-related events.<br>
|
||||
* <br>
|
||||
* The implant Second Wind is technically an invalid `ImplantType` for this packet.
|
||||
* This owes to the unique activation trigger for that implant - a near-death experience of ~0HP.
|
||||
* @see `ImplantType`
|
||||
* @param player_guid the player
|
||||
* @param action
|
||||
* 0 : add implant
|
||||
* with status = 0 to 9 (from ImplantType)
|
||||
* 1 : remove implant
|
||||
* seems work with any value in status
|
||||
* 2 : init implant
|
||||
* status : 0 to "uninit"
|
||||
* status : 1 to init
|
||||
* 3 : activate implant
|
||||
* status : 0 to desactivate
|
||||
* status : 1 to activate
|
||||
* 4 : number of implant slots unlocked
|
||||
* status : 0 = no implant slot
|
||||
* status : 1 = first implant slot + "implant message"
|
||||
* status : 2 or 3 = unlock second & third slots
|
||||
* 5 : out of stamina message
|
||||
* status : 0 to stop the lock
|
||||
* status : 1 to active the lock
|
||||
* @param action how to affect the implant or the slot
|
||||
* @param implantSlot : from 0 to 2
|
||||
* @param status : see action
|
||||
* @param status : a value that depends on context from `ImplantAction`:<br>
|
||||
* `Add` - 0-9 depending on the `ImplantType`<br>
|
||||
* `Remove` - any valid value; field is not significant to this action<br>
|
||||
* `Initialization` - 0 to revoke slot; 1 to allocate implant slot<br>
|
||||
* `Activation` - 0 to deactivate implant; 1 to activate implant<br>
|
||||
* `UnlockMessage` - 0-3 as an unlocked implant slot; display a message<br>
|
||||
* `OutOfStamina` - lock implant; 0 to lock; 1 to unlock; display a message
|
||||
*/
|
||||
final case class AvatarImplantMessage(player_guid : PlanetSideGUID,
|
||||
action : Int,
|
||||
action : ImplantAction.Value,
|
||||
implantSlot : Int,
|
||||
status : Int)
|
||||
extends PlanetSideGamePacket {
|
||||
|
|
@ -46,7 +54,7 @@ final case class AvatarImplantMessage(player_guid : PlanetSideGUID,
|
|||
object AvatarImplantMessage extends Marshallable[AvatarImplantMessage] {
|
||||
implicit val codec : Codec[AvatarImplantMessage] = (
|
||||
("player_guid" | PlanetSideGUID.codec) ::
|
||||
("action" | uintL(3)) ::
|
||||
("action" | ImplantAction.codec) ::
|
||||
("implantSlot" | uint2L) ::
|
||||
("status" | uint4L)
|
||||
).as[AvatarImplantMessage]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
*/
|
||||
final case class AvatarSearchCriteriaMessage(unk1 : PlanetSideGUID,
|
||||
unk2 : List[Int])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = AvatarSearchCriteriaMessage
|
||||
def opcode = GamePacketOpcode.AvatarSearchCriteriaMessage
|
||||
def encode = AvatarSearchCriteriaMessage.encode(this)
|
||||
}
|
||||
|
||||
object AvatarSearchCriteriaMessage extends Marshallable[AvatarSearchCriteriaMessage] {
|
||||
implicit val codec : Codec[AvatarSearchCriteriaMessage] = (
|
||||
("unk1" | PlanetSideGUID.codec) ::
|
||||
("unk2" | PacketHelpers.listOfNSized(6, uint8L))
|
||||
).exmap[AvatarSearchCriteriaMessage] (
|
||||
{
|
||||
case a :: b :: HNil =>
|
||||
Attempt.Successful(AvatarSearchCriteriaMessage(a, b))
|
||||
},
|
||||
{
|
||||
case AvatarSearchCriteriaMessage(a, b) =>
|
||||
if(b.length != 6) {
|
||||
Attempt.Failure(Err("list must have 6 entries"))
|
||||
}
|
||||
else if(b.count(i => { i < 0 || i > 255 }) > 0) {
|
||||
Attempt.Failure(Err("list entries must be 0-255 inclusive"))
|
||||
}
|
||||
else {
|
||||
Attempt.Successful(a :: b :: HNil)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
import scala.annotation.switch
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
* @param unk3 na
|
||||
*/
|
||||
final case class Statistics(unk1 : Option[Int],
|
||||
unk2 : Option[Int],
|
||||
unk3 : List[Long])
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param unk na
|
||||
* @param stats na
|
||||
*/
|
||||
final case class AvatarStatisticsMessage(unk : Int,
|
||||
stats : Statistics)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = AvatarStatisticsMessage
|
||||
def opcode = GamePacketOpcode.AvatarStatisticsMessage
|
||||
def encode = AvatarStatisticsMessage.encode(this)
|
||||
}
|
||||
|
||||
object AvatarStatisticsMessage extends Marshallable[AvatarStatisticsMessage] {
|
||||
/**
|
||||
* na
|
||||
*/
|
||||
private val longCodec : Codec[Statistics] = ulong(32).hlist.exmap (
|
||||
{
|
||||
case n :: HNil =>
|
||||
Attempt.Successful(Statistics(None,None, List(n)))
|
||||
},
|
||||
{
|
||||
case Statistics(_, _, Nil) =>
|
||||
Attempt.Failure(Err("missing value (32-bit)"))
|
||||
|
||||
case Statistics(_, _, n) =>
|
||||
Attempt.Successful(n.head :: HNil)
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* na
|
||||
*/
|
||||
private val complexCodec : Codec[Statistics] = (
|
||||
uint(5) ::
|
||||
uintL(11) ::
|
||||
PacketHelpers.listOfNSized(8, uint32L)
|
||||
).exmap[Statistics] (
|
||||
{
|
||||
case a :: b :: c :: HNil =>
|
||||
Attempt.Successful(Statistics(Some(a), Some(b), c))
|
||||
},
|
||||
{
|
||||
case Statistics(None, _, _) =>
|
||||
Attempt.Failure(Err("missing value (5-bit)"))
|
||||
|
||||
case Statistics(_, None, _) =>
|
||||
Attempt.Failure(Err("missing value (11-bit)"))
|
||||
|
||||
case Statistics(a, b, c) =>
|
||||
if(c.length != 8) {
|
||||
Attempt.Failure(Err("list must have 8 entries"))
|
||||
}
|
||||
else {
|
||||
Attempt.Successful(a.get :: b.get :: c :: HNil)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param n na
|
||||
* @return na
|
||||
*/
|
||||
private def selectCodec(n : Int) : Codec[Statistics] = (n : @switch) match {
|
||||
case 2 | 3 =>
|
||||
longCodec
|
||||
case _ =>
|
||||
complexCodec
|
||||
}
|
||||
|
||||
implicit val codec : Codec[AvatarStatisticsMessage] = (
|
||||
("unk" | uint(3)) >>:~ { unk =>
|
||||
("stats" | selectCodec(unk)).hlist
|
||||
}).as[AvatarStatisticsMessage]
|
||||
}
|
||||
|
||||
object Statistics {
|
||||
def apply(unk : Long) : Statistics =
|
||||
Statistics(None, None, List(unk))
|
||||
|
||||
def apply(unk1 : Int, unk2 : Int, unk3 : List[Long]) : Statistics =
|
||||
Statistics(Some(unk1), Some(unk2), unk3)
|
||||
}
|
||||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ final case class Additional3(unk1 : Boolean,
|
|||
* 064 - Health Module<br>
|
||||
* 128 - Pain Module<br>
|
||||
* `
|
||||
* @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) ::
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import scodec.codecs._
|
|||
* This generates the event message "The [empire] have captured [continent]."
|
||||
* If the continent_guid is not a valid zone, no message is displayed.
|
||||
* If empire is not a valid empire, or refers to the neutral or Black Ops forces, no message is displayed.
|
||||
* @param continent_guid identifies the zone (continent)
|
||||
* @param continent_id identifies the zone (continent)
|
||||
* @param empire identifies the empire
|
||||
*/
|
||||
final case class ContinentalLockUpdateMessage(continent_guid : PlanetSideGUID,
|
||||
final case class ContinentalLockUpdateMessage(continent_id : Int,
|
||||
empire : PlanetSideEmpire.Value)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = ContinentalLockUpdateMessage
|
||||
|
|
@ -25,7 +25,7 @@ final case class ContinentalLockUpdateMessage(continent_guid : PlanetSideGUID,
|
|||
|
||||
object ContinentalLockUpdateMessage extends Marshallable[ContinentalLockUpdateMessage] {
|
||||
implicit val codec : Codec[ContinentalLockUpdateMessage] = (
|
||||
("continent_guid" | PlanetSideGUID.codec) ::
|
||||
("continent_id" | uint16L) ::
|
||||
("empire" | PlanetSideEmpire.codec)
|
||||
).as[ContinentalLockUpdateMessage]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2016 PSForever.net to present
|
||||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param zone_id the continent
|
||||
* @param building_id the building
|
||||
* @param density na
|
||||
*/
|
||||
final case class DensityLevelUpdateMessage(zone_id : Int,
|
||||
building_id : Int,
|
||||
density : List[Int])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = DensityLevelUpdateMessage
|
||||
def opcode = GamePacketOpcode.DensityLevelUpdateMessage
|
||||
def encode = DensityLevelUpdateMessage.encode(this)
|
||||
}
|
||||
|
||||
object DensityLevelUpdateMessage extends Marshallable[DensityLevelUpdateMessage] {
|
||||
implicit val codec : Codec[DensityLevelUpdateMessage] = (
|
||||
("zone_id" | uint16L) ::
|
||||
("building_id" | uint16L) ::
|
||||
("density" | PacketHelpers.listOfNSized(8, uint(3)))
|
||||
).exmap[DensityLevelUpdateMessage] (
|
||||
{
|
||||
case a :: b :: c :: HNil =>
|
||||
Attempt.Successful(DensityLevelUpdateMessage(a, b, c))
|
||||
},
|
||||
{
|
||||
case DensityLevelUpdateMessage(a, b, c) =>
|
||||
if(c.length != 8) {
|
||||
Attempt.Failure(Err("list must have 8 entries"))
|
||||
}
|
||||
else if(c.count(i => { i < 0 || i > 7 }) > 0) {
|
||||
Attempt.Failure(Err("list entries must be 0-7 inclusive"))
|
||||
}
|
||||
else {
|
||||
Attempt.Successful(a :: b :: c :: HNil)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -6,6 +6,22 @@ import scodec.Codec
|
|||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
object FriendAction extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val
|
||||
InitializeFriendList,
|
||||
AddFriend,
|
||||
RemoveFriend,
|
||||
UpdateFriend,
|
||||
InitializeIgnoreList,
|
||||
AddIgnoredPlayer,
|
||||
RemoveIgnoredPlayer
|
||||
= Value
|
||||
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry in the list of players known to and tracked by this player.
|
||||
* They're called "friends" even though they can be used for a list of ignored players as well.
|
||||
|
|
@ -37,7 +53,7 @@ final case class Friend(name : String,
|
|||
* @param unk3 na; always `true`?
|
||||
* @param friends a list of `Friend`s
|
||||
*/
|
||||
final case class FriendsResponse(action : Int,
|
||||
final case class FriendsResponse(action : FriendAction.Value,
|
||||
unk1 : Int,
|
||||
unk2 : Boolean,
|
||||
unk3 : Boolean,
|
||||
|
|
@ -66,7 +82,7 @@ object Friend extends Marshallable[Friend] {
|
|||
|
||||
object FriendsResponse extends Marshallable[FriendsResponse] {
|
||||
implicit val codec : Codec[FriendsResponse] = (
|
||||
("action" | uintL(3)) ::
|
||||
("action" | FriendAction.codec) ::
|
||||
("unk1" | uint4L) ::
|
||||
("unk2" | bool) ::
|
||||
("unk3" | bool) ::
|
||||
|
|
@ -76,8 +92,8 @@ object FriendsResponse extends Marshallable[FriendsResponse] {
|
|||
})
|
||||
).xmap[FriendsResponse] (
|
||||
{
|
||||
case act :: u1 :: u2 :: u3 :: num :: friend1 :: friends :: HNil =>
|
||||
val friendList : List[Friend] = if(friend1.isDefined) { friend1.get :: friends } else { friends }
|
||||
case act :: u1 :: u2 :: u3 :: _ :: friend1 :: friends :: HNil =>
|
||||
val friendList : List[Friend] = if(friend1.isDefined) { friend1.get +: friends } else { friends }
|
||||
FriendsResponse(act, u1, u2, u3, friendList)
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ object HackState extends Enumeration {
|
|||
* Upon the hack's completion, the target on the client will automatically revert back to its original state, if possible.
|
||||
* (It will still be necessary to alert this change from the server's perspective.)
|
||||
* @param unk1 na;
|
||||
* 0 commonly;
|
||||
* 3 for building objects during login phase;
|
||||
* hack type?
|
||||
* @param target_guid the target of the hack
|
||||
* @param player_guid the player
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ final case class HotSpotInfo(x : Float,
|
|||
* <br>
|
||||
* Exploration:<br>
|
||||
* What does (zone) priority entail?
|
||||
* @param continent_guid the zone (continent)
|
||||
* @param continent_id the zone
|
||||
* @param priority na
|
||||
* @param spots a List of HotSpotInfo
|
||||
*/
|
||||
final case class HotSpotUpdateMessage(continent_guid : PlanetSideGUID,
|
||||
final case class HotSpotUpdateMessage(continent_id : Int,
|
||||
priority : Int,
|
||||
spots : List[HotSpotInfo] = Nil)
|
||||
spots : List[HotSpotInfo])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = HotSpotUpdateMessage
|
||||
def opcode = GamePacketOpcode.HotSpotUpdateMessage
|
||||
|
|
@ -47,9 +47,9 @@ final case class HotSpotUpdateMessage(continent_guid : PlanetSideGUID,
|
|||
|
||||
object HotSpotInfo extends Marshallable[HotSpotInfo] {
|
||||
/*
|
||||
the scale is technically not "correct"
|
||||
the client is looking for a normal 0-8192 value
|
||||
we are trying to enforce a more modest graphic scale at 128.0f
|
||||
the client is looking for a normal 0-8192 value where default is 1.0f
|
||||
we try to enforce a more modest graphic scale where default is 64.0f (arbitrary)
|
||||
personally, I'd like scale to equal the sprite width in map units but the pulsation makes it hard to apply
|
||||
*/
|
||||
implicit val codec : Codec[HotSpotInfo] = {
|
||||
("x" | newcodecs.q_float(0.0, 8192.0, 20)) ::
|
||||
|
|
@ -60,8 +60,8 @@ object HotSpotInfo extends Marshallable[HotSpotInfo] {
|
|||
|
||||
object HotSpotUpdateMessage extends Marshallable[HotSpotUpdateMessage] {
|
||||
implicit val codec : Codec[HotSpotUpdateMessage] = (
|
||||
("continent_guid" | PlanetSideGUID.codec) ::
|
||||
("continent_guid" | uint16L) ::
|
||||
("priority" | uint4L) ::
|
||||
("spots" | PacketHelpers.listOfNAligned(longL(8), 4, HotSpotInfo.codec))
|
||||
("spots" | PacketHelpers.listOfNAligned(longL(12), 0, HotSpotInfo.codec))
|
||||
).as[HotSpotUpdateMessage]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import scodec.{Attempt, Codec}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* An `Enumeration` of the valid chat channels.
|
||||
*/
|
||||
object ChatChannel extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val
|
||||
Unknown,
|
||||
Tells,
|
||||
Local,
|
||||
Squad,
|
||||
Outfit,
|
||||
Command,
|
||||
Platoon,
|
||||
Broadcast,
|
||||
SquadLeader
|
||||
= Value
|
||||
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(7))
|
||||
}
|
||||
|
||||
/**
|
||||
* Which comm. channels are allowed to display in the main chat window.
|
||||
* The server sends a `SetChatFilterMessage` and the client responds with the same during login.<br>
|
||||
* <br>
|
||||
* Nine channels exist.
|
||||
* Their values can be modified by radio buttons found under the current chat window's "Options" pane.
|
||||
* Each time the client updates the channel permissions, it sends this packet to the server nine times.
|
||||
* The packet starts with the previous channel filter states and then updates each channel sequentially.<br>
|
||||
* <br>
|
||||
* The `send_channel` and the `channel_filter` values are in the following order:<br>
|
||||
* Unknown, Tells, Local, Squad, Outfit, Command, Platoon, Broadcast, Squad Leader<br>
|
||||
* The first channel is unlisted.
|
||||
* @param send_channel automatically select the fully qualified channel to which the user sends messages
|
||||
* @param origin where this packet was dispatched;
|
||||
* `true`, from the server; `false`, from the client
|
||||
* @param whitelist each channel permitted to post its messages;
|
||||
* when evaluated from a packet, always in original order
|
||||
*/
|
||||
final case class SetChatFilterMessage(send_channel : ChatChannel.Value,
|
||||
origin : Boolean,
|
||||
whitelist : List[ChatChannel.Value])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = SetChatFilterMessage
|
||||
def opcode = GamePacketOpcode.SetChatFilterMessage
|
||||
def encode = SetChatFilterMessage.encode(this)
|
||||
}
|
||||
|
||||
object SetChatFilterMessage extends Marshallable[SetChatFilterMessage] {
|
||||
/**
|
||||
* Transform a `List` of `Boolean` values into a `List` of `ChatChannel` values.
|
||||
* @param filters the boolean values representing ordered channel filters
|
||||
* @return the names of the channels permitted
|
||||
*/
|
||||
private def stateArrayToChannelFilters(filters : List[Boolean]) : List[ChatChannel.Value] = {
|
||||
(0 until 9)
|
||||
.filter(channel => { filters(channel) })
|
||||
.map(channel => ChatChannel(channel))
|
||||
.toList
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a `List` of `ChatChannel` values into a `List` of `Boolean` values.
|
||||
* @param filters the names of the channels permitted
|
||||
* @return the boolean values representing ordered channel filters
|
||||
*/
|
||||
private def channelFiltersToStateArray(filters : List[ChatChannel.Value]) : List[Boolean] = {
|
||||
import scala.collection.mutable.ListBuffer
|
||||
val list = ListBuffer.fill(9)(false)
|
||||
filters.foreach(channel => { list(channel.id) = true })
|
||||
list.toList
|
||||
}
|
||||
|
||||
implicit val codec : Codec[SetChatFilterMessage] = (
|
||||
("send_channel" | ChatChannel.codec) ::
|
||||
("origin" | bool) ::
|
||||
("whitelist" | PacketHelpers.listOfNSized(9, bool))
|
||||
).exmap[SetChatFilterMessage] (
|
||||
{
|
||||
case a :: b :: c :: HNil =>
|
||||
Attempt.Successful(SetChatFilterMessage(a, b, stateArrayToChannelFilters(c)))
|
||||
},
|
||||
{
|
||||
case SetChatFilterMessage(a, b, c) =>
|
||||
Attempt.Successful(a :: b :: channelFiltersToStateArray(c) :: HNil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ import scodec.codecs._
|
|||
* @param zone the zone
|
||||
* @param unk na
|
||||
*/
|
||||
final case class ZoneForcedCavernConnectionsMessage(zone : PlanetSideGUID,
|
||||
final case class ZoneForcedCavernConnectionsMessage(zone : Int,
|
||||
unk : Int)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = ZoneForcedCavernConnectionsMessage
|
||||
|
|
@ -20,7 +20,7 @@ final case class ZoneForcedCavernConnectionsMessage(zone : PlanetSideGUID,
|
|||
|
||||
object ZoneForcedCavernConnectionsMessage extends Marshallable[ZoneForcedCavernConnectionsMessage] {
|
||||
implicit val codec : Codec[ZoneForcedCavernConnectionsMessage] = (
|
||||
("zone" | PlanetSideGUID.codec) ::
|
||||
("zone" | uint16L) ::
|
||||
("unk" | uint2L)
|
||||
).as[ZoneForcedCavernConnectionsMessage]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import scodec.codecs._
|
|||
* @param unk na;
|
||||
* usually `true`
|
||||
*/
|
||||
final case class ZoneLockInfoMessage(zone : PlanetSideGUID,
|
||||
final case class ZoneLockInfoMessage(zone : Int,
|
||||
lock_status : Boolean,
|
||||
unk : Boolean)
|
||||
extends PlanetSideGamePacket {
|
||||
|
|
@ -25,7 +25,7 @@ final case class ZoneLockInfoMessage(zone : PlanetSideGUID,
|
|||
|
||||
object ZoneLockInfoMessage extends Marshallable[ZoneLockInfoMessage] {
|
||||
implicit val codec : Codec[ZoneLockInfoMessage] = (
|
||||
("zone" | PlanetSideGUID.codec) ::
|
||||
("zone" | uint16L) ::
|
||||
("lock_status" | bool) ::
|
||||
("unk" | bool)
|
||||
).as[ZoneLockInfoMessage]
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import scodec.codecs._
|
|||
* Sanctuary zones possess strange queue values that are occasionally zero'd.
|
||||
* They do not have a lock icon and may not limit populations the same way as normal zones.
|
||||
*
|
||||
* @param continent_guid identifies the zone (continent)
|
||||
* @param zone_id the continent
|
||||
* @param zone_queue the maximum population of all three (four) empires that can join this zone
|
||||
* @param tr_queue the maximum number of TR players that can join this zone
|
||||
* @param tr_pop the current TR population in this zone
|
||||
|
|
@ -43,7 +43,7 @@ import scodec.codecs._
|
|||
* @param bo_queue the maximum number of Black OPs players that can join this zone
|
||||
* @param bo_pop the current Black OPs population in this zone
|
||||
*/
|
||||
final case class ZonePopulationUpdateMessage(continent_guid : PlanetSideGUID,
|
||||
final case class ZonePopulationUpdateMessage(zone_id : Int,
|
||||
zone_queue : Long,
|
||||
tr_queue : Long,
|
||||
tr_pop : Long,
|
||||
|
|
@ -61,7 +61,7 @@ final case class ZonePopulationUpdateMessage(continent_guid : PlanetSideGUID,
|
|||
|
||||
object ZonePopulationUpdateMessage extends Marshallable[ZonePopulationUpdateMessage] {
|
||||
implicit val codec : Codec[ZonePopulationUpdateMessage] = (
|
||||
("continent_guid" | PlanetSideGUID.codec) ::
|
||||
("zone_id" | uint16L) ::
|
||||
("zone_queue" | uint32L) ::
|
||||
("tr_queue" | uint32L) :: ("tr_pop" | uint32L) ::
|
||||
("nc_queue" | uint32L) :: ("nc_pop" | uint32L) ::
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package game
|
|||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.ImplantType
|
||||
import scodec.bits._
|
||||
|
||||
class AvatarImplantMessageTest extends Specification {
|
||||
|
|
@ -14,7 +13,7 @@ class AvatarImplantMessageTest extends Specification {
|
|||
PacketCoding.DecodePacket(string).require match {
|
||||
case AvatarImplantMessage(player_guid, unk1, unk2, implant) =>
|
||||
player_guid mustEqual PlanetSideGUID(3171)
|
||||
unk1 mustEqual 3
|
||||
unk1 mustEqual ImplantAction.Activation
|
||||
unk2 mustEqual 1
|
||||
implant mustEqual 1
|
||||
case _ =>
|
||||
|
|
@ -23,7 +22,7 @@ class AvatarImplantMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = AvatarImplantMessage(PlanetSideGUID(3171), 3, 1, 1)
|
||||
val msg = AvatarImplantMessage(PlanetSideGUID(3171), ImplantAction.Activation, 1, 1)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import scodec.bits._
|
||||
|
||||
class AvatarSearchCriteriaMessageTest extends Specification {
|
||||
val string = hex"64 C604 00 00 00 00 00 00"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case AvatarSearchCriteriaMessage(unk1, unk2) =>
|
||||
unk1 mustEqual PlanetSideGUID(1222)
|
||||
unk2.length mustEqual 6
|
||||
unk2.head mustEqual 0
|
||||
unk2(1) mustEqual 0
|
||||
unk2(2) mustEqual 0
|
||||
unk2(3) mustEqual 0
|
||||
unk2(4) mustEqual 0
|
||||
unk2(5) mustEqual 0
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = AvatarSearchCriteriaMessage(PlanetSideGUID(1222), List(0, 0, 0, 0, 0, 0))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
|
||||
"encode (failure; wrong number of list entries)" in {
|
||||
val msg = AvatarSearchCriteriaMessage(PlanetSideGUID(1222), List(0))
|
||||
PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
|
||||
}
|
||||
|
||||
"encode (failure; list number too big)" in {
|
||||
val msg = AvatarSearchCriteriaMessage(PlanetSideGUID(1222), List(0, 0, 0, 0, 0, 256))
|
||||
PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
|
||||
}
|
||||
|
||||
"encode (failure; list number too small)" in {
|
||||
val msg = AvatarSearchCriteriaMessage(PlanetSideGUID(1222), List(0, 0, 0, -1, 0, 0))
|
||||
PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
|
||||
}
|
||||
}
|
||||
79
common/src/test/scala/game/AvatarStatisticsMessageTest.scala
Normal file
79
common/src/test/scala/game/AvatarStatisticsMessageTest.scala
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import scodec.bits._
|
||||
|
||||
class AvatarStatisticsMessageTest extends Specification {
|
||||
val string_long = hex"7F 4 00000000 0"
|
||||
val string_complex = hex"7F 01 3C 40 20 00 00 00 C0 00 00 00 00 00 00 00 20 00 00 00 20 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00"
|
||||
|
||||
"decode (long)" in {
|
||||
PacketCoding.DecodePacket(string_long).require match {
|
||||
case AvatarStatisticsMessage(unk, stats) =>
|
||||
unk mustEqual 2
|
||||
stats.unk1 mustEqual None
|
||||
stats.unk2 mustEqual None
|
||||
stats.unk3.length mustEqual 1
|
||||
stats.unk3.head mustEqual 0
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"decode (complex)" in {
|
||||
PacketCoding.DecodePacket(string_complex).require match {
|
||||
case AvatarStatisticsMessage(unk, stats) =>
|
||||
unk mustEqual 0
|
||||
stats.unk1 mustEqual Some(1)
|
||||
stats.unk2 mustEqual Some(572)
|
||||
stats.unk3.length mustEqual 8
|
||||
stats.unk3.head mustEqual 1
|
||||
stats.unk3(1) mustEqual 6
|
||||
stats.unk3(2) mustEqual 0
|
||||
stats.unk3(3) mustEqual 1
|
||||
stats.unk3(4) mustEqual 1
|
||||
stats.unk3(5) mustEqual 2
|
||||
stats.unk3(6) mustEqual 0
|
||||
stats.unk3(7) mustEqual 0
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode (long)" in {
|
||||
val msg = AvatarStatisticsMessage(2, Statistics(0L))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_long
|
||||
}
|
||||
|
||||
"encode (complex)" in {
|
||||
val msg = AvatarStatisticsMessage(0, Statistics(1, 572, List[Long](1,6,0,1,1,2,0,0)))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_complex
|
||||
}
|
||||
|
||||
"encode (failure; long; missing value)" in {
|
||||
val msg = AvatarStatisticsMessage(0, Statistics(None, None,List(0L)))
|
||||
PacketCoding.EncodePacket(msg).isFailure mustEqual true
|
||||
}
|
||||
|
||||
"encode (failure; complex; missing value (5-bit))" in {
|
||||
val msg = AvatarStatisticsMessage(0, Statistics(None, Some(572), List[Long](1,6,0,1,1,2,0,0)))
|
||||
PacketCoding.EncodePacket(msg).isFailure mustEqual true
|
||||
}
|
||||
|
||||
"encode (failure; complex; missing value (11-bit))" in {
|
||||
val msg = AvatarStatisticsMessage(0, Statistics(Some(1), None, List[Long](1,6,0,1,1,2,0,0)))
|
||||
PacketCoding.EncodePacket(msg).isFailure mustEqual true
|
||||
}
|
||||
|
||||
"encode (failure; complex; wrong number of list entries)" in {
|
||||
val msg = AvatarStatisticsMessage(0, Statistics(Some(1), None, List[Long](1,6,0,1)))
|
||||
PacketCoding.EncodePacket(msg).isFailure mustEqual true
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class ContinentalLockUpdateMessageTest extends Specification {
|
|||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case ContinentalLockUpdateMessage(continent_guid, empire) =>
|
||||
continent_guid mustEqual PlanetSideGUID(22)
|
||||
continent_guid mustEqual 22
|
||||
empire mustEqual PlanetSideEmpire.NC
|
||||
case _ =>
|
||||
ko
|
||||
|
|
@ -21,7 +21,7 @@ class ContinentalLockUpdateMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = ContinentalLockUpdateMessage(PlanetSideGUID(22), PlanetSideEmpire.NC)
|
||||
val msg = ContinentalLockUpdateMessage(22, PlanetSideEmpire.NC)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import scodec.bits._
|
||||
|
||||
class DensityLevelUpdateMessageTest extends Specification {
|
||||
val string = hex"cd 0100 1f4e 000000"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case DensityLevelUpdateMessage(zone_id, building_id, unk) =>
|
||||
zone_id mustEqual 1
|
||||
building_id mustEqual 19999
|
||||
unk.length mustEqual 8
|
||||
unk.head mustEqual 0
|
||||
unk(1) mustEqual 0
|
||||
unk(2) mustEqual 0
|
||||
unk(3) mustEqual 0
|
||||
unk(4) mustEqual 0
|
||||
unk(5) mustEqual 0
|
||||
unk(6) mustEqual 0
|
||||
unk(7) mustEqual 0
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = DensityLevelUpdateMessage(1, 19999, List(0,0, 0,0, 0,0, 0,0))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
|
||||
"encode (failure; wrong number of list entries)" in {
|
||||
val msg = DensityLevelUpdateMessage(1, 19999, List(0))
|
||||
PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
|
||||
}
|
||||
|
||||
"encode (failure; list number too big)" in {
|
||||
val msg = DensityLevelUpdateMessage(1, 19999, List(0,0, 0,0, 0,0, 0,8))
|
||||
PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
|
||||
}
|
||||
|
||||
"encode (failure; list number too small)" in {
|
||||
val msg = DensityLevelUpdateMessage(1, 19999, List(0,0, 0,0, 0,-1, 0,0))
|
||||
PacketCoding.EncodePacket(msg).isSuccessful mustEqual false
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ class FriendsResponseTest extends Specification {
|
|||
"decode (one friend)" in {
|
||||
PacketCoding.DecodePacket(stringOneFriend).require match {
|
||||
case FriendsResponse(action, unk2, unk3, unk4, list) =>
|
||||
action mustEqual 3
|
||||
action mustEqual FriendAction.UpdateFriend
|
||||
unk2 mustEqual 0
|
||||
unk3 mustEqual true
|
||||
unk4 mustEqual true
|
||||
|
|
@ -29,7 +29,7 @@ class FriendsResponseTest extends Specification {
|
|||
"decode (multiple friends)" in {
|
||||
PacketCoding.DecodePacket(stringManyFriends).require match {
|
||||
case FriendsResponse(action, unk2, unk3, unk4, list) =>
|
||||
action mustEqual 0
|
||||
action mustEqual FriendAction.InitializeFriendList
|
||||
unk2 mustEqual 0
|
||||
unk3 mustEqual true
|
||||
unk4 mustEqual true
|
||||
|
|
@ -52,7 +52,7 @@ class FriendsResponseTest extends Specification {
|
|||
"decode (short)" in {
|
||||
PacketCoding.DecodePacket(stringShort).require match {
|
||||
case FriendsResponse(action, unk2, unk3, unk4, list) =>
|
||||
action mustEqual 4
|
||||
action mustEqual FriendAction.InitializeIgnoreList
|
||||
unk2 mustEqual 0
|
||||
unk3 mustEqual true
|
||||
unk4 mustEqual true
|
||||
|
|
@ -63,7 +63,7 @@ class FriendsResponseTest extends Specification {
|
|||
}
|
||||
|
||||
"encode (one friend)" in {
|
||||
val msg = FriendsResponse(3, 0, true, true,
|
||||
val msg = FriendsResponse(FriendAction.UpdateFriend, 0, true, true,
|
||||
Friend("KurtHectic-G", false) ::
|
||||
Nil)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
|
@ -72,7 +72,7 @@ class FriendsResponseTest extends Specification {
|
|||
}
|
||||
|
||||
"encode (multiple friends)" in {
|
||||
val msg = FriendsResponse(0, 0, true, true,
|
||||
val msg = FriendsResponse(FriendAction.InitializeFriendList, 0, true, true,
|
||||
Friend("Angello-W", false) ::
|
||||
Friend("thephattphrogg", false) ::
|
||||
Friend("Kimpossible12", false) ::
|
||||
|
|
@ -85,7 +85,7 @@ class FriendsResponseTest extends Specification {
|
|||
}
|
||||
|
||||
"encode (short)" in {
|
||||
val msg = FriendsResponse(4, 0, true, true)
|
||||
val msg = FriendsResponse(FriendAction.InitializeIgnoreList, 0, true, true)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual stringShort
|
||||
|
|
|
|||
|
|
@ -4,17 +4,19 @@ package game
|
|||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.Vector3
|
||||
import scodec.bits._
|
||||
|
||||
class HotSpotUpdateMessageTest extends Specification {
|
||||
val stringClear = hex"9F 0500 1 00 0"
|
||||
val stringOne = hex"9F 0500 1 01 0 00 2E9 00 145 80000 0"
|
||||
val stringTwo = hex"9F 0500 5 02 0 00 D07 00 8CA 80000 00 BEA 00 4C4 80000"
|
||||
val stringClear = hex"9F 0500 1 000"
|
||||
val stringOne = hex"9F 0500 1 010 002E9 00145 80000 0"
|
||||
val stringTwo = hex"9F 0500 5 020 00D07 008CA 80000 00BEA 004C4 80000"
|
||||
val stringThree = hex"9F 0A00 4 030 00FC8 00F0A 80000 002E9 00BEA 80000 00FC8 00BEA 80000 0"
|
||||
|
||||
"decode (clear)" in {
|
||||
PacketCoding.DecodePacket(stringClear).require match {
|
||||
case HotSpotUpdateMessage(continent_guid, unk, spots) =>
|
||||
continent_guid mustEqual PlanetSideGUID(5)
|
||||
case HotSpotUpdateMessage(continent_id, unk, spots) =>
|
||||
continent_id mustEqual 5
|
||||
unk mustEqual 1
|
||||
spots.size mustEqual 0
|
||||
case _ =>
|
||||
|
|
@ -24,13 +26,11 @@ class HotSpotUpdateMessageTest extends Specification {
|
|||
|
||||
"decode (one)" in {
|
||||
PacketCoding.DecodePacket(stringOne).require match {
|
||||
case HotSpotUpdateMessage(continent_guid, unk, spots) =>
|
||||
continent_guid mustEqual PlanetSideGUID(5)
|
||||
case HotSpotUpdateMessage(continent_id, unk, spots) =>
|
||||
continent_id mustEqual 5
|
||||
unk mustEqual 1
|
||||
spots.size mustEqual 1
|
||||
spots.head.x mustEqual 4700.0f
|
||||
spots.head.y mustEqual 2600.0f
|
||||
spots.head.scale mustEqual 64.0f
|
||||
spots.head mustEqual HotSpotInfo(4700.0f, 2600.0f, 64.0f)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -38,36 +38,52 @@ class HotSpotUpdateMessageTest extends Specification {
|
|||
|
||||
"decode (two)" in {
|
||||
PacketCoding.DecodePacket(stringTwo).require match {
|
||||
case HotSpotUpdateMessage(continent_guid, unk, spots) =>
|
||||
continent_guid mustEqual PlanetSideGUID(5)
|
||||
case HotSpotUpdateMessage(continent_id, unk, spots) =>
|
||||
continent_id mustEqual 5
|
||||
unk mustEqual 5
|
||||
spots.size mustEqual 2
|
||||
spots.head.x mustEqual 4000.0f
|
||||
spots.head.y mustEqual 5400.0f
|
||||
spots.head.scale mustEqual 64.0f
|
||||
spots(1).x mustEqual 5500.0f
|
||||
spots(1).y mustEqual 2200.0f
|
||||
spots(1).scale mustEqual 64.0f
|
||||
spots.head mustEqual HotSpotInfo(4000.0f, 5400.0f, 64.0f)
|
||||
spots(1) mustEqual HotSpotInfo(5500.0f, 2200.0f, 64.0f)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"decode (three)" in {
|
||||
PacketCoding.DecodePacket(stringThree).require match {
|
||||
case HotSpotUpdateMessage(continent_id, unk, spots) =>
|
||||
continent_id mustEqual 10
|
||||
unk mustEqual 4
|
||||
spots.size mustEqual 3
|
||||
spots.head mustEqual HotSpotInfo(4600.0f, 5600.0f, 64.0f)
|
||||
spots(1) mustEqual HotSpotInfo(4700.0f, 5500.0f, 64.0f)
|
||||
spots(2) mustEqual HotSpotInfo(4600.0f, 5500.0f, 64.0f)
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode (clear)" in {
|
||||
val msg = HotSpotUpdateMessage(PlanetSideGUID(5),1)
|
||||
val msg = HotSpotUpdateMessage(5,1, Nil)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
pkt mustEqual stringClear
|
||||
}
|
||||
|
||||
"encode (one)" in {
|
||||
val msg = HotSpotUpdateMessage(PlanetSideGUID(5),1, HotSpotInfo(4700.0f, 2600.0f, 64.0f)::Nil)
|
||||
val msg = HotSpotUpdateMessage(5,1, List(HotSpotInfo(4700.0f, 2600.0f, 64.0f)))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
pkt mustEqual stringOne
|
||||
}
|
||||
|
||||
"encode (two)" in {
|
||||
val msg = HotSpotUpdateMessage(PlanetSideGUID(5),5, HotSpotInfo(4000.0f, 5400.0f, 64.0f)::HotSpotInfo(5500.0f, 2200.0f, 64.0f)::Nil)
|
||||
val msg = HotSpotUpdateMessage(5,5, List(HotSpotInfo(4000.0f, 5400.0f, 64.0f), HotSpotInfo(5500.0f, 2200.0f, 64.0f)))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
pkt mustEqual stringTwo
|
||||
}
|
||||
|
||||
"encode (three)" in {
|
||||
val msg = HotSpotUpdateMessage(10,4, List(HotSpotInfo(4600.0f, 5600.0f, 64.0f), HotSpotInfo(4700.0f, 5500.0f, 64.0f), HotSpotInfo(4600.0f, 5500.0f, 64.0f)))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
pkt mustEqual stringThree
|
||||
}
|
||||
}
|
||||
|
|
|
|||
75
common/src/test/scala/game/SetChatFilterMessageTest.scala
Normal file
75
common/src/test/scala/game/SetChatFilterMessageTest.scala
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import scodec.bits._
|
||||
|
||||
class SetChatFilterMessageTest extends Specification {
|
||||
val string = hex"63 05FF80"
|
||||
val string_custom = hex"63 05C180"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case SetChatFilterMessage(send, origin, filters) =>
|
||||
send mustEqual ChatChannel.Local
|
||||
origin mustEqual true
|
||||
filters.length mustEqual 9
|
||||
filters.head mustEqual ChatChannel.Unknown
|
||||
filters(1) mustEqual ChatChannel.Tells
|
||||
filters(2) mustEqual ChatChannel.Local
|
||||
filters(3) mustEqual ChatChannel.Squad
|
||||
filters(4) mustEqual ChatChannel.Outfit
|
||||
filters(5) mustEqual ChatChannel.Command
|
||||
filters(6) mustEqual ChatChannel.Platoon
|
||||
filters(7) mustEqual ChatChannel.Broadcast
|
||||
filters(8) mustEqual ChatChannel.SquadLeader
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"decode (custom)" in {
|
||||
PacketCoding.DecodePacket(string_custom).require match {
|
||||
case SetChatFilterMessage(send, origin, filters) =>
|
||||
send mustEqual ChatChannel.Local
|
||||
origin mustEqual true
|
||||
filters.length mustEqual 4
|
||||
filters.head mustEqual ChatChannel.Unknown
|
||||
filters(1) mustEqual ChatChannel.Tells
|
||||
filters(2) mustEqual ChatChannel.Broadcast
|
||||
filters(3) mustEqual ChatChannel.SquadLeader
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = SetChatFilterMessage(ChatChannel.Local, true, List(ChatChannel.Unknown, ChatChannel.Tells, ChatChannel.Local, ChatChannel.Squad, ChatChannel.Outfit, ChatChannel.Command, ChatChannel.Platoon, ChatChannel.Broadcast, ChatChannel.SquadLeader))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
|
||||
"encode (success; same channel listed multiple times)" in {
|
||||
val msg = SetChatFilterMessage(ChatChannel.Local, true, List(ChatChannel.Unknown, ChatChannel.Unknown, ChatChannel.Tells, ChatChannel.Tells, ChatChannel.Local, ChatChannel.Squad, ChatChannel.Outfit, ChatChannel.Command, ChatChannel.Platoon, ChatChannel.Broadcast, ChatChannel.SquadLeader))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
|
||||
"encode (success; out of order)" in {
|
||||
val msg = SetChatFilterMessage(ChatChannel.Local, true, List(ChatChannel.Squad, ChatChannel.Outfit, ChatChannel.SquadLeader, ChatChannel.Unknown, ChatChannel.Command, ChatChannel.Platoon, ChatChannel.Broadcast, ChatChannel.Tells, ChatChannel.Local))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
|
||||
"encode (success; custom)" in {
|
||||
val msg = SetChatFilterMessage(ChatChannel.Local, true, List(ChatChannel.Unknown, ChatChannel.Tells, ChatChannel.Broadcast, ChatChannel.SquadLeader))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_custom
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ class ZoneForcedCavernConnectionsMessageTest extends Specification {
|
|||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case ZoneForcedCavernConnectionsMessage(zone, unk) =>
|
||||
zone mustEqual PlanetSideGUID(32)
|
||||
zone mustEqual 32
|
||||
unk mustEqual 1
|
||||
case _ =>
|
||||
ko
|
||||
|
|
@ -21,7 +21,7 @@ class ZoneForcedCavernConnectionsMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = ZoneForcedCavernConnectionsMessage(PlanetSideGUID(32), 1)
|
||||
val msg = ZoneForcedCavernConnectionsMessage(32, 1)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class ZoneLockInfoMesageTest extends Specification {
|
|||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case ZoneLockInfoMessage(zone, locked, unk) =>
|
||||
zone mustEqual PlanetSideGUID(27)
|
||||
zone mustEqual 27
|
||||
locked mustEqual false
|
||||
unk mustEqual true
|
||||
case _ =>
|
||||
|
|
@ -21,7 +21,7 @@ class ZoneLockInfoMesageTest extends Specification {
|
|||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = ZoneLockInfoMessage(PlanetSideGUID(27), false, true)
|
||||
val msg = ZoneLockInfoMessage(27, false, true)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class ZonePopulationUpdateMessageTest extends Specification {
|
|||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case ZonePopulationUpdateMessage(continent_guid, zone_queue, tr_queue, tr_pop, nc_queue, nc_pop, vs_queue, vs_pop, bo_queue, bo_pop) =>
|
||||
continent_guid mustEqual PlanetSideGUID(4)
|
||||
continent_guid mustEqual 4
|
||||
zone_queue mustEqual 414
|
||||
tr_queue mustEqual 138
|
||||
tr_pop mustEqual 37
|
||||
|
|
@ -28,7 +28,7 @@ class ZonePopulationUpdateMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = ZonePopulationUpdateMessage(PlanetSideGUID(4), 414, 138, 37, 138, 37, 138, 37, 138, 37)
|
||||
val msg = ZonePopulationUpdateMessage(4, 414, 138, 37, 138, 37, 138, 37, 138, 37)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,15 +48,27 @@ object Maps {
|
|||
val map12 = new ZoneMap("map12")
|
||||
|
||||
val map13 = new ZoneMap("map13") {
|
||||
LocalObject(ServerObjectBuilder(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180))))
|
||||
LocalObject(ServerObjectBuilder(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180))))
|
||||
LocalObject(ServerObjectBuilder(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0))))
|
||||
LocalObject(ServerObjectBuilder(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0))))
|
||||
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))
|
||||
|
||||
LocalObject(ServerObjectBuilder(520, ImplantTerminalMech.Constructor)) //Hart B
|
||||
LocalObject(ServerObjectBuilder(853, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(855, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(860, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(1081, Terminal.Constructor(implant_terminal_interface))) //tube 520
|
||||
TerminalToInterface(520, 1081)
|
||||
|
||||
LocalBuilding(2, FoundationBuilder(Building.Structure)) //HART building C
|
||||
LocalObject(ServerObjectBuilder(186, Terminal.Constructor(cert_terminal)))
|
||||
LocalObject(ServerObjectBuilder(187, Terminal.Constructor(cert_terminal)))
|
||||
LocalObject(ServerObjectBuilder(188, Terminal.Constructor(cert_terminal)))
|
||||
LocalObject(ServerObjectBuilder(362, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(370, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(371, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(372, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(373, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(374, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(375, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(394, Door.Constructor))
|
||||
|
|
@ -66,7 +78,6 @@ object Maps {
|
|||
LocalObject(ServerObjectBuilder(398, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(462, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(463, Door.Constructor))
|
||||
LocalObject(ServerObjectBuilder(520, ImplantTerminalMech.Constructor)) //Hart B
|
||||
LocalObject(ServerObjectBuilder(522, ImplantTerminalMech.Constructor)) //Hart C
|
||||
LocalObject(ServerObjectBuilder(523, ImplantTerminalMech.Constructor)) //Hart C
|
||||
LocalObject(ServerObjectBuilder(524, ImplantTerminalMech.Constructor)) //Hart C
|
||||
|
|
@ -75,10 +86,6 @@ object Maps {
|
|||
LocalObject(ServerObjectBuilder(527, ImplantTerminalMech.Constructor)) //Hart C
|
||||
LocalObject(ServerObjectBuilder(528, ImplantTerminalMech.Constructor)) //Hart C
|
||||
LocalObject(ServerObjectBuilder(529, ImplantTerminalMech.Constructor)) //Hart C
|
||||
LocalObject(ServerObjectBuilder(556, IFFLock.Constructor))
|
||||
LocalObject(ServerObjectBuilder(557, IFFLock.Constructor))
|
||||
LocalObject(ServerObjectBuilder(558, IFFLock.Constructor))
|
||||
LocalObject(ServerObjectBuilder(559, IFFLock.Constructor))
|
||||
LocalObject(ServerObjectBuilder(686, Locker.Constructor))
|
||||
LocalObject(ServerObjectBuilder(687, Locker.Constructor))
|
||||
LocalObject(ServerObjectBuilder(688, Locker.Constructor))
|
||||
|
|
@ -87,17 +94,10 @@ object Maps {
|
|||
LocalObject(ServerObjectBuilder(691, Locker.Constructor))
|
||||
LocalObject(ServerObjectBuilder(692, Locker.Constructor))
|
||||
LocalObject(ServerObjectBuilder(693, Locker.Constructor))
|
||||
LocalObject(ServerObjectBuilder(186, Terminal.Constructor(cert_terminal)))
|
||||
LocalObject(ServerObjectBuilder(187, Terminal.Constructor(cert_terminal)))
|
||||
LocalObject(ServerObjectBuilder(188, Terminal.Constructor(cert_terminal)))
|
||||
LocalObject(ServerObjectBuilder(842, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(843, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(844, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(845, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(853, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(855, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(860, Terminal.Constructor(order_terminal)))
|
||||
LocalObject(ServerObjectBuilder(1081, Terminal.Constructor(implant_terminal_interface))) //tube 520
|
||||
LocalObject(ServerObjectBuilder(1082, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
|
||||
LocalObject(ServerObjectBuilder(1083, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
|
||||
LocalObject(ServerObjectBuilder(1084, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
|
||||
|
|
@ -106,16 +106,6 @@ object Maps {
|
|||
LocalObject(ServerObjectBuilder(1087, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
|
||||
LocalObject(ServerObjectBuilder(1088, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
|
||||
LocalObject(ServerObjectBuilder(1089, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
|
||||
LocalObject(ServerObjectBuilder(1063, Terminal.Constructor(ground_vehicle_terminal)))
|
||||
LocalObject(ServerObjectBuilder(500,
|
||||
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
|
||||
)) //TODO guid not correct
|
||||
LocalObject(ServerObjectBuilder(304, Terminal.Constructor(dropship_vehicle_terminal)))
|
||||
LocalObject(ServerObjectBuilder(501,
|
||||
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
|
||||
)) //TODO guid not correct
|
||||
|
||||
LocalBuilding(2, FoundationBuilder(Building.Structure))
|
||||
ObjectToBuilding(186, 2)
|
||||
ObjectToBuilding(187, 2)
|
||||
ObjectToBuilding(188, 2)
|
||||
|
|
@ -139,30 +129,14 @@ object Maps {
|
|||
ObjectToBuilding(843, 2)
|
||||
ObjectToBuilding(844, 2)
|
||||
ObjectToBuilding(845, 2)
|
||||
ObjectToBuilding(853, 2) //TODO check building_id
|
||||
ObjectToBuilding(855, 2) //TODO check building_id
|
||||
ObjectToBuilding(860, 2) //TODO check building_id
|
||||
ObjectToBuilding(1063, 2) //TODO unowned courtyard terminal?
|
||||
ObjectToBuilding(500, 2) //TODO unowned courtyard spawnpad?
|
||||
ObjectToBuilding(304, 2) //TODO unowned courtyard terminal?
|
||||
ObjectToBuilding(501, 2) //TODO unowned courtyard spawnpad?
|
||||
|
||||
LocalBuilding(29, FoundationBuilder(Building.Structure))
|
||||
ObjectToBuilding(330, 29)
|
||||
ObjectToBuilding(332, 29)
|
||||
ObjectToBuilding(556, 29)
|
||||
ObjectToBuilding(558, 29)
|
||||
|
||||
//ObjectToBuilding(1081, ?)
|
||||
//ObjectToBuilding(520, ?)
|
||||
|
||||
DoorToLock(330, 558)
|
||||
DoorToLock(331, 559)
|
||||
DoorToLock(332, 556)
|
||||
DoorToLock(333, 557)
|
||||
TerminalToSpawnPad(1063, 500)
|
||||
TerminalToSpawnPad(304, 501)
|
||||
TerminalToInterface(520, 1081)
|
||||
ObjectToBuilding(1082, 2)
|
||||
ObjectToBuilding(1083, 2)
|
||||
ObjectToBuilding(1084, 2)
|
||||
ObjectToBuilding(1085, 2)
|
||||
ObjectToBuilding(1086, 2)
|
||||
ObjectToBuilding(1087, 2)
|
||||
ObjectToBuilding(1088, 2)
|
||||
ObjectToBuilding(1089, 2)
|
||||
TerminalToInterface(522, 1082)
|
||||
TerminalToInterface(523, 1083)
|
||||
TerminalToInterface(524, 1084)
|
||||
|
|
@ -171,6 +145,50 @@ object Maps {
|
|||
TerminalToInterface(527, 1087)
|
||||
TerminalToInterface(528, 1088)
|
||||
TerminalToInterface(529, 1089)
|
||||
|
||||
LocalBuilding(29, FoundationBuilder(Building.Structure)) //South Villa Gun Tower
|
||||
LocalObject(ServerObjectBuilder(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180))))
|
||||
LocalObject(ServerObjectBuilder(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180))))
|
||||
LocalObject(ServerObjectBuilder(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0))))
|
||||
LocalObject(ServerObjectBuilder(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0))))
|
||||
LocalObject(ServerObjectBuilder(556, IFFLock.Constructor))
|
||||
LocalObject(ServerObjectBuilder(557, IFFLock.Constructor))
|
||||
LocalObject(ServerObjectBuilder(558, IFFLock.Constructor))
|
||||
LocalObject(ServerObjectBuilder(559, IFFLock.Constructor))
|
||||
ObjectToBuilding(330, 29)
|
||||
ObjectToBuilding(331, 29)
|
||||
ObjectToBuilding(332, 29)
|
||||
ObjectToBuilding(333, 29)
|
||||
ObjectToBuilding(556, 29)
|
||||
ObjectToBuilding(557, 29)
|
||||
ObjectToBuilding(558, 29)
|
||||
ObjectToBuilding(559, 29)
|
||||
DoorToLock(330, 558)
|
||||
DoorToLock(331, 559)
|
||||
DoorToLock(332, 556)
|
||||
DoorToLock(333, 557)
|
||||
|
||||
LocalBuilding(51, FoundationBuilder(Building.Structure))
|
||||
LocalObject(ServerObjectBuilder(304, Terminal.Constructor(dropship_vehicle_terminal)))
|
||||
LocalObject(ServerObjectBuilder(292,
|
||||
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
|
||||
))
|
||||
ObjectToBuilding(304, 51)
|
||||
ObjectToBuilding(292, 51)
|
||||
TerminalToSpawnPad(304, 292)
|
||||
|
||||
LocalBuilding(77, FoundationBuilder(Building.Structure))
|
||||
LocalObject(ServerObjectBuilder(1063, Terminal.Constructor(ground_vehicle_terminal)))
|
||||
LocalObject(ServerObjectBuilder(706,
|
||||
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
|
||||
))
|
||||
ObjectToBuilding(1063, 77)
|
||||
ObjectToBuilding(706, 77)
|
||||
TerminalToSpawnPad(1063, 706)
|
||||
|
||||
ObjectToBuilding(853, 2) //TODO check building_id
|
||||
ObjectToBuilding(855, 2) //TODO check building_id
|
||||
ObjectToBuilding(860, 2) //TODO check building_id
|
||||
}
|
||||
|
||||
val map14 = new ZoneMap("map13")
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ 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, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState}
|
||||
import net.psforever.objects.zones.{InterstellarCluster, Zone}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types._
|
||||
|
|
@ -798,7 +801,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
|
||||
val slot = slotNumber.get
|
||||
log.info(s"$message - put in slot $slot")
|
||||
sendResponse(AvatarImplantMessage(tplayer.GUID, 0, slot, implant_type.id))
|
||||
sendResponse(AvatarImplantMessage(tplayer.GUID, ImplantAction.Add, slot, implant_type.id))
|
||||
sendResponse(ItemTransactionResultMessage(terminal_guid, TransactionType.Learn, true))
|
||||
}
|
||||
else {
|
||||
|
|
@ -833,7 +836,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
|
||||
val slot = slotNumber.get
|
||||
log.info(s"$tplayer is selling $implant_type - take from slot $slot")
|
||||
sendResponse(AvatarImplantMessage(tplayer.GUID, 1, slot, 0))
|
||||
sendResponse(AvatarImplantMessage(tplayer.GUID, ImplantAction.Remove, slot, 0))
|
||||
sendResponse(ItemTransactionResultMessage(terminal_guid, TransactionType.Sell, true))
|
||||
}
|
||||
else {
|
||||
|
|
@ -976,40 +979,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case VehicleLoaded(_/*vehicle*/) => ;
|
||||
//currently being handled by VehicleSpawnPad.LoadVehicle during testing phase
|
||||
|
||||
case Zone.ClientInitialization(/*initList*/_) =>
|
||||
//TODO iterate over initList; for now, just do this
|
||||
sendResponse(
|
||||
BuildingInfoUpdateMessage(
|
||||
PlanetSideGUID(6), //Ceryshen
|
||||
PlanetSideGUID(2), //Anguta
|
||||
8, //80% NTU
|
||||
false, //Base hacked
|
||||
PlanetSideEmpire.NEUTRAL, //Base hacked by NC
|
||||
0, //10 minutes remaining for hack
|
||||
PlanetSideEmpire.VS, //Base owned by VS
|
||||
0, //!! Field != 0 will cause malformed packet. See class def.
|
||||
None,
|
||||
PlanetSideGeneratorState.Normal, //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(ContinentalLockUpdateMessage(PlanetSideGUID(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
|
||||
sendResponse(ZonePopulationUpdateMessage(PlanetSideGUID(13), 414, 138, 0, 138, 0, 138, 0, 138, 0))
|
||||
case Zone.ClientInitialization(zone) =>
|
||||
val continentNumber = zone.Number
|
||||
val poplist = LivePlayerList.ZonePopulation(continentNumber, _ => true)
|
||||
val popBO = 0 //TODO black ops test (partition)
|
||||
val popTR = poplist.count(_.Faction == PlanetSideEmpire.TR)
|
||||
val popNC = poplist.count(_.Faction == PlanetSideEmpire.NC)
|
||||
val popVS = poplist.count(_.Faction == PlanetSideEmpire.VS)
|
||||
|
||||
zone.Buildings.foreach({ case(id, building) => initBuilding(continentNumber, id, building) })
|
||||
sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
|
||||
sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
|
||||
//CaptureFlagUpdateMessage()
|
||||
//VanuModuleUpdateMessage()
|
||||
//ModuleLimitsMessage()
|
||||
sendResponse(ZoneInfoMessage(continentNumber, true, 0))
|
||||
sendResponse(ZoneLockInfoMessage(continentNumber, false, true))
|
||||
sendResponse(ZoneForcedCavernConnectionsMessage(continentNumber, 0))
|
||||
sendResponse(HotSpotUpdateMessage(continentNumber, 1, Nil)) //normally set in bulk; should be fine doing per continent
|
||||
|
||||
case InterstellarCluster.ClientInitializationComplete(tplayer)=>
|
||||
//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
|
||||
//PropertyOverrideMessage
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1))
|
||||
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
|
||||
sendResponse(FriendsResponse(FriendAction.InitializeFriendList, 0, true, true, Nil))
|
||||
sendResponse(FriendsResponse(FriendAction.InitializeIgnoreList, 0, true, true, Nil))
|
||||
|
||||
//LoadMapMessage will cause the client to send back a BeginZoningMessage packet (see below)
|
||||
sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L))
|
||||
log.info("Load the now-registered player")
|
||||
//load the now-registered player
|
||||
tplayer.Spawn
|
||||
|
|
@ -1023,13 +1020,35 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
val guid = tplayer.GUID
|
||||
LivePlayerList.Assign(continent.Number, sessionId, guid)
|
||||
sendResponse(SetCurrentAvatarMessage(guid,0,0))
|
||||
sendResponse(CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
|
||||
|
||||
(1 to 73).foreach( i => {
|
||||
(0 until DetailedCharacterData.numberOfImplantSlots(tplayer.BEP)).foreach(slot => {
|
||||
sendResponse(AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 1)) //init implant slot
|
||||
sendResponse(AvatarImplantMessage(guid, ImplantAction.Activation, slot, 0)) //deactivate implant
|
||||
//TODO: if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
|
||||
})
|
||||
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
|
||||
//TODO: if Medkit does not have shortcut, add to a free slot or write over slot 64
|
||||
sendResponse(CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))
|
||||
sendResponse(ChangeShortcutBankMessage(guid, 0))
|
||||
//FavoritesMessage
|
||||
sendResponse(SetChatFilterMessage(ChatChannel.Local, false, ChatChannel.values.toList)) //TODO will not always be "on" like this
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Nothing, 0,0, tplayer.Position, 0, true))
|
||||
sendResponse(PlanetsideAttributeMessage(guid, 53, 1))
|
||||
sendResponse(AvatarSearchCriteriaMessage(guid, List(0,0,0,0,0,0)))
|
||||
(1 to 73).foreach(i => {
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(i), 67, 0))
|
||||
})
|
||||
(0 to 30).foreach(i => { //TODO 30 for a new character only?
|
||||
sendResponse(AvatarStatisticsMessage(2, Statistics(0L)))
|
||||
})
|
||||
//AvatarAwardMessage
|
||||
//DisplayAwardMessage
|
||||
//SquadDefinitionActionMessage and SquadDetailDefinitionUpdateMessage
|
||||
//MapObjectStateBlockMessage and ObjectCreateMessage
|
||||
//TacticsMessage
|
||||
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on
|
||||
|
||||
case Zone.ItemFromGround(tplayer, item) =>
|
||||
val obj_guid = item.GUID
|
||||
|
|
@ -1241,19 +1260,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case msg @ BeginZoningMessage() =>
|
||||
log.info("Reticulating splines ...")
|
||||
//map-specific initializations
|
||||
//TODO continent.ClientConfiguration()
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1))
|
||||
|
||||
sendResponse(SetEmpireMessage(PlanetSideGUID(2), PlanetSideEmpire.VS)) //HART building C
|
||||
sendResponse(SetEmpireMessage(PlanetSideGUID(29), PlanetSideEmpire.NC)) //South Villa Gun Tower
|
||||
|
||||
configZone(continent) //todo density
|
||||
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))
|
||||
//custom
|
||||
sendResponse(ContinentalLockUpdateMessage(13, PlanetSideEmpire.VS)) // "The VS have captured the VS Sanctuary."
|
||||
(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(
|
||||
|
|
@ -1408,6 +1421,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) =>
|
||||
log.info(s"SpawnRequestMessage: $msg")
|
||||
|
||||
case msg @ SetChatFilterMessage(send_channel, origin, whitelist) =>
|
||||
log.info("SetChatFilters: " + msg)
|
||||
|
||||
case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) =>
|
||||
// TODO: Prevents log spam, but should be handled correctly
|
||||
if (messagetype != ChatMessageType.CMT_TOGGLE_GM) {
|
||||
|
|
@ -2019,11 +2035,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
log.info("ItemTransaction: " + msg)
|
||||
continent.GUID(terminal_guid) match {
|
||||
case Some(term : Terminal) =>
|
||||
if(player.Faction == term.Faction) {
|
||||
term.Actor ! Terminal.Request(player, msg)
|
||||
}
|
||||
case Some(obj : PlanetSideGameObject) => ;
|
||||
case None => ;
|
||||
log.info(s"ItemTransaction: ${term.Definition.Name} found")
|
||||
term.Actor ! Terminal.Request(player, msg)
|
||||
case Some(obj : PlanetSideGameObject) =>
|
||||
log.error(s"ItemTransaction: $obj is not a terminal")
|
||||
case _ =>
|
||||
log.error(s"ItemTransaction: $terminal_guid does not exist")
|
||||
}
|
||||
|
||||
case msg @ FavoritesRequest(player_guid, unk, action, line, label) =>
|
||||
|
|
@ -3050,6 +3067,111 @@ 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(
|
||||
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,
|
||||
building.Faction,
|
||||
0,
|
||||
None,
|
||||
PlanetSideGeneratorState.Normal,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
0,
|
||||
Nil,
|
||||
0,
|
||||
false,
|
||||
8,
|
||||
None,
|
||||
false,
|
||||
false
|
||||
)
|
||||
)
|
||||
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 = {
|
||||
zone.Buildings.foreach({case (id, building) =>
|
||||
sendResponse(SetEmpireMessage(PlanetSideGUID(id), building.Faction))
|
||||
building.Amenities.foreach(amenity => {
|
||||
val amenityId = amenity.GUID
|
||||
sendResponse(PlanetsideAttributeMessage(amenityId, 50, 0))
|
||||
sendResponse(PlanetsideAttributeMessage(amenityId, 51, 0))
|
||||
})
|
||||
sendResponse(HackMessage(3, PlanetSideGUID(id), PlanetSideGUID(0), 0, 3212836864L, HackState.HackCleared, 8))
|
||||
})
|
||||
}
|
||||
|
||||
def failWithError(error : String) = {
|
||||
log.error(error)
|
||||
sendResponse(ConnectionClose())
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ object Zones {
|
|||
super.Init(context)
|
||||
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
Building(2).get.Faction = PlanetSideEmpire.VS //HART building C
|
||||
Buildings.values.foreach(building => { building.Faction = PlanetSideEmpire.VS })
|
||||
Building(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue