mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
added a refactored squad joining entrypoint; initial work and test on SquadWaypointEvent packet; initial work on SquadWaypointRequest packet; squad waypoints up to #5 (squad experience) should be working
This commit is contained in:
parent
56d8748e99
commit
60d65e22d3
|
|
@ -472,7 +472,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x80 => noDecoder(GenericObjectAction2Message)
|
||||
case 0x81 => game.DestroyDisplayMessage.decode
|
||||
case 0x82 => noDecoder(TriggerBotAction)
|
||||
case 0x83 => noDecoder(SquadWaypointRequest)
|
||||
case 0x83 => game.SquadWaypointRequest.decode
|
||||
case 0x84 => game.SquadWaypointEvent.decode
|
||||
case 0x85 => noDecoder(OffshoreVehicleMessage)
|
||||
case 0x86 => game.ObjectDeployedMessage.decode
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@ import scodec.{Attempt, Codec, Err}
|
|||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
final case class WaypointEvent(unk1 : Int,
|
||||
final case class WaypointEvent(zone_number : Int,
|
||||
pos : Vector3,
|
||||
unk2 : Int)
|
||||
unk : Int)
|
||||
|
||||
final case class SquadWaypointEvent(unk1 : Int,
|
||||
unk2 : Int,
|
||||
unk3 : Long,
|
||||
unk4 : Int,
|
||||
final case class SquadWaypointEvent(event_type : WaypointEventAction.Value,
|
||||
unk : Int,
|
||||
char_id : Long,
|
||||
waypoint_type : Int,
|
||||
unk5 : Option[Long],
|
||||
unk6 : Option[WaypointEvent])
|
||||
waypoint_info : Option[WaypointEvent])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = SquadWaypointEvent
|
||||
def opcode = GamePacketOpcode.SquadWaypointEvent
|
||||
|
|
@ -24,55 +24,55 @@ final case class SquadWaypointEvent(unk1 : Int,
|
|||
}
|
||||
|
||||
object SquadWaypointEvent extends Marshallable[SquadWaypointEvent] {
|
||||
def apply(unk1 : Int, unk2 : Int, unk3 : Long, unk4 : Int, unk_a : Long) : SquadWaypointEvent =
|
||||
SquadWaypointEvent(unk1, unk2, unk3, unk4, Some(unk_a), None)
|
||||
def Add(unk : Int, char_id : Long, waypoint_type : Int, waypoint : WaypointEvent) : SquadWaypointEvent =
|
||||
SquadWaypointEvent(WaypointEventAction.Add, unk, char_id, waypoint_type, None, Some(waypoint))
|
||||
|
||||
def apply(unk1 : Int, unk2 : Int, unk3 : Long, unk4 : Int, unk_a : Int, pos : Vector3, unk_b : Int) : SquadWaypointEvent =
|
||||
SquadWaypointEvent(unk1, unk2, unk3, unk4, None, Some(WaypointEvent(unk_a, pos, unk_b)))
|
||||
def Unknown1(unk : Int, char_id : Long, waypoint_type : Int, unk_a : Long) : SquadWaypointEvent =
|
||||
SquadWaypointEvent(WaypointEventAction.Unknown1, unk, char_id, waypoint_type, Some(unk_a), None)
|
||||
|
||||
def apply(unk1 : Int, unk2 : Int, unk3 : Long, unk4 : Int) : SquadWaypointEvent =
|
||||
SquadWaypointEvent(unk1, unk2, unk3, unk4, None, None)
|
||||
def Remove(unk : Int, char_id : Long, waypoint_type : Int) : SquadWaypointEvent =
|
||||
SquadWaypointEvent(WaypointEventAction.Remove, unk, char_id, waypoint_type, None, None)
|
||||
|
||||
private val waypoint_codec : Codec[WaypointEvent] = (
|
||||
("unk1" | uint16L) ::
|
||||
("zone_number" | uint16L) ::
|
||||
("pos" | Vector3.codec_pos) ::
|
||||
("unk2" | uint(3))
|
||||
("unk" | uint(3))
|
||||
).as[WaypointEvent]
|
||||
|
||||
implicit val codec : Codec[SquadWaypointEvent] = (
|
||||
("unk1" | uint2) >>:~ { unk1 =>
|
||||
("unk2" | uint16L) ::
|
||||
("unk3" | uint32L) ::
|
||||
("unk4" | uint8L) ::
|
||||
("unk5" | conditional(unk1 == 1, uint32L)) ::
|
||||
("unk6" | conditional(unk1 == 0, waypoint_codec))
|
||||
("event_type" | WaypointEventAction.codec) >>:~ { event_type =>
|
||||
("unk" | uint16L) ::
|
||||
("char_id" | uint32L) ::
|
||||
("waypoint_type" | uint8L) ::
|
||||
("unk5" | conditional(event_type == WaypointEventAction.Unknown1, uint32L)) ::
|
||||
("waypoint_info" | conditional(event_type == WaypointEventAction.Add, waypoint_codec))
|
||||
}
|
||||
).exmap[SquadWaypointEvent] (
|
||||
{
|
||||
case 0 :: a :: b :: c :: None :: Some(d) :: HNil =>
|
||||
Attempt.Successful(SquadWaypointEvent(0, a, b, c, None, Some(d)))
|
||||
case WaypointEventAction.Add :: a :: char_id :: waypoint_type :: None :: Some(waypoint) :: HNil =>
|
||||
Attempt.Successful(SquadWaypointEvent(WaypointEventAction.Add, a, char_id, waypoint_type, None, Some(waypoint)))
|
||||
|
||||
case 1 :: a :: b :: c :: Some(d) :: None :: HNil =>
|
||||
Attempt.Successful(SquadWaypointEvent(1, a, b, c, Some(d), None))
|
||||
case WaypointEventAction.Unknown1 :: a :: char_id :: waypoint_type :: Some(d) :: None :: HNil =>
|
||||
Attempt.Successful(SquadWaypointEvent(WaypointEventAction.Unknown1, a, char_id, waypoint_type, Some(d), None))
|
||||
|
||||
case a :: b :: c :: d :: None :: None :: HNil =>
|
||||
Attempt.Successful(SquadWaypointEvent(a, b, c, d, None, None))
|
||||
case event_type :: b :: char_id :: waypoint_type :: None :: None :: HNil =>
|
||||
Attempt.Successful(SquadWaypointEvent(event_type, b, char_id, waypoint_type, None, None))
|
||||
|
||||
case n :: _ :: _ :: _ :: _ :: _ :: HNil =>
|
||||
Attempt.Failure(Err(s"unexpected format for unk1 - $n"))
|
||||
case data =>
|
||||
Attempt.Failure(Err(s"unexpected format for $data"))
|
||||
},
|
||||
{
|
||||
case SquadWaypointEvent(0, a, b, c, None, Some(d)) =>
|
||||
Attempt.Successful(0 :: a :: b :: c :: None :: Some(d) :: HNil)
|
||||
case SquadWaypointEvent(WaypointEventAction.Add, a, char_id, waypoint_type, None, Some(waypoint)) =>
|
||||
Attempt.Successful(WaypointEventAction.Add :: a :: char_id :: waypoint_type :: None :: Some(waypoint) :: HNil)
|
||||
|
||||
case SquadWaypointEvent(1, a, b, c, Some(d), None) =>
|
||||
Attempt.Successful(1 :: a :: b :: c :: Some(d) :: None :: HNil)
|
||||
case SquadWaypointEvent(WaypointEventAction.Unknown1, a, char_id, waypoint_type, Some(d), None) =>
|
||||
Attempt.Successful(WaypointEventAction.Unknown1 :: a :: char_id :: waypoint_type :: Some(d) :: None :: HNil)
|
||||
|
||||
case SquadWaypointEvent(a, b, c, d, None, None) =>
|
||||
Attempt.Successful(a :: b :: c :: d :: None :: None :: HNil)
|
||||
case SquadWaypointEvent(event_type, b, char_id, waypoint_type, None, None) =>
|
||||
Attempt.Successful(event_type :: b :: char_id :: waypoint_type :: None :: None :: HNil)
|
||||
|
||||
case SquadWaypointEvent(n, _, _, _, _, _) =>
|
||||
Attempt.Failure(Err(s"unexpected format for unk1 - $n"))
|
||||
case data =>
|
||||
Attempt.Failure(Err(s"unexpected format for $data"))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import net.psforever.types.Vector3
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* Actions that can be requested of the specific waypoint.
|
||||
*/
|
||||
object WaypointEventAction extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val
|
||||
Add,
|
||||
Unknown1,
|
||||
Remove,
|
||||
Unknown3 //unconfirmed
|
||||
= Value
|
||||
|
||||
implicit val codec : Codec[WaypointEventAction.Value] = PacketHelpers.createEnumerationCodec(enum = this, uint2)
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param zone_number the zone
|
||||
* @param pos the continental map coordinate location of the waypoint;
|
||||
* the z-coordinate is almost always 0.0
|
||||
*/
|
||||
final case class WaypointInfo(zone_number : Int,
|
||||
pos : Vector3)
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param request_type the action to be performed
|
||||
* @param char_id the unique id of player setting the waypoint
|
||||
* @param waypoint_type the waypoint being updated;
|
||||
* 0-3 for the standard squad waypoints numbered "1-4";
|
||||
* 4 for the squad leader experience waypoint;
|
||||
* cycles through 0-3 continuously
|
||||
*
|
||||
* @param unk4 na
|
||||
* @param waypoint_info essential data about the waypoint
|
||||
*/
|
||||
final case class SquadWaypointRequest(request_type : WaypointEventAction.Value,
|
||||
char_id : Long,
|
||||
waypoint_type : Int,
|
||||
unk4 : Option[Long],
|
||||
waypoint_info : Option[WaypointInfo])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = SquadWaypointRequest
|
||||
def opcode = GamePacketOpcode.SquadWaypointRequest
|
||||
def encode = SquadWaypointRequest.encode(this)
|
||||
}
|
||||
|
||||
object SquadWaypointRequest extends Marshallable[SquadWaypointRequest] {
|
||||
def Add(char_id : Long, waypoint_type : Int, 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 =
|
||||
SquadWaypointRequest(WaypointEventAction.Unknown1, char_id, waypoint_type, Some(unk_a), None)
|
||||
|
||||
def Remove(char_id : Long, waypoint_type : Int) : SquadWaypointRequest =
|
||||
SquadWaypointRequest(WaypointEventAction.Remove, char_id, waypoint_type, None, None)
|
||||
|
||||
private val waypoint_codec : Codec[WaypointInfo] = (
|
||||
("zone_number" | uint16L) ::
|
||||
("pos" | Vector3.codec_pos)
|
||||
).xmap[WaypointInfo] (
|
||||
{
|
||||
case zone_number :: pos :: HNil => WaypointInfo(zone_number, pos)
|
||||
},
|
||||
{
|
||||
case WaypointInfo(zone_number, pos) => zone_number :: pos.xy :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
implicit val codec : Codec[SquadWaypointRequest] = (
|
||||
("request_type" | WaypointEventAction.codec) >>:~ { request_type =>
|
||||
("char_id" | uint32L) ::
|
||||
("waypoint_type" | uint8L) ::
|
||||
("unk4" | conditional(request_type == WaypointEventAction.Unknown1, uint32L)) ::
|
||||
("waypoint" | conditional(request_type == WaypointEventAction.Add, waypoint_codec))
|
||||
}
|
||||
).exmap[SquadWaypointRequest] (
|
||||
{
|
||||
case WaypointEventAction.Add :: char_id :: waypoint_type :: None :: Some(waypoint) :: HNil =>
|
||||
Attempt.Successful(SquadWaypointRequest(WaypointEventAction.Add, char_id, waypoint_type, None, Some(waypoint)))
|
||||
|
||||
case WaypointEventAction.Unknown1 :: char_id :: waypoint_type :: Some(d) :: None :: HNil =>
|
||||
Attempt.Successful(SquadWaypointRequest(WaypointEventAction.Unknown1, char_id, waypoint_type, Some(d), None))
|
||||
|
||||
case request_type :: char_id :: waypoint_type :: None :: None :: HNil =>
|
||||
Attempt.Successful(SquadWaypointRequest(request_type, char_id, waypoint_type, None, None))
|
||||
|
||||
case data =>
|
||||
Attempt.Failure(Err(s"unexpected format while decoding - $data"))
|
||||
},
|
||||
{
|
||||
case SquadWaypointRequest(WaypointEventAction.Add, char_id, waypoint_type, None, Some(waypoint)) =>
|
||||
Attempt.Successful(WaypointEventAction.Add :: char_id :: waypoint_type :: None :: Some(waypoint) :: HNil)
|
||||
|
||||
case SquadWaypointRequest(WaypointEventAction.Unknown1, char_id, waypoint_type, Some(d), None) =>
|
||||
Attempt.Successful(WaypointEventAction.Unknown1 :: char_id :: waypoint_type :: Some(d) :: None :: HNil)
|
||||
|
||||
case SquadWaypointRequest(request_type, char_id, waypoint_type, None, None) =>
|
||||
Attempt.Successful(request_type :: char_id :: waypoint_type :: None :: None :: HNil)
|
||||
|
||||
case data : SquadWaypointRequest =>
|
||||
Attempt.Failure(Err(s"unexpected format while encoding - $data"))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package services.teamwork
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.{SquadRequestType, Vector3}
|
||||
|
|
@ -9,7 +8,8 @@ import net.psforever.types.{SquadRequestType, Vector3}
|
|||
object SquadAction {
|
||||
trait Action
|
||||
|
||||
final case class Definition(player : Player, zone : Zone, guid : PlanetSideGUID, line : Int, action : SquadAction) extends Action
|
||||
final case class Definition(zone : Zone, 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 Update(char_id : Long, health : Int, max_health : Int, armor : Int, max_armor : Int, pos : Vector3, zone_number : Int) extends Action
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,5 +29,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 SquadSearchResults() extends Response
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import net.psforever.objects.Player
|
|||
import net.psforever.objects.definition.converter.StatConverter
|
||||
import net.psforever.objects.loadouts.SquadLoadout
|
||||
import net.psforever.objects.teamwork.{Member, Squad}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types._
|
||||
import services.{GenericEventBus, Service}
|
||||
|
|
@ -44,6 +43,8 @@ class SquadService extends Actor {
|
|||
*/
|
||||
private val initialAssociation : ListBuffer[PlanetSideGUID] = new ListBuffer[PlanetSideGUID]()
|
||||
private val queuedInvites : mutable.LongMap[List[Invitation]] = mutable.LongMap[List[Invitation]]()
|
||||
private val waypoints : TrieMap[PlanetSideGUID, Array[WaypointData]] =
|
||||
new TrieMap[PlanetSideGUID, Array[WaypointData]]()
|
||||
private val viewDetails : mutable.LongMap[PlanetSideGUID] = mutable.LongMap[PlanetSideGUID]()
|
||||
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
|
@ -122,7 +123,7 @@ class SquadService extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
def GetLeadingSquad(player : Player, zone : Int, opt : Option[Squad]) : Option[Squad] = {
|
||||
def GetLeadingSquad(player : Player, opt : Option[Squad]) : Option[Squad] = {
|
||||
val charId = player.CharId
|
||||
opt match {
|
||||
case Some(squad) =>
|
||||
|
|
@ -350,80 +351,31 @@ class SquadService extends Actor {
|
|||
RemoveQueuedInvites(invitedPlayer) //TODO deal with these somehow
|
||||
case _ => ;
|
||||
}
|
||||
// if(idToSquad.get(guid).isEmpty) {
|
||||
// log.warn("Accept->Invite: the squad no longer exists")
|
||||
// }
|
||||
// else if(memberToSquad.get(invitedPlayer).nonEmpty) {
|
||||
// log.warn("Accept->Invite: player is already a member of a squad and can not join a second one")
|
||||
// }
|
||||
// else {
|
||||
// val squad = idToSquad(guid)
|
||||
// if(!squad.AutoApproveInvitationRequests && squad.Leader.CharId != invitingPlayer) {
|
||||
// //the inviting player was not the squad leader and this decision should be bounced off the squad leader
|
||||
// val bid = IndirectVacancy(tplayer, guid)
|
||||
// AddInvite(squad.Leader.CharId, bid) match {
|
||||
// case out @ Some(_) if out.contains(bid) =>
|
||||
// HandleBidForPosition(bid, tplayer)
|
||||
// case _ => ;
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// //if a suitable position in the squad can be found, player may occupy it
|
||||
// squad.Membership.zipWithIndex.find({ case (member, index) =>
|
||||
// ValidOpenSquadPosition(squad, index, member, tplayer.Certifications)
|
||||
// }) match {
|
||||
// case Some((_, line)) =>
|
||||
// SquadEvents.publish(SquadServiceResponse(s"/$invitingPlayer/Squad", SquadResponse.Membership(SquadResponseType.Accept, 0, 0, invitingPlayer, Some(invitedPlayer), tplayer.Name, false, Some(None))))
|
||||
// SquadEvents.publish(SquadServiceResponse(s"/$invitedPlayer/Squad", SquadResponse.Membership(SquadResponseType.Accept, 0, 0, invitedPlayer, Some(invitingPlayer), "", true, Some(None))))
|
||||
// JoinSquad(tplayer, squad, line)
|
||||
// RemoveQueuedInvites(invitedPlayer) //TODO deal with these somehow
|
||||
// case _ => ;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
case Some(SpontaneousInvite(invitingPlayer)) =>
|
||||
//we were invited by someone into a new squad they would form
|
||||
//originally, we were invited by someone into a new squad they would form
|
||||
val invitingPlayerCharId = invitingPlayer.CharId
|
||||
(GetParticipatingSquad(invitingPlayer) match {
|
||||
case Some(participating) =>
|
||||
if(participating.Leader.CharId == invitingPlayerCharId) {
|
||||
Some(participating)
|
||||
}
|
||||
else {
|
||||
//inviter joined a squad and is not its leader; bounce this request off of the squad leader
|
||||
participating.Membership.zipWithIndex.find({ case (member, index) =>
|
||||
ValidOpenSquadPosition(participating, index, member, tplayer.Certifications)
|
||||
}) match {
|
||||
case Some((_, line)) =>
|
||||
val bid = BidForPosition(tplayer, participating.GUID, line)
|
||||
AddInvite(participating.Leader.CharId, bid) match {
|
||||
case out @ Some(_) if out.contains(bid) =>
|
||||
HandleBidForPosition(bid, tplayer)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
None
|
||||
}
|
||||
case None =>
|
||||
//invitingPlayer became part of a squad while invited player was answering the original summons
|
||||
Some(participating)
|
||||
case _ =>
|
||||
//generate a new squad, with invitingPlayer as the leader
|
||||
val squad = StartSquad(invitingPlayer)
|
||||
squad.Task = s"${tplayer.Name}'s Squad"
|
||||
SquadEvents.publish(SquadServiceResponse(s"/$invitingPlayer/Squad", SquadResponse.AssociateWithSquad(squad.GUID)))
|
||||
squad.Task = s"${invitingPlayer.Name}'s Squad"
|
||||
SquadEvents.publish( SquadServiceResponse(s"/$invitingPlayerCharId/Squad", SquadResponse.AssociateWithSquad(squad.GUID)) )
|
||||
Some(squad)
|
||||
}) match {
|
||||
case Some(squad) =>
|
||||
squad.Membership.zipWithIndex.find({ case (member, index) =>
|
||||
ValidOpenSquadPosition(squad, index, member, tplayer.Certifications)
|
||||
}) match {
|
||||
HandleVacancyInvite(squad.GUID, tplayer.CharId, invitingPlayerCharId, tplayer) match {
|
||||
case Some((_, line)) =>
|
||||
SquadEvents.publish( SquadServiceResponse(s"/$invitedPlayer/Squad", SquadResponse.Membership(SquadResponseType.Accept, 0, 0, invitedPlayer, Some(invitingPlayerCharId), "", true, Some(None))) )
|
||||
JoinSquad(tplayer, squad, line)
|
||||
SquadEvents.publish( SquadServiceResponse(s"/$invitingPlayerCharId/Squad", SquadResponse.Membership(SquadResponseType.Accept, 0, 0, invitingPlayerCharId, Some(invitedPlayer), tplayer.Name, false, Some(None))) )
|
||||
RemoveQueuedInvites(invitedPlayer) //TODO deal with these somehow
|
||||
RemoveQueuedInvites(tplayer.CharId) //TODO deal with these somehow
|
||||
case _ => ;
|
||||
}
|
||||
case None => ;
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case None =>
|
||||
|
|
@ -566,32 +518,46 @@ class SquadService extends Actor {
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
case SquadAction.Update(char_id, health, max_health, armor, max_armor, pos, zone_number) =>
|
||||
memberToSquad.get(char_id) match {
|
||||
case SquadAction.Waypoint(_, wtype, _, info) =>
|
||||
val playerCharId = tplayer.CharId
|
||||
(GetLeadingSquad(tplayer, None) match {
|
||||
case Some(squad) =>
|
||||
squad.Membership.find(_.CharId == char_id) match {
|
||||
case Some(member) =>
|
||||
member.Health = StatConverter.Health(health, max_health, min=1, max=64)
|
||||
member.Armor = StatConverter.Health(armor, max_armor, min=1, max=64)
|
||||
member.Position = pos
|
||||
member.ZoneId = zone_number
|
||||
sender ! SquadServiceResponse("", SquadResponse.UpdateMembers(
|
||||
squad,
|
||||
squad.Membership
|
||||
.filterNot { _.CharId == 0 }
|
||||
.map { member => SquadAction.Update(member.CharId, member.Health, 0, member.Armor, 0, member.Position, member.ZoneId) }
|
||||
.toList
|
||||
))
|
||||
case _ => ;
|
||||
info match {
|
||||
case Some(winfo) =>
|
||||
(Some(squad), AddWaypoint(squad.GUID, wtype, winfo))
|
||||
case _ =>
|
||||
RemoveWaypoint(squad.GUID, wtype)
|
||||
(Some(squad), None)
|
||||
}
|
||||
|
||||
case None => ;
|
||||
case _ => (None, None)
|
||||
}) match {
|
||||
case (Some(squad), Some(waypoint)) =>
|
||||
//waypoint added or updated
|
||||
squad.Membership
|
||||
.filterNot { member => member.CharId == tplayer.CharId }
|
||||
.foreach { member =>
|
||||
val charId = member.CharId
|
||||
SquadEvents.publish(
|
||||
SquadServiceResponse(s"/$charId/Squad", SquadResponse.WaypointEvent(WaypointEventAction.Add, playerCharId, wtype, None, info, 1))
|
||||
)
|
||||
}
|
||||
case (Some(squad), None) =>
|
||||
//waypoint removed?
|
||||
squad.Membership
|
||||
.filterNot { member => member.CharId == tplayer.CharId }
|
||||
.foreach { member =>
|
||||
val charId = member.CharId
|
||||
SquadEvents.publish(
|
||||
SquadServiceResponse(s"/$charId/Squad", SquadResponse.WaypointEvent(WaypointEventAction.Remove, playerCharId, wtype, None, None, 0))
|
||||
)
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case SquadAction.Definition(tplayer : Player, zone : Zone, guid : PlanetSideGUID, line : Int, action : SquadAction) =>
|
||||
case SquadAction.Definition(zone, guid, line, action) =>
|
||||
import net.psforever.packet.game.SquadAction._
|
||||
val pSquadOpt = GetParticipatingSquad(tplayer)
|
||||
val lSquadOpt = GetLeadingSquad(tplayer, zone.Number, pSquadOpt)
|
||||
val lSquadOpt = GetLeadingSquad(tplayer, pSquadOpt)
|
||||
//the following actions can only be performed by a squad's leader
|
||||
action match {
|
||||
case SaveSquadFavorite() =>
|
||||
|
|
@ -624,17 +590,17 @@ class SquadService extends Actor {
|
|||
UpdateSquadListWhenListed(squad, SquadInfo().Task(purpose))
|
||||
UpdateSquadDetail(squad.GUID, squad, SquadDetail().Task(purpose))
|
||||
|
||||
case ChangeSquadZone(zone) =>
|
||||
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed squad's ops zone to $zone")
|
||||
case ChangeSquadZone(zone_id) =>
|
||||
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed squad's ops zone to $zone_id")
|
||||
val squad = lSquadOpt.getOrElse(StartSquad(tplayer))
|
||||
squad.ZoneId = zone.zoneId.toInt
|
||||
UpdateSquadListWhenListed(squad, SquadInfo().ZoneId(zone))
|
||||
squad.ZoneId = zone_id.zoneId.toInt
|
||||
UpdateSquadListWhenListed(squad, SquadInfo().ZoneId(zone_id))
|
||||
InitialAssociation(squad)
|
||||
sender ! SquadServiceResponse("", SquadResponse.Detail(
|
||||
squad.GUID,
|
||||
SquadService.Detail.Publish(squad))
|
||||
)
|
||||
UpdateSquadDetail(squad.GUID, squad.Membership.map { _m => _m.CharId }.filterNot { _ == squad.Leader.CharId }, SquadDetail().ZoneId(zone))
|
||||
UpdateSquadDetail(squad.GUID, squad.Membership.map { _m => _m.CharId }.filterNot { _ == squad.Leader.CharId }, SquadDetail().ZoneId(zone_id))
|
||||
|
||||
case CloseSquadMemberPosition(position) =>
|
||||
val squad = lSquadOpt.getOrElse(StartSquad(tplayer))
|
||||
|
|
@ -906,6 +872,12 @@ class SquadService extends Actor {
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
//the following action can be peprformed by anyone
|
||||
case (_, SearchForSquadsWithParticularRole(role, requirements, zone_id, search_mode)) =>
|
||||
//though we should be able correctly search squads as is intended
|
||||
//I don't know how search results should be prioritized or even how to return search results to the user
|
||||
sender ! SquadServiceResponse("", SquadResponse.SquadSearchResults())
|
||||
|
||||
//the following action can be performed by anyone
|
||||
case (_, DisplaySquad()) =>
|
||||
idToSquad.get(guid) match {
|
||||
|
|
@ -926,6 +898,28 @@ class SquadService extends Actor {
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
case SquadAction.Update(char_id, health, max_health, armor, max_armor, pos, zone_number) =>
|
||||
memberToSquad.get(char_id) match {
|
||||
case Some(squad) =>
|
||||
squad.Membership.find(_.CharId == char_id) match {
|
||||
case Some(member) =>
|
||||
member.Health = StatConverter.Health(health, max_health, min=1, max=64)
|
||||
member.Armor = StatConverter.Health(armor, max_armor, min=1, max=64)
|
||||
member.Position = pos
|
||||
member.ZoneId = zone_number
|
||||
sender ! SquadServiceResponse("", SquadResponse.UpdateMembers(
|
||||
squad,
|
||||
squad.Membership
|
||||
.filterNot { _.CharId == 0 }
|
||||
.map { member => SquadAction.Update(member.CharId, member.Health, 0, member.Armor, 0, member.Position, member.ZoneId) }
|
||||
.toList
|
||||
))
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case None => ;
|
||||
}
|
||||
|
||||
case msg =>
|
||||
log.info(s"Unhandled message $msg from $sender")
|
||||
}
|
||||
|
|
@ -1146,6 +1140,7 @@ class SquadService extends Actor {
|
|||
.filterNot { _.CharId == 0 }
|
||||
.foreach { member =>
|
||||
SquadEvents.publish(SquadServiceResponse(s"/${member.CharId}/Squad", SquadResponse.Join(squad, indices)))
|
||||
InitWaypoints(member.CharId, squad.GUID)
|
||||
}
|
||||
//fully update for all users
|
||||
UpdateSquadDetail(squad.GUID, squad)
|
||||
|
|
@ -1156,6 +1151,7 @@ class SquadService extends Actor {
|
|||
val indices = squad.Membership.zipWithIndex.collect({ case (member, index) if member.CharId != 0 => index }).toList
|
||||
SquadEvents.publish(SquadServiceResponse(s"/$charId/Squad", SquadResponse.Join(squad, indices)))
|
||||
InitSquadDetail(squad.GUID, Seq(charId), squad)
|
||||
InitWaypoints(charId, squad.GUID)
|
||||
//other squad members see new member joining the squad
|
||||
val updatedIndex = List(line)
|
||||
val otherMembers = squad.Membership.filterNot { member => member.CharId == 0 || member.CharId == charId }.map{ _.CharId }
|
||||
|
|
@ -1163,7 +1159,7 @@ class SquadService extends Actor {
|
|||
SquadEvents.publish(SquadServiceResponse(s"/$member/Squad", SquadResponse.Join(squad, updatedIndex)))
|
||||
}
|
||||
val details = SquadDetail().Members(List(SquadPositionEntry(line, SquadPositionDetail().CharId(charId).Name(player.Name))))
|
||||
UpdateSquadDetail(squad.GUID, Seq(charId), details)
|
||||
UpdateSquadDetail(squad.GUID, otherMembers, details)
|
||||
}
|
||||
UpdateSquadListWhenListed(squad, SquadInfo().Size(size))
|
||||
true
|
||||
|
|
@ -1207,7 +1203,6 @@ class SquadService extends Actor {
|
|||
}
|
||||
|
||||
def CloseOutSquad(squad : Squad, membership : Iterable[(Member, Int)], updateList : List[(Long, Int)]) : Unit = {
|
||||
val leaderCharId = squad.Leader.CharId
|
||||
membership.foreach {
|
||||
case (member, _) =>
|
||||
val charId = member.CharId
|
||||
|
|
@ -1217,6 +1212,7 @@ class SquadService extends Actor {
|
|||
SquadEvents.publish( SquadServiceResponse(s"/$charId/Squad", SquadResponse.Leave(squad, updateList)) )
|
||||
}
|
||||
idToSquad.remove(squad.GUID)
|
||||
waypoints.remove(squad.GUID)
|
||||
UpdateSquadList(squad, None)
|
||||
}
|
||||
|
||||
|
|
@ -1332,9 +1328,70 @@ class SquadService extends Actor {
|
|||
toMembers.foreach { charId => SquadEvents.publish(SquadServiceResponse(s"/$charId/Squad", output)) }
|
||||
}
|
||||
}
|
||||
|
||||
def AddWaypoint(guid : PlanetSideGUID, waypointType : Int, info : WaypointInfo) : Option[WaypointData] = {
|
||||
(waypoints.get(guid) match {
|
||||
case Some(array) =>
|
||||
array
|
||||
case None if idToSquad.get(guid).nonEmpty =>
|
||||
log.debug(s"initializing squad waypoint system for squad #${guid.guid}")
|
||||
val array = Array.fill[WaypointData](5)(new WaypointData())
|
||||
waypoints(guid) = array
|
||||
array
|
||||
case _ =>
|
||||
log.warn(s"squad #${guid.guid} does not currently exist so it can not render waypoints")
|
||||
Array.empty[WaypointData]
|
||||
}).lift(waypointType) match {
|
||||
case Some(point) =>
|
||||
//update the waypoint
|
||||
log.debug(s"rendering squad waypoint $waypointType for squad #${guid.guid}")
|
||||
point.zone_number = info.zone_number
|
||||
point.pos = info.pos
|
||||
Some(point)
|
||||
case _ =>
|
||||
log.warn(s"no squad waypoint $waypointType found")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def RemoveWaypoint(guid : PlanetSideGUID, waypointType : Int) : Unit = {
|
||||
(waypoints.get(guid) match {
|
||||
case Some(array) =>
|
||||
array
|
||||
case None =>
|
||||
Array.empty[WaypointData]
|
||||
}).lift(waypointType) match {
|
||||
case Some(point) =>
|
||||
//update the waypoint
|
||||
log.debug(s"removing squad waypoint $waypointType for squad #${guid.guid}")
|
||||
point.zone_number = 1
|
||||
point.pos = Vector3.z(1)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
||||
def InitWaypoints(toCharId : Long, guid : PlanetSideGUID) : Unit = {
|
||||
(idToSquad.get(guid), waypoints.get(guid)) match {
|
||||
case (Some(squad), Some(list)) =>
|
||||
val vz1 = Vector3.z(1)
|
||||
SquadEvents.publish(
|
||||
SquadServiceResponse(s"/$toCharId/Squad", SquadResponse.InitWaypoints(squad.Leader.CharId,
|
||||
list.zipWithIndex.collect { case (point, index) if point.pos != vz1 =>
|
||||
(index, WaypointInfo(point.zone_number, point.pos), 1)
|
||||
}
|
||||
))
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SquadService {
|
||||
class WaypointData() {
|
||||
var zone_number : Int = 1
|
||||
var pos : Vector3 = Vector3.z(1) //a waypoint with a non-zero z-coordinate will flag as not getting drawn
|
||||
}
|
||||
|
||||
abstract class Invitation(char_id : Long, name : String) {
|
||||
def InviterCharId : Long = char_id
|
||||
def InviterName : String = name
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package game
|
|||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.{SquadWaypointEvent, WaypointEvent}
|
||||
import net.psforever.packet.game.{SquadWaypointEvent, WaypointEventAction, WaypointEvent}
|
||||
import net.psforever.types.Vector3
|
||||
import scodec.bits._
|
||||
|
||||
|
|
@ -16,12 +16,12 @@ class SquadWaypointEventTest extends Specification {
|
|||
"decode (1)" in {
|
||||
PacketCoding.DecodePacket(string_1).require match {
|
||||
case SquadWaypointEvent(unk1, unk2, unk3, unk4, unk5, unk6) =>
|
||||
unk1 mustEqual 2
|
||||
unk1 mustEqual WaypointEventAction.Remove
|
||||
unk2 mustEqual 11
|
||||
unk3 mustEqual 31155863L
|
||||
unk4 mustEqual 0
|
||||
unk5 mustEqual None
|
||||
unk6 mustEqual None
|
||||
unk5.isEmpty mustEqual true
|
||||
unk6.isEmpty mustEqual true
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -30,12 +30,12 @@ class SquadWaypointEventTest extends Specification {
|
|||
"decode (2)" in {
|
||||
PacketCoding.DecodePacket(string_2).require match {
|
||||
case SquadWaypointEvent(unk1, unk2, unk3, unk4, unk5, unk6) =>
|
||||
unk1 mustEqual 2
|
||||
unk1 mustEqual WaypointEventAction.Remove
|
||||
unk2 mustEqual 10
|
||||
unk3 mustEqual 0L
|
||||
unk4 mustEqual 4
|
||||
unk5 mustEqual None
|
||||
unk6 mustEqual None
|
||||
unk5.isEmpty mustEqual true
|
||||
unk6.isEmpty mustEqual true
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -44,12 +44,12 @@ class SquadWaypointEventTest extends Specification {
|
|||
"decode (3)" in {
|
||||
PacketCoding.DecodePacket(string_3).require match {
|
||||
case SquadWaypointEvent(unk1, unk2, unk3, unk4, unk5, unk6) =>
|
||||
unk1 mustEqual 0
|
||||
unk1 mustEqual WaypointEventAction.Add
|
||||
unk2 mustEqual 3
|
||||
unk3 mustEqual 41581052L
|
||||
unk4 mustEqual 1
|
||||
unk5 mustEqual None
|
||||
unk6 mustEqual Some(WaypointEvent(10, Vector3(3457.9688f, 5514.4688f, 0.0f), 1))
|
||||
unk5.isEmpty mustEqual true
|
||||
unk6.contains( WaypointEvent(10, Vector3(3457.9688f, 5514.4688f, 0.0f), 1) ) mustEqual true
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -58,40 +58,40 @@ class SquadWaypointEventTest extends Specification {
|
|||
"decode (4)" in {
|
||||
PacketCoding.DecodePacket(string_4).require match {
|
||||
case SquadWaypointEvent(unk1, unk2, unk3, unk4, unk5, unk6) =>
|
||||
unk1 mustEqual 1
|
||||
unk1 mustEqual WaypointEventAction.Unknown1
|
||||
unk2 mustEqual 3
|
||||
unk3 mustEqual 41581052L
|
||||
unk4 mustEqual 1
|
||||
unk5 mustEqual Some(4L)
|
||||
unk6 mustEqual None
|
||||
unk5.contains( 4L ) mustEqual true
|
||||
unk6.isEmpty mustEqual true
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode (1)" in {
|
||||
val msg = SquadWaypointEvent(2, 11, 31155863L, 0)
|
||||
val msg = SquadWaypointEvent.Remove(11, 31155863L, 0)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_1
|
||||
}
|
||||
|
||||
"encode (2)" in {
|
||||
val msg = SquadWaypointEvent(2, 10, 0L, 4)
|
||||
val msg = SquadWaypointEvent.Remove(10, 0L, 4)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_2
|
||||
}
|
||||
|
||||
"encode (3)" in {
|
||||
val msg = SquadWaypointEvent(0, 3, 41581052L, 1, 10, Vector3(3457.9688f, 5514.4688f, 0.0f), 1)
|
||||
val msg = SquadWaypointEvent.Add(3, 41581052L, 1, 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(1, 3, 41581052L, 1, 4L)
|
||||
val msg = SquadWaypointEvent.Unknown1(3, 41581052L, 1, 4L)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_4
|
||||
|
|
|
|||
|
|
@ -516,15 +516,32 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
log.info(s"SquadCards: ${squadUI.map { case(id, card) => s"[${card.index}:${card.name}/$id]" }.mkString}")
|
||||
|
||||
case SquadResponse.PromoteMember(squad, char_id, from_index, to_index) =>
|
||||
//promotion will swap visual cards, but we must fix the backend
|
||||
//promotion will swap cards visually, but we must fix the backend
|
||||
val id = 11
|
||||
sendResponse(SquadMemberEvent.Promote(id, char_id))
|
||||
SwapSquadUIElements(squad, from_index, to_index)
|
||||
log.info(s"SquadCards: ${squadUI.map { case(id, card) => s"[${card.index}:${card.name}/$id]" }.mkString}")
|
||||
|
||||
case SquadResponse.SquadSearchResults() =>
|
||||
//I don't actually know how to return search results
|
||||
sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), 0, SquadAction.NoSquadSearchResults()))
|
||||
|
||||
case SquadResponse.InitWaypoints(char_id, waypoints) =>
|
||||
val id = 11
|
||||
StartBundlingPackets()
|
||||
waypoints.foreach { case (waypoint_type, info, unk) =>
|
||||
sendResponse(SquadWaypointEvent.Add(id, char_id, waypoint_type, WaypointEvent(info.zone_number, info.pos, unk)))
|
||||
}
|
||||
StopBundlingPackets()
|
||||
|
||||
case SquadResponse.WaypointEvent(WaypointEventAction.Add, char_id, waypoint_type, _, Some(info), unk) =>
|
||||
val id = 11
|
||||
sendResponse(SquadWaypointEvent.Add(id, char_id, waypoint_type, WaypointEvent(info.zone_number, info.pos, unk)))
|
||||
|
||||
case SquadResponse.WaypointEvent(WaypointEventAction.Remove, char_id, waypoint_type, _, _, _) =>
|
||||
val id = 11
|
||||
sendResponse(SquadWaypointEvent.Remove(id, char_id, waypoint_type))
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
|
|
@ -4960,12 +4977,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ SquadDefinitionActionMessage(u1, u2, action) =>
|
||||
log.info(s"SquadDefinitionAction: $msg")
|
||||
squadService ! SquadServiceMessage(player, SquadServiceAction.Definition(player, continent, u1, u2, action))
|
||||
squadService ! SquadServiceMessage(player, SquadServiceAction.Definition(continent, u1, u2, action))
|
||||
|
||||
case msg @ SquadMembershipRequest(request_type, unk2, unk3, player_name, unk5) =>
|
||||
log.info(s"$msg")
|
||||
squadService ! SquadServiceMessage(player, SquadServiceAction.Membership(request_type, unk2, unk3, player_name, unk5))
|
||||
|
||||
case msg @ SquadWaypointRequest(request, _, wtype, unk, info) =>
|
||||
log.info(s"Waypoint Request: $msg")
|
||||
squadService ! SquadServiceMessage(player, SquadServiceAction.Waypoint(request, wtype, unk, info))
|
||||
|
||||
case msg @ GenericCollisionMsg(u1, p, t, php, thp, pv, tv, ppos, tpos, u2, u3, u4) =>
|
||||
log.info("Ouch! " + msg)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue