Merge branch 'master' into gh-pages
Dieser Commit ist enthalten in:
Commit
56b81281a8
@ -11,12 +11,12 @@
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>net.sourceforge.metrics.builder</name>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<name>net.sourceforge.metrics.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<version>2.4.5</version>
|
||||
<packaging>jar</packaging>
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
|
||||
|
@ -124,6 +124,7 @@ class CleanupStaticMembers {
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_RESET_FIELD).error(e).messageParam(field.getName(), e.getMessage())
|
||||
);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,14 +220,15 @@ public class CommandFilter extends CommandBase {
|
||||
|
||||
// Script engine
|
||||
private ScriptEngine engine;
|
||||
private boolean uninitialized;
|
||||
|
||||
public CommandFilter(ErrorReporter reporter, Plugin plugin, ProtocolConfig config) {
|
||||
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 2);
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
|
||||
// Start the engine
|
||||
initalizeScript();
|
||||
// Tell the filter system to initialize the script first chance it gets
|
||||
this.uninitialized = true;
|
||||
}
|
||||
|
||||
private void initalizeScript() {
|
||||
@ -238,6 +239,8 @@ public class CommandFilter extends CommandBase {
|
||||
// Oh for ..
|
||||
if (!isInitialized()) {
|
||||
throw new ScriptException("A JavaScript engine could not be found.");
|
||||
} else {
|
||||
plugin.getLogger().info("Loaded command filter engine.");
|
||||
}
|
||||
} catch (ScriptException e1) {
|
||||
// It's not a huge deal
|
||||
@ -342,12 +345,25 @@ public class CommandFilter extends CommandBase {
|
||||
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.
|
||||
Usage: /<command> add|remove name [packet IDs]
|
||||
*/
|
||||
@Override
|
||||
protected boolean handleCommand(CommandSender sender, String[] args) {
|
||||
checkScriptStatus();
|
||||
|
||||
if (!config.isDebug()) {
|
||||
sender.sendMessage(ChatColor.RED + "Debug mode must be enabled in the configuration first!");
|
||||
return true;
|
||||
|
@ -85,6 +85,12 @@ public final class Packets {
|
||||
public static final int ENTITY_HEAD_ROTATION = 35;
|
||||
public static final int ENTITY_STATUS = 38;
|
||||
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 MOB_EFFECT = 41;
|
||||
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 UPDATE_SIGN = 130;
|
||||
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 STATISTIC = 200;
|
||||
public static final int PLAYER_INFO = 201;
|
||||
|
@ -19,6 +19,7 @@ package com.comphenix.protocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
@ -26,6 +27,8 @@ import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
/**
|
||||
@ -48,6 +51,7 @@ class ProtocolConfig {
|
||||
private static final String INJECTION_METHOD = "injection method";
|
||||
|
||||
private static final String SCRIPT_ENGINE_NAME = "script engine";
|
||||
private static final String SUPPRESSED_REPORTS = "suppressed reports";
|
||||
|
||||
private static final String UPDATER_NOTIFY = "notify";
|
||||
private static final String UPDATER_DOWNLAD = "download";
|
||||
@ -68,6 +72,9 @@ class ProtocolConfig {
|
||||
private boolean configChanged;
|
||||
private boolean valuesChanged;
|
||||
|
||||
// Modifications
|
||||
private int modCount;
|
||||
|
||||
public ProtocolConfig(Plugin plugin) {
|
||||
this(plugin, plugin.getConfig());
|
||||
}
|
||||
@ -84,6 +91,7 @@ class ProtocolConfig {
|
||||
// Reset
|
||||
configChanged = false;
|
||||
valuesChanged = false;
|
||||
modCount++;
|
||||
|
||||
this.config = plugin.getConfig();
|
||||
this.lastUpdateTime = loadLastUpdate();
|
||||
@ -199,6 +207,7 @@ class ProtocolConfig {
|
||||
*/
|
||||
public void setAutoNotify(boolean value) {
|
||||
setConfig(updater, UPDATER_NOTIFY, value);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,6 +224,7 @@ class ProtocolConfig {
|
||||
*/
|
||||
public void setAutoDownload(boolean value) {
|
||||
setConfig(updater, UPDATER_DOWNLAD, value);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -233,6 +243,24 @@ class ProtocolConfig {
|
||||
*/
|
||||
public void setDebug(boolean 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)
|
||||
delaySeconds = DEFAULT_UPDATER_DELAY;
|
||||
setConfig(updater, UPDATER_DELAY, delaySeconds);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -275,6 +304,7 @@ class ProtocolConfig {
|
||||
*/
|
||||
public void setIgnoreVersionCheck(String ignoreVersion) {
|
||||
setConfig(global, IGNORE_VERSION_CHECK, ignoreVersion);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -294,6 +324,7 @@ class ProtocolConfig {
|
||||
*/
|
||||
public void setMetricsEnabled(boolean enabled) {
|
||||
setConfig(global, METRICS_ENABLED, enabled);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -313,6 +344,7 @@ class ProtocolConfig {
|
||||
*/
|
||||
public void setBackgroundCompilerEnabled(boolean 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.
|
||||
* <p>
|
||||
* Note that this is not considered to modify the configuration, so the modification count
|
||||
* will not be incremented.
|
||||
* @param lastTimeSeconds - new last update time.
|
||||
*/
|
||||
public void setAutoLastTime(long lastTimeSeconds) {
|
||||
@ -348,6 +383,7 @@ class ProtocolConfig {
|
||||
*/
|
||||
public void setScriptEngineName(String name) {
|
||||
setConfig(global, SCRIPT_ENGINE_NAME, name);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -380,6 +416,15 @@ class ProtocolConfig {
|
||||
*/
|
||||
public void setInjectionMethod(PlayerInjectHooks hook) {
|
||||
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.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
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.error.BasicErrorReporter;
|
||||
import com.comphenix.protocol.error.DelegatedErrorReporter;
|
||||
import com.comphenix.protocol.error.DetailedErrorReporter;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
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.utility.ChatExtensions;
|
||||
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.
|
||||
@ -136,12 +141,12 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
|
||||
// Add global parameters
|
||||
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
|
||||
reporter = detailedReporter;
|
||||
reporter = getFilteredReporter(detailedReporter);
|
||||
|
||||
try {
|
||||
config = new ProtocolConfig(this);
|
||||
} 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
|
||||
if (deleteConfig()) {
|
||||
@ -168,7 +173,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
|
||||
unhookTask = new DelayedSingleTask(this);
|
||||
protocolManager = new PacketFilterManager(
|
||||
getClassLoader(), getServer(), this, version, unhookTask, detailedReporter);
|
||||
getClassLoader(), getServer(), this, version, unhookTask, reporter);
|
||||
|
||||
// Setup error reporter
|
||||
detailedReporter.addGlobalParameter("manager", protocolManager);
|
||||
@ -183,23 +188,52 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
protocolManager.setPlayerHook(hook);
|
||||
}
|
||||
} 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
|
||||
commandProtocol = new CommandProtocol(detailedReporter, this, updater, config);
|
||||
commandFilter = new CommandFilter(detailedReporter, this, config);
|
||||
commandPacket = new CommandPacket(detailedReporter, this, logger, commandFilter, protocolManager);
|
||||
commandProtocol = new CommandProtocol(reporter, this, updater, config);
|
||||
commandFilter = new CommandFilter(reporter, this, config);
|
||||
commandPacket = new CommandPacket(reporter, this, logger, commandFilter, protocolManager);
|
||||
|
||||
// Send logging information to player listeners too
|
||||
setupBroadcastUsers(PERMISSION_INFO);
|
||||
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return config.getFile().delete();
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.
|
||||
@ -16,6 +18,9 @@ import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
public class ReportType {
|
||||
private final String errorFormat;
|
||||
|
||||
// Used to store the report name
|
||||
protected String reportName;
|
||||
|
||||
/**
|
||||
* Construct a new report type.
|
||||
* @param errorFormat - string used to format the underlying report.
|
||||
@ -41,26 +46,70 @@ public class ReportType {
|
||||
return errorFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the full canonical name of a given report type.
|
||||
* <p>
|
||||
* This is in the format <i>canonical_name_of_class#report_type</i>
|
||||
* @param clazz - the sender class.
|
||||
* @param type - the report instance.
|
||||
* @return The full canonical name.
|
||||
*/
|
||||
public static String getReportName(Class<?> sender, ReportType type) {
|
||||
if (sender == null)
|
||||
throw new IllegalArgumentException("sender cannot be NUll.");
|
||||
|
||||
// Whether or not we need to retrieve the report name again
|
||||
if (type.reportName == null) {
|
||||
for (Field field : getReportFields(sender)) {
|
||||
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 clazz - sender class.
|
||||
* @param sender - sender class.
|
||||
* @return All associated reports.
|
||||
*/
|
||||
public static ReportType[] getReports(Class<?> clazz) {
|
||||
if (clazz == null)
|
||||
throw new IllegalArgumentException("clazz cannot be NULL.");
|
||||
public static ReportType[] getReports(Class<?> sender) {
|
||||
if (sender == null)
|
||||
throw new IllegalArgumentException("sender cannot be NULL.");
|
||||
List<ReportType> result = new ArrayList<ReportType>();
|
||||
|
||||
for (Field field : clazz.getFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers()) &&
|
||||
ReportType.class.isAssignableFrom(field.getDeclaringClass())) {
|
||||
try {
|
||||
result.add((ReportType) field.get(null));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Unable to access field.", e);
|
||||
}
|
||||
// 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -286,42 +286,10 @@ public class PacketContainer implements Serializable {
|
||||
* @return A modifier for ItemStack array fields.
|
||||
*/
|
||||
public StructureModifier<ItemStack[]> getItemArrayModifier() {
|
||||
|
||||
final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
|
||||
|
||||
// Convert to and from the Bukkit wrapper
|
||||
return structureModifier.<ItemStack[]>withType(
|
||||
MinecraftReflection.getItemStackArrayClass(),
|
||||
BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() {
|
||||
|
||||
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;
|
||||
}
|
||||
}));
|
||||
BukkitConverters.getIgnoreNull(new ItemStackArrayConverter()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -556,4 +524,40 @@ public class PacketContainer implements Serializable {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,19 +173,7 @@ class NetworkServerInjector extends PlayerInjector {
|
||||
// Share callback filter - that way, we avoid generating a new class for
|
||||
// every logged in player.
|
||||
if (callbackFilter == null) {
|
||||
final Method sendPacket = MinecraftMethods.getSendPacketMethod();
|
||||
|
||||
callbackFilter = new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
if (isCallableEqual(sendPacket, method)) {
|
||||
foundSendPacket = true;
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
callbackFilter = new SendMethodFilter();
|
||||
}
|
||||
|
||||
ex.setClassLoader(classLoader);
|
||||
@ -229,20 +217,6 @@ class NetworkServerInjector extends PlayerInjector {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param first - first method.
|
||||
* @param second - second method.
|
||||
* @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());
|
||||
}
|
||||
|
||||
private Object getProxyServerHandler() {
|
||||
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
|
||||
try {
|
||||
@ -349,4 +323,36 @@ class NetworkServerInjector extends PlayerInjector {
|
||||
public PlayerInjectHooks getHookType() {
|
||||
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a CallbackFilter that only matches the SendPacket method.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static class SendMethodFilter implements CallbackFilter {
|
||||
private Method sendPacket = MinecraftMethods.getSendPacketMethod();
|
||||
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
if (isCallableEqual(sendPacket, method)) {
|
||||
NetworkServerInjector.foundSendPacket = true;
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param first - first method.
|
||||
* @param second - second method.
|
||||
* @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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ public abstract class PlayerInjector implements SocketInjector {
|
||||
public static final ReportType REPORT_CANNOT_UPDATE_PLAYER = new ReportType("Cannot update player in PlayerEvent.");
|
||||
public static final ReportType REPORT_CANNOT_HANDLE_PACKET = new ReportType("Cannot handle server packet.");
|
||||
|
||||
public static final ReportType REPORT_INVALID_NETWORK_MANAGER = new ReportType("NetworkManager doesn't appear to be valid.");
|
||||
|
||||
// Net login handler stuff
|
||||
private static Field netLoginNetworkField;
|
||||
|
||||
@ -296,6 +298,9 @@ public abstract class PlayerInjector implements SocketInjector {
|
||||
return socketAddress;
|
||||
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// Inform about the state of the network manager too
|
||||
reporter.reportWarning(
|
||||
this, Report.newBuilder(REPORT_INVALID_NETWORK_MANAGER).callerParam(networkManager).build());
|
||||
throw new IllegalAccessException("Unable to read the socket address field.");
|
||||
}
|
||||
}
|
||||
@ -358,13 +363,16 @@ public abstract class PlayerInjector implements SocketInjector {
|
||||
|
||||
private Field getProxyField(Object notchEntity, Field serverField) {
|
||||
try {
|
||||
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
|
||||
Object currentHandler = FieldUtils.readField(serverHandlerField, notchEntity, true);
|
||||
|
||||
// Is this a Minecraft hook?
|
||||
if (handler != null && !MinecraftReflection.isMinecraftObject(handler)) {
|
||||
// This is bad
|
||||
if (currentHandler == null)
|
||||
throw new IllegalAccessError("Unable to fetch server handler: was NUll.");
|
||||
|
||||
// See if this isn't a standard net handler class
|
||||
if (!isStandardMinecraftNetHandler(currentHandler)) {
|
||||
// This is our proxy object
|
||||
if (handler instanceof Factory)
|
||||
if (currentHandler instanceof Factory)
|
||||
return null;
|
||||
|
||||
hasProxyType = true;
|
||||
@ -372,7 +380,7 @@ public abstract class PlayerInjector implements SocketInjector {
|
||||
|
||||
// No? Is it a Proxy type?
|
||||
try {
|
||||
FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true);
|
||||
FuzzyReflection reflection = FuzzyReflection.fromObject(currentHandler, true);
|
||||
|
||||
// It might be
|
||||
return reflection.getFieldByType("NetServerHandler", MinecraftReflection.getNetServerHandlerClass());
|
||||
@ -390,6 +398,20 @@ public abstract class PlayerInjector implements SocketInjector {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given object is a standard Minecraft net handler.
|
||||
* @param obj the object to test.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
private boolean isStandardMinecraftNetHandler(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
Class<?> clazz = obj.getClass();
|
||||
|
||||
return MinecraftReflection.getNetLoginHandlerClass().equals(clazz) ||
|
||||
MinecraftReflection.getNetServerHandlerClass().equals(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current net handler for this player.
|
||||
* @return Current net handler.
|
||||
|
@ -18,6 +18,7 @@
|
||||
package com.comphenix.protocol.injector.player;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
@ -81,7 +82,7 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
||||
private NetLoginInjector netLoginInjector;
|
||||
|
||||
// The last successful player hook
|
||||
private PlayerInjector lastSuccessfulHook;
|
||||
private WeakReference<PlayerInjector> lastSuccessfulHook;
|
||||
|
||||
// Dummy injection
|
||||
private Cache<Player, PlayerInjector> dummyInjectors =
|
||||
@ -343,6 +344,11 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
||||
|
||||
// Get socket and socket injector
|
||||
SocketAddress address = injector.getAddress();
|
||||
|
||||
// Ignore logged out players
|
||||
if (address == null)
|
||||
return null;
|
||||
|
||||
SocketInjector previous = inputStreamLookup.peekSocketInjector(address);
|
||||
|
||||
// Close any previously associated hooks before we proceed
|
||||
@ -394,7 +400,7 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
||||
|
||||
// Update values
|
||||
if (injector != null)
|
||||
lastSuccessfulHook = injector;
|
||||
lastSuccessfulHook = new WeakReference<PlayerInjector>(injector);
|
||||
if (permanentHook != getPlayerHook(phase))
|
||||
setPlayerHook(phase, tempHook);
|
||||
|
||||
@ -432,13 +438,18 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
||||
|
||||
@Override
|
||||
public void updatePlayer(Player player) {
|
||||
SocketInjector injector = inputStreamLookup.peekSocketInjector(player.getAddress());
|
||||
SocketAddress address = player.getAddress();
|
||||
|
||||
if (injector != null) {
|
||||
injector.setUpdatedPlayer(player);
|
||||
} else {
|
||||
inputStreamLookup.setSocketInjector(player.getAddress(),
|
||||
new BukkitSocketInjector(player));
|
||||
// Ignore logged out players
|
||||
if (address != null) {
|
||||
SocketInjector injector = inputStreamLookup.peekSocketInjector(address);
|
||||
|
||||
if (injector != null) {
|
||||
injector.setUpdatedPlayer(player);
|
||||
} else {
|
||||
inputStreamLookup.setSocketInjector(player.getAddress(),
|
||||
new BukkitSocketInjector(player));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,13 +650,23 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
||||
@Override
|
||||
public void checkListener(Set<PacketListener> listeners) {
|
||||
// Make sure the current listeners are compatible
|
||||
if (lastSuccessfulHook != null) {
|
||||
if (getLastSuccessfulHook() != null) {
|
||||
for (PacketListener listener : listeners) {
|
||||
checkListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last successful hook.
|
||||
* <p>
|
||||
* May be NULL if the hook has been uninjected.
|
||||
* @return Last successful hook.
|
||||
*/
|
||||
private PlayerInjector getLastSuccessfulHook() {
|
||||
return lastSuccessfulHook != null ? lastSuccessfulHook.get() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a listener is valid or not.
|
||||
* <p>
|
||||
@ -654,8 +675,10 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
||||
*/
|
||||
@Override
|
||||
public void checkListener(PacketListener listener) {
|
||||
if (lastSuccessfulHook != null) {
|
||||
UnsupportedListener result = lastSuccessfulHook.checkListener(version, listener);
|
||||
PlayerInjector last = getLastSuccessfulHook();
|
||||
|
||||
if (last != null) {
|
||||
UnsupportedListener result = last.checkListener(version, listener);
|
||||
|
||||
// We won't prevent the listener, as it may still have valid packets
|
||||
if (result != null) {
|
||||
|
@ -5,7 +5,6 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
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.utility.MinecraftReflection;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Offload all the work to Spigot, if possible.
|
||||
@ -59,6 +59,42 @@ public class SpigotPacketInjector implements SpigotPacketListener {
|
||||
*/
|
||||
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.
|
||||
* @return The listener class.
|
||||
@ -107,42 +143,6 @@ public class SpigotPacketInjector implements SpigotPacketListener {
|
||||
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.
|
||||
* @param plugin - the parent plugin.
|
||||
@ -455,7 +455,7 @@ public class SpigotPacketInjector implements SpigotPacketListener {
|
||||
// Clean up
|
||||
playerInjector.remove(injector.getPlayer());
|
||||
playerInjector.remove(injector.getUpdatedPlayer());
|
||||
networkManagerInjector.remove(injector);
|
||||
networkManagerInjector.remove(injector.getNetworkManager());
|
||||
}
|
||||
}, CLEANUP_DELAY);
|
||||
}
|
||||
|
@ -23,3 +23,5 @@ global:
|
||||
|
||||
# The engine used by the filter command
|
||||
script engine: JavaScript
|
||||
|
||||
suppressed reports:
|
@ -1,5 +1,5 @@
|
||||
name: ProtocolLib
|
||||
version: 2.4.3
|
||||
version: 2.4.5
|
||||
description: Provides read/write access to the Minecraft protocol.
|
||||
author: Comphenix
|
||||
website: http://www.comphenix.net/ProtocolLib
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren