Added configuration and commands.
The two commands added are as follows: protocol: Reload the configuration, check for updates and download the most recent version. packet: Add or remove a simple packet inspector. Can even display the content of each packet.
Dieser Commit ist enthalten in:
Ursprung
3d0dce7e8d
Commit
7952da99c9
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry including="**/*.java" kind="src" output="target/classes" path="src/main/java">
|
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="optional" value="true"/>
|
<attribute name="optional" value="true"/>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
@ -13,6 +13,11 @@
|
|||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
encoding//src/main/java=cp1252
|
encoding//src/main/java=cp1252
|
||||||
|
encoding//src/main/resources=cp1252
|
||||||
encoding//src/test/java=cp1252
|
encoding//src/test/java=cp1252
|
||||||
encoding/<project>=cp1252
|
encoding/<project>=cp1252
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
<defaultGoal>clean install</defaultGoal>
|
<defaultGoal>clean install</defaultGoal>
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
<resource>
|
||||||
<directory>src/main/java</directory>
|
<directory>src/main/resources</directory>
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>**/*.java</exclude>
|
<exclude>**/*.java</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
|
325
ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java
Normale Datei
325
ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java
Normale Datei
@ -0,0 +1,325 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||||
|
import org.apache.commons.lang.builder.ToStringStyle;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.concurrency.AbstractIntervalTree;
|
||||||
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
|
import com.comphenix.protocol.events.ListenerPriority;
|
||||||
|
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||||
|
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.google.common.collect.DiscreteDomains;
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import com.google.common.collect.Ranges;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the "packet" debug command.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class CommandPacket implements CommandExecutor {
|
||||||
|
private interface DetailedPacketListener extends PacketListener {
|
||||||
|
/**
|
||||||
|
* Determine whether or not the given packet listener is detailed or not.
|
||||||
|
* @return TRUE if it is detailed, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isDetailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum SubCommand {
|
||||||
|
ADD, REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of this command.
|
||||||
|
*/
|
||||||
|
public static final String NAME = "packet";
|
||||||
|
|
||||||
|
private Plugin plugin;
|
||||||
|
private Logger logger;
|
||||||
|
private ProtocolManager manager;
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.logger = logger;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a packet listener interval tree.
|
||||||
|
* @return Construct the tree.
|
||||||
|
*/
|
||||||
|
private AbstractIntervalTree<Integer, DetailedPacketListener> createTree(final ConnectionSide side) {
|
||||||
|
return new AbstractIntervalTree<Integer, DetailedPacketListener>() {
|
||||||
|
@Override
|
||||||
|
protected Integer decrementKey(Integer key) {
|
||||||
|
return key != null ? key - 1 : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer incrementKey(Integer key) {
|
||||||
|
return key != null ? key + 1 : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onEntryAdded(Entry added) {
|
||||||
|
// Ensure that the starting ID and the ending ID is correct
|
||||||
|
// This is necessary because the interval tree may change the range.
|
||||||
|
if (added != null) {
|
||||||
|
Range<Integer> key = added.getKey();
|
||||||
|
DetailedPacketListener listener = added.getValue();
|
||||||
|
DetailedPacketListener corrected = createPacketListener(
|
||||||
|
side, key.lowerEndpoint(), key.upperEndpoint(), listener.isDetailed());
|
||||||
|
|
||||||
|
added.setValue(corrected);
|
||||||
|
|
||||||
|
if (corrected != null) {
|
||||||
|
manager.addPacketListener(corrected);
|
||||||
|
} else {
|
||||||
|
// Never mind
|
||||||
|
remove(key.lowerEndpoint(), key.upperEndpoint());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onEntryRemoved(Entry removed) {
|
||||||
|
// Remove the listener
|
||||||
|
if (removed != null) {
|
||||||
|
DetailedPacketListener listener = removed.getValue();
|
||||||
|
|
||||||
|
if (listener != null) {
|
||||||
|
manager.removePacketListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Description: Adds or removes a simple packet listener.
|
||||||
|
Usage: /<command> add|remove client|server|both [ID start] [ID stop] [detailed]
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
// Make sure we're dealing with the correct command
|
||||||
|
if (!command.getName().equalsIgnoreCase(NAME))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// We need at least one argument
|
||||||
|
if (args != null && args.length > 0) {
|
||||||
|
try {
|
||||||
|
SubCommand subCommand = parseCommand(args, 0);
|
||||||
|
ConnectionSide side = parseSide(args, 1, ConnectionSide.BOTH);
|
||||||
|
|
||||||
|
int idStart = parseInteger(args, 2, 0);
|
||||||
|
int idStop = parseInteger(args, 3, 255);
|
||||||
|
|
||||||
|
// 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.");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Perform command
|
||||||
|
if (subCommand == SubCommand.ADD)
|
||||||
|
addPacketListeners(side, idStart, idStop, detailed);
|
||||||
|
else
|
||||||
|
removePacketListeners(side, idStart, idStop, detailed);
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
sender.sendMessage(ChatColor.DARK_RED + "Cannot parse number: " + e.getMessage());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
sender.sendMessage(ChatColor.DARK_RED + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Integer> getValidPackets(ConnectionSide side) throws FieldAccessException {
|
||||||
|
if (side.isForClient())
|
||||||
|
return Packets.Client.getSupported();
|
||||||
|
else if (side.isForServer())
|
||||||
|
return Packets.Server.getSupported();
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Illegal side: " + side);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DetailedPacketListener createPacketListener(final ConnectionSide side, int idStart, int idStop, final boolean detailed) {
|
||||||
|
|
||||||
|
Set<Integer> range = Ranges.closed(idStart, idStop).asSet(DiscreteDomains.integers());
|
||||||
|
Set<Integer> packets;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Only use supported packet IDs
|
||||||
|
packets = getValidPackets(side);
|
||||||
|
packets.retainAll(range);
|
||||||
|
|
||||||
|
} catch (FieldAccessException e) {
|
||||||
|
// Don't filter anything then
|
||||||
|
packets = range;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore empty sets
|
||||||
|
if (packets.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Create the listener we will be using
|
||||||
|
final ListeningWhitelist whitelist = new ListeningWhitelist(ListenerPriority.MONITOR, packets, GamePhase.BOTH);
|
||||||
|
|
||||||
|
return new DetailedPacketListener() {
|
||||||
|
@Override
|
||||||
|
public void onPacketSending(PacketEvent event) {
|
||||||
|
if (side.isForServer()) {
|
||||||
|
printInformation(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketReceiving(PacketEvent event) {
|
||||||
|
if (side.isForClient()) {
|
||||||
|
printInformation(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printInformation(PacketEvent event) {
|
||||||
|
String verb = side.isForClient() ? "Received" : "Sent";
|
||||||
|
String shortDescription = String.format(
|
||||||
|
"%s packet %s (%s)",
|
||||||
|
verb,
|
||||||
|
event.getPacketID(),
|
||||||
|
Packets.getDeclaredName(event.getPacketID())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Detailed will print the packet's content too
|
||||||
|
if (detailed) {
|
||||||
|
logger.info(shortDescription + ":\n" +
|
||||||
|
ToStringBuilder.reflectionToString(event.getPacket().getHandle(), ToStringStyle.MULTI_LINE_STYLE)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
logger.info(shortDescription + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListeningWhitelist getSendingWhitelist() {
|
||||||
|
return side.isForServer() ? whitelist : ListeningWhitelist.EMPTY_WHITELIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListeningWhitelist getReceivingWhitelist() {
|
||||||
|
return side.isForClient() ? whitelist : ListeningWhitelist.EMPTY_WHITELIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Plugin getPlugin() {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDetailed() {
|
||||||
|
return detailed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void 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)
|
||||||
|
getListenerTree(side).put(idStart, idStop, listener);
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("No packets found in the range " + idStart + " - " + idStop + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePacketListeners(ConnectionSide side, int idStart, int idStop, boolean detailed) {
|
||||||
|
// The interval tree will automatically remove the listeners for us
|
||||||
|
getListenerTree(side).remove(idStart, idStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractIntervalTree<Integer, DetailedPacketListener> getListenerTree(ConnectionSide side) {
|
||||||
|
if (side.isForClient())
|
||||||
|
return clientListeners;
|
||||||
|
else if (side.isForServer())
|
||||||
|
return serverListeners;
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Not a legal connection side.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private SubCommand parseCommand(String[] args, int index) {
|
||||||
|
String text = args[index].toLowerCase();
|
||||||
|
|
||||||
|
// Parse this too
|
||||||
|
if ("add".startsWith(text))
|
||||||
|
return SubCommand.ADD;
|
||||||
|
else if ("remove".startsWith(text))
|
||||||
|
return SubCommand.REMOVE;
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException(text + " is not a valid sub command. Must be add or remove.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConnectionSide parseSide(String[] args, int index, ConnectionSide defaultValue) {
|
||||||
|
if (index < args.length) {
|
||||||
|
String text = args[index].toLowerCase();
|
||||||
|
|
||||||
|
// Parse the side gracefully
|
||||||
|
if ("both".startsWith(text))
|
||||||
|
return ConnectionSide.BOTH;
|
||||||
|
else if ("client".startsWith(text))
|
||||||
|
return ConnectionSide.CLIENT_SIDE;
|
||||||
|
else if ("server".startsWith(text))
|
||||||
|
return ConnectionSide.SERVER_SIDE;
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException(text + " is not a connection side.");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a boolean
|
||||||
|
private boolean parseBoolean(String[] args, int index, boolean defaultValue) {
|
||||||
|
if (index < args.length) {
|
||||||
|
return Boolean.parseBoolean(args[index]);
|
||||||
|
} else {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.metrics.Updater;
|
||||||
|
import com.comphenix.protocol.metrics.Updater.UpdateResult;
|
||||||
|
import com.comphenix.protocol.metrics.Updater.UpdateType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the "protocol" administration command.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class CommandProtocol implements CommandExecutor {
|
||||||
|
/**
|
||||||
|
* Name of this command.
|
||||||
|
*/
|
||||||
|
public static final String NAME = "protocol";
|
||||||
|
|
||||||
|
private Plugin plugin;
|
||||||
|
private Updater updater;
|
||||||
|
|
||||||
|
public CommandProtocol(Plugin plugin, Updater updater) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.updater = updater;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
// Make sure we're dealing with the correct command
|
||||||
|
if (!command.getName().equalsIgnoreCase(NAME))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// We need one argument (the sub-command)
|
||||||
|
if (args != null && args.length == 1) {
|
||||||
|
String subCommand = args[0];
|
||||||
|
|
||||||
|
// Only return TRUE if we executed the correct command
|
||||||
|
if (subCommand.equalsIgnoreCase("config"))
|
||||||
|
reloadConfiguration(sender);
|
||||||
|
else if (subCommand.equalsIgnoreCase("check"))
|
||||||
|
checkVersion(sender);
|
||||||
|
else if (subCommand.equalsIgnoreCase("update"))
|
||||||
|
updateVersion(sender);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkVersion(final CommandSender sender) {
|
||||||
|
// Perform on an async thread
|
||||||
|
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
UpdateResult result = updater.update(UpdateType.NO_DOWNLOAD, true);
|
||||||
|
sender.sendMessage(ChatColor.DARK_BLUE + "Version check: " + result.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateVersion(final CommandSender sender) {
|
||||||
|
// Perform on an async thread
|
||||||
|
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
UpdateResult result = updater.update(UpdateType.DEFAULT, true);
|
||||||
|
sender.sendMessage(ChatColor.DARK_BLUE + "Update: " + result.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadConfiguration(CommandSender sender) {
|
||||||
|
plugin.saveConfig();
|
||||||
|
plugin.reloadConfig();
|
||||||
|
sender.sendMessage(ChatColor.DARK_BLUE + "Reloaded configuration!");
|
||||||
|
}
|
||||||
|
}
|
135
ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolConfig.java
Normale Datei
135
ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolConfig.java
Normale Datei
@ -0,0 +1,135 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.Configuration;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the configuration of ProtocolLib.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class ProtocolConfig {
|
||||||
|
|
||||||
|
private static final String SECTION_GLOBAL = "global";
|
||||||
|
private static final String SECTION_AUTOUPDATER = "auto updater";
|
||||||
|
|
||||||
|
private static final String UPDATER_NOTIFY = "notify";
|
||||||
|
private static final String UPDATER_DOWNLAD = "download";
|
||||||
|
private static final String UPDATER_DELAY = "delay";
|
||||||
|
private static final String UPDATER_LAST_TIME = "last";
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
private static final long DEFAULT_UPDATER_DELAY = 60;
|
||||||
|
|
||||||
|
private Plugin plugin;
|
||||||
|
private Configuration config;
|
||||||
|
|
||||||
|
private ConfigurationSection global;
|
||||||
|
private ConfigurationSection updater;
|
||||||
|
|
||||||
|
public ProtocolConfig(Plugin plugin) {
|
||||||
|
this(plugin, plugin.getConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProtocolConfig(Plugin plugin, Configuration config) {
|
||||||
|
this.config = config;
|
||||||
|
loadSections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load data sections.
|
||||||
|
* @param copyDefaults - whether or not to copy configuration defaults.
|
||||||
|
*/
|
||||||
|
private void loadSections(boolean copyDefaults) {
|
||||||
|
if (config != null) {
|
||||||
|
global = config.getConfigurationSection(SECTION_GLOBAL);
|
||||||
|
}
|
||||||
|
if (global != null) {
|
||||||
|
updater = global.getConfigurationSection(SECTION_AUTOUPDATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically copy defaults
|
||||||
|
if (copyDefaults && (global == null || updater == null)) {
|
||||||
|
config.options().copyDefaults(true);
|
||||||
|
loadSections(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve whether or not ProtocolLib should determine if a new version has been released.
|
||||||
|
* @return TRUE if it should do this automatically, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isAutoNotify() {
|
||||||
|
return updater.getBoolean(UPDATER_NOTIFY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether or not ProtocolLib should determine if a new version has been released.
|
||||||
|
* @param value - TRUE to do this automatically, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public void setAutoNotify(boolean value) {
|
||||||
|
updater.set(UPDATER_NOTIFY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve whether or not ProtocolLib should automatically download the new version.
|
||||||
|
* @return TRUE if it should, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isAutoDownload() {
|
||||||
|
return updater != null && updater.getBoolean(UPDATER_DOWNLAD, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether or not ProtocolLib should automatically download the new version.
|
||||||
|
* @param value - TRUE if it should. FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public void setAutoDownload(boolean value) {
|
||||||
|
updater.set(UPDATER_DOWNLAD, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the amount of time to wait until checking for a new update.
|
||||||
|
* @return The amount of time to wait.
|
||||||
|
*/
|
||||||
|
public long getAutoDelay() {
|
||||||
|
// Note that the delay must be greater than 59 seconds
|
||||||
|
return Math.max(updater.getInt(UPDATER_DELAY, 0), DEFAULT_UPDATER_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the amount of time to wait until checking for a new update.
|
||||||
|
* <p>
|
||||||
|
* This time must be greater than 59 seconds.
|
||||||
|
* @param delaySeconds - the amount of time to wait.
|
||||||
|
*/
|
||||||
|
public void setAutoDelay(long delaySeconds) {
|
||||||
|
// Silently fix the delay
|
||||||
|
if (delaySeconds < DEFAULT_UPDATER_DELAY)
|
||||||
|
delaySeconds = DEFAULT_UPDATER_DELAY;
|
||||||
|
updater.set(UPDATER_DELAY, delaySeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the last time we updated, in seconds since 1970.01.01 00:00.
|
||||||
|
* @return Last update time.
|
||||||
|
*/
|
||||||
|
public long getAutoLastTime() {
|
||||||
|
return updater.getLong(UPDATER_LAST_TIME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the last time we updated, in seconds since 1970.01.01 00:00.
|
||||||
|
* @param lastTimeSeconds - new last update time.
|
||||||
|
*/
|
||||||
|
public void setAutoLastTime(long lastTimeSeconds) {
|
||||||
|
updater.set(UPDATER_LAST_TIME, lastTimeSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the current configuration file.
|
||||||
|
*/
|
||||||
|
public void saveAll() {
|
||||||
|
plugin.saveConfig();
|
||||||
|
}
|
||||||
|
}
|
@ -27,12 +27,10 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||||
import com.comphenix.protocol.error.DetailedErrorReporter;
|
import com.comphenix.protocol.error.DetailedErrorReporter;
|
||||||
import com.comphenix.protocol.error.ErrorReporter;
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
import com.comphenix.protocol.events.ConnectionSide;
|
|
||||||
import com.comphenix.protocol.events.MonitorAdapter;
|
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
|
||||||
import com.comphenix.protocol.injector.DelayedSingleTask;
|
import com.comphenix.protocol.injector.DelayedSingleTask;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||||
import com.comphenix.protocol.metrics.Statistics;
|
import com.comphenix.protocol.metrics.Statistics;
|
||||||
|
import com.comphenix.protocol.metrics.Updater;
|
||||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,12 +40,11 @@ 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;
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
||||||
// Information logger
|
|
||||||
private Logger logger;
|
|
||||||
|
|
||||||
// Error reporter
|
// Error reporter
|
||||||
private ErrorReporter reporter;
|
private ErrorReporter reporter;
|
||||||
|
|
||||||
@ -66,37 +63,67 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
// Used to unhook players after a delay
|
// Used to unhook players after a delay
|
||||||
private DelayedSingleTask unhookTask;
|
private DelayedSingleTask unhookTask;
|
||||||
|
|
||||||
// Used for debugging
|
// Settings/options
|
||||||
private boolean debugListener;
|
private ProtocolConfig config;
|
||||||
|
|
||||||
|
// Updater
|
||||||
|
private Updater updater;
|
||||||
|
|
||||||
|
// Commands
|
||||||
|
private CommandProtocol commandProtocol;
|
||||||
|
private CommandPacket commandPacket;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
// Add global parameters
|
// Add global parameters
|
||||||
DetailedErrorReporter reporter = new DetailedErrorReporter();
|
DetailedErrorReporter reporter = new DetailedErrorReporter();
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
updater = new Updater(this, "protocollib", getFile(), "protocol.info");
|
||||||
|
config = new ProtocolConfig(this);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger = getLoggerSafely();
|
|
||||||
unhookTask = new DelayedSingleTask(this);
|
unhookTask = new DelayedSingleTask(this);
|
||||||
protocolManager = new PacketFilterManager(getClassLoader(), getServer(), unhookTask, reporter);
|
protocolManager = new PacketFilterManager(getClassLoader(), getServer(), unhookTask, reporter);
|
||||||
reporter.addGlobalParameter("manager", protocolManager);
|
reporter.addGlobalParameter("manager", protocolManager);
|
||||||
|
|
||||||
|
// Initialize command handlers
|
||||||
|
commandProtocol = new CommandProtocol(this, updater);
|
||||||
|
commandPacket = new CommandPacket(this, getLoggerSafely(), protocolManager);
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
reporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager);
|
reporter.reportDetailed(this, "Cannot load ProtocolLib.", e, protocolManager);
|
||||||
|
disablePlugin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reloadConfig() {
|
||||||
|
super.reloadConfig();
|
||||||
|
// Reload configuration
|
||||||
|
config = new ProtocolConfig(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
try {
|
try {
|
||||||
Server server = getServer();
|
Server server = getServer();
|
||||||
PluginManager manager = server.getPluginManager();
|
PluginManager manager = server.getPluginManager();
|
||||||
|
|
||||||
|
// Don't do anything else!
|
||||||
|
if (manager == null)
|
||||||
|
return;
|
||||||
|
|
||||||
// Initialize background compiler
|
// Initialize background compiler
|
||||||
if (backgroundCompiler == null) {
|
if (backgroundCompiler == null) {
|
||||||
backgroundCompiler = new BackgroundCompiler(getClassLoader());
|
backgroundCompiler = new BackgroundCompiler(getClassLoader());
|
||||||
BackgroundCompiler.setInstance(backgroundCompiler);
|
BackgroundCompiler.setInstance(backgroundCompiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up command handlers
|
||||||
|
getCommand(CommandProtocol.NAME).setExecutor(commandProtocol);
|
||||||
|
getCommand(CommandPacket.NAME).setExecutor(commandPacket);
|
||||||
|
|
||||||
// Notify server managers of incompatible plugins
|
// Notify server managers of incompatible plugins
|
||||||
checkForIncompatibility(manager);
|
checkForIncompatibility(manager);
|
||||||
|
|
||||||
@ -104,8 +131,8 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
protocolManager.registerEvents(manager, this);
|
protocolManager.registerEvents(manager, this);
|
||||||
|
|
||||||
// Worker that ensures that async packets are eventually sent
|
// Worker that ensures that async packets are eventually sent
|
||||||
|
// It also performs the update check.
|
||||||
createAsyncTask(server);
|
createAsyncTask(server);
|
||||||
//toggleDebugListener();
|
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
reporter.reportDetailed(this, "Cannot enable ProtocolLib.", e);
|
reporter.reportDetailed(this, "Cannot enable ProtocolLib.", e);
|
||||||
@ -123,39 +150,6 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle a listener that prints every sent and received packet.
|
|
||||||
*/
|
|
||||||
void toggleDebugListener() {
|
|
||||||
|
|
||||||
if (debugListener) {
|
|
||||||
protocolManager.removePacketListeners(this);
|
|
||||||
} else {
|
|
||||||
// DEBUG DEBUG
|
|
||||||
protocolManager.addPacketListener(new MonitorAdapter(this, ConnectionSide.BOTH, logger) {
|
|
||||||
@Override
|
|
||||||
public void onPacketReceiving(PacketEvent event) {
|
|
||||||
Object handle = event.getPacket().getHandle();
|
|
||||||
|
|
||||||
logger.info(String.format(
|
|
||||||
"RECEIVING %s@%s from %s.",
|
|
||||||
handle.getClass().getSimpleName(), handle.hashCode(), event.getPlayer().getName()
|
|
||||||
));
|
|
||||||
};
|
|
||||||
@Override
|
|
||||||
public void onPacketSending(PacketEvent event) {
|
|
||||||
Object handle = event.getPacket().getHandle();
|
|
||||||
|
|
||||||
logger.info(String.format(
|
|
||||||
"SENDING %s@%s from %s.",
|
|
||||||
handle.getClass().getSimpleName(), handle.hashCode(), event.getPlayer().getName()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
debugListener = !debugListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the current plugin.
|
* Disable the current plugin.
|
||||||
*/
|
*/
|
||||||
@ -176,6 +170,9 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
|
|
||||||
// We KNOW we're on the main thread at the moment
|
// We KNOW we're on the main thread at the moment
|
||||||
manager.sendProcessedPackets(tickCounter++, true);
|
manager.sendProcessedPackets(tickCounter++, true);
|
||||||
|
|
||||||
|
// Check for updates too
|
||||||
|
checkUpdates();
|
||||||
}
|
}
|
||||||
}, ASYNC_PACKET_DELAY, ASYNC_PACKET_DELAY);
|
}, ASYNC_PACKET_DELAY, ASYNC_PACKET_DELAY);
|
||||||
|
|
||||||
@ -186,6 +183,24 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkUpdates() {
|
||||||
|
// Ignore milliseconds - it's pointless
|
||||||
|
long currentTime = System.currentTimeMillis() / MILLI_PER_SECOND;
|
||||||
|
|
||||||
|
// Should we update?
|
||||||
|
if (currentTime < config.getAutoLastTime() + config.getAutoDelay()) {
|
||||||
|
// Great. Save this check.
|
||||||
|
config.setAutoLastTime(currentTime);
|
||||||
|
config.saveAll();
|
||||||
|
|
||||||
|
// Initiate the update from the console
|
||||||
|
if (config.isAutoDownload())
|
||||||
|
commandProtocol.updateVersion(getServer().getConsoleSender());
|
||||||
|
else if (config.isAutoNotify())
|
||||||
|
commandProtocol.checkVersion(getServer().getConsoleSender());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void checkForIncompatibility(PluginManager manager) {
|
private void checkForIncompatibility(PluginManager manager) {
|
||||||
// Plugin authors: Notify me to remove these
|
// Plugin authors: Notify me to remove these
|
||||||
String[] incompatiblePlugins = {};
|
String[] incompatiblePlugins = {};
|
||||||
@ -223,6 +238,20 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
cleanup.resetAll();
|
cleanup.resetAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the Bukkit logger first, before we try to create our own
|
||||||
|
private Logger getLoggerSafely() {
|
||||||
|
Logger log = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log = getLogger();
|
||||||
|
} catch (Throwable e) { }
|
||||||
|
|
||||||
|
// Use the default logger instead
|
||||||
|
if (log == null)
|
||||||
|
log = Logger.getLogger("Minecraft");
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the packet protocol manager.
|
* Retrieves the packet protocol manager.
|
||||||
* @return Packet protocol manager, or NULL if it has been disabled.
|
* @return Packet protocol manager, or NULL if it has been disabled.
|
||||||
@ -240,20 +269,4 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
public Statistics getStatistics() {
|
public Statistics getStatistics() {
|
||||||
return statistisc;
|
return statistisc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the Bukkit logger first, before we try to create our own
|
|
||||||
private Logger getLoggerSafely() {
|
|
||||||
|
|
||||||
Logger log = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
log = getLogger();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// We'll handle it
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log == null)
|
|
||||||
log = Logger.getLogger("Minecraft");
|
|
||||||
return log;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,362 @@
|
|||||||
|
package com.comphenix.protocol.concurrency;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a generic store of intervals and associated values. No two intervals
|
||||||
|
* can overlap in this representation.
|
||||||
|
* <p>
|
||||||
|
* Note that this implementation is not thread safe.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*
|
||||||
|
* @param <TKey> - type of the key. Must implement Comparable.
|
||||||
|
* @param <TValue> - type of the value to associate.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue> {
|
||||||
|
|
||||||
|
protected enum State {
|
||||||
|
OPEN,
|
||||||
|
CLOSE,
|
||||||
|
BOTH
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a range and a value in this interval tree.
|
||||||
|
*/
|
||||||
|
public class Entry implements Map.Entry<Range<TKey>, TValue> {
|
||||||
|
private final Range<TKey> key;
|
||||||
|
private final TValue value;
|
||||||
|
|
||||||
|
public Entry(Range<TKey> key, TValue value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Range<TKey> getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TValue getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TValue setValue(TValue value) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single end point (open, close or both) of a range.
|
||||||
|
*/
|
||||||
|
protected class EndPoint {
|
||||||
|
|
||||||
|
// Whether or not the end-point is opening a range, closing a range or both.
|
||||||
|
public State state;
|
||||||
|
|
||||||
|
// The value this range contains
|
||||||
|
public TValue value;
|
||||||
|
|
||||||
|
public EndPoint(State state, TValue value) {
|
||||||
|
this.state = state;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To quickly look up ranges we'll index them by endpoints
|
||||||
|
protected NavigableMap<TKey, EndPoint> bounds = new TreeMap<TKey, EndPoint>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes every interval that intersects with the given range.
|
||||||
|
* @param lowerBound - lowest value to remove.
|
||||||
|
* @param upperBound - highest value to remove.
|
||||||
|
*/
|
||||||
|
public void remove(TKey lowerBound, TKey upperBound) {
|
||||||
|
remove(lowerBound, upperBound, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes every interval that intersects with the given range.
|
||||||
|
* @param lowerBound - lowest value to remove.
|
||||||
|
* @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) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
Set<Entry> resized = new HashSet<Entry>();
|
||||||
|
Set<Entry> removed = new HashSet<Entry>();
|
||||||
|
|
||||||
|
// Remove the previous element too. A close end-point must be preceded by an OPEN end-point.
|
||||||
|
if (first != null && range.get(first).state == State.CLOSE) {
|
||||||
|
TKey key = bounds.floorKey(first);
|
||||||
|
EndPoint removedPoint = removeIfNonNull(key);
|
||||||
|
|
||||||
|
// Add the interval back
|
||||||
|
if (removedPoint != null && preserveDifference) {
|
||||||
|
resized.add(putUnsafe(key, decrementKey(lowerBound), removedPoint.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the closing element too.
|
||||||
|
if (last != null && range.get(last).state == State.OPEN) {
|
||||||
|
TKey key = bounds.ceilingKey(last);
|
||||||
|
EndPoint removedPoint = removeIfNonNull(key);
|
||||||
|
|
||||||
|
if (removedPoint != null && preserveDifference) {
|
||||||
|
resized.add(putUnsafe(incrementKey(upperBound), key, removedPoint.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the removed entries too
|
||||||
|
getEntries(removed, range);
|
||||||
|
invokeEntryRemoved(removed);
|
||||||
|
|
||||||
|
if (preserveDifference) {
|
||||||
|
invokeEntryRemoved(resized);
|
||||||
|
invokeEntryAdded(resized);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the range as well
|
||||||
|
range.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper
|
||||||
|
private EndPoint removeIfNonNull(TKey key) {
|
||||||
|
if (key != null) {
|
||||||
|
return bounds.remove(key);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a given end point
|
||||||
|
protected EndPoint addEndPoint(TKey key, TValue value, State state) {
|
||||||
|
EndPoint endPoint = bounds.get(key);
|
||||||
|
|
||||||
|
if (endPoint != null) {
|
||||||
|
endPoint.state = State.BOTH;
|
||||||
|
} else {
|
||||||
|
endPoint = bounds.put(key, new EndPoint(state, value));
|
||||||
|
}
|
||||||
|
return endPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates a given interval of keys with a certain value. Any previous
|
||||||
|
* association will be overwritten in the given interval.
|
||||||
|
* <p>
|
||||||
|
* Overlapping intervals are not permitted. A key can only be associated with a single value.
|
||||||
|
*
|
||||||
|
* @param lowerBound - the minimum key (inclusive).
|
||||||
|
* @param upperBound - the maximum key (inclusive).
|
||||||
|
* @param value - the value, or NULL to reset this range.
|
||||||
|
*/
|
||||||
|
public void put(TKey lowerBound, TKey upperBound, TValue value) {
|
||||||
|
// While we don't permit overlapping intervals, we'll still allow overwriting existing intervals.
|
||||||
|
remove(lowerBound, upperBound, true);
|
||||||
|
invokeEntryAdded(putUnsafe(lowerBound, upperBound, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates a given interval without performing any interval checks.
|
||||||
|
* @param lowerBound - the minimum key (inclusive).
|
||||||
|
* @param upperBound - the maximum key (inclusive).
|
||||||
|
* @param value - the value, or NULL to reset the range.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
Range<TKey> range = Ranges.closed(lowerBound, upperBound);
|
||||||
|
return new Entry(range, value);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to verify the validity of the given interval.
|
||||||
|
* @param lowerBound - lower bound (inclusive).
|
||||||
|
* @param upperBound - upper bound (inclusive).
|
||||||
|
*/
|
||||||
|
private void checkBounds(TKey lowerBound, TKey upperBound) {
|
||||||
|
if (lowerBound == null)
|
||||||
|
throw new IllegalAccessError("lowerbound cannot be NULL.");
|
||||||
|
if (upperBound == null)
|
||||||
|
throw new IllegalAccessError("upperBound cannot be NULL.");
|
||||||
|
if (upperBound.compareTo(lowerBound) < 0)
|
||||||
|
throw new IllegalArgumentException("upperBound cannot be less than lowerBound.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the given key is within an interval.
|
||||||
|
* @param key - key to check.
|
||||||
|
* @return TRUE if the given key is within an interval in this tree, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean containsKey(TKey key) {
|
||||||
|
return getEndPoint(key) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerates over every range in this interval tree.
|
||||||
|
* @return Number of ranges.
|
||||||
|
*/
|
||||||
|
public Set<Entry> entrySet() {
|
||||||
|
// Don't mind the Java noise
|
||||||
|
Set<Entry> result = new HashSet<Entry>();
|
||||||
|
getEntries(result, bounds);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove every interval.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
if (!bounds.isEmpty()) {
|
||||||
|
remove(bounds.firstKey(), bounds.lastKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a map of end points into a set of entries.
|
||||||
|
* @param destination - set of entries.
|
||||||
|
* @param map - a map of end points.
|
||||||
|
*/
|
||||||
|
private void getEntries(Set<Entry> destination, NavigableMap<TKey, EndPoint> map) {
|
||||||
|
Map.Entry<TKey, EndPoint> last = null;
|
||||||
|
|
||||||
|
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));
|
||||||
|
break;
|
||||||
|
case CLOSE:
|
||||||
|
Range<TKey> range = Ranges.closed(last.getKey(), entry.getKey());
|
||||||
|
destination.add(new Entry(range, entry.getValue().value));
|
||||||
|
break;
|
||||||
|
case OPEN:
|
||||||
|
// We don't know the full range yet
|
||||||
|
last = entry;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Illegal open/close state detected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts every range from the given tree into the current tree.
|
||||||
|
* @param other - the other tree to read from.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value of the range that matches the given key, or NULL if nothing was found.
|
||||||
|
* @param key - the level to read for.
|
||||||
|
* @return The correct amount of experience, or NULL if nothing was recorded.
|
||||||
|
*/
|
||||||
|
public TValue get(TKey key) {
|
||||||
|
EndPoint point = getEndPoint(key);
|
||||||
|
|
||||||
|
if (point != null)
|
||||||
|
return point.value;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the end-point composite associated with this key.
|
||||||
|
* @param key - key to search for.
|
||||||
|
* @return The end point found, or NULL.
|
||||||
|
*/
|
||||||
|
protected EndPoint getEndPoint(TKey key) {
|
||||||
|
EndPoint ends = bounds.get(key);
|
||||||
|
|
||||||
|
if (ends != null) {
|
||||||
|
// This is a piece of cake
|
||||||
|
return ends;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// We need to determine if the point intersects with a range
|
||||||
|
TKey left = bounds.floorKey(key);
|
||||||
|
|
||||||
|
// We only need to check to the left
|
||||||
|
if (left != null && bounds.get(left).state == State.OPEN) {
|
||||||
|
return bounds.get(left);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invokeEntryAdded(Entry added) {
|
||||||
|
if (added != null) {
|
||||||
|
onEntryAdded(added);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invokeEntryAdded(Set<Entry> added) {
|
||||||
|
for (Entry entry : added) {
|
||||||
|
onEntryAdded(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invokeEntryRemoved(Set<Entry> removed) {
|
||||||
|
for (Entry entry : removed) {
|
||||||
|
onEntryRemoved(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listeners for added or removed entries
|
||||||
|
/**
|
||||||
|
* Invoked when an entry is added.
|
||||||
|
* @param added - the entry that was added.
|
||||||
|
*/
|
||||||
|
protected void onEntryAdded(Entry added) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when an entry is removed.
|
||||||
|
* @param removed - the removed entry.
|
||||||
|
*/
|
||||||
|
protected void onEntryRemoved(Entry removed) { }
|
||||||
|
|
||||||
|
// Helpers for decrementing or incrementing key values
|
||||||
|
/**
|
||||||
|
* Decrement the given key by one unit.
|
||||||
|
* @param key - the key that should be decremented.
|
||||||
|
* @return The new decremented key.
|
||||||
|
*/
|
||||||
|
protected abstract TKey decrementKey(TKey key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the given key by one unit.
|
||||||
|
* @param key - the key that should be incremented.
|
||||||
|
* @return The new incremented key.
|
||||||
|
*/
|
||||||
|
protected abstract TKey incrementKey(TKey key);
|
||||||
|
}
|
@ -28,6 +28,9 @@ public class DetailedErrorReporter implements ErrorReporter {
|
|||||||
public static final String DEFAULT_SUPPORT_URL = "http://dev.bukkit.org/server-mods/protocollib/";
|
public static final String DEFAULT_SUPPORT_URL = "http://dev.bukkit.org/server-mods/protocollib/";
|
||||||
public static final String PLUGIN_NAME = "ProtocolLib";
|
public static final String PLUGIN_NAME = "ProtocolLib";
|
||||||
|
|
||||||
|
// Users that are informed about errors in the chat
|
||||||
|
public static final String ERROR_PERMISSION = "protocol.info";
|
||||||
|
|
||||||
// We don't want to spam the server
|
// We don't want to spam the server
|
||||||
public static final int DEFAULT_MAX_ERROR_COUNT = 20;
|
public static final int DEFAULT_MAX_ERROR_COUNT = 20;
|
||||||
|
|
||||||
@ -158,6 +161,14 @@ public class DetailedErrorReporter implements ErrorReporter {
|
|||||||
if (Bukkit.getServer() != null) {
|
if (Bukkit.getServer() != null) {
|
||||||
writer.println("Server:");
|
writer.println("Server:");
|
||||||
writer.println(addPrefix(Bukkit.getServer().getVersion(), SECOND_LEVEL_PREFIX));
|
writer.println(addPrefix(Bukkit.getServer().getVersion(), SECOND_LEVEL_PREFIX));
|
||||||
|
|
||||||
|
// Inform of this occurrence
|
||||||
|
if (ERROR_PERMISSION != null) {
|
||||||
|
Bukkit.getServer().broadcast(
|
||||||
|
String.format("Error %s (%s) occured in %s.", message, error, sender),
|
||||||
|
ERROR_PERMISSION
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure it is reported
|
// Make sure it is reported
|
||||||
|
@ -50,26 +50,31 @@ public class Updater
|
|||||||
private static final String DBOUrl = "http://dev.bukkit.org/server-mods/";
|
private static final String DBOUrl = "http://dev.bukkit.org/server-mods/";
|
||||||
private static final int BYTE_SIZE = 1024; // Used for downloading files
|
private static final int BYTE_SIZE = 1024; // Used for downloading files
|
||||||
|
|
||||||
private Plugin plugin;
|
private final Plugin plugin;
|
||||||
private UpdateType type;
|
private final String slug;
|
||||||
private String downloadedVersion;
|
|
||||||
private String versionTitle;
|
|
||||||
private String versionLink;
|
|
||||||
private long totalSize; // Holds the total size of the file
|
|
||||||
private int sizeLine; // Used for detecting file size
|
|
||||||
private int multiplier; // Used for determining when to broadcast download updates
|
|
||||||
private boolean announce; // Whether to announce file downloads
|
|
||||||
private URL url; // Connecting to RSS
|
|
||||||
|
|
||||||
private String updateFolder = YamlConfiguration.loadConfiguration(new File("bukkit.yml")).getString("settings.update-folder"); // The folder that downloads will be placed in
|
private volatile long totalSize; // Holds the total size of the file
|
||||||
|
private volatile int sizeLine; // Used for detecting file size
|
||||||
|
private volatile int multiplier; // Used for determining when to broadcast download updates
|
||||||
|
|
||||||
|
private volatile URL url; // Connecting to RSS
|
||||||
|
|
||||||
|
private volatile String updateFolder = YamlConfiguration.loadConfiguration(new File("bukkit.yml")).getString("settings.update-folder"); // The folder that downloads will be placed in
|
||||||
|
|
||||||
// Used for determining the outcome of the update process
|
// Used for determining the outcome of the update process
|
||||||
private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS;
|
private volatile Updater.UpdateResult result = Updater.UpdateResult.SUCCESS;
|
||||||
private String slug;
|
|
||||||
private File file;
|
// Whether to announce file downloads
|
||||||
|
private volatile boolean announce;
|
||||||
|
|
||||||
|
private volatile UpdateType type;
|
||||||
|
private volatile String downloadedVersion;
|
||||||
|
private volatile String versionTitle;
|
||||||
|
private volatile String versionLink;
|
||||||
|
private volatile File file;
|
||||||
|
|
||||||
// Used to announce progress
|
// Used to announce progress
|
||||||
private Logger logger;
|
private volatile Logger logger;
|
||||||
|
|
||||||
// Strings for reading RSS
|
// Strings for reading RSS
|
||||||
private static final String TITLE = "title";
|
private static final String TITLE = "title";
|
||||||
@ -84,38 +89,46 @@ public class Updater
|
|||||||
/**
|
/**
|
||||||
* The updater found an update, and has readied it to be loaded the next time the server restarts/reloads.
|
* The updater found an update, and has readied it to be loaded the next time the server restarts/reloads.
|
||||||
*/
|
*/
|
||||||
SUCCESS(1),
|
SUCCESS(1, "The updater found an update, and has readied it to be loaded the next time the server restarts/reloads."),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The updater did not find an update, and nothing was downloaded.
|
* The updater did not find an update, and nothing was downloaded.
|
||||||
*/
|
*/
|
||||||
NO_UPDATE(2),
|
NO_UPDATE(2, "The updater did not find an update, and nothing was downloaded."),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The updater found an update, but was unable to download it.
|
* The updater found an update, but was unable to download it.
|
||||||
*/
|
*/
|
||||||
FAIL_DOWNLOAD(3),
|
FAIL_DOWNLOAD(3, "The updater found an update, but was unable to download it."),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For some reason, the updater was unable to contact dev.bukkit.org to download the file.
|
* For some reason, the updater was unable to contact dev.bukkit.org to download the file.
|
||||||
*/
|
*/
|
||||||
FAIL_DBO(4),
|
FAIL_DBO(4, "For some reason, the updater was unable to contact dev.bukkit.org to download the file."),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When running the version check, the file on DBO did not contain the a version in the format 'vVersion' such as 'v1.0'.
|
* When running the version check, the file on DBO did not contain the a version in the format 'vVersion' such as 'v1.0'.
|
||||||
*/
|
*/
|
||||||
FAIL_NOVERSION(5),
|
FAIL_NOVERSION(5, "When running the version check, the file on DBO did not contain the a version in the format 'vVersion' such as 'v1.0'."),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The slug provided by the plugin running the updater was invalid and doesn't exist on DBO.
|
* The slug provided by the plugin running the updater was invalid and doesn't exist on DBO.
|
||||||
*/
|
*/
|
||||||
FAIL_BADSLUG(6),
|
FAIL_BADSLUG(6, "The slug provided by the plugin running the updater was invalid and doesn't exist on DBO."),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded.
|
* The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded.
|
||||||
*/
|
*/
|
||||||
UPDATE_AVAILABLE(7);
|
UPDATE_AVAILABLE(7, "The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded.");
|
||||||
|
|
||||||
private static final Map<Integer, Updater.UpdateResult> valueList = new HashMap<Integer, Updater.UpdateResult>();
|
private static final Map<Integer, Updater.UpdateResult> valueList = new HashMap<Integer, Updater.UpdateResult>();
|
||||||
private final int value;
|
private final int value;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
private UpdateResult(int value)
|
private UpdateResult(int value, String description)
|
||||||
{
|
{
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getValue()
|
public int getValue()
|
||||||
@ -128,6 +141,11 @@ public class Updater
|
|||||||
return valueList.get(value);
|
return valueList.get(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
for(Updater.UpdateResult result : Updater.UpdateResult.values())
|
for(Updater.UpdateResult result : Updater.UpdateResult.values())
|
||||||
@ -240,7 +258,7 @@ public class Updater
|
|||||||
* True if the program should announce the progress of new updates in console
|
* True if the program should announce the progress of new updates in console
|
||||||
* @return The result of the update process.
|
* @return The result of the update process.
|
||||||
*/
|
*/
|
||||||
public UpdateResult update(UpdateType type, boolean announce)
|
public synchronized UpdateResult update(UpdateType type, boolean announce)
|
||||||
{
|
{
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.announce = announce;
|
this.announce = announce;
|
||||||
|
10
ProtocolLib/src/main/resources/config.yml
Normale Datei
10
ProtocolLib/src/main/resources/config.yml
Normale Datei
@ -0,0 +1,10 @@
|
|||||||
|
global:
|
||||||
|
# Settings for the automatic version updater
|
||||||
|
auto updater:
|
||||||
|
notify: true
|
||||||
|
download: true
|
||||||
|
|
||||||
|
# Number of seconds to wait until a new update is downloaded
|
||||||
|
delay = 43200 # 12 hours
|
||||||
|
# Last update time
|
||||||
|
last = 0
|
@ -10,12 +10,12 @@ database: false
|
|||||||
commands:
|
commands:
|
||||||
protocol:
|
protocol:
|
||||||
description: Performs administrative tasks regarding ProtocolLib.
|
description: Performs administrative tasks regarding ProtocolLib.
|
||||||
usage: /<command> [reload|update]
|
usage: /<command> config|check|update
|
||||||
permission: experiencemod.admin
|
permission: experiencemod.admin
|
||||||
permission-message: You don't have <permission>
|
permission-message: You don't have <permission>
|
||||||
packet:
|
packet:
|
||||||
description: Adds or removes a simple packet listener.
|
description: Adds or removes a simple packet listener.
|
||||||
usage: /<command> [add|remove|clear] [ID start] [ID stop]
|
usage: /<command> add|remove client|server|both [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>
|
||||||
|
|
||||||
@ -29,5 +29,5 @@ permissions:
|
|||||||
description: Able to initiate the update process, and can configure debug mode.
|
description: Able to initiate the update process, and can configure debug mode.
|
||||||
default: op
|
default: op
|
||||||
protocol.info:
|
protocol.info:
|
||||||
description: Can read update notifications, debug messages and error reports.
|
description: Can read update notifications and error reports.
|
||||||
default: op
|
default: op
|
In neuem Issue referenzieren
Einen Benutzer sperren