From eaa730ddfc593db27d1480e56e16be6d9c4748dd Mon Sep 17 00:00:00 2001 From: BuildTools Date: Thu, 2 Jul 2015 17:51:32 +0200 Subject: [PATCH] IdUtil MaintainedSets rework, and proper sender tab completion. --- .../massivecore/MassiveCoreEngineMain.java | 11 +- .../massivecore/SenderPresence.java | 29 +++ .../massivecraft/massivecore/SenderType.java | 10 + .../massivecore/cmd/arg/ARPlayer.java | 4 +- .../massivecore/cmd/arg/ARSender.java | 4 +- .../massivecore/cmd/arg/ARSenderEntity.java | 14 +- .../massivecore/cmd/arg/ARSenderId.java | 14 +- .../cmd/arg/ARSenderIdAbstract.java | 80 ++++--- .../mixin/MessageMixinDefault.java | 8 +- .../massivecore/store/SenderColl.java | 12 +- .../SenderIdSourceMixinAllSenderIds.java | 4 +- .../massivecraft/massivecore/util/IdUtil.java | 221 ++++++++++-------- .../massivecore/util/SenderMap.java | 206 ++++++++++++++++ .../massivecore/util/SenderMapTest.java | 54 +++++ 14 files changed, 511 insertions(+), 160 deletions(-) create mode 100644 src/com/massivecraft/massivecore/SenderPresence.java create mode 100644 src/com/massivecraft/massivecore/SenderType.java create mode 100644 src/com/massivecraft/massivecore/util/SenderMap.java create mode 100644 src/com/massivecraft/massivecore/util/SenderMapTest.java diff --git a/src/com/massivecraft/massivecore/MassiveCoreEngineMain.java b/src/com/massivecraft/massivecore/MassiveCoreEngineMain.java index 2c975449..d0a02ef0 100644 --- a/src/com/massivecraft/massivecore/MassiveCoreEngineMain.java +++ b/src/com/massivecraft/massivecore/MassiveCoreEngineMain.java @@ -141,17 +141,18 @@ public class MassiveCoreEngineMain extends EngineAbstract Player watcher = event.getPlayer(); if (MUtil.isntPlayer(watcher)) return; - // Get the lowercased token - String tokenlc = event.getLastToken().toLowerCase(); + // Get the lowercased token predicate + Predictate predictate = PredictateStartsWithIgnoreCase.get(event.getLastToken()); // Create a case insensitive set to check for already added stuff Set current = new TreeSet(String.CASE_INSENSITIVE_ORDER); current.addAll(event.getTabCompletions()); - // Add names of all online senders that match and isn't added yet. - for (String senderName : IdUtil.getOnlineNames()) + // Add names of all online senders that match and isn't added yet. + // TODO: Should this only be players? Would a player actually want to tab-complete @console? + for (String senderName : IdUtil.getNames(SenderPresence.ONLINE, SenderType.ANY)) { - if (!senderName.toLowerCase().startsWith(tokenlc)) continue; + if (!predictate.apply(senderName)) continue; if (current.contains(senderName)) continue; if (!Mixin.canSee(watcher, senderName)) continue; diff --git a/src/com/massivecraft/massivecore/SenderPresence.java b/src/com/massivecraft/massivecore/SenderPresence.java new file mode 100644 index 00000000..c0721114 --- /dev/null +++ b/src/com/massivecraft/massivecore/SenderPresence.java @@ -0,0 +1,29 @@ +package com.massivecraft.massivecore; + +public enum SenderPresence +{ + // IMP NOTE: These must be sorted, with the most strict first + // and the most loose at the end. + LOCAL, // Online and logged in on this very server. + ONLINE, // Online somewhere on the cloud. May be this server may be another server. + OFFLINE, // The opposite of online. + ANY, // Any. Local, Online or Offline. + + ; + + // -------------------------------------------- // + // GET FROM ONLINE VALUE + // -------------------------------------------- // + + public static SenderPresence fromOnline(Boolean online) + { + if (online == null) return null; + return fromOnline(online.booleanValue()); + } + + public static SenderPresence fromOnline(boolean online) + { + return online ? ONLINE : OFFLINE; + } + +} diff --git a/src/com/massivecraft/massivecore/SenderType.java b/src/com/massivecraft/massivecore/SenderType.java new file mode 100644 index 00000000..2af0813c --- /dev/null +++ b/src/com/massivecraft/massivecore/SenderType.java @@ -0,0 +1,10 @@ +package com.massivecraft.massivecore; + +public enum SenderType +{ + PLAYER, // A player. Such as Notch or Dinnerbone. @console is not a player. + NONPLAYER, // A sender which is not a player. Such as @console. + ANY, // Anyone. Both players, and nonplayers. + + ; +} diff --git a/src/com/massivecraft/massivecore/cmd/arg/ARPlayer.java b/src/com/massivecraft/massivecore/cmd/arg/ARPlayer.java index 3348065f..02c7ed0b 100644 --- a/src/com/massivecraft/massivecore/cmd/arg/ARPlayer.java +++ b/src/com/massivecraft/massivecore/cmd/arg/ARPlayer.java @@ -2,6 +2,8 @@ package com.massivecraft.massivecore.cmd.arg; import org.bukkit.entity.Player; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; import com.massivecraft.massivecore.store.SenderIdSourceMixinAllSenderIds; import com.massivecraft.massivecore.util.IdUtil; @@ -15,7 +17,7 @@ public class ARPlayer extends ARSenderIdAbstract public static ARPlayer get() { return i; } private ARPlayer() { - super(SenderIdSourceMixinAllSenderIds.get(), true, true); + super(SenderIdSourceMixinAllSenderIds.get(), SenderPresence.LOCAL, SenderType.PLAYER); } // -------------------------------------------- // diff --git a/src/com/massivecraft/massivecore/cmd/arg/ARSender.java b/src/com/massivecraft/massivecore/cmd/arg/ARSender.java index 36df8b21..8a4a1ac1 100644 --- a/src/com/massivecraft/massivecore/cmd/arg/ARSender.java +++ b/src/com/massivecraft/massivecore/cmd/arg/ARSender.java @@ -2,6 +2,8 @@ package com.massivecraft.massivecore.cmd.arg; import org.bukkit.command.CommandSender; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; import com.massivecraft.massivecore.store.SenderIdSourceMixinAllSenderIds; import com.massivecraft.massivecore.util.IdUtil; @@ -15,7 +17,7 @@ public class ARSender extends ARSenderIdAbstract public static ARSender get() { return i; } private ARSender() { - super(SenderIdSourceMixinAllSenderIds.get(), true, false); + super(SenderIdSourceMixinAllSenderIds.get(), SenderPresence.LOCAL, SenderType.ANY); } // -------------------------------------------- // diff --git a/src/com/massivecraft/massivecore/cmd/arg/ARSenderEntity.java b/src/com/massivecraft/massivecore/cmd/arg/ARSenderEntity.java index 667e3cbc..04388d9b 100644 --- a/src/com/massivecraft/massivecore/cmd/arg/ARSenderEntity.java +++ b/src/com/massivecraft/massivecore/cmd/arg/ARSenderEntity.java @@ -3,6 +3,8 @@ package com.massivecraft.massivecore.cmd.arg; import java.util.Collection; import org.bukkit.command.CommandSender; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; import com.massivecraft.massivecore.store.SenderColl; import com.massivecraft.massivecore.store.SenderEntity; @@ -18,15 +20,15 @@ public class ARSenderEntity> extends ARSenderIdAbstrac // CONSTRUCT // -------------------------------------------- // - private ARSenderEntity(SenderColl coll, boolean onlineOnly, boolean playerOnly) + private ARSenderEntity(SenderColl coll, SenderPresence presence, SenderType type) { - super(coll, onlineOnly, playerOnly); + super(coll, presence, type); this.coll = coll; } - private ARSenderEntity(SenderColl coll, boolean onlineOnly) + private ARSenderEntity(SenderColl coll, SenderPresence presence) { - super(coll, onlineOnly); + super(coll, presence); this.coll = coll; } @@ -40,8 +42,8 @@ public class ARSenderEntity> extends ARSenderIdAbstrac // GET // -------------------------------------------- // - public static > ARSenderEntity get(SenderColl coll, boolean onlineOnly, boolean playerOnly) { return new ARSenderEntity(coll, onlineOnly, playerOnly); } - public static > ARSenderEntity get(SenderColl coll, boolean onlineOnly) { return new ARSenderEntity(coll, onlineOnly); } + public static > ARSenderEntity get(SenderColl coll, SenderPresence presence, SenderType type) { return new ARSenderEntity(coll, presence, type); } + public static > ARSenderEntity get(SenderColl coll, SenderPresence presence) { return new ARSenderEntity(coll, presence); } public static > ARSenderEntity get(SenderColl coll) { return new ARSenderEntity(coll); } // -------------------------------------------- // diff --git a/src/com/massivecraft/massivecore/cmd/arg/ARSenderId.java b/src/com/massivecraft/massivecore/cmd/arg/ARSenderId.java index ecfcff6c..3f2b0bb3 100644 --- a/src/com/massivecraft/massivecore/cmd/arg/ARSenderId.java +++ b/src/com/massivecraft/massivecore/cmd/arg/ARSenderId.java @@ -1,5 +1,7 @@ package com.massivecraft.massivecore.cmd.arg; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; import com.massivecraft.massivecore.store.SenderIdSource; import com.massivecraft.massivecore.store.SenderIdSourceMixinAllSenderIds; @@ -9,14 +11,14 @@ public class ARSenderId extends ARSenderIdAbstract // CONSTRUCT // -------------------------------------------- // - private ARSenderId(SenderIdSource source, boolean onlineOnly, boolean playerOnly) + private ARSenderId(SenderIdSource source, SenderPresence presence, SenderType type) { - super(source, onlineOnly, playerOnly); + super(source, presence, type); } - private ARSenderId(SenderIdSource source, boolean onlineOnly) + private ARSenderId(SenderIdSource source, SenderPresence presence) { - super(source, onlineOnly); + super(source, presence); } private ARSenderId(SenderIdSource source) @@ -35,8 +37,8 @@ public class ARSenderId extends ARSenderIdAbstract // GET // -------------------------------------------- // - public static ARSenderId get(SenderIdSource source, boolean onlineOnly, boolean playerOnly) { return new ARSenderId(source, onlineOnly, playerOnly); } - public static ARSenderId get(SenderIdSource source, boolean onlineOnly) { return new ARSenderId(source, onlineOnly); } + public static ARSenderId get(SenderIdSource source, SenderPresence presence, SenderType type) { return new ARSenderId(source, presence, type); } + public static ARSenderId get(SenderIdSource source, SenderPresence presence) { return new ARSenderId(source, presence); } public static ARSenderId get(SenderIdSource source) { return new ARSenderId(source); } // -------------------------------------------- // diff --git a/src/com/massivecraft/massivecore/cmd/arg/ARSenderIdAbstract.java b/src/com/massivecraft/massivecore/cmd/arg/ARSenderIdAbstract.java index 13db6286..77f5fce9 100644 --- a/src/com/massivecraft/massivecore/cmd/arg/ARSenderIdAbstract.java +++ b/src/com/massivecraft/massivecore/cmd/arg/ARSenderIdAbstract.java @@ -1,14 +1,14 @@ package com.massivecraft.massivecore.cmd.arg; import java.util.Collection; -import java.util.List; +import java.util.Collections; import java.util.Set; import org.bukkit.command.CommandSender; import com.massivecraft.massivecore.MassiveException; -import com.massivecraft.massivecore.collections.MassiveList; -import com.massivecraft.massivecore.mixin.Mixin; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; import com.massivecraft.massivecore.store.SenderIdSource; import com.massivecraft.massivecore.util.IdUtil; import com.massivecraft.massivecore.util.MUtil; @@ -20,28 +20,32 @@ public abstract class ARSenderIdAbstract extends ARAbstract // -------------------------------------------- // protected final SenderIdSource source; - protected final boolean onlineOnly; - protected final boolean playerOnly; + protected final SenderPresence presence; + protected final SenderType type; // -------------------------------------------- // // CONSTRUCT // -------------------------------------------- // - public ARSenderIdAbstract(SenderIdSource source, boolean onlineOnly, boolean playerOnly) + public ARSenderIdAbstract(SenderIdSource source, SenderPresence presence, SenderType type) { + if (source == null) throw new NullPointerException("source"); + if (presence == null) throw new NullPointerException("presence"); + if (type == null) throw new NullPointerException("type"); + this.source = source; - this.onlineOnly = onlineOnly; - this.playerOnly = playerOnly; + this.presence = presence; + this.type = type; } - public ARSenderIdAbstract(SenderIdSource source, boolean online) + public ARSenderIdAbstract(SenderIdSource source, SenderPresence presence) { - this(source, online, false); + this(source, presence, SenderType.ANY); } public ARSenderIdAbstract(SenderIdSource source) { - this(source, false); + this(source, SenderPresence.ANY); } // -------------------------------------------- // @@ -57,8 +61,14 @@ public abstract class ARSenderIdAbstract extends ARAbstract @Override public String getTypeName() { - if (onlineOnly) return "online player"; - else return "player"; + switch (presence) + { + case LOCAL: + case ONLINE: return "online player"; + case OFFLINE: return "offline player"; + case ANY: return "player"; + } + throw new UnsupportedOperationException("Unknown SenderPresence: " + presence); } @Override @@ -93,34 +103,29 @@ public abstract class ARSenderIdAbstract extends ARAbstract if (MUtil.isUuid(arg)) return true; // Check data presence. This handles specials like "@console". - if (IdUtil.getIdToData().containsKey(arg)) return true; - if (IdUtil.getNameToData().containsKey(arg)) return true; + if (IdUtil.getRegistryIdToSender().containsKey(arg)) return true; return false; } @Override public Collection getTabList(CommandSender sender, String arg) - { - Set names; - if (onlineOnly) - { - names = playerOnly ? IdUtil.getOnlinePlayerNames() : IdUtil.getOnlineNames(); - - List ret = new MassiveList(); - for (String name : names) - { - if ( ! Mixin.canSee(sender, name)) continue; - ret.add(name); - } - return ret; - } - else - { - names = playerOnly ? IdUtil.getAllPlayerNames() : IdUtil.getAllNames(); - - return names; - } + { + // Step 1: Calculate presence. + SenderPresence presence = this.presence; + if (presence == SenderPresence.ANY) presence = SenderPresence.ONLINE; + + // Special step: We don't tab complete offline players. + if (presence == SenderPresence.OFFLINE) return Collections.emptySet(); + + // Step 2: Calculate type. + SenderType type = this.type; + + // Step 3: Create the ret. + Set ret = IdUtil.getNames(presence, type); + + // Step 4: Return the ret. + return ret; } // -------------------------------------------- // @@ -135,14 +140,13 @@ public abstract class ARSenderIdAbstract extends ARAbstract String senderId = arg.toLowerCase(); String betterId = IdUtil.getId(senderId); if (betterId != null) senderId = betterId; - for (Collection coll : this.source.getSenderIdCollections()) { // If the senderId exists ... if ( ! coll.contains(senderId)) continue; - // ... and the online check passes ... - if (this.onlineOnly && !Mixin.isOnline(senderId)) continue; + // ... and the presence check passes ... + if (IdUtil.getMaintainedIds().contains(senderId, presence, type) || IdUtil.getMaintainedNames().contains(senderId, presence, type)) continue; // ... and the result is non null ... T result = this.getResultForSenderId(senderId); diff --git a/src/com/massivecraft/massivecore/mixin/MessageMixinDefault.java b/src/com/massivecraft/massivecore/mixin/MessageMixinDefault.java index a3f22274..57626841 100644 --- a/src/com/massivecraft/massivecore/mixin/MessageMixinDefault.java +++ b/src/com/massivecraft/massivecore/mixin/MessageMixinDefault.java @@ -27,7 +27,7 @@ public class MessageMixinDefault extends MessageMixinAbstract public boolean messageAll(Collection messages) { if (messages == null) return false; - for (CommandSender sender : IdUtil.getOnlineSenders()) + for (CommandSender sender : IdUtil.getLocalSenders()) { this.messageOne(sender, messages); } @@ -39,7 +39,7 @@ public class MessageMixinDefault extends MessageMixinAbstract { if (predictate == null) return false; if (messages == null) return false; - for (CommandSender sender : IdUtil.getOnlineSenders()) + for (CommandSender sender : IdUtil.getLocalSenders()) { if (!predictate.apply(sender)) continue; this.messageOne(sender, messages); @@ -62,7 +62,7 @@ public class MessageMixinDefault extends MessageMixinAbstract public boolean messageRawAll(Collection msons) { if (msons == null) return false; - for (CommandSender sender : IdUtil.getOnlineSenders()) + for (CommandSender sender : IdUtil.getLocalSenders()) { this.messageRawOne(sender, msons); } @@ -74,7 +74,7 @@ public class MessageMixinDefault extends MessageMixinAbstract { if (predictate == null) return false; if (msons == null) return false; - for (CommandSender sender : IdUtil.getOnlineSenders()) + for (CommandSender sender : IdUtil.getLocalSenders()) { if ( ! predictate.apply(sender)) continue; this.messageRawOne(sender, msons); diff --git a/src/com/massivecraft/massivecore/store/SenderColl.java b/src/com/massivecraft/massivecore/store/SenderColl.java index 656fe447..52dff7fb 100644 --- a/src/com/massivecraft/massivecore/store/SenderColl.java +++ b/src/com/massivecraft/massivecore/store/SenderColl.java @@ -9,6 +9,8 @@ import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; import com.massivecraft.massivecore.Predictate; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; import com.massivecraft.massivecore.cmd.arg.ARSenderEntity; import com.massivecraft.massivecore.cmd.arg.ARSenderId; import com.massivecraft.massivecore.util.IdUtil; @@ -114,7 +116,7 @@ public class SenderColl> extends Coll implements Se // You could say the corresponding entities latently exist in the collection because it's creative. if (this.isCreative()) { - ret.add(IdUtil.getAllIds()); + ret.add(IdUtil.getIds(SenderPresence.ANY, SenderType.ANY)); } return ret; @@ -129,9 +131,9 @@ public class SenderColl> extends Coll implements Se return ARSenderEntity.get(this); } - public ARSenderEntity getAREntity(boolean online) + public ARSenderEntity getAREntity(SenderPresence presence) { - return ARSenderEntity.get(this, online); + return ARSenderEntity.get(this, presence); } public ARSenderId getARId() @@ -139,9 +141,9 @@ public class SenderColl> extends Coll implements Se return ARSenderId.get(this); } - public ARSenderId getARId(boolean online) + public ARSenderId getARId(SenderPresence presence) { - return ARSenderId.get(this, online); + return ARSenderId.get(this, presence); } // -------------------------------------------- // diff --git a/src/com/massivecraft/massivecore/store/SenderIdSourceMixinAllSenderIds.java b/src/com/massivecraft/massivecore/store/SenderIdSourceMixinAllSenderIds.java index 630accef..5d3533e0 100644 --- a/src/com/massivecraft/massivecore/store/SenderIdSourceMixinAllSenderIds.java +++ b/src/com/massivecraft/massivecore/store/SenderIdSourceMixinAllSenderIds.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; import com.massivecraft.massivecore.util.IdUtil; public class SenderIdSourceMixinAllSenderIds implements SenderIdSource @@ -23,7 +25,7 @@ public class SenderIdSourceMixinAllSenderIds implements SenderIdSource public Collection> getSenderIdCollections() { List> ret = new ArrayList>(); - ret.add(IdUtil.getAllIds()); + ret.add(IdUtil.getIds(SenderPresence.ANY, SenderType.ANY)); return ret; } diff --git a/src/com/massivecraft/massivecore/util/IdUtil.java b/src/com/massivecraft/massivecore/util/IdUtil.java index e86d9c9d..2c82f284 100644 --- a/src/com/massivecraft/massivecore/util/IdUtil.java +++ b/src/com/massivecraft/massivecore/util/IdUtil.java @@ -5,12 +5,12 @@ import java.lang.reflect.Type; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.ConcurrentSkipListSet; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -24,6 +24,8 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerLoginEvent; import com.massivecraft.massivecore.MassiveCore; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; import com.massivecraft.massivecore.event.EventMassiveCorePlayerLeave; import com.massivecraft.massivecore.event.EventMassiveCoreSenderRegister; import com.massivecraft.massivecore.event.EventMassiveCoreSenderUnregister; @@ -105,25 +107,20 @@ public class IdUtil implements Listener, Runnable // MAINTAINED SETS // -------------------------------------------- // // Used for chat tab completion, argument readers, etc. + + private static SenderMap maintainedIds = new SenderMap(); + public static SenderMap getMaintainedIds() { return maintainedIds; } + public static Set getIds(SenderPresence presence, SenderType type) + { + return maintainedIds.getValues(presence, type); + } - private static Set onlineIds = new ConcurrentSkipListSet(String.CASE_INSENSITIVE_ORDER); - public static Set getOnlineIds() { return Collections.unmodifiableSet(onlineIds); } - - private static Set onlineNames = new ConcurrentSkipListSet(String.CASE_INSENSITIVE_ORDER); - public static Set getOnlineNames() { return Collections.unmodifiableSet(onlineNames); } - - private static Set onlinePlayerNames = new ConcurrentSkipListSet(String.CASE_INSENSITIVE_ORDER); - public static Set getOnlinePlayerNames() { return Collections.unmodifiableSet(onlinePlayerNames); } - - - private static Set allIds = new ConcurrentSkipListSet(String.CASE_INSENSITIVE_ORDER); - public static Set getAllIds() { return Collections.unmodifiableSet(allIds); } - - private static Set allNames = new ConcurrentSkipListSet(String.CASE_INSENSITIVE_ORDER); - public static Set getAllNames() { return Collections.unmodifiableSet(allNames); } - - private static Set allPlayerNames = new ConcurrentSkipListSet(String.CASE_INSENSITIVE_ORDER); - public static Set getAllPlayerNames() { return Collections.unmodifiableSet(allPlayerNames); } + private static SenderMap maintainedNames = new SenderMap(); + public static SenderMap getMaintainedNames() { return maintainedNames; } + public static Set getNames(SenderPresence presence, SenderType type) + { + return maintainedNames.getValues(presence, type); + } // -------------------------------------------- // // REGISTRY @@ -132,7 +129,10 @@ public class IdUtil implements Listener, Runnable // It's assumed that the getName() returns the name which is also the id. private static Map registryIdToSender = new ConcurrentHashMap(); + public static Map getRegistryIdToSender() { return Collections.unmodifiableMap(registryIdToSender); } + private static Map registrySenderToId = new ConcurrentHashMap(); + public static Map getRegistrySenderToId() { return Collections.unmodifiableMap(registrySenderToId); } public static void register(CommandSender sender) { @@ -145,7 +145,7 @@ public class IdUtil implements Listener, Runnable registrySenderToId.put(sender, id); // Update data before the event is ran so that data is available. - update(id, name, true); + update(id, name, SenderPresence.LOCAL); EventMassiveCoreSenderRegister event = new EventMassiveCoreSenderRegister(sender, data); event.run(); @@ -163,7 +163,7 @@ public class IdUtil implements Listener, Runnable if (removed == null) return; // Update data before the event is ran so that data is available. - update(id, name, false); + update(id, name, SenderPresence.OFFLINE); EventMassiveCoreSenderUnregister event = new EventMassiveCoreSenderUnregister(sender, data); event.run(); @@ -174,7 +174,7 @@ public class IdUtil implements Listener, Runnable // -------------------------------------------- // // Used for retrieving the full set of senders currently present on this server. - public static Set getOnlineSenders() + public static Set getLocalSenders() { Set ret = new LinkedHashSet(); @@ -199,8 +199,7 @@ public class IdUtil implements Listener, Runnable { if (id == null) throw new NullPointerException("id"); - onlineIds.remove(id); - allIds.remove(id); + maintainedIds.removeValueCompletely(id); IdData data = idToData.remove(id); if (data == null) return null; @@ -213,10 +212,7 @@ public class IdUtil implements Listener, Runnable { if (name == null) throw new NullPointerException("name"); - onlineNames.remove(name); - onlinePlayerNames.remove(name); - allNames.remove(name); - allPlayerNames.remove(name); + maintainedNames.removeValueCompletely(name); IdData data = nameToData.remove(name); if (data == null) return null; @@ -224,9 +220,11 @@ public class IdUtil implements Listener, Runnable return data.getId(); } - private static void addData(IdData data, boolean online) + private static void addData(IdData data, SenderPresence presence) { if (data == null) throw new NullPointerException("data"); + if (presence == null) throw new NullPointerException("presence"); + String id = data.getId(); String name = data.getName(); @@ -244,18 +242,12 @@ public class IdUtil implements Listener, Runnable if (id != null && name != null) { - boolean player = MUtil.isValidPlayerName(name); - - if (online) onlineIds.add(id); - allIds.add(id); - - if (online && player) onlinePlayerNames.add(name); - if (online) onlineNames.add(name); - if (player) allPlayerNames.add(name); - allNames.add(name); + List presences = SenderMap.getPresences(presence); + maintainedIds.addValue(id, presences); + maintainedNames.addValue(name, presences); } } - + public static void update(String id, String name) { update(id, name, null); @@ -265,13 +257,13 @@ public class IdUtil implements Listener, Runnable { update(id, name, millis, null); } - - public static void update(final String id, final String name, Boolean online) + + public static void update(final String id, final String name, SenderPresence presence) { - update(id, name, System.currentTimeMillis(), online); + update(id, name, System.currentTimeMillis(), presence); } - public static void update(final String id, final String name, final long millis, Boolean online) + public static void update(final String id, final String name, final long millis, SenderPresence presence) { // First Null Check if (id == null && name == null) throw new NullPointerException("Either id or name must be set. They can't both be null."); @@ -292,35 +284,47 @@ public class IdUtil implements Listener, Runnable // The previousMillis is now null or the lowest of the two. if (previousMillis != null && previousMillis > millis) return; - // Online Fix - if (online == null) + // Presence Fix + if (presence == null) { if (id != null) { - online = onlineIds.contains(id); + presence = maintainedIds.getPresence(id); } else if (name != null) { - online = onlineNames.contains(name); + presence = maintainedNames.getPresence(name); } } // Removal of previous data - // TODO: This is not optimal but will do for now. It only "uproots" one step. - String otherId = null; - String otherName = null; - - if (id != null) otherName = removeId(id); - if (name != null) otherId = removeName(name); - - if (otherId != null && !otherId.equals(id)) removeId(otherId); - if (otherName != null && !otherName.equals(name)) removeName(otherName); + removeIdAndNameRecurse(id, name); // Adding new data IdData data = new IdData(id, name, millis); - addData(data, online); + addData(data, presence); } + private static void removeIdAndNameRecurse(String id, String name) + { + String otherId = null; + String otherName = null; + + // Remove first + if (id != null) otherName = removeId(id); + if (name != null) otherId = removeName(name); + + // If equal, then null. We shouldn't perform the same operation twice. + if (otherId != null && otherId.equals(id)) otherId = null; + if (otherName != null && otherName.equals(name)) otherName = null; + + // If any isn't null. Repeat + if (otherId != null || otherName != null) + { + removeIdAndNameRecurse(otherId, otherName); + } + } + // -------------------------------------------- // // SETUP // -------------------------------------------- // @@ -339,13 +343,9 @@ public class IdUtil implements Listener, Runnable idToData.clear(); nameToData.clear(); - onlineIds.clear(); - onlineNames.clear(); - onlinePlayerNames.clear(); - - allIds.clear(); - allNames.clear(); - allPlayerNames.clear(); + maintainedIds.clear(); + maintainedNames.clear(); + // Load Datas loadDatas(); @@ -394,9 +394,11 @@ public class IdUtil implements Listener, Runnable String name = player.getName(); // Declaring Existence? Sure, whatever you were before! + // It can definitely not be local at this point. + // But online or offline is fine. boolean online = Mixin.isOnline(player); - update(id, name, online); + update(id, name, SenderPresence.fromOnline(online)); } // Can't be cancelled @@ -410,10 +412,8 @@ public class IdUtil implements Listener, Runnable String id = uuid.toString(); String name = player.getName(); - // Joining? Sure, the player is online! - boolean online = true; - - update(id, name, online); + // Joining? The player must be local at this point. + update(id, name, SenderPresence.LOCAL); } // Can't be cancelled @@ -428,9 +428,21 @@ public class IdUtil implements Listener, Runnable String name = player.getName(); // Leaving? Is it an actuall leave? - boolean online = !Mixin.isActualLeave(event); + SenderPresence presence; + if (Mixin.isActualLeave(event)) + { + // They actually left. + // They are offline. + presence = SenderPresence.OFFLINE; + } + else + { + // They didn't actually leave. + // They are online, but not local. + presence = SenderPresence.ONLINE; + } - update(id, name, online); + update(id, name, presence); } // -------------------------------------------- // @@ -462,10 +474,16 @@ public class IdUtil implements Listener, Runnable // Player // CommandSender - // UUID - if (senderObject instanceof CommandSender || senderObject instanceof UUID) + if (senderObject instanceof CommandSender) { - String id = getId(senderObject); + String id = getIdFromSender((CommandSender) senderObject); + return getIdToData().get(id); + } + + // UUID + if (senderObject instanceof UUID) + { + String id = getIdFromUuid((UUID) senderObject); return getIdToData().get(id); } @@ -620,8 +638,8 @@ public class IdUtil implements Listener, Runnable // Already Done if (senderObject instanceof String && MUtil.isUuid((String)senderObject)) return (String)senderObject; - // Console Type - if (senderObject instanceof ConsoleCommandSender) return CONSOLE_ID; + // Console + // Handled at "Command Sender" // Console Id/Name if (CONSOLE_ID.equals(senderObject)) return CONSOLE_ID; @@ -634,13 +652,13 @@ public class IdUtil implements Listener, Runnable } // Player - if (senderObject instanceof Player) return ((Player)senderObject).getUniqueId().toString(); + // Handled at "Command Sender" - // CommandSender - if (senderObject instanceof CommandSender) return ((CommandSender)senderObject).getName().toLowerCase(); + // Command Sender + if (senderObject instanceof CommandSender) return getIdFromSender((CommandSender) senderObject); // UUID - if (senderObject instanceof UUID) return ((UUID)senderObject).toString(); + if (senderObject instanceof UUID) return getIdFromUuid((UUID) senderObject); // String // Handled at "Data" @@ -655,6 +673,18 @@ public class IdUtil implements Listener, Runnable // Return Null return null; } + + public static String getIdFromSender(CommandSender sender) + { + if (sender instanceof Player) return ((Player) sender).getUniqueId().toString(); + if (sender instanceof ConsoleCommandSender) return CONSOLE_ID; + return sender.getName().toLowerCase(); + } + + public static String getIdFromUuid(UUID uuid) + { + return uuid.toString(); + } public static String getName(Object senderObject) { @@ -664,8 +694,8 @@ public class IdUtil implements Listener, Runnable // Already Done // Handled at "Data" (not applicable - names can look differently) - // Console Type - if (senderObject instanceof ConsoleCommandSender) return CONSOLE_ID; + // Console + // Handled at "Command Sender" // Console Id/Name if (CONSOLE_ID.equals(senderObject)) return CONSOLE_ID; @@ -678,10 +708,10 @@ public class IdUtil implements Listener, Runnable } // Player - // Handled at "CommandSender" - + // Handled at "Command Sender" + // CommandSender - if (senderObject instanceof CommandSender) return ((CommandSender)senderObject).getName(); + if (senderObject instanceof CommandSender) return getNameFromSender((CommandSender)senderObject); // UUID // Handled at "Data". @@ -705,10 +735,15 @@ public class IdUtil implements Listener, Runnable return null; } + public static String getNameFromSender(CommandSender sender) + { + if (sender instanceof ConsoleCommandSender) return CONSOLE_ID; + return sender.getName(); + } + public static boolean isOnline(Object senderObject) { // Fix the id ... - if (senderObject == null) return false; String id = getId(senderObject); if (id == null) return false; @@ -717,7 +752,7 @@ public class IdUtil implements Listener, Runnable if (CONSOLE_ID.equals(id)) return true; // ... return by (case insensitive) set contains. - return getOnlineIds().contains(id); + return IdUtil.getIds(SenderPresence.ONLINE, SenderType.ANY).contains(id); } public static Long getMillis(Object senderObject) @@ -733,7 +768,7 @@ public class IdUtil implements Listener, Runnable public static boolean isPlayerId(String string) { - // NOTE: Assuming all custom ids look like "@shite". + // NOTE: Assuming all custom ids isn't a valid player name or id. return MUtil.isValidPlayerName(string) || MUtil.isUuid(string); } @@ -805,20 +840,20 @@ public class IdUtil implements Listener, Runnable MassiveCore.get().log(Txt.parse("Loading Cachefile datas...")); for (IdData data : getCachefileDatas()) { - update(data.getId(), data.getName(), data.getMillis(), false); + update(data.getId(), data.getName(), data.getMillis(), SenderPresence.OFFLINE); } MassiveCore.get().log(Txt.parse("Loading Onlineplayer datas...")); - for (IdData data : getOnlineplayerDatas()) + for (IdData data : getLocalPlayerDatas()) { - update(data.getId(), data.getName(), data.getMillis(), true); + update(data.getId(), data.getName(), data.getMillis(), SenderPresence.LOCAL); } MassiveCore.get().log(Txt.parse("Loading Registry datas...")); for (String id : registryIdToSender.keySet()) { String name = id; - update(id, name, true); + update(id, name, SenderPresence.LOCAL); } MassiveCore.get().log(Txt.parse("Saving Cachefile...")); @@ -867,7 +902,7 @@ public class IdUtil implements Listener, Runnable // -------------------------------------------- // // This data source is simply based on the players currently online - public static Set getOnlineplayerDatas() + public static Set getLocalPlayerDatas() { Set ret = new LinkedHashSet(); diff --git a/src/com/massivecraft/massivecore/util/SenderMap.java b/src/com/massivecraft/massivecore/util/SenderMap.java new file mode 100644 index 00000000..1a757843 --- /dev/null +++ b/src/com/massivecraft/massivecore/util/SenderMap.java @@ -0,0 +1,206 @@ +package com.massivecraft.massivecore.util; + +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +import com.google.common.collect.ImmutableList; +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; + +public final class SenderMap +{ + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + private final Map>> innerMap; + + public Map>> getInnerMap() + { + return innerMap; + } + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public SenderMap() + { + innerMap = new EnumMap>>(SenderPresence.class); + for (SenderPresence presence : SenderPresence.values()) + { + Map> map = new EnumMap>(SenderType.class); + for (SenderType type : SenderType.values()) + { + Set set = new ConcurrentSkipListSet(String.CASE_INSENSITIVE_ORDER); + map.put(type, set); + } + innerMap.put(presence, map); + } + } + + // -------------------------------------------- // + // GET + // -------------------------------------------- // + + public Set getValues(SenderPresence presence, SenderType type) + { + if (presence == null) throw new NullPointerException("presence"); + if (type == null) throw new NullPointerException("type"); + + return Collections.unmodifiableSet(getRawValues(presence, type)); + } + + private Set getRawValues(SenderPresence presence, SenderType type) + { + return innerMap.get(presence).get(type); + } + + public SenderPresence getPresence(String value) + { + if (value == null) throw new NullPointerException("value"); + return getPresence(value, SenderType.ANY); + } + + public SenderPresence getPresence(String value, SenderType type) + { + if (value == null) throw new NullPointerException("value"); + if (type == null) throw new NullPointerException("type"); + + + for (SenderPresence presence : SenderPresence.values()) + { + if (contains(value, presence, type)) return presence; + } + + return null; + } + + // -------------------------------------------- // + // CONTAINS + // -------------------------------------------- // + + public boolean contains(String value, SenderPresence presence, SenderType type) + { + if (value == null) throw new NullPointerException("value"); + if (presence == null) throw new NullPointerException("presence"); + if (type == null) throw new NullPointerException("type"); + + return getRawValues(presence, type).contains(value); + } + + // -------------------------------------------- // + // CLEAR + // -------------------------------------------- // + + public void clear() + { + for (Map> map : innerMap.values()) + { + for (Set set : map.values()) + { + set.clear(); + } + } + } + + // -------------------------------------------- // + // ADD + // -------------------------------------------- // + + public void addValue(String value, SenderPresence presence) + { + if (value == null) throw new NullPointerException("value"); + if (presence == null) throw new NullPointerException("presence"); + + addValue(value, getPresences(presence)); + } + + public void addValue(String value, List presences) + { + if (value == null) throw new NullPointerException("value"); + if (presences == null) throw new NullPointerException("presences"); + + addValue(value, presences, getSenderTypes(value)); + } + + public void addValue(String value, List presences, List types) + { + if (value == null) throw new NullPointerException("value"); + if (presences == null) throw new NullPointerException("presences"); + if (types == null) throw new NullPointerException("types"); + + for (SenderPresence presence : presences) + { + Map> map = innerMap.get(presence); + for (SenderType type : types) + { + Set set = map.get(type); + set.add(value); + } + } + } + + // -------------------------------------------- // + // REMOVE + // -------------------------------------------- // + + public boolean removeValueCompletely(String value) + { + if (value == null) throw new NullPointerException("value"); + + boolean ret = false; + for (Map> map : innerMap.values()) + { + for (Set set : map.values()) + { + ret |= set.remove(value); + } + } + return ret; + } + + // -------------------------------------------- // + // UTIL + // -------------------------------------------- // + + public static final List LOCAL_PRESENCES = ImmutableList.of(SenderPresence.LOCAL, SenderPresence.ONLINE, SenderPresence.ANY); + public static final List ONLINE_PRESENCES = ImmutableList.of(SenderPresence.ONLINE, SenderPresence.ANY); + public static final List OFFLINE_PRESENCES = ImmutableList.of(SenderPresence.OFFLINE, SenderPresence.ANY); + + // This accepts the most strict presence, + // and returns all other which also match. + public static List getPresences(SenderPresence presence) + { + if (presence == null) throw new NullPointerException("presence"); + switch (presence) + { + case LOCAL : return LOCAL_PRESENCES; + case ONLINE : return ONLINE_PRESENCES; + case OFFLINE : return OFFLINE_PRESENCES; + case ANY : throw new UnsupportedOperationException("SenderPresence.ANY is not supported. You must know wether it is online or offline."); + } + throw new UnsupportedOperationException("Unknown SenderPresence: " + null); + } + + public static final List PLAYER_TYPES = ImmutableList.of(SenderType.PLAYER, SenderType.ANY); + public static final List NONPLAYER_TYPES = ImmutableList.of(SenderType.NONPLAYER, SenderType.ANY); + + public static List getSenderTypes(String value) + { + if (value == null) throw new NullPointerException("value"); + if (isPlayerValue(value)) return PLAYER_TYPES; + else return NONPLAYER_TYPES; + } + + public static boolean isPlayerValue(String value) + { + return IdUtil.isPlayerId(value); + //return MUtil.isValidPlayerName(value) || MUtil.isUuid(value); + } + +} diff --git a/src/com/massivecraft/massivecore/util/SenderMapTest.java b/src/com/massivecraft/massivecore/util/SenderMapTest.java new file mode 100644 index 00000000..db885195 --- /dev/null +++ b/src/com/massivecraft/massivecore/util/SenderMapTest.java @@ -0,0 +1,54 @@ +package com.massivecraft.massivecore.util; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import com.massivecraft.massivecore.SenderPresence; +import com.massivecraft.massivecore.SenderType; + +class SenderMapTest +{ + + static SenderMap map = new SenderMap(); + public static void main(String[] args) + { + map.addValue("Madus", SenderPresence.LOCAL); // Satisfying + //printMap(); + + map.removeValueCompletely("Madus"); // Satisfying + //printMap(); + + map.addValue("Madus", SenderPresence.LOCAL); // Satisfying + map.addValue("Cayorion", SenderPresence.ONLINE); // Satisfying + //printMap(); + + map.removeValueCompletely("madus"); // Satisfying (case insensitive) + //printMap(); + + map.addValue("@console", SenderPresence.OFFLINE); // Satisfying + //printMap(); + + System.out.println(map.getPresence("@console")); // Satisfying (OFFLINE) + System.out.println(map.getPresence("caYorioN")); // Satisfying (ONLINE) + System.out.println(map.getPresence("MaDus")); // Satisfying (null) + } + + public static void printMap() + { + printMap(map); + } + + public static void printMap(SenderMap map) + { + for (Entry>> entry1 : map.getInnerMap().entrySet()) + { + System.out.println(entry1.getKey()); + for (Entry> entry2 : entry1.getValue().entrySet()) + { + System.out.println(" " + entry2.getKey() + ": " + entry2.getValue()); + } + } + } + +}