Skip to content

Commit 427f60e

Browse files
authored
Fix matching players with similar names (#6263)
Fixes an issue where matchPlayer didn't always match exact matches first. Also adds a test for this method to ensure stuff like this doesn't happen in the future. Fixes #6246
1 parent efbcfb3 commit 427f60e

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

Essentials/src/main/java/com/earth2me/essentials/Essentials.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,10 +960,16 @@ public User matchUser(final Server server, final User sourceUser, final String s
960960
try {
961961
exPlayer = server.getPlayer(UUID.fromString(searchTerm));
962962
} catch (final IllegalArgumentException ex) {
963+
// Prefer exact online name match first always
963964
if (getOffline) {
965+
// When offline lookups are allowed, do not pick partial online matches here; allow exact offline match later
964966
exPlayer = server.getPlayerExact(searchTerm);
965967
} else {
966-
exPlayer = server.getPlayer(searchTerm);
968+
exPlayer = server.getPlayerExact(searchTerm);
969+
if (exPlayer == null) {
970+
// Only consider partial/prefix online match when not explicitly doing an offline-capable lookup
971+
exPlayer = server.getPlayer(searchTerm);
972+
}
967973
}
968974
}
969975

@@ -1000,6 +1006,16 @@ public User matchUser(final Server server, final User sourceUser, final String s
10001006
}
10011007
}
10021008
} else {
1009+
// Prefer exact username match among the matched players
1010+
for (final Player player : matches) {
1011+
if (player.getName().equalsIgnoreCase(searchTerm)) {
1012+
final User userMatch = getUser(player);
1013+
if (getHidden || canInteractWith(sourceUser, userMatch)) {
1014+
return userMatch;
1015+
}
1016+
}
1017+
}
1018+
// Then prefer display name/prefix match as before
10031019
for (final Player player : matches) {
10041020
final User userMatch = getUser(player);
10051021
if (userMatch.getDisplayName().startsWith(searchTerm) && (getHidden || canInteractWith(sourceUser, userMatch))) {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.earth2me.essentials;
2+
3+
import com.earth2me.essentials.commands.PlayerNotFoundException;
4+
import org.junit.jupiter.api.AfterEach;
5+
import org.junit.jupiter.api.BeforeEach;
6+
import org.junit.jupiter.api.Test;
7+
import org.mockbukkit.mockbukkit.MockBukkit;
8+
import org.mockbukkit.mockbukkit.ServerMock;
9+
import org.mockbukkit.mockbukkit.entity.PlayerMock;
10+
11+
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
import static org.junit.jupiter.api.Assertions.assertThrows;
13+
14+
public class MatchUserTest {
15+
private Essentials ess;
16+
private ServerMock server;
17+
18+
@BeforeEach
19+
public void setUp() {
20+
this.server = MockBukkit.mock();
21+
Essentials.TESTING = true;
22+
ess = MockBukkit.load(Essentials.class);
23+
}
24+
25+
@AfterEach
26+
public void tearDown() {
27+
MockBukkit.unmock();
28+
}
29+
30+
@Test
31+
public void exactMatchPreferredOverPartialOnline() throws Exception {
32+
final PlayerMock exactPlayer = server.addPlayer("Ginshin");
33+
final PlayerMock partialPlayer = server.addPlayer("Ginshin_BOT");
34+
final PlayerMock callerBase = server.addPlayer("Caller");
35+
36+
final User caller = ess.getUser(callerBase);
37+
ess.getUser(exactPlayer);
38+
ess.getUser(partialPlayer);
39+
40+
final User matched = ess.matchUser(server, caller, "Ginshin", false, false);
41+
assertEquals("Ginshin", matched.getName());
42+
}
43+
44+
@Test
45+
public void hiddenPlayerExactOnlyWhenOfflineLookupPlayerCaller() throws Exception {
46+
final PlayerMock hiddenBase = server.addPlayer("Hidden");
47+
final PlayerMock callerBase = server.addPlayer("Caller");
48+
49+
final User hidden = ess.getUser(hiddenBase);
50+
final User caller = ess.getUser(callerBase);
51+
52+
hidden.setVanished(true);
53+
54+
// Without offline-capable lookup, hidden target should not be found
55+
assertThrows(PlayerNotFoundException.class,
56+
() -> ess.matchUser(server, caller, "Hidden", false, false));
57+
58+
// With offline-capable lookup, only exact matches should return the hidden user
59+
assertThrows(PlayerNotFoundException.class,
60+
() -> ess.matchUser(server, caller, "Hid", false, true));
61+
62+
final User matched = ess.matchUser(server, caller, "Hidden", false, true);
63+
assertEquals("Hidden", matched.getName());
64+
}
65+
66+
@Test
67+
public void hiddenPlayerExactOnlyWhenOfflineLookupConsoleCaller() throws Exception {
68+
final PlayerMock hiddenBase = server.addPlayer("HiddenTwo");
69+
final User hidden = ess.getUser(hiddenBase);
70+
71+
hidden.setHidden(true);
72+
73+
// Console caller represented by null source user
74+
assertThrows(PlayerNotFoundException.class,
75+
() -> ess.matchUser(server, null, "HiddenT", false, true));
76+
77+
final User matched = ess.matchUser(server, null, "HiddenTwo", false, true);
78+
assertEquals("HiddenTwo", matched.getName());
79+
}
80+
}
81+
82+

0 commit comments

Comments
 (0)