mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
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:
parent
2a1b051710
commit
60a11b8c52
|
|
@ -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!")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
|||
13
common/src/main/scala/services/teamwork/SquadAction.scala
Normal file
13
common/src/main/scala/services/teamwork/SquadAction.scala
Normal 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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue