From 8b7b19fada1c1b4e9ded4a711b4d61b08e8d2aca Mon Sep 17 00:00:00 2001 From: Olof Larsson Date: Thu, 22 Aug 2013 13:36:37 +0200 Subject: [PATCH] Adding in our own per recipent chat event through PlayerConnection wrapping. --- src/com/massivecraft/mcore/ConfServer.java | 2 + .../massivecraft/mcore/InternalListener.java | 13 + .../massivecraft/mcore/event/MCoreEvent.java | 14 + .../MCorePlayerToRecipientChatEvent.java | 48 ++ .../wrap/PlayerConnectionWrapAbstract.java | 412 ++++++++++++++++++ .../mcore/wrap/PlayerConnectionWrapMCore.java | 273 ++++++++++++ 6 files changed, 762 insertions(+) create mode 100644 src/com/massivecraft/mcore/event/MCorePlayerToRecipientChatEvent.java create mode 100644 src/com/massivecraft/mcore/wrap/PlayerConnectionWrapAbstract.java create mode 100644 src/com/massivecraft/mcore/wrap/PlayerConnectionWrapMCore.java diff --git a/src/com/massivecraft/mcore/ConfServer.java b/src/com/massivecraft/mcore/ConfServer.java index eec3b26c..a3056119 100644 --- a/src/com/massivecraft/mcore/ConfServer.java +++ b/src/com/massivecraft/mcore/ConfServer.java @@ -31,6 +31,8 @@ public class ConfServer extends SimpleConfig public static String dburi = "default"; + public static boolean usingPlayerConnectionWrap = true; + public static List aliasesOuterMCore = MUtil.list("mcore"); public static List aliasesOuterMCoreUsys = MUtil.list("usys"); public static List aliasesOuterMCoreMStore = MUtil.list("mstore"); diff --git a/src/com/massivecraft/mcore/InternalListener.java b/src/com/massivecraft/mcore/InternalListener.java index 092ca9eb..11dd1ce0 100644 --- a/src/com/massivecraft/mcore/InternalListener.java +++ b/src/com/massivecraft/mcore/InternalListener.java @@ -13,6 +13,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent.Result; @@ -32,6 +33,7 @@ import com.massivecraft.mcore.store.Coll; import com.massivecraft.mcore.store.SenderColl; import com.massivecraft.mcore.util.SenderUtil; import com.massivecraft.mcore.util.SmokeUtil; +import com.massivecraft.mcore.wrap.PlayerConnectionWrapMCore; public class InternalListener implements Listener { @@ -52,6 +54,17 @@ public class InternalListener implements Listener Bukkit.getPluginManager().registerEvents(this, MCore.get()); } + // -------------------------------------------- // + // PLAYER CONNECTION WRAP INJECTION + // -------------------------------------------- // + + @EventHandler(priority = EventPriority.LOWEST) + public void playerConnectionWrapInjection(final PlayerJoinEvent event) + { + if (!ConfServer.usingPlayerConnectionWrap) return; + new PlayerConnectionWrapMCore(event.getPlayer()); + } + // -------------------------------------------- // // PERMISSION DENIED FORMAT // -------------------------------------------- // diff --git a/src/com/massivecraft/mcore/event/MCoreEvent.java b/src/com/massivecraft/mcore/event/MCoreEvent.java index e5815d85..8d2630bc 100644 --- a/src/com/massivecraft/mcore/event/MCoreEvent.java +++ b/src/com/massivecraft/mcore/event/MCoreEvent.java @@ -30,4 +30,18 @@ public abstract class MCoreEvent extends Event implements Runnable, Cancellable Bukkit.getPluginManager().callEvent(this); } + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public MCoreEvent() + { + super(); + } + + public MCoreEvent(boolean isAsync) + { + super(isAsync); + } + } diff --git a/src/com/massivecraft/mcore/event/MCorePlayerToRecipientChatEvent.java b/src/com/massivecraft/mcore/event/MCorePlayerToRecipientChatEvent.java new file mode 100644 index 00000000..0c766be0 --- /dev/null +++ b/src/com/massivecraft/mcore/event/MCorePlayerToRecipientChatEvent.java @@ -0,0 +1,48 @@ +package com.massivecraft.mcore.event; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +public class MCorePlayerToRecipientChatEvent extends MCoreEvent +{ + // -------------------------------------------- // + // REQUIRED EVENT CODE + // -------------------------------------------- // + + private static final HandlerList handlers = new HandlerList(); + @Override public HandlerList getHandlers() { return handlers; } + public static HandlerList getHandlerList() { return handlers; } + + // -------------------------------------------- // + // FIELD + // -------------------------------------------- // + + private final Player sender; + public Player getSender() { return this.sender; } + + private final CommandSender recipient; + public CommandSender getRecipient() { return this.recipient; } + + private String message; + public String getMessage() { return this.message; } + public void setMessage(String message) { this.message = message; } + + private String format; + public String getFormat() { return this.format; } + public void setFormat(String format) { this.format = format; } + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public MCorePlayerToRecipientChatEvent(boolean async, Player sender, CommandSender recipient, String message, String format) + { + super(async); + this.sender = sender; + this.recipient = recipient; + this.message = message; + this.format = format; + } + +} diff --git a/src/com/massivecraft/mcore/wrap/PlayerConnectionWrapAbstract.java b/src/com/massivecraft/mcore/wrap/PlayerConnectionWrapAbstract.java new file mode 100644 index 00000000..7cbf92f1 --- /dev/null +++ b/src/com/massivecraft/mcore/wrap/PlayerConnectionWrapAbstract.java @@ -0,0 +1,412 @@ +package com.massivecraft.mcore.wrap; + +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_6_R2.util.LazyPlayerSet; +import org.bukkit.craftbukkit.v1_6_R2.CraftServer; +import org.bukkit.craftbukkit.v1_6_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; + +import com.massivecraft.mcore.store.accessor.AccessorUtil; +import com.massivecraft.mcore.store.accessor.PropertyAccessor; + +import net.minecraft.server.v1_6_R2.EntityPlayer; +import net.minecraft.server.v1_6_R2.Packet; +import net.minecraft.server.v1_6_R2.Packet0KeepAlive; +import net.minecraft.server.v1_6_R2.Packet101CloseWindow; +import net.minecraft.server.v1_6_R2.Packet102WindowClick; +import net.minecraft.server.v1_6_R2.Packet106Transaction; +import net.minecraft.server.v1_6_R2.Packet107SetCreativeSlot; +import net.minecraft.server.v1_6_R2.Packet108ButtonClick; +import net.minecraft.server.v1_6_R2.Packet130UpdateSign; +import net.minecraft.server.v1_6_R2.Packet15Place; +import net.minecraft.server.v1_6_R2.Packet16BlockItemSwitch; +import net.minecraft.server.v1_6_R2.Packet18ArmAnimation; +import net.minecraft.server.v1_6_R2.Packet19EntityAction; +import net.minecraft.server.v1_6_R2.Packet202Abilities; +import net.minecraft.server.v1_6_R2.Packet203TabComplete; +import net.minecraft.server.v1_6_R2.Packet204LocaleAndViewDistance; +import net.minecraft.server.v1_6_R2.Packet205ClientCommand; +import net.minecraft.server.v1_6_R2.Packet250CustomPayload; +import net.minecraft.server.v1_6_R2.Packet255KickDisconnect; +import net.minecraft.server.v1_6_R2.Packet3Chat; +import net.minecraft.server.v1_6_R2.Packet7UseEntity; +import net.minecraft.server.v1_6_R2.Packet9Respawn; +import net.minecraft.server.v1_6_R2.Packet14BlockDig; +import net.minecraft.server.v1_6_R2.Packet10Flying; +import net.minecraft.server.v1_6_R2.MinecraftServer; +import net.minecraft.server.v1_6_R2.Packet27PlayerInput; +import net.minecraft.server.v1_6_R2.PlayerConnection; + +/** + * It's sometime fun to wrap the NMS PlayerConnection with a version that behaves slightly differently. + * This class is an unmodified wrap to be used just for that. + * Just extend this class with the features you want and then instantiate for the player. + * It's perfectly fine to wrap a wrap the wraps a wrap and so on. + * + * Great care should be taken when updating this class to a new NMS version. + * Follow the following steps: + * + * 1. Make sure that all public methods are overriden to call the this.inner. + * 2. Take a look at the method constructor. Does it do something new fancy? + * 3. Where are the public fields altered? Make sure to update them accordingly. (remember to check mc-dev as well as CraftBukkit) + * 4. handleCommandPublic method update though copy paste and minor adjustments. + * + * https://github.com/Bukkit/CraftBukkit/commits/master/src/main/java/net/minecraft/server/PlayerConnection.java + * This file was last updated for the following commit: + * "Fix missed diff for chat packets. Fixes BUKKIT-4666" + * Wolvereness authored 2013-08-07 + */ +public abstract class PlayerConnectionWrapAbstract extends PlayerConnection +{ + // -------------------------------------------- // + // STATIC UTILS + // -------------------------------------------- // + + public static PlayerConnection getPlayerConnection(Player player) + { + CraftPlayer cplayer = (CraftPlayer)player; + EntityPlayer eplayer = cplayer.getHandle(); + return eplayer.playerConnection; + } + + public static void setPlayerConnection(Player player, PlayerConnection playerConnection) + { + CraftPlayer cplayer = (CraftPlayer)player; + EntityPlayer eplayer = cplayer.getHandle(); + eplayer.playerConnection = playerConnection; + } + + // -------------------------------------------- // + // FIEDS + // -------------------------------------------- // + + private final PlayerConnection inner; + + // -------------------------------------------- // + // RAW NATIVE FIELD THEORY + // -------------------------------------------- // + + // public final INetworkManager networkManager; + // This one should never change since it's final. + // Using the very same must thus be fine. + + // private final MinecraftServer minecraftServer; + // Same argumentation as above. + + // public boolean disconnected; + // This one is set in two locations only from within this class + // 1. public void a(String s, Object[] aobject) + // 2. public void disconnect(String s) + // For that reason we update the reference upwards at those locations. + + // public EntityPlayer player; + // This one is set in two locations only from within this class + // 1. The constructor + // 2. public void a(Packet205ClientCommand packet205clientcommand) + // For that reason we update the reference upwards at those locations. + + // public boolean checkMovement = true; // CraftBukkit - private -> public + // This one was made public but I do not see why. + // It's only used from within this class. + // Thus we do not need to keep it updated in the wrapper. + + // -------------------------------------------- // + // MAKING STUFF PUBLIC + // -------------------------------------------- // + + public final static transient PropertyAccessor minecraftServerAccessor = AccessorUtil.createPropertyAccessor(PlayerConnection.class, "minecraftServer"); + public MinecraftServer getMinecraftServer() + { + return (MinecraftServer) minecraftServerAccessor.get(this); + } + + public final static transient PropertyAccessor serverAccessor = AccessorUtil.createPropertyAccessor(PlayerConnection.class, "server"); + public CraftServer getServer() + { + return (CraftServer) serverAccessor.get(this); + } + + public final static transient PropertyAccessor chatSpamFieldAccessor = AccessorUtil.createPropertyAccessor(PlayerConnection.class, "chatSpamField"); + @SuppressWarnings("unchecked") + public AtomicIntegerFieldUpdater getChatSpamField() + { + return (AtomicIntegerFieldUpdater) chatSpamFieldAccessor.get(this); + } + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + // Note that instantiating this class or a subclass of this class will cause the PlayerConnection to be injected. + // It's designed that way in the vanilla NMS superclass. + + public PlayerConnectionWrapAbstract(Player player) + { + this(getPlayerConnection(player)); + } + + public PlayerConnectionWrapAbstract(PlayerConnection inner) + { + super(inner.player.server, inner.networkManager, inner.player); + + this.inner = inner; + + this.disconnected = inner.disconnected; + this.checkMovement = inner.checkMovement; + } + + // -------------------------------------------- // + // WRAP + // -------------------------------------------- // + + @Override + public CraftPlayer getPlayer() + { + return this.inner.getPlayer(); + } + + @Override + public void e() + { + this.inner.e(); + } + + @Override + public void disconnect(String s) + { + this.inner.disconnect(s); + this.disconnected = this.inner.disconnected; + } + + @Override + public void a(Packet27PlayerInput packet27playerinput) + { + this.inner.a(packet27playerinput); + } + + @Override + public void a(Packet10Flying packet10flying) + { + this.inner.a(packet10flying); + } + + @Override + public void a(double d0, double d1, double d2, float f, float f1) + { + this.inner.a(d0, d1, d2, f, f1); + } + + @Override + public void teleport(Location dest) + { + this.inner.teleport(dest); + } + + @Override + public void a(Packet14BlockDig packet14blockdig) + { + this.inner.a(packet14blockdig); + } + + @Override + public void a(Packet15Place packet15place) + { + this.inner.a(packet15place); + } + + @Override + public void a(String s, Object[] aobject) + { + this.inner.a(s, aobject); + this.disconnected = this.inner.disconnected; + } + + @Override + public void onUnhandledPacket(Packet packet) + { + this.inner.onUnhandledPacket(packet); + } + + @Override + public void sendPacket(Packet packet) + { + this.inner.sendPacket(packet); + } + + @Override + public void a(Packet16BlockItemSwitch packet16blockitemswitch) + { + this.inner.a(packet16blockitemswitch); + } + + @Override + public void a(Packet3Chat packet3chat) + { + this.inner.a(packet3chat); + } + + @Override + public void chat(String s, boolean async) + { + this.inner.chat(s, async); + } + + public void handleCommandPublic(String s) + { + // CraftBukkit start + CraftPlayer player = this.getPlayer(); + + PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet()); + this.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + try { + this.getMinecraftServer().getLogger().info(event.getPlayer().getName() + " issued server command: " + event.getMessage()); // CraftBukkit + if (this.getServer().dispatchCommand(event.getPlayer(), event.getMessage().substring(1))) { + return; + } + } catch (org.bukkit.command.CommandException ex) { + player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command"); + java.util.logging.Logger.getLogger(PlayerConnection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + return; + } + // CraftBukkit end + + /* CraftBukkit start - No longer needed as we have already handled it in server.dispatchServerCommand above. + this.minecraftServer.getCommandHandler().a(this.player, s); + // CraftBukkit end */ + } + + @Override + public void a(Packet18ArmAnimation packet18armanimation) + { + this.inner.a(packet18armanimation); + } + + @Override + public void a(Packet19EntityAction packet19entityaction) + { + this.inner.a(packet19entityaction); + } + + @Override + public void a(Packet255KickDisconnect packet255kickdisconnect) + { + this.inner.a(packet255kickdisconnect); + } + + @Override + public int lowPriorityCount() + { + return this.inner.lowPriorityCount(); + } + + @Override + public void a(Packet7UseEntity packet7useentity) + { + this.inner.a(packet7useentity); + } + + @Override + public void a(Packet205ClientCommand packet205clientcommand) + { + this.inner.a(packet205clientcommand); + this.player = this.inner.player; + } + + @Override + public boolean b() + { + return this.inner.b(); + } + + @Override + public void a(Packet9Respawn packet9respawn) + { + this.inner.a(packet9respawn); + } + + @Override + public void handleContainerClose(Packet101CloseWindow packet101closewindow) + { + this.inner.handleContainerClose(packet101closewindow); + } + + @Override + public void a(Packet102WindowClick packet102windowclick) + { + this.inner.a(packet102windowclick); + } + + @Override + public void a(Packet108ButtonClick packet108buttonclick) + { + this.inner.a(packet108buttonclick); + } + + @Override + public void a(Packet107SetCreativeSlot packet107setcreativeslot) + { + this.inner.a(packet107setcreativeslot); + } + + @Override + public void a(Packet106Transaction packet106transaction) + { + this.inner.a(packet106transaction); + } + + @Override + public void a(Packet130UpdateSign packet130updatesign) + { + this.inner.a(packet130updatesign); + } + + @Override + public void a(Packet0KeepAlive packet0keepalive) + { + this.inner.a(packet0keepalive); + } + + @Override + public boolean a() + { + return this.inner.a(); + } + + @Override + public void a(Packet202Abilities packet202abilities) + { + this.inner.a(packet202abilities); + } + + @Override + public void a(Packet203TabComplete packet203tabcomplete) + { + this.inner.a(packet203tabcomplete); + } + + @Override + public void a(Packet204LocaleAndViewDistance packet204localeandviewdistance) + { + this.inner.a(packet204localeandviewdistance); + } + + @Override + public void a(Packet250CustomPayload packet250custompayload) + { + this.inner.a(packet250custompayload); + } + + @Override + public boolean c() + { + return this.inner.c(); + } + +} diff --git a/src/com/massivecraft/mcore/wrap/PlayerConnectionWrapMCore.java b/src/com/massivecraft/mcore/wrap/PlayerConnectionWrapMCore.java new file mode 100644 index 00000000..2d95f106 --- /dev/null +++ b/src/com/massivecraft/mcore/wrap/PlayerConnectionWrapMCore.java @@ -0,0 +1,273 @@ +package com.massivecraft.mcore.wrap; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import net.minecraft.server.v1_6_R2.ChatMessage; +import net.minecraft.server.v1_6_R2.EnumChatFormat; +import net.minecraft.server.v1_6_R2.SharedConstants; +import net.minecraft.server.v1_6_R2.Packet3Chat; + +import org.apache.commons.lang3.StringUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.v1_6_R2.util.LazyPlayerSet; +import org.bukkit.craftbukkit.v1_6_R2.util.Waitable; +import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerChatEvent; + +import com.massivecraft.mcore.event.MCorePlayerToRecipientChatEvent; + +/** + * This is the MCore PlayerConnectionWrap. + * It will be injected for all players unless you disable that from the ServerConf. + * + * Currently it adds in the features required for the event MCorePlayerToRecipientChatEvent. + * + * To update void a(Packet3Chat packet3chat): + * 1. Paste in the method a(Packet3Chat packet3chat): + * 2. Change the private field references to the public ones. + * + * To update void chat(String s, boolean async): + * 1. Paste in the method chat(String s, boolean async) + * 2. Change the private field references to the public ones. + * 3. Replace the message distribution parts. + * + * https://github.com/Bukkit/CraftBukkit/commits/master/src/main/java/net/minecraft/server/PlayerConnection.java + * This file was last updated for the following commit: + * "Fix missed diff for chat packets. Fixes BUKKIT-4666" + * Wolvereness authored 2013-08-07 + */ +@SuppressWarnings("deprecation") +public class PlayerConnectionWrapMCore extends PlayerConnectionWrapAbstract +{ + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public PlayerConnectionWrapMCore(Player player) + { + super(player); + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + public void distributeMessage(boolean async, Player sender, Set recipients, String message, String format) + { + // The console shall not have special attention! + Set recipientsAndConsole = new LinkedHashSet(recipients); + recipientsAndConsole.add(this.getMinecraftServer().console); + + // For each of the recipients + for (CommandSender recipient : recipientsAndConsole) + { + // Run the event for this unique recipient + MCorePlayerToRecipientChatEvent event = new MCorePlayerToRecipientChatEvent(async, sender, recipient, message, format); + event.run(); + + // Format and send with the format and message from this recipient's own event. + String playerMessage = String.format(event.getFormat(), sender.getDisplayName(), event.getMessage()); + recipient.sendMessage(playerMessage); + } + + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @SuppressWarnings("rawtypes") + @Override + public void a(Packet3Chat packet3chat) + { + if (this.player.getChatFlags() == 2) { + this.sendPacket(new Packet3Chat(ChatMessage.e("chat.cannotSend").a(EnumChatFormat.RED))); + } else { + String s = packet3chat.message; + + if (s.length() > 100) { + // CraftBukkit start + if (packet3chat.a_()) { + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { + PlayerConnectionWrapMCore.this.disconnect("Chat message too long"); + return null; + } + }; + + this.getMinecraftServer().processQueue.add(waitable); + + try { + waitable.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } else { + this.disconnect("Chat message too long"); + } + // CraftBukkit end + } else { + s = StringUtils.normalizeSpace(s); + + for (int i = 0; i < s.length(); ++i) { + if (!SharedConstants.isAllowedChatCharacter(s.charAt(i))) { + // CraftBukkit start + if (packet3chat.a_()) { + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { + PlayerConnectionWrapMCore.this.disconnect("Illegal characters in chat"); + return null; + } + }; + + this.getMinecraftServer().processQueue.add(waitable); + + try { + waitable.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } else { + this.disconnect("Illegal characters in chat"); + } + // CraftBukkit end + return; + } + } + + // CraftBukkit start + if (this.player.getChatFlags() == 1 && !s.startsWith("/")) { + this.sendPacket(new Packet3Chat(ChatMessage.e("chat.cannotSend").a(EnumChatFormat.RED))); + return; + } + + this.chat(s, packet3chat.a_()); + + // This section stays because it is only applicable to packets + if (this.getChatSpamField().addAndGet(this, 20) > 200 && !this.getMinecraftServer().getPlayerList().isOp(this.player.getName())) { // CraftBukkit use thread-safe spam + if (packet3chat.a_()) { + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { + PlayerConnectionWrapMCore.this.disconnect("disconnect.spam"); + return null; + } + }; + + this.getMinecraftServer().processQueue.add(waitable); + + try { + waitable.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } else { + this.disconnect("disconnect.spam"); + } + } + } + } + } + + @SuppressWarnings("rawtypes") + @Override + public void chat(String s, boolean async) + { + if (!this.player.dead) { + if (s.length() == 0) { + this.getMinecraftServer().getLogger().warning(this.player.getName() + " tried to send an empty message"); + return; + } + + if (getPlayer().isConversing()) { + getPlayer().acceptConversationInput(s); + return; + } + + if (s.startsWith("/")) { + this.handleCommandPublic(s); + return; + } else { + Player player = this.getPlayer(); + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(async, player, s, new LazyPlayerSet()); + this.getServer().getPluginManager().callEvent(event); + + if (PlayerChatEvent.getHandlerList().getRegisteredListeners().length != 0) { + // Evil plugins still listening to deprecated event + final PlayerChatEvent queueEvent = new PlayerChatEvent(player, event.getMessage(), event.getFormat(), event.getRecipients()); + queueEvent.setCancelled(event.isCancelled()); + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { + org.bukkit.Bukkit.getPluginManager().callEvent(queueEvent); + + if (queueEvent.isCancelled()) { + return null; + } + + // MCore - start + distributeMessage(false, queueEvent.getPlayer(), queueEvent.getRecipients(), queueEvent.getMessage(), queueEvent.getFormat()); + /*String message = String.format(queueEvent.getFormat(), queueEvent.getPlayer().getDisplayName(), queueEvent.getMessage()); + minecraftServerPublic.console.sendMessage(message); + if (((LazyPlayerSet) queueEvent.getRecipients()).isLazy()) { + for (Object player : minecraftServerPublic.getPlayerList().players) { + ((EntityPlayer) player).sendMessage(ChatMessage.d(message)); + } + } else { + for (Player player : queueEvent.getRecipients()) { + player.sendMessage(message); + } + }*/ + // MCore - end + return null; + }}; + if (async) { + this.getMinecraftServer().processQueue.add(waitable); + } else { + waitable.run(); + } + try { + waitable.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on! + } catch (ExecutionException e) { + throw new RuntimeException("Exception processing chat event", e.getCause()); + } + } else { + if (event.isCancelled()) { + return; + } + + // MCore - start + distributeMessage(async, event.getPlayer(), event.getRecipients(), event.getMessage(), event.getFormat()); + /*s = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()); + minecraftServerPublic.console.sendMessage(s); + if (((LazyPlayerSet) event.getRecipients()).isLazy()) { + for (Object recipient : minecraftServerPublic.getPlayerList().players) { + ((EntityPlayer) recipient).sendMessage(ChatMessage.d(s)); + } + } else { + for (Player recipient : event.getRecipients()) { + recipient.sendMessage(s); + } + }*/ + // MCore - end + } + } + } + + return; + } + +}