Merge pull request #193 from Fate-JH/server-init

Packets, and a Warp Gate
This commit is contained in:
Fate-JH 2018-03-15 19:44:06 -04:00 committed by GitHub
commit bd76d28564
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1031 additions and 271 deletions

View file

@ -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
}
}

View file

@ -16,7 +16,7 @@ import net.psforever.packet.game.objectcreate.ObjectClass
*/ */
class ImplantTerminalInterfaceDefinition extends TerminalDefinition(ObjectClass.implant_terminal_interface) { class ImplantTerminalInterfaceDefinition extends TerminalDefinition(ObjectClass.implant_terminal_interface) {
Packet = new ImplantTerminalInterfaceConverter Packet = new ImplantTerminalInterfaceConverter
Name = "implante_terminal_interface" Name = "implant_terminal_interface"
private val implants : Map[String, ImplantDefinition] = Map ( private val implants : Map[String, ImplantDefinition] = Map (
"advanced_regen" -> GlobalDefinitions.advanced_regen, "advanced_regen" -> GlobalDefinitions.advanced_regen,

View file

@ -50,9 +50,7 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
} }
case InterstellarCluster.RequestClientInitialization(tplayer) => case InterstellarCluster.RequestClientInitialization(tplayer) =>
zones.foreach(zone => { zones.foreach(zone => { sender ! Zone.ClientInitialization(zone.ClientInitialization()) })
sender ! Zone.ClientInitialization(zone.ClientInitialization()) //do this for each Zone
})
sender ! InterstellarCluster.ClientInitializationComplete(tplayer) //will be processed after all Zones sender ! InterstellarCluster.ClientInitializationComplete(tplayer) //will be processed after all Zones
case _ => ; case _ => ;

View file

@ -10,7 +10,6 @@ import net.psforever.objects.guid.actor.UniqueNumberSystem
import net.psforever.objects.guid.selector.RandomSelector import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.LimitedNumberSource import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.structures.{Amenity, Building}
import net.psforever.packet.GamePacket
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.Vector3 import net.psforever.types.Vector3
@ -218,6 +217,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
def Transport : ActorRef = transport def Transport : ActorRef = transport
def Buildings : Map[Int, Building] = buildings
def Building(id : Int) : Option[Building] = { def Building(id : Int) : Option[Building] = {
buildings.get(id) buildings.get(id)
} }
@ -250,25 +251,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
* - `ZonePopulationUpdateMessage` * - `ZonePopulationUpdateMessage`
* @return a `List` of `GamePacket` messages * @return a `List` of `GamePacket` messages
*/ */
def ClientInitialization() : List[GamePacket] = { def ClientInitialization() : Zone = this
//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]
}
} }
object Zone { object Zone {
@ -308,11 +291,11 @@ object Zone {
/** /**
* Message to report the packet messages that initialize the client. * 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> * @see `Zone.ClientInitialization()`<br>
* `InterstallarCluster` * `InterstallarCluster`
*/ */
final case class ClientInitialization(list : List[GamePacket]) final case class ClientInitialization(zone : Zone)
/** /**
* Overloaded constructor. * Overloaded constructor.

View file

@ -436,8 +436,8 @@ object GamePacketOpcode extends Enumeration {
case 0x60 => game.FavoritesMessage.decode case 0x60 => game.FavoritesMessage.decode
case 0x61 => game.ObjectDetectedMessage.decode case 0x61 => game.ObjectDetectedMessage.decode
case 0x62 => game.SplashHitMessage.decode case 0x62 => game.SplashHitMessage.decode
case 0x63 => noDecoder(SetChatFilterMessage) case 0x63 => game.SetChatFilterMessage.decode
case 0x64 => noDecoder(AvatarSearchCriteriaMessage) case 0x64 => game.AvatarSearchCriteriaMessage.decode
case 0x65 => noDecoder(AvatarSearchResponse) case 0x65 => noDecoder(AvatarSearchResponse)
case 0x66 => game.WeaponJammedMessage.decode case 0x66 => game.WeaponJammedMessage.decode
case 0x67 => noDecoder(LinkDeadAwarenessMsg) case 0x67 => noDecoder(LinkDeadAwarenessMsg)
@ -468,7 +468,7 @@ object GamePacketOpcode extends Enumeration {
case 0x7c => game.DismountBuildingMsg.decode case 0x7c => game.DismountBuildingMsg.decode
case 0x7d => noDecoder(UnknownMessage125) case 0x7d => noDecoder(UnknownMessage125)
case 0x7e => noDecoder(UnknownMessage126) case 0x7e => noDecoder(UnknownMessage126)
case 0x7f => noDecoder(AvatarStatisticsMessage) case 0x7f => game.AvatarStatisticsMessage.decode
// OPCODES 0x80-8f // OPCODES 0x80-8f
case 0x80 => noDecoder(GenericObjectAction2Message) case 0x80 => noDecoder(GenericObjectAction2Message)
@ -561,7 +561,7 @@ object GamePacketOpcode extends Enumeration {
case 0xca => noDecoder(OutfitBenefitMessage) case 0xca => noDecoder(OutfitBenefitMessage)
case 0xcb => noDecoder(EmpireChangeTimeMessage) case 0xcb => noDecoder(EmpireChangeTimeMessage)
case 0xcc => noDecoder(ClockCalibrationMessage) case 0xcc => noDecoder(ClockCalibrationMessage)
case 0xcd => noDecoder(DensityLevelUpdateMessage) case 0xcd => game.DensityLevelUpdateMessage.decode
case 0xce => noDecoder(ActOfGodMessage) case 0xce => noDecoder(ActOfGodMessage)
case 0xcf => noDecoder(AvatarAwardMessage) case 0xcf => noDecoder(AvatarAwardMessage)

View file

@ -1,40 +1,48 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.ImplantType
import scodec.Codec import scodec.Codec
import scodec.codecs._ 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> * <br>
* The implant Second Wind is technically an invalid `ImplantType` for this packet. * 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. * This owes to the unique activation trigger for that implant - a near-death experience of ~0HP.
* @see `ImplantType`
* @param player_guid the player * @param player_guid the player
* @param action * @param action how to affect the implant or the slot
* 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 implantSlot : from 0 to 2 * @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, final case class AvatarImplantMessage(player_guid : PlanetSideGUID,
action : Int, action : ImplantAction.Value,
implantSlot : Int, implantSlot : Int,
status : Int) status : Int)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
@ -46,7 +54,7 @@ final case class AvatarImplantMessage(player_guid : PlanetSideGUID,
object AvatarImplantMessage extends Marshallable[AvatarImplantMessage] { object AvatarImplantMessage extends Marshallable[AvatarImplantMessage] {
implicit val codec : Codec[AvatarImplantMessage] = ( implicit val codec : Codec[AvatarImplantMessage] = (
("player_guid" | PlanetSideGUID.codec) :: ("player_guid" | PlanetSideGUID.codec) ::
("action" | uintL(3)) :: ("action" | ImplantAction.codec) ::
("implantSlot" | uint2L) :: ("implantSlot" | uint2L) ::
("status" | uint4L) ("status" | uint4L)
).as[AvatarImplantMessage] ).as[AvatarImplantMessage]

View file

@ -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)
}
}
)
}

View file

@ -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)
}

View file

@ -17,17 +17,17 @@ import scodec.codecs._
* I believe these `Boolean` values actually indicate some measure of warpgate operation. * I believe these `Boolean` values actually indicate some measure of warpgate operation.
* Geowarps, for example, though their appearance does not change, recieve this packet. * Geowarps, for example, though their appearance does not change, recieve this packet.
* Moreover, they can operate as a receiving-end broadcast gate. * Moreover, they can operate as a receiving-end broadcast gate.
* @param continent_guid identifies the zone (continent) * @param continent_id the zone
* @param building_guid identifies the warpgate (see `BuildingInfoUpdateMessage`) * @param building_id the warp gate (see `BuildingInfoUpdateMessage`)
* @param unk1 na * @param unk1 na
* @param unk2 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, final case class BroadcastWarpgateUpdateMessage(continent_id : Int,
building_guid : PlanetSideGUID, building_id : Int,
unk1 : Boolean, unk1 : Boolean,
unk2 : Boolean, unk2 : Boolean,
is_broadcast : Boolean) broadcast : Boolean)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = BroadcastWarpgateUpdateMessage type Packet = BroadcastWarpgateUpdateMessage
def opcode = GamePacketOpcode.BroadcastWarpgateUpdateMessage def opcode = GamePacketOpcode.BroadcastWarpgateUpdateMessage
@ -36,10 +36,10 @@ final case class BroadcastWarpgateUpdateMessage(continent_guid : PlanetSideGUID,
object BroadcastWarpgateUpdateMessage extends Marshallable[BroadcastWarpgateUpdateMessage] { object BroadcastWarpgateUpdateMessage extends Marshallable[BroadcastWarpgateUpdateMessage] {
implicit val codec : Codec[BroadcastWarpgateUpdateMessage] = ( implicit val codec : Codec[BroadcastWarpgateUpdateMessage] = (
("continent_guid" | PlanetSideGUID.codec) :: ("continent_id" | uint16L) ::
("building_guid" | PlanetSideGUID.codec) :: ("building_id" | uint16L) ::
("unk1" | bool) :: ("unk1" | bool) ::
("unk2" | bool) :: ("unk2" | bool) ::
("is_broadcast" | bool) ("broadcast" | bool)
).as[BroadcastWarpgateUpdateMessage] ).as[BroadcastWarpgateUpdateMessage]
} }

View file

@ -87,8 +87,8 @@ final case class Additional3(unk1 : Boolean,
* 064 - Health Module<br> * 064 - Health Module<br>
* 128 - Pain Module<br> * 128 - Pain Module<br>
* ` * `
* @param continent_guid the continent (zone) * @param continent_id the continent (zone)
* @param building_guid the building * @param building_id the building
* @param ntu_level if the building has a silo, the amount of NTU in that silo; * @param ntu_level if the building has a silo, the amount of NTU in that silo;
* NTU is reported in multiples of 10%; * NTU is reported in multiples of 10%;
* valid for 0 (0%) to 10 (100%) * 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_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 * @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, final case class BuildingInfoUpdateMessage(continent_id : Int,
building_guid : PlanetSideGUID, building_id : Int,
ntu_level : Int, ntu_level : Int,
is_hacked : Boolean, is_hacked : Boolean,
empire_hack : PlanetSideEmpire.Value, empire_hack : PlanetSideEmpire.Value,
@ -171,8 +171,8 @@ object BuildingInfoUpdateMessage extends Marshallable[BuildingInfoUpdateMessage]
).as[Additional3] ).as[Additional3]
implicit val codec : Codec[BuildingInfoUpdateMessage] = ( implicit val codec : Codec[BuildingInfoUpdateMessage] = (
("continent_guid" | PlanetSideGUID.codec) :: ("continent_id" | uint16L) ::
("building_guid" | PlanetSideGUID.codec) :: ("building_id" | uint16L) ::
("ntu_level" | uint4L) :: ("ntu_level" | uint4L) ::
("is_hacked" | bool ) :: ("is_hacked" | bool ) ::
("empire_hack" | PlanetSideEmpire.codec) :: ("empire_hack" | PlanetSideEmpire.codec) ::

View file

@ -12,10 +12,10 @@ import scodec.codecs._
* This generates the event message "The [empire] have captured [continent]." * This generates the event message "The [empire] have captured [continent]."
* If the continent_guid is not a valid zone, no message is displayed. * 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. * 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 * @param empire identifies the empire
*/ */
final case class ContinentalLockUpdateMessage(continent_guid : PlanetSideGUID, final case class ContinentalLockUpdateMessage(continent_id : Int,
empire : PlanetSideEmpire.Value) empire : PlanetSideEmpire.Value)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = ContinentalLockUpdateMessage type Packet = ContinentalLockUpdateMessage
@ -25,7 +25,7 @@ final case class ContinentalLockUpdateMessage(continent_guid : PlanetSideGUID,
object ContinentalLockUpdateMessage extends Marshallable[ContinentalLockUpdateMessage] { object ContinentalLockUpdateMessage extends Marshallable[ContinentalLockUpdateMessage] {
implicit val codec : Codec[ContinentalLockUpdateMessage] = ( implicit val codec : Codec[ContinentalLockUpdateMessage] = (
("continent_guid" | PlanetSideGUID.codec) :: ("continent_id" | uint16L) ::
("empire" | PlanetSideEmpire.codec) ("empire" | PlanetSideEmpire.codec)
).as[ContinentalLockUpdateMessage] ).as[ContinentalLockUpdateMessage]
} }

View file

@ -1,4 +1,4 @@
// Copyright (c) 2016 PSForever.net to present // Copyright (c) 2017 PSForever
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}

View file

@ -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)
}
}
)
}

