sufficient functionality that allows the UI for squads to display properly on squad joining and on squad leaving; clarification for SquadMemberEvent fields; QoL support functionality intergrated into SDDUM streams

This commit is contained in:
FateJH 2019-07-08 23:04:57 -04:00
parent 2a1b051710
commit 60a11b8c52
9 changed files with 456 additions and 309 deletions

View file

@ -82,12 +82,12 @@ class Squad(squadId : PlanetSideGUID, alignment : PlanetSideEmpire.Value) extend
LeaderPositionIndex
}
def Leader : String = {
membership.lift(leaderPositionIndex) match {
case Some(member) =>
member.Name
case None =>
""
def Leader : Member = {
membership(leaderPositionIndex) match {
case member if !member.Name.equals("") =>
member
case _ =>
throw new Exception("can not find squad leader!")
}
}

View file

@ -198,17 +198,16 @@ final case class SquadDetail(unk1 : Option[Int],
leader_name.orElse(Some("")),
task.orElse(Some("")),
zone_id.orElse(Some(PlanetSideZoneID(0))),
unk7.orElse(Some(0)),
unk7.orElse(Some(4983296)), //FullSquad value
{
val complete = SquadPositionDetail().Complete
Some(member_info match {
case Some(info) =>
//create one list that ensures all existing positions are "complete" then add a list of the missing indices
val fields = info.collect {
case SquadPositionEntry(a, Some(b)) => SquadPositionEntry(a, b.Complete)
case out @ SquadPositionEntry(_, None) => out
}
val indices = info.map { case SquadPositionEntry(a, _) => a }
val (indices, fields) = info.collect {
case SquadPositionEntry(a, Some(b)) => (a, SquadPositionEntry(a, b.Complete))
case out @ SquadPositionEntry(a, None) => (a, out)
}.unzip
((0 to 9).toSet.diff(indices.toSet).map { SquadPositionEntry(_, complete) } ++ fields).toList.sortBy(_.index)
case None =>
//original list
@ -585,7 +584,6 @@ object SquadDetailDefinitionUpdateMessage extends Marshallable[SquadDetailDefini
private def membersCodec(bitsOverByte : StreamLengthToken) : Codec[SquadDetail] = {
import shapeless.::
(
//TODO you can replace this outer structure with an either Codec
bool >>:~ { flag =>
conditional(flag, {
bitsOverByte.Add(4)
@ -593,15 +591,15 @@ object SquadDetailDefinitionUpdateMessage extends Marshallable[SquadDetailDefini
}) ::
conditional(!flag, {
bitsOverByte.Add(3)
uint(2) :: FullyPopulatedPositions.codec(bitsOverByte)
uint2 :: FullyPopulatedPositions.codec(bitsOverByte)
})
}
).exmap[SquadDetail] (
{
case true :: Some(_ :: member_list :: HNil) :: _ :: HNil =>
Attempt.successful(SquadDetail(None, None, None, None, None, None, None, None, Some(member_list.toList)))
Attempt.successful(SquadDetail(None, None, None, None, None, None, None, None, Some(ignoreTerminatingEntry(member_list.toList))))
case false :: None :: Some(_ :: member_list :: HNil) :: HNil =>
Attempt.successful(SquadDetail(None, None, None, None, None, None, None, None, Some(member_list.toList)))
Attempt.successful(SquadDetail(None, None, None, None, None, None, None, None, Some(ignoreTerminatingEntry(member_list.toList))))
},
{
case SquadDetail(_, _, _, _, _, _, _, _, Some(member_list)) =>
@ -612,16 +610,54 @@ object SquadDetailDefinitionUpdateMessage extends Marshallable[SquadDetailDefini
}
.flatten
.count(_.isEmpty) == 0) {
Attempt.successful(false :: None :: Some(2 :: member_list.toVector :: HNil) :: HNil)
Attempt.successful(false :: None :: Some(2 :: ensureTerminatingEntry(member_list).toVector :: HNil) :: HNil)
}
else {
Attempt.successful(true :: Some(4 :: member_list.toVector :: HNil) :: None :: HNil)
Attempt.successful(true :: Some(4 :: ensureTerminatingEntry(member_list).toVector :: HNil) :: None :: HNil)
}
case _ =>
Attempt.failure(Err("failed to encode squad data for members"))
}
)
}
//TODO while this pattern looks elegant, bitsOverByte does not accumulate properly with the either(bool, L, R); why?
// private def membersCodec(bitsOverByte : StreamLengthToken) : Codec[SquadDetail] = {
// import shapeless.::
// either(bool,
// { //false
// bitsOverByte.Add(3)
// uint2 :: FullyPopulatedPositions.codec(bitsOverByte)
// },
// { //true
// bitsOverByte.Add(4)
// uint(3) :: vector(ItemizedPositions.codec(bitsOverByte))
// }
// ).exmap[SquadDetail] (
// {
// case Left(_ :: member_list :: HNil) =>
// Attempt.successful(SquadDetail(None, None, None, None, None, None, None, None, Some(ignoreTerminatingEntry(member_list.toList))))
// case Right(_ :: member_list :: HNil) =>
// Attempt.successful(SquadDetail(None, None, None, None, None, None, None, None, Some(ignoreTerminatingEntry(member_list.toList))))
// },
// {
// case SquadDetail(_, _, _, _, _, _, _, _, Some(member_list)) =>
// if(member_list
// .collect { case position if position.info.nonEmpty =>
// val info = position.info.get
// List(info.is_closed, info.role, info.detailed_orders, info.requirements, info.char_id, info.name)
// }
// .flatten
// .count(_.isEmpty) == 0) {
// Attempt.successful(Left(2 :: ensureTerminatingEntry(member_list).toVector :: HNil))
// }
// else {
// Attempt.successful(Right(4 :: ensureTerminatingEntry(member_list).toVector :: HNil))
// }
// case _ =>
// Attempt.failure(Err("failed to encode squad data for members"))
// }
// )
// }
/**
* A failing pattern for when the coded value is not tied to a known field pattern.
* This pattern does not read or write any bit data.
@ -1245,10 +1281,9 @@ object SquadDetailDefinitionUpdateMessage extends Marshallable[SquadDetailDefini
}).xmap[Vector[SquadPositionEntry]] (
{
case _ :: _ :: linkedMembers :: HNil =>
unlinkFields(linkedMembers).toVector
ignoreTerminatingEntry(unlinkFields(linkedMembers)).toVector
},
//TODO "memberList.size - 1"? the only two examples are "10" anyway
memberList => memberList.size - 1 :: 12 :: linkFields(memberList.reverse.toList) :: HNil
memberList => 10 :: 12 :: linkFields(ensureTerminatingEntry(memberList.toList).reverse) :: HNil
)
}
}
@ -1320,6 +1355,32 @@ object SquadDetailDefinitionUpdateMessage extends Marshallable[SquadDetailDefini
)
}
/**
* The last entry in the sequence of squad information listings should be a dummied listing with an index of 255.
* Ensure that this terminal entry is located at the end.
* @param list the listing of squad information
* @return the listing of squad information, with a specific final entry
*/
private def ensureTerminatingEntry(list : List[SquadPositionEntry]) : List[SquadPositionEntry] = {
list.lastOption match {
case Some(SquadPositionEntry(255, _)) => list
case Some(_) | None => list :+ SquadPositionEntry(255, None)
}
}
/**
* The last entry in the sequence of squad information listings should be a dummied listing with an index of 255.
* Remove this terminal entry from the end of the list so as not to hassle with it.
* @param list the listing of squad information
* @return the listing of squad information, with a specific final entry truncated
*/
private def ignoreTerminatingEntry(list : List[SquadPositionEntry]) : List[SquadPositionEntry] = {
list.lastOption match {
case Some(SquadPositionEntry(255, _)) => list.init
case Some(_) | None => list
}
}
implicit val codec : Codec[SquadDetailDefinitionUpdateMessage] = {
import shapeless.::
import net.psforever.newcodecs.newcodecs

View file

@ -8,10 +8,10 @@ import shapeless.{::, HNil}
final case class SquadMemberEvent(unk1 : Int,
unk2 : Int,
unk3 : Long,
unk4 : Int,
unk5 : Option[String],
unk6 : Option[Int],
char_id : Long,
member_position : Int,
player_name : Option[String],
zone_number : Option[Int],
unk7 : Option[Long])
extends PlanetSideGamePacket {
type Packet = SquadMemberEvent
@ -20,40 +20,40 @@ final case class SquadMemberEvent(unk1 : Int,
}
object SquadMemberEvent extends Marshallable[SquadMemberEvent] {
def apply(unk1 : Int, unk2 : Int, unk3 : Long, unk4 : Int) : SquadMemberEvent =
SquadMemberEvent(unk1, unk2, unk3, unk4, None, None, None)
def apply(unk1 : Int, unk2 : Int, char_id : Long, member_position : Int) : SquadMemberEvent =
SquadMemberEvent(unk1, unk2, char_id, member_position, None, None, None)
def apply(unk2 : Int, unk3 : Long, unk4 : Int, unk5 : String, unk6 : Int, unk7 : Long) : SquadMemberEvent =
SquadMemberEvent(0, unk2, unk3, unk4, Some(unk5), Some(unk6), Some(unk7))
def apply(unk2 : Int, char_id : Long, member_position : Int, player_name : String, zone_number : Int, unk7 : Long) : SquadMemberEvent =
SquadMemberEvent(0, unk2, char_id, member_position, Some(player_name), Some(zone_number), Some(unk7))
def apply(unk2 : Int, unk3 : Long, unk4 : Int, unk6 : Int) : SquadMemberEvent =
SquadMemberEvent(3, unk2, unk3, unk4, None, Some(unk6), None)
def apply(unk2 : Int, char_id : Long, member_position : Int, zone_number : Int) : SquadMemberEvent =
SquadMemberEvent(3, unk2, char_id, member_position, None, Some(zone_number), None)
def apply(unk2 : Int, unk3 : Long, unk4 : Int, unk7 : Long) : SquadMemberEvent =
SquadMemberEvent(4, unk2, unk3, unk4, None, None, Some(unk7))
def apply(unk2 : Int, char_id : Long, member_position : Int, unk7 : Long) : SquadMemberEvent =
SquadMemberEvent(4, unk2, char_id, member_position, None, None, Some(unk7))
implicit val codec : Codec[SquadMemberEvent] = (
("unk1" | uintL(3)) >>:~ { unk1 =>
("unk1" | uint(3)) >>:~ { unk1 =>
("unk2" | uint16L) ::
("unk3" | uint32L) ::
("unk4" | uintL(4)) ::
conditional(unk1 == 0, "unk5" | PacketHelpers.encodedWideStringAligned(1)) ::
conditional(unk1 == 0 || unk1 == 3, "unk6" | uint16L) ::
("char_id" | uint32L) ::
("member_position" | uint4) ::
conditional(unk1 == 0, "player_name" | PacketHelpers.encodedWideStringAligned(1)) ::
conditional(unk1 == 0 || unk1 == 3, "zone_number" | uint16L) ::
conditional(unk1 == 0 || unk1 == 4, "unk7" | uint32L)
}).exmap[SquadMemberEvent] (
{
case unk1 :: unk2 :: unk3 :: unk4 :: unk5 :: unk6 :: unk7 :: HNil =>
Attempt.Successful(SquadMemberEvent(unk1, unk2, unk3, unk4, unk5, unk6, unk7))
case unk1 :: unk2 :: char_id :: member_position :: player_name :: zone_number :: unk7 :: HNil =>
Attempt.Successful(SquadMemberEvent(unk1, unk2, char_id, member_position, player_name, zone_number, unk7))
},
{
case data @ SquadMemberEvent(0, unk2, unk3, unk4, Some(unk5), Some(unk6), Some(unk7)) =>
Attempt.Successful(0 :: unk2 :: unk3 :: unk4 :: Some(unk5) :: Some(unk6) :: Some(unk7) :: HNil)
case data @ SquadMemberEvent(3, unk2, unk3, unk4, None, Some(unk6), None) =>
Attempt.Successful(3 :: unk2 :: unk3 :: unk4 :: None :: Some(unk6) :: None :: HNil)
case data @ SquadMemberEvent(4, unk2, unk3, unk4, None, None, Some(unk7)) =>
Attempt.Successful(4 :: unk2 :: unk3 :: unk4 :: None :: None :: Some(unk7) :: HNil)
case data @ SquadMemberEvent(unk1, unk2, unk3, unk4, None, None, None) =>
Attempt.Successful(unk1 :: unk2 :: unk3 :: unk4 :: None :: None :: None :: HNil)
case data @ SquadMemberEvent(0, unk2, char_id, member_position, Some(player_name), Some(zone_number), Some(unk7)) =>
Attempt.Successful(0 :: unk2 :: char_id :: member_position :: Some(player_name) :: Some(zone_number) :: Some(unk7) :: HNil)
case data @ SquadMemberEvent(3, unk2, char_id, member_position, None, Some(zone_number), None) =>
Attempt.Successful(3 :: unk2 :: char_id :: member_position :: None :: Some(zone_number) :: None :: HNil)
case data @ SquadMemberEvent(4, unk2, char_id, member_position, None, None, Some(unk7)) =>
Attempt.Successful(4 :: unk2 :: char_id :: member_position :: None :: None :: Some(unk7) :: HNil)
case data @ SquadMemberEvent(unk1, unk2, char_id, member_position, None, None, None) =>
Attempt.Successful(unk1 :: unk2 :: char_id :: member_position :: None :: None :: None :: HNil)
case data =>
Attempt.Failure(Err(s"SquadMemberEvent can not encode with this pattern - $data"))
}

View file

@ -0,0 +1,13 @@
// Copyright (c) 2019 PSForever
package services.teamwork
import net.psforever.objects.Player
import net.psforever.packet.game._
import net.psforever.types.SquadRequestType
object SquadAction {
trait 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 Definition(player : Player, zone_ordinal_number : Int, guid : PlanetSideGUID, line : Int, action : SquadAction)
}

View file

@ -1,7 +1,9 @@
// Copyright (c) 2019 PSForever
package services.teamwork
import net.psforever.objects.teamwork.{Member, Squad}
import net.psforever.packet.game._
import net.psforever.types.SquadRequestType
object SquadResponse {
trait Response
@ -10,5 +12,9 @@ object SquadResponse {
final case class Update(infos : Iterable[(Int, SquadInfo)]) extends Response
final case class Remove(infos : Iterable[Int]) extends Response
final case class Membership(request_type : SquadRequestType.Value, unk1 : Int, unk2 : Int, unk3 : Long, unk4 : Option[Long], player_name : String, unk5 : Boolean, unk6 : Option[Option[String]]) extends Response //see SquadMembershipResponse
final case class Join(squad : Squad, positionsToUpdate : List[Int]) extends Response
final case class Leave(squad : Squad, positionsToUpdate : List[(Long, Int)]) extends Response
final case class Detail(guid : PlanetSideGUID, leader : String, task : String, zone : PlanetSideZoneID, member_info : List[SquadPositionDetail]) extends Response
}

View file

@ -3,12 +3,13 @@ package services.teamwork
import akka.actor.Actor
import net.psforever.objects.Player
import net.psforever.objects.teamwork.{Member, Squad}
import net.psforever.objects.teamwork.Squad
import net.psforever.packet.game._
import net.psforever.types.{PlanetSideEmpire, Vector3}
import net.psforever.types.{PlanetSideEmpire, SquadRequestType, Vector3}
import services.{GenericEventBus, Service}
import scala.collection.concurrent.TrieMap
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
//import scala.concurrent.duration._
@ -22,11 +23,35 @@ class SquadService extends Actor {
PlanetSideEmpire.NC -> ListBuffer.empty,
PlanetSideEmpire.VS -> ListBuffer.empty
)
private val bids : mutable.LongMap[(PlanetSideGUID, Int)] = mutable.LongMap[(PlanetSideGUID, Int)]()
private [this] val log = org.log4s.getLogger
override def preStart = {
log.info("Starting...")
val testSquad = new Squad(PlanetSideGUID(3), PlanetSideEmpire.VS)
testSquad.Task = "\\#66CCFF Sentinels of Auraxis\\#FFFFFF ... \\#40FF40 Squad Up!!"
testSquad.ZoneId = 5
testSquad.Membership(0).Name = "Wizkid45"
testSquad.Membership(0).CharId = 30910985L
testSquad.Membership(0).ZoneId = 5
testSquad.Membership(0).Position = Vector3(5526.5234f, 3818.7344f, 54.59375f)
testSquad.Membership(1).Name = "xoBLADEox"
testSquad.Membership(1).CharId = 42781919L
testSquad.Membership(1).ZoneId = 5
testSquad.Membership(1).Position = Vector3(4673.5312f, 2604.8047f, 40.015625f)
testSquad.Membership(3).Name = "cabal0428"
testSquad.Membership(3).CharId = 353380L
testSquad.Membership(3).ZoneId = 5
testSquad.Membership(3).Position = Vector3(4727.492f, 2613.5312f, 51.390625f)
testSquad.Membership(4).Name = "xSkiku"
testSquad.Membership(4).CharId = 41588340L
testSquad.Membership(4).ZoneId = 5
testSquad.Membership(4).Position = Vector3(3675.0f, 4789.8047f, 63.21875f)
idToSquad(PlanetSideGUID(3)) = testSquad
testSquad.Listed = true
UpdateSquadList(testSquad, List())
}
def GetNextSquadId() : PlanetSideGUID = {
@ -55,7 +80,7 @@ class SquadService extends Actor {
val name = player.Name
val squadOut = opt match {
case Some(squad) =>
if(squad.Leader.equals(name)) {
if(squad.Leader.Name.equals(name)) {
squad
}
else {
@ -64,7 +89,7 @@ class SquadService extends Actor {
case None =>
memberToSquad.get(name) match {
case Some(squad) if squad.Leader.equals(name) =>
case Some(squad) if squad.Leader.Name.equals(name) =>
squad
case _ =>
val faction = player.Faction
@ -115,7 +140,7 @@ class SquadService extends Actor {
SquadEvents.unsubscribe(sender())
memberToSquad.get(name) match {
case Some(squad) =>
if(squad.Leader.equals(name)) {
if(squad.Leader.Name.equals(name)) {
//we were the leader
if(squad.Membership.count(p => p.Name.equals("")) > 1) {
//other players were in the squad; publicly disband it
@ -144,190 +169,218 @@ class SquadService extends Actor {
}
case None => ;
}
//TODO leave squad, if joined to one, and perform clean-up
//TODO leave squad, if joined to one, and perform clean-up
case Service.Leave(None) | Service.LeaveAll() => ;
case SquadServiceMessage.SquadDefinitionAction(tplayer, zone_ordinal_number, guid, _, action) =>
import net.psforever.packet.game.SquadAction._
val squadOpt = GetParticipatingSquad(tplayer, zone_ordinal_number)
action match {
case SaveSquadDefinition() =>
case ChangeSquadPurpose(purpose) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed his squad's task to $purpose")
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Task = purpose
UpdateSquadList(squad, List(SquadInfo.Field.Task))
UpdateSquadDetail(squad)
case ChangeSquadZone(zone) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed squad's ops zone to $zone")
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.ZoneId = zone.zoneId.toInt
UpdateSquadList(squad, List(SquadInfo.Field.ZoneId))
UpdateSquadDetail(squad)
case CloseSquadMemberPosition(position) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(true) =>
squad.Availability.update(position, false)
log.info(s"${tplayer.Name}-${tplayer.Faction} has closed the #$position position in squad")
val memberPosition = squad.Membership(position)
val listingChanged = if(memberPosition.Name.nonEmpty) {
List(SquadInfo.Field.Size, SquadInfo.Field.Capacity)
case SquadServiceMessage(tplayer, squad_action) => squad_action match {
case SquadAction.Membership(request_type, char_id, optional_char_id, name, unk5) => request_type match {
case SquadRequestType.Accept =>
bids.get(char_id) match {
case Some((squadGUID, line)) if idToSquad.get(squadGUID).nonEmpty =>
//join squad
val squad = idToSquad(squadGUID)
val position = squad.Membership(line)
if(squad.Availability(line) && position.CharId == 0 &&
tplayer.Certifications.intersect(position.Requirements) == position.Requirements) {
position.Name = tplayer.Name
position.CharId = char_id
position.Health = tplayer.Health
position.Armor = tplayer.Armor
position.Position = tplayer.Position
position.ZoneId = 13
memberToSquad(tplayer.Name) = squad
sender ! SquadServiceResponse("", SquadResponse.Join(
squad,
squad.Membership.zipWithIndex.collect({ case (member, index) if member.CharId != 0 => index }).toList
))
}
else {
List(SquadInfo.Field.Capacity)
}
memberPosition.Close()
UpdateSquadList(squad, listingChanged)
UpdateSquadDetail(squad)
case Some(false) | None => ;
bids.remove(char_id)
case _ => ;
}
case AddSquadMemberPosition(position) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(false) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has opened the #$position position in squad")
squad.Availability.update(position, true)
UpdateSquadList(squad, List(SquadInfo.Field.Capacity))
UpdateSquadDetail(squad)
case Some(true) | None => ;
}
case ChangeSquadMemberRequirementsRole(position, role) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(true) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed the role of squad position #$position")
squad.Membership(position).Role = role
UpdateSquadDetail(squad)
case Some(false) | None => ;
}
case ChangeSquadMemberRequirementsDetailedOrders(position, orders) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(true) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed the orders for squad position #$position")
squad.Membership(position).Orders = orders
UpdateSquadDetail(squad)
case Some(false) | None => ;
}
case ChangeSquadMemberRequirementsCertifications(position, certs) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(true) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed the requirements for squad position #$position")
squad.Membership(position).Requirements = certs
UpdateSquadDetail(squad)
case Some(false) | None => ;
}
case LocationFollowsSquadLead(state) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
if(state) {
log.info(s"${tplayer.Name}-${tplayer.Faction} has moves the rally to the leader's position")
}
else {
log.info(s"${tplayer.Name}-${tplayer.Faction} has let the rally move freely")
}
squad.LocationFollowsSquadLead = state
case AutoApproveInvitationRequests(state) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
if(state) {
log.info(s"${tplayer.Name}-${tplayer.Faction} is allowing all requests to join the squad")
}
else {
log.info(s"${tplayer.Name}-${tplayer.Faction} has started screening invitation requests")
}
squad.AutoApproveInvitationRequests = state
case SelectRoleForYourself(line) =>
//TODO need to ask permission from the squad leader, unless our character is the squad leader or already currently in the squad
val name = tplayer.Name
squadOpt match {
case Some(squad) =>
{
if(squad.Availability(line))
squad.Membership.lift(line)
else
None
} match {
case Some(desiredPosition : Member)
if desiredPosition.Requirements.intersect(tplayer.Certifications) == desiredPosition.Requirements =>
//our character is qualified for this new position
if(squad.Leader.equals(tplayer.Name)) {
squad.LeaderPositionIndex = line //update
}
val hadPreviousPosition = squad.Membership.find(_.Name == name) match {
case Some(currentPosition)=>
currentPosition.Name = ""
currentPosition.CharId = 0L
currentPosition.ZoneId = 0
currentPosition.Health = 0
currentPosition.Armor = 0
currentPosition.Position = Vector3.Zero
true
case None =>
false
}
desiredPosition.Name = name
desiredPosition.CharId = tplayer.CharId
desiredPosition.ZoneId = zone_ordinal_number
desiredPosition.Health = tplayer.Health
desiredPosition.Armor = tplayer.Armor
desiredPosition.Position = tplayer.Position
if(!hadPreviousPosition) {
UpdateSquadList(squad, List(SquadInfo.Field.Size))
}
UpdateSquadDetail(squad)
case None => ;
}
case None => ;
}
case ListSquad() =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
if(!squad.Listed) {
log.info(s"${tplayer.Name}-${tplayer.Faction} has opened recruitment for this squad")
squad.Listed = true
}
UpdateSquadList(squad, List())
case ResetAll() =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Task = ""
squad.ZoneId = None
squad.Availability.indices.foreach { i =>
squad.Availability.update(i, true)
}
squad.Membership.foreach(position => {
position.Role = ""
position.Orders = ""
position.Requirements = Set()
})
UpdateSquadList(squad, List(SquadInfo.Field.Task, SquadInfo.Field.ZoneId, SquadInfo.Field.Size, SquadInfo.Field.Capacity))
UpdateSquadDetail(squad)
case DisplaySquad() =>
idToSquad.get(guid) match {
case Some(squad) =>
sender ! SquadServiceResponse(s"${tplayer.Name}/Squad", GenSquadDetail(squad))
case None => ;
case SquadRequestType.Leave =>
if(optional_char_id.contains(char_id)) {
//we're leaving the squad on our own
val name = tplayer.Name
val squad = memberToSquad(name)
val membership = squad.Membership.zipWithIndex
val (member, index) = membership
.find { case (_member, _) => _member.Name == name }
.get
val updateList = membership.collect({ case (_member, _) if _member.CharId > 0 => (_member.CharId, index) }).toList
memberToSquad.remove(name)
member.Name = ""
member.CharId = 0
sender ! SquadServiceResponse("", SquadResponse.Leave(squad, updateList))
}
case _ => ;
}
case msg =>
log.info(s"Unhandled message $msg from $sender")
case SquadAction.Definition(tplayer : Player, zone_ordinal_number : Int, guid : PlanetSideGUID, line : Int, action : SquadAction) =>
import net.psforever.packet.game.SquadAction._
val squadOpt = GetParticipatingSquad(tplayer, zone_ordinal_number)
action match {
case SaveSquadDefinition() =>
case ChangeSquadPurpose(purpose) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed his squad's task to $purpose")
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Task = purpose
UpdateSquadList(squad, List(SquadInfo.Field.Task))
UpdateSquadDetail(squad)
case ChangeSquadZone(zone) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed squad's ops zone to $zone")
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.ZoneId = zone.zoneId.toInt
UpdateSquadList(squad, List(SquadInfo.Field.ZoneId))
UpdateSquadDetail(squad)
case CloseSquadMemberPosition(position) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(true) =>
squad.Availability.update(position, false)
log.info(s"${tplayer.Name}-${tplayer.Faction} has closed the #$position position in squad")
val memberPosition = squad.Membership(position)
val listingChanged = if(memberPosition.Name.nonEmpty) {
List(SquadInfo.Field.Size, SquadInfo.Field.Capacity)
}
else {
List(SquadInfo.Field.Capacity)
}
memberPosition.Close()
UpdateSquadList(squad, listingChanged)
UpdateSquadDetail(squad)
case Some(false) | None => ;
}
case AddSquadMemberPosition(position) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(false) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has opened the #$position position in squad")
squad.Availability.update(position, true)
UpdateSquadList(squad, List(SquadInfo.Field.Capacity))
UpdateSquadDetail(squad)
case Some(true) | None => ;
}
case ChangeSquadMemberRequirementsRole(position, role) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(true) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed the role of squad position #$position")
squad.Membership(position).Role = role
UpdateSquadDetail(squad)
case Some(false) | None => ;
}
case ChangeSquadMemberRequirementsDetailedOrders(position, orders) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(true) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed the orders for squad position #$position")
squad.Membership(position).Orders = orders
UpdateSquadDetail(squad)
case Some(false) | None => ;
}
case ChangeSquadMemberRequirementsCertifications(position, certs) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Availability.lift(position) match {
case Some(true) =>
log.info(s"${tplayer.Name}-${tplayer.Faction} has changed the requirements for squad position #$position")
squad.Membership(position).Requirements = certs
UpdateSquadDetail(squad)
case Some(false) | None => ;
}
case LocationFollowsSquadLead(state) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
if(state) {
log.info(s"${tplayer.Name}-${tplayer.Faction} has moves the rally to the leader's position")
}
else {
log.info(s"${tplayer.Name}-${tplayer.Faction} has let the rally move freely")
}
squad.LocationFollowsSquadLead = state
case AutoApproveInvitationRequests(state) =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
if(state) {
log.info(s"${tplayer.Name}-${tplayer.Faction} is allowing all requests to join the squad")
}
else {
log.info(s"${tplayer.Name}-${tplayer.Faction} has started screening invitation requests")
}
squad.AutoApproveInvitationRequests = state
case SelectRoleForYourself(position) =>
//TODO need to ask permission from the squad leader, unless our character is the squad leader or already currently in the squad
val name = tplayer.Name
squadOpt match {
case Some(squad) if squad.GUID == guid =>
//already a member of this squad; swap positions freely
case Some(squad) =>
//not a member of the requesting squad; do nothing
case None =>
//not a member of any squad; consider request of joining the target squad
idToSquad.get(guid) match {
case Some(squad) =>
val member = squad.Membership(position)
if(squad.Availability(position) && member.CharId == 0 &&
tplayer.Certifications.intersect(member.Requirements) == member.Requirements) {
bids(tplayer.CharId) = (guid, position)
val leader = squad.Leader
//TODO need to ask permission from the squad leader, unless auto-approve is in effect
sender ! SquadServiceResponse("", SquadResponse.Membership(SquadRequestType.Invite, 0, 0, leader.CharId, Some(tplayer.CharId), leader.Name, false, None))
}
case None =>
//squad does not exist!? assume old data
//reload squad list data and blank the squad definition the user is looking at
}
}
case ListSquad() =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
if(!squad.Listed) {
log.info(s"${tplayer.Name}-${tplayer.Faction} has opened recruitment for this squad")
squad.Listed = true
}
UpdateSquadList(squad, List())
case ResetAll() =>
val squad = GetLeadingSquad(tplayer, zone_ordinal_number, squadOpt)
squad.Task = ""
squad.ZoneId = None
squad.Availability.indices.foreach { i =>
squad.Availability.update(i, true)
}
squad.Membership.foreach(position => {
position.Role = ""
position.Orders = ""
position.Requirements = Set()
})
UpdateSquadList(squad, List(SquadInfo.Field.Task, SquadInfo.Field.ZoneId, SquadInfo.Field.Size, SquadInfo.Field.Capacity))
UpdateSquadDetail(squad)
case DisplaySquad() =>
idToSquad.get(guid) match {
case Some(squad) =>
sender ! SquadServiceResponse(s"${tplayer.Name}/Squad", GenSquadDetail(squad))
case None => ;
}
case _ => ;
}
case msg =>
log.info(s"Unhandled message $msg from $sender")
}
}
def UpdateSquadList(squad : Squad, listingChanged : List[Int]) : Unit = {
@ -380,7 +433,7 @@ class SquadService extends Actor {
def GenSquadDetail(squad : Squad) : SquadResponse.Detail = {
SquadResponse.Detail(
squad.GUID,
squad.Leader,
squad.Leader.Name,
squad.Task,
PlanetSideZoneID(squad.ZoneId),
squad.Membership.zipWithIndex.map({ case (p, index) =>
@ -410,7 +463,7 @@ class SquadService extends Actor {
object SquadService {
def Publish(squad : Squad) : SquadInfo = {
SquadInfo(
squad.Leader,
squad.Leader.Name,
squad.Task,
PlanetSideZoneID(squad.ZoneId),
squad.Size,

View file

@ -2,12 +2,9 @@
package services.teamwork
import net.psforever.objects.Player
import net.psforever.packet.game.{PlanetSideGUID, SquadAction}
final case class SquadServiceMessage(forChannel : String, actionMessage : Any)
final case class SquadServiceMessage(tplayer : Player, actionMessage : Any)
object SquadServiceMessage {
final case class SquadDefinitionAction(player : Player, zone_ordinal_number : Int, guid : PlanetSideGUID, line : Int, action : SquadAction)
final case class RecoverSquadMembership()
}

View file

@ -146,7 +146,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
guid mustEqual PlanetSideGUID(3)
detail match {
case SquadDetail(None, None, None, None, None, None, None, None, Some(members)) =>
members.size mustEqual 2
members.size mustEqual 1
members.head.index mustEqual 5
members.head.info match {
case Some(SquadPositionDetail(Some(is_closed), None, None, None, None, None)) =>
@ -169,7 +169,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
guid mustEqual PlanetSideGUID(7)
detail match {
case SquadDetail(None, None, None, None, None, None, None, None, Some(members)) =>
members.size mustEqual 2
members.size mustEqual 1
members.head.index mustEqual 0
members.head.info match {
case Some(SquadPositionDetail(None, Some(role), None, None, None, None)) =>
@ -192,7 +192,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
guid mustEqual PlanetSideGUID(1)
detail match {
case SquadDetail(None, None, None, None, None, None, None, None, Some(members)) =>
members.size mustEqual 2
members.size mustEqual 1
members.head.index mustEqual 6
members.head.info match {
case Some(SquadPositionDetail(None, Some(role), None, Some(req), None, None)) =>
@ -217,7 +217,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
guid mustEqual PlanetSideGUID(3)
detail match {
case SquadDetail(None, None, None, None, None, None, None, None, Some(members)) =>
members.size mustEqual 2
members.size mustEqual 1
members.head.index mustEqual 5
members.head.info match {
case Some(SquadPositionDetail(None, None, None, None, Some(char_id), Some(name))) =>
@ -242,7 +242,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
detail match {
case SquadDetail(None, None, None, None, None, Some(task), None, None, Some(members)) =>
task mustEqual "\\#FF0000 The \\#ffffff Blades"
members.size mustEqual 11
members.size mustEqual 10
//
members.head.index mustEqual 9
members.head.info match {
@ -399,7 +399,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
detail match {
case SquadDetail(None, None, None, None, None, Some(task), None, None, Some(member_list)) =>
task mustEqual "PSForever Packet Collection"
member_list.size mustEqual 11
member_list.size mustEqual 10
member_list.head mustEqual SquadPositionEntry(9,Some(
SquadPositionDetail(
Some(false),
@ -516,8 +516,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
.Field1(0)
.LeaderCharId(1221560L)
.Members(List(
SquadPositionEntry(6, SquadPositionDetail().Player(0L, "")),
SquadPositionEntry(255, None)
SquadPositionEntry(6, SquadPositionDetail().Player(0L, ""))
))
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -540,8 +539,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
.Leader(42631712L, "Jaako")
.Field3(556403L)
.Members(List(
SquadPositionEntry(0, SquadPositionDetail().Player(0L, "")),
SquadPositionEntry(255, None)
SquadPositionEntry(0, SquadPositionDetail().Player(0L, ""))
))
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -593,8 +591,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
SquadPositionEntry(3, SquadPositionDetail().Role("The Princess")),
SquadPositionEntry(2, SquadPositionDetail().Role("The Prince")),
SquadPositionEntry(1, SquadPositionDetail().Role("The Queen")),
SquadPositionEntry(0, SquadPositionDetail().Role("The King")),
SquadPositionEntry(255, None)
SquadPositionEntry(0, SquadPositionDetail().Role("The King"))
))
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -606,8 +603,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
PlanetSideGUID(3),
SquadDetail()
.Members(List(
SquadPositionEntry(5, SquadPositionDetail.Closed),
SquadPositionEntry(255, None)
SquadPositionEntry(5, SquadPositionDetail.Closed)
))
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -620,8 +616,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
PlanetSideGUID(7),
SquadDetail()
.Members(List(
SquadPositionEntry(0, SquadPositionDetail().Role("Commander")),
SquadPositionEntry(255, None)
SquadPositionEntry(0, SquadPositionDetail().Role("Commander"))
))
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -635,8 +630,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
.Members(List(
SquadPositionEntry(6, SquadPositionDetail()
.Role("ADV Hacker")
.Requirements(Set(CertificationType.AdvancedHacking))),
SquadPositionEntry(255, None)
.Requirements(Set(CertificationType.AdvancedHacking)))
))
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -648,8 +642,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
PlanetSideGUID(3),
SquadDetail()
.Members(List(
SquadPositionEntry(5, SquadPositionDetail().Player(1218249L, "Duckmaster43")),
SquadPositionEntry(255, None)
SquadPositionEntry(5, SquadPositionDetail().Player(1218249L, "Duckmaster43"))
))
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -671,8 +664,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
SquadPositionEntry(3, SquadPositionDetail().Role("")),
SquadPositionEntry(2, SquadPositionDetail().Role("")),
SquadPositionEntry(1, SquadPositionDetail().Role("")),
SquadPositionEntry(0, SquadPositionDetail().Role("")),
SquadPositionEntry(255, None)
SquadPositionEntry(0, SquadPositionDetail().Role(""))
))
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
@ -715,7 +707,7 @@ class SquadDetailDefinitionUpdateMessageTest extends Specification {
PlanetSideGUID(3),
SquadDetail
.Task("PSForever Packet Collection")
.Members((0 to 9).map { index => SquadPositionEntry(index, position) }.reverse.toList :+ SquadPositionEntry(255, None))
.Members((0 to 9).map { index => SquadPositionEntry(index, position) }.reverse.toList)
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_mixed

View file

@ -52,7 +52,7 @@ import services.galaxy.{GalaxyResponse, GalaxyServiceResponse}
import services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
import services.vehicle.support.TurretUpgrader
import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import services.teamwork._
import services.teamwork.{SquadAction => SquadServiceAction, SquadServiceMessage, SquadServiceResponse, SquadResponse, SquadService}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
@ -376,16 +376,65 @@ class WorldSessionActor extends Actor with MDCContextAware {
SquadDetailDefinitionUpdateMessage(
guid,
SquadDetail()
.LeaderCharId(member_info.find(_.name == leader).get.char_id.get)
.LeaderCharId(member_info.find(_.name.contains(leader)).get.char_id.get)
.LeaderName(leader)
.Task(task)
.ZoneId(zone)
.Members(
member_info.zipWithIndex.map { case (a, b) => SquadPositionEntry(b, a) }
)
.Members(member_info.zipWithIndex.map { case (a, b) => SquadPositionEntry(b, a) })
.Complete
)
)
case SquadResponse.Membership(request_type, unk1, unk2, unk3, unk4, player_name, unk5, unk6) =>
sendResponse(SquadMembershipResponse(request_type, unk1, unk2, unk3, unk4, player_name, unk5, unk6))
case SquadResponse.Join(squad, positionsToUpdate) =>
val leader = squad.Leader
val id = 11
val membershipPositions = squad.Membership
.zipWithIndex
.filter { case (_, index ) => positionsToUpdate.contains(index) }
sendResponse(SquadMembershipResponse(SquadRequestType.Accept, 0, 0, player.CharId, Some(leader.CharId), player.Name, true, Some(None)))
//load each member's entry
membershipPositions.foreach { case(member, index) =>
sendResponse(SquadMemberEvent(0, id, member.CharId, index, Some(member.Name), Some(member.ZoneId), Some(0)))
}
//repeat own entry and initialize connection to squad
membershipPositions.find({ case(member, _) => member.CharId == avatar.CharId }) match {
case Some((member, index)) =>
sendResponse(SquadMemberEvent(0, id, member.CharId, index, Some(member.Name), Some(member.ZoneId), Some(0)))
sendResponse(PlanetsideAttributeMessage(player.GUID, 31, id)) //associate with squad?
sendResponse(PlanetsideAttributeMessage(player.GUID, 32, index)) //associate with member position in squad?
case _ => ;
}
//send first update for map icons
sendResponse(SquadState(PlanetSideGUID(id),
membershipPositions
.filterNot { case (member, _) => member.CharId == avatar.CharId }
.map{ case (member, _) => SquadStateInfo(member.CharId, 64,64, member.Position, 2,2, false, 429, None,None) }
.toList
))
//a finalization? what does this do?
sendResponse(SquadDefinitionActionMessage(squad.GUID, 0, SquadAction.Unknown(18, hex"00".toBitVector.take(6))))
case SquadResponse.Leave(_, positionsToUpdate) =>
val id = 11
sendResponse(SquadMembershipResponse(SquadRequestType.Leave, 0,1, avatar.CharId, Some(avatar.CharId), avatar.name, true, Some(None)))
//remove each member's entry (our own too)
positionsToUpdate.foreach { case(member, index) =>
sendResponse(SquadMemberEvent(1, id, member, index, None, None, None))
}
//repeat own entry and rescind connection to squad
positionsToUpdate.find({ case(member, _) => member == avatar.CharId }) match {
case Some((member, index)) =>
sendResponse(SquadMemberEvent(1, id, member, index, None, None, None))
sendResponse(PlanetsideAttributeMessage(player.GUID, 31, 0)) //disassociate with squad?
sendResponse(PlanetsideAttributeMessage(player.GUID, 32, 0)) //disassociate with member position in squad?
sendResponse(PlanetsideAttributeMessage(player.GUID, 34, 4294967295L)) //unknown
case _ => ;
}
//a finalization? what does this do?
sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), 0, SquadAction.Unknown(18, hex"00".toBitVector.take(6))))
case _ => ;
}
@ -2911,42 +2960,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
interstellarFerryTopLevelGUID = None
case _ => ;
}
//SQUAD TESTING CODE
sendResponse(ReplicationStreamMessage(
5,
Some(6),
Vector(
SquadListing(0, SquadInfo(Some("xNick"), Some("FLY,ALL WELCOME!"), Some(PlanetSideZoneID(7)), Some(8), Some(10), Some(PlanetSideGUID(1)))),
SquadListing(1, SquadInfo(Some("HofD"), Some("=KOK+SPC+FLY= All Welcome"), Some(PlanetSideZoneID(7)), Some(3), Some(10), Some(PlanetSideGUID(3))))
)
))
sendResponse(
SquadDetailDefinitionUpdateMessage(
PlanetSideGUID(3),
SquadDetail(
3,
1792,
42771010L,
529745L,
"HofD",
"\\#ffdc00***\\#9640ff=KOK+SPC+FLY=\\#ffdc00***\\#FF4040 All Welcome",
PlanetSideZoneID(7),
4983296,
List(
SquadPositionEntry(0, SquadPositionDetail("\\#ff0000 |||||||||||||||||||||||", "", Set(), 0, "")),
SquadPositionEntry(1, SquadPositionDetail("\\#ffdc00 C", "", Set(), 0, "")),
SquadPositionEntry(2, SquadPositionDetail("\\#ffdc00 H", "", Set(), 42644970L, "OpolE")),
SquadPositionEntry(3, SquadPositionDetail("\\#ffdc00 I", "", Set(), 41604210L, "BobaF3tt907")),
SquadPositionEntry(4, SquadPositionDetail("\\#ffdc00 N", "", Set(), 0, "")),
SquadPositionEntry(5, SquadPositionDetail("\\#ffdc00 A", "", Set(), 0, "")),
SquadPositionEntry(6, SquadPositionDetail("\\#ff0000 |||||||||||||||||||||||", "", Set(), 0, "")),
SquadPositionEntry(7, SquadPositionDetail("\\#9640ff K", "", Set(), 0, "")),
SquadPositionEntry(8, SquadPositionDetail("\\#9640ff O", "", Set(), 42771010L ,"HofD")),
SquadPositionEntry(9, SquadPositionDetail("\\#9640ff K", "", Set(), 0, ""))
)
)
)
)
squadService ! Service.Join(s"${avatar.faction}") //channel will be player.Faction
}
def handleControlPkt(pkt : PlanetSideControlPacket) = {
@ -3359,18 +3373,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) =>
if(deadState == DeadState.Alive) {
if(!player.Crouching && is_crouching) { //SQUAD TESTING CODE
sendRawResponse(hex"e80300818800015c5189004603408c000000012000ff")
// sendResponse(SquadMembershipResponse(SquadRequestType.Invite,0,0,42771010,Some(avatar.CharId),"HofD",false,None))
// sendResponse(SquadMembershipResponse(SquadRequestType.Accept,0,0,avatar.CharId,Some(42771010),"VirusGiver",true,Some(None)))
// sendResponse(SquadMemberEvent(0,7,42771010,0,Some("HofD"),Some(7),Some(529745)))
// sendResponse(SquadMemberEvent(0,7,42644970,1,Some("OpolE"),Some(7),Some(6418)))
// sendResponse(SquadMemberEvent(0,7,41604210,8,Some("BobaF3tt907"),Some(12),Some(8097)))
// sendResponse(PlanetsideAttributeMessage(player.GUID, 49, 7))
// sendResponse(PlanetsideAttributeMessage(player.GUID, 50, 2))
// sendResponse(PlanetsideAttributeMessage(player.GUID, 51, 8))
// sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(3), 0, SquadAction.Unknown(18, hex"".toBitVector)))
// sendResponse(PlanetsideAttributeMessage(player.GUID, 83, 0))
// sendResponse(SquadState(PlanetSideGUID(7),List(SquadStateInfo(avatar.CharId,64,64,Vector3(3464.0469f,4065.5703f,20.015625f),2,2,false,0,None,None))))
//...
}
player.Position = pos
player.Velocity = vel
@ -4850,7 +4853,29 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ SquadDefinitionActionMessage(u1, u2, action) =>
log.info(s"SquadDefinitionAction: $msg")
squadService ! SquadServiceMessage.SquadDefinitionAction(player, continent.Number, u1, u2, action)
squadService ! SquadServiceMessage(player, SquadServiceAction.Definition(player, continent.Number, 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))
// if(request_type == SquadRequestType.Accept) {
// sendResponse(SquadMembershipResponse(SquadRequestType.Accept, 0, 0, unk2, Some(30910985L), player.Name, true, Some(None)))
// sendResponse(SquadMemberEvent(0, 11, 30910985L, 0, Some("Wizkid45"), Some(5), Some(320036)))
// sendResponse(SquadMemberEvent(0, 11, 42781919L, 1, Some("xoBLADEox"), Some(5), Some(528805)))
// sendResponse(SquadMemberEvent(0, 11, avatar.CharId, 2, Some(player.Name), Some(13), Some(320036)))
// sendResponse(SquadMemberEvent(0, 11, 353380L, 3, Some("cabal0428") ,Some(5), Some(8156)))
// sendResponse(SquadMemberEvent(0, 11, 41588340L, 4 ,Some("xSkiku"), Some(5), Some(0)))
// sendResponse(SquadMemberEvent(0, 11, avatar.CharId, 2, Some(player.Name), Some(13), Some(320036)))
// sendResponse(PlanetsideAttributeMessage(player.GUID, 31, 11)) //associate with squad?
// sendResponse(PlanetsideAttributeMessage(player.GUID, 32, 2)) //associate with member position in squad?
// sendResponse(SquadState(PlanetSideGUID(11),List(
// SquadStateInfo(30910985L, 50,64, Vector3(5526.5234f,3818.7344f,54.59375f), 2,2, false, 429, None,None),
// SquadStateInfo(42781919L, 64,0, Vector3(4673.5312f,2604.8047f,40.015625f), 2,2, false, 149, None,None),
// SquadStateInfo(353380L, 64,64, Vector3(4727.492f,2613.5312f,51.390625f), 2,2, false, 0, None,None),
// SquadStateInfo(41588340L, 64,64, Vector3(3675.0f,4789.8047f,63.21875f), 2,2, false, 0, None,None)
// )))
// sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(3), 0, SquadAction.Unknown(18, hex"00".toBitVector.take(6))))
// }
case msg @ GenericCollisionMsg(u1, p, t, php, thp, pv, tv, ppos, tpos, u2, u3, u4) =>
log.info("Ouch! " + msg)