203 lines
7.2 KiB
Java
203 lines
7.2 KiB
Java
package tc.oc.commons.bukkit.nick;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import javax.annotation.Nullable;
|
|
import javax.inject.Inject;
|
|
import javax.inject.Singleton;
|
|
import org.bukkit.command.CommandSender;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.EventBus;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
|
import org.bukkit.event.player.PlayerQuitEvent;
|
|
import tc.oc.api.bukkit.friends.OnlineFriends;
|
|
import tc.oc.api.bukkit.users.BukkitUserStore;
|
|
import tc.oc.api.bukkit.users.OnlinePlayers;
|
|
import tc.oc.api.docs.PlayerId;
|
|
import tc.oc.api.docs.Session;
|
|
import tc.oc.api.docs.UserId;
|
|
import tc.oc.api.users.UserSearchResponse;
|
|
import tc.oc.commons.bukkit.event.UserLoginEvent;
|
|
import tc.oc.commons.bukkit.util.PlayerStates;
|
|
import tc.oc.commons.core.plugin.PluginFacet;
|
|
import tc.oc.minecraft.scheduler.SyncExecutor;
|
|
|
|
@Singleton
|
|
public class IdentityProviderImpl implements IdentityProvider, Listener, PluginFacet {
|
|
|
|
private static final String REVEAL_ALL_PERMISSION = "nick.see-through-all";
|
|
|
|
private final BukkitUserStore userStore;
|
|
private final EventBus eventBus;
|
|
private final OnlinePlayers onlinePlayers;
|
|
private final PlayerStates playerStates;
|
|
private final OnlineFriends friendMap;
|
|
private final SyncExecutor syncExecutor;
|
|
private final ConsoleIdentity consoleIdentity;
|
|
|
|
private final Map<Player, Identity> identities = new HashMap<>();
|
|
private final Map<String, Player> nicknames = new HashMap<>();
|
|
|
|
@Inject
|
|
IdentityProviderImpl(BukkitUserStore userStore, EventBus eventBus, OnlinePlayers onlinePlayers, PlayerStates playerStates, OnlineFriends friendMap, SyncExecutor syncExecutor, ConsoleIdentity consoleIdentity) {
|
|
this.userStore = userStore;
|
|
this.eventBus = eventBus;
|
|
this.onlinePlayers = onlinePlayers;
|
|
this.playerStates = playerStates;
|
|
this.friendMap = friendMap;
|
|
this.syncExecutor = syncExecutor;
|
|
this.consoleIdentity = consoleIdentity;
|
|
}
|
|
|
|
@Override
|
|
public boolean revealAll(CommandSender viewer) {
|
|
return viewer.hasPermission(REVEAL_ALL_PERMISSION);
|
|
}
|
|
|
|
@Override
|
|
public boolean reveal(CommandSender viewer, UserId userId) {
|
|
return revealAll(viewer) || friendMap.areFriends(viewer, userId);
|
|
}
|
|
|
|
@Override
|
|
public Identity createIdentity(PlayerId playerId, @Nullable String nickname) {
|
|
return new IdentityImpl(onlinePlayers, friendMap, playerStates, this, playerId, nickname);
|
|
}
|
|
|
|
@Override
|
|
public Identity createIdentity(Player player, @Nullable String nickname) {
|
|
return createIdentity(userStore.getUser(player), nickname);
|
|
}
|
|
|
|
@Override
|
|
public Identity createIdentity(CommandSender player) {
|
|
return player instanceof Player ? createIdentity((Player) player, null) : consoleIdentity();
|
|
}
|
|
|
|
@Override
|
|
public Identity consoleIdentity() {
|
|
return consoleIdentity;
|
|
}
|
|
|
|
@Override
|
|
public Identity createIdentity(Session session) {
|
|
return createIdentity(session.user(), session.nickname());
|
|
}
|
|
|
|
@Override
|
|
public Identity createIdentity(UserSearchResponse response) {
|
|
if(response.last_session != null) {
|
|
return createIdentity(response.user, response.last_session.nickname());
|
|
} else {
|
|
return createIdentity(response.user, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Identity currentIdentity(CommandSender player) {
|
|
if(player instanceof Player) {
|
|
return currentIdentity(userStore.getUser((Player) player), (Player) player);
|
|
} else {
|
|
return consoleIdentity;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Identity currentIdentity(PlayerId playerId) {
|
|
return currentIdentity(playerId, onlinePlayers.find(playerId));
|
|
}
|
|
|
|
private Identity currentIdentity(PlayerId playerId, @Nullable Player player) {
|
|
Identity identity = identities.get(player);
|
|
if(identity == null) {
|
|
identity = createIdentity(playerId, null);
|
|
if(player != null && player.willBeOnline()) {
|
|
identities.put(player, identity);
|
|
}
|
|
}
|
|
return identity;
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Identity onlineIdentity(String name) {
|
|
Player player = nicknames.get(name);
|
|
if(player == null) player = onlinePlayers.find(name);
|
|
return player == null ? null : currentIdentity(player);
|
|
}
|
|
|
|
@Override
|
|
public void changeIdentity(Player player, @Nullable String nickname) {
|
|
final Identity oldIdentity = currentIdentity(player);
|
|
if(Objects.equals(oldIdentity.getNickname(), nickname)) return;
|
|
|
|
applyNickname(player, oldIdentity.getNickname(), nickname);
|
|
eventBus.callEvent(new PlayerIdentityChangeEvent(player, oldIdentity, currentIdentity(player)));
|
|
}
|
|
|
|
protected void validateNickname(@Nullable String nickname) {
|
|
if(nickname != null && onlinePlayers.find(nickname) != null) {
|
|
throw new IllegalArgumentException("Nickname '" + nickname + "' is the name of a real online player");
|
|
}
|
|
}
|
|
|
|
protected void applyNickname(Player player, @Nullable String oldNickname, @Nullable String newNickname) {
|
|
validateNickname(newNickname);
|
|
|
|
if(oldNickname != null) {
|
|
nicknames.remove(oldNickname);
|
|
}
|
|
|
|
final Identity identity = createIdentity(userStore.getUser(player), newNickname);
|
|
if(player.willBeOnline()) {
|
|
identities.put(player, identity);
|
|
|
|
if(newNickname != null) {
|
|
nicknames.put(newNickname, player);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If a player has a nickname set in their user document on login, apply it.
|
|
* This needs to run before {@link PlayerAppearanceListener#refreshNamesOnLogin}
|
|
*/
|
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
|
public void applyNicknameOnLogin(UserLoginEvent event) {
|
|
if(event.getUser().nickname() != null) {
|
|
applyNickname(event.getPlayer(), null, event.getUser().nickname());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clean up after quitting players
|
|
*/
|
|
@EventHandler(priority = EventPriority.MONITOR)
|
|
public void deactivateNickOnQuit(PlayerQuitEvent event) {
|
|
final Identity identity = identities.remove(event.getPlayer());
|
|
if(identity != null && identity.getNickname() != null) {
|
|
nicknames.remove(identity.getNickname());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear any nickname that collides with the real name of a player logging in.
|
|
* This ensures that usernames + nicknames together contain no duplicates.
|
|
* The user who's nickname was cleared is not notified of this, but this
|
|
* should be an extremely rare situation, so it's not a big problem.
|
|
*/
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
|
|
public void clearConflictingNicks(AsyncPlayerPreLoginEvent event) {
|
|
final String name = event.getName();
|
|
syncExecutor.execute(() -> {
|
|
final Player player = nicknames.get(name);
|
|
if(player != null) {
|
|
changeIdentity(player, null);
|
|
}
|
|
});
|
|
}
|
|
}
|