Massive update.
Dieser Commit ist enthalten in:
Ursprung
413eb283a4
Commit
f16581cdf4
@ -1,10 +1,16 @@
|
|||||||
package com.comphenix.protocol;
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
import net.minecraft.server.Packet;
|
||||||
import org.apache.commons.lang.builder.ToStringStyle;
|
import net.sf.cglib.proxy.Factory;
|
||||||
|
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
@ -12,6 +18,7 @@ import org.bukkit.command.CommandSender;
|
|||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import com.comphenix.protocol.concurrency.AbstractIntervalTree;
|
import com.comphenix.protocol.concurrency.AbstractIntervalTree;
|
||||||
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
import com.comphenix.protocol.events.ConnectionSide;
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
import com.comphenix.protocol.events.ListenerPriority;
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||||
@ -19,6 +26,8 @@ import com.comphenix.protocol.events.PacketEvent;
|
|||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
import com.comphenix.protocol.injector.GamePhase;
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
|
import com.comphenix.protocol.reflect.PrettyPrinter;
|
||||||
|
import com.comphenix.protocol.utility.ChatExtensions;
|
||||||
import com.google.common.collect.DiscreteDomains;
|
import com.google.common.collect.DiscreteDomains;
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
import com.google.common.collect.Ranges;
|
import com.google.common.collect.Ranges;
|
||||||
@ -48,16 +57,21 @@ class CommandPacket implements CommandExecutor {
|
|||||||
|
|
||||||
private Plugin plugin;
|
private Plugin plugin;
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
private ErrorReporter reporter;
|
||||||
private ProtocolManager manager;
|
private ProtocolManager manager;
|
||||||
|
|
||||||
|
private ChatExtensions chatter;
|
||||||
|
|
||||||
// Registered packet listeners
|
// Registered packet listeners
|
||||||
private AbstractIntervalTree<Integer, DetailedPacketListener> clientListeners = createTree(ConnectionSide.CLIENT_SIDE);
|
private AbstractIntervalTree<Integer, DetailedPacketListener> clientListeners = createTree(ConnectionSide.CLIENT_SIDE);
|
||||||
private AbstractIntervalTree<Integer, DetailedPacketListener> serverListeners = createTree(ConnectionSide.SERVER_SIDE);
|
private AbstractIntervalTree<Integer, DetailedPacketListener> serverListeners = createTree(ConnectionSide.SERVER_SIDE);
|
||||||
|
|
||||||
public CommandPacket(Plugin plugin, Logger logger, ProtocolManager manager) {
|
public CommandPacket(Plugin plugin, Logger logger, ErrorReporter reporter, ProtocolManager manager) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
this.reporter = reporter;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.chatter = new ChatExtensions(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,6 +125,33 @@ class CommandPacket implements CommandExecutor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message without invoking the packet listeners.
|
||||||
|
* @param player - the player to send it to.
|
||||||
|
* @param message - the message to send.
|
||||||
|
* @return TRUE if the message was sent successfully, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public void sendMessageSilently(CommandSender receiver, String message) {
|
||||||
|
try {
|
||||||
|
chatter.sendMessageSilently(receiver, message);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
reporter.reportDetailed(this, "Cannot send chat message.", e, receiver, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast a message without invoking any packet listeners.
|
||||||
|
* @param message - message to send.
|
||||||
|
* @param permission - permission required to receieve the message. NULL to target everyone.
|
||||||
|
*/
|
||||||
|
public void broadcastMessageSilently(String message, String permission) {
|
||||||
|
try {
|
||||||
|
chatter.broadcastMessageSilently(message, permission);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
reporter.reportDetailed(this, "Cannot send chat message.", e, message, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Description: Adds or removes a simple packet listener.
|
* Description: Adds or removes a simple packet listener.
|
||||||
Usage: /<command> add|remove client|server|both [ID start] [ID stop] [detailed]
|
Usage: /<command> add|remove client|server|both [ID start] [ID stop] [detailed]
|
||||||
@ -127,37 +168,189 @@ class CommandPacket implements CommandExecutor {
|
|||||||
SubCommand subCommand = parseCommand(args, 0);
|
SubCommand subCommand = parseCommand(args, 0);
|
||||||
ConnectionSide side = parseSide(args, 1, ConnectionSide.BOTH);
|
ConnectionSide side = parseSide(args, 1, ConnectionSide.BOTH);
|
||||||
|
|
||||||
int idStart = parseInteger(args, 2, 0);
|
Integer lastIndex = args.length - 1;
|
||||||
int idStop = parseInteger(args, 3, 255);
|
Boolean detailed = parseBoolean(args, lastIndex);
|
||||||
|
|
||||||
|
// See if the last element is a boolean
|
||||||
|
if (detailed == null) {
|
||||||
|
detailed = false;
|
||||||
|
} else {
|
||||||
|
lastIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the packet IDs are valid
|
// Make sure the packet IDs are valid
|
||||||
if (idStart < 0 || idStart > 255)
|
List<Range<Integer>> ranges = getRanges(args, 2, lastIndex, Ranges.closed(0, 255));
|
||||||
throw new IllegalAccessError("The starting packet ID must be within 0 - 255.");
|
|
||||||
if (idStop < 0 || idStop > 255)
|
|
||||||
throw new IllegalAccessError("The stop packet ID must be within 0 - 255.");
|
|
||||||
|
|
||||||
// Special case. If stop is not set, but start is set, use a interval size of 1.
|
if (ranges.isEmpty()) {
|
||||||
if (args.length == 3)
|
// Use every packet ID
|
||||||
idStop = idStart + 1;
|
ranges.add(Ranges.closed(0, 255));
|
||||||
|
}
|
||||||
boolean detailed = parseBoolean(args, 4, false);
|
|
||||||
|
|
||||||
// Perform command
|
// Perform command
|
||||||
if (subCommand == SubCommand.ADD)
|
if (subCommand == SubCommand.ADD) {
|
||||||
addPacketListeners(side, idStart, idStop, detailed);
|
for (Range<Integer> range : ranges) {
|
||||||
else
|
DetailedPacketListener listener = addPacketListeners(side, range.lowerEndpoint(), range.upperEndpoint(), detailed);
|
||||||
removePacketListeners(side, idStart, idStop, detailed);
|
sendMessageSilently(sender, ChatColor.BLUE + "Added listener " + getWhitelistInfo(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (subCommand == SubCommand.REMOVE) {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// Remove each packet listener
|
||||||
|
for (Range<Integer> range : ranges) {
|
||||||
|
count += removePacketListeners(side, range.lowerEndpoint(), range.upperEndpoint(), detailed).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessageSilently(sender, ChatColor.BLUE + "Fully removed " + count + " listeners.");
|
||||||
|
}
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "Cannot parse number: " + e.getMessage());
|
sendMessageSilently(sender, ChatColor.RED + "Cannot parse number: " + e.getMessage());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
sender.sendMessage(ChatColor.DARK_RED + e.getMessage());
|
sendMessageSilently(sender, ChatColor.RED + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse ranges from an array of tokens.
|
||||||
|
* @param args - array of tokens.
|
||||||
|
* @param offset - beginning offset.
|
||||||
|
* @param legalRange - range of legal values.
|
||||||
|
* @return The parsed ranges.
|
||||||
|
*/
|
||||||
|
public static List<Range<Integer>> getRanges(String[] args, int offset, int lastIndex, Range<Integer> legalRange) {
|
||||||
|
List<String> tokens = tokenizeInput(args, offset, lastIndex);
|
||||||
|
List<Range<Integer>> ranges = new ArrayList<Range<Integer>>();
|
||||||
|
|
||||||
|
for (int i = 0; i < tokens.size(); i++) {
|
||||||
|
Range<Integer> range;
|
||||||
|
String current = tokens.get(i);
|
||||||
|
String next = i + 1 < tokens.size() ? tokens.get(i + 1) : null;
|
||||||
|
|
||||||
|
// Yoda equality is done for null-safety
|
||||||
|
if ("-".equals(current)) {
|
||||||
|
throw new IllegalArgumentException("A hyphen must appear between two numbers.");
|
||||||
|
} else if ("-".equals(next)) {
|
||||||
|
if (i + 2 >= tokens.size())
|
||||||
|
throw new IllegalArgumentException("Cannot form a range without a upper limit.");
|
||||||
|
|
||||||
|
// This is a proper range
|
||||||
|
range = Ranges.closed(Integer.parseInt(current), Integer.parseInt(tokens.get(i + 2)));
|
||||||
|
ranges.add(range);
|
||||||
|
|
||||||
|
// Skip the two next tokens
|
||||||
|
i += 2;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Just a single number
|
||||||
|
range = Ranges.singleton(Integer.parseInt(current));
|
||||||
|
ranges.add(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate ranges
|
||||||
|
if (!legalRange.encloses(range)) {
|
||||||
|
throw new IllegalArgumentException(range + " is not in the range " + range.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return simplify(ranges, legalRange.upperEndpoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplify a list of ranges by assuming a maximum value.
|
||||||
|
* @param ranges - the list of ranges to simplify.
|
||||||
|
* @param maximum - the maximum value (minimum value is always 0).
|
||||||
|
* @return A simplified list of ranges.
|
||||||
|
*/
|
||||||
|
private static List<Range<Integer>> simplify(List<Range<Integer>> ranges, int maximum) {
|
||||||
|
List<Range<Integer>> result = new ArrayList<Range<Integer>>();
|
||||||
|
boolean[] set = new boolean[maximum + 1];
|
||||||
|
int start = -1;
|
||||||
|
|
||||||
|
// Set every ID
|
||||||
|
for (Range<Integer> range : ranges) {
|
||||||
|
for (int id : range.asSet(DiscreteDomains.integers())) {
|
||||||
|
set[id] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate ranges from this set
|
||||||
|
for (int i = 0; i <= set.length; i++) {
|
||||||
|
if (i < set.length && set[i]) {
|
||||||
|
if (start < 0) {
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (start > 0) {
|
||||||
|
result.add(Ranges.closed(start, i - 1));
|
||||||
|
start = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> tokenizeInput(String[] args, int offset, int lastIndex) {
|
||||||
|
List<String> tokens = new ArrayList<String>();
|
||||||
|
|
||||||
|
// Tokenize the input
|
||||||
|
for (int i = offset; i <= lastIndex; i++) {
|
||||||
|
String text = args[i];
|
||||||
|
StringBuilder number = new StringBuilder();
|
||||||
|
|
||||||
|
for (int j = 0; j < text.length(); j++) {
|
||||||
|
char current = text.charAt(j);
|
||||||
|
|
||||||
|
if (Character.isDigit(current)) {
|
||||||
|
number.append(current);
|
||||||
|
} else if (Character.isWhitespace(current)) {
|
||||||
|
// That's ok
|
||||||
|
} else if (current == '-') {
|
||||||
|
// Add the number token first
|
||||||
|
if (number.length() > 0) {
|
||||||
|
tokens.add(number.toString());
|
||||||
|
number.setLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.add(Character.toString(current));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Illegal character '" + current + "' found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the number token, if it hasn't already
|
||||||
|
if (number.length() > 0)
|
||||||
|
tokens.add(number.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve whitelist information about a given listener.
|
||||||
|
* @param listener - the given listener.
|
||||||
|
* @return Whitelist information.
|
||||||
|
*/
|
||||||
|
private String getWhitelistInfo(PacketListener listener) {
|
||||||
|
boolean sendingEmpty = ListeningWhitelist.isEmpty(listener.getSendingWhitelist());
|
||||||
|
boolean receivingEmpty = ListeningWhitelist.isEmpty(listener.getReceivingWhitelist());
|
||||||
|
|
||||||
|
if (!sendingEmpty && !receivingEmpty)
|
||||||
|
return String.format("Sending: %s, Receiving: %s", listener.getSendingWhitelist(), listener.getReceivingWhitelist());
|
||||||
|
else if (!sendingEmpty)
|
||||||
|
return listener.getSendingWhitelist().toString();
|
||||||
|
else if (!receivingEmpty)
|
||||||
|
return listener.getReceivingWhitelist().toString();
|
||||||
|
else
|
||||||
|
return "[None]";
|
||||||
|
}
|
||||||
|
|
||||||
private Set<Integer> getValidPackets(ConnectionSide side) throws FieldAccessException {
|
private Set<Integer> getValidPackets(ConnectionSide side) throws FieldAccessException {
|
||||||
if (side.isForClient())
|
if (side.isForClient())
|
||||||
return Packets.Client.getSupported();
|
return Packets.Client.getSupported();
|
||||||
@ -174,7 +367,7 @@ class CommandPacket implements CommandExecutor {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Only use supported packet IDs
|
// Only use supported packet IDs
|
||||||
packets = getValidPackets(side);
|
packets = new HashSet<Integer>(getValidPackets(side));
|
||||||
packets.retainAll(range);
|
packets.retainAll(range);
|
||||||
|
|
||||||
} catch (FieldAccessException e) {
|
} catch (FieldAccessException e) {
|
||||||
@ -207,17 +400,32 @@ class CommandPacket implements CommandExecutor {
|
|||||||
private void printInformation(PacketEvent event) {
|
private void printInformation(PacketEvent event) {
|
||||||
String verb = side.isForClient() ? "Received" : "Sent";
|
String verb = side.isForClient() ? "Received" : "Sent";
|
||||||
String shortDescription = String.format(
|
String shortDescription = String.format(
|
||||||
"%s packet %s (%s)",
|
"%s %s (%s) from %s",
|
||||||
verb,
|
verb,
|
||||||
|
Packets.getDeclaredName(event.getPacketID()),
|
||||||
event.getPacketID(),
|
event.getPacketID(),
|
||||||
Packets.getDeclaredName(event.getPacketID())
|
event.getPlayer().getName()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Detailed will print the packet's content too
|
// Detailed will print the packet's content too
|
||||||
if (detailed) {
|
if (detailed) {
|
||||||
logger.info(shortDescription + ":\n" +
|
try {
|
||||||
ToStringBuilder.reflectionToString(event.getPacket().getHandle(), ToStringStyle.MULTI_LINE_STYLE)
|
Packet packet = event.getPacket().getHandle();
|
||||||
);
|
Class<?> clazz = packet.getClass();
|
||||||
|
|
||||||
|
// Get the first Minecraft super class
|
||||||
|
while ((!clazz.getName().startsWith("net.minecraft.server") ||
|
||||||
|
Factory.class.isAssignableFrom(clazz)) && clazz != Object.class) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(shortDescription + ":\n" +
|
||||||
|
PrettyPrinter.printObject(packet, clazz, Packet.class)
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
logger.log(Level.WARNING, "Unable to use reflection.", e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info(shortDescription + ".");
|
logger.info(shortDescription + ".");
|
||||||
}
|
}
|
||||||
@ -245,19 +453,23 @@ class CommandPacket implements CommandExecutor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPacketListeners(ConnectionSide side, int idStart, int idStop, boolean detailed) {
|
public DetailedPacketListener addPacketListeners(ConnectionSide side, int idStart, int idStop, boolean detailed) {
|
||||||
DetailedPacketListener listener = createPacketListener(side, idStart, idStop, detailed);
|
DetailedPacketListener listener = createPacketListener(side, idStart, idStop, detailed);
|
||||||
|
|
||||||
// The trees will manage the listeners for us
|
// The trees will manage the listeners for us
|
||||||
if (listener != null)
|
if (listener != null) {
|
||||||
getListenerTree(side).put(idStart, idStop, listener);
|
getListenerTree(side).put(idStart, idStop, listener);
|
||||||
else
|
return listener;
|
||||||
|
} else {
|
||||||
throw new IllegalArgumentException("No packets found in the range " + idStart + " - " + idStop + ".");
|
throw new IllegalArgumentException("No packets found in the range " + idStart + " - " + idStop + ".");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removePacketListeners(ConnectionSide side, int idStart, int idStop, boolean detailed) {
|
public Set<AbstractIntervalTree<Integer, DetailedPacketListener>.Entry> removePacketListeners(
|
||||||
|
ConnectionSide side, int idStart, int idStop, boolean detailed) {
|
||||||
|
|
||||||
// The interval tree will automatically remove the listeners for us
|
// The interval tree will automatically remove the listeners for us
|
||||||
getListenerTree(side).remove(idStart, idStop);
|
return getListenerTree(side).remove(idStart, idStop);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractIntervalTree<Integer, DetailedPacketListener> getListenerTree(ConnectionSide side) {
|
private AbstractIntervalTree<Integer, DetailedPacketListener> getListenerTree(ConnectionSide side) {
|
||||||
@ -286,9 +498,7 @@ class CommandPacket implements CommandExecutor {
|
|||||||
String text = args[index].toLowerCase();
|
String text = args[index].toLowerCase();
|
||||||
|
|
||||||
// Parse the side gracefully
|
// Parse the side gracefully
|
||||||
if ("both".startsWith(text))
|
if ("client".startsWith(text))
|
||||||
return ConnectionSide.BOTH;
|
|
||||||
else if ("client".startsWith(text))
|
|
||||||
return ConnectionSide.CLIENT_SIDE;
|
return ConnectionSide.CLIENT_SIDE;
|
||||||
else if ("server".startsWith(text))
|
else if ("server".startsWith(text))
|
||||||
return ConnectionSide.SERVER_SIDE;
|
return ConnectionSide.SERVER_SIDE;
|
||||||
@ -301,25 +511,16 @@ class CommandPacket implements CommandExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse a boolean
|
// Parse a boolean
|
||||||
private boolean parseBoolean(String[] args, int index, boolean defaultValue) {
|
private Boolean parseBoolean(String[] args, int index) {
|
||||||
if (index < args.length) {
|
if (index < args.length) {
|
||||||
return Boolean.parseBoolean(args[index]);
|
if (args[index].equalsIgnoreCase("true"))
|
||||||
|
return true;
|
||||||
|
else if (args[index].equalsIgnoreCase("false"))
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return defaultValue;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// And an integer
|
|
||||||
private int parseInteger(String[] args, int index, int defaultValue) {
|
|
||||||
if (index < args.length) {
|
|
||||||
return Integer.parseInt(args[index]);
|
|
||||||
} else {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cleanupAll() {
|
|
||||||
clientListeners.clear();
|
|
||||||
serverListeners.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ package com.comphenix.protocol;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Handler;
|
||||||
|
import java.util.logging.LogRecord;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
@ -42,6 +44,7 @@ import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
|||||||
public class ProtocolLibrary extends JavaPlugin {
|
public class ProtocolLibrary extends JavaPlugin {
|
||||||
|
|
||||||
private static final long MILLI_PER_SECOND = 1000;
|
private static final long MILLI_PER_SECOND = 1000;
|
||||||
|
private static final String PERMISSION_INFO = "protocol.info";
|
||||||
|
|
||||||
// There should only be one protocol manager, so we'll make it static
|
// There should only be one protocol manager, so we'll make it static
|
||||||
private static PacketFilterManager protocolManager;
|
private static PacketFilterManager protocolManager;
|
||||||
@ -70,17 +73,21 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
// Updater
|
// Updater
|
||||||
private Updater updater;
|
private Updater updater;
|
||||||
|
|
||||||
|
// Logger
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
private CommandProtocol commandProtocol;
|
private CommandProtocol commandProtocol;
|
||||||
private CommandPacket commandPacket;
|
private CommandPacket commandPacket;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
|
// Load configuration
|
||||||
|
logger = getLoggerSafely();
|
||||||
|
|
||||||
// Add global parameters
|
// Add global parameters
|
||||||
DetailedErrorReporter reporter = new DetailedErrorReporter();
|
DetailedErrorReporter reporter = new DetailedErrorReporter();
|
||||||
|
updater = new Updater(this, logger, "protocollib", getFile(), "protocol.info");
|
||||||
// Load configuration
|
|
||||||
updater = new Updater(this, "protocollib", getFile(), "protocol.info");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
config = new ProtocolConfig(this);
|
config = new ProtocolConfig(this);
|
||||||
@ -99,7 +106,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
|
|
||||||
// Initialize command handlers
|
// Initialize command handlers
|
||||||
commandProtocol = new CommandProtocol(this, updater);
|
commandProtocol = new CommandProtocol(this, updater);
|
||||||
commandPacket = new CommandPacket(this, getLoggerSafely(), protocolManager);
|
commandPacket = new CommandPacket(this, logger, reporter, protocolManager);
|
||||||
|
|
||||||
|
// Send logging information to player listeners too
|
||||||
|
broadcastUsers(PERMISSION_INFO);
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
reporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager);
|
reporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager);
|
||||||
@ -121,6 +131,26 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
config = new ProtocolConfig(this);
|
config = new ProtocolConfig(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void broadcastUsers(final String permission) {
|
||||||
|
// Broadcast information to every user too
|
||||||
|
logger.addHandler(new Handler() {
|
||||||
|
@Override
|
||||||
|
public void publish(LogRecord record) {
|
||||||
|
commandPacket.broadcastMessageSilently(record.getMessage(), permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
// Not needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws SecurityException {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
try {
|
try {
|
||||||
|
@ -6,8 +6,6 @@ import java.util.NavigableMap;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import org.apache.commons.lang.NotImplementedException;
|
|
||||||
|
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
import com.google.common.collect.Ranges;
|
import com.google.common.collect.Ranges;
|
||||||
|
|
||||||
@ -35,11 +33,18 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
*/
|
*/
|
||||||
public class Entry implements Map.Entry<Range<TKey>, TValue> {
|
public class Entry implements Map.Entry<Range<TKey>, TValue> {
|
||||||
private final Range<TKey> key;
|
private final Range<TKey> key;
|
||||||
private final TValue value;
|
private EndPoint left;
|
||||||
|
private EndPoint right;
|
||||||
|
|
||||||
|
Entry(Range<TKey> key, EndPoint left, EndPoint right) {
|
||||||
|
if (left == null)
|
||||||
|
throw new IllegalAccessError("left cannot be NUll");
|
||||||
|
if (right == null)
|
||||||
|
throw new IllegalAccessError("right cannot be NUll");
|
||||||
|
|
||||||
public Entry(Range<TKey> key, TValue value) {
|
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.value = value;
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -49,12 +54,17 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TValue getValue() {
|
public TValue getValue() {
|
||||||
return value;
|
return left.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TValue setValue(TValue value) {
|
public TValue setValue(TValue value) {
|
||||||
throw new NotImplementedException();
|
TValue old = left.value;
|
||||||
|
|
||||||
|
// Set both end points
|
||||||
|
left.value = value;
|
||||||
|
right.value = value;
|
||||||
|
return old;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,8 +93,8 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
* @param lowerBound - lowest value to remove.
|
* @param lowerBound - lowest value to remove.
|
||||||
* @param upperBound - highest value to remove.
|
* @param upperBound - highest value to remove.
|
||||||
*/
|
*/
|
||||||
public void remove(TKey lowerBound, TKey upperBound) {
|
public Set<Entry> remove(TKey lowerBound, TKey upperBound) {
|
||||||
remove(lowerBound, upperBound, false);
|
return remove(lowerBound, upperBound, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,13 +103,13 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
* @param upperBound - highest value to remove.
|
* @param upperBound - highest value to remove.
|
||||||
* @param preserveOutside - whether or not to preserve the intervals that are partially outside.
|
* @param preserveOutside - whether or not to preserve the intervals that are partially outside.
|
||||||
*/
|
*/
|
||||||
public void remove(TKey lowerBound, TKey upperBound, boolean preserveDifference) {
|
public Set<Entry> remove(TKey lowerBound, TKey upperBound, boolean preserveDifference) {
|
||||||
checkBounds(lowerBound, upperBound);
|
checkBounds(lowerBound, upperBound);
|
||||||
NavigableMap<TKey, EndPoint> range = bounds.subMap(lowerBound, true, upperBound, true);
|
NavigableMap<TKey, EndPoint> range = bounds.subMap(lowerBound, true, upperBound, true);
|
||||||
|
|
||||||
boolean emptyRange = range.isEmpty();
|
boolean emptyRange = range.isEmpty();
|
||||||
TKey first = emptyRange ? range.firstKey() : null;
|
TKey first = !emptyRange ? range.firstKey() : null;
|
||||||
TKey last = emptyRange ? range.lastKey() : null;
|
TKey last = !emptyRange ? range.lastKey() : null;
|
||||||
|
|
||||||
Set<Entry> resized = new HashSet<Entry>();
|
Set<Entry> resized = new HashSet<Entry>();
|
||||||
Set<Entry> removed = new HashSet<Entry>();
|
Set<Entry> removed = new HashSet<Entry>();
|
||||||
@ -136,6 +146,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
|
|
||||||
// Remove the range as well
|
// Remove the range as well
|
||||||
range.clear();
|
range.clear();
|
||||||
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper
|
// Helper
|
||||||
@ -154,7 +165,8 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
if (endPoint != null) {
|
if (endPoint != null) {
|
||||||
endPoint.state = State.BOTH;
|
endPoint.state = State.BOTH;
|
||||||
} else {
|
} else {
|
||||||
endPoint = bounds.put(key, new EndPoint(state, value));
|
endPoint = new EndPoint(state, value);
|
||||||
|
bounds.put(key, endPoint);
|
||||||
}
|
}
|
||||||
return endPoint;
|
return endPoint;
|
||||||
}
|
}
|
||||||
@ -184,11 +196,11 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
private Entry putUnsafe(TKey lowerBound, TKey upperBound, TValue value) {
|
private Entry putUnsafe(TKey lowerBound, TKey upperBound, TValue value) {
|
||||||
// OK. Add the end points now
|
// OK. Add the end points now
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
addEndPoint(lowerBound, value, State.OPEN);
|
EndPoint left = addEndPoint(lowerBound, value, State.OPEN);
|
||||||
addEndPoint(upperBound, value, State.CLOSE);
|
EndPoint right = addEndPoint(upperBound, value, State.CLOSE);
|
||||||
|
|
||||||
Range<TKey> range = Ranges.closed(lowerBound, upperBound);
|
Range<TKey> range = Ranges.closed(lowerBound, upperBound);
|
||||||
return new Entry(range, value);
|
return new Entry(range, left, right);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -248,11 +260,12 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
for (Map.Entry<TKey, EndPoint> entry : bounds.entrySet()) {
|
for (Map.Entry<TKey, EndPoint> entry : bounds.entrySet()) {
|
||||||
switch (entry.getValue().state) {
|
switch (entry.getValue().state) {
|
||||||
case BOTH:
|
case BOTH:
|
||||||
destination.add(new Entry(Ranges.singleton(entry.getKey()), entry.getValue().value));
|
EndPoint point = entry.getValue();
|
||||||
|
destination.add(new Entry(Ranges.singleton(entry.getKey()), point, point));
|
||||||
break;
|
break;
|
||||||
case CLOSE:
|
case CLOSE:
|
||||||
Range<TKey> range = Ranges.closed(last.getKey(), entry.getKey());
|
Range<TKey> range = Ranges.closed(last.getKey(), entry.getKey());
|
||||||
destination.add(new Entry(range, entry.getValue().value));
|
destination.add(new Entry(range, last.getValue(), entry.getValue()));
|
||||||
break;
|
break;
|
||||||
case OPEN:
|
case OPEN:
|
||||||
// We don't know the full range yet
|
// We don't know the full range yet
|
||||||
@ -271,7 +284,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
|||||||
public void putAll(AbstractIntervalTree<TKey, TValue> other) {
|
public void putAll(AbstractIntervalTree<TKey, TValue> other) {
|
||||||
// Naively copy every range.
|
// Naively copy every range.
|
||||||
for (Entry entry : other.entrySet()) {
|
for (Entry entry : other.entrySet()) {
|
||||||
put(entry.key.lowerEndpoint(), entry.key.upperEndpoint(), entry.value);
|
put(entry.key.lowerEndpoint(), entry.key.upperEndpoint(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +137,20 @@ public class ListeningWhitelist {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given whitelist is empty or not.
|
||||||
|
* @param whitelist - the whitelist to test.
|
||||||
|
* @return TRUE if the whitelist is empty, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(ListeningWhitelist whitelist) {
|
||||||
|
if (whitelist == EMPTY_WHITELIST)
|
||||||
|
return true;
|
||||||
|
else if (whitelist == null)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return whitelist.getWhitelist().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj){
|
public boolean equals(final Object obj){
|
||||||
if(obj instanceof ListeningWhitelist){
|
if(obj instanceof ListeningWhitelist){
|
||||||
@ -157,5 +171,4 @@ public class ListeningWhitelist {
|
|||||||
.add("priority", priority)
|
.add("priority", priority)
|
||||||
.add("packets", whitelist).toString();
|
.add("packets", whitelist).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -212,41 +212,12 @@ public class Updater
|
|||||||
* @param permission
|
* @param permission
|
||||||
* Permission needed to read the output of the update process.
|
* Permission needed to read the output of the update process.
|
||||||
*/
|
*/
|
||||||
public Updater(Plugin plugin, String slug, File file, String permission)
|
public Updater(Plugin plugin, Logger logger, String slug, File file, String permission)
|
||||||
{
|
{
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.slug = slug;
|
this.slug = slug;
|
||||||
|
this.logger = logger;
|
||||||
// Prevent issues with older versions of Bukkit
|
|
||||||
try {
|
|
||||||
logger = plugin.getLogger();
|
|
||||||
logger.getLevel();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
logger = Logger.getLogger("Minecraft");
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcastUsers(plugin.getServer(), permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void broadcastUsers(final Server server, final String permission) {
|
|
||||||
// Broadcast information to every user too
|
|
||||||
logger.addHandler(new Handler() {
|
|
||||||
@Override
|
|
||||||
public void publish(LogRecord record) {
|
|
||||||
server.broadcast(record.getMessage(), permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() {
|
|
||||||
// Not needed.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws SecurityException {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,172 @@
|
|||||||
|
package com.comphenix.protocol.reflect;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Primitives;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to print the content of an arbitrary class.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class PrettyPrinter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How far we will recurse.
|
||||||
|
*/
|
||||||
|
public final static int RECURSE_DEPTH = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the content of an object.
|
||||||
|
* @param object - the object to serialize.
|
||||||
|
* @param stop - superclass that will stop the process.
|
||||||
|
* @return String representation of the class.
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
public static String printObject(Object object, Class<?> start, Class<?> stop) throws IllegalAccessException {
|
||||||
|
return printObject(object, start, stop, RECURSE_DEPTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the content of an object.
|
||||||
|
* @param object - the object to serialize.
|
||||||
|
* @param stop - superclass that will stop the process.
|
||||||
|
* @param depth - how far in the hierachy until we stop.
|
||||||
|
* @return String representation of the class.
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
public static String printObject(Object object, Class<?> start, Class<?> stop, int hierachyDepth) throws IllegalAccessException {
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
Set<Object> previous = new HashSet<Object>();
|
||||||
|
|
||||||
|
// Start and stop
|
||||||
|
output.append("{ ");
|
||||||
|
printObject(output, object, start, stop, previous, hierachyDepth);
|
||||||
|
output.append(" }");
|
||||||
|
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static void printIterables(StringBuilder output, Iterable iterable, Class<?> current, Class<?> stop,
|
||||||
|
Set<Object> previous, int hierachyIndex) throws IllegalAccessException {
|
||||||
|
|
||||||
|
boolean first = true;
|
||||||
|
output.append("(");
|
||||||
|
|
||||||
|
for (Object value : iterable) {
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
output.append(", ");
|
||||||
|
|
||||||
|
// Handle exceptions
|
||||||
|
if (value != null)
|
||||||
|
printValue(output, value, value.getClass(), stop, previous, hierachyIndex - 1);
|
||||||
|
else
|
||||||
|
output.append("NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
output.append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printArray(StringBuilder output, Object array, Class<?> current, Class<?> stop,
|
||||||
|
Set<Object> previous, int hierachyIndex) throws IllegalAccessException {
|
||||||
|
|
||||||
|
Class<?> component = current.getComponentType();
|
||||||
|
boolean first = true;
|
||||||
|
|
||||||
|
if (!component.isArray())
|
||||||
|
output.append(component.getName());
|
||||||
|
output.append("[");
|
||||||
|
|
||||||
|
for (int i = 0; i < Array.getLength(array); i++) {
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
output.append(", ");
|
||||||
|
|
||||||
|
// Handle exceptions
|
||||||
|
try {
|
||||||
|
printValue(output, Array.get(array, i), component, stop, previous, hierachyIndex - 1);
|
||||||
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
break;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.append("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal recursion method
|
||||||
|
private static void printObject(StringBuilder output, Object object, Class<?> current, Class<?> stop,
|
||||||
|
Set<Object> previous, int hierachyIndex) throws IllegalAccessException {
|
||||||
|
// Trickery
|
||||||
|
boolean first = true;
|
||||||
|
|
||||||
|
// See if we're supposed to skip this class
|
||||||
|
if (current == Object.class || (stop != null && current.equals(stop))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't iterate twice
|
||||||
|
previous.add(object);
|
||||||
|
|
||||||
|
// Hard coded limit
|
||||||
|
if (hierachyIndex < 0) {
|
||||||
|
output.append("...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Field field : current.getDeclaredFields()) {
|
||||||
|
int mod = field.getModifiers();
|
||||||
|
|
||||||
|
// Skip a good number of the fields
|
||||||
|
if (!Modifier.isTransient(mod) && !Modifier.isStatic(mod)) {
|
||||||
|
Class<?> type = field.getType();
|
||||||
|
Object value = FieldUtils.readField(field, object, true);
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
output.append(", ");
|
||||||
|
|
||||||
|
output.append(field.getName());
|
||||||
|
output.append(" = ");
|
||||||
|
printValue(output, value, type, stop, previous, hierachyIndex - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse
|
||||||
|
printObject(output, object, current.getSuperclass(), stop, previous, hierachyIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static void printValue(StringBuilder output, Object value, Class<?> type,
|
||||||
|
Class<?> stop, Set<Object> previous, int hierachyIndex) throws IllegalAccessException {
|
||||||
|
// Just print primitive types
|
||||||
|
if (value == null) {
|
||||||
|
output.append("NULL");
|
||||||
|
} else if (type.isPrimitive() || Primitives.isWrapperType(type) || type == String.class || hierachyIndex <= 0) {
|
||||||
|
output.append(value);
|
||||||
|
} else if (type.isArray()) {
|
||||||
|
printArray(output, value, type, stop, previous, hierachyIndex);
|
||||||
|
} else if (Iterable.class.isAssignableFrom(type)) {
|
||||||
|
printIterables(output, (Iterable) value, type, stop, previous, hierachyIndex);
|
||||||
|
} else if (ClassLoader.class.isAssignableFrom(type) || previous.contains(value)) {
|
||||||
|
// Don't print previous objects
|
||||||
|
output.append(value);
|
||||||
|
} else {
|
||||||
|
output.append("{ ");
|
||||||
|
printObject(output, value, value.getClass(), stop, previous, hierachyIndex);
|
||||||
|
output.append(" }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.Packets;
|
||||||
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
|
import com.comphenix.protocol.injector.PacketConstructor;
|
||||||
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for sending chat messages.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class ChatExtensions {
|
||||||
|
|
||||||
|
// Used to sent chat messages
|
||||||
|
private PacketConstructor chatConstructor;
|
||||||
|
private ProtocolManager manager;
|
||||||
|
|
||||||
|
public ChatExtensions(ProtocolManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message without invoking the packet listeners.
|
||||||
|
* @param player - the player to send it to.
|
||||||
|
* @param message - the message to send.
|
||||||
|
* @return TRUE if the message was sent successfully, FALSE otherwise.
|
||||||
|
* @throws InvocationTargetException If we were unable to send the message.
|
||||||
|
*/
|
||||||
|
public void sendMessageSilently(CommandSender receiver, String message) throws InvocationTargetException {
|
||||||
|
if (receiver == null)
|
||||||
|
throw new IllegalArgumentException("receiver cannot be NULL.");
|
||||||
|
if (message == null)
|
||||||
|
throw new IllegalArgumentException("message cannot be NULL.");
|
||||||
|
|
||||||
|
// Handle the player case by manually sending packets
|
||||||
|
if (receiver instanceof Player) {
|
||||||
|
sendMessageSilently((Player) receiver, message);
|
||||||
|
} else {
|
||||||
|
receiver.sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message without invoking the packet listeners.
|
||||||
|
* @param player - the player to send it to.
|
||||||
|
* @param message - the message to send.
|
||||||
|
* @return TRUE if the message was sent successfully, FALSE otherwise.
|
||||||
|
* @throws InvocationTargetException If we were unable to send the message.
|
||||||
|
*/
|
||||||
|
private void sendMessageSilently(Player player, String message) throws InvocationTargetException {
|
||||||
|
if (chatConstructor == null)
|
||||||
|
chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, message);
|
||||||
|
|
||||||
|
try {
|
||||||
|
manager.sendServerPacket(player, chatConstructor.createPacket(message), false);
|
||||||
|
} catch (FieldAccessException e) {
|
||||||
|
throw new InvocationTargetException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast a message without invoking any packet listeners.
|
||||||
|
* @param message - message to send.
|
||||||
|
* @param permission - permission required to receieve the message. NULL to target everyone.
|
||||||
|
* @throws InvocationTargetException If we were unable to send the message.
|
||||||
|
*/
|
||||||
|
public void broadcastMessageSilently(String message, String permission) throws InvocationTargetException {
|
||||||
|
if (message == null)
|
||||||
|
throw new IllegalArgumentException("message cannot be NULL.");
|
||||||
|
|
||||||
|
// Send this message to every online player
|
||||||
|
for (Player player : Bukkit.getServer().getOnlinePlayers()) {
|
||||||
|
if (permission == null || player.hasPermission(permission)) {
|
||||||
|
sendMessageSilently(player, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ commands:
|
|||||||
permission-message: You don't have <permission>
|
permission-message: You don't have <permission>
|
||||||
packet:
|
packet:
|
||||||
description: Add or remove a simple packet listener.
|
description: Add or remove a simple packet listener.
|
||||||
usage: /<command> add|remove client|server|both [ID start] [ID stop] [detailed]
|
usage: /<command> add|remove client|server [ID start]-[ID stop] [detailed]
|
||||||
permission: experiencemod.admin
|
permission: experiencemod.admin
|
||||||
permission-message: You don't have <permission>
|
permission-message: You don't have <permission>
|
||||||
|
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren