Archiviert
13
0

Merge branch 'master' into gh-pages

Dieser Commit ist enthalten in:
Kristian S. Stangeland 2013-06-21 01:12:07 +02:00
Commit 56b81281a8
15 geänderte Dateien mit 2260 neuen und 2046 gelöschten Zeilen

Datei anzeigen

@ -11,12 +11,12 @@
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>net.sourceforge.metrics.builder</name> <name>org.eclipse.m2e.core.maven2Builder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name> <name>net.sourceforge.metrics.builder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>

Datei anzeigen

@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<version>2.4.3</version> <version>2.4.5</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<description>Provides read/write access to the Minecraft protocol.</description> <description>Provides read/write access to the Minecraft protocol.</description>

Datei anzeigen

@ -1,167 +1,168 @@
/* /*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland * Copyright (C) 2012 Kristian S. Stangeland
* *
* This program is free software; you can redistribute it and/or modify it under the terms of the * 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 * GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version. * 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; * 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. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. * 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; * 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 * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA * 02111-1307 USA
*/ */
package com.comphenix.protocol; package com.comphenix.protocol;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.comphenix.protocol.async.AsyncListenerHandler; import com.comphenix.protocol.async.AsyncListenerHandler;
import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report; import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ListeningWhitelist; import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.BukkitUnwrapper; import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup; import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory; import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector; import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.MethodUtils; import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.reflect.ObjectWriter; import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler; import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.StructureCompiler; import com.comphenix.protocol.reflect.compiler.StructureCompiler;
import com.comphenix.protocol.reflect.instances.CollectionGenerator; import com.comphenix.protocol.reflect.instances.CollectionGenerator;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.PrimitiveGenerator; import com.comphenix.protocol.reflect.instances.PrimitiveGenerator;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer; import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
/** /**
* Used to fix ClassLoader leaks that may lead to filling up the permanent generation. * Used to fix ClassLoader leaks that may lead to filling up the permanent generation.
* *
* @author Kristian * @author Kristian
*/ */
class CleanupStaticMembers { class CleanupStaticMembers {
// Reports // Reports
public final static ReportType REPORT_CANNOT_RESET_FIELD = new ReportType("Unable to reset field %s: %s"); public final static ReportType REPORT_CANNOT_RESET_FIELD = new ReportType("Unable to reset field %s: %s");
public final static ReportType REPORT_CANNOT_UNLOAD_CLASS = new ReportType("Unable to unload class %s."); public final static ReportType REPORT_CANNOT_UNLOAD_CLASS = new ReportType("Unable to unload class %s.");
private ClassLoader loader; private ClassLoader loader;
private ErrorReporter reporter; private ErrorReporter reporter;
public CleanupStaticMembers(ClassLoader loader, ErrorReporter reporter) { public CleanupStaticMembers(ClassLoader loader, ErrorReporter reporter) {
this.loader = loader; this.loader = loader;
this.reporter = reporter; this.reporter = reporter;
} }
/** /**
* Ensure that the previous ClassLoader is not leaking. * Ensure that the previous ClassLoader is not leaking.
*/ */
public void resetAll() { public void resetAll() {
// This list must always be updated // This list must always be updated
Class<?>[] publicClasses = { Class<?>[] publicClasses = {
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class, AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class, BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class, PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class,
BackgroundCompiler.class, StructureCompiler.class, BackgroundCompiler.class, StructureCompiler.class,
ObjectWriter.class, Packets.Server.class, Packets.Client.class, ObjectWriter.class, Packets.Server.class, Packets.Client.class,
ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class, ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class,
AbstractInputStreamLookup.class, TemporaryPlayerFactory.class, SpigotPacketInjector.class, AbstractInputStreamLookup.class, TemporaryPlayerFactory.class, SpigotPacketInjector.class,
MinecraftReflection.class, NbtBinarySerializer.class MinecraftReflection.class, NbtBinarySerializer.class
}; };
String[] internalClasses = { String[] internalClasses = {
"com.comphenix.protocol.events.SerializedOfflinePlayer", "com.comphenix.protocol.events.SerializedOfflinePlayer",
"com.comphenix.protocol.injector.player.InjectedServerConnection", "com.comphenix.protocol.injector.player.InjectedServerConnection",
"com.comphenix.protocol.injector.player.NetworkFieldInjector", "com.comphenix.protocol.injector.player.NetworkFieldInjector",
"com.comphenix.protocol.injector.player.NetworkObjectInjector", "com.comphenix.protocol.injector.player.NetworkObjectInjector",
"com.comphenix.protocol.injector.player.NetworkServerInjector", "com.comphenix.protocol.injector.player.NetworkServerInjector",
"com.comphenix.protocol.injector.player.PlayerInjector", "com.comphenix.protocol.injector.player.PlayerInjector",
"com.comphenix.protocol.injector.EntityUtilities", "com.comphenix.protocol.injector.EntityUtilities",
"com.comphenix.protocol.injector.packet.PacketRegistry", "com.comphenix.protocol.injector.packet.PacketRegistry",
"com.comphenix.protocol.injector.packet.PacketInjector", "com.comphenix.protocol.injector.packet.PacketInjector",
"com.comphenix.protocol.injector.packet.ReadPacketModifier", "com.comphenix.protocol.injector.packet.ReadPacketModifier",
"com.comphenix.protocol.injector.StructureCache", "com.comphenix.protocol.injector.StructureCache",
"com.comphenix.protocol.reflect.compiler.BoxingHelper", "com.comphenix.protocol.reflect.compiler.BoxingHelper",
"com.comphenix.protocol.reflect.compiler.MethodDescriptor", "com.comphenix.protocol.reflect.compiler.MethodDescriptor",
"com.comphenix.protocol.wrappers.nbt.WrappedElement", "com.comphenix.protocol.wrappers.nbt.WrappedElement",
}; };
resetClasses(publicClasses); resetClasses(publicClasses);
resetClasses(getClasses(loader, internalClasses)); resetClasses(getClasses(loader, internalClasses));
} }
private void resetClasses(Class<?>[] classes) { private void resetClasses(Class<?>[] classes) {
// Reset each class one by one // Reset each class one by one
for (Class<?> clazz : classes) { for (Class<?> clazz : classes) {
resetClass(clazz); resetClass(clazz);
} }
} }
private void resetClass(Class<?> clazz) { private void resetClass(Class<?> clazz) {
for (Field field : clazz.getFields()) { for (Field field : clazz.getFields()) {
Class<?> type = field.getType(); Class<?> type = field.getType();
// Only check static non-primitive fields. We also skip strings. // Only check static non-primitive fields. We also skip strings.
if (Modifier.isStatic(field.getModifiers()) && if (Modifier.isStatic(field.getModifiers()) &&
!type.isPrimitive() && !type.equals(String.class)) { !type.isPrimitive() && !type.equals(String.class)) {
try { try {
setFinalStatic(field, null); setFinalStatic(field, null);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
// Just inform the player // Just inform the player
reporter.reportWarning(this, reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_RESET_FIELD).error(e).messageParam(field.getName(), e.getMessage()) Report.newBuilder(REPORT_CANNOT_RESET_FIELD).error(e).messageParam(field.getName(), e.getMessage())
); );
} e.printStackTrace();
} }
} }
} }
}
// HACK! HAACK!
private static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException { // HACK! HAACK!
int modifier = field.getModifiers(); private static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException {
boolean isFinal = Modifier.isFinal(modifier); int modifier = field.getModifiers();
boolean isFinal = Modifier.isFinal(modifier);
Field modifiersField = isFinal ? FieldUtils.getField(Field.class, "modifiers", true) : null;
Field modifiersField = isFinal ? FieldUtils.getField(Field.class, "modifiers", true) : null;
// We have to remove the final field first
if (isFinal) { // We have to remove the final field first
FieldUtils.writeField(modifiersField, field, modifier & ~Modifier.FINAL, true); if (isFinal) {
} FieldUtils.writeField(modifiersField, field, modifier & ~Modifier.FINAL, true);
}
// Now we can safely modify the field
FieldUtils.writeStaticField(field, newValue, true); // Now we can safely modify the field
FieldUtils.writeStaticField(field, newValue, true);
// Revert modifier
if (isFinal) { // Revert modifier
FieldUtils.writeField(modifiersField, field, modifier, true); if (isFinal) {
} FieldUtils.writeField(modifiersField, field, modifier, true);
} }
}
private Class<?>[] getClasses(ClassLoader loader, String[] names) {
List<Class<?>> output = new ArrayList<Class<?>>(); private Class<?>[] getClasses(ClassLoader loader, String[] names) {
List<Class<?>> output = new ArrayList<Class<?>>();
for (String name : names) {
try { for (String name : names) {
output.add(loader.loadClass(name)); try {
} catch (ClassNotFoundException e) { output.add(loader.loadClass(name));
// Warn the user } catch (ClassNotFoundException e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_UNLOAD_CLASS).error(e).messageParam(name)); // Warn the user
} reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_UNLOAD_CLASS).error(e).messageParam(name));
} }
}
return output.toArray(new Class<?>[0]);
} return output.toArray(new Class<?>[0]);
} }
}

