Merge branch 'master' into gh-pages
Dieser Commit ist enthalten in:
Commit
56b81281a8
@ -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>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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:
|
@ -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
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren