Ignore leave events for spectators.

Spectators produce leave events, but they won't have a join event that
matches the leave event they produce. This is because spectators either
joined _as_ spectators (spectating doesn't produce join events), or they
_transitioned_ into the spectator state after _dying_ in the session (in
which case they already produced a death event that "cements" the player
session).

This fixes an edge case problem that occurs when a spectator produces a
leave event while exactly one player is currently in the lobby. In this
case, the leave event triggers a session deletion, since the leave event
logic "thinks" the leave event is triggered by the player in the lobby.
It would be possible to prevent that problem with additional checks in
the leave event logic, but the underlying problem really is that leave
events produced by spectators shouldn't be recorded or reacted to in any
way, shape, or form, as they are not relevant to the stats (leave times
are only really relevant if the leave event happens _during_ a session,
which this change does not affect).
This commit is contained in:
Andreas Troelsen
2022-07-25 17:30:19 +02:00
parent 85df0066f1
commit 9e92094501
2 changed files with 21 additions and 0 deletions
@@ -88,6 +88,10 @@ public class SessionListener implements Listener {
Arena arena = event.getArena(); Arena arena = event.getArena();
Player player = event.getPlayer(); Player player = event.getPlayer();
if (arena.inSpec(player)) {
return;
}
Session session = sessionStore.getByArena(arena); Session session = sessionStore.getByArena(arena);
if (session == null) { if (session == null) {
log.warning("Unexpected leave event for non-existent session of arena " + arena.getSlug()); log.warning("Unexpected leave event for non-existent session of arena " + arena.getSlug());
@@ -110,10 +110,23 @@ class SessionListenerTest {
verify(session).playerReady(player, className); verify(session).playerReady(player, className);
} }
@Test
void ignoresPlayerLeaveForSpectators() {
Player player = mock(Player.class);
Arena arena = mock(Arena.class);
when(arena.inSpec(player)).thenReturn(true);
ArenaPlayerLeaveEvent event = new ArenaPlayerLeaveEvent(player, arena);
subject.on(event);
verifyNoInteractions(sessionStore);
}
@Test @Test
void logsWarningIfPlayerLeavesInNonExistentSession() { void logsWarningIfPlayerLeavesInNonExistentSession() {
Player player = mock(Player.class); Player player = mock(Player.class);
Arena arena = mock(Arena.class); Arena arena = mock(Arena.class);
when(arena.inSpec(player)).thenReturn(false);
when(sessionStore.getByArena(arena)).thenReturn(null); when(sessionStore.getByArena(arena)).thenReturn(null);
ArenaPlayerLeaveEvent event = new ArenaPlayerLeaveEvent(player, arena); ArenaPlayerLeaveEvent event = new ArenaPlayerLeaveEvent(player, arena);
@@ -127,6 +140,7 @@ class SessionListenerTest {
Player player = mock(Player.class); Player player = mock(Player.class);
Arena arena = mock(Arena.class); Arena arena = mock(Arena.class);
Session session = mock(Session.class); Session session = mock(Session.class);
when(arena.inSpec(player)).thenReturn(false);
when(arena.isRunning()).thenReturn(false); when(arena.isRunning()).thenReturn(false);
when(arena.getPlayersInLobby()).thenReturn(Collections.singleton(player)); when(arena.getPlayersInLobby()).thenReturn(Collections.singleton(player));
when(sessionStore.getByArena(arena)).thenReturn(session); when(sessionStore.getByArena(arena)).thenReturn(session);
@@ -142,6 +156,7 @@ class SessionListenerTest {
Player player = mock(Player.class); Player player = mock(Player.class);
Arena arena = mock(Arena.class); Arena arena = mock(Arena.class);
Session session = mock(Session.class); Session session = mock(Session.class);
when(arena.inSpec(player)).thenReturn(false);
when(arena.isRunning()).thenReturn(false); when(arena.isRunning()).thenReturn(false);
when(arena.getPlayersInLobby()).thenReturn(Collections.singleton(player)); when(arena.getPlayersInLobby()).thenReturn(Collections.singleton(player));
when(sessionStore.getByArena(arena)).thenReturn(session); when(sessionStore.getByArena(arena)).thenReturn(session);
@@ -158,6 +173,7 @@ class SessionListenerTest {
Player other = mock(Player.class); Player other = mock(Player.class);
Arena arena = mock(Arena.class); Arena arena = mock(Arena.class);
Session session = mock(Session.class); Session session = mock(Session.class);
when(arena.inSpec(player)).thenReturn(false);
when(arena.isRunning()).thenReturn(false); when(arena.isRunning()).thenReturn(false);
when(arena.getPlayersInLobby()).thenReturn(new HashSet<>(Arrays.asList(player, other))); when(arena.getPlayersInLobby()).thenReturn(new HashSet<>(Arrays.asList(player, other)));
when(sessionStore.getByArena(arena)).thenReturn(session); when(sessionStore.getByArena(arena)).thenReturn(session);
@@ -173,6 +189,7 @@ class SessionListenerTest {
Player player = mock(Player.class); Player player = mock(Player.class);
Arena arena = mock(Arena.class); Arena arena = mock(Arena.class);
Session session = mock(Session.class); Session session = mock(Session.class);
when(arena.inSpec(player)).thenReturn(false);
when(arena.isRunning()).thenReturn(true); when(arena.isRunning()).thenReturn(true);
when(sessionStore.getByArena(arena)).thenReturn(session); when(sessionStore.getByArena(arena)).thenReturn(session);
ArenaPlayerLeaveEvent event = new ArenaPlayerLeaveEvent(player, arena); ArenaPlayerLeaveEvent event = new ArenaPlayerLeaveEvent(player, arena);