Datei anzeigen

@ -220,14 +220,15 @@ public class CommandFilter extends CommandBase {
// Script engine // Script engine
private ScriptEngine engine; private ScriptEngine engine;
private boolean uninitialized;
public CommandFilter(ErrorReporter reporter, Plugin plugin, ProtocolConfig config) { public CommandFilter(ErrorReporter reporter, Plugin plugin, ProtocolConfig config) {
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 2); super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 2);
this.plugin = plugin; this.plugin = plugin;
this.config = config; this.config = config;
// Start the engine // Tell the filter system to initialize the script first chance it gets
initalizeScript(); this.uninitialized = true;
} }
private void initalizeScript() { private void initalizeScript() {
@ -238,6 +239,8 @@ public class CommandFilter extends CommandBase {
// Oh for .. // Oh for ..
if (!isInitialized()) { if (!isInitialized()) {
throw new ScriptException("A JavaScript engine could not be found."); throw new ScriptException("A JavaScript engine could not be found.");
} else {
plugin.getLogger().info("Loaded command filter engine.");
} }
} catch (ScriptException e1) { } catch (ScriptException e1) {
// It's not a huge deal // It's not a huge deal
@ -342,12 +345,25 @@ public class CommandFilter extends CommandBase {
return true; return true;
} }
/**
* Initialize the script engine if necessary.
*/
private void checkScriptStatus() {
// Start the engine
if (uninitialized) {
uninitialized = false;
initalizeScript();
}
}
/* /*
* Description: Adds or removes a simple packet filter. * Description: Adds or removes a simple packet filter.
Usage: /<command> add|remove name [packet IDs] Usage: /<command> add|remove name [packet IDs]
*/ */
@Override @Override
protected boolean handleCommand(CommandSender sender, String[] args) { protected boolean handleCommand(CommandSender sender, String[] args) {
checkScriptStatus();
if (!config.isDebug()) { if (!config.isDebug()) {
sender.sendMessage(ChatColor.RED + "Debug mode must be enabled in the configuration first!"); sender.sendMessage(ChatColor.RED + "Debug mode must be enabled in the configuration first!");
return true; return true;

Datei anzeigen

@ -85,6 +85,12 @@ public final class Packets {
public static final int ENTITY_HEAD_ROTATION = 35; public static final int ENTITY_HEAD_ROTATION = 35;
public static final int ENTITY_STATUS = 38; public static final int ENTITY_STATUS = 38;
public static final int ATTACH_ENTITY = 39; public static final int ATTACH_ENTITY = 39;
/**
* Sent when an entities DataWatcher is updated.
* <p>
* Remember to clone the packet if you are modifying it.
*/
public static final int ENTITY_METADATA = 40; public static final int ENTITY_METADATA = 40;
public static final int MOB_EFFECT = 41; public static final int MOB_EFFECT = 41;
public static final int REMOVE_MOB_EFFECT = 42; public static final int REMOVE_MOB_EFFECT = 42;
@ -109,6 +115,12 @@ public final class Packets {
public static final int SET_CREATIVE_SLOT = 107; public static final int SET_CREATIVE_SLOT = 107;
public static final int UPDATE_SIGN = 130; public static final int UPDATE_SIGN = 130;
public static final int ITEM_DATA = 131; public static final int ITEM_DATA = 131;
/**
* Sent the first time a tile entity (chest inventory, etc.) is withing range of the player, or has been updated.
* <p>
* Remember to clone the packet if you are modifying it.
*/
public static final int TILE_ENTITY_DATA = 132; public static final int TILE_ENTITY_DATA = 132;
public static final int STATISTIC = 200; public static final int STATISTIC = 200;
public static final int PLAYER_INFO = 201; public static final int PLAYER_INFO = 201;

Datei anzeigen

@ -19,6 +19,7 @@ package com.comphenix.protocol;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import org.bukkit.configuration.Configuration; import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
@ -26,6 +27,8 @@ import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.Files; import com.google.common.io.Files;
/** /**
@ -48,6 +51,7 @@ class ProtocolConfig {
private static final String INJECTION_METHOD = "injection method"; private static final String INJECTION_METHOD = "injection method";
private static final String SCRIPT_ENGINE_NAME = "script engine"; 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_NOTIFY = "notify";
private static final String UPDATER_DOWNLAD = "download"; private static final String UPDATER_DOWNLAD = "download";
@ -68,6 +72,9 @@ class ProtocolConfig {
private boolean configChanged; private boolean configChanged;
private boolean valuesChanged; private boolean valuesChanged;
// Modifications
private int modCount;
public ProtocolConfig(Plugin plugin) { public ProtocolConfig(Plugin plugin) {
this(plugin, plugin.getConfig()); this(plugin, plugin.getConfig());
} }
@ -84,6 +91,7 @@ class ProtocolConfig {
// Reset // Reset
configChanged = false; configChanged = false;
valuesChanged = false; valuesChanged = false;
modCount++;
this.config = plugin.getConfig(); this.config = plugin.getConfig();
this.lastUpdateTime = loadLastUpdate(); this.lastUpdateTime = loadLastUpdate();
@ -199,6 +207,7 @@ class ProtocolConfig {
*/ */
public void setAutoNotify(boolean value) { public void setAutoNotify(boolean value) {
setConfig(updater, UPDATER_NOTIFY, value); setConfig(updater, UPDATER_NOTIFY, value);
modCount++;
} }
/** /**
@ -215,6 +224,7 @@ class ProtocolConfig {
*/ */
public void setAutoDownload(boolean value) { public void setAutoDownload(boolean value) {
setConfig(updater, UPDATER_DOWNLAD, value); setConfig(updater, UPDATER_DOWNLAD, value);
modCount++;
} }
/** /**
@ -233,6 +243,24 @@ class ProtocolConfig {
*/ */
public void setDebug(boolean value) { public void setDebug(boolean value) {
setConfig(global, DEBUG_MODE_ENABLED, value); setConfig(global, DEBUG_MODE_ENABLED, value);
modCount++;
}
/**
* Retrieve an immutable list of every suppressed report type.
* @return Every suppressed report type.
*/
public ImmutableList<String> getSuppressedReports() {
return ImmutableList.copyOf(global.getStringList(SUPPRESSED_REPORTS));
}
/**
* Set the list of suppressed report types,
* @param reports - suppressed report types.
*/
public void setSuppressedReports(List<String> reports) {
global.set(SUPPRESSED_REPORTS, Lists.newArrayList(reports));
modCount++;
} }
/** /**
@ -255,6 +283,7 @@ class ProtocolConfig {
if (delaySeconds < DEFAULT_UPDATER_DELAY) if (delaySeconds < DEFAULT_UPDATER_DELAY)
delaySeconds = DEFAULT_UPDATER_DELAY; delaySeconds = DEFAULT_UPDATER_DELAY;
setConfig(updater, UPDATER_DELAY, delaySeconds); setConfig(updater, UPDATER_DELAY, delaySeconds);
modCount++;
} }
/** /**
@ -275,6 +304,7 @@ class ProtocolConfig {
*/ */
public void setIgnoreVersionCheck(String ignoreVersion) { public void setIgnoreVersionCheck(String ignoreVersion) {
setConfig(global, IGNORE_VERSION_CHECK, ignoreVersion); setConfig(global, IGNORE_VERSION_CHECK, ignoreVersion);
modCount++;
} }
/** /**
@ -294,6 +324,7 @@ class ProtocolConfig {
*/ */
public void setMetricsEnabled(boolean enabled) { public void setMetricsEnabled(boolean enabled) {
setConfig(global, METRICS_ENABLED, enabled); setConfig(global, METRICS_ENABLED, enabled);
modCount++;
} }
/** /**
@ -313,6 +344,7 @@ class ProtocolConfig {
*/ */
public void setBackgroundCompilerEnabled(boolean enabled) { public void setBackgroundCompilerEnabled(boolean enabled) {
setConfig(global, BACKGROUND_COMPILER_ENABLED, enabled); setConfig(global, BACKGROUND_COMPILER_ENABLED, enabled);
modCount++;
} }
/** /**
@ -325,6 +357,9 @@ class ProtocolConfig {
/** /**
* Set the last time we updated, in seconds since 1970.01.01 00:00. * 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. * @param lastTimeSeconds - new last update time.
*/ */
public void setAutoLastTime(long lastTimeSeconds) { public void setAutoLastTime(long lastTimeSeconds) {
@ -348,6 +383,7 @@ class ProtocolConfig {
*/ */
public void setScriptEngineName(String name) { public void setScriptEngineName(String name) {
setConfig(global, SCRIPT_ENGINE_NAME, name); setConfig(global, SCRIPT_ENGINE_NAME, name);
modCount++;
} }
/** /**
@ -380,6 +416,15 @@ class ProtocolConfig {
*/ */
public void setInjectionMethod(PlayerInjectHooks hook) { public void setInjectionMethod(PlayerInjectHooks hook) {
setConfig(global, INJECTION_METHOD, hook.name()); setConfig(global, INJECTION_METHOD, hook.name());
modCount++;
}
/**
* Retrieve the number of modifications made to this configuration.
* @return The number of modifications.
*/
public int getModificationCount() {
return modCount;
} }
/** /**

Datei anzeigen

@ -19,6 +19,7 @@ package com.comphenix.protocol;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Set;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.LogRecord; import java.util.logging.LogRecord;
@ -34,6 +35,7 @@ import org.bukkit.plugin.java.JavaPlugin;
import com.comphenix.protocol.async.AsyncFilterManager; import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.BasicErrorReporter; import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.DelegatedErrorReporter;
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.error.Report; import com.comphenix.protocol.error.Report;
@ -47,6 +49,9 @@ import com.comphenix.protocol.metrics.Updater.UpdateResult;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler; import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.utility.ChatExtensions; import com.comphenix.protocol.utility.ChatExtensions;
import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
/** /**
* The main entry point for ProtocolLib. * The main entry point for ProtocolLib.
@ -136,12 +141,12 @@ public class ProtocolLibrary extends JavaPlugin {
// Add global parameters // Add global parameters
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this); DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
reporter = detailedReporter; reporter = getFilteredReporter(detailedReporter);
try { try {
config = new ProtocolConfig(this); config = new ProtocolConfig(this);
} catch (Exception e) { } catch (Exception e) {
detailedReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_CONFIG).error(e)); reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_CONFIG).error(e));
// Load it again // Load it again
if (deleteConfig()) { if (deleteConfig()) {
@ -168,7 +173,7 @@ public class ProtocolLibrary extends JavaPlugin {
unhookTask = new DelayedSingleTask(this); unhookTask = new DelayedSingleTask(this);
protocolManager = new PacketFilterManager( protocolManager = new PacketFilterManager(
getClassLoader(), getServer(), this, version, unhookTask, detailedReporter); getClassLoader(), getServer(), this, version, unhookTask, reporter);
// Setup error reporter // Setup error reporter
detailedReporter.addGlobalParameter("manager", protocolManager); detailedReporter.addGlobalParameter("manager", protocolManager);
@ -183,23 +188,52 @@ public class ProtocolLibrary extends JavaPlugin {
protocolManager.setPlayerHook(hook); protocolManager.setPlayerHook(hook);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
detailedReporter.reportWarning(config, Report.newBuilder(REPORT_CANNOT_PARSE_INJECTION_METHOD).error(e)); reporter.reportWarning(config, Report.newBuilder(REPORT_CANNOT_PARSE_INJECTION_METHOD).error(e));
} }
// Initialize command handlers // Initialize command handlers
commandProtocol = new CommandProtocol(detailedReporter, this, updater, config); commandProtocol = new CommandProtocol(reporter, this, updater, config);
commandFilter = new CommandFilter(detailedReporter, this, config); commandFilter = new CommandFilter(reporter, this, config);
commandPacket = new CommandPacket(detailedReporter, this, logger, commandFilter, protocolManager); commandPacket = new CommandPacket(reporter, this, logger, commandFilter, protocolManager);
// Send logging information to player listeners too // Send logging information to player listeners too
setupBroadcastUsers(PERMISSION_INFO); setupBroadcastUsers(PERMISSION_INFO);
} catch (Throwable e) { } catch (Throwable e) {
detailedReporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_LOAD_ERROR).error(e).callerParam(protocolManager)); reporter.reportDetailed(this, Report.newBuilder(REPORT_PLUGIN_LOAD_ERROR).error(e).callerParam(protocolManager));
disablePlugin(); disablePlugin();
} }
} }
/**
* Retrieve a error reporter that may be filtered by the configuration.
* @return The new default error reporter.
*/
private ErrorReporter getFilteredReporter(ErrorReporter reporter) {
return new DelegatedErrorReporter(reporter) {
private int lastModCount = -1;
private Set<String> reports = Sets.newHashSet();
@Override
protected Report filterReport(Object sender, Report report, boolean detailed) {
String canonicalName = ReportType.getReportName(sender.getClass(), report.getType());
String reportName = Iterables.getLast(Splitter.on("#").split(canonicalName)).toUpperCase();
if (config != null && config.getModificationCount() != lastModCount) {
// Update our cached set again
reports = Sets.newHashSet(config.getSuppressedReports());
lastModCount = config.getModificationCount();
}
// Cancel reports either on the full canonical name, or just the report name
if (reports.contains(canonicalName) || reports.contains(reportName))
return null;
else
return report;
}
};
}
private boolean deleteConfig() { private boolean deleteConfig() {
return config.getFile().delete(); return config.getFile().delete();
} }

Datei anzeigen

@ -1,66 +1,115 @@
package com.comphenix.protocol.error; package com.comphenix.protocol.error;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
/** import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
* Represents a strongly-typed report. Subclasses should be immutable.
* <p> /**
* By convention, a report must be declared as a static field publicly accessible from the sender class. * Represents a strongly-typed report. Subclasses should be immutable.
* @author Kristian * <p>
*/ * By convention, a report must be declared as a static field publicly accessible from the sender class.
public class ReportType { * @author Kristian
private final String errorFormat; */
public class ReportType {
/** private final String errorFormat;
* Construct a new report type.
* @param errorFormat - string used to format the underlying report. // Used to store the report name
*/ protected String reportName;
public ReportType(String errorFormat) {
this.errorFormat = errorFormat; /**
} * Construct a new report type.
* @param errorFormat - string used to format the underlying report.
/** */
* Convert the given report to a string, using the provided parameters. public ReportType(String errorFormat) {
* @param parameters - parameters to insert, or NULL to insert nothing. this.errorFormat = errorFormat;
* @return The full report in string format. }
*/
public String getMessage(Object[] parameters) { /**
if (parameters == null || parameters.length == 0) * Convert the given report to a string, using the provided parameters.
return toString(); * @param parameters - parameters to insert, or NULL to insert nothing.
else * @return The full report in string format.
return String.format(errorFormat, parameters); */
} public String getMessage(Object[] parameters) {
if (parameters == null || parameters.length == 0)
@Override return toString();
public String toString() { else
return errorFormat; return String.format(errorFormat, parameters);
} }
/** @Override
* Retrieve all publicly associated reports. public String toString() {
* @param clazz - sender class. return errorFormat;
* @return All associated reports. }
*/
public static ReportType[] getReports(Class<?> clazz) { /**
if (clazz == null) * Retrieve the full canonical name of a given report type.
throw new IllegalArgumentException("clazz cannot be NULL."); * <p>
List<ReportType> result = new ArrayList<ReportType>(); * This is in the format <i>canonical_name_of_class#report_type</i>
* @param clazz - the sender class.
for (Field field : clazz.getFields()) { * @param type - the report instance.
if (Modifier.isStatic(field.getModifiers()) && * @return The full canonical name.
ReportType.class.isAssignableFrom(field.getDeclaringClass())) { */
try { public static String getReportName(Class<?> sender, ReportType type) {
result.add((ReportType) field.get(null)); if (sender == null)
} catch (IllegalAccessException e) { throw new IllegalArgumentException("sender cannot be NUll.");
throw new FieldAccessException("Unable to access field.", e);
} // Whether or not we need to retrieve the report name again
} if (type.reportName == null) {
} for (Field field : getReportFields(sender)) {
return result.toArray(new ReportType[0]); try {
} field.setAccessible(true);
}
if (field.get(null) == type) {
// We got the right field!
return type.reportName = field.getDeclaringClass().getCanonicalName() + "#" + field.getName();
}
} catch (IllegalAccessException e) {
throw new FieldAccessException("Unable to read field " + field, e);
}
}
throw new IllegalArgumentException("Cannot find report name for " + type);
}
return type.reportName;
}
/**
* Retrieve all publicly associated reports.
* @param sender - sender class.
* @return All associated reports.
*/
public static ReportType[] getReports(Class<?> sender) {
if (sender == null)
throw new IllegalArgumentException("sender cannot be NULL.");
List<ReportType> result = new ArrayList<ReportType>();
// Go through all the fields
for (Field field : getReportFields(sender)) {
try {
field.setAccessible(true);
result.add((ReportType) field.get(null));
} catch (IllegalAccessException e) {
throw new FieldAccessException("Unable to read field " + field, e);
}
}
return result.toArray(new ReportType[0]);
}
/**
* Retrieve all publicly associated report fields.
* @param clazz - sender class.
* @return All associated report fields.
*/
private static List<Field> getReportFields(Class<?> clazz) {
return FuzzyReflection.fromClass(clazz).getFieldList(
FuzzyFieldContract.newBuilder().
requireModifier(Modifier.STATIC).
typeDerivedOf(ReportType.class).
build()
);
}
}

Datei anzeigen

@ -285,43 +285,11 @@ public class PacketContainer implements Serializable {
* internal Minecraft ItemStack. * internal Minecraft ItemStack.
* @return A modifier for ItemStack array fields. * @return A modifier for ItemStack array fields.
*/ */
public StructureModifier<ItemStack[]> getItemArrayModifier() { public StructureModifier<ItemStack[]> getItemArrayModifier() {
final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<ItemStack[]>withType( return structureModifier.<ItemStack[]>withType(
MinecraftReflection.getItemStackArrayClass(), MinecraftReflection.getItemStackArrayClass(),
BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() { BukkitConverters.getIgnoreNull(new ItemStackArrayConverter()));
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
Class<?> nmsStack = MinecraftReflection.getItemStackClass();
Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length);
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
}
return result;
}
@Override
public ItemStack[] getSpecific(Object generic) {
Object[] input = (Object[]) generic;
ItemStack[] result = new ItemStack[input.length];
// Add the wrapper
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getSpecific(input[i]);
}
return result;
}
@Override
public Class<ItemStack[]> getSpecificType() {
return ItemStack[].class;
}
}));
} }
/** /**
@ -556,4 +524,40 @@ public class PacketContainer implements Serializable {
return method; return method;
} }
/**
* Represents an equivalent converter for ItemStack arrays.
* @author Kristian
*/
private static class ItemStackArrayConverter implements EquivalentConverter<ItemStack[]> {
final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
Class<?> nmsStack = MinecraftReflection.getItemStackClass();
Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length);
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
}
return result;
}
@Override
public ItemStack[] getSpecific(Object generic) {
Object[] input = (Object[]) generic;
ItemStack[] result = new ItemStack[input.length];
// Add the wrapper
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getSpecific(input[i]);
}
return result;
}
@Override
public Class<ItemStack[]> getSpecificType() {
return ItemStack[].class;
}
}
} }

