diff --git a/src/com/massivecraft/massivecore/MassiveCore.java b/src/com/massivecraft/massivecore/MassiveCore.java index 16e8ea13..076fcc7f 100644 --- a/src/com/massivecraft/massivecore/MassiveCore.java +++ b/src/com/massivecraft/massivecore/MassiveCore.java @@ -27,7 +27,6 @@ import com.massivecraft.massivecore.adapter.MsonAdapter; import com.massivecraft.massivecore.adapter.MsonEventAdapter; import com.massivecraft.massivecore.adapter.PlayerInventoryAdapter; import com.massivecraft.massivecore.adapter.UUIDAdapter; -import com.massivecraft.massivecore.chestgui.EngineChestGui; import com.massivecraft.massivecore.collections.BackstringEnumSet; import com.massivecraft.massivecore.collections.MassiveList; import com.massivecraft.massivecore.collections.MassiveListDef; @@ -44,21 +43,27 @@ import com.massivecraft.massivecore.command.massivecore.CmdMassiveCoreBuffer; import com.massivecraft.massivecore.command.massivecore.CmdMassiveCoreCmdurl; import com.massivecraft.massivecore.command.massivecore.CmdMassiveCoreStore; import com.massivecraft.massivecore.command.massivecore.CmdMassiveCoreUsys; +import com.massivecraft.massivecore.engine.EngineMassiveCoreGank; +import com.massivecraft.massivecore.engine.EngineMassiveCoreChestGui; +import com.massivecraft.massivecore.engine.EngineMassiveCoreCollTick; import com.massivecraft.massivecore.engine.EngineMassiveCoreCommandRegistration; +import com.massivecraft.massivecore.engine.EngineMassiveCoreDatabase; import com.massivecraft.massivecore.engine.EngineMassiveCoreDestination; import com.massivecraft.massivecore.engine.EngineMassiveCoreMain; +import com.massivecraft.massivecore.engine.EngineMassiveCorePlayerLeave; +import com.massivecraft.massivecore.engine.EngineMassiveCorePlayerState; import com.massivecraft.massivecore.engine.EngineMassiveCorePlayerUpdate; +import com.massivecraft.massivecore.engine.EngineMassiveCoreScheduledTeleport; +import com.massivecraft.massivecore.engine.EngineMassiveCoreTeleportMixinCause; import com.massivecraft.massivecore.engine.EngineMassiveCoreVariable; import com.massivecraft.massivecore.engine.EngineMassiveCoreWorldNameSet; import com.massivecraft.massivecore.integration.vault.IntegrationVault; -import com.massivecraft.massivecore.mixin.EngineTeleportMixinCause; import com.massivecraft.massivecore.mson.Mson; import com.massivecraft.massivecore.mson.MsonEvent; import com.massivecraft.massivecore.ps.PS; import com.massivecraft.massivecore.ps.PSAdapter; import com.massivecraft.massivecore.store.ModificationPollerLocal; import com.massivecraft.massivecore.store.ModificationPollerRemote; -import com.massivecraft.massivecore.teleport.EngineScheduledTeleport; import com.massivecraft.massivecore.util.IdUtil; import com.massivecraft.massivecore.util.MUtil; import com.massivecraft.massivecore.util.PlayerUtil; @@ -189,18 +194,22 @@ public class MassiveCore extends MassivePlugin IdUtil.setup(); // Engine - EngineCollTick.get().activate(); - EngineMassiveCoreMain.get().activate(); - EngineMassiveCoreVariable.get().activate(); - EngineScheduledTeleport.get().activate(); - EngineTeleportMixinCause.get().activate(); - EngineMassiveCoreWorldNameSet.get().activate(); + EngineMassiveCoreChestGui.get().activate(); + EngineMassiveCoreCollTick.get().activate(); EngineMassiveCoreCommandRegistration.get().activate(); + EngineMassiveCoreDatabase.get().activate(); EngineMassiveCoreDestination.get().activate(); + EngineMassiveCoreGank.get().activate(); + EngineMassiveCoreMain.get().activate(); + EngineMassiveCorePlayerLeave.get().activate(); + EngineMassiveCorePlayerState.get().activate(); EngineMassiveCorePlayerUpdate.get().activate(); + EngineMassiveCoreScheduledTeleport.get().activate(); + EngineMassiveCoreTeleportMixinCause.get().activate(); + EngineMassiveCoreVariable.get().activate(); + EngineMassiveCoreWorldNameSet.get().activate(); + PlayerUtil.get().activate(); - EngineChestGui.get().activate(); - EngineGank.get().activate(); // Collections MultiverseColl.get().init(); diff --git a/src/com/massivecraft/massivecore/PlayerState.java b/src/com/massivecraft/massivecore/PlayerState.java new file mode 100644 index 00000000..fd986096 --- /dev/null +++ b/src/com/massivecraft/massivecore/PlayerState.java @@ -0,0 +1,51 @@ +package com.massivecraft.massivecore; + +import java.util.UUID; + +import org.bukkit.entity.Player; + +import com.massivecraft.massivecore.engine.EngineMassiveCorePlayerState; + +/** + * This enumeration is used to keep track of where a player currently is within the login --> join --> play --> leave cycle. + * Bukkit does not provide this information so we forge it as well as we can by listening to LOWEST on a few different events. + * + * When is this information useful? + * For example you may want to handle events differently depending on whether the player actually is online yet. + * Say you want to store last teleport position by logging it on the player teleport event. + * During the login and join phase the server teleports the player around to get the player in position. + * So for such a "last tp position" system you may want to ignore any teleports other than during player state JOINED. + * + * EngineMassiveCorePlayerState takes care of updating the information. + */ +public enum PlayerState +{ + // -------------------------------------------- // + // ENUM + // -------------------------------------------- // + + LOGASYNC, // During AsyncPlayerLoginEvent + LOGSYNC, // During PlayerLoginEvent + JOINING, // During PlayerJoinEvent + JOINED, // Regular situation. The player is online and playing. + LEAVING, // From the start of EventMassiveCorePlayerLeave till the player actually disconnects. + LEFT, // The player is fully disconnected and offline. + + // END OF LIST + ; + + // -------------------------------------------- // + // STATIC + // -------------------------------------------- // + + public static PlayerState get(UUID id) + { + return EngineMassiveCorePlayerState.get().getState(id); + } + + public static PlayerState get(Player player) + { + return EngineMassiveCorePlayerState.get().getState(player); + } + +} diff --git a/src/com/massivecraft/massivecore/chestgui/EngineChestGui.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreChestGui.java similarity index 85% rename from src/com/massivecraft/massivecore/chestgui/EngineChestGui.java rename to src/com/massivecraft/massivecore/engine/EngineMassiveCoreChestGui.java index 77d4ce9d..0373d235 100644 --- a/src/com/massivecraft/massivecore/chestgui/EngineChestGui.java +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreChestGui.java @@ -1,4 +1,4 @@ -package com.massivecraft.massivecore.chestgui; +package com.massivecraft.massivecore.engine; import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; @@ -10,17 +10,19 @@ import org.bukkit.plugin.Plugin; import com.massivecraft.massivecore.EngineAbstract; import com.massivecraft.massivecore.MassiveCore; +import com.massivecraft.massivecore.chestgui.ChestAction; +import com.massivecraft.massivecore.chestgui.ChestGui; import com.massivecraft.massivecore.mixin.Mixin; import com.massivecraft.massivecore.util.InventoryUtil; -public class EngineChestGui extends EngineAbstract +public class EngineMassiveCoreChestGui extends EngineAbstract { // -------------------------------------------- // // INSTANCE & CONSTRUCT // -------------------------------------------- // - private static EngineChestGui i = new EngineChestGui(); - public static EngineChestGui get() { return i; } + private static EngineMassiveCoreChestGui i = new EngineMassiveCoreChestGui(); + public static EngineMassiveCoreChestGui get() { return i; } // -------------------------------------------- // // OVERRIDE diff --git a/src/com/massivecraft/massivecore/EngineCollTick.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreCollTick.java similarity index 64% rename from src/com/massivecraft/massivecore/EngineCollTick.java rename to src/com/massivecraft/massivecore/engine/EngineMassiveCoreCollTick.java index 65efba34..256ef670 100644 --- a/src/com/massivecraft/massivecore/EngineCollTick.java +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreCollTick.java @@ -1,17 +1,19 @@ -package com.massivecraft.massivecore; +package com.massivecraft.massivecore.engine; import org.bukkit.plugin.Plugin; +import com.massivecraft.massivecore.EngineAbstract; +import com.massivecraft.massivecore.MassiveCore; import com.massivecraft.massivecore.store.Coll; -public class EngineCollTick extends EngineAbstract +public class EngineMassiveCoreCollTick extends EngineAbstract { // -------------------------------------------- // // INSTANCE & CONSTRUCT // -------------------------------------------- // - protected static EngineCollTick i = new EngineCollTick(); - public static EngineCollTick get() { return i; } + protected static EngineMassiveCoreCollTick i = new EngineMassiveCoreCollTick(); + public static EngineMassiveCoreCollTick get() { return i; } // -------------------------------------------- // // OVERRIDE: ENGINE diff --git a/src/com/massivecraft/massivecore/engine/EngineMassiveCoreDatabase.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreDatabase.java new file mode 100644 index 00000000..07ce49d1 --- /dev/null +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreDatabase.java @@ -0,0 +1,260 @@ +package com.massivecraft.massivecore.engine; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; + +import com.massivecraft.massivecore.EngineAbstract; +import com.massivecraft.massivecore.MassiveCore; +import com.massivecraft.massivecore.collections.MassiveMap; +import com.massivecraft.massivecore.event.EventMassiveCorePlayerLeave; +import com.massivecraft.massivecore.event.EventMassiveCoreSenderRegister; +import com.massivecraft.massivecore.event.EventMassiveCoreSenderUnregister; +import com.massivecraft.massivecore.store.Coll; +import com.massivecraft.massivecore.store.SenderColl; +import com.massivecraft.massivecore.util.IdUtil; +import com.massivecraft.massivecore.util.MUtil; +import com.massivecraft.massivecore.xlib.gson.JsonObject; + +public class EngineMassiveCoreDatabase extends EngineAbstract +{ + // -------------------------------------------- // + // INSTANCE & CONSTRUCT + // -------------------------------------------- // + + private static EngineMassiveCoreDatabase i = new EngineMassiveCoreDatabase(); + public static EngineMassiveCoreDatabase get() { return i; } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Plugin getPlugin() + { + return MassiveCore.get(); + } + + @Override + public void activate() + { + super.activate(); + } + + // -------------------------------------------- // + // PLAYER AND SENDER REFERENCES + // -------------------------------------------- // + + public static Map idToPlayerLoginEvent = new MassiveMap(); + + // This method sets the sender reference to what you decide. + public static void setSenderReferences(CommandSender sender, CommandSender reference, PlayerLoginEvent event) + { + if (MUtil.isntSender(sender)) return; + + String id = IdUtil.getId(sender); + if (id != null) + { + SenderColl.setSenderReferences(id, reference); + if (event == null) + { + idToPlayerLoginEvent.remove(id); + } + else + { + idToPlayerLoginEvent.put(id, event); + } + } + } + + // This method sets the sender reference based on it's online state. + public static void setSenderReferences(Player player, PlayerLoginEvent event) + { + Player reference = player; + if ( ! player.isOnline()) reference = null; + setSenderReferences(player, reference, event); + } + + // Same as above but next tick. + public static void setSenderReferencesSoon(final Player player, final PlayerLoginEvent event) + { + Bukkit.getScheduler().scheduleSyncDelayedTask(MassiveCore.get(), new Runnable() + { + @Override + public void run() + { + setSenderReferences(player, event); + } + }); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void setSenderReferencesLoginLowest(PlayerLoginEvent event) + { + final Player player = event.getPlayer(); + + // We set the reference at LOWEST so that it's present during this PlayerLoginEvent event. + setSenderReferences(player, player, event); + + // And the next tick we update the reference based on it's online state. + // Not all players succeed in logging in. They may for example be banned. + setSenderReferencesSoon(player, null); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void setSenderReferencesQuitMonitor(PlayerQuitEvent event) + { + // PlayerQuitEvents are /probably/ trustworthy. + // We check ourselves the next tick just to be on the safe side. + setSenderReferencesSoon(event.getPlayer(), null); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void setSenderReferencesRegisterMonitor(EventMassiveCoreSenderRegister event) + { + // This one we can however trust. + setSenderReferences(event.getSender(), event.getSender(), null); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void setSenderReferencesUnregisterMonitor(EventMassiveCoreSenderUnregister event) + { + // This one we can however trust. + setSenderReferences(event.getSender(), null, null); + } + + // -------------------------------------------- // + // SYNC: LOGIN + // -------------------------------------------- // + // This section handles the automatic sync of a players corresponding massive store entries on login. + // If possible the database IO is made during the AsyncPlayerPreLoginEvent to offloat the main server thread. + + protected Map, Entry>> idToRemoteEntries = new ConcurrentHashMap<>(); + + // Intended to be ran asynchronously. + public void storeRemoteEntries(final String playerId) + { + // Create remote entries ... + Map, Entry> remoteEntries = createRemoteEntries(playerId); + + // ... store them ... + this.idToRemoteEntries.put(playerId, remoteEntries); + + // ... and make sure they are removed after 30 seconds. + // Without this we might cause a memory leak. + // Players might trigger AsyncPlayerPreLoginEvent but not PlayerLoginEvent. + // Using WeakHashMap is not an option since the player object does not exist at AsyncPlayerPreLoginEvent. + Bukkit.getScheduler().runTaskLaterAsynchronously(this.getPlugin(), new Runnable() + { + @Override + public void run() + { + idToRemoteEntries.remove(playerId); + } + }, 20*30); + } + + // Intended to be ran synchronously. + // It will use remoteEntries from AsyncPlayerPreLoginEvent if possible. + // If no such remoteEntries are available it will create them and thus lock the main server thread a bit. + public Map, Entry> getRemoteEntries(String playerId) + { + // If there are stored remote entries we used those ... + Map, Entry> ret = idToRemoteEntries.remove(playerId); + if (ret != null) return ret; + + // ... otherwise we create brand new ones. + return createRemoteEntries(playerId); + } + + // Used by the two methods above. + public Map, Entry> createRemoteEntries(String playerId) + { + // Create Ret + Map, Entry> ret = new HashMap, Entry>(); + + // Fill Ret + for (final SenderColl coll : Coll.getSenderInstances()) + { + Entry remoteEntry = coll.getDb().load(coll, playerId); + ret.put(coll, remoteEntry); + } + + // Return Ret + return ret; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void massiveStoreLoginSync(AsyncPlayerPreLoginEvent event) + { + // DEBUG + // long before = System.nanoTime(); + + // If the login was allowed ... + if (event.getLoginResult() != Result.ALLOWED) return; + + // ... get player id ... + final String playerId = event.getUniqueId().toString(); + + // ... and store the remote entries. + this.storeRemoteEntries(playerId); + + // DEBUG + // long after = System.nanoTime(); + // long duration = after - before; + // double ms = (double)duration / 1000000D; + // String message = Txt.parse("AsyncPlayerPreLoginEvent for %s took %.2f ms.", event.getName(), ms); + // MassiveCore.get().log(message); + // NOTE: I get values between 5 and 55 ms! + } + + // Can not be cancelled. + @EventHandler(priority = EventPriority.LOWEST) + public void massiveStoreLoginSync(PlayerLoginEvent event) + { + // Get player id ... + Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + final String playerId = player.getUniqueId().toString(); + + // ... get remote entries ... + Map, Entry> remoteEntries = getRemoteEntries(playerId); + + // ... and sync each of them. + for (Entry, Entry> entry : remoteEntries.entrySet()) + { + SenderColl coll = entry.getKey(); + Entry remoteEntry = entry.getValue(); + coll.syncId(playerId, null, remoteEntry); + } + } + + // -------------------------------------------- // + // SYNC: LEAVE + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.MONITOR) + public void syncOnPlayerLeave(EventMassiveCorePlayerLeave event) + { + final Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + final String id = player.getUniqueId().toString(); + for (SenderColl coll : Coll.getSenderInstances()) + { + coll.syncId(id); + } + } + +} diff --git a/src/com/massivecraft/massivecore/EngineGank.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreGank.java similarity index 93% rename from src/com/massivecraft/massivecore/EngineGank.java rename to src/com/massivecraft/massivecore/engine/EngineMassiveCoreGank.java index f0409fa2..0670e69a 100644 --- a/src/com/massivecraft/massivecore/EngineGank.java +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreGank.java @@ -1,4 +1,4 @@ -package com.massivecraft.massivecore; +package com.massivecraft.massivecore.engine; import java.util.Map; import java.util.Map.Entry; @@ -17,15 +17,15 @@ import com.massivecraft.massivecore.MassiveCore; import com.massivecraft.massivecore.collections.MassiveMap; import com.massivecraft.massivecore.util.MUtil; -public class EngineGank extends EngineAbstract +public class EngineMassiveCoreGank extends EngineAbstract { // -------------------------------------------- // // INSTANCE & CONSTRUCT // -------------------------------------------- // - private static EngineGank i = new EngineGank(); - public static EngineGank get() { return i; } - public EngineGank() {} + private static EngineMassiveCoreGank i = new EngineMassiveCoreGank(); + public static EngineMassiveCoreGank get() { return i; } + public EngineMassiveCoreGank() {} // -------------------------------------------- // // OVERRIDE diff --git a/src/com/massivecraft/massivecore/engine/EngineMassiveCoreMain.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreMain.java index 0c1d5be8..c42df497 100644 --- a/src/com/massivecraft/massivecore/engine/EngineMassiveCoreMain.java +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreMain.java @@ -3,25 +3,18 @@ package com.massivecraft.massivecore.engine; import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.event.player.AsyncPlayerPreLoginEvent; -import org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result; import org.bukkit.event.player.PlayerChatTabCompleteEvent; import org.bukkit.event.player.PlayerKickEvent; -import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; @@ -34,21 +27,14 @@ import com.massivecraft.massivecore.Predicate; import com.massivecraft.massivecore.PredicateStartsWithIgnoreCase; import com.massivecraft.massivecore.SenderPresence; import com.massivecraft.massivecore.SenderType; -import com.massivecraft.massivecore.collections.MassiveMap; import com.massivecraft.massivecore.event.EventMassiveCoreAfterPlayerRespawn; import com.massivecraft.massivecore.event.EventMassiveCoreAfterPlayerTeleport; import com.massivecraft.massivecore.event.EventMassiveCorePermissionDeniedFormat; -import com.massivecraft.massivecore.event.EventMassiveCorePlayerLeave; import com.massivecraft.massivecore.event.EventMassiveCorePlayerToRecipientChat; -import com.massivecraft.massivecore.event.EventMassiveCoreSenderRegister; -import com.massivecraft.massivecore.event.EventMassiveCoreSenderUnregister; import com.massivecraft.massivecore.mixin.Mixin; -import com.massivecraft.massivecore.store.Coll; -import com.massivecraft.massivecore.store.SenderColl; import com.massivecraft.massivecore.util.IdUtil; import com.massivecraft.massivecore.util.MUtil; import com.massivecraft.massivecore.util.SmokeUtil; -import com.massivecraft.massivecore.xlib.gson.JsonObject; public class EngineMassiveCoreMain extends EngineAbstract { @@ -73,7 +59,6 @@ public class EngineMassiveCoreMain extends EngineAbstract public void activate() { super.activate(); - EventMassiveCorePlayerLeave.player2event.clear(); } // -------------------------------------------- // @@ -186,96 +171,6 @@ public class EngineMassiveCoreMain extends EngineAbstract event.setCancelled(true); } - // -------------------------------------------- // - // PLAYER AND SENDER REFERENCES - // -------------------------------------------- // - // Note: For now we update both names and ids. - // That way collections in plugins that haven't yet undergone update will still work. - - public static Map idToPlayerLoginEvent = new MassiveMap(); - - // This method sets the sender reference to what you decide. - public static void setSenderReferences(CommandSender sender, CommandSender reference, PlayerLoginEvent event) - { - if (MUtil.isntSender(sender)) return; - - String id = IdUtil.getId(sender); - if (id != null) - { - SenderColl.setSenderReferences(id, reference); - if (event == null) - { - idToPlayerLoginEvent.remove(id); - } - else - { - idToPlayerLoginEvent.put(id, event); - } - } - - String name = IdUtil.getName(sender); - if (name != null) - { - SenderColl.setSenderReferences(name, reference); - } - } - - // This method sets the sender reference based on it's online state. - public static void setSenderReferences(Player player, PlayerLoginEvent event) - { - Player reference = player; - if ( ! player.isOnline()) reference = null; - setSenderReferences(player, reference, event); - } - - // Same as above but next tick. - public static void setSenderReferencesSoon(final Player player, final PlayerLoginEvent event) - { - Bukkit.getScheduler().scheduleSyncDelayedTask(MassiveCore.get(), new Runnable() - { - @Override - public void run() - { - setSenderReferences(player, event); - } - }); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void setSenderReferencesLoginLowest(PlayerLoginEvent event) - { - final Player player = event.getPlayer(); - - // We set the reference at LOWEST so that it's present during this PlayerLoginEvent event. - setSenderReferences(player, player, event); - - // And the next tick we update the reference based on it's online state. - // Not all players succeed in logging in. They may for example be banned. - setSenderReferencesSoon(player, null); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void setSenderReferencesQuitMonitor(PlayerQuitEvent event) - { - // PlayerQuitEvents are /probably/ trustworthy. - // We check ourselves the next tick just to be on the safe side. - setSenderReferencesSoon(event.getPlayer(), null); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void setSenderReferencesRegisterMonitor(EventMassiveCoreSenderRegister event) - { - // This one we can however trust. - setSenderReferences(event.getSender(), event.getSender(), null); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void setSenderReferencesUnregisterMonitor(EventMassiveCoreSenderUnregister event) - { - // This one we can however trust. - setSenderReferences(event.getSender(), null, null); - } - // -------------------------------------------- // // AFTER EVENTS // -------------------------------------------- // @@ -332,173 +227,4 @@ public class EngineMassiveCoreMain extends EngineAbstract }); } - // -------------------------------------------- // - // PLAYER LEAVE EVENT - // -------------------------------------------- // - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void leaveEventKickCall(PlayerKickEvent event) - { - Player player = event.getPlayer(); - if (MUtil.isntPlayer(player)) return; - - new EventMassiveCorePlayerLeave(player, true, "kick", event.getReason()).run(); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void leaveEventQuitCall(PlayerQuitEvent event) - { - Player player = event.getPlayer(); - if (MUtil.isntPlayer(player)) return; - - new EventMassiveCorePlayerLeave(player, false, "quit", null).run(); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void leaveEventQuitClear(PlayerQuitEvent event) - { - Player player = event.getPlayer(); - if (MUtil.isntPlayer(player)) return; - final UUID uuid = player.getUniqueId(); - - // We do the schedule in order for the set to be correct through out the whole MONITOR priority state. - - Bukkit.getScheduler().scheduleSyncDelayedTask(MassiveCore.get(), new Runnable() - { - @Override - public void run() - { - EventMassiveCorePlayerLeave.player2event.remove(uuid); - } - }); - } - - // -------------------------------------------- // - // MASSIVE STORE: LOGIN SYNC - // -------------------------------------------- // - // This section handles the automatic sync of a players corresponding massive store entries on login. - // If possible the database IO is made during the AsyncPlayerPreLoginEvent to offloat the main server thread. - - protected Map, Entry>> idToRemoteEntries = new ConcurrentHashMap<>(); - - // Intended to be ran asynchronously. - public void storeRemoteEntries(final String playerId) - { - // Create remote entries ... - Map, Entry> remoteEntries = createRemoteEntries(playerId); - - // ... store them ... - this.idToRemoteEntries.put(playerId, remoteEntries); - - // ... and make sure they are removed after 30 seconds. - // Without this we might cause a memory leak. - // Players might trigger AsyncPlayerPreLoginEvent but not PlayerLoginEvent. - // Using WeakHashMap is not an option since the player object does not exist at AsyncPlayerPreLoginEvent. - Bukkit.getScheduler().runTaskLaterAsynchronously(this.getPlugin(), new Runnable() - { - @Override - public void run() - { - idToRemoteEntries.remove(playerId); - } - }, 20*30); - } - - // Intended to be ran synchronously. - // It will use remoteEntries from AsyncPlayerPreLoginEvent if possible. - // If no such remoteEntries are available it will create them and thus lock the main server thread a bit. - public Map, Entry> getRemoteEntries(String playerId) - { - // If there are stored remote entries we used those ... - Map, Entry> ret = idToRemoteEntries.remove(playerId); - if (ret != null) return ret; - - // ... otherwise we create brand new ones. - return createRemoteEntries(playerId); - } - - // Used by the two methods above. - public Map, Entry> createRemoteEntries(String playerId) - { - // Create Ret - Map, Entry> ret = new HashMap, Entry>(); - - // Fill Ret - for (final SenderColl coll : Coll.getSenderInstances()) - { - Entry remoteEntry = coll.getDb().load(coll, playerId); - ret.put(coll, remoteEntry); - } - - // Return Ret - return ret; - } - - @EventHandler(priority = EventPriority.MONITOR) - public void massiveStoreLoginSync(AsyncPlayerPreLoginEvent event) - { - // DEBUG - // long before = System.nanoTime(); - - // If the login was allowed ... - if (event.getLoginResult() != Result.ALLOWED) return; - - // ... get player id ... - final String playerId = event.getUniqueId().toString(); - - // ... and store the remote entries. - this.storeRemoteEntries(playerId); - - // DEBUG - // long after = System.nanoTime(); - // long duration = after - before; - // double ms = (double)duration / 1000000D; - // String message = Txt.parse("AsyncPlayerPreLoginEvent for %s took %.2f ms.", event.getName(), ms); - // MassiveCore.get().log(message); - // NOTE: I get values between 5 and 55 ms! - } - - // Can not be cancelled. - @EventHandler(priority = EventPriority.LOWEST) - public void massiveStoreLoginSync(PlayerLoginEvent event) - { - // Get player id ... - Player player = event.getPlayer(); - if (MUtil.isntPlayer(player)) return; - final String playerId = player.getUniqueId().toString(); - - // ... get remote entries ... - Map, Entry> remoteEntries = getRemoteEntries(playerId); - - // ... and sync each of them. - for (Entry, Entry> entry : remoteEntries.entrySet()) - { - SenderColl coll = entry.getKey(); - Entry remoteEntry = entry.getValue(); - coll.syncId(playerId, null, remoteEntry); - } - } - - // -------------------------------------------- // - // SYNC PLAYER ON LOGON AND LEAVE - // -------------------------------------------- // - - @EventHandler(priority = EventPriority.MONITOR) - public void syncOnPlayerLeave(EventMassiveCorePlayerLeave event) - { - Player player = event.getPlayer(); - this.syncAllForPlayer(player); - } - - public void syncAllForPlayer(Player player) - { - if (MUtil.isntPlayer(player)) return; - - String playerId = player.getUniqueId().toString(); - for (SenderColl coll : Coll.getSenderInstances()) - { - coll.syncId(playerId); - } - } - } diff --git a/src/com/massivecraft/massivecore/engine/EngineMassiveCorePlayerLeave.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCorePlayerLeave.java new file mode 100644 index 00000000..9db85a69 --- /dev/null +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCorePlayerLeave.java @@ -0,0 +1,80 @@ +package com.massivecraft.massivecore.engine; + +import java.util.UUID; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; + +import com.massivecraft.massivecore.EngineAbstract; +import com.massivecraft.massivecore.MassiveCore; +import com.massivecraft.massivecore.event.EventMassiveCorePlayerLeave; +import com.massivecraft.massivecore.util.MUtil; + +public class EngineMassiveCorePlayerLeave extends EngineAbstract +{ + // -------------------------------------------- // + // INSTANCE & CONSTRUCT + // -------------------------------------------- // + + private static EngineMassiveCorePlayerLeave i = new EngineMassiveCorePlayerLeave(); + public static EngineMassiveCorePlayerLeave get() { return i; } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Plugin getPlugin() + { + return MassiveCore.get(); + } + + @Override + public void activate() + { + super.activate(); + EventMassiveCorePlayerLeave.player2event.clear(); + } + + // -------------------------------------------- // + // KICK (RUN) + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void runKick(PlayerKickEvent event) + { + final Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + + new EventMassiveCorePlayerLeave(player, true, "kick", event.getReason()).run(); + } + + // -------------------------------------------- // + // QUIT (RUN AND CLEAR) + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.MONITOR) + public void runQuit(PlayerQuitEvent event) + { + final Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + final UUID uuid = player.getUniqueId(); + + new EventMassiveCorePlayerLeave(player, false, "quit", null).run(); + + // We do the schedule in order for the set to be correct through out the whole MONITOR priority state. + Bukkit.getScheduler().scheduleSyncDelayedTask(MassiveCore.get(), new Runnable() + { + @Override + public void run() + { + EventMassiveCorePlayerLeave.player2event.remove(uuid); + } + }); + } + +} diff --git a/src/com/massivecraft/massivecore/engine/EngineMassiveCorePlayerState.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCorePlayerState.java new file mode 100644 index 00000000..b9b45e51 --- /dev/null +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCorePlayerState.java @@ -0,0 +1,161 @@ +package com.massivecraft.massivecore.engine; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; + +import com.massivecraft.massivecore.EngineAbstract; +import com.massivecraft.massivecore.MassiveCore; +import com.massivecraft.massivecore.PlayerState; +import com.massivecraft.massivecore.event.EventMassiveCorePlayerLeave; +import com.massivecraft.massivecore.util.MUtil; + +public class EngineMassiveCorePlayerState extends EngineAbstract +{ + // -------------------------------------------- // + // INSTANCE & CONSTRUCT + // -------------------------------------------- // + + private static EngineMassiveCorePlayerState i = new EngineMassiveCorePlayerState(); + public static EngineMassiveCorePlayerState get() { return i; } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Plugin getPlugin() + { + return MassiveCore.get(); + } + + @Override + public void activate() + { + super.activate(); + + idToState.clear(); + for (Player player : MUtil.getOnlinePlayers()) + { + idToState.put(player.getUniqueId(), PlayerState.JOINED); + } + } + + // -------------------------------------------- // + // STATE STORAGE + // -------------------------------------------- // + + protected Map idToState = new ConcurrentHashMap(); + + public PlayerState getState(UUID id) + { + PlayerState ret = this.idToState.get(id); + if (ret == null) ret = PlayerState.LEFT; + return ret; + } + + public PlayerState getState(Player player) + { + return this.getState(player.getUniqueId()); + } + + // -------------------------------------------- // + // LOGASYNC + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.LOWEST) + public void logasync(AsyncPlayerPreLoginEvent event) + { + final UUID id = event.getUniqueId(); + + this.idToState.put(id, PlayerState.LOGASYNC); + } + + // -------------------------------------------- // + // LOGSYNC + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.LOWEST) + public void logsync(PlayerLoginEvent event) + { + final Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + final UUID id = player.getUniqueId(); + + this.idToState.put(id, PlayerState.LOGSYNC); + } + + // -------------------------------------------- // + // JOINING + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.LOWEST) + public void joining(PlayerJoinEvent event) + { + final Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + final UUID id = player.getUniqueId(); + + this.idToState.put(id, PlayerState.JOINING); + } + + // -------------------------------------------- // + // JOINED + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.MONITOR) + public void joined(PlayerJoinEvent event) + { + final Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + final UUID id = player.getUniqueId(); + + Bukkit.getScheduler().scheduleSyncDelayedTask(MassiveCore.get(), new Runnable() + { + @Override + public void run() + { + idToState.put(id, PlayerState.JOINED); + } + }); + } + + // -------------------------------------------- // + // LEAVING + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.LOWEST) + public void leaving(EventMassiveCorePlayerLeave event) + { + final Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + final UUID id = player.getUniqueId(); + + this.idToState.put(id, PlayerState.LEAVING); + } + + // -------------------------------------------- // + // LEFT + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.MONITOR) + public void left(PlayerQuitEvent event) + { + final Player player = event.getPlayer(); + if (MUtil.isntPlayer(player)) return; + final UUID id = player.getUniqueId(); + + this.idToState.remove(id); + } + +} diff --git a/src/com/massivecraft/massivecore/teleport/EngineScheduledTeleport.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreScheduledTeleport.java similarity index 89% rename from src/com/massivecraft/massivecore/teleport/EngineScheduledTeleport.java rename to src/com/massivecraft/massivecore/engine/EngineMassiveCoreScheduledTeleport.java index 3acc3d5a..16fbdc4b 100644 --- a/src/com/massivecraft/massivecore/teleport/EngineScheduledTeleport.java +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreScheduledTeleport.java @@ -1,4 +1,4 @@ -package com.massivecraft.massivecore.teleport; +package com.massivecraft.massivecore.engine; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -16,18 +16,19 @@ import com.massivecraft.massivecore.EngineAbstract; import com.massivecraft.massivecore.MassiveCore; import com.massivecraft.massivecore.event.EventMassiveCorePlayerLeave; import com.massivecraft.massivecore.mixin.Mixin; +import com.massivecraft.massivecore.teleport.ScheduledTeleport; import com.massivecraft.massivecore.util.IdUtil; import com.massivecraft.massivecore.util.MUtil; import com.massivecraft.massivecore.util.TimeUnit; -public class EngineScheduledTeleport extends EngineAbstract +public class EngineMassiveCoreScheduledTeleport extends EngineAbstract { // -------------------------------------------- // // INSTANCE & CONSTRUCT // -------------------------------------------- // - private static EngineScheduledTeleport i = new EngineScheduledTeleport(); - public static EngineScheduledTeleport get() { return i; } + private static EngineMassiveCoreScheduledTeleport i = new EngineMassiveCoreScheduledTeleport(); + public static EngineMassiveCoreScheduledTeleport get() { return i; } // -------------------------------------------- // // SCHEDULED TELEPORT INDEX diff --git a/src/com/massivecraft/massivecore/mixin/EngineTeleportMixinCause.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreTeleportMixinCause.java similarity index 83% rename from src/com/massivecraft/massivecore/mixin/EngineTeleportMixinCause.java rename to src/com/massivecraft/massivecore/engine/EngineMassiveCoreTeleportMixinCause.java index f40a0619..2f66d302 100644 --- a/src/com/massivecraft/massivecore/mixin/EngineTeleportMixinCause.java +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreTeleportMixinCause.java @@ -1,4 +1,4 @@ -package com.massivecraft.massivecore.mixin; +package com.massivecraft.massivecore.engine; import java.util.Collections; import java.util.Set; @@ -13,15 +13,15 @@ import org.bukkit.plugin.Plugin; import com.massivecraft.massivecore.EngineAbstract; import com.massivecraft.massivecore.MassiveCore; -public class EngineTeleportMixinCause extends EngineAbstract +public class EngineMassiveCoreTeleportMixinCause extends EngineAbstract { // -------------------------------------------- // // INSTANCE & CONSTRUCT // -------------------------------------------- // - private static EngineTeleportMixinCause i = new EngineTeleportMixinCause(); - public static EngineTeleportMixinCause get() { return i; } - public EngineTeleportMixinCause() {} + private static EngineMassiveCoreTeleportMixinCause i = new EngineMassiveCoreTeleportMixinCause(); + public static EngineMassiveCoreTeleportMixinCause get() { return i; } + public EngineMassiveCoreTeleportMixinCause() {} // -------------------------------------------- // // OVERRIDE diff --git a/src/com/massivecraft/massivecore/event/EventMassiveCorePlayerLeave.java b/src/com/massivecraft/massivecore/event/EventMassiveCorePlayerLeave.java index ea96b170..69ecc6ed 100644 --- a/src/com/massivecraft/massivecore/event/EventMassiveCorePlayerLeave.java +++ b/src/com/massivecraft/massivecore/event/EventMassiveCorePlayerLeave.java @@ -9,6 +9,8 @@ import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import com.massivecraft.massivecore.util.MUtil; + /** * The EventMassiveCorePlayerLeave is a non-cancellable event. * It is run at the MONITOR of either PlayerKickEvent or PlayerQuitEvent. @@ -67,7 +69,8 @@ public class EventMassiveCorePlayerLeave extends Event implements Runnable @Override public void run() { - UUID uuid = this.player.getUniqueId(); + if (MUtil.isntPlayer(player)) return; + final UUID uuid = this.player.getUniqueId(); // Someone may already have issued a player leave event for this disconnect. // We ignore that since we want one leave event called per disconnect only. diff --git a/src/com/massivecraft/massivecore/mixin/TeleportMixinAbstract.java b/src/com/massivecraft/massivecore/mixin/TeleportMixinAbstract.java index fc52b631..ac52f15d 100644 --- a/src/com/massivecraft/massivecore/mixin/TeleportMixinAbstract.java +++ b/src/com/massivecraft/massivecore/mixin/TeleportMixinAbstract.java @@ -4,6 +4,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.permissions.Permissible; import com.massivecraft.massivecore.MassiveCoreMConf; +import com.massivecraft.massivecore.engine.EngineMassiveCoreTeleportMixinCause; import com.massivecraft.massivecore.teleport.Destination; public abstract class TeleportMixinAbstract implements TeleportMixin @@ -24,7 +25,7 @@ public abstract class TeleportMixinAbstract implements TeleportMixin @Override public boolean isCausedByMixin(PlayerTeleportEvent event) { - return EngineTeleportMixinCause.get().isCausedByTeleportMixin(event); + return EngineMassiveCoreTeleportMixinCause.get().isCausedByTeleportMixin(event); } @Override diff --git a/src/com/massivecraft/massivecore/mixin/TeleportMixinDefault.java b/src/com/massivecraft/massivecore/mixin/TeleportMixinDefault.java index 1148ac5f..444641dd 100644 --- a/src/com/massivecraft/massivecore/mixin/TeleportMixinDefault.java +++ b/src/com/massivecraft/massivecore/mixin/TeleportMixinDefault.java @@ -5,6 +5,7 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.util.Vector; +import com.massivecraft.massivecore.engine.EngineMassiveCoreTeleportMixinCause; import com.massivecraft.massivecore.event.EventMassiveCorePlayerPSTeleport; import com.massivecraft.massivecore.ps.PS; import com.massivecraft.massivecore.teleport.Destination; @@ -49,9 +50,9 @@ public class TeleportMixinDefault extends TeleportMixinAbstract if (vehicle != null) vehicle.eject(); // Do the teleport - EngineTeleportMixinCause.get().setMixinCausedTeleportIncoming(true); + EngineMassiveCoreTeleportMixinCause.get().setMixinCausedTeleportIncoming(true); player.teleport(location); - EngineTeleportMixinCause.get().setMixinCausedTeleportIncoming(false); + EngineMassiveCoreTeleportMixinCause.get().setMixinCausedTeleportIncoming(false); // Bukkit velocity Vector velocity = null; diff --git a/src/com/massivecraft/massivecore/teleport/ScheduledTeleport.java b/src/com/massivecraft/massivecore/teleport/ScheduledTeleport.java index 6a972aff..b3b3ac28 100644 --- a/src/com/massivecraft/massivecore/teleport/ScheduledTeleport.java +++ b/src/com/massivecraft/massivecore/teleport/ScheduledTeleport.java @@ -1,5 +1,6 @@ package com.massivecraft.massivecore.teleport; +import com.massivecraft.massivecore.engine.EngineMassiveCoreScheduledTeleport; import com.massivecraft.massivecore.mixin.Mixin; import com.massivecraft.massivecore.mixin.TeleporterException; @@ -41,17 +42,17 @@ public class ScheduledTeleport implements Runnable public boolean isScheduled() { - return EngineScheduledTeleport.get().isScheduled(this); + return EngineMassiveCoreScheduledTeleport.get().isScheduled(this); } public ScheduledTeleport schedule() { - return EngineScheduledTeleport.get().schedule(this); + return EngineMassiveCoreScheduledTeleport.get().schedule(this); } public boolean unschedule() { - return EngineScheduledTeleport.get().unschedule(this); + return EngineMassiveCoreScheduledTeleport.get().unschedule(this); } // -------------------------------------------- // diff --git a/src/com/massivecraft/massivecore/util/MUtil.java b/src/com/massivecraft/massivecore/util/MUtil.java index 876964d9..853f0fcd 100644 --- a/src/com/massivecraft/massivecore/util/MUtil.java +++ b/src/com/massivecraft/massivecore/util/MUtil.java @@ -63,6 +63,7 @@ import com.massivecraft.massivecore.Predicate; import com.massivecraft.massivecore.collections.MassiveList; import com.massivecraft.massivecore.collections.MassiveSet; import com.massivecraft.massivecore.collections.MassiveTreeSet; +import com.massivecraft.massivecore.engine.EngineMassiveCoreDatabase; import com.massivecraft.massivecore.engine.EngineMassiveCoreMain; import com.massivecraft.massivecore.engine.EngineMassiveCoreWorldNameSet; import com.massivecraft.massivecore.util.extractor.Extractor; @@ -224,7 +225,7 @@ public class MUtil if (address != null) return getIp(address); String id = IdUtil.getId(player); - PlayerLoginEvent event = EngineMassiveCoreMain.idToPlayerLoginEvent.get(id); + PlayerLoginEvent event = EngineMassiveCoreDatabase.idToPlayerLoginEvent.get(id); if (event != null) return getIp(event); return null; diff --git a/src/com/massivecraft/massivecore/util/PlayerUtil.java b/src/com/massivecraft/massivecore/util/PlayerUtil.java index 6d12f34d..1f502e06 100644 --- a/src/com/massivecraft/massivecore/util/PlayerUtil.java +++ b/src/com/massivecraft/massivecore/util/PlayerUtil.java @@ -2,11 +2,7 @@ package com.massivecraft.massivecore.util; import java.util.HashMap; import java.util.Map; -import java.util.Set; import java.util.UUID; -import java.util.concurrent.ConcurrentSkipListSet; - -import org.bukkit.Bukkit; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -19,7 +15,6 @@ import org.bukkit.event.player.PlayerAnimationType; import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.plugin.Plugin; @@ -48,12 +43,6 @@ public class PlayerUtil extends EngineAbstract idToDamageEvent.clear(); idToArmSwingEvent.clear(); - joinedPlayerIds.clear(); - for (Player player : MUtil.getOnlinePlayers()) - { - joinedPlayerIds.add(player.getUniqueId()); - } - idToLastMoveMillis.clear(); } @@ -77,47 +66,6 @@ public class PlayerUtil extends EngineAbstract idToArmSwingEvent.clear(); } - // -------------------------------------------- // - // IS JOINED - // -------------------------------------------- // - - private static Set joinedPlayerIds = new ConcurrentSkipListSet(); - - @EventHandler(priority = EventPriority.MONITOR) - public void isJoined(PlayerJoinEvent event) - { - Player player = event.getPlayer(); - if (MUtil.isntPlayer(player)) return; - - final UUID id = player.getUniqueId(); - Bukkit.getScheduler().scheduleSyncDelayedTask(MassiveCore.get(), new Runnable() - { - @Override - public void run() - { - joinedPlayerIds.add(id); - } - }); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void isJoined(PlayerQuitEvent event) - { - Player player = event.getPlayer(); - if (MUtil.isntPlayer(player)) return; - - final UUID id = player.getUniqueId(); - joinedPlayerIds.remove(id); - } - - public static boolean isJoined(Player player) - { - if (player == null) throw new NullPointerException("player was null"); - final UUID id = player.getUniqueId(); - return joinedPlayerIds.contains(id); - - } - // -------------------------------------------- // // LAST MOVE & STAND STILL (MILLIS) // -------------------------------------------- //