diff --git a/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala b/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala index 29b3ad78..a1562302 100644 --- a/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala +++ b/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala @@ -3,6 +3,7 @@ package net.psforever.objects.teamwork import akka.actor.{Actor, ActorContext, ActorRef, Cancellable, Props} import net.psforever.objects.DefaultCancellable +import net.psforever.types.SquadWaypoints import services.teamwork.SquadService.WaypointData import services.teamwork.SquadSwitchboard @@ -70,7 +71,7 @@ class SquadFeatures(val Squad : Squad) { def Start(implicit context : ActorContext) : SquadFeatures = { switchboard = context.actorOf(Props[SquadSwitchboard], s"squad${Squad.GUID.guid}") - waypoints = Array.fill[WaypointData](5)(new WaypointData()) + waypoints = Array.fill[WaypointData](SquadWaypoints.values.size)(new WaypointData()) this } diff --git a/common/src/main/scala/net/psforever/packet/game/SquadDetailDefinitionUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/SquadDetailDefinitionUpdateMessage.scala index 225489b7..055a031a 100644 --- a/common/src/main/scala/net/psforever/packet/game/SquadDetailDefinitionUpdateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/SquadDetailDefinitionUpdateMessage.scala @@ -114,7 +114,10 @@ final case class SquadPositionDetail(is_closed : Option[Boolean], * when 255, this indicated the end of enumerated squad position data and the data for that position is absent * @param info the squad position field data */ -final case class SquadPositionEntry(index : Int, info : Option[SquadPositionDetail]) +final case class SquadPositionEntry(index : Int, info : Option[SquadPositionDetail]) { + assert((index > -1 && index < 10) || index == 255, "index value is out of range 0=>n<=9 or n=255") + assert(if(index == 255) { info.isEmpty } else { true }, "index=255 indicates end of stream exclusively and field data should be blank") +} /** * Information regarding a squad's position as a series of common fields. diff --git a/common/src/main/scala/net/psforever/packet/game/SquadWaypointEvent.scala b/common/src/main/scala/net/psforever/packet/game/SquadWaypointEvent.scala index 4fe9f649..ff466782 100644 --- a/common/src/main/scala/net/psforever/packet/game/SquadWaypointEvent.scala +++ b/common/src/main/scala/net/psforever/packet/game/SquadWaypointEvent.scala @@ -1,8 +1,8 @@ -// Copyright (c) 2017 PSForever +// Copyright (c) 2019 PSForever package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} -import net.psforever.types.Vector3 +import net.psforever.types.{SquadWaypoints, Vector3} import scodec.{Attempt, Codec, Err} import scodec.codecs._ import shapeless.{::, HNil} @@ -14,7 +14,7 @@ final case class WaypointEvent(zone_number : Int, final case class SquadWaypointEvent(event_type : WaypointEventAction.Value, unk : Int, char_id : Long, - waypoint_type : Int, + waypoint_type : SquadWaypoints.Value, unk5 : Option[Long], waypoint_info : Option[WaypointEvent]) extends PlanetSideGamePacket { @@ -24,13 +24,13 @@ final case class SquadWaypointEvent(event_type : WaypointEventAction.Value, } object SquadWaypointEvent extends Marshallable[SquadWaypointEvent] { - def Add(unk : Int, char_id : Long, waypoint_type : Int, waypoint : WaypointEvent) : SquadWaypointEvent = + def Add(unk : Int, char_id : Long, waypoint_type : SquadWaypoints.Value, waypoint : WaypointEvent) : SquadWaypointEvent = SquadWaypointEvent(WaypointEventAction.Add, unk, char_id, waypoint_type, None, Some(waypoint)) - def Unknown1(unk : Int, char_id : Long, waypoint_type : Int, unk_a : Long) : SquadWaypointEvent = + def Unknown1(unk : Int, char_id : Long, waypoint_type : SquadWaypoints.Value, unk_a : Long) : SquadWaypointEvent = SquadWaypointEvent(WaypointEventAction.Unknown1, unk, char_id, waypoint_type, Some(unk_a), None) - def Remove(unk : Int, char_id : Long, waypoint_type : Int) : SquadWaypointEvent = + def Remove(unk : Int, char_id : Long, waypoint_type : SquadWaypoints.Value) : SquadWaypointEvent = SquadWaypointEvent(WaypointEventAction.Remove, unk, char_id, waypoint_type, None, None) private val waypoint_codec : Codec[WaypointEvent] = ( @@ -43,7 +43,7 @@ object SquadWaypointEvent extends Marshallable[SquadWaypointEvent] { ("event_type" | WaypointEventAction.codec) >>:~ { event_type => ("unk" | uint16L) :: ("char_id" | uint32L) :: - ("waypoint_type" | uint8L) :: + ("waypoint_type" | SquadWaypoints.codec) :: ("unk5" | conditional(event_type == WaypointEventAction.Unknown1, uint32L)) :: ("waypoint_info" | conditional(event_type == WaypointEventAction.Add, waypoint_codec)) } diff --git a/common/src/main/scala/net/psforever/packet/game/SquadWaypointRequest.scala b/common/src/main/scala/net/psforever/packet/game/SquadWaypointRequest.scala index 5b7ebd57..3384f8b5 100644 --- a/common/src/main/scala/net/psforever/packet/game/SquadWaypointRequest.scala +++ b/common/src/main/scala/net/psforever/packet/game/SquadWaypointRequest.scala @@ -2,7 +2,7 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} -import net.psforever.types.Vector3 +import net.psforever.types.{SquadWaypoints, Vector3} import scodec.{Attempt, Codec, Err} import scodec.codecs._ import shapeless.{::, HNil} @@ -46,7 +46,7 @@ final case class WaypointInfo(zone_number : Int, */ final case class SquadWaypointRequest(request_type : WaypointEventAction.Value, char_id : Long, - waypoint_type : Int, + waypoint_type : SquadWaypoints.Value, unk4 : Option[Long], waypoint_info : Option[WaypointInfo]) extends PlanetSideGamePacket { @@ -56,13 +56,13 @@ final case class SquadWaypointRequest(request_type : WaypointEventAction.Value, } object SquadWaypointRequest extends Marshallable[SquadWaypointRequest] { - def Add(char_id : Long, waypoint_type : Int, waypoint : WaypointInfo) : SquadWaypointRequest = + def Add(char_id : Long, waypoint_type : SquadWaypoints.Value, waypoint : WaypointInfo) : SquadWaypointRequest = SquadWaypointRequest(WaypointEventAction.Add, char_id, waypoint_type, None, Some(waypoint)) - def Unknown1(char_id : Long, waypoint_type : Int, unk_a : Long) : SquadWaypointRequest = + def Unknown1(char_id : Long, waypoint_type : SquadWaypoints.Value, unk_a : Long) : SquadWaypointRequest = SquadWaypointRequest(WaypointEventAction.Unknown1, char_id, waypoint_type, Some(unk_a), None) - def Remove(char_id : Long, waypoint_type : Int) : SquadWaypointRequest = + def Remove(char_id : Long, waypoint_type : SquadWaypoints.Value) : SquadWaypointRequest = SquadWaypointRequest(WaypointEventAction.Remove, char_id, waypoint_type, None, None) private val waypoint_codec : Codec[WaypointInfo] = ( @@ -80,7 +80,7 @@ object SquadWaypointRequest extends Marshallable[SquadWaypointRequest] { implicit val codec : Codec[SquadWaypointRequest] = ( ("request_type" | WaypointEventAction.codec) >>:~ { request_type => ("char_id" | uint32L) :: - ("waypoint_type" | uint8L) :: + ("waypoint_type" | SquadWaypoints.codec) :: ("unk4" | conditional(request_type == WaypointEventAction.Unknown1, uint32L)) :: ("waypoint" | conditional(request_type == WaypointEventAction.Add, waypoint_codec)) } diff --git a/common/src/main/scala/net/psforever/types/SquadRequestType.scala b/common/src/main/scala/net/psforever/types/SquadRequestType.scala index a9c35f7a..1947abe7 100644 --- a/common/src/main/scala/net/psforever/types/SquadRequestType.scala +++ b/common/src/main/scala/net/psforever/types/SquadRequestType.scala @@ -24,11 +24,4 @@ object SquadRequestType extends Enumeration { = Value implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L) - - def toResponse(request : SquadRequestType.Value) : SquadResponseType.Value = { - val id = request.id - if(id < 6) SquadResponseType(id) - else if(id > 6) SquadResponseType(id - 1) - else throw new NoSuchElementException("request does not have an applicable response") - } } diff --git a/common/src/main/scala/net/psforever/types/SquadResponseType.scala b/common/src/main/scala/net/psforever/types/SquadResponseType.scala index 1f73ccc4..84ab57ad 100644 --- a/common/src/main/scala/net/psforever/types/SquadResponseType.scala +++ b/common/src/main/scala/net/psforever/types/SquadResponseType.scala @@ -23,11 +23,4 @@ object SquadResponseType extends Enumeration { = Value implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L) - - def fromRequest(response : SquadResponseType.Value) : SquadRequestType.Value = { - val id = response.id - if(id < 6) SquadRequestType(id) - else if(id > 5) SquadRequestType(id + 1) - else throw new NoSuchElementException("response does not stem from an applicable request") - } } diff --git a/common/src/main/scala/net/psforever/types/SquadWaypoints.scala b/common/src/main/scala/net/psforever/types/SquadWaypoints.scala new file mode 100644 index 00000000..5a11ea95 --- /dev/null +++ b/common/src/main/scala/net/psforever/types/SquadWaypoints.scala @@ -0,0 +1,18 @@ +// Copyright (c) 2019 PSForever +package net.psforever.types + +import net.psforever.packet.PacketHelpers +import scodec.codecs._ + +object SquadWaypoints extends Enumeration { + type Type = Value + val + One, + Two, + Three, + Four, + ExperienceRally + = Value + + implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L) +} diff --git a/common/src/main/scala/services/teamwork/SquadAction.scala b/common/src/main/scala/services/teamwork/SquadAction.scala index a0034da5..0a1b9971 100644 --- a/common/src/main/scala/services/teamwork/SquadAction.scala +++ b/common/src/main/scala/services/teamwork/SquadAction.scala @@ -3,7 +3,7 @@ package services.teamwork import net.psforever.objects.zones.Zone import net.psforever.packet.game._ -import net.psforever.types.{PlanetSideEmpire, SquadRequestType, Vector3} +import net.psforever.types.{PlanetSideEmpire, SquadRequestType, SquadWaypoints, Vector3} object SquadAction { trait Action @@ -13,6 +13,6 @@ object SquadAction { final case class Definition(guid : PlanetSideGUID, line : Int, action : SquadAction) extends Action final case class Membership(request_type : SquadRequestType.Value, unk2 : Long, unk3 : Option[Long], player_name : String, unk5 : Option[Option[String]]) extends Action - final case class Waypoint(event_type : WaypointEventAction.Value, waypoint_type : Int, unk : Option[Long], waypoint_info : Option[WaypointInfo]) extends Action + final case class Waypoint(event_type : WaypointEventAction.Value, waypoint_type : SquadWaypoints.Value, unk : Option[Long], waypoint_info : Option[WaypointInfo]) extends Action final case class Update(char_id : Long, health : Int, max_health : Int, armor : Int, max_armor : Int, pos : Vector3, zone_number : Int) extends Action } diff --git a/common/src/main/scala/services/teamwork/SquadResponse.scala b/common/src/main/scala/services/teamwork/SquadResponse.scala index 1b3ae6fb..159f6fe2 100644 --- a/common/src/main/scala/services/teamwork/SquadResponse.scala +++ b/common/src/main/scala/services/teamwork/SquadResponse.scala @@ -3,7 +3,7 @@ package services.teamwork import net.psforever.objects.teamwork.Squad import net.psforever.packet.game._ -import net.psforever.types.SquadResponseType +import net.psforever.types.{SquadResponseType, SquadWaypoints} object SquadResponse { trait Response @@ -27,8 +27,8 @@ object SquadResponse { final case class Detail(guid : PlanetSideGUID, squad_detail : SquadDetail) extends Response - final case class InitWaypoints(char_id : Long, waypoints : Iterable[(Int, WaypointInfo, Int)]) extends Response - final case class WaypointEvent(event_type : WaypointEventAction.Value, char_id : Long, waypoint_type : Int, unk5 : Option[Long], waypoint_info : Option[WaypointInfo], unk : Int) extends Response + final case class InitWaypoints(char_id : Long, waypoints : Iterable[(SquadWaypoints.Value, WaypointInfo, Int)]) extends Response + final case class WaypointEvent(event_type : WaypointEventAction.Value, char_id : Long, waypoint_type : SquadWaypoints.Value, unk5 : Option[Long], waypoint_info : Option[WaypointInfo], unk : Int) extends Response final case class SquadSearchResults() extends Response } diff --git a/common/src/main/scala/services/teamwork/SquadService.scala b/common/src/main/scala/services/teamwork/SquadService.scala index 07b5d037..72267a6e 100644 --- a/common/src/main/scala/services/teamwork/SquadService.scala +++ b/common/src/main/scala/services/teamwork/SquadService.scala @@ -2544,16 +2544,14 @@ class SquadService extends Actor { * @see `SquadWaypointRequest` * @see `WaypointInfo` * @param guid the squad's unique identifier - * @param waypointType the type of the waypoint as an integer; - * 0-4 are squad waypoints; - * 5 is the squad leader's experience waypoint + * @param waypointType the type of the waypoint * @param info information about the waypoint, as was reported by the client's packet * @return the waypoint data, if the waypoint type is changed */ - def AddWaypoint(guid : PlanetSideGUID, waypointType : Int, info : WaypointInfo) : Option[WaypointData] = { + def AddWaypoint(guid : PlanetSideGUID, waypointType : SquadWaypoints.Value, info : WaypointInfo) : Option[WaypointData] = { squadFeatures.get(guid) match { case Some(features) => - features.Waypoints.lift(waypointType) match { + features.Waypoints.lift(waypointType.id) match { case Some(point) => point.zone_number = info.zone_number point.pos = info.pos @@ -2576,14 +2574,12 @@ class SquadService extends Actor { * All of the waypoints constantly exist as long as the squad to which they are attached exists. * They are merely "activated" and "deactivated." * @param guid the squad's unique identifier - * @param waypointType the type of the waypoint as an integer; - * 0-4 are squad waypoints; - * 5 is the squad leader's experience waypoint + * @param waypointType the type of the waypoint */ - def RemoveWaypoint(guid : PlanetSideGUID, waypointType : Int) : Unit = { + def RemoveWaypoint(guid : PlanetSideGUID, waypointType : SquadWaypoints.Value) : Unit = { squadFeatures.get(guid) match { case Some(features) => - features.Waypoints.lift(waypointType) match { + features.Waypoints.lift(waypointType.id) match { case Some(point) => point.pos = Vector3.z(1) case _ => @@ -2608,7 +2604,7 @@ class SquadService extends Actor { Publish( toCharId, SquadResponse.InitWaypoints(squad.Leader.CharId, list.zipWithIndex.collect { case (point, index) if point.pos != vz1 => - (index, WaypointInfo(point.zone_number, point.pos), 1) + (SquadWaypoints(index), WaypointInfo(point.zone_number, point.pos), 1) } ) ) diff --git a/common/src/test/scala/game/SquadWaypointEventTest.scala b/common/src/test/scala/game/SquadWaypointEventTest.scala index 5903a997..199b0bb1 100644 --- a/common/src/test/scala/game/SquadWaypointEventTest.scala +++ b/common/src/test/scala/game/SquadWaypointEventTest.scala @@ -3,8 +3,8 @@ package game import org.specs2.mutable._ import net.psforever.packet._ -import net.psforever.packet.game.{SquadWaypointEvent, WaypointEventAction, WaypointEvent} -import net.psforever.types.Vector3 +import net.psforever.packet.game.{SquadWaypointEvent, WaypointEvent, WaypointEventAction} +import net.psforever.types.{SquadWaypoints, Vector3} import scodec.bits._ class SquadWaypointEventTest extends Specification { @@ -19,7 +19,7 @@ class SquadWaypointEventTest extends Specification { unk1 mustEqual WaypointEventAction.Remove unk2 mustEqual 11 unk3 mustEqual 31155863L - unk4 mustEqual 0 + unk4 mustEqual SquadWaypoints.One unk5.isEmpty mustEqual true unk6.isEmpty mustEqual true case _ => @@ -33,7 +33,7 @@ class SquadWaypointEventTest extends Specification { unk1 mustEqual WaypointEventAction.Remove unk2 mustEqual 10 unk3 mustEqual 0L - unk4 mustEqual 4 + unk4 mustEqual SquadWaypoints.ExperienceRally unk5.isEmpty mustEqual true unk6.isEmpty mustEqual true case _ => @@ -47,7 +47,7 @@ class SquadWaypointEventTest extends Specification { unk1 mustEqual WaypointEventAction.Add unk2 mustEqual 3 unk3 mustEqual 41581052L - unk4 mustEqual 1 + unk4 mustEqual SquadWaypoints.Two unk5.isEmpty mustEqual true unk6.contains( WaypointEvent(10, Vector3(3457.9688f, 5514.4688f, 0.0f), 1) ) mustEqual true case _ => @@ -61,7 +61,7 @@ class SquadWaypointEventTest extends Specification { unk1 mustEqual WaypointEventAction.Unknown1 unk2 mustEqual 3 unk3 mustEqual 41581052L - unk4 mustEqual 1 + unk4 mustEqual SquadWaypoints.Two unk5.contains( 4L ) mustEqual true unk6.isEmpty mustEqual true case _ => @@ -70,28 +70,28 @@ class SquadWaypointEventTest extends Specification { } "encode (1)" in { - val msg = SquadWaypointEvent.Remove(11, 31155863L, 0) + val msg = SquadWaypointEvent.Remove(11, 31155863L, SquadWaypoints.One) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_1 } "encode (2)" in { - val msg = SquadWaypointEvent.Remove(10, 0L, 4) + val msg = SquadWaypointEvent.Remove(10, 0L, SquadWaypoints.ExperienceRally) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_2 } "encode (3)" in { - val msg = SquadWaypointEvent.Add(3, 41581052L, 1, WaypointEvent(10, Vector3(3457.9688f, 5514.4688f, 0.0f), 1)) + val msg = SquadWaypointEvent.Add(3, 41581052L, SquadWaypoints.Two, WaypointEvent(10, Vector3(3457.9688f, 5514.4688f, 0.0f), 1)) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_3 } "encode (4)" in { - val msg = SquadWaypointEvent.Unknown1(3, 41581052L, 1, 4L) + val msg = SquadWaypointEvent.Unknown1(3, 41581052L, SquadWaypoints.Two, 4L) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_4 diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 8d263f31..e48f1c4d 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -121,14 +121,14 @@ class WorldSessionActor extends Actor with MDCContextAware { val squadUI : LongMap[SquadUIElement] = new LongMap[SquadUIElement]() var squad_supplement_id : Int = 0 /** - * `AvatarConverter` can only rely on the `Avatar`-local Looking For Squad variable. - * When joining or creating a squad, the original state of the avatar's local LFS variable is blanked. + * When joining or creating a squad, the original state of the avatar's internal LFS variable is blanked. * This `WSA`-local variable is then used to indicate the ongoing state of the LFS UI component, * now called "Looking for Squad Member." + * Only the squad leader may toggle the LFSM marquee. * Upon leaving or disbanding a squad, this value is made false. - * Control switching between the `Avatar`-local and the `WSA`-local variable is contingent on `squadUI` being populated. + * Control switching between the `Avatar`-local and the `WorldSessionActor`-local variable is contingent on `squadUI` being populated. */ - var lfs : Boolean = false + var lfsm : Boolean = false var squadChannel : Option[String] = None var squadSetup : () => Unit = FirstTimeSquadSetup var squadUpdateCounter : Int = 0 @@ -492,7 +492,7 @@ class WorldSessionActor extends Actor with MDCContextAware { avatarService ! AvatarServiceMessage(s"${continent.Id}/${player.Faction}", AvatarAction.PlanetsideAttribute(playerGuid, 31, 0)) sendResponse(PlanetsideAttributeMessage(playerGuid, 32, 0)) //disassociate with member position in squad? sendResponse(PlanetsideAttributeMessage(playerGuid, 34, 4294967295L)) //unknown, perhaps unrelated? - lfs = false + lfsm = false //a finalization? what does this do? sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), 0, SquadAction.Unknown(18))) squad_supplement_id = 0 @@ -523,11 +523,11 @@ class WorldSessionActor extends Actor with MDCContextAware { //are we being demoted? if(squadUI(charId).index == 0) { //lfsm -> lfs - if(lfs) { + if(lfsm) { sendResponse(PlanetsideAttributeMessage(guid, 53, 0)) avatarService ! AvatarServiceMessage(factionOnContinentChannel, AvatarAction.PlanetsideAttribute(guid, 53, 0)) } - lfs = false + lfsm = false sendResponse(PlanetsideAttributeMessage(guid, 32, from_index)) //associate with member position in squad } //are we being promoted? @@ -3055,7 +3055,7 @@ class WorldSessionActor extends Actor with MDCContextAware { deadState = DeadState.Alive sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0, 0, tplayer.Position, player.Faction, true)) //looking for squad (members) - if(tplayer.LFS || lfs) { + if(tplayer.LFS || lfsm) { sendResponse(PlanetsideAttributeMessage(guid, 53, 1)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(guid, 53, 1)) } @@ -3692,7 +3692,6 @@ class WorldSessionActor extends Actor with MDCContextAware { } vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.VehicleState(player.GUID, vehicle_guid, unk1, pos, ang, vel, flight, unk6, unk7, wheels, unk9, unkA)) } - //vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.VehicleState(player.GUID, vehicle_guid, unk1, pos, ang, vel, flight, unk6, unk7, wheels, unk9, unkA)) updateSquad() case (None, _) => //log.error(s"VehicleState: no vehicle $vehicle_guid found in zone") @@ -4797,8 +4796,8 @@ class WorldSessionActor extends Actor with MDCContextAware { } else if(action == 36) { //Looking For Squad ON if(squadUI.nonEmpty) { - if(!lfs && squadUI(player.CharId).index == 0) { - lfs = true + if(!lfsm && squadUI(player.CharId).index == 0) { + lfsm = true avatarService ! AvatarServiceMessage(s"${continent.Id}/${player.Faction}", AvatarAction.PlanetsideAttribute(player.GUID, 53, 1)) } } @@ -4809,8 +4808,8 @@ class WorldSessionActor extends Actor with MDCContextAware { } else if(action == 37) { //Looking For Squad OFF if(squadUI.nonEmpty) { - if(lfs && squadUI(player.CharId).index == 0) { - lfs = false + if(lfsm && squadUI(player.CharId).index == 0) { + lfsm = false avatarService ! AvatarServiceMessage(s"${continent.Id}/${player.Faction}", AvatarAction.PlanetsideAttribute(player.GUID, 53, 0)) } }