From 5916fa68be43bee7834d4acafe03b2ce5e5c773f Mon Sep 17 00:00:00 2001 From: FateJH Date: Mon, 21 Oct 2019 19:41:29 -0400 Subject: [PATCH] SquadAction and SquadResponse were worked into SquadServiceMessage and SquadServiceResponse, respectively, so that traits could be sealed; the continuous join request prompt has been exorcised from squad operations; switching to auto-approve for squads will now resolve all pending join requests; enumerated shared cases in SDAM --- .../objects/teamwork/SquadFeatures.scala | 19 ++-------- .../game/SquadDefinitionActionMessage.scala | 19 ++++++++-- .../SquadDetailDefinitionUpdateMessage.scala | 38 ------------------- .../scala/services/teamwork/SquadAction.scala | 18 --------- .../services/teamwork/SquadResponse.scala | 34 ----------------- .../services/teamwork/SquadService.scala | 33 +++++++++++----- .../teamwork/SquadServiceMessage.scala | 14 +++++++ .../teamwork/SquadServiceResponse.scala | 31 +++++++++++++++ .../src/main/scala/WorldSessionActor.scala | 5 ++- 9 files changed, 91 insertions(+), 120 deletions(-) delete mode 100644 common/src/main/scala/services/teamwork/SquadAction.scala delete mode 100644 common/src/main/scala/services/teamwork/SquadResponse.scala diff --git a/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala b/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala index a1562302..2e1a5f05 100644 --- a/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala +++ b/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala @@ -1,8 +1,7 @@ // Copyright (c) 2019 PSForever package net.psforever.objects.teamwork -import akka.actor.{Actor, ActorContext, ActorRef, Cancellable, Props} -import net.psforever.objects.DefaultCancellable +import akka.actor.{Actor, ActorContext, ActorRef, Props} import net.psforever.types.SquadWaypoints import services.teamwork.SquadService.WaypointData import services.teamwork.SquadSwitchboard @@ -11,8 +10,8 @@ class SquadFeatures(val Squad : Squad) { /** * `initialAssociation` per squad is similar to "Does this squad want to recruit members?" * The squad does not have to be flagged. - * Dispatches an `AssociateWithSquad` `SDAM` to the squad leader and ??? - * and then a `SDDUM` that includes at least the squad owner name and char id. + * Dispatches an `AssociateWithSquad` `SquadDefinitionActionMessage` packet to the squad leader and ??? + * and then a `SquadDetailDefinitionUpdateMessage` that includes at least the squad owner name and char id. * Dispatched only once when a squad is first listed * or when the squad leader searches for recruits by proximity or for certain roles or by invite * or when a spontaneous squad forms, @@ -52,8 +51,6 @@ class SquadFeatures(val Squad : Squad) { * Handle persistent data related to `ProximityInvite` and `LookingForSquadRoleInvite` messages */ private var proxyInvites : List[Long] = Nil - - private var requestInvitePrompt : Cancellable = DefaultCancellable.obj /** * These useres rejected invitation to this squad. * For the purposes of wide-searches for membership @@ -79,7 +76,6 @@ class SquadFeatures(val Squad : Squad) { switchboard ! akka.actor.PoisonPill switchboard = Actor.noSender waypoints = Array.empty - requestInvitePrompt.cancel this } @@ -143,13 +139,4 @@ class SquadFeatures(val Squad : Squad) { } def ToChannel : String = channel - - def Prompt : Cancellable = requestInvitePrompt - - def Prompt_=(callback: Cancellable) : Cancellable = { - if(requestInvitePrompt.isCancelled) { - requestInvitePrompt = callback - } - Prompt - } } diff --git a/common/src/main/scala/net/psforever/packet/game/SquadDefinitionActionMessage.scala b/common/src/main/scala/net/psforever/packet/game/SquadDefinitionActionMessage.scala index a0a02c7d..ccdc41b5 100644 --- a/common/src/main/scala/net/psforever/packet/game/SquadDefinitionActionMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/SquadDefinitionActionMessage.scala @@ -426,16 +426,23 @@ object SquadDefinitionActionMessage extends Marshallable[SquadDefinitionActionMe ((code : @switch) match { case 0 => displaySquadCodec case 1 => squadMemberInitializationIssueCodec + case 2 => unknownCodec(action = 2) case 3 => saveSquadFavoriteCodec case 4 => loadSquadFavoriteCodec case 5 => deleteSquadFavoriteCodec + case 6 => unknownCodec(action = 6) case 7 => listSquadFavoriteCodec case 8 => requestListSquadCodec case 9 => stopListSquadCodec case 10 => selectRoleForYourselfCodec + case 11 => unknownCodec(action = 11) + case 12 => unknownCodec(action = 12) + case 13 => unknownCodec(action = 13) + case 14 => unknownCodec(action = 14) case 15 => cancelSelectRoleForYourselfCodec case 16 => associateWithSquadCodec case 17 => setListSquadCodec + case 18 => unknownCodec(action = 18) case 19 => changeSquadPurposeCodec case 20 => changeSquadZoneCodec case 21 => closeSquadMemberPositionCodec @@ -444,17 +451,23 @@ object SquadDefinitionActionMessage extends Marshallable[SquadDefinitionActionMe case 24 => changeSquadMemberRequirementsDetailedOrdersCodec case 25 => changeSquadMemberRequirementsCertificationsCodec case 26 => resetAllCodec + //case 27 => ? case 28 => autoApproveInvitationRequestsCodec + case 29 => unknownCodec(action = 29) + case 30 => unknownCodec(action = 30) case 31 => locationFollowsSquadLeadCodec + case 32 => unknownCodec(action = 32) + case 33 => unknownCodec(action = 33) case 34 => searchForSquadsWithParticularRoleCodec case 35 => cancelSquadSearchCodec + case 36 => unknownCodec(action = 36) + case 37 => unknownCodec(action = 37) case 38 => assignSquadMemberToRoleCodec case 39 => noSquadSearchResultsCodec case 40 => findLfsSoldiersForRoleCodec case 41 => cancelFindCodec - case 2 | 6 | 11 | 12 | 13 | - 14 | 18 | 29 | 30 | 32 | - 33 | 36 | 37 | 42 | 43 => unknownCodec(code) + case 42 => unknownCodec(action = 42) + case 43 => unknownCodec(action = 43) case _ => failureCodec(code) }).asInstanceOf[Codec[SquadAction]] } diff --git a/common/src/main/scala/net/psforever/packet/game/SquadDetailDefinitionUpdateMessage.scala b/common/src/main/scala/net/psforever/packet/game/SquadDetailDefinitionUpdateMessage.scala index 055a031a..7c60d048 100644 --- a/common/src/main/scala/net/psforever/packet/game/SquadDetailDefinitionUpdateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/SquadDetailDefinitionUpdateMessage.scala @@ -657,44 +657,6 @@ object SquadDetailDefinitionUpdateMessage extends Marshallable[SquadDetailDefini } ) } - //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. diff --git a/common/src/main/scala/services/teamwork/SquadAction.scala b/common/src/main/scala/services/teamwork/SquadAction.scala deleted file mode 100644 index 0a1b9971..00000000 --- a/common/src/main/scala/services/teamwork/SquadAction.scala +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2019 PSForever -package services.teamwork - -import net.psforever.objects.zones.Zone -import net.psforever.packet.game._ -import net.psforever.types.{PlanetSideEmpire, SquadRequestType, SquadWaypoints, Vector3} - -object SquadAction { - trait Action - - final case class InitSquadList() extends Action - final case class InitCharId() extends Action - - final case class Definition(guid : PlanetSideGUID, line : Int, action : SquadAction) extends Action - final case class Membership(request_type : SquadRequestType.Value, unk2 : Long, unk3 : Option[Long], player_name : String, unk5 : Option[Option[String]]) extends Action - final case class Waypoint(event_type : WaypointEventAction.Value, waypoint_type : SquadWaypoints.Value, unk : Option[Long], waypoint_info : Option[WaypointInfo]) extends Action - final case class Update(char_id : Long, health : Int, max_health : Int, armor : Int, max_armor : Int, pos : Vector3, zone_number : Int) extends Action -} diff --git a/common/src/main/scala/services/teamwork/SquadResponse.scala b/common/src/main/scala/services/teamwork/SquadResponse.scala deleted file mode 100644 index 159f6fe2..00000000 --- a/common/src/main/scala/services/teamwork/SquadResponse.scala +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2019 PSForever -package services.teamwork - -import net.psforever.objects.teamwork.Squad -import net.psforever.packet.game._ -import net.psforever.types.{SquadResponseType, SquadWaypoints} - -object SquadResponse { - trait Response - - final case class ListSquadFavorite(line : Int, task : String) extends Response - - final case class InitList(info : Vector[SquadInfo]) extends Response - final case class UpdateList(infos : Iterable[(Int, SquadInfo)]) extends Response - final case class RemoveFromList(infos : Iterable[Int]) extends Response - - final case class AssociateWithSquad(squad_guid : PlanetSideGUID) extends Response - final case class SetListSquad(squad_guid : PlanetSideGUID) extends Response - - final case class Membership(request_type : SquadResponseType.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 WantsSquadPosition(leader_char_id : Long, bid_name : String) extends Response - final case class Join(squad : Squad, positionsToUpdate : List[Int], channel : String) extends Response - final case class Leave(squad : Squad, positionsToUpdate : List[(Long, Int)]) extends Response - final case class UpdateMembers(squad : Squad, update_info : List[SquadAction.Update]) extends Response - final case class AssignMember(squad : Squad, from_index : Int, to_index : Int) extends Response - final case class PromoteMember(squad : Squad, char_id : Long, from_index : Int, to_index : Int) extends Response - - final case class Detail(guid : PlanetSideGUID, squad_detail : SquadDetail) extends Response - - final case class InitWaypoints(char_id : Long, waypoints : Iterable[(SquadWaypoints.Value, WaypointInfo, Int)]) extends Response - final case class WaypointEvent(event_type : WaypointEventAction.Value, char_id : Long, waypoint_type : SquadWaypoints.Value, unk5 : Option[Long], waypoint_info : Option[WaypointInfo], unk : Int) extends Response - - final case class SquadSearchResults() extends Response -} diff --git a/common/src/main/scala/services/teamwork/SquadService.scala b/common/src/main/scala/services/teamwork/SquadService.scala index 72267a6e..ad717e59 100644 --- a/common/src/main/scala/services/teamwork/SquadService.scala +++ b/common/src/main/scala/services/teamwork/SquadService.scala @@ -612,7 +612,6 @@ class SquadService extends Actor { //player requested to join a squad's specific position //invitedPlayer is actually the squad leader; petitioner is the actual "invitedPlayer" val features = squadFeatures(guid) - features.Prompt.cancel JoinSquad(petitioner, features.Squad, position) RemoveInvitesForSquadAndPosition(guid, position) @@ -832,7 +831,6 @@ class SquadService extends Actor { //rejectingPlayer is the squad leader; candidate is the would-be squad member who was rejected val features = squadFeatures(guid) features.Refuse = rejectingPlayer - features.Prompt.cancel (Some(rejectingPlayer), None) case _ => ; @@ -902,8 +900,6 @@ class SquadService extends Actor { val (member, index) = membership.zipWithIndex.find { case (_member, _) => _member.CharId == promotedPlayer }.get val features = squadFeatures(squad.GUID) SwapMemberPosition(leader, member) - //cancel previous leader invite prompt, if any - features.Prompt.cancel //move around invites so that the proper squad leader deals with them val leaderInvite = invites.remove(promotingPlayer) val leaderQueuedInvites = queuedInvites.remove(promotingPlayer).toList.flatten @@ -1108,6 +1104,28 @@ class SquadService extends Actor { case AutoApproveInvitationRequests(state) => val features = squadFeatures(lSquadOpt.getOrElse(StartSquad(tplayer)).GUID) features.AutoApproveInvitationRequests = state + if(state) { + //allowed auto-approval - resolve the requests (only) + val charId = tplayer.CharId + val (requests, others) = (invites.get(charId).toList ++ queuedInvites.get(charId).toList) + .partition({ case _ : RequestRole => true}) + invites.remove(charId) + queuedInvites.remove(charId) + previousInvites.remove(charId) + requests.foreach { + case request : RequestRole => + JoinSquad(request.player, features.Squad, request.position) + case _ => ; + } + others.collect { case invite : Invitation => invite } match { + case Nil => ; + case x :: Nil => + AddInviteAndRespond(charId, x, x.InviterCharId, x.InviterName) + case x :: xs => + AddInviteAndRespond(charId, x, x.InviterCharId, x.InviterName) + queuedInvites += charId -> xs + } + } case FindLfsSoldiersForRole(position) => lSquadOpt match { @@ -1439,9 +1457,6 @@ class SquadService extends Actor { debug(s"Unhandled message $msg from $sender") } - case data @ SquadResponse.WantsSquadPosition(leader_char_id, _) => - Publish(leader_char_id, data) - case msg => debug(s"Unhandled message $msg from $sender") } @@ -2145,9 +2160,7 @@ class SquadService extends Actor { self ! SquadServiceMessage(player, Zone.Nowhere, SquadAction.Membership(SquadRequestType.Accept, leaderCharId, None, "", None)) } else { - import scala.concurrent.duration._ - import scala.concurrent.ExecutionContext.Implicits.global - features.Prompt = context.system.scheduler.schedule(1 milliseconds, 45000 milliseconds, self, SquadResponse.WantsSquadPosition(leaderCharId, player.Name)) + Publish(leaderCharId, SquadResponse.WantsSquadPosition(leaderCharId, player.Name)) } true case _ => diff --git a/common/src/main/scala/services/teamwork/SquadServiceMessage.scala b/common/src/main/scala/services/teamwork/SquadServiceMessage.scala index c1826303..fd856ae0 100644 --- a/common/src/main/scala/services/teamwork/SquadServiceMessage.scala +++ b/common/src/main/scala/services/teamwork/SquadServiceMessage.scala @@ -3,9 +3,23 @@ package services.teamwork import net.psforever.objects.Player import net.psforever.objects.zones.Zone +import net.psforever.packet.game.{PlanetSideGUID, SquadAction => PacketSquadAction, WaypointEventAction, WaypointInfo} +import net.psforever.types.{SquadRequestType, SquadWaypoints, Vector3} final case class SquadServiceMessage(tplayer : Player, zone : Zone, actionMessage : Any) object SquadServiceMessage { final case class RecoverSquadMembership() } + +object SquadAction { + sealed trait Action + + final case class InitSquadList() extends Action + final case class InitCharId() extends Action + + final case class Definition(guid : PlanetSideGUID, line : Int, action : PacketSquadAction) extends Action + final case class Membership(request_type : SquadRequestType.Value, unk2 : Long, unk3 : Option[Long], player_name : String, unk5 : Option[Option[String]]) extends Action + final case class Waypoint(event_type : WaypointEventAction.Value, waypoint_type : SquadWaypoints.Value, unk : Option[Long], waypoint_info : Option[WaypointInfo]) extends Action + final case class Update(char_id : Long, health : Int, max_health : Int, armor : Int, max_armor : Int, pos : Vector3, zone_number : Int) extends Action +} diff --git a/common/src/main/scala/services/teamwork/SquadServiceResponse.scala b/common/src/main/scala/services/teamwork/SquadServiceResponse.scala index 47cf0387..82fd1339 100644 --- a/common/src/main/scala/services/teamwork/SquadServiceResponse.scala +++ b/common/src/main/scala/services/teamwork/SquadServiceResponse.scala @@ -1,6 +1,9 @@ // Copyright (c) 2019 PSForever package services.teamwork +import net.psforever.objects.teamwork.Squad +import net.psforever.packet.game._ +import net.psforever.types.{SquadResponseType, SquadWaypoints} import services.GenericEventBusMsg final case class SquadServiceResponse(toChannel : String, exclude : Iterable[Long], response : SquadResponse.Response) extends GenericEventBusMsg @@ -12,3 +15,31 @@ object SquadServiceResponse { def apply(toChannel : String, exclude : Long, response : SquadResponse.Response) : SquadServiceResponse = SquadServiceResponse(toChannel, Seq(exclude), response) } + +object SquadResponse { + sealed trait Response + + final case class ListSquadFavorite(line : Int, task : String) extends Response + + final case class InitList(info : Vector[SquadInfo]) extends Response + final case class UpdateList(infos : Iterable[(Int, SquadInfo)]) extends Response + final case class RemoveFromList(infos : Iterable[Int]) extends Response + + final case class AssociateWithSquad(squad_guid : PlanetSideGUID) extends Response + final case class SetListSquad(squad_guid : PlanetSideGUID) extends Response + + final case class Membership(request_type : SquadResponseType.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 WantsSquadPosition(leader_char_id : Long, bid_name : String) extends Response + final case class Join(squad : Squad, positionsToUpdate : List[Int], channel : String) extends Response + final case class Leave(squad : Squad, positionsToUpdate : List[(Long, Int)]) extends Response + final case class UpdateMembers(squad : Squad, update_info : List[SquadAction.Update]) extends Response + final case class AssignMember(squad : Squad, from_index : Int, to_index : Int) extends Response + final case class PromoteMember(squad : Squad, char_id : Long, from_index : Int, to_index : Int) extends Response + + final case class Detail(guid : PlanetSideGUID, squad_detail : SquadDetail) extends Response + + final case class InitWaypoints(char_id : Long, waypoints : Iterable[(SquadWaypoints.Value, WaypointInfo, Int)]) extends Response + final case class WaypointEvent(event_type : WaypointEventAction.Value, char_id : Long, waypoint_type : SquadWaypoints.Value, unk5 : Option[Long], waypoint_info : Option[WaypointInfo], unk : Int) extends Response + + final case class SquadSearchResults() extends Response +} diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index e48f1c4d..4c1e6fc9 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -539,7 +539,6 @@ class WorldSessionActor extends Actor with MDCContextAware { SwapSquadUIElements(squad, from_index, to_index) case SquadResponse.UpdateMembers(squad, positions) => - import services.teamwork.SquadAction.{Update => SAUpdate} val pairedEntries = positions.collect { case entry if squadUI.contains(entry.char_id) => (entry, squadUI(entry.char_id)) @@ -3220,6 +3219,10 @@ class WorldSessionActor extends Actor with MDCContextAware { val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate" log.info(s"New world login to $server with Token:$token. $clientVersion") //TODO begin temp player character auto-loading; remove later + //this is all just temporary character creation used in the dev branch, making explicit values that allow for testing + //the unique character identifier number for this testing character is based on the original test character, + //whose identifier number was 41605314 + //all head features, faction, and sex also match that test character import net.psforever.objects.GlobalDefinitions._ import net.psforever.types.CertificationType._ val faction = PlanetSideEmpire.VS