Massive update.
Dieser Commit ist enthalten in:
Ursprung
413eb283a4
Commit
f16581cdf4
@ -1,10 +1,16 @@
|
||||
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.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@ -12,6 +18,7 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.concurrency.AbstractIntervalTree;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
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.injector.GamePhase;
|
||||
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.Range;
|
||||
import com.google.common.collect.Ranges;
|
||||
@ -48,16 +57,21 @@ class CommandPacket implements CommandExecutor {
|
||||
|
||||
private Plugin plugin;
|
||||
private Logger logger;
|
||||
private ErrorReporter reporter;
|
||||
private ProtocolManager manager;
|
||||
|
||||
private ChatExtensions chatter;
|
||||
|
||||
// Registered packet listeners
|
||||
private AbstractIntervalTree<Integer, DetailedPacketListener> clientListeners = createTree(ConnectionSide.CLIENT_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.logger = logger;
|
||||
this.reporter = reporter;
|
||||
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.
|
||||
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);
|
||||
ConnectionSide side = parseSide(args, 1, ConnectionSide.BOTH);
|
||||
|
||||
int idStart = parseInteger(args, 2, 0);
|
||||
int idStop = parseInteger(args, 3, 255);
|
||||
Integer lastIndex = args.length - 1;
|
||||
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
|
||||
if (idStart < 0 || idStart > 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.");
|
||||
List<Range<Integer>> ranges = getRanges(args, 2, lastIndex, Ranges.closed(0, 255));
|
||||
|
||||
// Special case. If stop is not set, but start is set, use a interval size of 1.
|
||||
if (args.length == 3)
|
||||
idStop = idStart + 1;
|
||||
|
||||
boolean detailed = parseBoolean(args, 4, false);
|
||||
if (ranges.isEmpty()) {
|
||||
// Use every packet ID
|
||||
ranges.add(Ranges.closed(0, 255));
|
||||
}
|
||||
|
||||
// Perform command
|
||||
if (subCommand == SubCommand.ADD)
|
||||
addPacketListeners(side, idStart, idStop, detailed);
|
||||
else
|
||||
removePacketListeners(side, idStart, idStop, detailed);
|
||||
if (subCommand == SubCommand.ADD) {
|
||||
for (Range<Integer> range : ranges) {
|
||||
DetailedPacketListener listener = addPacketListeners(side, range.lowerEndpoint(), range.upperEndpoint(), 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) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Cannot parse number: " + e.getMessage());
|
||||
sendMessageSilently(sender, ChatColor.RED + "Cannot parse number: " + e.getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + e.getMessage());
|
||||
sendMessageSilently(sender, ChatColor.RED + e.getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (side.isForClient())
|
||||
return Packets.Client.getSupported();
|
||||
@ -174,7 +367,7 @@ class CommandPacket implements CommandExecutor {
|
||||
|
||||
try {
|
||||
// Only use supported packet IDs
|
||||
packets = getValidPackets(side);
|
||||
packets = new HashSet<Integer>(getValidPackets(side));
|
||||
packets.retainAll(range);
|
||||
|
||||
} catch (FieldAccessException e) {
|
||||
@ -207,17 +400,32 @@ class CommandPacket implements CommandExecutor {
|
||||
private void printInformation(PacketEvent event) {
|
||||
String verb = side.isForClient() ? "Received" : "Sent";
|
||||
String shortDescription = String.format(
|
||||
"%s packet %s (%s)",
|
||||
"%s %s (%s) from %s",
|
||||
verb,
|
||||
Packets.getDeclaredName(event.getPacketID()),
|
||||
event.getPacketID(),
|
||||
Packets.getDeclaredName(event.getPacketID())
|
||||
event.getPlayer().getName()
|
||||
);
|
||||
|
||||
// Detailed will print the packet's content too
|
||||
if (detailed) {
|
||||
logger.info(shortDescription + ":\n" +
|
||||
ToStringBuilder.reflectionToString(event.getPacket().getHandle(), ToStringStyle.MULTI_LINE_STYLE)
|
||||
);
|
||||
try {
|
||||
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 {
|
||||
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);
|
||||
|
||||
// The trees will manage the listeners for us
|
||||
if (listener != null)
|
||||
if (listener != null) {
|
||||
getListenerTree(side).put(idStart, idStop, listener);
|
||||
else
|
||||
return listener;
|
||||
} else {
|
||||
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
|
||||
getListenerTree(side).remove(idStart, idStop);
|
||||
return getListenerTree(side).remove(idStart, idStop);
|
||||
}
|
||||
|
||||
private AbstractIntervalTree<Integer, DetailedPacketListener> getListenerTree(ConnectionSide side) {
|
||||
@ -286,9 +498,7 @@ class CommandPacket implements CommandExecutor {
|
||||
String text = args[index].toLowerCase();
|
||||
|
||||
// Parse the side gracefully
|
||||
if ("both".startsWith(text))
|
||||
return ConnectionSide.BOTH;
|
||||
else if ("client".startsWith(text))
|
||||
if ("client".startsWith(text))
|
||||
return ConnectionSide.CLIENT_SIDE;
|
||||
else if ("server".startsWith(text))
|
||||
return ConnectionSide.SERVER_SIDE;
|
||||
@ -301,25 +511,16 @@ class CommandPacket implements CommandExecutor {
|
||||
}
|
||||
|
||||
// Parse a boolean
|
||||
private boolean parseBoolean(String[] args, int index, boolean defaultValue) {
|
||||
private Boolean parseBoolean(String[] args, int index) {
|
||||
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 {
|
||||
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.IOException;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.Server;
|
||||
@ -42,6 +44,7 @@ import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
public class ProtocolLibrary extends JavaPlugin {
|
||||
|
||||
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
|
||||
private static PacketFilterManager protocolManager;
|
||||
@ -70,17 +73,21 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
// Updater
|
||||
private Updater updater;
|
||||
|
||||
// Logger
|
||||
private Logger logger;
|
||||
|
||||
// Commands
|
||||
private CommandProtocol commandProtocol;
|
||||
private CommandPacket commandPacket;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Load configuration
|
||||
logger = getLoggerSafely();
|
||||
|
||||
// Add global parameters
|
||||
DetailedErrorReporter reporter = new DetailedErrorReporter();
|
||||
|
||||
// Load configuration
|
||||
updater = new Updater(this, "protocollib", getFile(), "protocol.info");
|
||||
updater = new Updater(this, logger, "protocollib", getFile(), "protocol.info");
|
||||
|
||||
try {
|
||||
config = new ProtocolConfig(this);
|
||||
@ -99,7 +106,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
|
||||
// Initialize command handlers
|
||||
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) {
|
||||
reporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager);
|
||||
@ -121,6 +131,26 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
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
|
||||
public void onEnable() {
|
||||
try {
|
||||
|
@ -6,8 +6,6 @@ import java.util.NavigableMap;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
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> {
|
||||
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.value = value;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,12 +54,17 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
|
||||
@Override
|
||||
public TValue getValue() {
|
||||
return value;
|
||||
return left.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 upperBound - highest value to remove.
|
||||
*/
|
||||
public void remove(TKey lowerBound, TKey upperBound) {
|
||||
remove(lowerBound, upperBound, false);
|
||||
public Set<Entry> remove(TKey lowerBound, TKey upperBound) {
|
||||
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 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);
|
||||
NavigableMap<TKey, EndPoint> range = bounds.subMap(lowerBound, true, upperBound, true);
|
||||
|
||||
boolean emptyRange = range.isEmpty();
|
||||
TKey first = emptyRange ? range.firstKey() : null;
|
||||
TKey last = emptyRange ? range.lastKey() : null;
|
||||
TKey first = !emptyRange ? range.firstKey() : null;
|
||||
TKey last = !emptyRange ? range.lastKey() : null;
|
||||
|
||||
Set<Entry> resized = 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
|
||||
range.clear();
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Helper
|
||||
@ -154,7 +165,8 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
if (endPoint != null) {
|
||||
endPoint.state = State.BOTH;
|
||||
} else {
|
||||
endPoint = bounds.put(key, new EndPoint(state, value));
|
||||
endPoint = new EndPoint(state, value);
|
||||
bounds.put(key, 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) {
|
||||
// OK. Add the end points now
|
||||
if (value != null) {
|
||||
addEndPoint(lowerBound, value, State.OPEN);
|
||||
addEndPoint(upperBound, value, State.CLOSE);
|
||||
EndPoint left = addEndPoint(lowerBound, value, State.OPEN);
|
||||
EndPoint right = addEndPoint(upperBound, value, State.CLOSE);
|
||||
|
||||
Range<TKey> range = Ranges.closed(lowerBound, upperBound);
|
||||
return new Entry(range, value);
|
||||
return new Entry(range, left, right);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -248,11 +260,12 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
|
||||
for (Map.Entry<TKey, EndPoint> entry : bounds.entrySet()) {
|
||||
switch (entry.getValue().state) {
|
||||
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;
|
||||
case CLOSE:
|
||||
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;
|
||||
case OPEN:
|
||||
// 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) {
|
||||
// Naively copy every range.
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public boolean equals(final Object obj){
|
||||
if(obj instanceof ListeningWhitelist){
|
||||
@ -157,5 +171,4 @@ public class ListeningWhitelist {
|
||||
.add("priority", priority)
|
||||
.add("packets", whitelist).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -212,41 +212,12 @@ public class Updater
|
||||
* @param permission
|
||||
* 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.file = file;
|
||||
this.slug = slug;
|
||||
|
||||
// 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.
|
||||
}
|
||||
});
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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>
|
||||
packet:
|
||||
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-message: You don't have <permission>
|
||||
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren