diff --git a/src/main/scala/net/psforever/actors/session/ChatActor.scala b/src/main/scala/net/psforever/actors/session/ChatActor.scala index 10280294..3d8ede65 100644 --- a/src/main/scala/net/psforever/actors/session/ChatActor.scala +++ b/src/main/scala/net/psforever/actors/session/ChatActor.scala @@ -117,7 +117,7 @@ class ChatActor( case LeaveChannel(channel) => chatService ! ChatService.LeaveChannel(chatServiceAdapter, channel) - channels = channels.filter(_ == channel) + channels = channels.filterNot(_ == channel) Behaviors.same case Message(message) => diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index f28afc5a..7359722c 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -775,7 +775,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con None ) } - .toList ) ) @@ -3303,6 +3302,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con */ def ZoneChangeSquadSetup(): Unit = { RespawnSquadSetup() + squadService ! SquadServiceMessage(player, continent, SquadServiceAction.InitSquadList()) GiveSquadColorsInZone() squadSetup = RespawnSquadSetup } diff --git a/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala b/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala index f4bee28b..4d2f9c29 100644 --- a/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala +++ b/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala @@ -33,10 +33,11 @@ class SquadFeatures(val Squad: Squad) { *
* All of the waypoints constantly exist as long as the squad to which they are attached exists. * They are merely "activated" and "deactivated." - * When "activated," the waypoint knows on which continent to appear and where on the map and in the game world to be positioned. - * Waypoints manifest in the game world as a far-off beam of light that extends into the sky + * When "activated," the waypoint knows on which continent to appear + * and where on the map and in the game world to be positioned. + * Waypoints manifest in the game world as a (usually far-off) beam of light that extends into the sky * and whose ground contact utilizes a downwards pulsating arrow. - * On the continental map and deployment map, they appear as a diamond, with a differentiating number where applicable. + * On the continental map and deployment map, they appear as a diamond, with a different number where applicable. * The squad leader experience rally, for example, does not have a number like the preceding four waypoints. * @see `Start` */ @@ -44,10 +45,10 @@ class SquadFeatures(val Squad: Squad) { /** * The particular position being recruited right at the moment. - * When `None`. no highlighted searches have been indicated. + * When `None`, no highlighted searches have been indicated. * When a positive integer or 0, indicates distributed `LookingForSquadRoleInvite` messages as recorded by `proxyInvites`. - * Only one position may bne actively recruited at a time in this case. - * When -1, indicates distributed `ProximityIvite` messages as recorded by `proxyInvites`. + * Only one position may be actively recruited at a time in this case. + * When -1, indicates distributed `ProximityInvite` messages as recorded by `proxyInvites`. * Previous efforts may or may not be forgotten if there is a switch between the two modes. */ private var searchForRole: Option[Int] = None @@ -58,14 +59,14 @@ class SquadFeatures(val Squad: Squad) { private var proxyInvites: List[Long] = Nil /** - * These useres rejected invitation to this squad. + * These users rejected invitation to this squad. * For the purposes of wide-searches for membership * such as Looking For Squad checks and proximity invitation, * the unique character identifier numbers in this list are skipped. * Direct invitation requests from the non sqad member should remain functional. */ private var refusedPlayers: List[Long] = Nil - private var autoApproveInvitationRequests: Boolean = true + private var autoApproveInvitationRequests: Boolean = false private var locationFollowsSquadLead: Boolean = true private var listed: Boolean = false diff --git a/src/main/scala/net/psforever/services/chat/ChatService.scala b/src/main/scala/net/psforever/services/chat/ChatService.scala index 9bbcd115..0ea7ee04 100644 --- a/src/main/scala/net/psforever/services/chat/ChatService.scala +++ b/src/main/scala/net/psforever/services/chat/ChatService.scala @@ -50,21 +50,21 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe this case LeaveChannel(actor, channel) => - subscriptions = subscriptions.filter { - case JoinChannel(a, _, c) => actor != a && channel != c + subscriptions = subscriptions.filterNot { + case JoinChannel(a, _, c) => actor == a && channel == c } this case LeaveAllChannels(actor) => - subscriptions = subscriptions.filter { - case JoinChannel(a, _, _) => actor != a + subscriptions = subscriptions.filterNot { + case JoinChannel(a, _, _) => actor == a } this case Message(session, message, channel) => (channel, message.messageType) match { - case (ChatChannel.Squad(_), CMT_SQUAD) => - case (ChatChannel.Default(), messageType) if messageType != CMT_SQUAD => + case (ChatChannel.Squad(_), CMT_SQUAD) => ; + case (ChatChannel.Default(), messageType) if messageType != CMT_SQUAD => ; case _ => log.error(s"invalid chat channel $channel for messageType ${message.messageType}") return this diff --git a/src/main/scala/net/psforever/services/teamwork/SquadService.scala b/src/main/scala/net/psforever/services/teamwork/SquadService.scala index 65fe1972..6136ec6d 100644 --- a/src/main/scala/net/psforever/services/teamwork/SquadService.scala +++ b/src/main/scala/net/psforever/services/teamwork/SquadService.scala @@ -553,7 +553,9 @@ class SquadService extends Actor { case (Some(squad1), Some(squad2)) if squad2.Size == 1 => //both players belong to squads, but the invitedPlayer's squad (squad2) is underutilized by comparison //treat the same as "the classic situation" using squad1 - if (!Refused(invitedPlayer).contains(invitingPlayer)) { + if (squad1.Size == squad1.Capacity) { + debug(s"$invitingPlayer tried to invite $invitedPlayer to a squad without available positions") + } else if (!Refused(invitedPlayer).contains(invitingPlayer)) { val charId = tplayer.CharId AddInviteAndRespond( invitedPlayer, @@ -561,16 +563,20 @@ class SquadService extends Actor { charId, tplayer.Name ) + } else { + debug(s"$invitedPlayer repeated a previous refusal to $invitingPlayer's invitation offer") } case (Some(squad1), Some(squad2)) if squad1.Size == 1 => //both players belong to squads, but the invitingPlayer's squad is underutilized by comparison //treat the same as "indirection ..." using squad2 val leader = squad2.Leader.CharId - if (Refused(invitingPlayer).contains(invitedPlayer)) { - debug(s"$invitedPlayer repeated a previous refusal to $invitingPlayer's invitation offer") + if (squad2.Size == squad2.Capacity) { + debug(s"$invitingPlayer's invitation got reversed to $invitedPlayer's squad, but the squad has no available positions") + } else if (Refused(invitingPlayer).contains(invitedPlayer)) { + debug(s"$invitingPlayer's invitation got reversed to $invitedPlayer's squad, but $invitedPlayer repeated a previous refusal to $invitingPlayer's invitation offer") } else if (Refused(invitingPlayer).contains(leader)) { - debug(s"$invitedPlayer repeated a previous refusal to $leader's invitation offer") + debug(s"$invitingPlayer's invitation got reversed to $invitedPlayer's squad, but $leader repeated a previous refusal to $invitingPlayer's invitation offer") } else { AddInviteAndRespond( leader, @@ -582,7 +588,9 @@ class SquadService extends Actor { case (Some(squad), None) => //the classic situation - if (!Refused(invitedPlayer).contains(invitingPlayer)) { + if (squad.Size == squad.Capacity) { + debug(s"$invitingPlayer tried to invite $invitedPlayer to a squad without available positions") + } else if (!Refused(invitedPlayer).contains(invitingPlayer)) { AddInviteAndRespond( invitedPlayer, VacancyInvite(tplayer.CharId, tplayer.Name, squad.GUID), @@ -596,10 +604,12 @@ class SquadService extends Actor { case (None, Some(squad)) => //indirection; we're trying to invite ourselves to someone else's squad val leader = squad.Leader.CharId - if (Refused(invitingPlayer).contains(invitedPlayer)) { - debug(s"$invitedPlayer repeated a previous refusal to $invitingPlayer's invitation offer") + if (squad.Size == squad.Capacity) { + debug(s"$invitingPlayer tried to invite to $invitedPlayer's squad, but the squad has no available positions") + } else if (Refused(invitingPlayer).contains(invitedPlayer)) { + debug(s"invitingPlayer tried to invite to $invitedPlayer's squad, but $invitedPlayer repeated a previous refusal to $invitingPlayer's invitation offer") } else if (Refused(invitingPlayer).contains(leader)) { - debug(s"$invitedPlayer repeated a previous refusal to $leader's invitation offer") + debug(s"invitingPlayer tried to invite to $invitedPlayer's squad, but $leader repeated a previous refusal to $invitingPlayer's invitation offer") } else { AddInviteAndRespond( squad.Leader.CharId, @@ -658,62 +668,64 @@ class SquadService extends Actor { //positions that can be recruited to val positions = squad.Membership.zipWithIndex .collect { case (member, index) if member.CharId == 0 && squad.Availability(index) => member } - /* - players who are: - - the same faction as the squad - - have Looking For Squad enabled - - do not currently belong to a squad - - are denied the opportunity to be invited - - are a certain distance from the squad leader (n < 25m) - */ - (zone.LivePlayers - .collect { - case player - if player.Faction == faction && player.avatar.lookingForSquad && - (memberToSquad.get(player.CharId).isEmpty || memberToSquad(player.CharId).Size == 1) && - !excusedInvites - .contains(player.CharId) && Refused(player.CharId).contains(squad.Leader.CharId) && - Vector3.DistanceSquared(player.Position, center) < 625f && { - positions - .map { role => - val requirementsToMeet = role.Requirements - requirementsToMeet.intersect(player.avatar.certifications) == requirementsToMeet - } - .foldLeft(false)(_ || _) - } => - player.CharId + if (positions.nonEmpty) { + /* + players who are: + - the same faction as the squad + - have Looking For Squad enabled + - do not currently belong to a squad + - are denied the opportunity to be invited + - are a certain distance from the squad leader (n < 25m) + */ + (zone.LivePlayers + .collect { + case player + if player.Faction == faction && player.avatar.lookingForSquad && + (memberToSquad.get(player.CharId).isEmpty || memberToSquad(player.CharId).Size == 1) && + !excusedInvites + .contains(player.CharId) && Refused(player.CharId).contains(squad.Leader.CharId) && + Vector3.DistanceSquared(player.Position, center) < 625f && { + positions + .map { role => + val requirementsToMeet = role.Requirements + requirementsToMeet.intersect(player.avatar.certifications) == requirementsToMeet + } + .foldLeft(false)(_ || _) + } => + player.CharId + } + .partition { charId => outstandingActiveInvites.contains(charId) } match { + case (Nil, Nil) => + //no one found + outstandingActiveInvites foreach RemoveInvite + features.ProxyInvites = Nil + None + case (outstandingPlayerList, invitedPlayerList) => + //players who were actively invited for the previous position and are eligible for the new position + features.SearchForRole = Some(-1) + outstandingPlayerList.foreach { charId => + val bid = invites(charId).asInstanceOf[LookingForSquadRoleInvite] + invites(charId) = ProximityInvite(bid.char_id, bid.name, sguid) + } + //players who were actively invited for the previous position but are ineligible for the new position + (features.ProxyInvites filterNot (outstandingPlayerList contains)) foreach RemoveInvite + features.ProxyInvites = outstandingPlayerList ++ invitedPlayerList + Some(invitedPlayerList) + }) match { + //add invitations for position in squad + case Some(invitedPlayers) => + val invitingPlayer = tplayer.CharId + val name = tplayer.Name + invitedPlayers.foreach { invitedPlayer => + AddInviteAndRespond( + invitedPlayer, + ProximityInvite(invitingPlayer, name, sguid), + invitingPlayer, + name + ) + } + case None => ; } - .partition { charId => outstandingActiveInvites.contains(charId) } match { - case (Nil, Nil) => - //no one found - outstandingActiveInvites foreach RemoveInvite - features.ProxyInvites = Nil - None - case (outstandingPlayerList, invitedPlayerList) => - //players who were actively invited for the previous position and are eligible for the new position - features.SearchForRole = Some(-1) - outstandingPlayerList.foreach { charId => - val bid = invites(charId).asInstanceOf[LookingForSquadRoleInvite] - invites(charId) = ProximityInvite(bid.char_id, bid.name, sguid) - } - //players who were actively invited for the previous position but are ineligible for the new position - (features.ProxyInvites filterNot (outstandingPlayerList contains)) foreach RemoveInvite - features.ProxyInvites = outstandingPlayerList ++ invitedPlayerList - Some(invitedPlayerList) - }) match { - //add invitations for position in squad - case Some(invitedPlayers) => - val invitingPlayer = tplayer.CharId - val name = tplayer.Name - invitedPlayers.foreach { invitedPlayer => - AddInviteAndRespond( - invitedPlayer, - ProximityInvite(invitingPlayer, name, sguid), - invitingPlayer, - name - ) - } - case None => ; } } @@ -2772,7 +2784,7 @@ class SquadService extends Actor { * At this point in the squad join process, the only consent required is that of the squad leader. * An automatic consent flag exists on the squad; * but, if that is not set, then the squad leader must be asked whether or not to accept or to reject the recruit. - * If the squad leader changes in the middle of the latter half of the process, + * If the squad leader changes in the middle or the latter half of the process, * the invitation may still fail even if the old squad leader accepts. * If the squad leader changes in the middle of the latter half of the process, * the inquiry might be posed again of the new squad leader, of whether to accept or to reject the recruit. @@ -2908,7 +2920,7 @@ class SquadService extends Actor { .unzip { case (member, index) => (member.CharId, index) } val toChannel = features.ToChannel memberCharIds.foreach { charId => - SquadEvents.subscribe(events, s"/$toChannel/Squad") + SquadEvents.subscribe(UserEvents(charId), s"/$toChannel/Squad") Publish( charId, SquadResponse.Join( @@ -2923,8 +2935,7 @@ class SquadService extends Actor { InitSquadDetail(squad) } else { //joining an active squad; everybody updates differently - val updatedIndex = List(position) - val toChannel = features.ToChannel + val toChannel = features.ToChannel //new member gets full squad UI updates Publish( charId, @@ -2938,7 +2949,7 @@ class SquadService extends Actor { ) ) //other squad members see new member joining the squad - Publish(toChannel, SquadResponse.Join(squad, updatedIndex, "")) + Publish(toChannel, SquadResponse.Join(squad, List(position), "")) InitWaypoints(charId, squad.GUID) InitSquadDetail(squad.GUID, Seq(charId), squad) UpdateSquadDetail(