Adding in our own per recipent chat event through PlayerConnection wrapping.

This commit is contained in:
Olof Larsson 2013-08-22 13:36:37 +02:00
parent 0fd2fc5bf5
commit 8b7b19fada
6 changed files with 762 additions and 0 deletions

View File

@ -31,6 +31,8 @@ public class ConfServer extends SimpleConfig
public static String dburi = "default"; public static String dburi = "default";
public static boolean usingPlayerConnectionWrap = true;
public static List<String> aliasesOuterMCore = MUtil.list("mcore"); public static List<String> aliasesOuterMCore = MUtil.list("mcore");
public static List<String> aliasesOuterMCoreUsys = MUtil.list("usys"); public static List<String> aliasesOuterMCoreUsys = MUtil.list("usys");
public static List<String> aliasesOuterMCoreMStore = MUtil.list("mstore"); public static List<String> aliasesOuterMCoreMStore = MUtil.list("mstore");

View File

@ -13,6 +13,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageByBlockEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result; 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.store.SenderColl;
import com.massivecraft.mcore.util.SenderUtil; import com.massivecraft.mcore.util.SenderUtil;
import com.massivecraft.mcore.util.SmokeUtil; import com.massivecraft.mcore.util.SmokeUtil;
import com.massivecraft.mcore.wrap.PlayerConnectionWrapMCore;
public class InternalListener implements Listener public class InternalListener implements Listener
{ {
@ -52,6 +54,17 @@ public class InternalListener implements Listener
Bukkit.getPluginManager().registerEvents(this, MCore.get()); 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 // PERMISSION DENIED FORMAT
// -------------------------------------------- // // -------------------------------------------- //

View File

@ -30,4 +30,18 @@ public abstract class MCoreEvent extends Event implements Runnable, Cancellable
Bukkit.getPluginManager().callEvent(this); Bukkit.getPluginManager().callEvent(this);
} }
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public MCoreEvent()
{
super();
}
public MCoreEvent(boolean isAsync)
{
super(isAsync);
}
} }

View File

@ -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;
}
}

View File

@ -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<PlayerConnection> getChatSpamField()
{
return (AtomicIntegerFieldUpdater<PlayerConnection>) 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();
}
}

View File

@ -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<Player> recipients, String message, String format)
{
// The console shall not have special attention!
Set<CommandSender> recipientsAndConsole = new LinkedHashSet<CommandSender>(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;
}
}