Add support for per issuer locale in Sponge and BungeeCord (#104)

* Add support for per issuer locale in Sponge and BungeeCord

* Added Optional<UUID> getUniqueId to CommandIssuer
* Added update task and delayed join listener in BungeeCord
* Added settings change listener in Sponge
* Moved setLocale method up to the CommandManager to remove the
amount of duplicated code

* Remove Optional usage

* Remove player terminology in core
This commit is contained in:
games647
2018-02-28 23:53:27 +01:00
committed by Daniel Ennis
parent c3ee9d4f20
commit edf7ecc020
12 changed files with 179 additions and 29 deletions
@@ -54,13 +54,14 @@ class ACFBukkitListener implements Listener {
this.manager.readPlayerLocale(player);
this.plugin.getServer().getScheduler().runTaskLater(this.plugin, () -> manager.readPlayerLocale(player), 20);
} else {
this.manager.setPlayerLocale(player, this.manager.getLocales().getDefaultLocale());
this.manager.setIssuerLocale(player, this.manager.getLocales().getDefaultLocale());
this.manager.notifyLocaleChange(this.manager.getCommandIssuer(player), null, this.manager.getLocales().getDefaultLocale());
}
}
@EventHandler
public void onPlayerJoin(PlayerQuitEvent event) {
manager.issuersLocale.remove(event.getPlayer().getUniqueId());
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
//cleanup
manager.issuersLocale.remove(quitEvent.getPlayer().getUniqueId());
}
}
@@ -25,8 +25,11 @@ package co.aikar.commands;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.UUID;
public class BukkitCommandIssuer implements CommandIssuer {
private final BukkitCommandManager manager;
@@ -52,6 +55,16 @@ public class BukkitCommandIssuer implements CommandIssuer {
return isPlayer() ? (Player) sender : null;
}
@Override
public @NotNull UUID getUniqueId() {
if (isPlayer()) {
return ((Player) sender).getUniqueId();
}
//generate a unique id based of the name (like for the console command sender)
return UUID.nameUUIDFromBytes(sender.getName().getBytes(StandardCharsets.UTF_8));
}
@Override
public CommandManager getManager() {
return manager;
@@ -26,7 +26,6 @@ package co.aikar.commands;
import co.aikar.commands.apachecommonslang.ApacheCommonsExceptionUtil;
import co.aikar.timings.lib.MCTiming;
import co.aikar.timings.lib.TimingManager;
import com.google.common.collect.Maps;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Server;
@@ -52,7 +51,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -80,7 +78,6 @@ public class BukkitCommandManager extends CommandManager<
protected BukkitLocales locales;
private boolean cantReadLocale = false;
protected boolean autoDetectFromClient = true;
protected Map<UUID, Locale> issuersLocale = Maps.newConcurrentMap();
@SuppressWarnings("JavaReflectionMemberAccess")
public BukkitCommandManager(Plugin plugin) {
@@ -93,7 +90,9 @@ public class BukkitCommandManager extends CommandManager<
this.formatters.put(MessageType.SYNTAX, new BukkitMessageFormatter(ChatColor.YELLOW, ChatColor.GREEN, ChatColor.WHITE));
this.formatters.put(MessageType.INFO, new BukkitMessageFormatter(ChatColor.BLUE, ChatColor.DARK_GREEN, ChatColor.GREEN));
this.formatters.put(MessageType.HELP, new BukkitMessageFormatter(ChatColor.AQUA, ChatColor.GREEN, ChatColor.YELLOW));
Bukkit.getPluginManager().registerEvents(new ACFBukkitListener(this, plugin), plugin);
getLocales(); // auto load locales
this.localeTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
if (this.cantReadLocale || !this.autoDetectFromClient) {
@@ -341,26 +340,6 @@ public class BukkitCommandManager extends CommandManager<
}
}
public Locale setPlayerLocale(Player player, Locale locale) {
Locale old = this.issuersLocale.put(player.getUniqueId(), locale);
if (!Objects.equals(old, locale)) {
this.notifyLocaleChange(getCommandIssuer(player), old, locale);
}
return old;
}
@Override
public Locale getIssuerLocale(CommandIssuer issuer) {
if (usingPerIssuerLocale() && issuer.getIssuer() instanceof Player) {
UUID uniqueId = ((Player) issuer.getIssuer()).getUniqueId();
Locale locale = issuersLocale.get(uniqueId);
if (locale != null) {
return locale;
}
}
return super.getIssuerLocale(issuer);
}
public boolean usePerIssuerLocale(boolean usePerIssuerLocale, boolean autoDetectFromClient) {
boolean old = this.usePerIssuerLocale;
this.usePerIssuerLocale = usePerIssuerLocale;
@@ -0,0 +1,37 @@
package co.aikar.commands;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
import java.util.concurrent.TimeUnit;
public class ACFBungeeListener implements Listener {
private final BungeeCommandManager manager;
private final Plugin plugin;
public ACFBungeeListener(BungeeCommandManager manager, Plugin plugin) {
this.manager = manager;
this.plugin = plugin;
}
@EventHandler
public void onPlayerJoin(PostLoginEvent loginEvent) {
ProxiedPlayer player = loginEvent.getPlayer();
//the client settings are sent after a successful login
Runnable task = () -> manager.readLocale(player);
plugin.getProxy().getScheduler().schedule(plugin, task, 1, TimeUnit.SECONDS);
}
@EventHandler
public void onDisconnect(PlayerDisconnectEvent disconnectEvent) {
//cleanup
ProxiedPlayer player = disconnectEvent.getPlayer();
manager.issuersLocale.remove(player.getUniqueId());
}
}
@@ -25,8 +25,11 @@ package co.aikar.commands;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import org.jetbrains.annotations.NotNull;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.UUID;
public class BungeeCommandIssuer implements CommandIssuer {
private final BungeeCommandManager manager;
@@ -57,6 +60,16 @@ public class BungeeCommandIssuer implements CommandIssuer {
return sender instanceof ProxiedPlayer;
}
@Override
public @NotNull UUID getUniqueId() {
if (isPlayer()) {
return ((ProxiedPlayer) sender).getUniqueId();
}
//generate a unique id based of the name (like for the console command sender)
return UUID.nameUUIDFromBytes(sender.getName().getBytes(StandardCharsets.UTF_8));
}
@Override
public void sendMessageInternal(String message) {
sender.sendMessage(ACFBungeeUtil.color(message));
@@ -26,13 +26,17 @@ package co.aikar.commands;
import co.aikar.commands.apachecommonslang.ApacheCommonsExceptionUtil;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -57,8 +61,16 @@ public class BungeeCommandManager extends CommandManager<
this.formatters.put(MessageType.SYNTAX, new BungeeMessageFormatter(ChatColor.YELLOW, ChatColor.GREEN, ChatColor.WHITE));
this.formatters.put(MessageType.INFO, new BungeeMessageFormatter(ChatColor.BLUE, ChatColor.DARK_GREEN, ChatColor.GREEN));
this.formatters.put(MessageType.HELP, new BungeeMessageFormatter(ChatColor.AQUA, ChatColor.GREEN, ChatColor.YELLOW));
getLocales(); // auto load locales
plugin.getProxy().getPluginManager().registerListener(plugin, new ACFBungeeListener(this, plugin));
//BungeeCord has no event for listening for client setting changes
plugin.getProxy().getScheduler().schedule(plugin, () -> {
ProxyServer.getInstance().getPlayers().forEach(this::readLocale);
}, 5, 5, TimeUnit.SECONDS);
// TODO more default dependencies for bungee
registerDependency(plugin.getClass(), plugin);
registerDependency(Plugin.class, plugin);
@@ -93,6 +105,17 @@ public class BungeeCommandManager extends CommandManager<
return locales;
}
public void readLocale(ProxiedPlayer player) {
if (!player.isConnected()) {
return;
}
//This can be null if we didn't received a settings packet
Locale locale = player.getLocale();
if (locale != null) {
setIssuerLocale(player, player.getLocale());
}
}
@Override
public void registerCommand(BaseCommand command) {
@@ -25,6 +25,9 @@ package co.aikar.commands;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public interface CommandIssuer {
/**
@@ -50,6 +53,11 @@ public interface CommandIssuer {
getManager().sendMessage(this, MessageType.INFO, MessageKeys.INFO_MESSAGE, "{message}", message);
}
/**
* @return the unique id of that issuer
*/
@NotNull UUID getUniqueId();
/**
* Has permission node
* @param permission
@@ -27,6 +27,7 @@ import co.aikar.commands.annotation.Dependency;
import co.aikar.locales.MessageKeyProvider;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import org.jetbrains.annotations.NotNull;
@@ -35,14 +36,16 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import java.util.UUID;
@SuppressWarnings("WeakerAccess")
public abstract class CommandManager <
@@ -76,6 +79,8 @@ public abstract class CommandManager <
protected MF defaultFormatter;
protected int defaultHelpPerPage = 10;
protected Map<UUID, Locale> issuersLocale = Maps.newConcurrentMap();
private Set<String> unstableAPIs = Sets.newHashSet();
public static CommandOperationContext getCurrentCommandOperationContext() {
@@ -344,7 +349,25 @@ public abstract class CommandManager <
});
}
public Locale setIssuerLocale(IT issuer, Locale locale) {
I commandIssuer = getCommandIssuer(issuer);
Locale old = issuersLocale.put(commandIssuer.getUniqueId(), locale);
if (!Objects.equals(old, locale)) {
this.notifyLocaleChange(commandIssuer, old, locale);
}
return old;
}
public Locale getIssuerLocale(CommandIssuer issuer) {
if (usingPerIssuerLocale()) {
Locale locale = issuersLocale.get(issuer.getUniqueId());
if (locale != null) {
return locale;
}
}
return getLocales().getDefaultLocale();
}
@@ -3,6 +3,9 @@ package co.aikar.commands;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.MessageEmbed;
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public class JDACommandEvent implements CommandIssuer {
@@ -34,6 +37,14 @@ public class JDACommandEvent implements CommandIssuer {
return false;
}
@Override
public @NotNull UUID getUniqueId() {
// Discord id only have 64 bit width (long) while UUIDs have twice the size.
// In order to keep it unique we use 0L for the first 64 bit.
long authorId = event.getAuthor().getIdLong();
return new UUID(0, authorId);
}
@Override
public boolean hasPermission(String permission) {
CommandPermissionResolver permissionResolver = this.manager.getPermissionResolver();
@@ -0,0 +1,28 @@
package co.aikar.commands;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.entity.living.humanoid.player.PlayerChangeClientSettingsEvent;
import org.spongepowered.api.event.filter.cause.First;
import org.spongepowered.api.event.network.ClientConnectionEvent;
public class ACFSpongeListener {
private final SpongeCommandManager manager;
public ACFSpongeListener(SpongeCommandManager manager) {
this.manager = manager;
}
@Listener(order = Order.POST)
public void onSettingsChange(PlayerChangeClientSettingsEvent changeSettingsEvent, @First Player targetPlayer) {
//this event will be fired on join as well as every time the player changes it
manager.setIssuerLocale(targetPlayer, targetPlayer.getLocale());
}
@Listener
public void onDisconnectCleanup(ClientConnectionEvent.Disconnect disconnectEvent, @First Player player) {
manager.issuersLocale.remove(player.getUniqueId());
}
}
@@ -23,11 +23,15 @@
package co.aikar.commands;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.text.serializer.TextSerializers;
import org.spongepowered.api.util.Identifiable;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.UUID;
public class SpongeCommandIssuer implements CommandIssuer {
@@ -49,6 +53,16 @@ public class SpongeCommandIssuer implements CommandIssuer {
return this.source;
}
@Override
public @NotNull UUID getUniqueId() {
if (isPlayer()) {
return ((Identifiable) source).getUniqueId();
}
//generate a unique id based of the name (like for the console command sender)
return UUID.nameUUIDFromBytes(source.getName().getBytes(StandardCharsets.UTF_8));
}
public Player getPlayer() {
return isPlayer() ? (Player) source : null;
}
@@ -68,7 +82,6 @@ public class SpongeCommandIssuer implements CommandIssuer {
return this.source.hasPermission(permission);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -68,6 +68,8 @@ public class SpongeCommandManager extends CommandManager<
this.formatters.put(MessageType.HELP, new SpongeMessageFormatter(TextColors.AQUA, TextColors.GREEN, TextColors.YELLOW));
getLocales(); // auto load locales
Sponge.getEventManager().registerListeners(plugin, new ACFSpongeListener(this));
//TODO more default dependencies for sponge
registerDependency(plugin.getClass(), plugin);
}
@@ -196,5 +198,4 @@ public class SpongeCommandManager extends CommandManager<
public SpongeConditionContext createConditionContext(CommandIssuer issuer, String config) {
return new SpongeConditionContext((SpongeCommandIssuer) issuer, config);
}
}