View file

@ -6,6 +6,22 @@ import scodec.Codec
import scodec.codecs._ import scodec.codecs._
import shapeless.{::, HNil} 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. * 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. * 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 unk3 na; always `true`?
* @param friends a list of `Friend`s * @param friends a list of `Friend`s
*/ */
final case class FriendsResponse(action : Int, final case class FriendsResponse(action : FriendAction.Value,
unk1 : Int, unk1 : Int,
unk2 : Boolean, unk2 : Boolean,
unk3 : Boolean, unk3 : Boolean,
@ -66,7 +82,7 @@ object Friend extends Marshallable[Friend] {
object FriendsResponse extends Marshallable[FriendsResponse] { object FriendsResponse extends Marshallable[FriendsResponse] {
implicit val codec : Codec[FriendsResponse] = ( implicit val codec : Codec[FriendsResponse] = (
("action" | uintL(3)) :: ("action" | FriendAction.codec) ::
("unk1" | uint4L) :: ("unk1" | uint4L) ::
("unk2" | bool) :: ("unk2" | bool) ::
("unk3" | bool) :: ("unk3" | bool) ::
@ -76,8 +92,8 @@ object FriendsResponse extends Marshallable[FriendsResponse] {
}) })
).xmap[FriendsResponse] ( ).xmap[FriendsResponse] (
{ {
case act :: u1 :: u2 :: u3 :: num :: friend1 :: friends :: HNil => case act :: u1 :: u2 :: u3 :: _ :: friend1 :: friends :: HNil =>
val friendList : List[Friend] = if(friend1.isDefined) { friend1.get :: friends } else { friends } val friendList : List[Friend] = if(friend1.isDefined) { friend1.get +: friends } else { friends }
FriendsResponse(act, u1, u2, u3, friendList) FriendsResponse(act, u1, u2, u3, friendList)
}, },
{ {

View file

@ -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. * 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.) * (It will still be necessary to alert this change from the server's perspective.)
* @param unk1 na; * @param unk1 na;
* 0 commonly;
* 3 for building objects during login phase;
* hack type? * hack type?
* @param target_guid the target of the hack * @param target_guid the target of the hack
* @param player_guid the player * @param player_guid the player

View file

@ -32,13 +32,13 @@ final case class HotSpotInfo(x : Float,
* <br> * <br>
* Exploration:<br> * Exploration:<br>
* What does (zone) priority entail? * What does (zone) priority entail?
* @param continent_guid the zone (continent) * @param continent_id the zone
* @param priority na * @param priority na
* @param spots a List of HotSpotInfo * @param spots a List of HotSpotInfo
*/ */
final case class HotSpotUpdateMessage(continent_guid : PlanetSideGUID, final case class HotSpotUpdateMessage(continent_id : Int,
priority : Int, priority : Int,
spots : List[HotSpotInfo] = Nil) spots : List[HotSpotInfo])
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = HotSpotUpdateMessage type Packet = HotSpotUpdateMessage
def opcode = GamePacketOpcode.HotSpotUpdateMessage def opcode = GamePacketOpcode.HotSpotUpdateMessage
@ -47,9 +47,9 @@ final case class HotSpotUpdateMessage(continent_guid : PlanetSideGUID,
object HotSpotInfo extends Marshallable[HotSpotInfo] { object HotSpotInfo extends Marshallable[HotSpotInfo] {
/* /*
the scale is technically not "correct" the client is looking for a normal 0-8192 value where default is 1.0f
the client is looking for a normal 0-8192 value we try to enforce a more modest graphic scale where default is 64.0f (arbitrary)
we are trying to enforce a more modest graphic scale at 128.0f 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] = { implicit val codec : Codec[HotSpotInfo] = {
("x" | newcodecs.q_float(0.0, 8192.0, 20)) :: ("x" | newcodecs.q_float(0.0, 8192.0, 20)) ::
@ -60,8 +60,8 @@ object HotSpotInfo extends Marshallable[HotSpotInfo] {
object HotSpotUpdateMessage extends Marshallable[HotSpotUpdateMessage] { object HotSpotUpdateMessage extends Marshallable[HotSpotUpdateMessage] {
implicit val codec : Codec[HotSpotUpdateMessage] = ( implicit val codec : Codec[HotSpotUpdateMessage] = (
("continent_guid" | PlanetSideGUID.codec) :: ("continent_guid" | uint16L) ::
("priority" | uint4L) :: ("priority" | uint4L) ::
("spots" | PacketHelpers.listOfNAligned(longL(8), 4, HotSpotInfo.codec)) ("spots" | PacketHelpers.listOfNAligned(longL(12), 0, HotSpotInfo.codec))
).as[HotSpotUpdateMessage] ).as[HotSpotUpdateMessage]
} }

View file

@ -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)
}
)
}

View file

@ -10,7 +10,7 @@ import scodec.codecs._
* @param zone the zone * @param zone the zone
* @param unk na * @param unk na
*/ */
final case class ZoneForcedCavernConnectionsMessage(zone : PlanetSideGUID, final case class ZoneForcedCavernConnectionsMessage(zone : Int,
unk : Int) unk : Int)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = ZoneForcedCavernConnectionsMessage type Packet = ZoneForcedCavernConnectionsMessage
@ -20,7 +20,7 @@ final case class ZoneForcedCavernConnectionsMessage(zone : PlanetSideGUID,
object ZoneForcedCavernConnectionsMessage extends Marshallable[ZoneForcedCavernConnectionsMessage] { object ZoneForcedCavernConnectionsMessage extends Marshallable[ZoneForcedCavernConnectionsMessage] {
implicit val codec : Codec[ZoneForcedCavernConnectionsMessage] = ( implicit val codec : Codec[ZoneForcedCavernConnectionsMessage] = (
("zone" | PlanetSideGUID.codec) :: ("zone" | uint16L) ::
("unk" | uint2L) ("unk" | uint2L)
).as[ZoneForcedCavernConnectionsMessage] ).as[ZoneForcedCavernConnectionsMessage]
} }

View file

@ -14,7 +14,7 @@ import scodec.codecs._
* @param unk na; * @param unk na;
* usually `true` * usually `true`
*/ */
final case class ZoneLockInfoMessage(zone : PlanetSideGUID, final case class ZoneLockInfoMessage(zone : Int,
lock_status : Boolean, lock_status : Boolean,
unk : Boolean) unk : Boolean)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
@ -25,7 +25,7 @@ final case class ZoneLockInfoMessage(zone : PlanetSideGUID,
object ZoneLockInfoMessage extends Marshallable[ZoneLockInfoMessage] { object ZoneLockInfoMessage extends Marshallable[ZoneLockInfoMessage] {
implicit val codec : Codec[ZoneLockInfoMessage] = ( implicit val codec : Codec[ZoneLockInfoMessage] = (
("zone" | PlanetSideGUID.codec) :: ("zone" | uint16L) ::
("lock_status" | bool) :: ("lock_status" | bool) ::
("unk" | bool) ("unk" | bool)
).as[ZoneLockInfoMessage] ).as[ZoneLockInfoMessage]

View file

@ -32,7 +32,7 @@ import scodec.codecs._
* Sanctuary zones possess strange queue values that are occasionally zero'd. * 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. * 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 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_queue the maximum number of TR players that can join this zone
* @param tr_pop the current TR population in 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_queue the maximum number of Black OPs players that can join this zone
* @param bo_pop the current Black OPs population in 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, zone_queue : Long,
tr_queue : Long, tr_queue : Long,
tr_pop : Long, tr_pop : Long,
@ -61,7 +61,7 @@ final case class ZonePopulationUpdateMessage(continent_guid : PlanetSideGUID,
object ZonePopulationUpdateMessage extends Marshallable[ZonePopulationUpdateMessage] { object ZonePopulationUpdateMessage extends Marshallable[ZonePopulationUpdateMessage] {
implicit val codec : Codec[ZonePopulationUpdateMessage] = ( implicit val codec : Codec[ZonePopulationUpdateMessage] = (
("continent_guid" | PlanetSideGUID.codec) :: ("zone_id" | uint16L) ::
("zone_queue" | uint32L) :: ("zone_queue" | uint32L) ::
("tr_queue" | uint32L) :: ("tr_pop" | uint32L) :: ("tr_queue" | uint32L) :: ("tr_pop" | uint32L) ::
("nc_queue" | uint32L) :: ("nc_pop" | uint32L) :: ("nc_queue" | uint32L) :: ("nc_pop" | uint32L) ::

View file

@ -4,7 +4,6 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.ImplantType
import scodec.bits._ import scodec.bits._
class AvatarImplantMessageTest extends Specification { class AvatarImplantMessageTest extends Specification {
@ -14,7 +13,7 @@ class AvatarImplantMessageTest extends Specification {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case AvatarImplantMessage(player_guid, unk1, unk2, implant) => case AvatarImplantMessage(player_guid, unk1, unk2, implant) =>
player_guid mustEqual PlanetSideGUID(3171) player_guid mustEqual PlanetSideGUID(3171)
unk1 mustEqual 3 unk1 mustEqual ImplantAction.Activation
unk2 mustEqual 1 unk2 mustEqual 1
implant mustEqual 1 implant mustEqual 1
case _ => case _ =>
@ -23,7 +22,7 @@ class AvatarImplantMessageTest extends Specification {
} }
"encode" in { "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 val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -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
}
}

View 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
}
}

View file

@ -12,8 +12,8 @@ class BroadcastWarpgateUpdateMessageTest extends Specification {
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case BroadcastWarpgateUpdateMessage(continent_guid, building_guid, state1, state2, state3) => case BroadcastWarpgateUpdateMessage(continent_guid, building_guid, state1, state2, state3) =>
continent_guid mustEqual PlanetSideGUID(13) continent_guid mustEqual 13
building_guid mustEqual PlanetSideGUID(1) building_guid mustEqual 1
state1 mustEqual false state1 mustEqual false
state2 mustEqual false state2 mustEqual false
state3 mustEqual true state3 mustEqual true
@ -23,7 +23,7 @@ class BroadcastWarpgateUpdateMessageTest extends Specification {
} }
"encode" in { "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 val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -12,29 +12,29 @@ class BuildingInfoUpdateMessageTest extends Specification {
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case BuildingInfoUpdateMessage(continent_guid : PlanetSideGUID, case BuildingInfoUpdateMessage(continent_guid,
building_guid : PlanetSideGUID, building_guid,
ntu_level : Int, ntu_level,
is_hacked : Boolean, is_hacked,
empire_hack : PlanetSideEmpire.Value, empire_hack,
hack_time_remaining : Long, hack_time_remaining,
empire_own : PlanetSideEmpire.Value, empire_own,
unk1 : Long, unk1,
unk1x : Option[Additional1], unk1x,
generator_state : PlanetSideGeneratorState.Value, generator_state,
spawn_tubes_normal : Boolean, spawn_tubes_normal,
force_dome_active : Boolean, force_dome_active,
lattice_benefit : Int, lattice_benefit,
unk3 : Int, unk3,
unk4 : List[Additional2], unk4,
unk5 : Long, unk5,
unk6 : Boolean, unk6,
unk7 : Int, unk7,
unk7x : Option[Additional3], unk7x,
boost_spawn_pain : Boolean, boost_spawn_pain,
boost_generator_pain : Boolean) => boost_generator_pain) =>
continent_guid mustEqual PlanetSideGUID(4) continent_guid mustEqual 4
building_guid mustEqual PlanetSideGUID(9) building_guid mustEqual 9
ntu_level mustEqual 1 ntu_level mustEqual 1
is_hacked mustEqual false is_hacked mustEqual false
empire_hack mustEqual PlanetSideEmpire.NEUTRAL empire_hack mustEqual PlanetSideEmpire.NEUTRAL
@ -61,8 +61,8 @@ class BuildingInfoUpdateMessageTest extends Specification {
} }
"encode" in { "encode" in {
val msg = BuildingInfoUpdateMessage(PlanetSideGUID(4), val msg = BuildingInfoUpdateMessage(4,
PlanetSideGUID(9), 9,
1, 1,
false, false,
PlanetSideEmpire.NEUTRAL, PlanetSideEmpire.NEUTRAL,

View file

@ -13,7 +13,7 @@ class ContinentalLockUpdateMessageTest extends Specification {
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case ContinentalLockUpdateMessage(continent_guid, empire) => case ContinentalLockUpdateMessage(continent_guid, empire) =>
continent_guid mustEqual PlanetSideGUID(22) continent_guid mustEqual 22
empire mustEqual PlanetSideEmpire.NC empire mustEqual PlanetSideEmpire.NC
case _ => case _ =>
ko ko
@ -21,7 +21,7 @@ class ContinentalLockUpdateMessageTest extends Specification {
} }
"encode" in { "encode" in {
val msg = ContinentalLockUpdateMessage(PlanetSideGUID(22), PlanetSideEmpire.NC) val msg = ContinentalLockUpdateMessage(22, PlanetSideEmpire.NC)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -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
}
}

View file

@ -14,7 +14,7 @@ class FriendsResponseTest extends Specification {
"decode (one friend)" in { "decode (one friend)" in {
PacketCoding.DecodePacket(stringOneFriend).require match { PacketCoding.DecodePacket(stringOneFriend).require match {
case FriendsResponse(action, unk2, unk3, unk4, list) => case FriendsResponse(action, unk2, unk3, unk4, list) =>
action mustEqual 3 action mustEqual FriendAction.UpdateFriend
unk2 mustEqual 0 unk2 mustEqual 0
unk3 mustEqual true unk3 mustEqual true
unk4 mustEqual true unk4 mustEqual true
@ -29,7 +29,7 @@ class FriendsResponseTest extends Specification {
"decode (multiple friends)" in { "decode (multiple friends)" in {
PacketCoding.DecodePacket(stringManyFriends).require match { PacketCoding.DecodePacket(stringManyFriends).require match {
case FriendsResponse(action, unk2, unk3, unk4, list) => case FriendsResponse(action, unk2, unk3, unk4, list) =>
action mustEqual 0 action mustEqual FriendAction.InitializeFriendList
unk2 mustEqual 0 unk2 mustEqual 0
unk3 mustEqual true unk3 mustEqual true
unk4 mustEqual true unk4 mustEqual true
@ -52,7 +52,7 @@ class FriendsResponseTest extends Specification {
"decode (short)" in { "decode (short)" in {
PacketCoding.DecodePacket(stringShort).require match { PacketCoding.DecodePacket(stringShort).require match {
case FriendsResponse(action, unk2, unk3, unk4, list) => case FriendsResponse(action, unk2, unk3, unk4, list) =>
action mustEqual 4 action mustEqual FriendAction.InitializeIgnoreList
unk2 mustEqual 0 unk2 mustEqual 0
unk3 mustEqual true unk3 mustEqual true
unk4 mustEqual true unk4 mustEqual true
@ -63,7 +63,7 @@ class FriendsResponseTest extends Specification {
} }
"encode (one friend)" in { "encode (one friend)" in {
val msg = FriendsResponse(3, 0, true, true, val msg = FriendsResponse(FriendAction.UpdateFriend, 0, true, true,
Friend("KurtHectic-G", false) :: Friend("KurtHectic-G", false) ::
Nil) Nil)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -72,7 +72,7 @@ class FriendsResponseTest extends Specification {
} }
"encode (multiple friends)" in { "encode (multiple friends)" in {
val msg = FriendsResponse(0, 0, true, true, val msg = FriendsResponse(FriendAction.InitializeFriendList, 0, true, true,
Friend("Angello-W", false) :: Friend("Angello-W", false) ::
Friend("thephattphrogg", false) :: Friend("thephattphrogg", false) ::
Friend("Kimpossible12", false) :: Friend("Kimpossible12", false) ::
@ -85,7 +85,7 @@ class FriendsResponseTest extends Specification {
} }
"encode (short)" in { "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 val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringShort pkt mustEqual stringShort

View file

@ -4,17 +4,19 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.Vector3
import scodec.bits._ import scodec.bits._
class HotSpotUpdateMessageTest extends Specification { class HotSpotUpdateMessageTest extends Specification {
val stringClear = hex"9F 0500 1 00 0" val stringClear = hex"9F 0500 1 000"
val stringOne = hex"9F 0500 1 01 0 00 2E9 00 145 80000 0" val stringOne = hex"9F 0500 1 010 002E9 00145 80000 0"
val stringTwo = hex"9F 0500 5 02 0 00 D07 00 8CA 80000 00 BEA 00 4C4 80000" 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 { "decode (clear)" in {
PacketCoding.DecodePacket(stringClear).require match { PacketCoding.DecodePacket(stringClear).require match {
case HotSpotUpdateMessage(continent_guid, unk, spots) => case HotSpotUpdateMessage(continent_id, unk, spots) =>
continent_guid mustEqual PlanetSideGUID(5) continent_id mustEqual 5
unk mustEqual 1 unk mustEqual 1
spots.size mustEqual 0 spots.size mustEqual 0
case _ => case _ =>
@ -24,13 +26,11 @@ class HotSpotUpdateMessageTest extends Specification {
"decode (one)" in { "decode (one)" in {
PacketCoding.DecodePacket(stringOne).require match { PacketCoding.DecodePacket(stringOne).require match {
case HotSpotUpdateMessage(continent_guid, unk, spots) => case HotSpotUpdateMessage(continent_id, unk, spots) =>
continent_guid mustEqual PlanetSideGUID(5) continent_id mustEqual 5
unk mustEqual 1 unk mustEqual 1
spots.size mustEqual 1 spots.size mustEqual 1
spots.head.x mustEqual 4700.0f spots.head mustEqual HotSpotInfo(4700.0f, 2600.0f, 64.0f)
spots.head.y mustEqual 2600.0f
spots.head.scale mustEqual 64.0f
case _ => case _ =>
ko ko
} }
@ -38,36 +38,52 @@ class HotSpotUpdateMessageTest extends Specification {
"decode (two)" in { "decode (two)" in {
PacketCoding.DecodePacket(stringTwo).require match { PacketCoding.DecodePacket(stringTwo).require match {
case HotSpotUpdateMessage(continent_guid, unk, spots) => case HotSpotUpdateMessage(continent_id, unk, spots) =>
continent_guid mustEqual PlanetSideGUID(5) continent_id mustEqual 5
unk mustEqual 5 unk mustEqual 5
spots.size mustEqual 2 spots.size mustEqual 2
spots.head.x mustEqual 4000.0f spots.head mustEqual HotSpotInfo(4000.0f, 5400.0f, 64.0f)
spots.head.y mustEqual 5400.0f spots(1) mustEqual HotSpotInfo(5500.0f, 2200.0f, 64.0f)
spots.head.scale mustEqual 64.0f case _ =>
spots(1).x mustEqual 5500.0f ko
spots(1).y mustEqual 2200.0f }
spots(1).scale mustEqual 64.0f }
"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 _ => case _ =>
ko ko
} }
} }
"encode (clear)" in { "encode (clear)" in {
val msg = HotSpotUpdateMessage(PlanetSideGUID(5),1) val msg = HotSpotUpdateMessage(5,1, Nil)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringClear pkt mustEqual stringClear
} }
"encode (one)" in { "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 val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringOne pkt mustEqual stringOne
} }
"encode (two)" in { "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 val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringTwo 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
}
} }

View 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
}
}

View file

@ -13,7 +13,7 @@ class ZoneForcedCavernConnectionsMessageTest extends Specification {
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case ZoneForcedCavernConnectionsMessage(zone, unk) => case ZoneForcedCavernConnectionsMessage(zone, unk) =>
zone mustEqual PlanetSideGUID(32) zone mustEqual 32
unk mustEqual 1 unk mustEqual 1
case _ => case _ =>
ko ko
@ -21,7 +21,7 @@ class ZoneForcedCavernConnectionsMessageTest extends Specification {
} }
"encode" in { "encode" in {
val msg = ZoneForcedCavernConnectionsMessage(PlanetSideGUID(32), 1) val msg = ZoneForcedCavernConnectionsMessage(32, 1)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -12,7 +12,7 @@ class ZoneLockInfoMesageTest extends Specification {
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case ZoneLockInfoMessage(zone, locked, unk) => case ZoneLockInfoMessage(zone, locked, unk) =>
zone mustEqual PlanetSideGUID(27) zone mustEqual 27
locked mustEqual false locked mustEqual false
unk mustEqual true unk mustEqual true
case _ => case _ =>
@ -21,7 +21,7 @@ class ZoneLockInfoMesageTest extends Specification {
} }
"encode" in { "encode" in {
val msg = ZoneLockInfoMessage(PlanetSideGUID(27), false, true) val msg = ZoneLockInfoMessage(27, false, true)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -12,7 +12,7 @@ class ZonePopulationUpdateMessageTest extends Specification {
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { 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) => 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 zone_queue mustEqual 414
tr_queue mustEqual 138 tr_queue mustEqual 138
tr_pop mustEqual 37 tr_pop mustEqual 37
@ -28,7 +28,7 @@ class ZonePopulationUpdateMessageTest extends Specification {
} }
"encode" in { "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 val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -6,7 +6,7 @@ import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.{Door, DoorControl} 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.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.PlanetSideEmpire 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 { class BuildingControl1Test extends ActorTest {
"Building Control" should { "Building Control" should {
"construct" in { "construct" in {

View file

@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package objects package objects
import akka.actor.{Actor, Props} import akka.actor.{Actor, ActorContext, Props}
import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.NumberPoolHub
import net.psforever.packet.game.PlanetSideGUID import net.psforever.packet.game.PlanetSideGUID
import net.psforever.objects.serverobject.ServerObjectBuilder import net.psforever.objects.serverobject.ServerObjectBuilder
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.objects.zones.Zone
import net.psforever.types.Vector3 import net.psforever.types.Vector3
@ -14,7 +14,23 @@ import scala.concurrent.duration.Duration
class BuildingBuilderTest extends ActorTest { class BuildingBuilderTest extends ActorTest {
"Building object" should { "Building object" should {
"build" in { "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 ! "!" actor ! "!"
val reply = receiveOne(Duration.create(1000, "ms")) 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 = { def receive : Receive = {
case _ => case _ =>
sender ! FoundationBuilder(Building.Structure).Build(building_id, zone)(context) sender ! FoundationBuilder(structure_con).Build(building_id, zone)(context)
} }
} }
} }

View file

@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.pad.VehicleSpawnPad 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.objects.serverobject.terminals.Terminal
import net.psforever.types.Vector3 import net.psforever.types.Vector3
@ -48,15 +48,27 @@ object Maps {
val map12 = new ZoneMap("map12") val map12 = new ZoneMap("map12")
val map13 = new ZoneMap("map13") { val map13 = new ZoneMap("map13") {
LocalObject(ServerObjectBuilder(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180)))) LocalBuilding(1, FoundationBuilder(WarpGate.Structure))
LocalObject(ServerObjectBuilder(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180)))) LocalBuilding(2, FoundationBuilder(WarpGate.Structure))
LocalObject(ServerObjectBuilder(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0)))) LocalBuilding(3, FoundationBuilder(WarpGate.Structure))
LocalObject(ServerObjectBuilder(333, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 111.140625f), Vector3(0, 0, 0))))
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(362, Door.Constructor))
LocalObject(ServerObjectBuilder(370, Door.Constructor)) LocalObject(ServerObjectBuilder(370, Door.Constructor))
LocalObject(ServerObjectBuilder(371, Door.Constructor)) LocalObject(ServerObjectBuilder(371, Door.Constructor))
LocalObject(ServerObjectBuilder(372, Door.Constructor))
LocalObject(ServerObjectBuilder(373, Door.Constructor))
LocalObject(ServerObjectBuilder(374, Door.Constructor)) LocalObject(ServerObjectBuilder(374, Door.Constructor))
LocalObject(ServerObjectBuilder(375, Door.Constructor)) LocalObject(ServerObjectBuilder(375, Door.Constructor))
LocalObject(ServerObjectBuilder(394, Door.Constructor)) LocalObject(ServerObjectBuilder(394, Door.Constructor))
@ -66,7 +78,6 @@ object Maps {
LocalObject(ServerObjectBuilder(398, Door.Constructor)) LocalObject(ServerObjectBuilder(398, Door.Constructor))
LocalObject(ServerObjectBuilder(462, Door.Constructor)) LocalObject(ServerObjectBuilder(462, Door.Constructor))
LocalObject(ServerObjectBuilder(463, Door.Constructor)) LocalObject(ServerObjectBuilder(463, Door.Constructor))
LocalObject(ServerObjectBuilder(520, ImplantTerminalMech.Constructor)) //Hart B
LocalObject(ServerObjectBuilder(522, ImplantTerminalMech.Constructor)) //Hart C LocalObject(ServerObjectBuilder(522, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(523, ImplantTerminalMech.Constructor)) //Hart C LocalObject(ServerObjectBuilder(523, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(524, 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(527, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(528, ImplantTerminalMech.Constructor)) //Hart C LocalObject(ServerObjectBuilder(528, ImplantTerminalMech.Constructor)) //Hart C
LocalObject(ServerObjectBuilder(529, 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(686, Locker.Constructor))
LocalObject(ServerObjectBuilder(687, Locker.Constructor)) LocalObject(ServerObjectBuilder(687, Locker.Constructor))
LocalObject(ServerObjectBuilder(688, Locker.Constructor)) LocalObject(ServerObjectBuilder(688, Locker.Constructor))
@ -87,17 +94,10 @@ object Maps {
LocalObject(ServerObjectBuilder(691, Locker.Constructor)) LocalObject(ServerObjectBuilder(691, Locker.Constructor))
LocalObject(ServerObjectBuilder(692, Locker.Constructor)) LocalObject(ServerObjectBuilder(692, Locker.Constructor))
LocalObject(ServerObjectBuilder(693, 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(842, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(843, Terminal.Constructor(order_terminal))) LocalObject(ServerObjectBuilder(843, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(844, Terminal.Constructor(order_terminal))) LocalObject(ServerObjectBuilder(844, Terminal.Constructor(order_terminal)))
LocalObject(ServerObjectBuilder(845, 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(1082, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1083, 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 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(1087, Terminal.Constructor(implant_terminal_interface))) //TODO guid not correct
LocalObject(ServerObjectBuilder(1088, 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(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(186, 2)
ObjectToBuilding(187, 2) ObjectToBuilding(187, 2)
ObjectToBuilding(188, 2) ObjectToBuilding(188, 2)
@ -139,30 +129,14 @@ object Maps {
ObjectToBuilding(843, 2) ObjectToBuilding(843, 2)
ObjectToBuilding(844, 2) ObjectToBuilding(844, 2)
ObjectToBuilding(845, 2) ObjectToBuilding(845, 2)
ObjectToBuilding(853, 2) //TODO check building_id ObjectToBuilding(1082, 2)
ObjectToBuilding(855, 2) //TODO check building_id ObjectToBuilding(1083, 2)
ObjectToBuilding(860, 2) //TODO check building_id ObjectToBuilding(1084, 2)
ObjectToBuilding(1063, 2) //TODO unowned courtyard terminal? ObjectToBuilding(1085, 2)
ObjectToBuilding(500, 2) //TODO unowned courtyard spawnpad? ObjectToBuilding(1086, 2)
ObjectToBuilding(304, 2) //TODO unowned courtyard terminal? ObjectToBuilding(1087, 2)
ObjectToBuilding(501, 2) //TODO unowned courtyard spawnpad? ObjectToBuilding(1088, 2)
ObjectToBuilding(1089, 2)
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)
TerminalToInterface(522, 1082) TerminalToInterface(522, 1082)
TerminalToInterface(523, 1083) TerminalToInterface(523, 1083)
TerminalToInterface(524, 1084) TerminalToInterface(524, 1084)
@ -171,6 +145,50 @@ object Maps {
TerminalToInterface(527, 1087) TerminalToInterface(527, 1087)
TerminalToInterface(528, 1088) TerminalToInterface(528, 1088)
TerminalToInterface(529, 1089) 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") val map14 = new ZoneMap("map13")

View file

@ -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.{MatrixTerminalDefinition, Terminal}
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState} 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.objects.zones.{InterstellarCluster, Zone}
import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game.objectcreate._
import net.psforever.types._ import net.psforever.types._
@ -798,7 +801,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) { if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
val slot = slotNumber.get val slot = slotNumber.get
log.info(s"$message - put in slot $slot") 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)) sendResponse(ItemTransactionResultMessage(terminal_guid, TransactionType.Learn, true))
} }
else { else {
@ -833,7 +836,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) { if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
val slot = slotNumber.get val slot = slotNumber.get
log.info(s"$tplayer is selling $implant_type - take from slot $slot") 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)) sendResponse(ItemTransactionResultMessage(terminal_guid, TransactionType.Sell, true))
} }
else { else {
@ -976,40 +979,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
case VehicleLoaded(_/*vehicle*/) => ; case VehicleLoaded(_/*vehicle*/) => ;
//currently being handled by VehicleSpawnPad.LoadVehicle during testing phase //currently being handled by VehicleSpawnPad.LoadVehicle during testing phase
case Zone.ClientInitialization(/*initList*/_) => case Zone.ClientInitialization(zone) =>
//TODO iterate over initList; for now, just do this val continentNumber = zone.Number
sendResponse( val poplist = LivePlayerList.ZonePopulation(continentNumber, _ => true)
BuildingInfoUpdateMessage( val popBO = 0 //TODO black ops test (partition)
PlanetSideGUID(6), //Ceryshen val popTR = poplist.count(_.Faction == PlanetSideEmpire.TR)
PlanetSideGUID(2), //Anguta val popNC = poplist.count(_.Faction == PlanetSideEmpire.NC)
8, //80% NTU val popVS = poplist.count(_.Faction == PlanetSideEmpire.VS)
false, //Base hacked
PlanetSideEmpire.NEUTRAL, //Base hacked by NC zone.Buildings.foreach({ case(id, building) => initBuilding(continentNumber, id, building) })
0, //10 minutes remaining for hack sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
PlanetSideEmpire.VS, //Base owned by VS sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
0, //!! Field != 0 will cause malformed packet. See class def. //CaptureFlagUpdateMessage()
None, //VanuModuleUpdateMessage()
PlanetSideGeneratorState.Normal, //Generator critical //ModuleLimitsMessage()
true, //Respawn tubes destroyed sendResponse(ZoneInfoMessage(continentNumber, true, 0))
true, //Force dome active sendResponse(ZoneLockInfoMessage(continentNumber, false, true))
16, //Tech plant lattice benefit sendResponse(ZoneForcedCavernConnectionsMessage(continentNumber, 0))
0, sendResponse(HotSpotUpdateMessage(continentNumber, 1, Nil)) //normally set in bulk; should be fine doing per continent
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 InterstellarCluster.ClientInitializationComplete(tplayer)=> case InterstellarCluster.ClientInitializationComplete(tplayer)=>
//this will cause the client to send back a BeginZoningMessage packet (see below) //PropertyOverrideMessage
sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L)) //VS Sanctuary 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") log.info("Load the now-registered player")
//load the now-registered player //load the now-registered player
tplayer.Spawn tplayer.Spawn
@ -1023,13 +1020,35 @@ class WorldSessionActor extends Actor with MDCContextAware {
val guid = tplayer.GUID val guid = tplayer.GUID
LivePlayerList.Assign(continent.Number, sessionId, guid) LivePlayerList.Assign(continent.Number, sessionId, guid)
sendResponse(SetCurrentAvatarMessage(guid,0,0)) 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)) 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) => case Zone.ItemFromGround(tplayer, item) =>
val obj_guid = item.GUID val obj_guid = item.GUID
@ -1241,19 +1260,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ BeginZoningMessage() => case msg @ BeginZoningMessage() =>
log.info("Reticulating splines ...") log.info("Reticulating splines ...")
//map-specific initializations //map-specific initializations
//TODO continent.ClientConfiguration() configZone(continent) //todo density
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
sendResponse(TimeOfDayMessage(1191182336)) sendResponse(TimeOfDayMessage(1191182336))
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list //custom
sendResponse(ContinentalLockUpdateMessage(13, PlanetSideEmpire.VS)) // "The VS have captured the VS Sanctuary."
sendResponse(ZonePopulationUpdateMessage(PlanetSideGUID(6), 414, 138, 0, 138, 0, 138, 0, 138, 0))
(1 to 255).foreach(i => { sendResponse(SetEmpireMessage(PlanetSideGUID(i), PlanetSideEmpire.VS)) }) (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 => { continent.EquipmentOnGround.foreach(item => {
val definition = item.Definition val definition = item.Definition
sendResponse( sendResponse(
@ -1408,6 +1421,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) => case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) =>
log.info(s"SpawnRequestMessage: $msg") 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) => case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) =>
// TODO: Prevents log spam, but should be handled correctly // TODO: Prevents log spam, but should be handled correctly
if (messagetype != ChatMessageType.CMT_TOGGLE_GM) { if (messagetype != ChatMessageType.CMT_TOGGLE_GM) {
@ -2019,11 +2035,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("ItemTransaction: " + msg) log.info("ItemTransaction: " + msg)
continent.GUID(terminal_guid) match { continent.GUID(terminal_guid) match {
case Some(term : Terminal) => case Some(term : Terminal) =>
if(player.Faction == term.Faction) { log.info(s"ItemTransaction: ${term.Definition.Name} found")
term.Actor ! Terminal.Request(player, msg) term.Actor ! Terminal.Request(player, msg)
} case Some(obj : PlanetSideGameObject) =>
case Some(obj : PlanetSideGameObject) => ; log.error(s"ItemTransaction: $obj is not a terminal")
case None => ; case _ =>
log.error(s"ItemTransaction: $terminal_guid does not exist")
} }
case msg @ FavoritesRequest(player_guid, unk, action, line, label) => 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") 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) = { def failWithError(error : String) = {
log.error(error) log.error(error)
sendResponse(ConnectionClose()) sendResponse(ConnectionClose())

View file

@ -39,7 +39,7 @@ object Zones {
super.Init(context) super.Init(context)
import net.psforever.types.PlanetSideEmpire 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 Building(29).get.Faction = PlanetSideEmpire.NC //South Villa Gun Tower
} }
} }