Datei anzeigen

@ -1,352 +1,358 @@
/* /*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland * Copyright (C) 2012 Kristian S. Stangeland
* *
* This program is free software; you can redistribute it and/or modify it under the terms of the * 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 * GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version. * 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; * 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. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. * 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; * 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 * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA * 02111-1307 USA
*/ */
package com.comphenix.protocol.injector.player; package com.comphenix.protocol.injector.player;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import net.sf.cglib.proxy.*; import net.sf.cglib.proxy.*;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.comphenix.protocol.concurrency.IntegerSet; import com.comphenix.protocol.concurrency.IntegerSet;
import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report; import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.PacketListener; import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter; import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator; import com.comphenix.protocol.reflect.instances.ExistingGenerator;
import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.utility.MinecraftVersion;
/** /**
* Represents a player hook into the NetServerHandler class. * Represents a player hook into the NetServerHandler class.
* *
* @author Kristian * @author Kristian
*/ */
class NetworkServerInjector extends PlayerInjector { class NetworkServerInjector extends PlayerInjector {
// Disconnected field // Disconnected field
public static final ReportType REPORT_ASSUMING_DISCONNECT_FIELD = new ReportType("Unable to find 'disconnected' field. Assuming %s."); public static final ReportType REPORT_ASSUMING_DISCONNECT_FIELD = new ReportType("Unable to find 'disconnected' field. Assuming %s.");
public static final ReportType REPORT_DISCONNECT_FIELD_MISSING = new ReportType("Cannot find disconnected field. Is ProtocolLib up to date?"); public static final ReportType REPORT_DISCONNECT_FIELD_MISSING = new ReportType("Cannot find disconnected field. Is ProtocolLib up to date?");
public static final ReportType REPORT_DISCONNECT_FIELD_FAILURE = new ReportType("Unable to update disconnected field. Player quit event may be sent twice."); public static final ReportType REPORT_DISCONNECT_FIELD_FAILURE = new ReportType("Unable to update disconnected field. Player quit event may be sent twice.");
private volatile static CallbackFilter callbackFilter; private volatile static CallbackFilter callbackFilter;
private volatile static boolean foundSendPacket; private volatile static boolean foundSendPacket;
private volatile static Field disconnectField; private volatile static Field disconnectField;
private InjectedServerConnection serverInjection; private InjectedServerConnection serverInjection;
// Determine if we're listening // Determine if we're listening
private IntegerSet sendingFilters; private IntegerSet sendingFilters;
// Used to create proxy objects // Used to create proxy objects
private ClassLoader classLoader; private ClassLoader classLoader;
// Whether or not the player has disconnected // Whether or not the player has disconnected
private boolean hasDisconnected; private boolean hasDisconnected;
// Used to copy fields // Used to copy fields
private final ObjectWriter writer = new ObjectWriter(); private final ObjectWriter writer = new ObjectWriter();
public NetworkServerInjector( public NetworkServerInjector(
ClassLoader classLoader, ErrorReporter reporter, Player player, ClassLoader classLoader, ErrorReporter reporter, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters, ListenerInvoker invoker, IntegerSet sendingFilters,
InjectedServerConnection serverInjection) throws IllegalAccessException { InjectedServerConnection serverInjection) throws IllegalAccessException {
super(reporter, player, invoker); super(reporter, player, invoker);
this.classLoader = classLoader; this.classLoader = classLoader;
this.sendingFilters = sendingFilters; this.sendingFilters = sendingFilters;
this.serverInjection = serverInjection; this.serverInjection = serverInjection;
} }
@Override @Override
protected boolean hasListener(int packetID) { protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID); return sendingFilters.contains(packetID);
} }
@Override @Override
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException { public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
Object serverDelegate = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue(); Object serverDelegate = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue();
if (serverDelegate != null) { if (serverDelegate != null) {
try { try {
// Note that invocation target exception is a wrapper for a checked exception // Note that invocation target exception is a wrapper for a checked exception
MinecraftMethods.getSendPacketMethod().invoke(serverDelegate, packet); MinecraftMethods.getSendPacketMethod().invoke(serverDelegate, packet);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw e; throw e;
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
throw e; throw e;
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new IllegalStateException("Unable to access send packet method.", e); throw new IllegalStateException("Unable to access send packet method.", e);
} }
} else { } else {
throw new IllegalStateException("Unable to load server handler. Cannot send packet."); throw new IllegalStateException("Unable to load server handler. Cannot send packet.");
} }
} }
@Override @Override
public void injectManager() { public void injectManager() {
if (serverHandlerRef == null) if (serverHandlerRef == null)
throw new IllegalStateException("Cannot find server handler."); throw new IllegalStateException("Cannot find server handler.");
// Don't inject twice // Don't inject twice
if (serverHandlerRef.getValue() instanceof Factory) if (serverHandlerRef.getValue() instanceof Factory)
return; return;
if (!tryInjectManager()) { if (!tryInjectManager()) {
Class<?> serverHandlerClass = MinecraftReflection.getNetServerHandlerClass(); Class<?> serverHandlerClass = MinecraftReflection.getNetServerHandlerClass();
// Try to override the proxied object // Try to override the proxied object
if (proxyServerField != null) { if (proxyServerField != null) {
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true); serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
serverHandler = serverHandlerRef.getValue(); serverHandler = serverHandlerRef.getValue();
if (serverHandler == null) if (serverHandler == null)
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL."); throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
else else
serverHandlerClass = serverHandler.getClass(); serverHandlerClass = serverHandler.getClass();
// Try again // Try again
if (tryInjectManager()) { if (tryInjectManager()) {
// It worked - probably // It worked - probably
return; return;
} }
} }
throw new RuntimeException( throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the " "Cannot hook player: Unable to find a valid constructor for the "
+ serverHandlerClass.getName() + " object."); + serverHandlerClass.getName() + " object.");
} }
} }
private boolean tryInjectManager() { private boolean tryInjectManager() {
Class<?> serverClass = serverHandler.getClass(); Class<?> serverClass = serverHandler.getClass();
Enhancer ex = new Enhancer(); Enhancer ex = new Enhancer();
Callback sendPacketCallback = new MethodInterceptor() { Callback sendPacketCallback = new MethodInterceptor() {
@Override @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object packet = args[0]; Object packet = args[0];
if (packet != null) { if (packet != null) {
packet = handlePacketSending(packet); packet = handlePacketSending(packet);
// A NULL packet indicate cancelling // A NULL packet indicate cancelling
if (packet != null) if (packet != null)
args[0] = packet; args[0] = packet;
else else
return null; return null;
} }
// Call the method directly // Call the method directly
return proxy.invokeSuper(obj, args); return proxy.invokeSuper(obj, args);
}; };
}; };
Callback noOpCallback = NoOp.INSTANCE; Callback noOpCallback = NoOp.INSTANCE;
// Share callback filter - that way, we avoid generating a new class for // Share callback filter - that way, we avoid generating a new class for
// every logged in player. // every logged in player.
if (callbackFilter == null) { if (callbackFilter == null) {
final Method sendPacket = MinecraftMethods.getSendPacketMethod(); callbackFilter = new SendMethodFilter();
}
callbackFilter = new CallbackFilter() {
@Override ex.setClassLoader(classLoader);
public int accept(Method method) { ex.setSuperclass(serverClass);
if (isCallableEqual(sendPacket, method)) { ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
foundSendPacket = true; ex.setCallbackFilter(callbackFilter);
return 0;
} else { // Find the Minecraft NetServerHandler superclass
return 1; Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
} ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
} DefaultInstances serverInstances = null;
};
} // Maybe the proxy instance can help?
Object proxyInstance = getProxyServerHandler();
ex.setClassLoader(classLoader);
ex.setSuperclass(serverClass); // Use the existing server proxy when we create one
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback }); if (proxyInstance != null && proxyInstance != serverHandler) {
ex.setCallbackFilter(callbackFilter); serverInstances = DefaultInstances.fromArray(generator,
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
// Find the Minecraft NetServerHandler superclass } else {
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass()); serverInstances = DefaultInstances.fromArray(generator);
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass); }
DefaultInstances serverInstances = null;
serverInstances.setNonNull(true);
// Maybe the proxy instance can help? serverInstances.setMaximumRecursion(1);
Object proxyInstance = getProxyServerHandler();
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
// Use the existing server proxy when we create one
if (proxyInstance != null && proxyInstance != serverHandler) { // Inject it now
serverInstances = DefaultInstances.fromArray(generator, if (proxyObject != null) {
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance })); // Did we override a sendPacket method?
} else { if (!foundSendPacket) {
serverInstances = DefaultInstances.fromArray(generator); throw new IllegalArgumentException("Unable to find a sendPacket method in " + serverClass);
} }
serverInstances.setNonNull(true); serverInjection.replaceServerHandler(serverHandler, proxyObject);
serverInstances.setMaximumRecursion(1); serverHandlerRef.setValue(proxyObject);
return true;
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass); } else {
return false;
// Inject it now }
if (proxyObject != null) { }
// Did we override a sendPacket method?
if (!foundSendPacket) { private Object getProxyServerHandler() {
throw new IllegalArgumentException("Unable to find a sendPacket method in " + serverClass); if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
} try {
return FieldUtils.readField(proxyServerField, serverHandler, true);
serverInjection.replaceServerHandler(serverHandler, proxyObject); } catch (Throwable e) {
serverHandlerRef.setValue(proxyObject); // Oh well
return true; }
} else { }
return false;
} return null;
} }
/** private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
* Determine if the two methods are equal in terms of call semantics. if (MinecraftReflection.isMinecraftClass(clazz))
* <p> return clazz;
* Two methods are equal if they have the same name, parameter types and return type. else if (clazz.equals(Object.class))
* @param first - first method. return clazz;
* @param second - second method. else
* @return TRUE if they are, FALSE otherwise. return getFirstMinecraftSuperClass(clazz.getSuperclass());
*/ }
private boolean isCallableEqual(Method first, Method second) {
return first.getName().equals(second.getName()) && @Override
first.getReturnType().equals(second.getReturnType()) && protected void cleanHook() {
Arrays.equals(first.getParameterTypes(), second.getParameterTypes()); if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
} writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue();
private Object getProxyServerHandler() {
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) { try {
try { if (getNetHandler() != null) {
return FieldUtils.readField(proxyServerField, serverHandler, true); // Restore packet listener
} catch (Throwable e) { try {
// Oh well FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true);
} } catch (IllegalAccessException e) {
} // Oh well
e.printStackTrace();
return null; }
} }
} catch (IllegalAccessException e) {
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) { e.printStackTrace();
if (MinecraftReflection.isMinecraftClass(clazz)) }
return clazz;
else if (clazz.equals(Object.class)) // Prevent the PlayerQuitEvent from being sent twice
return clazz; if (hasDisconnected) {
else setDisconnect(serverHandlerRef.getValue(), true);
return getFirstMinecraftSuperClass(clazz.getSuperclass()); }
} }
@Override serverInjection.revertServerHandler(serverHandler);
protected void cleanHook() { }
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass()); @Override
serverHandlerRef.revertValue(); public void handleDisconnect() {
hasDisconnected = true;
try { }
if (getNetHandler() != null) {
// Restore packet listener /**
try { * Set the disconnected field in a NetServerHandler.
FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true); * @param handler - the NetServerHandler.
} catch (IllegalAccessException e) { * @param value - the new value.
// Oh well */
e.printStackTrace(); private void setDisconnect(Object handler, boolean value) {
} // Set it
} try {
} catch (IllegalAccessException e) { // Load the field
e.printStackTrace(); if (disconnectField == null) {
} disconnectField = FuzzyReflection.fromObject(handler).getFieldByName("disconnected.*");
}
// Prevent the PlayerQuitEvent from being sent twice FieldUtils.writeField(disconnectField, handler, value);
if (hasDisconnected) {
setDisconnect(serverHandlerRef.getValue(), true); } catch (IllegalArgumentException e) {
} // Assume it's the first ...
} if (disconnectField == null) {
disconnectField = FuzzyReflection.fromObject(handler).getFieldByType("disconnected", boolean.class);
serverInjection.revertServerHandler(serverHandler); reporter.reportWarning(this, Report.newBuilder(REPORT_ASSUMING_DISCONNECT_FIELD).messageParam(disconnectField));
}
// Try again
@Override if (disconnectField != null) {
public void handleDisconnect() { setDisconnect(handler, value);
hasDisconnected = true; return;
} }
}
/**
* Set the disconnected field in a NetServerHandler. // This is really bad
* @param handler - the NetServerHandler. reporter.reportDetailed(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_MISSING).error(e));
* @param value - the new value.
*/ } catch (IllegalAccessException e) {
private void setDisconnect(Object handler, boolean value) { reporter.reportWarning(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_FAILURE).error(e));
// Set it }
try { }
// Load the field
if (disconnectField == null) { @Override
disconnectField = FuzzyReflection.fromObject(handler).getFieldByName("disconnected.*"); public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) {
} // We support everything
FieldUtils.writeField(disconnectField, handler, value); return null;
}
} catch (IllegalArgumentException e) {
// Assume it's the first ... @Override
if (disconnectField == null) { public boolean canInject(GamePhase phase) {
disconnectField = FuzzyReflection.fromObject(handler).getFieldByType("disconnected", boolean.class); // Doesn't work when logging in
reporter.reportWarning(this, Report.newBuilder(REPORT_ASSUMING_DISCONNECT_FIELD).messageParam(disconnectField)); return phase == GamePhase.PLAYING;
}
// Try again
if (disconnectField != null) { @Override
setDisconnect(handler, value); public PlayerInjectHooks getHookType() {
return; return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
} }
}
/**
// This is really bad * Represents a CallbackFilter that only matches the SendPacket method.
reporter.reportDetailed(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_MISSING).error(e)); * @author Kristian
*/
} catch (IllegalAccessException e) { private static class SendMethodFilter implements CallbackFilter {
reporter.reportWarning(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_FAILURE).error(e)); private Method sendPacket = MinecraftMethods.getSendPacketMethod();
}
} @Override
public int accept(Method method) {
@Override if (isCallableEqual(sendPacket, method)) {
public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) { NetworkServerInjector.foundSendPacket = true;
// We support everything return 0;
return null; } else {
} return 1;
}
@Override }
public boolean canInject(GamePhase phase) {
// Doesn't work when logging in /**
return phase == GamePhase.PLAYING; * Determine if the two methods are equal in terms of call semantics.
} * <p>
* Two methods are equal if they have the same name, parameter types and return type.
@Override * @param first - first method.
public PlayerInjectHooks getHookType() { * @param second - second method.
return PlayerInjectHooks.NETWORK_SERVER_OBJECT; * @return TRUE if they are, FALSE otherwise.
} */
} private boolean isCallableEqual(Method first, Method second) {
return first.getName().equals(second.getName()) &&
first.getReturnType().equals(second.getReturnType()) &&
Arrays.equals(first.getParameterTypes(), second.getParameterTypes());
}
}
}

