mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
experimental invitation management commands for squad leaders, mostly untested atm; messages for being denied squad admission
This commit is contained in:
parent
d15e916f46
commit
2372a95040
|
|
@ -138,6 +138,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case "grenade" => ops.customCommandGrenade(session, log)
|
||||
case "macro" => ops.customCommandMacro(session, params)
|
||||
case "progress" => ops.customCommandProgress(session, params)
|
||||
case "squad" => ops.customCommandSquad(params)
|
||||
case _ =>
|
||||
// command was not handled
|
||||
sendResponse(
|
||||
|
|
|
|||
|
|
@ -349,6 +349,9 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
|
|||
case SquadResponse.WaypointEvent(WaypointEventAction.Remove, char_id, waypoint_type, _, _, _) =>
|
||||
sendResponse(SquadWaypointEvent.Remove(ops.squad_supplement_id, char_id, waypoint_type))
|
||||
|
||||
case SquadResponse.SquadRelatedComment(comment) =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_227, comment))
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.Cancellable
|
||||
import net.psforever.objects.LivePlayerList
|
||||
import akka.actor.{ActorRef => ClassicActorRef}
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.actors.session.spectator.SpectatorMode
|
||||
|
|
@ -12,6 +14,7 @@ import net.psforever.objects.zones.ZoneInfo
|
|||
import net.psforever.packet.game.SetChatFilterMessage
|
||||
import net.psforever.services.chat.{DefaultChannel, SquadChannel}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.teamwork.SquadService
|
||||
import net.psforever.types.ChatMessageType.CMT_QUIT
|
||||
import org.log4s.Logger
|
||||
|
||||
|
|
@ -54,6 +57,7 @@ class ChatOperations(
|
|||
val sessionLogic: SessionData,
|
||||
val avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
val chatService: typed.ActorRef[ChatService.Command],
|
||||
val squadService: ClassicActorRef,
|
||||
val cluster: typed.ActorRef[InterstellarClusterService.Command],
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
|
|
@ -1284,6 +1288,32 @@ class ChatOperations(
|
|||
true
|
||||
}
|
||||
|
||||
def customCommandSquad(params: Seq[String]): Boolean = {
|
||||
params match {
|
||||
case "invites" :: _ =>
|
||||
squadService ! SquadService.ListAllCurrentInvites
|
||||
|
||||
case "accept" :: names if names.contains("all") =>
|
||||
squadService ! SquadService.ChainAcceptance(player, player.CharId, Nil)
|
||||
case "accept" :: names if names.nonEmpty =>
|
||||
val results = names.flatMap { name =>
|
||||
LivePlayerList.WorldPopulation { case (_, p) => p.name.equals(name) }.map(_.id.toLong)
|
||||
}
|
||||
squadService ! SquadService.ChainAcceptance(player, player.CharId, results)
|
||||
|
||||
case "reject" :: names if names.contains("all") =>
|
||||
squadService ! SquadService.ChainRejection(player, player.CharId, Nil)
|
||||
case "reject" :: names if names.nonEmpty =>
|
||||
val results = names.flatMap { name =>
|
||||
LivePlayerList.WorldPopulation { case (_, p) => p.name.equals(name) }.map(_.id.toLong)
|
||||
}
|
||||
squadService ! SquadService.ChainRejection(player, player.CharId, results)
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
def firstParam[T](
|
||||
session: Session,
|
||||
buffer: Iterable[String],
|
||||
|
|
|
|||
|
|
@ -165,15 +165,15 @@ class SessionData(
|
|||
case LookupResult("squad", endpoint) =>
|
||||
squadService = endpoint
|
||||
buildDependentOperationsForSquad(endpoint)
|
||||
buildDependentOperationsForChat(chatService, endpoint, cluster)
|
||||
true
|
||||
case ICS.InterstellarClusterServiceKey.Listing(listings) =>
|
||||
cluster = listings.head
|
||||
buildDependentOperationsForZoning(galaxyService, cluster)
|
||||
buildDependentOperationsForChat(chatService, cluster)
|
||||
true
|
||||
case ChatService.ChatServiceKey.Listing(listings) =>
|
||||
chatService = listings.head
|
||||
buildDependentOperationsForChat(chatService, cluster)
|
||||
buildDependentOperationsForChat(chatService, squadService, cluster)
|
||||
true
|
||||
|
||||
case _ =>
|
||||
|
|
@ -200,9 +200,16 @@ class SessionData(
|
|||
}
|
||||
}
|
||||
|
||||
def buildDependentOperationsForChat(chatService: typed.ActorRef[ChatService.Command], clusterActor: typed.ActorRef[ICS.Command]): Unit = {
|
||||
if (chatOpt.isEmpty && chatService != Default.typed.Actor && clusterActor != Default.typed.Actor) {
|
||||
chatOpt = Some(new ChatOperations(sessionLogic=this, avatarActor, chatService, clusterActor, context))
|
||||
def buildDependentOperationsForChat(
|
||||
chatService: typed.ActorRef[ChatService.Command],
|
||||
squadService: ActorRef,
|
||||
clusterActor: typed.ActorRef[ICS.Command]
|
||||
): Unit = {
|
||||
if (chatOpt.isEmpty &&
|
||||
chatService != Default.typed.Actor &&
|
||||
squadService !=Default.Actor &&
|
||||
clusterActor != Default.typed.Actor) {
|
||||
chatOpt = Some(new ChatOperations(sessionLogic=this, avatarActor, chatService, squadService, clusterActor, context))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ package net.psforever.services.teamwork
|
|||
import akka.actor.ActorRef
|
||||
import akka.pattern.ask
|
||||
import akka.util.Timeout
|
||||
|
||||
import scala.annotation.unused
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -119,14 +121,15 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
val leader = squad2.Leader.CharId
|
||||
Allowed(invitedPlayer, invitingPlayer)
|
||||
Allowed(leader, invitingPlayer)
|
||||
lazy val preface = s"$invitingPlayer's invitation got reversed to $invitedPlayer's squad, but"
|
||||
if (squad2.Size == squad2.Capacity) {
|
||||
log.debug(s"$invitingPlayer's invitation got reversed to $invitedPlayer's squad, but the squad has no available positions")
|
||||
log.debug(s"$preface the squad has no available positions")
|
||||
} else if (Refused(invitingPlayer).contains(invitedPlayer)) {
|
||||
log.debug(s"$invitingPlayer's invitation got reversed to $invitedPlayer's squad, but $invitedPlayer repeated a previous refusal to $invitingPlayer's invitation offer")
|
||||
log.debug(s"$preface $invitedPlayer repeated a previous refusal to $invitingPlayer's invitation offer")
|
||||
} else if (Refused(invitingPlayer).contains(leader)) {
|
||||
log.debug(s"$invitingPlayer's invitation got reversed to $invitedPlayer's squad, but $leader repeated a previous refusal to $invitingPlayer's invitation offer")
|
||||
log.debug(s"$preface $leader repeated a previous refusal to $invitingPlayer's invitation offer")
|
||||
} else if (features.DeniedPlayers().contains(invitingPlayer)) {
|
||||
log.debug(s"$invitingPlayer's invitation got reversed to $invitedPlayer's squad, but $invitingPlayer is denied the invitation")
|
||||
log.debug(s"$preface $invitingPlayer is denied the invitation")
|
||||
} else {
|
||||
features.AllowedPlayers(invitedPlayer)
|
||||
AddInviteAndRespond(
|
||||
|
|
@ -165,7 +168,7 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
val availableForJoiningSquad = notLimitedByEnrollmentInSquad(invitedPlayerSquadOpt, invitedPlayer)
|
||||
acceptedInvite match {
|
||||
case Some(RequestRole(petitioner, features, position))
|
||||
if canEnrollInSquad(features, petitioner.CharId) =>
|
||||
if SquadInvitationManager.canEnrollInSquad(features, petitioner.CharId) =>
|
||||
//player requested to join a squad's specific position
|
||||
//invitedPlayer is actually the squad leader; petitioner is the actual "invitedPlayer"
|
||||
if (JoinSquad(petitioner, features, position)) {
|
||||
|
|
@ -174,7 +177,7 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
}
|
||||
|
||||
case Some(IndirectInvite(recruit, features))
|
||||
if canEnrollInSquad(features, recruit.CharId) =>
|
||||
if SquadInvitationManager.canEnrollInSquad(features, recruit.CharId) =>
|
||||
//tplayer / invitedPlayer is actually the squad leader
|
||||
val recruitCharId = recruit.CharId
|
||||
HandleVacancyInvite(features, recruitCharId, invitedPlayer, recruit) match {
|
||||
|
|
@ -188,7 +191,7 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
}
|
||||
|
||||
case Some(VacancyInvite(invitingPlayer, _, features))
|
||||
if availableForJoiningSquad && canEnrollInSquad(features, invitedPlayer) =>
|
||||
if availableForJoiningSquad && SquadInvitationManager.canEnrollInSquad(features, invitedPlayer) =>
|
||||
//accepted an invitation to join an existing squad
|
||||
HandleVacancyInvite(features, invitedPlayer, invitingPlayer, tplayer) match {
|
||||
case Some((_, line)) =>
|
||||
|
|
@ -204,7 +207,7 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
SquadMembershipAcceptInviteAction(invitingPlayer, tplayer, invitedPlayer)
|
||||
|
||||
case Some(LookingForSquadRoleInvite(member, features, position))
|
||||
if availableForJoiningSquad && canEnrollInSquad(features, invitedPlayer) =>
|
||||
if availableForJoiningSquad && SquadInvitationManager.canEnrollInSquad(features, invitedPlayer) =>
|
||||
val invitingPlayer = member.CharId
|
||||
features.ProxyInvites = features.ProxyInvites.filterNot { _ == invitedPlayer }
|
||||
if (JoinSquad(tplayer, features, position)) {
|
||||
|
|
@ -215,7 +218,7 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
}
|
||||
|
||||
case Some(ProximityInvite(member, features, position))
|
||||
if availableForJoiningSquad && canEnrollInSquad(features, invitedPlayer) =>
|
||||
if availableForJoiningSquad && SquadInvitationManager.canEnrollInSquad(features, invitedPlayer) =>
|
||||
val invitingPlayer = member.CharId
|
||||
features.ProxyInvites = features.ProxyInvites.filterNot { _ == invitedPlayer }
|
||||
if (JoinSquad(tplayer, features, position)) {
|
||||
|
|
@ -280,10 +283,6 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
}
|
||||
}
|
||||
|
||||
def canEnrollInSquad(features: SquadFeatures, charId: Long): Boolean = {
|
||||
!features.Squad.Membership.exists { _.CharId == charId }
|
||||
}
|
||||
|
||||
def SquadMembershipAcceptInviteAction(invitingPlayer: Player, player: Player, invitedPlayer: Long): Unit = {
|
||||
//originally, we were invited by someone into a new squad they would form
|
||||
val invitingPlayerCharId = invitingPlayer.CharId
|
||||
|
|
@ -416,24 +415,75 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
def handleRejection(
|
||||
tplayer: Player,
|
||||
rejectingPlayer: Long,
|
||||
squadsToLeaders: List[(PlanetSideGUID, Long)]
|
||||
squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||
): Unit = {
|
||||
val rejectedBid = RemoveInvite(rejectingPlayer)
|
||||
(rejectedBid match {
|
||||
FormatRejection(
|
||||
DoRejection(rejectedBid, tplayer, rejectingPlayer, squadsToLeaders),
|
||||
tplayer
|
||||
)
|
||||
NextInviteAndRespond(rejectingPlayer)
|
||||
}
|
||||
|
||||
def ParseRejection(
|
||||
rejectedBid: Option[Invitation],
|
||||
@unused tplayer: Player,
|
||||
rejectingPlayer: Long,
|
||||
squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||
): (Option[Long], Option[Long]) = {
|
||||
rejectedBid match {
|
||||
case Some(SpontaneousInvite(leader)) =>
|
||||
//rejectingPlayer is the would-be squad member; the would-be squad leader sent the request and was rejected
|
||||
val invitingPlayerCharId = leader.CharId
|
||||
(Some(rejectingPlayer), Some(invitingPlayerCharId))
|
||||
|
||||
case Some(VacancyInvite(leader, _, _))
|
||||
/*if SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejectingPlayer)*/ =>
|
||||
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||
(Some(rejectingPlayer), Some(leader))
|
||||
|
||||
case Some(ProximityInvite(_, _, _))
|
||||
/*if SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejectingPlayer)*/ =>
|
||||
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||
(Some(rejectingPlayer), None)
|
||||
|
||||
case Some(LookingForSquadRoleInvite(member, _, _))
|
||||
if member.CharId != rejectingPlayer =>
|
||||
val leaderCharId = member.CharId
|
||||
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||
(Some(rejectingPlayer), Some(leaderCharId))
|
||||
|
||||
case Some(RequestRole(rejected, features, _))
|
||||
if SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejected.CharId) =>
|
||||
//rejected is the would-be squad member; rejectingPlayer is the squad leader who rejected the request
|
||||
(Some(rejectingPlayer), None)
|
||||
|
||||
case _ => //TODO IndirectInvite, etc., but how to handle them?
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
|
||||
def DoRejection(
|
||||
rejectedBid: Option[Invitation],
|
||||
tplayer: Player,
|
||||
rejectingPlayer: Long,
|
||||
squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)]
|
||||
): (Option[Long], Option[Long], String) = {
|
||||
rejectedBid match {
|
||||
case Some(SpontaneousInvite(leader)) =>
|
||||
//rejectingPlayer is the would-be squad member; the would-be squad leader sent the request and was rejected
|
||||
val invitingPlayerCharId = leader.CharId
|
||||
Refused(rejectingPlayer, invitingPlayerCharId)
|
||||
(Some(rejectingPlayer), Some(invitingPlayerCharId))
|
||||
(Some(rejectingPlayer), Some(invitingPlayerCharId), "anonymous")
|
||||
|
||||
case Some(VacancyInvite(leader, _, _))
|
||||
/*if notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejectingPlayer)*/ =>
|
||||
case Some(VacancyInvite(leader, _, features))
|
||||
/*if SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejectingPlayer)*/ =>
|
||||
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||
Refused(rejectingPlayer, leader)
|
||||
(Some(rejectingPlayer), Some(leader))
|
||||
(Some(rejectingPlayer), Some(leader), features.Squad.Task)
|
||||
|
||||
case Some(ProximityInvite(_, features, position))
|
||||
/*if notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejectingPlayer)*/ =>
|
||||
/*if SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejectingPlayer)*/ =>
|
||||
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||
ReloadProximityInvite(
|
||||
tplayer.Zone.Players,
|
||||
|
|
@ -441,52 +491,53 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
features,
|
||||
position
|
||||
)
|
||||
(Some(rejectingPlayer), None)
|
||||
(Some(rejectingPlayer), None, features.Squad.Task)
|
||||
|
||||
case Some(LookingForSquadRoleInvite(member, guid, position))
|
||||
case Some(LookingForSquadRoleInvite(member, features, position))
|
||||
if member.CharId != rejectingPlayer =>
|
||||
val leaderCharId = member.CharId
|
||||
//rejectingPlayer is the would-be squad member; the squad leader sent the request and was rejected
|
||||
ReloadSearchForRoleInvite(
|
||||
LivePlayerList.WorldPopulation { _ => true },
|
||||
rejectingPlayer,
|
||||
guid,
|
||||
features,
|
||||
position
|
||||
)
|
||||
(Some(rejectingPlayer), Some(leaderCharId))
|
||||
(Some(rejectingPlayer), Some(leaderCharId), features.Squad.Task)
|
||||
|
||||
case Some(RequestRole(rejected, features, _))
|
||||
if notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejected.CharId) =>
|
||||
if SquadInvitationManager.notLeaderOfThisSquad(squadsToLeaders, features.Squad.GUID, rejected.CharId) =>
|
||||
//rejected is the would-be squad member; rejectingPlayer is the squad leader who rejected the request
|
||||
features.DeniedPlayers(rejected.CharId)
|
||||
(Some(rejectingPlayer), None)
|
||||
(Some(rejectingPlayer), None, features.Squad.Task)
|
||||
|
||||
case _ => ; //TODO IndirectInvite, etc., but how to handle them?
|
||||
(None, None)
|
||||
}) match {
|
||||
case (Some(rejected), Some(invited)) =>
|
||||
case _ => //TODO IndirectInvite, etc., but how to handle them?
|
||||
(None, None, "n|a")
|
||||
}
|
||||
}
|
||||
|
||||
def FormatRejection(
|
||||
rejectedPair: (Option[Long], Option[Long], String),
|
||||
tplayer: Player
|
||||
): Unit = {
|
||||
rejectedPair match {
|
||||
case (Some(rejected), Some(inviter), squadName) =>
|
||||
subs.Publish(
|
||||
rejected,
|
||||
SquadResponse.Membership(SquadResponseType.Reject, 0, 0, rejected, Some(invited), "", unk5=true, Some(None))
|
||||
SquadResponse.Membership(SquadResponseType.Reject, 0, 0, rejected, Some(inviter), "", unk5=true, Some(None))
|
||||
)
|
||||
subs.Publish(
|
||||
invited,
|
||||
SquadResponse.Membership(SquadResponseType.Reject, 0, 0, invited, Some(rejected), tplayer.Name, unk5=false, Some(None))
|
||||
inviter,
|
||||
SquadResponse.Membership(SquadResponseType.Reject, 0, 0, inviter, Some(rejected), tplayer.Name, unk5=false, Some(None))
|
||||
)
|
||||
case (Some(rejected), None) =>
|
||||
subs.Publish(rejected, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
case (Some(rejected), None, squadName) =>
|
||||
subs.Publish(
|
||||
rejected,
|
||||
SquadResponse.Membership(SquadResponseType.Reject, 0, 0, rejected, Some(rejected), "", unk5=true, Some(None))
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
NextInviteAndRespond(rejectingPlayer)
|
||||
}
|
||||
|
||||
def notLeaderOfThisSquad(squadsToLeaders: List[(PlanetSideGUID, Long)], guid: PlanetSideGUID, charId: Long): Boolean = {
|
||||
squadsToLeaders.find { case (squadGuid, _) => squadGuid == guid } match {
|
||||
case Some((_, leaderId)) => leaderId != charId
|
||||
case None => false
|
||||
subs.Publish(rejected, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -611,8 +662,14 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
tplayer: Player,
|
||||
features: SquadFeatures
|
||||
): Unit = {
|
||||
SquadActionDefinitionAutoApproveInvitationRequests(tplayer.CharId, features)
|
||||
}
|
||||
|
||||
def SquadActionDefinitionAutoApproveInvitationRequests(
|
||||
charId: Long,
|
||||
features: SquadFeatures
|
||||
): Unit = {
|
||||
//allowed auto-approval - resolve the requests (only)
|
||||
val charId = tplayer.CharId
|
||||
val (requests, others) =
|
||||
(invites.get(charId) match {
|
||||
case Some(invite) => invite +: queuedInvites.getOrElse(charId, Nil)
|
||||
|
|
@ -911,6 +968,155 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
}
|
||||
}
|
||||
|
||||
def listCurrentInvitations(charId: Long): List[String] = {
|
||||
((invites.get(charId), queuedInvites.get(charId)) match {
|
||||
case (Some(invite), Some(invites)) =>
|
||||
invite +: invites
|
||||
case (Some(invite), None) =>
|
||||
List(invite)
|
||||
case (None, Some(invites)) =>
|
||||
invites
|
||||
case _ =>
|
||||
List()
|
||||
}).collect {
|
||||
case RequestRole(player, _, _) => player.Name
|
||||
case IndirectInvite(player, features) if !features.Squad.Leader.Name.equals(player.Name) => player.Name
|
||||
}
|
||||
}
|
||||
|
||||
def tryChainAcceptance(
|
||||
inviter: Player,
|
||||
charId: Long,
|
||||
list: List[Long],
|
||||
features: SquadFeatures
|
||||
): Unit = {
|
||||
//filter queued invites
|
||||
lazy val squadToLeader = List((features.Squad.GUID, features))
|
||||
lazy val squadName = features.Squad.Task
|
||||
var foundPairs: List[(Player, Invitation)] = List()
|
||||
val unmatchedInvites = queuedInvites
|
||||
.getOrElse(charId, Nil)
|
||||
.filter {
|
||||
case invite @ RequestRole(invitee, _, _)
|
||||
if list.contains(invitee.CharId) && !features.Squad.Leader.Name.equals(invitee.Name) =>
|
||||
foundPairs = foundPairs :+ (invitee, invite)
|
||||
false
|
||||
case invite @ IndirectInvite(invitee, _)
|
||||
if list.contains(invitee.CharId) && !features.Squad.Leader.Name.equals(invitee.Name) =>
|
||||
foundPairs = foundPairs :+ (invitee, invite)
|
||||
false
|
||||
case _ =>
|
||||
true
|
||||
}
|
||||
//handle active invite
|
||||
val clearedActiveInvite = invites
|
||||
.get(charId)
|
||||
.collect {
|
||||
case invite @ RequestRole(invitee, _, _)
|
||||
if list.contains(invitee.CharId) && !features.Squad.Leader.Name.equals(invitee.Name) =>
|
||||
SquadActionMembershipAcceptInvite(inviter, invitee.CharId, Some(invite), Some(features))
|
||||
invites.remove(charId)
|
||||
true
|
||||
case invite @ IndirectInvite(invitee, _)
|
||||
if list.contains(invitee.CharId) && !features.Squad.Leader.Name.equals(invitee.Name) =>
|
||||
SquadActionMembershipAcceptInvite(inviter, invitee.CharId, Some(invite), Some(features))
|
||||
invites.remove(charId)
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
//handle selected queued invites
|
||||
val pairIterator = foundPairs.iterator
|
||||
while (pairIterator.hasNext && features.Squad.Capacity < features.Squad.Size) {
|
||||
val (player, invite) = pairIterator.next()
|
||||
SquadActionMembershipAcceptInvite(inviter, player.CharId, Some(invite), Some(features))
|
||||
}
|
||||
//evaluate final squad composition
|
||||
if (features.Squad.Capacity < features.Squad.Size) {
|
||||
//replace unfiltered invites
|
||||
if (unmatchedInvites.isEmpty) {
|
||||
queuedInvites.remove(charId)
|
||||
} else {
|
||||
queuedInvites.put(charId, unmatchedInvites)
|
||||
}
|
||||
//manage next invitation
|
||||
clearedActiveInvite.collect {
|
||||
case true => NextInviteAndRespond(charId)
|
||||
}
|
||||
} else {
|
||||
//squad is full
|
||||
previousInvites.remove(charId)
|
||||
queuedInvites.remove(charId)
|
||||
clearedActiveInvite.collect {
|
||||
case true => invites.remove(charId)
|
||||
}
|
||||
unmatchedInvites.foreach { invite =>
|
||||
val (refusedId, _) = ParseRejection(Some(invite), inviter, inviter.CharId, squadToLeader)
|
||||
subs.Publish(refusedId, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
}
|
||||
NextInviteAndRespond(charId)
|
||||
}
|
||||
//clean up any incomplete selected invites
|
||||
pairIterator.foreach { case (_, invite) =>
|
||||
val (refusedId, _) = ParseRejection(Some(invite), inviter, inviter.CharId, squadToLeader)
|
||||
subs.Publish(refusedId, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
}
|
||||
}
|
||||
|
||||
def tryChainRejection(
|
||||
inviter: Player,
|
||||
charId: Long,
|
||||
list: List[Long],
|
||||
features: SquadFeatures): Unit = {
|
||||
//handle queued invites
|
||||
lazy val squadToLeader = List((features.Squad.GUID, features))
|
||||
lazy val squadName = features.Squad.Task
|
||||
val unmatchedInvites = queuedInvites
|
||||
.getOrElse(charId, Nil)
|
||||
.filter {
|
||||
case invite @ RequestRole(invitee, _, _)
|
||||
if list.contains(invitee.CharId) && !features.Squad.Leader.Name.equals(invitee.Name) =>
|
||||
val (refusedId, _, _) = DoRejection(Some(invite), inviter, charId, squadToLeader)
|
||||
subs.Publish(refusedId, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
false
|
||||
case invite @ IndirectInvite(invitee, _)
|
||||
if list.contains(invitee.CharId) && !features.Squad.Leader.Name.equals(invitee.Name) =>
|
||||
val (refusedId, _, _) = DoRejection(Some(invite), inviter, charId, squadToLeader)
|
||||
subs.Publish(refusedId, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
false
|
||||
case _ =>
|
||||
true
|
||||
}
|
||||
queuedInvites.put(charId, unmatchedInvites)
|
||||
//handle active invite
|
||||
invites
|
||||
.get(charId)
|
||||
.collect {
|
||||
case invite @ RequestRole(player, features, _)
|
||||
if list.contains(player.CharId) && !features.Squad.Leader.Name.equals(player.Name) =>
|
||||
val (refusedId, _, _) = DoRejection(Some(invite), inviter, charId, squadToLeader)
|
||||
subs.Publish(refusedId, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
invites.remove(charId)
|
||||
NextInviteAndRespond(charId)
|
||||
case invite @ IndirectInvite(player, features)
|
||||
if list.contains(player.CharId) && !features.Squad.Leader.Name.equals(player.Name) =>
|
||||
val (refusedId, _, _) = DoRejection(Some(invite), inviter, charId, squadToLeader)
|
||||
subs.Publish(refusedId, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
invites.remove(charId)
|
||||
NextInviteAndRespond(charId)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def tryChainRejectionAll(charId: Long, features: SquadFeatures): Unit = {
|
||||
val squadName = features.Squad.Task
|
||||
CleanUpAllInvitesToSquad(features)
|
||||
.filterNot(_ == charId)
|
||||
.foreach { refusedId =>
|
||||
subs.Publish(refusedId, SquadResponse.SquadRelatedComment(s"Your request to join squad '$squadName' has been refused."))
|
||||
}
|
||||
}
|
||||
|
||||
def ShiftInvitesToPromotedSquadLeader(
|
||||
sponsoringPlayer: Long,
|
||||
promotedPlayer: Long
|
||||
|
|
@ -1695,18 +1901,18 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
* @see `VacancyInvite`
|
||||
* @param features the squad identifier
|
||||
*/
|
||||
def CleanUpAllInvitesToSquad(features: SquadFeatures): Unit = {
|
||||
def CleanUpAllInvitesToSquad(features: SquadFeatures): List[Long] = {
|
||||
val guid = features.Squad.GUID
|
||||
//clean up invites
|
||||
val activeInviteIds = {
|
||||
val keys = invites.keys.toSeq
|
||||
invites.values.zipWithIndex
|
||||
.collect {
|
||||
case (VacancyInvite(_, _, _squad), index) if _squad.Squad.GUID == guid => index
|
||||
case (IndirectInvite(_, _squad), index) if _squad.Squad.GUID == guid => index
|
||||
case (LookingForSquadRoleInvite(_, _squad, _), index) if _squad.Squad.GUID == guid => index
|
||||
case (ProximityInvite(_, _squad, _), index) if _squad.Squad.GUID == guid => index
|
||||
case (RequestRole(_, _squad, _), index) if _squad.Squad.GUID == guid => index
|
||||
case (VacancyInvite(_, _, f), index) if f.Squad.GUID == guid => index
|
||||
case (IndirectInvite(_, f), index) if f.Squad.GUID == guid => index
|
||||
case (LookingForSquadRoleInvite(_, f, _), index) if f.Squad.GUID == guid => index
|
||||
case (ProximityInvite(_, f, _), index) if f.Squad.GUID == guid => index
|
||||
case (RequestRole(_, f, _), index) if f.Squad.GUID == guid => index
|
||||
}
|
||||
.map { index =>
|
||||
val key = keys(index)
|
||||
|
|
@ -1723,12 +1929,12 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
case (queue, index) =>
|
||||
val key = keys(index)
|
||||
val (targets, retained) = queue.partition {
|
||||
case VacancyInvite(_, _, _squad) => _squad.Squad.GUID == guid
|
||||
case IndirectInvite(_, _squad) => _squad.Squad.GUID == guid
|
||||
case LookingForSquadRoleInvite(_, _squad, _) => _squad.Squad.GUID == guid
|
||||
case ProximityInvite(_, _squad, _) => _squad.Squad.GUID == guid
|
||||
case RequestRole(_, _squad, _) => _squad.Squad.GUID == guid
|
||||
case _ => false
|
||||
case VacancyInvite(_, _, f) => f.Squad.GUID == guid
|
||||
case IndirectInvite(_, f) => f.Squad.GUID == guid
|
||||
case LookingForSquadRoleInvite(_, f, _) => f.Squad.GUID == guid
|
||||
case ProximityInvite(_, f, _) => f.Squad.GUID == guid
|
||||
case RequestRole(_, f, _) => f.Squad.GUID == guid
|
||||
case _ => false
|
||||
}
|
||||
if (retained.isEmpty) {
|
||||
queuedInvites.remove(key)
|
||||
|
|
@ -1744,7 +1950,9 @@ class SquadInvitationManager(subs: SquadSubscriptionEntity, parent: ActorRef) {
|
|||
.flatten
|
||||
.toList
|
||||
}
|
||||
CleanUpSquadFeatures(activeInviteIds ++ queuedInviteIds, features, position = -1)
|
||||
val allInviteIds = (activeInviteIds ++ queuedInviteIds).distinct
|
||||
CleanUpSquadFeatures(allInviteIds, features, position = -1)
|
||||
allInviteIds
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2151,4 +2359,15 @@ object SquadInvitationManager {
|
|||
}
|
||||
|
||||
final case class FinishStartSquad(features: SquadFeatures)
|
||||
|
||||
def canEnrollInSquad(features: SquadFeatures, charId: Long): Boolean = {
|
||||
!features.Squad.Membership.exists { _.CharId == charId }
|
||||
}
|
||||
|
||||
def notLeaderOfThisSquad(squadsToLeaders: List[(PlanetSideGUID, SquadFeatures)], guid: PlanetSideGUID, charId: Long): Boolean = {
|
||||
squadsToLeaders.find { case (squadGuid, _) => squadGuid == guid } match {
|
||||
case Some((_, features)) => features.Squad.Leader.CharId != charId
|
||||
case None => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
package net.psforever.services.teamwork
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Terminated}
|
||||
import net.psforever.actors.session.SessionActor
|
||||
import net.psforever.packet.game.ChatMsg
|
||||
import net.psforever.types.ChatMessageType
|
||||
|
||||
import java.io.{PrintWriter, StringWriter}
|
||||
import scala.annotation.unused
|
||||
|
|
@ -111,7 +114,7 @@ class SquadService extends Actor {
|
|||
* @return `true`, if the identifier is reset; `false`, otherwise
|
||||
*/
|
||||
def TryResetSquadId(): Boolean = {
|
||||
if (squadFeatures.isEmpty) {
|
||||
if (squadFeatures.isEmpty && LivePlayerList.WorldPopulation(_ => true).isEmpty) {
|
||||
sid = 1
|
||||
true
|
||||
} else {
|
||||
|
|
@ -199,7 +202,7 @@ class SquadService extends Actor {
|
|||
LeaveInGeneral(sender())
|
||||
|
||||
case Terminated(actorRef) =>
|
||||
TerminatedBy(actorRef)
|
||||
LeaveInGeneral(actorRef)
|
||||
|
||||
case message @ SquadServiceMessage(tplayer, zone, squad_action) =>
|
||||
squad_action match {
|
||||
|
|
@ -243,16 +246,39 @@ class SquadService extends Actor {
|
|||
case SquadService.ResendActiveInvite(charId) =>
|
||||
invitations.resendActiveInvite(charId)
|
||||
|
||||
case SquadService.ListAllCurrentInvites(charId) =>
|
||||
ListCurrentInvitations(charId)
|
||||
|
||||
case SquadService.ChainAcceptance(player, charId, list) =>
|
||||
ChainAcceptanceIntoSquad(player, charId, list)
|
||||
|
||||
case SquadService.ChainRejection(player, charId, list) =>
|
||||
ChainRejectionFromSquad(player, charId, list)
|
||||
|
||||
case msg =>
|
||||
log.warn(s"Unhandled message $msg from ${sender()}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a faction-wide channel.
|
||||
* @param faction sub-channel name
|
||||
* @param sender subscriber
|
||||
*/
|
||||
def JoinByFaction(faction: String, sender: ActorRef): Unit = {
|
||||
val path = s"/$faction/Squad"
|
||||
log.trace(s"$sender has joined $path")
|
||||
subs.SquadEvents.subscribe(sender, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a client-specific channel.
|
||||
* The channel name is expected to be a `Long` number but is passed as a `String`.
|
||||
* As is the case, the actual channel name may not parse into a proper long integer after being passed and
|
||||
* there are failure cases.
|
||||
* @param charId sub-channel name
|
||||
* @param sender subscriber
|
||||
* @see `SquadInvitationManager.handleJoin`
|
||||
*/
|
||||
def JoinByCharacterId(charId: String, sender: ActorRef): Unit = {
|
||||
try {
|
||||
val longCharId = charId.toLong
|
||||
|
|
@ -272,12 +298,23 @@ class SquadService extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from a faction-wide channel.
|
||||
* @param faction sub-channel name
|
||||
* @param sender subscriber
|
||||
*/
|
||||
def LeaveByFaction(faction: String, sender: ActorRef): Unit = {
|
||||
val path = s"/$faction/Squad"
|
||||
log.trace(s"$sender has left $path")
|
||||
subs.SquadEvents.unsubscribe(sender, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from a client-specific channel.
|
||||
* @param charId sub-channel name
|
||||
* @param sender subscriber
|
||||
* @see `LeaveService`
|
||||
*/
|
||||
def LeaveByCharacterId(charId: String, sender: ActorRef): Unit = {
|
||||
try {
|
||||
LeaveService(charId.toLong, sender)
|
||||
|
|
@ -292,23 +329,23 @@ class SquadService extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming a subscriber that matches previously subscribed data,
|
||||
* completely unsubscribe and forget this entry.
|
||||
* @param sender subscriber
|
||||
* @see `LeaveService`
|
||||
*/
|
||||
def LeaveInGeneral(sender: ActorRef): Unit = {
|
||||
subs.UserEvents find { case (_, subscription) => subscription.path.equals(sender.path) } match {
|
||||
context.unwatch(sender)
|
||||
subs.UserEvents.find {
|
||||
case (_, subscription) => (subscription eq sender) || subscription.path.equals(sender.path)
|
||||
} match {
|
||||
case Some((to, _)) =>
|
||||
LeaveService(to, sender)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def TerminatedBy(requestee: ActorRef): Unit = {
|
||||
context.unwatch(requestee)
|
||||
subs.UserEvents find { case (_, subscription) => subscription eq requestee } match {
|
||||
case Some((to, _)) =>
|
||||
LeaveService(to, requestee)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def performStartSquad(sender: ActorRef, player: Player): Unit = {
|
||||
val invitingPlayerCharId = player.CharId
|
||||
if (EnsureEmptySquad(invitingPlayerCharId)) {
|
||||
|
|
@ -544,7 +581,7 @@ class SquadService extends Actor {
|
|||
invitations.handleRejection(
|
||||
tplayer,
|
||||
rejectingPlayer,
|
||||
squadFeatures.map { case (guid, features) => (guid, features.Squad.Leader.CharId) }.toList
|
||||
squadFeatures.map { case (guid, features) => (guid, features) }.toList
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1084,9 +1121,14 @@ class SquadService extends Actor {
|
|||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* Completely remove information about a former subscriber from the squad management service.
|
||||
* @param charId the player's unique character identifier number
|
||||
* @param sender the `ActorRef` associated with this character
|
||||
* @see `DisbandSquad`
|
||||
* @see `LeaveSquad`
|
||||
* @see `SquadInvitationManager.handleLeave`
|
||||
* @see `SquadSwitchboard.PanicLeaveSquad`
|
||||
* @see `TryResetSquadId`
|
||||
*/
|
||||
def LeaveService(charId: Long, sender: ActorRef): Unit = {
|
||||
subs.MonitorSquadDetails.subtractOne(charId)
|
||||
|
|
@ -1129,8 +1171,7 @@ class SquadService extends Actor {
|
|||
}
|
||||
subs.SquadEvents.unsubscribe(sender) //just to make certain
|
||||
searchData.remove(charId)
|
||||
//todo turn this back on. See PR 1157 for why it was commented out.
|
||||
//TryResetSquadId()
|
||||
TryResetSquadId()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1285,6 +1326,50 @@ class SquadService extends Actor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
def ListCurrentInvitations(charId: Long) : Unit = {
|
||||
GetLeadingSquad(charId, None)
|
||||
.map { _ =>
|
||||
invitations.listCurrentInvitations(charId)
|
||||
}
|
||||
.collect {
|
||||
case listOfInvites if listOfInvites.nonEmpty =>
|
||||
listOfInvites match {
|
||||
case active :: queued if queued.nonEmpty =>
|
||||
subs.Publish(charId, SquadResponse.WantsSquadPosition(charId, s"$active, ${queued.mkString(", ")}"))
|
||||
listOfInvites
|
||||
case active :: _ =>
|
||||
subs.Publish(charId, SquadResponse.WantsSquadPosition(charId, active))
|
||||
listOfInvites
|
||||
}
|
||||
}
|
||||
.orElse {
|
||||
context.self ! SessionActor.SendResponse(ChatMsg(ChatMessageType.UNK_227, "You do not have any current invites to manage."))
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def ChainAcceptanceIntoSquad(player: Player, charId: Long, listOfCharIds: List[Long]): Unit = {
|
||||
GetLeadingSquad(charId, None)
|
||||
.foreach { features =>
|
||||
if (listOfCharIds.nonEmpty) {
|
||||
invitations.tryChainAcceptance(player, charId, listOfCharIds, features)
|
||||
} else {
|
||||
invitations.SquadActionDefinitionAutoApproveInvitationRequests(charId, features)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def ChainRejectionFromSquad(player: Player, charId: Long, listOfCharIds: List[Long]): Unit = {
|
||||
GetLeadingSquad(charId, None)
|
||||
.foreach { features =>
|
||||
if (listOfCharIds.nonEmpty) {
|
||||
invitations.tryChainRejection(player, charId, listOfCharIds, features)
|
||||
} else {
|
||||
invitations.tryChainRejectionAll(charId, features)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SquadService {
|
||||
|
|
@ -1294,6 +1379,12 @@ object SquadService {
|
|||
|
||||
final case class ResendActiveInvite(charId: Long)
|
||||
|
||||
final case class ListAllCurrentInvites(charId: Long)
|
||||
|
||||
final case class ChainAcceptance(player: Player, charId: Long, listOfCharIds: List[Long])
|
||||
|
||||
final case class ChainRejection(player: Player, charId: Long, listOfCharIds: List[Long])
|
||||
|
||||
/**
|
||||
* A message to indicate that the squad list needs to update for the clients.
|
||||
* @param features the squad
|
||||
|
|
|
|||
|
|
@ -73,4 +73,6 @@ object SquadResponse {
|
|||
unk2: Int,
|
||||
zoneNumber: Int
|
||||
) extends Response
|
||||
|
||||
final case class SquadRelatedComment(str: String) extends Response
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ class SquadSubscriptionEntity {
|
|||
case str if str.matches("\\d+") =>
|
||||
Publish(to.toLong, msg, excluded)
|
||||
case _ =>
|
||||
log.warn(s"Publish(String): subscriber information is an unhandled format - $to")
|
||||
log.warn(s"Publish(String): subscriber information is an unhandled format - $to; message $msg dropped")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ class SquadSubscriptionEntity {
|
|||
case Some(user) =>
|
||||
user ! SquadServiceResponse("", msg)
|
||||
case None =>
|
||||
log.warn(s"Publish(Long): subscriber information can not be found - $to")
|
||||
log.warn(s"Publish(Long): subscriber information can not be found - $to; message $msg dropped")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ class SquadSubscriptionEntity {
|
|||
* @param msg a message that can be stored in a `SquadServiceResponse` object
|
||||
*/
|
||||
def Publish[ANY >: Any](to: ANY, msg: SquadResponse.Response): Unit = {
|
||||
log.warn(s"Publish(Any): subscriber information is an unhandled format - $to")
|
||||
log.warn(s"Publish(Any): subscriber information is an unhandled format - $to; message $msg dropped")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -187,7 +187,7 @@ class SquadSubscriptionEntity {
|
|||
* @param excluded a group of character identifier numbers who should not receive the message
|
||||
*/
|
||||
def Publish[ANY >: Any](to: ANY, msg: SquadResponse.Response, excluded: Iterable[Long]): Unit = {
|
||||
log.warn(s"Publish(Any): subscriber information is an unhandled format - $to")
|
||||
log.warn(s"Publish(Any): subscriber information is an unhandled format - $to; message $msg dropped")
|
||||
}
|
||||
|
||||
/* The following functions are related to common communications of squad information, mainly detail. */
|
||||
|
|
@ -211,7 +211,7 @@ class SquadSubscriptionEntity {
|
|||
}
|
||||
|
||||
/**
|
||||
* Dispatch an intial message entailing the strategic information and the composition of this squad.
|
||||
* Dispatch an initial message entailing the strategic information and the composition of this squad.
|
||||
* The details of the squad will be updated in full and be sent to all indicated observers.
|
||||
* @see `SquadService.PublishFullDetails`
|
||||
* @param guid the unique squad identifier to be used when composing the details for this message
|
||||
|
|
|
|||
Loading…
Reference in a new issue