Skip to content

Fix matching players with similar names #6263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Essentials/src/main/java/com/earth2me/essentials/Essentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -956,10 +956,16 @@ public User matchUser(final Server server, final User sourceUser, final String s
try {
exPlayer = server.getPlayer(UUID.fromString(searchTerm));
} catch (final IllegalArgumentException ex) {
// Prefer exact online name match first always
if (getOffline) {
// When offline lookups are allowed, do not pick partial online matches here; allow exact offline match later
exPlayer = server.getPlayerExact(searchTerm);
} else {
exPlayer = server.getPlayer(searchTerm);
exPlayer = server.getPlayerExact(searchTerm);
if (exPlayer == null) {
// Only consider partial/prefix online match when not explicitly doing an offline-capable lookup
exPlayer = server.getPlayer(searchTerm);
}
}
}

Expand Down Expand Up @@ -996,6 +1002,16 @@ public User matchUser(final Server server, final User sourceUser, final String s
}
}
} else {
// Prefer exact username match among the matched players
for (final Player player : matches) {
if (player.getName().equalsIgnoreCase(searchTerm)) {
final User userMatch = getUser(player);
if (getHidden || canInteractWith(sourceUser, userMatch)) {
return userMatch;
}
}
}
// Then prefer display name/prefix match as before
for (final Player player : matches) {
final User userMatch = getUser(player);
if (userMatch.getDisplayName().startsWith(searchTerm) && (getHidden || canInteractWith(sourceUser, userMatch))) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.earth2me.essentials;

import com.earth2me.essentials.commands.PlayerNotFoundException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockbukkit.mockbukkit.MockBukkit;
import org.mockbukkit.mockbukkit.ServerMock;
import org.mockbukkit.mockbukkit.entity.PlayerMock;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class MatchUserTest {
private Essentials ess;
private ServerMock server;

@BeforeEach
public void setUp() {
this.server = MockBukkit.mock();
Essentials.TESTING = true;
ess = MockBukkit.load(Essentials.class);
}

@AfterEach
public void tearDown() {
MockBukkit.unmock();
}

@Test
public void exactMatchPreferredOverPartialOnline() throws Exception {
final PlayerMock exactPlayer = server.addPlayer("Ginshin");
final PlayerMock partialPlayer = server.addPlayer("Ginshin_BOT");
final PlayerMock callerBase = server.addPlayer("Caller");

final User caller = ess.getUser(callerBase);
ess.getUser(exactPlayer);
ess.getUser(partialPlayer);

final User matched = ess.matchUser(server, caller, "Ginshin", false, false);
assertEquals("Ginshin", matched.getName());
}

@Test
public void hiddenPlayerExactOnlyWhenOfflineLookupPlayerCaller() throws Exception {
final PlayerMock hiddenBase = server.addPlayer("Hidden");
final PlayerMock callerBase = server.addPlayer("Caller");

final User hidden = ess.getUser(hiddenBase);
final User caller = ess.getUser(callerBase);

hidden.setVanished(true);

// Without offline-capable lookup, hidden target should not be found
assertThrows(PlayerNotFoundException.class,
() -> ess.matchUser(server, caller, "Hidden", false, false));

// With offline-capable lookup, only exact matches should return the hidden user
assertThrows(PlayerNotFoundException.class,
() -> ess.matchUser(server, caller, "Hid", false, true));

final User matched = ess.matchUser(server, caller, "Hidden", false, true);
assertEquals("Hidden", matched.getName());
}

@Test
public void hiddenPlayerExactOnlyWhenOfflineLookupConsoleCaller() throws Exception {
final PlayerMock hiddenBase = server.addPlayer("HiddenTwo");
final User hidden = ess.getUser(hiddenBase);

hidden.setHidden(true);

// Console caller represented by null source user
assertThrows(PlayerNotFoundException.class,
() -> ess.matchUser(server, null, "HiddenT", false, true));

final User matched = ess.matchUser(server, null, "HiddenTwo", false, true);
assertEquals("HiddenTwo", matched.getName());
}
}


Loading