Datei anzeigen

@ -5,7 +5,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -37,6 +36,7 @@ import com.comphenix.protocol.reflect.MethodInfo;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.MapMaker; import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;
/** /**
* Offload all the work to Spigot, if possible. * Offload all the work to Spigot, if possible.
@ -58,6 +58,42 @@ public class SpigotPacketInjector implements SpigotPacketListener {
* The amount of ticks to wait before removing all traces of a player. * The amount of ticks to wait before removing all traces of a player.
*/ */
private static final int CLEANUP_DELAY = 100; private static final int CLEANUP_DELAY = 100;
// The listener we will register on Spigot.
// Unfortunately, due to the use of PlayerConnection, INetworkManager and Packet, we're
// unable to reference it directly. But with CGLib, it shouldn't cost us much.
private Object dynamicListener;
// Reference to ProtocolLib
private Plugin plugin;
// Different sending filters
private IntegerSet queuedFilters;
private IntegerSet reveivedFilters;
// NetworkManager to injector and player
private ConcurrentMap<Object, NetworkObjectInjector> networkManagerInjector = Maps.newConcurrentMap();
// Player to injector
private ConcurrentMap<Player, NetworkObjectInjector> playerInjector = Maps.newConcurrentMap();
// Responsible for informing the PL packet listeners
private ListenerInvoker invoker;
private ErrorReporter reporter;
private Server server;
private ClassLoader classLoader;
/**
* Create a new spigot injector.
*/
public SpigotPacketInjector(ClassLoader classLoader, ErrorReporter reporter, ListenerInvoker invoker, Server server) {
this.classLoader = classLoader;
this.reporter = reporter;
this.invoker = invoker;
this.server = server;
this.queuedFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
this.reveivedFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
}
/** /**
* Retrieve the spigot packet listener class. * Retrieve the spigot packet listener class.
@ -107,42 +143,6 @@ public class SpigotPacketInjector implements SpigotPacketListener {
return getSpigotListenerClass() != null; return getSpigotListenerClass() != null;
} }
// The listener we will register on Spigot.
// Unfortunately, due to the use of PlayerConnection, INetworkManager and Packet, we're
// unable to reference it directly. But with CGLib, it shouldn't cost us much.
private Object dynamicListener;
// Reference to ProtocolLib
private Plugin plugin;
// Different sending filters
private IntegerSet queuedFilters;
private IntegerSet reveivedFilters;
// NetworkManager to injector and player
private ConcurrentMap<Object, NetworkObjectInjector> networkManagerInjector = new ConcurrentHashMap<Object, NetworkObjectInjector>();
// Player to injector
private ConcurrentMap<Player, NetworkObjectInjector> playerInjector = new ConcurrentHashMap<Player, NetworkObjectInjector>();
// Responsible for informing the PL packet listeners
private ListenerInvoker invoker;
private ErrorReporter reporter;
private Server server;
private ClassLoader classLoader;
/**
* Create a new spigot injector.
*/
public SpigotPacketInjector(ClassLoader classLoader, ErrorReporter reporter, ListenerInvoker invoker, Server server) {
this.classLoader = classLoader;
this.reporter = reporter;
this.invoker = invoker;
this.server = server;
this.queuedFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
this.reveivedFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
}
/** /**
* Register the Spigot packet injector. * Register the Spigot packet injector.
* @param plugin - the parent plugin. * @param plugin - the parent plugin.
@ -455,7 +455,7 @@ public class SpigotPacketInjector implements SpigotPacketListener {
// Clean up // Clean up
playerInjector.remove(injector.getPlayer()); playerInjector.remove(injector.getPlayer());
playerInjector.remove(injector.getUpdatedPlayer()); playerInjector.remove(injector.getUpdatedPlayer());
networkManagerInjector.remove(injector); networkManagerInjector.remove(injector.getNetworkManager());
} }
}, CLEANUP_DELAY); }, CLEANUP_DELAY);
} }

Datei anzeigen

@ -22,4 +22,6 @@ global:
debug: false debug: false
# The engine used by the filter command # The engine used by the filter command
script engine: JavaScript script engine: JavaScript
suppressed reports:

Datei anzeigen

@ -1,5 +1,5 @@
name: ProtocolLib name: ProtocolLib
version: 2.4.3 version: 2.4.5
description: Provides read/write access to the Minecraft protocol. description: Provides read/write access to the Minecraft protocol.
author: Comphenix author: Comphenix
website: http://www.comphenix.net/ProtocolLib website: http://www.comphenix.net/ProtocolLib