Reimplement auto updater, fix legacy packets
Also a few minor bug fixes and improvements Fixes #127
Dieser Commit ist enthalten in:
Ursprung
e84fca699d
Commit
395d77e721
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
@ -14,7 +14,6 @@
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.io.File;
|
||||
@ -36,7 +35,9 @@ import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager;
|
||||
import com.comphenix.protocol.timing.TimingReportGenerator;
|
||||
import com.google.common.io.Closer;
|
||||
import com.comphenix.protocol.updater.Updater;
|
||||
import com.comphenix.protocol.updater.Updater.UpdateType;
|
||||
import com.comphenix.protocol.utility.Closer;
|
||||
|
||||
/**
|
||||
* Handles the "protocol" administration command.
|
||||
@ -50,10 +51,14 @@ class CommandProtocol extends CommandBase {
|
||||
public static final String NAME = "protocol";
|
||||
|
||||
private Plugin plugin;
|
||||
private Updater updater;
|
||||
private ProtocolConfig config;
|
||||
|
||||
public CommandProtocol(ErrorReporter reporter, Plugin plugin) {
|
||||
public CommandProtocol(ErrorReporter reporter, Plugin plugin, Updater updater, ProtocolConfig config) {
|
||||
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 1);
|
||||
this.plugin = plugin;
|
||||
this.updater = updater;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,23 +66,37 @@ class CommandProtocol extends CommandBase {
|
||||
String subCommand = args[0];
|
||||
|
||||
// Only return TRUE if we executed the correct command
|
||||
if (subCommand.equalsIgnoreCase("config") || subCommand.equalsIgnoreCase("reload"))
|
||||
if (subCommand.equalsIgnoreCase("config") || subCommand.equalsIgnoreCase("reload")) {
|
||||
reloadConfiguration(sender);
|
||||
else if (subCommand.equalsIgnoreCase("timings"))
|
||||
} else if (subCommand.equalsIgnoreCase("check")) {
|
||||
checkVersion(sender);
|
||||
} else if (subCommand.equalsIgnoreCase("update")) {
|
||||
updateVersion(sender);
|
||||
} else if (subCommand.equalsIgnoreCase("timings")) {
|
||||
toggleTimings(sender, args);
|
||||
else if (subCommand.equalsIgnoreCase("listeners"))
|
||||
printListeners(sender);
|
||||
else if (subCommand.equalsIgnoreCase("version"))
|
||||
} else if (subCommand.equalsIgnoreCase("listeners")) {
|
||||
printListeners(sender, args);
|
||||
} else if (subCommand.equalsIgnoreCase("version")) {
|
||||
printVersion(sender);
|
||||
else if (subCommand.equalsIgnoreCase("dump"))
|
||||
} else if (subCommand.equalsIgnoreCase("dump")) {
|
||||
dump(sender);
|
||||
else
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void checkVersion(final CommandSender sender) {
|
||||
performUpdate(sender, UpdateType.NO_DOWNLOAD);
|
||||
}
|
||||
|
||||
public void updateVersion(final CommandSender sender) {
|
||||
performUpdate(sender, UpdateType.DEFAULT);
|
||||
}
|
||||
|
||||
// Display every listener on the server
|
||||
private void printListeners(final CommandSender sender) {
|
||||
private void printListeners(final CommandSender sender, String[] args) {
|
||||
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
sender.sendMessage(ChatColor.GOLD + "Packet listeners:");
|
||||
@ -92,6 +111,28 @@ class CommandProtocol extends CommandBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void performUpdate(final CommandSender sender, UpdateType type) {
|
||||
if (updater.isChecking()) {
|
||||
sender.sendMessage(ChatColor.RED + "Already checking for an update.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform on an async thread
|
||||
Runnable notify = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (updater.shouldNotify() || config.isDebug()) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "[ProtocolLib] " + updater.getResult());
|
||||
}
|
||||
|
||||
updater.removeListener(this);
|
||||
updateFinished();
|
||||
}
|
||||
};
|
||||
updater.start(type);
|
||||
updater.addListener(notify);
|
||||
}
|
||||
|
||||
private void toggleTimings(CommandSender sender, String[] args) {
|
||||
TimedListenerManager manager = TimedListenerManager.getInstance();
|
||||
boolean state = !manager.isTiming(); // toggle
|
||||
@ -135,11 +176,27 @@ class CommandProtocol extends CommandBase {
|
||||
// Print to a text file
|
||||
generator.saveTo(destination, manager);
|
||||
manager.clear();
|
||||
|
||||
} catch (IOException e) {
|
||||
reporter.reportMinimal(plugin, "saveTimings()", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent further automatic updates until the next delay.
|
||||
*/
|
||||
public void updateFinished() {
|
||||
long currentTime = System.currentTimeMillis() / ProtocolLibrary.MILLI_PER_SECOND;
|
||||
|
||||
config.setAutoLastTime(currentTime);
|
||||
config.saveAll();
|
||||
}
|
||||
|
||||
public void reloadConfiguration(CommandSender sender) {
|
||||
plugin.reloadConfig();
|
||||
sender.sendMessage(ChatColor.YELLOW + "Reloaded configuration!");
|
||||
}
|
||||
|
||||
private void printVersion(CommandSender sender) {
|
||||
PluginDescriptionFile desc = plugin.getDescription();
|
||||
|
||||
@ -148,11 +205,6 @@ class CommandProtocol extends CommandBase {
|
||||
sender.sendMessage(ChatColor.WHITE + "Issues: " + ChatColor.GREEN + "https://github.com/dmulloy2/ProtocolLib/issues");
|
||||
}
|
||||
|
||||
public void reloadConfiguration(CommandSender sender) {
|
||||
plugin.reloadConfig();
|
||||
sender.sendMessage(ChatColor.YELLOW + "Reloaded configuration!");
|
||||
}
|
||||
|
||||
private static SimpleDateFormat FILE_FORMAT;
|
||||
private static SimpleDateFormat TIMESTAMP_FORMAT;
|
||||
|
||||
@ -207,10 +259,7 @@ class CommandProtocol extends CommandBase {
|
||||
ProtocolLibrary.getStaticLogger().log(Level.SEVERE, "Failed to create dump:", ex);
|
||||
sender.sendMessage(ChatColor.RED + "Failed to create dump! Check console!");
|
||||
} finally {
|
||||
try {
|
||||
closer.close();
|
||||
} catch (IOException ex1) {
|
||||
}
|
||||
closer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
@ -11,6 +12,7 @@ import java.util.concurrent.Future;
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.ObjectEnum;
|
||||
@ -635,7 +637,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
||||
* @param packetId - the packet ID.
|
||||
* @return The corresponding packet type.
|
||||
* @throws IllegalArgumentException If the current packet could not be found.
|
||||
* @deprecated IDs are no longer reliable
|
||||
*/
|
||||
@Deprecated
|
||||
public static PacketType findCurrent(Protocol protocol, Sender sender, int packetId) {
|
||||
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
|
||||
|
||||
@ -645,13 +649,34 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
||||
"(Protocol: " + protocol + ", Sender: " + sender + ")");
|
||||
}
|
||||
|
||||
public static PacketType findCurrent(Protocol protocol, Sender sender, String name) {
|
||||
name = format(protocol, sender, name);
|
||||
PacketType type = getLookup().getFromCurrent(protocol, sender, name);
|
||||
|
||||
if (type != null) {
|
||||
return type;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot find packet " + name +
|
||||
"(Protocol: " + protocol + ", Sender: " + sender + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private static String format(Protocol protocol, Sender sender, String name) {
|
||||
if (name.contains("Packet"))
|
||||
return name;
|
||||
|
||||
return String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet exists.
|
||||
* @param protocol - the protocol.
|
||||
* @param sender - the sender.
|
||||
* @param packetId - the packet ID.
|
||||
* @return TRUE if it exists, FALSE otherwise.
|
||||
* @deprecated IDs are no longer reliable
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean hasCurrent(Protocol protocol, Sender sender, int packetId) {
|
||||
return getLookup().getFromCurrent(protocol, sender, packetId) != null;
|
||||
}
|
||||
@ -680,7 +705,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a packet type from a protocol, sender and packet ID.
|
||||
* Retrieve a packet type from a protocol, sender and packet ID, for pre-1.8.
|
||||
* <p>
|
||||
* The packet will automatically be registered if its missing.
|
||||
* @param protocol - the current protocol.
|
||||
@ -689,21 +714,59 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
||||
* @param packetClass - the packet class
|
||||
* @return The corresponding packet type.
|
||||
*/
|
||||
public static PacketType fromCurrent(Protocol protocol, Sender sender, int packetId, Class<?> packetClass) {
|
||||
String className = packetClass.getSimpleName();
|
||||
for (PacketType type : PacketType.values()) {
|
||||
for (String name : type.classNames) {
|
||||
if (className.equals(name)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
public static PacketType fromID(Protocol protocol, Sender sender, int packetId, Class<?> packetClass) {
|
||||
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
|
||||
|
||||
if (type == null) {
|
||||
type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, packetClass.getName());
|
||||
type.dynamic = true;
|
||||
|
||||
// Many may be scheduled, but only the first will be executed
|
||||
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
PacketType type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, className);
|
||||
type.dynamic = true;
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a packet type from a protocol, sender, ID, and class for 1.8+
|
||||
* <p>
|
||||
* The packet will automatically be registered if its missing.
|
||||
* @param protocol - the current protocol.
|
||||
* @param sender - the sender.
|
||||
* @param packetId - the packet ID. Can be UNKNOWN_PACKET.
|
||||
* @param packetClass - the packet class.
|
||||
* @return The corresponding packet type.
|
||||
*/
|
||||
public static PacketType fromCurrent(Protocol protocol, Sender sender, int packetId, Class<?> packetClass) {
|
||||
ClassLookup lookup = getLookup().getClassLookup();
|
||||
Map<String, PacketType> map = lookup.getMap(protocol, sender);
|
||||
|
||||
// Check the map first
|
||||
String className = packetClass.getSimpleName();
|
||||
PacketType type = map.get(className);
|
||||
if (type == null) {
|
||||
// Then check any aliases
|
||||
for (PacketType check : map.values()) {
|
||||
String[] aliases = check.getClassNames();
|
||||
if (aliases.length > 1) {
|
||||
for (String alias : aliases) {
|
||||
if (alias.equals(className)) {
|
||||
// We have a match!
|
||||
type = check;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Guess we don't support this packet :/
|
||||
type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, className);
|
||||
type.dynamic = true;
|
||||
|
||||
// Many may be scheduled, but only the first will be executed
|
||||
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
// Many may be scheduled, but only the first will be executed
|
||||
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -826,7 +889,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
||||
|
||||
this.classNames = new String[names.length];
|
||||
for (int i = 0; i < classNames.length; i++) {
|
||||
classNames[i] = String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), names[i]);
|
||||
classNames[i] = format(protocol, sender, names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -894,6 +957,10 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
||||
return currentId;
|
||||
}
|
||||
|
||||
public String[] getClassNames() {
|
||||
return classNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the equivalent packet class.
|
||||
* @return The packet class, or NULL if not found.
|
||||
|
@ -2,6 +2,8 @@ package com.comphenix.protocol;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
@ -15,7 +17,7 @@ import com.google.common.collect.Multimap;
|
||||
* @author Kristian
|
||||
*/
|
||||
class PacketTypeLookup {
|
||||
private static class ProtocolSenderLookup {
|
||||
public static class ProtocolSenderLookup {
|
||||
// Unroll lookup for performance reasons
|
||||
public final IntegerMap<PacketType> HANDSHAKE_CLIENT = IntegerMap.newMap();
|
||||
public final IntegerMap<PacketType> HANDSHAKE_SERVER = IntegerMap.newMap();
|
||||
@ -48,13 +50,49 @@ class PacketTypeLookup {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClassLookup {
|
||||
// Unroll lookup for performance reasons
|
||||
public final Map<String, PacketType> HANDSHAKE_CLIENT = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> HANDSHAKE_SERVER = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> GAME_CLIENT = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> GAME_SERVER = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> STATUS_CLIENT = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> STATUS_SERVER = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> LOGIN_CLIENT = new ConcurrentHashMap<String, PacketType>();
|
||||
public final Map<String, PacketType> LOGIN_SERVER = new ConcurrentHashMap<String, PacketType>();
|
||||
|
||||
/**
|
||||
* Retrieve the correct integer map for a specific protocol and sender.
|
||||
* @param protocol - the protocol.
|
||||
* @param sender - the sender.
|
||||
* @return The integer map of packets.
|
||||
*/
|
||||
public Map<String, PacketType> getMap(Protocol protocol, Sender sender) {
|
||||
switch (protocol) {
|
||||
case HANDSHAKING:
|
||||
return sender == Sender.CLIENT ? HANDSHAKE_CLIENT : HANDSHAKE_SERVER;
|
||||
case PLAY:
|
||||
return sender == Sender.CLIENT ? GAME_CLIENT : GAME_SERVER;
|
||||
case STATUS:
|
||||
return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER;
|
||||
case LOGIN:
|
||||
return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unable to find protocol " + protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Packet IDs from 1.6.4 and below
|
||||
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<PacketType>();
|
||||
private final IntegerMap<PacketType> serverLookup = new IntegerMap<PacketType>();
|
||||
private final IntegerMap<PacketType> clientLookup = new IntegerMap<PacketType>();
|
||||
|
||||
// Packets for 1.7.2
|
||||
private final ProtocolSenderLookup currentLookup = new ProtocolSenderLookup();
|
||||
private final ProtocolSenderLookup idLookup = new ProtocolSenderLookup();
|
||||
|
||||
// Packets for 1.8+
|
||||
private final ClassLookup classLookup = new ClassLookup();
|
||||
|
||||
// Packets based on name
|
||||
private final Multimap<String, PacketType> nameLookup = HashMultimap.create();
|
||||
@ -79,9 +117,9 @@ class PacketTypeLookup {
|
||||
}
|
||||
// Skip unknown current packets
|
||||
if (type.getCurrentId() != PacketType.UNKNOWN_PACKET) {
|
||||
currentLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
|
||||
idLookup.getMap(type.getProtocol(), type.getSender()).put(type.getCurrentId(), type);
|
||||
classLookup.getMap(type.getProtocol(), type.getSender()).put(type.getClassNames()[0], type);
|
||||
}
|
||||
// Save name
|
||||
nameLookup.put(type.name(), type);
|
||||
}
|
||||
return this;
|
||||
@ -132,8 +170,18 @@ class PacketTypeLookup {
|
||||
* @param sender - the sender.
|
||||
* @param packetId - the packet ID.
|
||||
* @return The corresponding packet type, or NULL if not found.
|
||||
* @deprecated IDs are no longer reliable
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketType getFromCurrent(Protocol protocol, Sender sender, int packetId) {
|
||||
return currentLookup.getMap(protocol, sender).get(packetId);
|
||||
return idLookup.getMap(protocol, sender).get(packetId);
|
||||
}
|
||||
|
||||
public PacketType getFromCurrent(Protocol protocol, Sender sender, String name) {
|
||||
return classLookup.getMap(protocol, sender).get(name);
|
||||
}
|
||||
|
||||
public ClassLookup getClassLookup() {
|
||||
return classLookup;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -25,8 +26,10 @@ import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
/**
|
||||
* Represents the configuration of ProtocolLib.
|
||||
@ -34,7 +37,10 @@ import com.google.common.collect.Lists;
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ProtocolConfig {
|
||||
private static final String LAST_UPDATE_FILE = "lastupdate";
|
||||
|
||||
private static final String SECTION_GLOBAL = "global";
|
||||
private static final String SECTION_AUTOUPDATER = "auto updater";
|
||||
|
||||
private static final String METRICS_ENABLED = "metrics";
|
||||
|
||||
@ -48,16 +54,33 @@ public class ProtocolConfig {
|
||||
private static final String SCRIPT_ENGINE_NAME = "script engine";
|
||||
private static final String SUPPRESSED_REPORTS = "suppressed reports";
|
||||
|
||||
private static final String UPDATER_NOTIFY = "notify";
|
||||
private static final String UPDATER_DOWNLAD = "download";
|
||||
private static final String UPDATER_DELAY = "delay";
|
||||
|
||||
// Defaults
|
||||
private static final long DEFAULT_UPDATER_DELAY = 43200;
|
||||
|
||||
private Plugin plugin;
|
||||
private Configuration config;
|
||||
private ConfigurationSection global;
|
||||
private boolean loadingSections;
|
||||
|
||||
private ConfigurationSection global;
|
||||
private ConfigurationSection updater;
|
||||
|
||||
// Last update time
|
||||
private long lastUpdateTime;
|
||||
private boolean configChanged;
|
||||
private boolean valuesChanged;
|
||||
|
||||
// Modifications
|
||||
private int modCount;
|
||||
|
||||
public ProtocolConfig(Plugin plugin) {
|
||||
this(plugin, plugin.getConfig());
|
||||
}
|
||||
|
||||
public ProtocolConfig(Plugin plugin, Configuration config) {
|
||||
this.plugin = plugin;
|
||||
reloadConfig();
|
||||
}
|
||||
@ -68,24 +91,118 @@ public class ProtocolConfig {
|
||||
public void reloadConfig() {
|
||||
// Reset
|
||||
configChanged = false;
|
||||
valuesChanged = false;
|
||||
modCount++;
|
||||
|
||||
this.config = plugin.getConfig();
|
||||
if (config != null) {
|
||||
config.options().copyDefaults(true);
|
||||
global = config.getConfigurationSection(SECTION_GLOBAL);
|
||||
this.lastUpdateTime = loadLastUpdate();
|
||||
loadSections(!loadingSections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the last update time stamp from the file system.
|
||||
*
|
||||
* @return Last update time stamp.
|
||||
*/
|
||||
private long loadLastUpdate() {
|
||||
File dataFile = getLastUpdateFile();
|
||||
|
||||
if (dataFile.exists()) {
|
||||
try {
|
||||
return Long.parseLong(Files.toString(dataFile, Charsets.UTF_8));
|
||||
} catch (NumberFormatException e) {
|
||||
plugin.getLogger().warning("Cannot parse " + dataFile + " as a number.");
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Cannot read " + dataFile);
|
||||
}
|
||||
}
|
||||
// Default last update
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the given time stamp.
|
||||
*
|
||||
* @param value - time stamp to store.
|
||||
*/
|
||||
private void saveLastUpdate(long value) {
|
||||
File dataFile = getLastUpdateFile();
|
||||
|
||||
// The data folder must exist
|
||||
dataFile.getParentFile().mkdirs();
|
||||
|
||||
if (dataFile.exists())
|
||||
dataFile.delete();
|
||||
|
||||
try {
|
||||
Files.write(Long.toString(value), dataFile, Charsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot write " + dataFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the file that is used to store the update time stamp.
|
||||
*
|
||||
* @return File storing the update time stamp.
|
||||
*/
|
||||
private File getLastUpdateFile() {
|
||||
return new File(plugin.getDataFolder(), LAST_UPDATE_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 && (!getFile().exists() || global == null || updater == null)) {
|
||||
loadingSections = true;
|
||||
|
||||
if (config != null)
|
||||
config.options().copyDefaults(true);
|
||||
plugin.saveDefaultConfig();
|
||||
plugin.reloadConfig();
|
||||
loadingSections = false;
|
||||
|
||||
// Inform the user
|
||||
ProtocolLibrary.log("Created default configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a particular configuration key value pair.
|
||||
*
|
||||
* @param section - the configuration root.
|
||||
* @param path - the path to the key.
|
||||
* @param value - the value to set.
|
||||
*/
|
||||
private void setConfig(ConfigurationSection section, String path, Object value) {
|
||||
configChanged = true;
|
||||
section.set(path, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getGlobalValue(String name, T def) {
|
||||
private <T> T getGlobalValue(String path, T def) {
|
||||
try {
|
||||
return (T) global.get(name, def);
|
||||
return (T) global.get(path, def);
|
||||
} catch (Throwable ex) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getUpdaterValue(String path, T def) {
|
||||
try {
|
||||
return (T) updater.get(path, def);
|
||||
} catch (Throwable ex) {
|
||||
return def;
|
||||
}
|
||||
@ -118,6 +235,44 @@ public class ProtocolConfig {
|
||||
global.set(DETAILED_ERROR, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getUpdaterValue(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) {
|
||||
setConfig(updater, UPDATER_NOTIFY, value);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not ProtocolLib should automatically download the new version.
|
||||
*
|
||||
* @return TRUE if it should, FALSE otherwise.
|
||||
*/
|
||||
public boolean isAutoDownload() {
|
||||
return updater != null && getUpdaterValue(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) {
|
||||
setConfig(updater, UPDATER_DOWNLAD, value);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not debug mode is enabled.
|
||||
* <p>
|
||||
@ -158,6 +313,31 @@ public class ProtocolConfig {
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(getUpdaterValue(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;
|
||||
setConfig(updater, UPDATER_DELAY, delaySeconds);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version of Minecraft to ignore the built-in safety feature.
|
||||
*
|
||||
@ -221,13 +401,34 @@ public class ProtocolConfig {
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last time we updated, in seconds since 1970.01.01 00:00.
|
||||
*
|
||||
* @return Last update time.
|
||||
*/
|
||||
public long getAutoLastTime() {
|
||||
return lastUpdateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last time we updated, in seconds since 1970.01.01 00:00.
|
||||
* <p>
|
||||
* Note that this is not considered to modify the configuration, so the modification count will not be incremented.
|
||||
*
|
||||
* @param lastTimeSeconds - new last update time.
|
||||
*/
|
||||
public void setAutoLastTime(long lastTimeSeconds) {
|
||||
this.valuesChanged = true;
|
||||
this.lastUpdateTime = lastTimeSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the unique name of the script engine to use for filtering.
|
||||
*
|
||||
* @return Script engine to use.
|
||||
*/
|
||||
public String getScriptEngineName() {
|
||||
return global.getString(SCRIPT_ENGINE_NAME, "JavaScript");
|
||||
return getGlobalValue(SCRIPT_ENGINE_NAME, "JavaScript");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -271,7 +472,7 @@ public class ProtocolConfig {
|
||||
/**
|
||||
* Set the starting injection method to use.
|
||||
*
|
||||
* @param hook Injection method
|
||||
* @return Injection method.
|
||||
*/
|
||||
public void setInjectionMethod(PlayerInjectHooks hook) {
|
||||
setConfig(global, INJECTION_METHOD, hook.name());
|
||||
@ -291,10 +492,13 @@ public class ProtocolConfig {
|
||||
* Save the current configuration file.
|
||||
*/
|
||||
public void saveAll() {
|
||||
if (valuesChanged)
|
||||
saveLastUpdate(lastUpdateTime);
|
||||
if (configChanged)
|
||||
plugin.saveConfig();
|
||||
|
||||
// And we're done
|
||||
valuesChanged = false;
|
||||
configChanged = false;
|
||||
}
|
||||
}
|
@ -50,6 +50,8 @@ import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.metrics.Statistics;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
import com.comphenix.protocol.updater.Updater;
|
||||
import com.comphenix.protocol.updater.Updater.UpdateType;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
@ -97,6 +99,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
*/
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2015-07-27";
|
||||
|
||||
// Update information
|
||||
static final String BUKKIT_DEV_SLUG = "protocollib";
|
||||
static final int BUKKIT_DEV_ID = 45564;
|
||||
|
||||
// Different commands
|
||||
private enum ProtocolCommand {
|
||||
FILTER,
|
||||
@ -142,6 +148,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
// Settings/options
|
||||
private int configExpectedMod = -1;
|
||||
|
||||
// Updater
|
||||
private Updater updater;
|
||||
private static boolean UPDATES_DISABLED;
|
||||
|
||||
// Logger
|
||||
private static Logger logger;
|
||||
private Handler redirectHandler;
|
||||
@ -205,6 +215,9 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
// Handle unexpected Minecraft versions
|
||||
MinecraftVersion version = verifyMinecraftVersion();
|
||||
|
||||
// Set updater - this will not perform any update automatically
|
||||
updater = Updater.create(this, BUKKIT_DEV_ID, getFile(), UpdateType.NO_DOWNLOAD, true);
|
||||
|
||||
unhookTask = new DelayedSingleTask(this);
|
||||
protocolManager = PacketFilterManager.newBuilder()
|
||||
.classLoader(getClassLoader())
|
||||
@ -254,7 +267,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
try {
|
||||
switch (command) {
|
||||
case PROTOCOL:
|
||||
commandProtocol = new CommandProtocol(reporter, this);
|
||||
commandProtocol = new CommandProtocol(reporter, this, updater, config);
|
||||
break;
|
||||
case FILTER:
|
||||
commandFilter = new CommandFilter(reporter, this, config);
|
||||
@ -559,6 +572,11 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
|
||||
// House keeping
|
||||
updateConfiguration();
|
||||
|
||||
// Check for updates too
|
||||
if (!UPDATES_DISABLED && (tickCounter % 20) == 0) {
|
||||
checkUpdates();
|
||||
}
|
||||
}
|
||||
}, ASYNC_MANAGER_DELAY, ASYNC_MANAGER_DELAY);
|
||||
} catch (OutOfMemoryError e) {
|
||||
@ -581,6 +599,29 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkUpdates() {
|
||||
// Ignore milliseconds - it's pointless
|
||||
long currentTime = System.currentTimeMillis() / MILLI_PER_SECOND;
|
||||
|
||||
try {
|
||||
long updateTime = config.getAutoLastTime() + config.getAutoDelay();
|
||||
|
||||
// Should we update?
|
||||
if (currentTime > updateTime && !updater.isChecking()) {
|
||||
// Initiate the update as if it came from the console
|
||||
if (config.isAutoDownload())
|
||||
commandProtocol.updateVersion(getServer().getConsoleSender());
|
||||
else if (config.isAutoNotify())
|
||||
commandProtocol.checkVersion(getServer().getConsoleSender());
|
||||
else
|
||||
commandProtocol.updateFinished();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_UPDATE_PLUGIN).error(e));
|
||||
UPDATES_DISABLED = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (skipDisable) {
|
||||
|
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2015 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.compat.netty;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
|
||||
import com.comphenix.protocol.injector.packet.MapContainer;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* @author dmulloy2
|
||||
*/
|
||||
|
||||
public class LegacyProtocolRegistry extends ProtocolRegistry {
|
||||
|
||||
public LegacyProtocolRegistry() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
final Object[] protocols = enumProtocol.getEnumConstants();
|
||||
List<Map<Integer, Class<?>>> serverMaps = Lists.newArrayList();
|
||||
List<Map<Integer, Class<?>>> clientMaps = Lists.newArrayList();
|
||||
StructureModifier<Object> modifier = null;
|
||||
|
||||
// Result
|
||||
Register result = new Register();
|
||||
|
||||
for (Object protocol : protocols) {
|
||||
if (modifier == null)
|
||||
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
|
||||
StructureModifier<Map<Integer, Class<?>>> maps = modifier.withTarget(protocol).withType(Map.class);
|
||||
|
||||
serverMaps.add(maps.read(0));
|
||||
clientMaps.add(maps.read(1));
|
||||
}
|
||||
// Maps we have to occationally check have changed
|
||||
for (Map<Integer, Class<?>> map : Iterables.concat(serverMaps, clientMaps)) {
|
||||
result.containers.add(new MapContainer(map));
|
||||
}
|
||||
|
||||
// Heuristic - there are more server packets than client packets
|
||||
if (sum(clientMaps) > sum(serverMaps)) {
|
||||
// Swap if this is violated
|
||||
List<Map<Integer, Class<?>>> temp = serverMaps;
|
||||
serverMaps = clientMaps;
|
||||
clientMaps = temp;
|
||||
}
|
||||
|
||||
for (int i = 0; i < protocols.length; i++) {
|
||||
Enum<?> enumProtocol = (Enum<?>) protocols[i];
|
||||
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
|
||||
|
||||
// Associate known types
|
||||
associatePackets(result, serverMaps.get(i), equivalent, Sender.SERVER);
|
||||
associatePackets(result, clientMaps.get(i), equivalent, Sender.CLIENT);
|
||||
}
|
||||
|
||||
// Exchange (thread safe, as we have only one writer)
|
||||
this.register = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender) {
|
||||
for (Entry<Integer, Class<?>> entry : lookup.entrySet()) {
|
||||
PacketType type = PacketType.fromID(protocol, sender, entry.getKey(), entry.getValue());
|
||||
|
||||
try {
|
||||
register.typeToClass.put(type, entry.getValue());
|
||||
|
||||
if (sender == Sender.SERVER)
|
||||
register.serverPackets.add(type);
|
||||
if (sender == Sender.CLIENT)
|
||||
register.clientPackets.add(type);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Sometimes this happens with fake packets, just ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2015 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.compat.netty.independent;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
|
||||
import com.comphenix.protocol.injector.packet.MapContainer;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* @author dmulloy2
|
||||
*/
|
||||
|
||||
public class NettyProtocolRegistry extends ProtocolRegistry {
|
||||
|
||||
@Override
|
||||
protected synchronized void initialize() {
|
||||
Object[] protocols = enumProtocol.getEnumConstants();
|
||||
|
||||
// ID to Packet class maps
|
||||
Map<Object, Map<Integer, Class<?>>> serverMaps = Maps.newLinkedHashMap();
|
||||
Map<Object, Map<Integer, Class<?>>> clientMaps = Maps.newLinkedHashMap();
|
||||
|
||||
Register result = new Register();
|
||||
StructureModifier<Object> modifier = null;
|
||||
|
||||
// Iterate through the protocols
|
||||
for (Object protocol : protocols) {
|
||||
if (modifier == null)
|
||||
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
|
||||
StructureModifier<Map<Object, Map<Integer, Class<?>>>> maps = modifier.withTarget(protocol).withType(Map.class);
|
||||
for (Entry<Object, Map<Integer, Class<?>>> entry : maps.read(0).entrySet()) {
|
||||
String direction = entry.getKey().toString();
|
||||
if (direction.contains("CLIENTBOUND")) { // Sent by Server
|
||||
serverMaps.put(protocol, entry.getValue());
|
||||
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
|
||||
clientMaps.put(protocol, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Maps we have to occationally check have changed
|
||||
for (Map<Integer, Class<?>> map : serverMaps.values()) {
|
||||
result.containers.add(new MapContainer(map));
|
||||
}
|
||||
|
||||
for (Map<Integer, Class<?>> map : clientMaps.values()) {
|
||||
result.containers.add(new MapContainer(map));
|
||||
}
|
||||
|
||||
for (int i = 0; i < protocols.length; i++) {
|
||||
Object protocol = protocols[i];
|
||||
Enum<?> enumProtocol = (Enum<?>) protocol;
|
||||
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
|
||||
|
||||
// Associate known types
|
||||
if (serverMaps.containsKey(protocol))
|
||||
associatePackets(result, serverMaps.get(protocol), equivalent, Sender.SERVER);
|
||||
if (clientMaps.containsKey(protocol))
|
||||
associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT);
|
||||
}
|
||||
|
||||
// Exchange (thread safe, as we have only one writer)
|
||||
this.register = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender) {
|
||||
for (Entry<Integer, Class<?>> entry : lookup.entrySet()) {
|
||||
PacketType type = PacketType.fromCurrent(protocol, sender, entry.getKey(), entry.getValue());
|
||||
|
||||
try {
|
||||
register.typeToClass.put(type, entry.getValue());
|
||||
|
||||
if (sender == Sender.SERVER)
|
||||
register.serverPackets.add(type);
|
||||
if (sender == Sender.CLIENT)
|
||||
register.clientPackets.add(type);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Sometimes this happens with fake packets, just ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.compat.netty.Netty;
|
||||
import com.comphenix.protocol.injector.packet.MapContainer;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a way of accessing the new netty Protocol enum.
|
||||
* @author Kristian
|
||||
*/
|
||||
// TODO: Handle modifications to the BiMap
|
||||
public class NettyProtocolRegistry {
|
||||
/**
|
||||
* Represents a register we are currently building.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static class Register {
|
||||
// The main lookup table
|
||||
public BiMap<PacketType, Class<?>> typeToClass = HashBiMap.create();
|
||||
public volatile Set<PacketType> serverPackets = Sets.newHashSet();
|
||||
public volatile Set<PacketType> clientPackets = Sets.newHashSet();
|
||||
public List<MapContainer> containers = Lists.newArrayList();
|
||||
|
||||
/**
|
||||
* Determine if the current register is outdated.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isOutdated() {
|
||||
for (MapContainer container : containers) {
|
||||
if (container.hasChanged()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> enumProtocol;
|
||||
|
||||
// Current register
|
||||
private volatile Register register;
|
||||
|
||||
public NettyProtocolRegistry() {
|
||||
enumProtocol = MinecraftReflection.getEnumProtocolClass();
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of the packet type lookup.
|
||||
* @return The packet type lookup.
|
||||
*/
|
||||
public Map<PacketType, Class<?>> getPacketTypeLookup() {
|
||||
return Collections.unmodifiableMap(register.typeToClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of the class to packet type lookup.
|
||||
* @return The packet type lookup.
|
||||
*/
|
||||
public Map<Class<?>, PacketType> getPacketClassLookup() {
|
||||
return Collections.unmodifiableMap(register.typeToClass.inverse());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known client packet, from every protocol.
|
||||
* @return Every client packet.
|
||||
*/
|
||||
public Set<PacketType> getClientPackets() {
|
||||
return Collections.unmodifiableSet(register.clientPackets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known server packet, from every protocol.
|
||||
* @return Every server packet.
|
||||
*/
|
||||
public Set<PacketType> getServerPackets() {
|
||||
return Collections.unmodifiableSet(register.serverPackets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that our local register is up-to-date with Minecraft.
|
||||
* <p>
|
||||
* This operation may block the calling thread.
|
||||
*/
|
||||
public synchronized void synchronize() {
|
||||
// See if the register is outdated
|
||||
if (register.isOutdated()) {
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the packet lookup tables in each protocol.
|
||||
*/
|
||||
private synchronized void initialize() {
|
||||
if (!Netty.isIndependent()) { // Check for 1.7
|
||||
initialize17();
|
||||
return;
|
||||
}
|
||||
|
||||
Object[] protocols = enumProtocol.getEnumConstants();
|
||||
|
||||
// ID to Packet class maps
|
||||
Map<Object, Map<Integer, Class<?>>> serverMaps = Maps.newLinkedHashMap();
|
||||
Map<Object, Map<Integer, Class<?>>> clientMaps = Maps.newLinkedHashMap();
|
||||
|
||||
Register result = new Register();
|
||||
StructureModifier<Object> modifier = null;
|
||||
|
||||
// Iterate through the protocols
|
||||
for (Object protocol : protocols) {
|
||||
if (modifier == null)
|
||||
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
|
||||
StructureModifier<Map<Object, Map<Integer, Class<?>>>> maps = modifier.withTarget(protocol).withType(Map.class);
|
||||
for (Entry<Object, Map<Integer, Class<?>>> entry : maps.read(0).entrySet()) {
|
||||
String direction = entry.getKey().toString();
|
||||
if (direction.contains("CLIENTBOUND")) { // Sent by Server
|
||||
serverMaps.put(protocol, entry.getValue());
|
||||
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
|
||||
clientMaps.put(protocol, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Maps we have to occationally check have changed
|
||||
for (Map<Integer, Class<?>> map : serverMaps.values()) {
|
||||
result.containers.add(new MapContainer(map));
|
||||
}
|
||||
|
||||
for (Map<Integer, Class<?>> map : clientMaps.values()) {
|
||||
result.containers.add(new MapContainer(map));
|
||||
}
|
||||
|
||||
// Heuristic - there are more server packets than client packets
|
||||
/* if (sum(clientMaps) > sum(serverMaps)) {
|
||||
// Swap if this is violated
|
||||
List<Map<Integer, Class<?>>> temp = serverMaps;
|
||||
serverMaps = clientMaps;
|
||||
clientMaps = temp;
|
||||
} */
|
||||
|
||||
for (int i = 0; i < protocols.length; i++) {
|
||||
Object protocol = protocols[i];
|
||||
Enum<?> enumProtocol = (Enum<?>) protocol;
|
||||
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
|
||||
|
||||
// Associate known types
|
||||
if (serverMaps.containsKey(protocol))
|
||||
associatePackets(result, serverMaps.get(protocol), equivalent, Sender.SERVER);
|
||||
if (clientMaps.containsKey(protocol))
|
||||
associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT);
|
||||
}
|
||||
|
||||
// Exchange (thread safe, as we have only one writer)
|
||||
this.register = result;
|
||||
}
|
||||
|
||||
private synchronized void initialize17() {
|
||||
final Object[] protocols = enumProtocol.getEnumConstants();
|
||||
List<Map<Integer, Class<?>>> serverMaps = Lists.newArrayList();
|
||||
List<Map<Integer, Class<?>>> clientMaps = Lists.newArrayList();
|
||||
StructureModifier<Object> modifier = null;
|
||||
|
||||
// Result
|
||||
Register result = new Register();
|
||||
|
||||
for (Object protocol : protocols) {
|
||||
if (modifier == null)
|
||||
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
|
||||
StructureModifier<Map<Integer, Class<?>>> maps = modifier.withTarget(protocol).withType(Map.class);
|
||||
|
||||
serverMaps.add(maps.read(0));
|
||||
clientMaps.add(maps.read(1));
|
||||
}
|
||||
// Maps we have to occationally check have changed
|
||||
for (Map<Integer, Class<?>> map : Iterables.concat(serverMaps, clientMaps)) {
|
||||
result.containers.add(new MapContainer(map));
|
||||
}
|
||||
|
||||
// Heuristic - there are more server packets than client packets
|
||||
if (sum(clientMaps) > sum(serverMaps)) {
|
||||
// Swap if this is violated
|
||||
List<Map<Integer, Class<?>>> temp = serverMaps;
|
||||
serverMaps = clientMaps;
|
||||
clientMaps = temp;
|
||||
}
|
||||
|
||||
for (int i = 0; i < protocols.length; i++) {
|
||||
Enum<?> enumProtocol = (Enum<?>) protocols[i];
|
||||
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
|
||||
|
||||
// Associate known types
|
||||
associatePackets(result, serverMaps.get(i), equivalent, Sender.SERVER);
|
||||
associatePackets(result, clientMaps.get(i), equivalent, Sender.CLIENT);
|
||||
}
|
||||
|
||||
// Exchange (thread safe, as we have only one writer)
|
||||
this.register = result;
|
||||
}
|
||||
|
||||
private void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender) {
|
||||
for (Entry<Integer, Class<?>> entry : lookup.entrySet()) {
|
||||
PacketType type = PacketType.fromCurrent(protocol, sender, entry.getKey(), entry.getValue());
|
||||
|
||||
try {
|
||||
register.typeToClass.put(type, entry.getValue());
|
||||
|
||||
if (sender == Sender.SERVER)
|
||||
register.serverPackets.add(type);
|
||||
if (sender == Sender.CLIENT)
|
||||
register.clientPackets.add(type);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Sometimes this happens with fake packets, just ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of mapping in all the maps.
|
||||
* @param maps - iterable of maps.
|
||||
* @return The sum of all the entries.
|
||||
*/
|
||||
private int sum(Iterable<? extends Map<Integer, Class<?>>> maps) {
|
||||
int count = 0;
|
||||
|
||||
for (Map<Integer, Class<?>> map : maps)
|
||||
count += map.size();
|
||||
return count;
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.injector.packet.MapContainer;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a way of accessing the new netty Protocol enum.
|
||||
* @author Kristian
|
||||
*/
|
||||
// TODO: Handle modifications to the BiMap
|
||||
public abstract class ProtocolRegistry {
|
||||
/**
|
||||
* Represents a register we are currently building.
|
||||
* @author Kristian
|
||||
*/
|
||||
protected static class Register {
|
||||
// The main lookup table
|
||||
public BiMap<PacketType, Class<?>> typeToClass = HashBiMap.create();
|
||||
public volatile Set<PacketType> serverPackets = Sets.newHashSet();
|
||||
public volatile Set<PacketType> clientPackets = Sets.newHashSet();
|
||||
public List<MapContainer> containers = Lists.newArrayList();
|
||||
|
||||
public Register() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current register is outdated.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isOutdated() {
|
||||
for (MapContainer container : containers) {
|
||||
if (container.hasChanged()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected Class<?> enumProtocol;
|
||||
|
||||
// Current register
|
||||
protected volatile Register register;
|
||||
|
||||
public ProtocolRegistry() {
|
||||
enumProtocol = MinecraftReflection.getEnumProtocolClass();
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of the packet type lookup.
|
||||
* @return The packet type lookup.
|
||||
*/
|
||||
public Map<PacketType, Class<?>> getPacketTypeLookup() {
|
||||
return Collections.unmodifiableMap(register.typeToClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of the class to packet type lookup.
|
||||
* @return The packet type lookup.
|
||||
*/
|
||||
public Map<Class<?>, PacketType> getPacketClassLookup() {
|
||||
return Collections.unmodifiableMap(register.typeToClass.inverse());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known client packet, from every protocol.
|
||||
* @return Every client packet.
|
||||
*/
|
||||
public Set<PacketType> getClientPackets() {
|
||||
return Collections.unmodifiableSet(register.clientPackets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known server packet, from every protocol.
|
||||
* @return Every server packet.
|
||||
*/
|
||||
public Set<PacketType> getServerPackets() {
|
||||
return Collections.unmodifiableSet(register.serverPackets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that our local register is up-to-date with Minecraft.
|
||||
* <p>
|
||||
* This operation may block the calling thread.
|
||||
*/
|
||||
public synchronized void synchronize() {
|
||||
// See if the register is outdated
|
||||
if (register.isOutdated()) {
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the packet lookup tables in each protocol.
|
||||
*/
|
||||
protected abstract void initialize();
|
||||
|
||||
protected abstract void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender);
|
||||
|
||||
/**
|
||||
* Retrieve the number of mapping in all the maps.
|
||||
* @param maps - iterable of maps.
|
||||
* @return The sum of all the entries.
|
||||
*/
|
||||
protected final int sum(Iterable<? extends Map<Integer, Class<?>>> maps) {
|
||||
int count = 0;
|
||||
|
||||
for (Map<Integer, Class<?>> map : maps)
|
||||
count += map.size();
|
||||
return count;
|
||||
}
|
||||
}
|
@ -25,12 +25,15 @@ import java.util.Set;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.compat.netty.LegacyProtocolRegistry;
|
||||
import com.comphenix.protocol.compat.netty.independent.NettyProtocolRegistry;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
|
||||
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
|
||||
import com.comphenix.protocol.injector.packet.LegacyPacketRegistry.InsufficientPacketsException;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.TroveWrapper.CannotFindTroveNoEntryValue;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Maps;
|
||||
@ -49,7 +52,7 @@ public class PacketRegistry {
|
||||
|
||||
// Two different packet registry
|
||||
private static volatile LegacyPacketRegistry LEGACY;
|
||||
private static volatile NettyProtocolRegistry NETTY;
|
||||
private static volatile ProtocolRegistry NETTY;
|
||||
|
||||
// Cached for legacy
|
||||
private static volatile Set<PacketType> NETTY_SERVER_PACKETS;
|
||||
@ -77,7 +80,11 @@ public class PacketRegistry {
|
||||
// Check for netty
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
if (NETTY == null) {
|
||||
NETTY = new NettyProtocolRegistry();
|
||||
if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE)) {
|
||||
NETTY = new NettyProtocolRegistry();
|
||||
} else {
|
||||
NETTY = new LegacyProtocolRegistry();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
initializeLegacy();
|
||||
|
@ -36,20 +36,12 @@ import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
public class Util {
|
||||
private static MethodAccessor getOnlinePlayers;
|
||||
private static boolean reflectionRequired;
|
||||
private static boolean isUsingSpigot;
|
||||
|
||||
static {
|
||||
try {
|
||||
Method method = Bukkit.class.getMethod("getOnlinePlayers");
|
||||
getOnlinePlayers = Accessors.getMethodAccessor(method);
|
||||
reflectionRequired = !method.getReturnType().isAssignableFrom(Collection.class);
|
||||
|
||||
try {
|
||||
Class.forName("org.bukkit.entity.Player.Spigot");
|
||||
isUsingSpigot = true;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
isUsingSpigot = false;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
throw new RuntimeException("Failed to obtain getOnlinePlayers method.", ex);
|
||||
}
|
||||
@ -87,10 +79,10 @@ public class Util {
|
||||
|
||||
/**
|
||||
* Whether or not this server is running Spigot. This works by checking
|
||||
* for a Spigot-specific API class, in this case {@link Player.Spigot}.
|
||||
* the server version for the String "Spigot"
|
||||
* @return True if it is, false if not.
|
||||
*/
|
||||
public static boolean isUsingSpigot() {
|
||||
return isUsingSpigot;
|
||||
return Bukkit.getServer().getVersion().contains("Spigot");
|
||||
}
|
||||
}
|
@ -295,6 +295,8 @@ public abstract class EnumWrappers {
|
||||
if (INITIALIZED)
|
||||
return;
|
||||
|
||||
INITIALIZED = true;
|
||||
|
||||
PROTOCOL_CLASS = getEnum(PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass(), 0);
|
||||
CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
|
||||
CHAT_VISIBILITY_CLASS = getEnum(PacketType.Play.Client.SETTINGS.getPacketClass(), 0);
|
||||
|
@ -1,4 +1,12 @@
|
||||
global:
|
||||
# Settings for the automatic version updater
|
||||
auto updater:
|
||||
notify: true
|
||||
download: false
|
||||
|
||||
# Number of seconds to wait until a new update is downloaded
|
||||
delay: 43200 # 12 hours
|
||||
|
||||
metrics: true
|
||||
|
||||
# Automatically compile structure modifiers
|
||||
|
@ -10,7 +10,7 @@ database: false
|
||||
commands:
|
||||
protocol:
|
||||
description: Performs administrative tasks regarding ProtocolLib.
|
||||
usage: /<command> config|timings|listeners|version|dump
|
||||
usage: /<command> config|check|update|timings|listeners|version|dump
|
||||
permission: protocol.admin
|
||||
permission-message: You don't have <permission>
|
||||
packet:
|
||||
|
@ -24,7 +24,6 @@ import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.SnapshotVersion;
|
||||
|
||||
public class MinecraftVersionTest {
|
||||
@Test
|
||||
@ -35,20 +34,27 @@ public class MinecraftVersionTest {
|
||||
MinecraftVersion lower = new MinecraftVersion(1, 0, 0);
|
||||
MinecraftVersion highest = new MinecraftVersion(1, 4, 5);
|
||||
|
||||
MinecraftVersion atLeast = new MinecraftVersion(1, 8, 8);
|
||||
|
||||
// Make sure this is valid
|
||||
assertTrue(lower.compareTo(within) < 0 && within.compareTo(highest) < 0);
|
||||
assertFalse(outside.compareTo(within) < 0 && outside.compareTo(highest) < 0);
|
||||
assertTrue(atLeast.isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE));
|
||||
}
|
||||
|
||||
@Test
|
||||
/* @Test
|
||||
public void testSnapshotVersion() {
|
||||
MinecraftVersion version = MinecraftVersion.fromServerVersion("git-Spigot-1119 (MC: 13w39b)");
|
||||
assertEquals(version.getSnapshot(), new SnapshotVersion("13w39b"));
|
||||
}
|
||||
} */
|
||||
|
||||
@Test
|
||||
public void testParsing() {
|
||||
assertEquals(MinecraftVersion.extractVersion("CraftBukkit R3.0 (MC: 1.4.3)"), "1.4.3");
|
||||
assertEquals(MinecraftVersion.extractVersion("CraftBukkit Test Beta 1 (MC: 1.10.01 )"), "1.10.01");
|
||||
assertEquals(MinecraftVersion.extractVersion("Hello (MC: 2.3.4 ) "), "2.3.4");
|
||||
assertEquals(MinecraftVersion.extractVersion("Hello (MC: 2.3.4)"), "2.3.4");
|
||||
|
||||
assertEquals(MinecraftVersion.fromServerVersion("git-Cauldron-Reloaded-1.7.10-1.1388.1.0 (MC: 1.7.10)"), new MinecraftVersion(1, 7, 10));
|
||||
assertEquals(MinecraftVersion.fromServerVersion("git-Bukkit-18fbb24 (MC: 1.8.8)"), new MinecraftVersion(1, 8, 8));
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ import org.junit.Test;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
|
||||
import com.comphenix.protocol.compat.netty.independent.NettyProtocolRegistry;
|
||||
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
|
||||
|
||||
public class PacketTypeTest {
|
||||
|
||||
@ -22,12 +23,12 @@ public class PacketTypeTest {
|
||||
|
||||
@Test
|
||||
public void testFindCurrent() {
|
||||
assertEquals(PacketType.Play.Client.STEER_VEHICLE, PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, 12));
|
||||
assertEquals(PacketType.Play.Client.STEER_VEHICLE, PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, "SteerVehicle"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureAllExist() {
|
||||
NettyProtocolRegistry registry = new NettyProtocolRegistry();
|
||||
ProtocolRegistry registry = new NettyProtocolRegistry();
|
||||
Map<PacketType, Class<?>> lookup = registry.getPacketTypeLookup();
|
||||
for (Entry<PacketType, Class<?>> entry : lookup.entrySet()) {
|
||||
PacketType type = entry.getKey();
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren