Skip to content

Commit 18b96d4

Browse files
authored
Merge pull request #11 from BentoBoxWorld/fix/teamlistener-corrupts-other-gamemode-islands
Fix TeamListener corrupting range on other game modes' islands
2 parents fccdd8c + a881760 commit 18b96d4

2 files changed

Lines changed: 137 additions & 4 deletions

File tree

src/main/java/world/bentobox/stranger/listeners/TeamListener.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,28 @@ public void onTeamJoin(TeamJoinedEvent e) {
6868
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
6969
public void onTeamKick(TeamKickEvent e) {
7070
if (!addon.inWorld(e.getIsland().getWorld()) // Not in game world
71-
|| addon.getSettings().getMemberBonus() != 0) {
72-
resize(e.getIsland());
71+
|| addon.getSettings().getMemberBonus() == 0) { // No resizing
72+
return;
7373
}
74+
resize(e.getIsland());
7475
}
7576

7677
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
7778
public void onTeamLeave(TeamLeaveEvent e) {
7879
if (!addon.inWorld(e.getIsland().getWorld()) // Not in game world
79-
|| addon.getSettings().getMemberBonus() != 0) {
80-
resize(e.getIsland());
80+
|| addon.getSettings().getMemberBonus() == 0) { // No resizing
81+
return;
8182
}
83+
resize(e.getIsland());
8284
}
8385

8486
protected int resize(Island claim) {
87+
// Guard: never resize an island that isn't governed by StrangerRealms.
88+
// Resizing would call setRange/setProtectionRange with StrangerRealms' distance,
89+
// corrupting the range of islands belonging to other game modes (see #issue).
90+
if (!addon.inWorld(claim.getWorld())) {
91+
return 0;
92+
}
8593
// Remove old claim from grid
8694
addon.getPlugin().getIslands().getIslandCache().getIslandGrid(claim.getWorld()).removeFromGrid(claim);
8795

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package world.bentobox.stranger.listeners;
2+
3+
import static org.mockito.ArgumentMatchers.any;
4+
import static org.mockito.ArgumentMatchers.anyInt;
5+
import static org.mockito.Mockito.mock;
6+
import static org.mockito.Mockito.never;
7+
import static org.mockito.Mockito.verify;
8+
import static org.mockito.Mockito.when;
9+
10+
import java.util.UUID;
11+
12+
import org.bukkit.World;
13+
import org.junit.jupiter.api.BeforeEach;
14+
import org.junit.jupiter.api.Test;
15+
import org.mockito.Mock;
16+
17+
import com.google.common.collect.ImmutableSet;
18+
19+
import world.bentobox.bentobox.api.events.team.TeamKickEvent;
20+
import world.bentobox.bentobox.api.events.team.TeamLeaveEvent;
21+
import world.bentobox.bentobox.database.objects.Island;
22+
import world.bentobox.bentobox.managers.island.IslandCache;
23+
import world.bentobox.bentobox.managers.island.IslandGrid;
24+
import world.bentobox.stranger.CommonTestSetup;
25+
import world.bentobox.stranger.Settings;
26+
import world.bentobox.stranger.StrangerRealms;
27+
28+
/**
29+
* Regression tests for TeamListener.
30+
*
31+
* <p>Background: a prior version of {@link TeamListener#onTeamKick(TeamKickEvent)} and
32+
* {@link TeamListener#onTeamLeave(TeamLeaveEvent)} had inverted boolean logic that caused
33+
* {@code resize()} to fire on islands that were <em>not</em> in StrangerRealms' world.
34+
* That in turn called {@link Island#setRange(int)} with StrangerRealms' configured distance
35+
* (typically 64 with defaults), corrupting the range field of other game modes' islands and
36+
* causing {@code Island distance mismatch} crashes on the next server restart.
37+
*/
38+
public class TeamListenerTest extends CommonTestSetup {
39+
40+
@Mock
41+
private World otherWorld; // a world NOT governed by StrangerRealms
42+
@Mock
43+
private Island otherIsland; // an island in that other world (e.g. AOneBlock)
44+
@Mock
45+
private Settings strangerSettings;
46+
@Mock
47+
private IslandCache islandCache;
48+
@Mock
49+
private IslandGrid islandGrid;
50+
51+
// Do NOT use @Mock for StrangerRealms — its static initializer touches
52+
// BentoBox.getInstance() (NamespacedKey for the warped compass recipe),
53+
// which fails before super.setUp() has installed the plugin singleton.
54+
// Construct it manually after super.setUp().
55+
private StrangerRealms addon;
56+
private TeamListener listener;
57+
58+
@Override
59+
@BeforeEach
60+
public void setUp() throws Exception {
61+
super.setUp();
62+
addon = mock(StrangerRealms.class);
63+
64+
// Addon points at the standard mocked BentoBox plugin/managers.
65+
when(addon.getPlugin()).thenReturn(plugin);
66+
when(addon.getIslands()).thenReturn(im);
67+
when(addon.getSettings()).thenReturn(strangerSettings);
68+
// Defaults from StrangerRealms config: distance 32, memberBonus 32.
69+
when(strangerSettings.getIslandDistance()).thenReturn(32);
70+
when(strangerSettings.getMemberBonus()).thenReturn(32);
71+
72+
// CRITICAL: the kicked/leaving island is in some other game mode's world.
73+
when(addon.inWorld(otherWorld)).thenReturn(false);
74+
when(otherIsland.getWorld()).thenReturn(otherWorld);
75+
// Island has two members so resize() would compute 32 + 32*(2-1) = 64.
76+
when(otherIsland.getMemberSet()).thenReturn(ImmutableSet.of(UUID.randomUUID(), UUID.randomUUID()));
77+
when(otherIsland.getProtectionRange()).thenReturn(50);
78+
79+
// Island grid wiring (in case the guard is removed in the future and resize is reached).
80+
when(im.getIslandCache()).thenReturn(islandCache);
81+
when(islandCache.getIslandGrid(any())).thenReturn(islandGrid);
82+
83+
listener = new TeamListener(addon);
84+
}
85+
86+
@Test
87+
public void onTeamKick_islandInOtherGamemode_doesNotMutateRange() {
88+
TeamKickEvent event = mock(TeamKickEvent.class);
89+
when(event.getIsland()).thenReturn(otherIsland);
90+
91+
listener.onTeamKick(event);
92+
93+
// The whole point of the fix: never touch the range / protection range of an
94+
// island that isn't in StrangerRealms' world.
95+
verify(otherIsland, never()).setRange(anyInt());
96+
verify(otherIsland, never()).setProtectionRange(anyInt());
97+
}
98+
99+
@Test
100+
public void onTeamLeave_islandInOtherGamemode_doesNotMutateRange() {
101+
TeamLeaveEvent event = mock(TeamLeaveEvent.class);
102+
when(event.getIsland()).thenReturn(otherIsland);
103+
104+
listener.onTeamLeave(event);
105+
106+
verify(otherIsland, never()).setRange(anyInt());
107+
verify(otherIsland, never()).setProtectionRange(anyInt());
108+
}
109+
110+
@Test
111+
public void onTeamKick_memberBonusZero_doesNotMutateRange() {
112+
// Even within StrangerRealms' own world, a zero memberBonus means resizing
113+
// is disabled and the early-return should fire.
114+
when(strangerSettings.getMemberBonus()).thenReturn(0);
115+
when(addon.inWorld(otherWorld)).thenReturn(true); // pretend the island IS in stranger world
116+
117+
TeamKickEvent event = mock(TeamKickEvent.class);
118+
when(event.getIsland()).thenReturn(otherIsland);
119+
120+
listener.onTeamKick(event);
121+
122+
verify(otherIsland, never()).setRange(anyInt());
123+
verify(otherIsland, never()).setProtectionRange(anyInt());
124+
}
125+
}

0 commit comments

Comments
 (0)