Archiviert
13
0

Merge branch 'master' into gh-pages

Dieser Commit ist enthalten in:
Kristian S. Stangeland 2012-12-27 17:41:23 +01:00
Commit 044a58f657
53 geänderte Dateien mit 2628 neuen und 230 gelöschten Zeilen

Datei anzeigen

@ -15,9 +15,15 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>net.sourceforge.metrics.builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>net.sourceforge.metrics.nature</nature>
</natures>
</projectDescription>

Datei anzeigen

@ -4,7 +4,7 @@
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>1.8.0</version>
<version>1.9.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://dev.bukkit.org/server-mods/protocollib/</url>
<developers>
@ -139,7 +139,7 @@
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>1.4.5-R0.3-SNAPSHOT</version>
<version>1.4.6-R0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
@ -154,6 +154,36 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>powermock-module-junit4-common</artifactId>
<groupId>org.powermock</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>powermock-api-support</artifactId>
<groupId>org.powermock</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<reporting>
<plugins>
@ -181,6 +211,7 @@
</snapshotRepository>
</distributionManagement>
<properties>
<powermock.version>1.5</powermock.version>
<project.build.sourceEncoding>cp1252</project.build.sourceEncoding>
</properties>
</project>

Datei anzeigen

@ -2,12 +2,13 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>1.8.0</version>
<version>1.9.0</version>
<packaging>jar</packaging>
<description>Provides read/write access to the Minecraft protocol.</description>
<properties>
<project.build.sourceEncoding>cp1252</project.build.sourceEncoding>
<powermock.version>1.5</powermock.version>
</properties>
<distributionManagement>
@ -202,7 +203,7 @@
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>1.4.5-R0.3-SNAPSHOT</version>
<version>1.4.6-R0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
@ -211,5 +212,23 @@
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.lang.reflect.Field;
@ -13,7 +30,7 @@ import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.reflect.ObjectCloner;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.StructureCompiler;
import com.comphenix.protocol.reflect.instances.CollectionGenerator;
@ -48,7 +65,7 @@ class CleanupStaticMembers {
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class,
BackgroundCompiler.class, StructureCompiler.class,
ObjectCloner.class, Packets.Server.class, Packets.Client.class,
ObjectWriter.class, Packets.Server.class, Packets.Client.class,
ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class
};

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import org.bukkit.ChatColor;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.lang.reflect.InvocationTargetException;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import org.bukkit.ChatColor;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.util.regex.Matcher;
@ -7,6 +24,7 @@ import org.bukkit.Server;
import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
/**
* Determine the current Minecraft version.
@ -22,6 +40,9 @@ class MinecraftVersion implements Comparable<MinecraftVersion> {
private final int major;
private final int minor;
private final int build;
// The development stage
private final String development;
/**
* Determine the current Minecraft version.
@ -36,11 +57,13 @@ class MinecraftVersion implements Comparable<MinecraftVersion> {
* @param versionOnly - the version in text form.
*/
public MinecraftVersion(String versionOnly) {
int[] numbers = parseVersion(versionOnly);
String[] section = versionOnly.split("-");
int[] numbers = parseVersion(section[0]);
this.major = numbers[0];
this.minor = numbers[1];
this.build = numbers[2];
this.development = section.length > 1 ? section[1] : null;
}
/**
@ -50,9 +73,21 @@ class MinecraftVersion implements Comparable<MinecraftVersion> {
* @param build - build version number.
*/
public MinecraftVersion(int major, int minor, int build) {
this(major, minor, build, null);
}
/**
* Construct a version object directly.
* @param major - major version number.
* @param minor - minor version number.
* @param build - build version number.
* @param development - development stage.
*/
public MinecraftVersion(int major, int minor, int build, String development) {
this.major = major;
this.minor = minor;
this.build = build;
this.development = development;
}
private int[] parseVersion(String version) {
@ -65,7 +100,7 @@ class MinecraftVersion implements Comparable<MinecraftVersion> {
// The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively.
for (int i = 0; i < Math.min(numbers.length, elements.length); i++)
numbers[i] = Integer.parseInt(elements[i].trim());
numbers[i] = Integer.parseInt(elements[i].trim());
return numbers;
}
@ -93,12 +128,23 @@ class MinecraftVersion implements Comparable<MinecraftVersion> {
return build;
}
/**
* Retrieve the development stage.
* @return Development stage, or NULL if this is a release.
*/
public String getDevelopmentStage() {
return development;
}
/**
* Retrieve the version String (major.minor.build) only.
* @return A normal version string.
*/
public String getVersion() {
return String.format("%s.%s.%s", major, minor, build);
if (development == null)
return String.format("%s.%s.%s", major, minor, build);
else
return String.format("%s.%s.%s-%s", major, minor, build, development);
}
@Override
@ -110,6 +156,8 @@ class MinecraftVersion implements Comparable<MinecraftVersion> {
compare(major, o.major).
compare(minor, o.minor).
compare(build, o.build).
// No development String means it's a release
compare(development, o.development, Ordering.natural().nullsLast()).
result();
}
@ -125,7 +173,8 @@ class MinecraftVersion implements Comparable<MinecraftVersion> {
return major == other.major &&
minor == other.minor &&
build == other.build;
build == other.build &&
Objects.equal(development, other.development);
}
return false;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.io.File;

Datei anzeigen

@ -17,11 +17,14 @@
package com.comphenix.protocol;
import java.io.File;
import java.io.IOException;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Server;
import org.bukkit.command.CommandExecutor;
@ -52,7 +55,7 @@ public class ProtocolLibrary extends JavaPlugin {
/**
* The maximum version ProtocolLib has been tested with,
*/
private static final String MAXIMUM_MINECRAFT_VERSION = "1.4.5";
private static final String MAXIMUM_MINECRAFT_VERSION = "1.4.6";
/**
* The number of milliseconds per second.
@ -65,7 +68,7 @@ public class ProtocolLibrary extends JavaPlugin {
private static PacketFilterManager protocolManager;
// Error reporter
private ErrorReporter reporter;
private static ErrorReporter reporter;
// Metrics and statistisc
private Statistics statistisc;
@ -97,6 +100,9 @@ public class ProtocolLibrary extends JavaPlugin {
private CommandProtocol commandProtocol;
private CommandPacket commandPacket;
// Whether or not disable is not needed
private boolean skipDisable;
@Override
public void onLoad() {
// Load configuration
@ -104,7 +110,6 @@ public class ProtocolLibrary extends JavaPlugin {
// Add global parameters
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
updater = new Updater(this, logger, "protocollib", getFile(), "protocol.info");
reporter = detailedReporter;
try {
@ -121,6 +126,12 @@ public class ProtocolLibrary extends JavaPlugin {
}
try {
// Check for other versions
checkConflictingVersions();
// Set updater
updater = new Updater(this, logger, "protocollib", getFile(), "protocol.info");
unhookTask = new DelayedSingleTask(this);
protocolManager = new PacketFilterManager(getClassLoader(), getServer(), unhookTask, detailedReporter);
detailedReporter.addGlobalParameter("manager", protocolManager);
@ -252,6 +263,45 @@ public class ProtocolLibrary extends JavaPlugin {
reporter.reportWarning(this, "Unable to retrieve current Minecraft version.", e);
}
}
private void checkConflictingVersions() {
Pattern ourPlugin = Pattern.compile("ProtocolLib-(.*)\\.jar");
MinecraftVersion currentVersion = new MinecraftVersion(this.getDescription().getVersion());
MinecraftVersion newestVersion = null;
try {
// Scan the plugin folder for newer versions of ProtocolLib
File pluginFolder = new File("plugins/");
for (File candidate : pluginFolder.listFiles()) {
if (candidate.isFile()) {
Matcher match = ourPlugin.matcher(candidate.getName());
if (match.matches()) {
MinecraftVersion version = new MinecraftVersion(match.group(1));
if (newestVersion == null || newestVersion.compareTo(version) < 0) {
newestVersion = version;
}
}
}
}
} catch (Exception e) {
reporter.reportWarning(this, "Unable to detect conflicting plugin versions.", e);
}
// See if the newest version is actually higher
if (newestVersion != null && currentVersion.compareTo(newestVersion) < 0) {
// We don't need to set internal classes or instances to NULL - that would break the other loaded plugin
skipDisable = true;
throw new IllegalStateException(
String.format("Detected a newer version of ProtocolLib (%s) in plugin folder than the current (%s). Disabling.",
newestVersion.getVersion(), currentVersion.getVersion())
);
}
}
private void registerCommand(String name, CommandExecutor executor) {
try {
@ -331,6 +381,10 @@ public class ProtocolLibrary extends JavaPlugin {
@Override
public void onDisable() {
if (skipDisable) {
return;
}
// Disable compiler
if (backgroundCompiler != null) {
backgroundCompiler.shutdownAll();
@ -353,6 +407,7 @@ public class ProtocolLibrary extends JavaPlugin {
protocolManager.close();
protocolManager = null;
statistisc = null;
reporter = null;
// Leaky ClassLoader begone!
CleanupStaticMembers cleanup = new CleanupStaticMembers(getClassLoader(), reporter);
@ -373,6 +428,14 @@ public class ProtocolLibrary extends JavaPlugin {
return log;
}
/**
* Retrieve the current error reporter.
* @return Current error reporter.
*/
public static ErrorReporter getErrorReporter() {
return reporter;
}
/**
* Retrieves the packet protocol manager.
* @return Packet protocol manager, or NULL if it has been disabled.

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.util.ArrayList;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.async;
import java.util.ArrayList;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.concurrency;
import java.util.HashSet;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.concurrency;
import java.util.Collection;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.error;
import java.io.PrintWriter;
@ -6,6 +23,9 @@ import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -15,6 +35,7 @@ import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.reflect.PrettyPrinter;
import com.google.common.primitives.Primitives;
/**
@ -35,10 +56,14 @@ public class DetailedErrorReporter implements ErrorReporter {
// We don't want to spam the server
public static final int DEFAULT_MAX_ERROR_COUNT = 20;
// Prevent spam per plugin too
private ConcurrentMap<String, AtomicInteger> warningCount = new ConcurrentHashMap<String, AtomicInteger>();
protected String prefix;
protected String supportURL;
protected int errorCount;
protected AtomicInteger internalErrorCount = new AtomicInteger();
protected int maxErrorCount;
protected Logger logger;
@ -97,23 +122,65 @@ public class DetailedErrorReporter implements ErrorReporter {
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) {
reportMinimal(sender, methodName, error);
// Print parameters, if they are given
if (parameters != null && parameters.length > 0) {
logger.log(Level.SEVERE, " Parameters:");
// Print each parameter
for (Object parameter : parameters) {
logger.log(Level.SEVERE, " " + getStringDescription(parameter));
if (reportMinimalNoSpam(sender, methodName, error)) {
// Print parameters, if they are given
if (parameters != null && parameters.length > 0) {
logger.log(Level.SEVERE, " Parameters:");
// Print each parameter
for (Object parameter : parameters) {
logger.log(Level.SEVERE, " " + getStringDescription(parameter));
}
}
}
}
@Override
public void reportMinimal(Plugin sender, String methodName, Throwable error) {
logger.log(Level.SEVERE, "[" + PLUGIN_NAME + "] Unhandled exception occured in " + methodName + " for " +
PacketAdapter.getPluginName(sender), error);
reportMinimalNoSpam(sender, methodName, error);
}
public boolean reportMinimalNoSpam(Plugin sender, String methodName, Throwable error) {
String pluginName = PacketAdapter.getPluginName(sender);
AtomicInteger counter = warningCount.get(pluginName);
// Thread safe pattern
if (counter == null) {
AtomicInteger created = new AtomicInteger();
counter = warningCount.putIfAbsent(pluginName, created);
if (counter == null) {
counter = created;
}
}
final int errorCount = counter.incrementAndGet();
// See if we should print the full error
if (errorCount < getMaxErrorCount()) {
logger.log(Level.SEVERE, "[" + PLUGIN_NAME + "] Unhandled exception occured in " +
methodName + " for " + pluginName, error);
return true;
} else {
// Nope - only print the error count occationally
if (isPowerOfTwo(errorCount)) {
logger.log(Level.SEVERE, "[" + PLUGIN_NAME + "] Unhandled exception number " + errorCount + " occured in " +
methodName + " for " + pluginName, error);
}
return false;
}
}
/**
* Determine if a given number is a power of two.
* <p>
* That is, if there exists an N such that 2^N = number.
* @param number - the number to check.
* @return TRUE if the given number is a power of two, FALSE otherwise.
*/
private boolean isPowerOfTwo(int number) {
return (number & (number - 1)) == 0;
}
@Override
@ -137,12 +204,18 @@ public class DetailedErrorReporter implements ErrorReporter {
public void reportDetailed(Object sender, String message, Throwable error, Object... parameters) {
final Plugin plugin = pluginReference.get();
final int errorCount = internalErrorCount.incrementAndGet();
// Do not overtly spam the server!
if (++errorCount > maxErrorCount) {
String maxReached = String.format("Reached maxmimum error count. Cannot pass error %s from %s.", error, sender);
logger.severe(maxReached);
return;
if (errorCount > getMaxErrorCount()) {
// Only allow the error count at rare occations
if (isPowerOfTwo(errorCount)) {
// Permit it - but print the number of exceptions first
reportWarning(this, "Internal exception count: " + errorCount + "!");
} else {
// NEVER SPAM THE CONSOLE
return;
}
}
StringWriter text = new StringWriter();
@ -230,12 +303,15 @@ public class DetailedErrorReporter implements ErrorReporter {
return (ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null));
} catch (Throwable ex) {
// Apache is probably missing
logger.warning("Cannot find Apache Commons. Object introspection disabled.");
apacheCommonsMissing = true;
}
// Just use toString()
return String.format("%s", value);
// Use our custom object printer instead
try {
return PrettyPrinter.printObject(value, value.getClass(), Object.class);
} catch (IllegalAccessException e) {
return "[Error: " + e.getMessage() + "]";
}
}
}
@ -249,11 +325,11 @@ public class DetailedErrorReporter implements ErrorReporter {
}
public int getErrorCount() {
return errorCount;
return internalErrorCount.get();
}
public void setErrorCount(int errorCount) {
this.errorCount = errorCount;
internalErrorCount.set(errorCount);
}
public int getMaxErrorCount() {

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.error;
import org.bukkit.plugin.Plugin;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.events;
import java.util.logging.Level;

Datei anzeigen

@ -17,20 +17,21 @@
package com.comphenix.protocol.events;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nullable;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
@ -39,12 +40,22 @@ import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
import com.comphenix.protocol.reflect.cloning.BukkitCloner;
import com.comphenix.protocol.reflect.cloning.Cloner;
import com.comphenix.protocol.reflect.cloning.CollectionCloner;
import com.comphenix.protocol.reflect.cloning.FieldCloner;
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
/**
@ -69,6 +80,28 @@ public class PacketContainer implements Serializable {
private static ConcurrentMap<Class<?>, Method> writeMethods = Maps.newConcurrentMap();
private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
// Used to clone packets
private static final AggregateCloner DEEP_CLONER = AggregateCloner.newBuilder().
instanceProvider(DefaultInstances.DEFAULT).
andThen(BukkitCloner.class).
andThen(ImmutableDetector.class).
andThen(CollectionCloner.class).
andThen(getSpecializedDeepClonerFactory()).
build();
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner.newBuilder().
instanceProvider(DefaultInstances.DEFAULT).
andThen(new Function<BuilderParameters, Cloner>() {
@Override
public Cloner apply(@Nullable BuilderParameters param) {
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
// Use a default writer with no concept of cloning
writer = new ObjectWriter();
}};
}
}).
build();
/**
* Creates a packet container for a new packet.
* @param id - ID of the packet to create.
@ -241,12 +274,12 @@ public class PacketContainer implements Serializable {
BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() {
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
Object[] result = new Object[specific.length];
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(
MinecraftReflection.getItemStackClass(), specific[i]);
result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
}
return result;
}
@ -364,40 +397,54 @@ public class PacketContainer implements Serializable {
return id;
}
/**
* Create a shallow copy of the current packet.
* <p>
* This merely writes the content of each field to the new class directly,
* without performing any expensive copies.
*
* @return A shallow copy of the current packet.
*/
public PacketContainer shallowClone() {
Object clonedPacket = SHALLOW_CLONER.clone(getHandle());
return new PacketContainer(getID(), clonedPacket);
}
/**
* Create a deep copy of the current packet.
* <p>
* This will perform a full copy of the entire object tree, only skipping
* known immutable objects and primitive types.
* <p>
* Note that the inflated buffers in packet 51 and 56 will be copied directly to save memory.
*
* @return A deep copy of the current packet.
*/
public PacketContainer deepClone() {
ObjectOutputStream output = null;
ObjectInputStream input = null;
try {
// Use a small buffer of 32 bytes initially.
ByteArrayOutputStream bufferOut = new ByteArrayOutputStream();
output = new ObjectOutputStream(bufferOut);
output.writeObject(this);
ByteArrayInputStream bufferIn = new ByteArrayInputStream(bufferOut.toByteArray());
input = new ObjectInputStream(bufferIn);
return (PacketContainer) input.readObject();
} catch (IOException e) {
throw new IllegalStateException("Unexpected error occured during object cloning.", e);
} catch (ClassNotFoundException e) {
// Cannot happen
throw new IllegalStateException("Unexpected failure with serialization.", e);
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException e) {
// STOP IT
Object clonedPacket = DEEP_CLONER.clone(getHandle());
return new PacketContainer(getID(), clonedPacket);
}
// To save space, we'll skip copying the inflated buffers in packet 51 and 56
private static Function<BuilderParameters, Cloner> getSpecializedDeepClonerFactory() {
// Look at what you've made me do Java, look at it!!
return new Function<BuilderParameters, Cloner>() {
@Override
public Cloner apply(@Nullable BuilderParameters param) {
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
this.writer = new ObjectWriter() {
protected void transformField(StructureModifier<Object> modifierSource,
StructureModifier<Object> modifierDest, int fieldIndex) {
// No need to clone inflated buffers
if (modifierSource.getField(fieldIndex).getName().startsWith("inflatedBuffer"))
modifierDest.write(fieldIndex, modifierSource.read(fieldIndex));
else
defaultTransform(modifierSource, modifierDest, getDefaultCloner(), fieldIndex);
};
};
}};
}
}
};
}
private void writeObject(ObjectOutputStream output) throws IOException {

Datei anzeigen

@ -17,14 +17,18 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.google.common.primitives.Primitives;
/**
* Represents an object capable of converting wrapped Bukkit objects into NMS objects.
@ -38,41 +42,33 @@ import com.comphenix.protocol.reflect.instances.DefaultInstances;
* @author Kristian
*/
public class BukkitUnwrapper implements Unwrapper {
private static Map<Class<?>, Unwrapper> unwrapperCache = new ConcurrentHashMap<Class<?>, Unwrapper>();
private static Map<Class<?>, Method> cache = new ConcurrentHashMap<Class<?>, Method>();
@SuppressWarnings("unchecked")
@Override
public Object unwrapItem(Object wrappedObject) {
// Special cases
if (wrappedObject == null) {
// Special case
if (wrappedObject == null)
return null;
} else if (wrappedObject instanceof Collection) {
return handleCollection((Collection<Object>) wrappedObject);
}
Class<?> currentClass = wrappedObject.getClass();
Method cachedMethod = initializeCache(currentClass);
try {
// Retrieve the handle
if (cachedMethod != null)
return cachedMethod.invoke(wrappedObject);
else
return null;
} catch (IllegalArgumentException e) {
// Impossible
// Next, check for types that doesn't have a getHandle()
if (wrappedObject instanceof Collection) {
return handleCollection((Collection<Object>) wrappedObject);
} else if (Primitives.isWrapperType(currentClass) || wrappedObject instanceof String) {
return null;
} catch (IllegalAccessException e) {
return null;
} catch (InvocationTargetException e) {
// This is REALLY bad
throw new RuntimeException("Minecraft error.", e);
}
Unwrapper specificUnwrapper = getSpecificUnwrapper(currentClass);
// Retrieve the handle
if (specificUnwrapper != null)
return specificUnwrapper.unwrapItem(wrappedObject);
else
return null;
}
// Handle a collection of items
private Object handleCollection(Collection<Object> wrappedObject) {
@SuppressWarnings("unchecked")
@ -91,24 +87,94 @@ public class BukkitUnwrapper implements Unwrapper {
}
}
private Method initializeCache(Class<?> type) {
/**
* Retrieve a cached class unwrapper for the given class.
* @param type - the type of the class.
* @return An unwrapper for the given class.
*/
private Unwrapper getSpecificUnwrapper(Class<?> type) {
// See if we're already determined this
if (cache.containsKey(type)) {
if (unwrapperCache.containsKey(type)) {
// We will never remove from the cache, so this ought to be thread safe
return cache.get(type);
return unwrapperCache.get(type);
}
try {
Method find = type.getMethod("getHandle");
final Method find = type.getMethod("getHandle");
// It's thread safe, as getMethod should return the same handle
cache.put(type, find);
return find;
Unwrapper methodUnwrapper = new Unwrapper() {
@Override
public Object unwrapItem(Object wrappedObject) {
try {
return find.invoke(wrappedObject);
} catch (IllegalArgumentException e) {
ProtocolLibrary.getErrorReporter().reportDetailed(
this, "Illegal argument.", e, wrappedObject, find);
} catch (IllegalAccessException e) {
// Should not occur either
return null;
} catch (InvocationTargetException e) {
// This is really bad
throw new RuntimeException("Minecraft error.", e);
}
return null;
}
};
unwrapperCache.put(type, methodUnwrapper);
return methodUnwrapper;
} catch (SecurityException e) {
return null;
ProtocolLibrary.getErrorReporter().reportDetailed(this, "Security limitation.", e, type.getName());
} catch (NoSuchMethodException e) {
// Try getting the field unwrapper too
Unwrapper fieldUnwrapper = getFieldUnwrapper(type);
if (fieldUnwrapper != null)
return fieldUnwrapper;
else
ProtocolLibrary.getErrorReporter().reportDetailed(this, "Cannot find method.", e, type.getName());
}
// Default method
return null;
}
/**
* Retrieve a cached unwrapper using the handle field.
* @param type - a cached field unwrapper.
* @return The cached field unwrapper.
*/
private Unwrapper getFieldUnwrapper(Class<?> type) {
final Field find = FieldUtils.getField(type, "handle", true);
// See if we succeeded
if (find != null) {
Unwrapper fieldUnwrapper = new Unwrapper() {
@Override
public Object unwrapItem(Object wrappedObject) {
try {
return FieldUtils.readField(find, wrappedObject, true);
} catch (IllegalAccessException e) {
ProtocolLibrary.getErrorReporter().reportDetailed(
this, "Cannot read field 'handle'.", e, wrappedObject, find.getName());
return null;
}
}
};
unwrapperCache.put(type, fieldUnwrapper);
return fieldUnwrapper;
} else {
// Inform about this too
ProtocolLibrary.getErrorReporter().reportDetailed(
this, "Could not find field 'handle'.",
new Exception("Unable to find 'handle'"), type.getName());
return null;
}
}

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
import org.bukkit.plugin.Plugin;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
/**

Datei anzeigen

@ -182,6 +182,22 @@ class MinecraftRegistry {
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
}
/**
* Retrieve the packet ID of a given packet.
* @param packet - the type of packet to check.
* @return The ID of the given packet.
* @throws IllegalArgumentException If this is not a valid packet.
*/
public static int getPacketID(Class<?> packet) {
if (packet == null)
throw new IllegalArgumentException("Packet type class cannot be NULL.");
if (!MinecraftReflection.getPacketClass().isAssignableFrom(packet))
throw new IllegalArgumentException("Type must be a packet.");
// The registry contains both the overridden and original packets
return getPacketToID().get(packet);
}
/**
* Find the first superclass that is not a CBLib proxy object.
* @param clazz - the class whose hierachy we're going to search through.

Datei anzeigen

@ -64,6 +64,27 @@ public class StructureCache {
return getStructure(id, true);
}
/**
* Retrieve a cached structure modifier given a packet type.
* @param packetType - packet type.
* @return A structure modifier.
*/
public static StructureModifier<Object> getStructure(Class<?> packetType) {
// Compile structures by default
return getStructure(packetType, true);
}
/**
* Retrieve a cached structure modifier given a packet type.
* @param packetType - packet type.
* @param compile - whether or not to asynchronously compile the structure modifier.
* @return A structure modifier.
*/
public static StructureModifier<Object> getStructure(Class<?> packetType, boolean compile) {
// Get the ID from the class
return getStructure(MinecraftRegistry.getPacketID(packetType), compile);
}
/**
* Retrieve a cached structure modifier for the given packet id.
* @param id - packet ID.

Datei anzeigen

@ -29,7 +29,7 @@ import org.bukkit.Server;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectCloner;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.utility.MinecraftReflection;
@ -77,7 +77,8 @@ class InjectedServerConnection {
return;
if (minecraftServerField == null)
minecraftServerField = FuzzyReflection.fromObject(server, true).getFieldByType(".*MinecraftServer");
minecraftServerField = FuzzyReflection.fromObject(server, true).
getFieldByType("MinecraftServer", MinecraftReflection.getMinecraftServerClass());
try {
minecraftServer = FieldUtils.readField(minecraftServerField, server, true);
@ -211,6 +212,9 @@ class InjectedServerConnection {
* Shut up Eclipse!
*/
private static final long serialVersionUID = 2070481080950500367L;
// Object writer we'll use
private final ObjectWriter writer = new ObjectWriter();
@Override
protected void onReplacing(Object inserting, Object replacement) {
@ -218,7 +222,7 @@ class InjectedServerConnection {
if (!(inserting instanceof Factory)) {
// If so, copy the content of the old element to the new
try {
ObjectCloner.copyTo(inserting, replacement, inserting.getClass());
writer.copyTo(inserting, replacement, inserting.getClass());
} catch (Throwable e) {
reporter.reportDetailed(InjectedServerConnection.this, "Cannot copy old " + inserting +
" to new.", e, inserting, replacement);

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.util.Arrays;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.util.concurrent.ConcurrentMap;
@ -8,6 +25,7 @@ import org.bukkit.entity.Player;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.player.TemporaryPlayerFactory.InjectContainer;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
/**
@ -62,9 +80,9 @@ class NetLoginInjector {
} catch (Throwable e) {
// Minecraft can't handle this, so we'll deal with it here
reporter.reportDetailed(this, "Unable to hook NetLoginHandler.", e, inserting);
reporter.reportDetailed(this, "Unable to hook " +
MinecraftReflection.getNetLoginHandlerName() + ".", e, inserting);
return inserting;
}
}
@ -103,7 +121,8 @@ class NetLoginInjector {
} catch (Throwable e) {
// Don't leak this to Minecraft
reporter.reportDetailed(this, "Cannot cleanup NetLoginHandler.", e, removing);
reporter.reportDetailed(this, "Cannot cleanup " +
MinecraftReflection.getNetLoginHandlerName() + ".", e, removing);
}
}
}

Datei anzeigen

@ -38,7 +38,7 @@ import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectCloner;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
@ -66,6 +66,9 @@ public class NetworkServerInjector extends PlayerInjector {
// Whether or not the player has disconnected
private boolean hasDisconnected;
// Used to copy fields
private final ObjectWriter writer = new ObjectWriter();
public NetworkServerInjector(
ClassLoader classLoader, ErrorReporter reporter, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters,
@ -141,7 +144,8 @@ public class NetworkServerInjector extends PlayerInjector {
}
throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
"Cannot hook player: Unable to find a valid constructor for the "
+ MinecraftReflection.getNetServerHandlerClass().getName() + " object.");
}
}
@ -247,7 +251,7 @@ public class NetworkServerInjector extends PlayerInjector {
@Override
protected void cleanHook() {
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
ObjectCloner.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue();
try {

Datei anzeigen

@ -156,7 +156,8 @@ abstract class PlayerInjector {
// Retrieve the server handler
if (serverHandlerField == null) {
serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(".*NetServerHandler");
serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(
"NetServerHandler", MinecraftReflection.getNetServerHandlerClass());
proxyServerField = getProxyField(notchEntity, serverHandlerField);
}
@ -166,7 +167,8 @@ abstract class PlayerInjector {
// Next, get the network manager
if (networkManagerField == null)
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(".*NetworkManager");
networkManagerField = FuzzyReflection.fromObject(serverHandler).
getFieldByType(".*" + MinecraftReflection.getNetworkManagerName());
initializeNetworkManager(networkManagerField, serverHandler);
}
}
@ -181,7 +183,8 @@ abstract class PlayerInjector {
loginHandler = netLoginHandler;
if (netLoginNetworkField == null)
netLoginNetworkField = FuzzyReflection.fromObject(netLoginHandler).getFieldByType(".*NetworkManager");
netLoginNetworkField = FuzzyReflection.fromObject(netLoginHandler).
getFieldByType(".*" + MinecraftReflection.getNetworkManagerName());
initializeNetworkManager(netLoginNetworkField, netLoginHandler);
}
}
@ -341,7 +344,7 @@ abstract class PlayerInjector {
FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true);
// It might be
return reflection.getFieldByType(".*NetServerHandler");
return reflection.getFieldByType("NetServerHandler", MinecraftReflection.getNetServerHandlerClass());
} catch (RuntimeException e) {
// Damn
@ -367,7 +370,7 @@ abstract class PlayerInjector {
try {
if (netHandlerField == null)
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType("net\\.minecraft\\.NetHandler");
getFieldByType("NetHandler", MinecraftReflection.getNetHandlerClass());
} catch (RuntimeException e1) {
// Swallow it
}
@ -398,7 +401,8 @@ abstract class PlayerInjector {
*/
private Object getEntityPlayer(Object netHandler) throws IllegalAccessException {
if (entityPlayerField == null)
entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(".*EntityPlayer");
entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(
"EntityPlayer", MinecraftReflection.getEntityPlayerClass());
return FieldUtils.readField(entityPlayerField, netHandler);
}

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.util.Arrays;

Datei anzeigen

@ -17,47 +17,88 @@
package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Can copy an object field by field.
*
* @author Kristian
*/
public class ObjectCloner {
public class ObjectWriter {
// Cache structure modifiers
@SuppressWarnings("rawtypes")
private static ConcurrentMap<Class, StructureModifier<Object>> cache =
new ConcurrentHashMap<Class, StructureModifier<Object>>();
/**
* Copy every field in object A to object B.
* Retrieve a usable structure modifier for the given object type.
* <p>
* Will attempt to reuse any other structure modifiers we have cached.
* @param type - the type of the object we are modifying.
* @return A structure modifier for the given type.
*/
private StructureModifier<Object> getModifier(Class<?> type) {
Class<?> packetClass = MinecraftReflection.getPacketClass();
// Handle subclasses of the packet class with our custom structure cache
if (!type.equals(packetClass) && packetClass.isAssignableFrom(type)) {
// Delegate to our already existing registry of structure modifiers
return StructureCache.getStructure(type);
}
StructureModifier<Object> modifier = cache.get(type);
// Create the structure modifier if we haven't already
if (modifier == null) {
StructureModifier<Object> value = new StructureModifier<Object>(type, null, false);
modifier = cache.putIfAbsent(type, value);
if (modifier == null)
modifier = value;
}
// And we're done
return modifier;
}
/**
* Copy every field in object A to object B. Each value is copied directly, and is not cloned.
* <p>
* The two objects must have the same number of fields of the same type.
* @param source - fields to copy.
* @param destination - fields to copy to.
* @param commonType - type containing each field to copy.
*/
public static void copyTo(Object source, Object destination, Class<?> commonType) {
public void copyTo(Object source, Object destination, Class<?> commonType) {
// Note that we indicate that public fields will be copied the first time around
copyToInternal(source, destination, commonType, true);
}
/**
* Called for every non-static field that will be copied.
* @param modifierSource - modifier for the original object.
* @param modifierDest - modifier for the new cloned object.
* @param fieldIndex - the current field index.
*/
protected void transformField(StructureModifier<Object> modifierSource, StructureModifier<Object> modifierDest, int fieldIndex) {
Object value = modifierSource.read(fieldIndex);
modifierDest.write(fieldIndex, value);
}
// Internal method that will actually implement the recursion
private void copyToInternal(Object source, Object destination, Class<?> commonType, boolean copyPublic) {
if (source == null)
throw new IllegalArgumentException("Source cannot be NULL");
if (destination == null)
throw new IllegalArgumentException("Destination cannot be NULL");
StructureModifier<Object> modifier = cache.get(commonType);
// Create the structure modifier if we haven't already
if (modifier == null) {
StructureModifier<Object> value = new StructureModifier<Object>(commonType, null, false);
modifier = cache.putIfAbsent(commonType, value);
if (modifier == null)
modifier = value;
}
StructureModifier<Object> modifier = getModifier(commonType);
// Add target
StructureModifier<Object> modifierSource = modifier.withTarget(source);
@ -66,20 +107,20 @@ public class ObjectCloner {
// Copy every field
try {
for (int i = 0; i < modifierSource.size(); i++) {
if (!modifierDest.isReadOnly(i)) {
Object value = modifierSource.read(i);
modifierDest.write(i, value);
}
Field field = modifierSource.getField(i);
int mod = field.getModifiers();
// System.out.println(String.format("Writing value %s to %s",
// value, modifier.getFields().get(i).getName()));
// Skip static fields. We also get the "public" fields fairly often, so we'll skip that.
if (!Modifier.isStatic(mod) && (!Modifier.isPublic(mod) || copyPublic)) {
transformField(modifierSource, modifierDest, i);
}
}
// Copy private fields underneath
Class<?> superclass = commonType.getSuperclass();
if (!superclass.equals(Object.class)) {
copyTo(source, destination, superclass);
copyToInternal(source, destination, superclass, false);
}
} catch (FieldAccessException e) {

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect;
import java.lang.reflect.Array;

Datei anzeigen

@ -201,10 +201,16 @@ public class StructureModifier<TField> {
* @return TRUE if the field by the given index is read-only, FALSE otherwise.
*/
public boolean isReadOnly(int fieldIndex) {
if (fieldIndex < 0 || fieldIndex >= data.size())
throw new IllegalArgumentException("Index parameter is not within [0 - " + data.size() + ")");
return Modifier.isFinal(data.get(fieldIndex).getModifiers());
return Modifier.isFinal(getField(fieldIndex).getModifiers());
}
/**
* Determine if a given field is public or not.
* @param fieldIndex - field index.
* @return TRUE if the field is public, FALSE otherwise.
*/
public boolean isPublic(int fieldIndex) {
return Modifier.isPublic(getField(fieldIndex).getModifiers());
}
/**
@ -499,6 +505,19 @@ public class StructureModifier<TField> {
return ImmutableList.copyOf(data);
}
/**
* Retrieve a field by index.
* @param fieldIndex - index of the field to retrieve.
* @return The field represented with the given index.
* @throws IllegalArgumentException If no field with the given index can be found.
*/
public Field getField(int fieldIndex) {
if (fieldIndex < 0 || fieldIndex >= data.size())
throw new IllegalArgumentException("Index parameter is not within [0 - " + data.size() + ")");
return data.get(fieldIndex);
}
/**
* Retrieve every value stored in the fields of the current type.
* @return Every field value.
@ -560,4 +579,6 @@ public class StructureModifier<TField> {
return result;
}
}

Datei anzeigen

@ -0,0 +1,263 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
import com.comphenix.protocol.reflect.instances.InstanceProvider;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
/**
* Implements a cloning procedure by trying multiple methods in turn until one is successful.
*
* @author Kristian
*/
public class AggregateCloner implements Cloner {
/**
* Supplies the cloner factories with necessary parameters.
*
* @author Kristian
*/
public static class BuilderParameters {
// Can only be modified by the builder
private InstanceProvider instanceProvider;
private Cloner aggregateCloner;
// Used to construct the different types
private InstanceProvider typeConstructor;
private BuilderParameters() {
// Only allow inner classes to construct it.
}
/**
* Retrieve the instance provider last set in the builder.
* @return Current instance provider.
*/
public InstanceProvider getInstanceProvider() {
return instanceProvider;
}
/**
* Retrieve the aggregate cloner that is being built.
* @return The parent cloner.
*/
public Cloner getAggregateCloner() {
return aggregateCloner;
}
}
/**
* Represents a builder for aggregate (combined) cloners.
*
* @author Kristian
*/
public static class Builder {
private List<Function<BuilderParameters, Cloner>> factories = Lists.newArrayList();
private BuilderParameters parameters;
/**
* Create a new aggregate builder.
*/
public Builder() {
this.parameters = new BuilderParameters();
}
/**
* Set the instance provider supplied to all cloners in this builder.
* @param provider - new instance provider.
* @return The current builder.
*/
public Builder instanceProvider(InstanceProvider provider) {
this.parameters.instanceProvider = provider;
return this;
}
/**
* Add the next cloner that will be considered in turn.
* @param type - the type of the next cloner.
* @return This builder.
*/
public Builder andThen(final Class<? extends Cloner> type) {
// Use reflection to generate a factory on the fly
return andThen(new Function<BuilderParameters, Cloner>() {
@Override
public Cloner apply(@Nullable BuilderParameters param) {
Object result = param.typeConstructor.create(type);
if (result == null) {
throw new IllegalStateException("Constructed NULL instead of " + type);
}
if (type.isAssignableFrom(result.getClass()))
return (Cloner) result;
else
throw new IllegalStateException("Constructed " + result.getClass() + " instead of " + type);
}
});
}
/**
* Add the next cloner that will be considered in turn.
* @param factory - factory constructing the next cloner.
* @return This builder.
*/
public Builder andThen(Function<BuilderParameters, Cloner> factory) {
factories.add(factory);
return this;
}
/**
* Build a new aggregate cloner using the supplied values.
* @return A new aggregate cloner.
*/
public AggregateCloner build() {
AggregateCloner newCloner = new AggregateCloner();
// The parameters we will pass to our cloners
Cloner paramCloner = new NullableCloner(newCloner);
InstanceProvider paramProvider = parameters.instanceProvider;
// Initialize parameters
parameters.aggregateCloner = paramCloner;
parameters.typeConstructor = DefaultInstances.fromArray(
ExistingGenerator.fromObjectArray(new Object[] { paramCloner, paramProvider })
);
// Build every cloner in the correct order
List<Cloner> cloners = Lists.newArrayList();
for (int i = 0; i < factories.size(); i++) {
Cloner cloner = factories.get(i).apply(parameters);
// See if we were successful
if (cloner != null)
cloners.add(cloner);
else
throw new IllegalArgumentException(
String.format("Cannot create cloner from %s (%s)", factories.get(i), i)
);
}
// We're done
newCloner.setCloners(cloners);
return newCloner;
}
}
/**
* Represents a default aggregate cloner.
*/
public static final AggregateCloner DEFAULT = newBuilder().
instanceProvider(DefaultInstances.DEFAULT).
andThen(BukkitCloner.class).
andThen(ImmutableDetector.class).
andThen(CollectionCloner.class).
andThen(FieldCloner.class).
build();
// List of clone methods
private List<Cloner> cloners;
private WeakReference<Object> lastObject;
private int lastResult;
/**
* Begins constructing a new aggregate cloner.
* @return A builder for a new aggregate cloner.
*/
public static Builder newBuilder() {
return new Builder();
}
/**
* Construct a new, empty aggregate cloner.
*/
private AggregateCloner() {
// Only used by our builder above.
}
/**
* Retrieves a view of the current list of cloners.
* @return Current cloners.
*/
public List<Cloner> getCloners() {
return Collections.unmodifiableList(cloners);
}
/**
* Set the cloners that will be used.
* @param cloners - the cloners that will be used.
*/
private void setCloners(Iterable<? extends Cloner> cloners) {
this.cloners = Lists.newArrayList(cloners);
}
@Override
public boolean canClone(Object source) {
// Optimize a bit
lastResult = getFirstCloner(source);
lastObject = new WeakReference<Object>(source);
return lastResult >= 0 && lastResult < cloners.size();
}
/**
* Retrieve the index of the first cloner capable of cloning the given object.
* <p>
* Returns an invalid index if no cloner is able to clone the object.
* @param source - the object to clone.
* @return The index of the cloner object.
*/
private int getFirstCloner(Object source) {
for (int i = 0; i < cloners.size(); i++) {
if (cloners.get(i).canClone(source))
return i;
}
return cloners.size();
}
@Override
public Object clone(Object source) {
if (source == null)
throw new IllegalAccessError("source cannot be NULL.");
int index = 0;
// Are we dealing with the same object?
if (lastObject != null && lastObject.get() == source) {
index = lastResult;
} else {
index = getFirstCloner(source);
}
// Make sure the object is valid
if (index < cloners.size()) {
return cloners.get(index).clone(source);
}
// Damn - failure
throw new IllegalArgumentException("Cannot clone " + source + ": No cloner is sutable.");
}
}

Datei anzeigen

@ -0,0 +1,73 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
/**
* Represents an object that can clone a specific list of Bukkit- and Minecraft-related objects.
*
* @author Kristian
*/
public class BukkitCloner implements Cloner {
// List of classes we support
private Class<?>[] clonableClasses = { MinecraftReflection.getItemStackClass(), MinecraftReflection.getChunkPositionClass(),
MinecraftReflection.getDataWatcherClass() };
private int findMatchingClass(Class<?> type) {
// See if is a subclass of any of our supported superclasses
for (int i = 0; i < clonableClasses.length; i++) {
if (clonableClasses[i].isAssignableFrom(type))
return i;
}
return -1;
}
@Override
public boolean canClone(Object source) {
if (source == null)
return false;
return findMatchingClass(source.getClass()) >= 0;
}
@Override
public Object clone(Object source) {
if (source == null)
throw new IllegalArgumentException("source cannot be NULL.");
// Convert to a wrapper
switch (findMatchingClass(source.getClass())) {
case 0:
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone());
case 1:
EquivalentConverter<ChunkPosition> chunkConverter = ChunkPosition.getConverter();
return chunkConverter.getGeneric(clonableClasses[1], chunkConverter.getSpecific(source));
case 2:
EquivalentConverter<WrappedDataWatcher> dataConverter = BukkitConverters.getDataWatcherConverter();
return dataConverter.getGeneric(clonableClasses[2], dataConverter.getSpecific(source).deepClone());
default:
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
}
}
}

Datei anzeigen

@ -0,0 +1,42 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
/**
* Represents an object that is capable of cloning other objects.
*
* @author Kristian
*/
public interface Cloner {
/**
* Determine whether or not the current cloner can clone the given object.
* @param source - the object that is being considered.
* @return TRUE if this cloner can actually clone the given object, FALSE otherwise.
*/
public boolean canClone(Object source);
/**
* Perform the clone.
* <p>
* This method should never be called unless a corresponding {@link #canClone(Object)} returns TRUE.
* @param source - the value to clone.
* @return A cloned value.
* @throws IllegalArgumentException If this cloner cannot perform the clone.
*/
public Object clone(Object source);
}

Datei anzeigen

@ -0,0 +1,200 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
/**
* Attempts to clone collection and array classes.
*
* @author Kristian
*/
public class CollectionCloner implements Cloner {
private final Cloner defaultCloner;
/**
* Constructs a new collection and array cloner with the given inner element cloner.
* @param defaultCloner - default inner element cloner.
*/
public CollectionCloner(Cloner defaultCloner) {
this.defaultCloner = defaultCloner;
}
@Override
public boolean canClone(Object source) {
if (source == null)
return false;
Class<?> clazz = source.getClass();
return Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) || clazz.isArray();
}
@SuppressWarnings("unchecked")
@Override
public Object clone(Object source) {
if (source == null)
throw new IllegalArgumentException("source cannot be NULL.");
Class<?> clazz = source.getClass();
if (source instanceof Collection) {
Collection<Object> copy = cloneConstructor(Collection.class, clazz, source);
// Next, clone each element in the collection
copy.clear();
for (Object element : (Collection<Object>) source) {
copy.add(getClone(element, source));
}
return copy;
} else if (source instanceof Map) {
Map<Object, Object> copy = cloneConstructor(Map.class, clazz, source);
// Next, clone each element in the collection
copy.clear();
for (Entry<Object, Object> element : ((Map<Object, Object>) source).entrySet()) {
Object key = getClone(element.getKey(), source);
Object value = getClone(element.getValue(), source);
copy.put(key, value);
}
return copy;
} else if (clazz.isArray()) {
// Get the length
int lenght = Array.getLength(source);
Class<?> component = clazz.getComponentType();
// Can we speed things up by making a shallow copy instead?
if (ImmutableDetector.isImmutable(component)) {
return clonePrimitive(component, source);
}
// Create a new copy
Object copy = Array.newInstance(clazz.getComponentType(), lenght);
// Set each element
for (int i = 0; i < lenght; i++) {
Object element = Array.get(source, i);
if (defaultCloner.canClone(element))
Array.set(copy, i, defaultCloner.clone(element));
else
throw new IllegalArgumentException("Cannot clone " + element + " in array " + source);
}
// And we're done
return copy;
}
throw new IllegalArgumentException(source + " is not an array nor a Collection.");
}
/**
* Clone an element using the default cloner.
* @param element - the element to clone.
* @param container - where the element is stored.
* @return The cloned element.
*/
private Object getClone(Object element, Object container) {
if (defaultCloner.canClone(element))
return defaultCloner.clone(element);
else
throw new IllegalArgumentException("Cannot clone " + element + " in container " + container);
}
/**
* Clone a primitive or immutable array by calling its clone method.
* @param component - the component type of the array.
* @param source - the array itself.
* @return The cloned array.
*/
private Object clonePrimitive(Class<?> component, Object source) {
// Cast and call the correct version
if (byte.class.equals(component))
return ((byte[]) source).clone();
else if (short.class.equals(component))
return ((short[]) source).clone();
else if (int.class.equals(component))
return ((int[]) source).clone();
else if (long.class.equals(component))
return ((long[]) source).clone();
else if (float.class.equals(component))
return ((float[]) source).clone();
else if (double.class.equals(component))
return ((double[]) source).clone();
else if (char.class.equals(component))
return ((char[]) source).clone();
else if (boolean.class.equals(component))
return ((boolean[]) source).clone();
else
return ((Object[]) source).clone();
}
/**
* Clone an object by calling its clone constructor, or alternatively, a "clone" method.
* @param superclass - the superclass we expect in the clone constructor.
* @param clazz - the class of the object.
* @param source - the object itself.
* @return A cloned object.
*/
@SuppressWarnings("unchecked")
private <T> T cloneConstructor(Class<?> superclass, Class<?> clazz, Object source) {
// Not all collections or maps implement "clone", but most *do* implement the "copy constructor" pattern
try {
Constructor<?> constructCopy = clazz.getConstructor(Collection.class);
return (T) constructCopy.newInstance(source);
} catch (NoSuchMethodException e) {
return (T) cloneObject(clazz, source);
} catch (Exception e) {
throw new RuntimeException("Cannot construct collection.", e);
}
}
/**
* Clone an object by calling "clone" using reflection.
* @param clazz - the class type.
* @param obj - the object to clone.
* @return The cloned object.
*/
private Object cloneObject(Class<?> clazz, Object source) {
// Try to clone it instead
try {
return clazz.getMethod("clone").invoke(source);
} catch (Exception e1) {
throw new RuntimeException("Cannot copy " + source + " (" + clazz + ")", e1);
}
}
/**
* Retrieve the default cloner used to clone the content of each element in the collection.
* @return Cloner used to clone elements.
*/
public Cloner getDefaultCloner() {
return defaultCloner;
}
}

Datei anzeigen

@ -0,0 +1,105 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.InstanceProvider;
/**
* Represents a class capable of cloning objects by deeply copying its fields.
*
* @author Kristian
*/
public class FieldCloner implements Cloner {
protected Cloner defaultCloner;
protected InstanceProvider instanceProvider;
// Used to clone objects
protected ObjectWriter writer;
/**
* Constructs a field cloner that copies objects by reading and writing the internal fields directly.
* @param defaultCloner - the default cloner used while copying fields.
* @param instanceProvider - used to construct new, empty copies of a given type.
*/
public FieldCloner(Cloner defaultCloner, InstanceProvider instanceProvider) {
this.defaultCloner = defaultCloner;
this.instanceProvider = instanceProvider;
// Remember to clone the value too
this.writer = new ObjectWriter() {
@Override
protected void transformField(StructureModifier<Object> modifierSource,
StructureModifier<Object> modifierDest, int fieldIndex) {
defaultTransform(modifierDest, modifierDest, getDefaultCloner(), fieldIndex);
}
};
}
/**
* Default implementation of the field transform. Applies a clone operation before a field value is written.
* @param modifierSource - modifier for the original object.
* @param modifierDest - modifier for the new cloned object.
* @param defaultCloner - cloner to use.
* @param fieldIndex - the current field index.
*/
protected void defaultTransform(StructureModifier<Object> modifierSource,
StructureModifier<Object> modifierDest, Cloner defaultCloner, int fieldIndex) {
Object value = modifierSource.read(fieldIndex);
modifierDest.write(fieldIndex, defaultCloner.clone(value));
}
@Override
public boolean canClone(Object source) {
if (source == null)
return false;
// Attempt to create the type
return instanceProvider.create(source.getClass()) != null;
}
@Override
public Object clone(Object source) {
if (source == null)
throw new IllegalArgumentException("source cannot be NULL.");
Object copy = instanceProvider.create(source.getClass());
// Copy public and private fields alike. Skip static fields.
writer.copyTo(source, copy, source.getClass());
return copy;
}
/**
* Retrieve the default cloner used to clone the content of each field.
* @return Cloner used to clone fields.
*/
public Cloner getDefaultCloner() {
return defaultCloner;
}
/**
* Retrieve the instance provider this cloner is using to create new, empty classes.
* @return The instance provider in use.
*/
public InstanceProvider getInstanceProvider() {
return instanceProvider;
}
}

Datei anzeigen

@ -0,0 +1,91 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.security.PublicKey;
import java.util.Locale;
import java.util.UUID;
import javax.crypto.SecretKey;
import com.google.common.primitives.Primitives;
/**
* Detects classes that are immutable, and thus doesn't require cloning.
* <p>
* This ought to have no false positives, but plenty of false negatives.
*
* @author Kristian
*/
public class ImmutableDetector implements Cloner {
// Notable immutable classes we might encounter
private static final Class<?>[] immutableClasses = {
StackTraceElement.class, BigDecimal.class,
BigInteger.class, Locale.class, UUID.class,
URL.class, URI.class, Inet4Address.class,
Inet6Address.class, InetSocketAddress.class,
SecretKey.class, PublicKey.class
};
@Override
public boolean canClone(Object source) {
// Don't accept NULL
if (source == null)
return false;
return isImmutable(source.getClass());
}
/**
* Determine if the given type is probably immutable.
* @param type - the type to check.
* @return TRUE if the type is immutable, FALSE otherwise.
*/
public static boolean isImmutable(Class<?> type) {
// Cases that are definitely not true
if (type.isArray())
return false;
// All primitive types
if (Primitives.isWrapperType(type) || String.class.equals(type))
return true;
// May not be true, but if so, that kind of code is broken anyways
if (type.isEnum())
return true;
for (Class<?> clazz : immutableClasses)
if (clazz.equals(type))
return true;
// Probably not
return false;
}
@Override
public Object clone(Object source) {
// Safe if the class is immutable
return source;
}
}

Datei anzeigen

@ -0,0 +1,49 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
/**
* Creates a cloner wrapper that accepts and clones NULL values.
*
* @author Kristian
*/
public class NullableCloner implements Cloner {
protected Cloner wrapped;
public NullableCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean canClone(Object source) {
return true;
}
@Override
public Object clone(Object source) {
// Don't pass the NULL value to the cloner
if (source == null)
return null;
else
return wrapped.clone(source);
}
public Cloner getWrapped() {
return wrapped;
}
}

Datei anzeigen

@ -20,6 +20,8 @@ package com.comphenix.protocol.reflect.instances;
import java.lang.reflect.Constructor;
import java.util.*;
import javax.annotation.Nullable;
import net.sf.cglib.proxy.Enhancer;
import com.google.common.base.Objects;
@ -30,7 +32,7 @@ import com.google.common.collect.ImmutableList;
* @author Kristian
*
*/
public class DefaultInstances {
public class DefaultInstances implements InstanceProvider {
/**
* Standard default instance provider.
@ -326,4 +328,9 @@ public class DefaultInstances {
}
return false;
}
@Override
public Object create(@Nullable Class<?> type) {
return getDefault(type);
}
}

Datei anzeigen

@ -18,13 +18,16 @@
package com.comphenix.protocol.reflect.instances;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.annotation.Nullable;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.google.common.collect.Lists;
/**
* Provides instance constructors using a list of existing values.
@ -33,8 +36,52 @@ import com.comphenix.protocol.reflect.FuzzyReflection;
* @author Kristian
*/
public class ExistingGenerator implements InstanceProvider {
/**
* Represents a single node in the tree of possible values.
*
* @author Kristian
*/
private static final class Node {
private Map<Class<?>, Node> children;
private Class<?> key;
private Object value;
private int level;
public Node(Class<?> key, Object value, int level) {
this.children = new HashMap<Class<?>, Node>();
this.key = key;
this.value = value;
this.level = level;
}
private Map<String, Object> existingValues = new HashMap<String, Object>();
public Node addChild(Node node) {
children.put(node.key, node);
return node;
}
public int getLevel() {
return level;
}
public Collection<Node> getChildren() {
return children.values();
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public Node getChild(Class<?> clazz) {
return children.get(clazz);
}
}
// Represents the root node
private Node root = new Node(null, null, 0);
private ExistingGenerator() {
// Only accessible to the constructors
@ -110,18 +157,94 @@ public class ExistingGenerator implements InstanceProvider {
if (value == null)
throw new IllegalArgumentException("Value cannot be NULL.");
existingValues.put(value.getClass().getName(), value);
}
private void addObject(Class<?> type, Object value) {
existingValues.put(type.getName(), value);
addObject(value.getClass(), value);
}
private void addObject(Class<?> type, Object value) {
Node node = getLeafNode(root, type, false);
// Set the value
node.setValue(value);
}
private Node getLeafNode(final Node start, Class<?> type, boolean readOnly) {
Class<?>[] path = getHierachy(type);
Node current = start;
for (int i = 0; i < path.length; i++) {
Node next = getNext(current, path[i], readOnly);
// Try every interface too
if (next == null && readOnly) {
current = null;
break;
}
current = next;
}
// And we're done
return current;
}
private Node getNext(Node current, Class<?> clazz, boolean readOnly) {
Node next = current.getChild(clazz);
// Add a new node if needed
if (next == null && !readOnly) {
next = current.addChild(new Node(clazz, null, current.getLevel() + 1));
}
// Add interfaces
if (next != null && !readOnly && !clazz.isInterface()) {
for (Class<?> clazzInterface : clazz.getInterfaces()) {
getLeafNode(root, clazzInterface, readOnly).addChild(next);
}
}
return next;
}
private Node getLowestLeaf(Node current) {
Node candidate = current;
// Depth-first search
for (Node child : current.getChildren()) {
Node subtree = getLowestLeaf(child);
// Get the lowest node
if (subtree.getValue() != null && candidate.getLevel() < subtree.getLevel()) {
candidate = subtree;
}
}
return candidate;
}
private Class<?>[] getHierachy(Class<?> type) {
LinkedList<Class<?>> levels = Lists.newLinkedList();
// Add each class from the hierachy
for (; type != null; type = type.getSuperclass()) {
levels.addFirst(type);
}
return levels.toArray(new Class<?>[0]);
}
@Override
public Object create(@Nullable Class<?> type) {
Object value = existingValues.get(type.getName());
// Locate the type in the hierachy
Node node = getLeafNode(root, type, true);
// Next, get the lowest leaf node
if (node != null) {
node = getLowestLeaf(node);
}
// NULL values indicate that the generator failed
return value;
if (node != null)
return node.getValue();
else
return null;
}
}

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.utility;
import java.util.Map;
@ -18,16 +35,24 @@ class CachedPackage {
this.cache = Maps.newConcurrentMap();
}
/**
* Associate a given class with a class name.
* @param className - class name.
* @param clazz - type of class.
*/
public void setPackageClass(String className, Class<?> clazz) {
cache.put(className, clazz);
}
/**
* Retrieve the class object of a specific class in the current package.
* @param className - the specific class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
@SuppressWarnings("rawtypes")
public Class getPackageClass(String className) {
public Class<?> getPackageClass(String className) {
try {
Class result = cache.get(className);
Class<?> result = cache.get(className);
// Concurrency is not a problem - we don't care if we look up a class twice
if (result == null) {

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.utility;
import java.lang.reflect.InvocationTargetException;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.utility;
import java.lang.reflect.Array;
@ -11,6 +28,7 @@ import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.google.common.base.Joiner;
/**
* Methods and constants specifically used in conjuction with reflecting Minecraft object.
@ -35,10 +53,14 @@ public class MinecraftReflection {
private static CachedPackage craftbukkitPackage;
// org.bukkit.craftbukkit
private static Class<?> craftItemStackClass;
private static Constructor<?> craftNMSConstructor;
private static Constructor<?> craftBukkitConstructor;
// New in 1.4.5
private static Method craftNMSMethod;
private static Method craftBukkitMethod;
private static boolean craftItemStackFailed;
// net.minecraft.server
private static Class<?> itemStackArrayClass;
@ -107,6 +129,24 @@ public class MinecraftReflection {
return fullName.substring(0, fullName.lastIndexOf("."));
}
/**
* Dynamically retrieve the Bukkit entity from a given entity.
* @param nmsObject - the NMS entity.
* @return A bukkit entity.
* @throws RuntimeException If we were unable to retrieve the Bukkit entity.
*/
public static Object getBukkitEntity(Object nmsObject) {
if (nmsObject == null)
return null;
// We will have to do this dynamically, unfortunately
try {
return nmsObject.getClass().getMethod("getBukkitEntity").invoke(nmsObject);
} catch (Exception e) {
throw new RuntimeException("Cannot get Bukkit entity from " + nmsObject, e);
}
}
/**
* Determine if a given object can be found within the package net.minecraft.server.
* @param obj - the object to test.
@ -133,31 +173,12 @@ public class MinecraftReflection {
String javaName = obj.getClass().getName();
return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className);
}
/**
* Dynamically retrieve the Bukkit entity from a given entity.
* @param nmsObject - the NMS entity.
* @return A bukkit entity.
* @throws RuntimeException If we were unable to retrieve the Bukkit entity.
*/
public static Object getBukkitEntity(Object nmsObject) {
if (nmsObject == null)
return null;
// We will have to do this dynamically, unfortunately
try {
return nmsObject.getClass().getMethod("getBukkitEntity").invoke(nmsObject);
} catch (Exception e) {
throw new RuntimeException("Cannot get Bukkit entity from " + nmsObject, e);
}
}
/**
* Determine if a given object is a ChunkPosition.
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isChunkPosition(Object obj) {
return getChunkPositionClass().isAssignableFrom(obj.getClass());
}
@ -167,7 +188,6 @@ public class MinecraftReflection {
* @param obj - the object to test.
* @return TRUE if it can, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isChunkCoordinates(Object obj) {
return getChunkCoordinatesClass().isAssignableFrom(obj.getClass());
}
@ -177,7 +197,6 @@ public class MinecraftReflection {
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isPacketClass(Object obj) {
return getPacketClass().isAssignableFrom(obj.getClass());
}
@ -187,7 +206,6 @@ public class MinecraftReflection {
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isLoginHandler(Object obj) {
return getNetLoginHandlerClass().isAssignableFrom(obj.getClass());
}
@ -197,7 +215,6 @@ public class MinecraftReflection {
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isMinecraftEntity(Object obj) {
return getEntityClass().isAssignableFrom(obj.getClass());
}
@ -207,7 +224,6 @@ public class MinecraftReflection {
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isItemStack(Object value) {
return getItemStackClass().isAssignableFrom(value.getClass());
}
@ -217,7 +233,6 @@ public class MinecraftReflection {
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isMinecraftPlayer(Object obj) {
return getEntityPlayerClass().isAssignableFrom(obj.getClass());
}
@ -227,7 +242,6 @@ public class MinecraftReflection {
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isWatchableObject(Object obj) {
return getWatchableObjectClass().isAssignableFrom(obj.getClass());
}
@ -237,7 +251,6 @@ public class MinecraftReflection {
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isDataWatcher(Object obj) {
return getDataWatcherClass().isAssignableFrom(obj.getClass());
}
@ -247,7 +260,6 @@ public class MinecraftReflection {
* @param obj - the given object.
* @return TRUE if it is, FALSE otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isCraftItemStack(Object obj) {
return getCraftItemStackClass().isAssignableFrom(obj.getClass());
}
@ -256,8 +268,7 @@ public class MinecraftReflection {
* Retrieve the EntityPlayer (NMS) class.
* @return The entity class.
*/
@SuppressWarnings("rawtypes")
public static Class getEntityPlayerClass() {
public static Class<?> getEntityPlayerClass() {
return getMinecraftClass("EntityPlayer");
}
@ -265,8 +276,7 @@ public class MinecraftReflection {
* Retrieve the entity (NMS) class.
* @return The entity class.
*/
@SuppressWarnings("rawtypes")
public static Class getEntityClass() {
public static Class<?> getEntityClass() {
return getMinecraftClass("Entity");
}
@ -274,8 +284,7 @@ public class MinecraftReflection {
* Retrieve the packet class.
* @return The packet class.
*/
@SuppressWarnings("rawtypes")
public static Class getPacketClass() {
public static Class<?> getPacketClass() {
return getMinecraftClass("Packet");
}
@ -283,17 +292,39 @@ public class MinecraftReflection {
* Retrieve the NetLoginHandler class.
* @return The NetLoginHandler class.
*/
@SuppressWarnings("rawtypes")
public static Class getNetLoginHandlerClass() {
return getMinecraftClass("NetLoginHandler");
public static Class<?> getNetLoginHandlerClass() {
return getMinecraftClass("NetLoginHandler", "PendingConnection");
}
/**
* Retrieve the NetLoginHandler class.
* @return The NetLoginHandler class.
* Retrieve the NetServerHandler class.
* @return The NetServerHandler class.
*/
@SuppressWarnings("rawtypes")
public static Class getItemStackClass() {
public static Class<?> getNetServerHandlerClass() {
return getMinecraftClass("NetServerHandler", "PlayerConnection");
}
/**
* Retrieve the NetworkManager class.
* @return The NetworkManager class.
*/
public static Class<?> getNetworkManagerClass() {
return getMinecraftClass("NetworkManager");
}
/**
* Retrieve the NetHandler class.
* @return The NetHandler class.
*/
public static Class<?> getNetHandlerClass() {
return getMinecraftClass("NetHandler", "Connection");
}
/**
* Retrieve the NMS ItemStack class.
* @return The ItemStack class.
*/
public static Class<?> getItemStackClass() {
return getMinecraftClass("ItemStack");
}
@ -301,17 +332,23 @@ public class MinecraftReflection {
* Retrieve the WorldType class.
* @return The WorldType class.
*/
@SuppressWarnings("rawtypes")
public static Class getWorldTypeClass() {
public static Class<?> getWorldTypeClass() {
return getMinecraftClass("WorldType");
}
/**
* Retrieve the MinecraftServer class.
* @return MinecraftServer class.
*/
public static Class<?> getMinecraftServerClass() {
return getMinecraftClass("MinecraftServer");
}
/**
* Retrieve the DataWatcher class.
* @return The DataWatcher class.
*/
@SuppressWarnings("rawtypes")
public static Class getDataWatcherClass() {
public static Class<?> getDataWatcherClass() {
return getMinecraftClass("DataWatcher");
}
@ -319,8 +356,7 @@ public class MinecraftReflection {
* Retrieve the ChunkPosition class.
* @return The ChunkPosition class.
*/
@SuppressWarnings("rawtypes")
public static Class getChunkPositionClass() {
public static Class<?> getChunkPositionClass() {
return getMinecraftClass("ChunkPosition");
}
@ -328,8 +364,7 @@ public class MinecraftReflection {
* Retrieve the ChunkPosition class.
* @return The ChunkPosition class.
*/
@SuppressWarnings("rawtypes")
public static Class getChunkCoordinatesClass() {
public static Class<?> getChunkCoordinatesClass() {
return getMinecraftClass("ChunkCoordinates");
}
@ -337,8 +372,7 @@ public class MinecraftReflection {
* Retrieve the WatchableObject class.
* @return The WatchableObject class.
*/
@SuppressWarnings("rawtypes")
public static Class getWatchableObjectClass() {
public static Class<?> getWatchableObjectClass() {
return getMinecraftClass("WatchableObject");
}
@ -346,8 +380,7 @@ public class MinecraftReflection {
* Retrieve the ItemStack[] class.
* @return The ItemStack[] class.
*/
@SuppressWarnings("rawtypes")
public static Class getItemStackArrayClass() {
public static Class<?> getItemStackArrayClass() {
if (itemStackArrayClass == null)
itemStackArrayClass = getArrayClass(getItemStackClass());
return itemStackArrayClass;
@ -358,8 +391,7 @@ public class MinecraftReflection {
* @param componentType - type of each element in the array.
* @return The class of the array.
*/
@SuppressWarnings("rawtypes")
public static Class getArrayClass(Class componentType) {
public static Class<?> getArrayClass(Class<?> componentType) {
// Bit of a hack, but it works
return Array.newInstance(componentType, 0).getClass();
}
@ -368,11 +400,8 @@ public class MinecraftReflection {
* Retrieve the CraftItemStack class.
* @return The CraftItemStack class.
*/
@SuppressWarnings("rawtypes")
public static Class getCraftItemStackClass() {
if (craftItemStackClass == null)
craftItemStackClass = getCraftBukkitClass("inventory.CraftItemStack");
return craftItemStackClass;
public static Class<?> getCraftItemStackClass() {
return getCraftBukkitClass("inventory.CraftItemStack");
}
/**
@ -380,12 +409,19 @@ public class MinecraftReflection {
* @param bukkitItemStack - the Bukkit ItemStack to convert.
* @return A CraftItemStack as an ItemStack.
*/
@SuppressWarnings("unchecked")
public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
// Delegate this task to the method that can execute it
if (craftBukkitMethod != null)
return getBukkitItemByMethod(bukkitItemStack);
if (craftBukkitConstructor == null) {
try {
craftBukkitConstructor = getCraftItemStackClass().getConstructor(ItemStack.class);
} catch (Exception e) {
// See if this method works
if (!craftItemStackFailed)
return getBukkitItemByMethod(bukkitItemStack);
throw new RuntimeException("Cannot find CraftItemStack(org.bukkit.inventory.ItemStack).", e);
}
}
@ -397,18 +433,43 @@ public class MinecraftReflection {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
private static ItemStack getBukkitItemByMethod(ItemStack bukkitItemStack) {
if (craftBukkitMethod == null) {
try {
craftBukkitMethod = getCraftItemStackClass().getMethod("asCraftCopy", ItemStack.class);
} catch (Exception e) {
craftItemStackFailed = true;
throw new RuntimeException("Cannot find CraftItemStack.asCraftCopy(org.bukkit.inventory.ItemStack).", e);
}
}
// Next, construct it
try {
return (ItemStack) craftBukkitMethod.invoke(null, bukkitItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
/**
* Retrieve the Bukkit ItemStack from a given net.minecraft.server ItemStack.
* @param minecraftItemStack - the NMS ItemStack to wrap.
* @return The wrapped ItemStack.
*/
@SuppressWarnings("unchecked")
public static ItemStack getBukkitItemStack(Object minecraftItemStack) {
// Delegate this task to the method that can execute it
if (craftNMSMethod != null)
return getBukkitItemByMethod(minecraftItemStack);
if (craftNMSConstructor == null) {
try {
craftNMSConstructor = getCraftItemStackClass().getConstructor(minecraftItemStack.getClass());
} catch (Exception e) {
// Give it a try
if (!craftItemStackFailed)
return getBukkitItemByMethod(minecraftItemStack);
throw new RuntimeException("Cannot find CraftItemStack(net.mineraft.server.ItemStack).", e);
}
}
@ -421,6 +482,24 @@ public class MinecraftReflection {
}
}
private static ItemStack getBukkitItemByMethod(Object minecraftItemStack) {
if (craftNMSMethod == null) {
try {
craftNMSMethod = getCraftItemStackClass().getMethod("asCraftMirror", minecraftItemStack.getClass());
} catch (Exception e) {
craftItemStackFailed = true;
throw new RuntimeException("Cannot find CraftItemStack.asCraftMirror(net.mineraft.server.ItemStack).", e);
}
}
// Next, construct it
try {
return (ItemStack) craftNMSMethod.invoke(null, minecraftItemStack);
} catch (Exception e) {
throw new RuntimeException("Cannot construct CraftItemStack.", e);
}
}
/**
* Retrieve the net.minecraft.server ItemStack from a Bukkit ItemStack.
* @param stack - the Bukkit ItemStack to convert.
@ -454,10 +533,63 @@ public class MinecraftReflection {
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
@SuppressWarnings("rawtypes")
public static Class getMinecraftClass(String className) {
public static Class<?> getMinecraftClass(String className) {
if (minecraftPackage == null)
minecraftPackage = new CachedPackage(getMinecraftPackage());
return minecraftPackage.getPackageClass(className);
}
/**
* Retrieve the first class that matches a specified Minecraft name.
* @param classes - the specific Minecraft class.
* @return Class object.
* @throws RuntimeException If we are unable to find any of the given classes.
*/
public static Class<?> getMinecraftClass(String className, String... aliases) {
try {
// Try the main class first
return getMinecraftClass(className);
} catch (RuntimeException e1) {
Class<?> success = null;
// Try every alias too
for (String alias : aliases) {
try {
success = getMinecraftClass(alias);
break;
} catch (RuntimeException e2) {
// Swallov
}
}
if (success != null) {
// Save it for later
minecraftPackage.setPackageClass(className, success);
return success;
} else {
// Hack failed
throw new RuntimeException(
String.format("Unable to find %s (%s)",
className,
Joiner.on(", ").join(aliases))
);
}
}
}
/**
* Dynamically retrieve the NetworkManager name.
* @return Name of the NetworkManager class.
*/
public static String getNetworkManagerName() {
return getNetworkManagerClass().getSimpleName();
}
/**
* Dynamically retrieve the name of the current NetLoginHandler.
* @return Name of the NetLoginHandler class.
*/
public static String getNetLoginHandlerName() {
return getNetLoginHandlerClass().getSimpleName();
}
}

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.ref.WeakReference;
@ -155,7 +172,6 @@ public class BukkitConverters {
return null;
return getIgnoreNull(new EquivalentConverter<WorldType>() {
@SuppressWarnings("unchecked")
@Override
public Object getGeneric(Class<?> genericType, WorldType specific) {
try {
@ -169,8 +185,7 @@ public class BukkitConverters {
throw new FieldAccessException("Cannot find the WorldType.getType() method.", e);
}
}
@SuppressWarnings("unchecked")
@Override
public WorldType getSpecific(Object generic) {
try {

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
@ -133,7 +150,6 @@ public class ChunkPosition {
*/
public static EquivalentConverter<ChunkPosition> getConverter() {
return new EquivalentConverter<ChunkPosition>() {
@SuppressWarnings("unchecked")
@Override
public Object getGeneric(Class<?> genericType, ChunkPosition specific) {
if (chunkPositionConstructor == null) {
@ -166,7 +182,8 @@ public class ChunkPosition {
if (intModifier.size() >= 3) {
try {
return new ChunkPosition(intModifier.read(0), intModifier.read(1), intModifier.read(2));
StructureModifier<Integer> instance = intModifier.withTarget(generic);
return new ChunkPosition(instance.read(0), instance.read(1), instance.read(2));
} catch (FieldAccessException e) {
// This is an exeptional work-around, so we don't want to burden the caller with the messy details
throw new RuntimeException("Field access error.", e);

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field;
@ -275,6 +292,37 @@ public class WrappedDataWatcher implements Iterable<WrappedWatchableObject> {
}
}
@Override
public boolean equals(Object obj) {
// Quick checks
if (obj == this)
return true;
if (obj == null)
return false;
if (obj instanceof WrappedDataWatcher) {
WrappedDataWatcher other = (WrappedDataWatcher) obj;
Iterator<WrappedWatchableObject> first = iterator(), second = other.iterator();
// Make sure they're the same size
if (size() != other.size())
return false;
for (; first.hasNext() && second.hasNext(); ) {
// See if the two elements are equal
if (!first.next().equals(second.next()))
return false;
}
return true;
}
return false;
}
@Override
public int hashCode() {
return getWatchableObjects().hashCode();
}
/**
* Retrieve a copy of every index associated with a watched object.
* @return Every watched object index.

Datei anzeigen

@ -1,3 +1,20 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
@ -49,7 +66,6 @@ public class WrappedWatchableObject {
* @param index - the index.
* @param value - non-null value of specific types.
*/
@SuppressWarnings("unchecked")
public WrappedWatchableObject(int index, Object value) {
if (value == null)
throw new IllegalArgumentException("Value cannot be NULL.");
@ -296,8 +312,8 @@ public class WrappedWatchableObject {
* @throws FieldAccessException If we're unable to use reflection.
*/
public WrappedWatchableObject deepClone() throws FieldAccessException {
@SuppressWarnings("unchecked")
WrappedWatchableObject clone = new WrappedWatchableObject(DefaultInstances.DEFAULT.getDefault(MinecraftReflection.getWatchableObjectClass()));
WrappedWatchableObject clone = new WrappedWatchableObject(
DefaultInstances.DEFAULT.getDefault(MinecraftReflection.getWatchableObjectClass()));
clone.setDirtyState(getDirtyState());
clone.setIndex(getIndex());

Datei anzeigen

@ -2,7 +2,7 @@ global:
# Settings for the automatic version updater
auto updater:
notify: true
download: true
download: false
# Number of seconds to wait until a new update is downloaded
delay: 43200 # 12 hours

Datei anzeigen

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

Datei anzeigen

@ -0,0 +1,382 @@
package com.comphenix.protocol.events;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.Array;
import java.util.List;
// Will have to be updated for every version though
import org.bukkit.craftbukkit.v1_4_6.inventory.CraftItemFactory;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.WorldType;
import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
// Ensure that the CraftItemFactory is mockable
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PrepareForTest(CraftItemFactory.class)
public class PacketContainerTest {
// Helper converters
private EquivalentConverter<WrappedDataWatcher> watchConvert = BukkitConverters.getDataWatcherConverter();
private EquivalentConverter<ItemStack> itemConvert = BukkitConverters.getItemStackConverter();
@BeforeClass
public static void initializeBukkit() throws IllegalAccessException {
// Initialize reflection
MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_4_6", "org.bukkit.craftbukkit.v1_4_6");
// Mock the server object
Server mockedServer = mock(Server.class);
ItemFactory mockedFactory = mock(CraftItemFactory.class);
ItemMeta mockedMeta = mock(ItemMeta.class);
when(mockedServer.getItemFactory()).thenReturn(mockedFactory);
when(mockedFactory.getItemMeta(any(Material.class))).thenReturn(mockedMeta);
// Inject this fake server
FieldUtils.writeStaticField(Bukkit.class, "server", mockedServer, true);
// And the fake item factory
FieldUtils.writeStaticField(CraftItemFactory.class, "instance", mockedFactory, true);
}
private <T> void testPrimitive(StructureModifier<T> modifier, int index, T initialValue, T testValue) {
// Check initial value
assertEquals(initialValue, modifier.read(index));
// Test assignment
modifier.write(index, testValue);
assertEquals(testValue, modifier.read(0));
}
private <T> void testObjectArray(StructureModifier<T[]> modifier, int index, T[] initialValue, T[] testValue) {
// Check initial value
assertNull(modifier.read(index));
modifier.writeDefaults();
// Test initial
assertArrayEquals(initialValue, modifier.read(index));
// Test assignment
modifier.write(index, testValue);
assertArrayEquals(testValue, modifier.read(0));
}
@Test
public void testGetByteArrays() {
// Contains a byte array we will test
PacketContainer customPayload = new PacketContainer(Packets.Server.CUSTOM_PAYLOAD);
StructureModifier<byte[]> bytes = customPayload.getByteArrays();
byte[] testArray = new byte[] { 1, 2, 3 };
// It's NULL at first
assertArrayEquals(null, (byte[]) bytes.read(0));
customPayload.getModifier().writeDefaults();
// Then it should create an empty array
assertArrayEquals(new byte[0], (byte[]) bytes.read(0));
// Check and see if we can write to it
bytes.write(0, testArray);
assertArrayEquals(testArray, (byte[]) bytes.read(0));
}
@Test
public void testGetBytes() {
PacketContainer spawnMob = new PacketContainer(Packets.Server.MOB_SPAWN);
testPrimitive(spawnMob.getBytes(), 0, (byte)0, (byte)1);
}
@Test
public void testGetShorts() {
PacketContainer itemData = new PacketContainer(Packets.Server.ITEM_DATA);
testPrimitive(itemData.getShorts(), 0, (short)0, (short)1);
}
@Test
public void testGetIntegers() {
PacketContainer updateSign = new PacketContainer(Packets.Server.UPDATE_SIGN);
testPrimitive(updateSign.getIntegers(), 0, (int)0, (int)1);
}
@Test
public void testGetLongs() {
PacketContainer updateTime = new PacketContainer(Packets.Server.UPDATE_TIME);
testPrimitive(updateTime.getLongs(), 0, (long)0, (long)1);
}
@Test
public void testGetFloat() {
PacketContainer explosion = new PacketContainer(Packets.Server.EXPLOSION);
testPrimitive(explosion.getFloat(), 0, (float)0, (float)0.8);
}
@Test
public void testGetDoubles() {
PacketContainer explosion = new PacketContainer(Packets.Server.EXPLOSION);
testPrimitive(explosion.getDoubles(), 0, (double)0, (double)0.8);
}
@Test
public void testGetStrings() {
PacketContainer explosion = new PacketContainer(Packets.Server.CHAT);
testPrimitive(explosion.getStrings(), 0, null, "hello");
}
@Test
public void testGetStringArrays() {
PacketContainer explosion = new PacketContainer(Packets.Server.UPDATE_SIGN);
testObjectArray(explosion.getStringArrays(), 0, new String[0], new String[] { "hello", "world" });
}
@Test
public void testGetIntegerArrays() {
// Contains a byte array we will test
PacketContainer mapChunkBulk = new PacketContainer(Packets.Server.MAP_CHUNK_BULK);
StructureModifier<int[]> integers = mapChunkBulk.getIntegerArrays();
int[] testArray = new int[] { 1, 2, 3 };
// Pre and post conditions
assertArrayEquals(null, (int[]) integers.read(0));
mapChunkBulk.getModifier().writeDefaults();
assertArrayEquals(new int[0], (int[]) integers.read(0));
integers.write(0, testArray);
assertArrayEquals(testArray, (int[]) integers.read(0));
}
@Test
public void testGetItemModifier() {
PacketContainer windowClick = new PacketContainer(Packets.Client.WINDOW_CLICK);
StructureModifier<ItemStack> items = windowClick.getItemModifier();
ItemStack goldAxe = new ItemStack(Material.GOLD_AXE);
assertNull(items.read(0));
// Insert the goldaxe and check if it's there
items.write(0, goldAxe);
assertTrue(equivalentItem(goldAxe, items.read(0)));
}
@Test
public void testGetItemArrayModifier() {
PacketContainer windowItems = new PacketContainer(Packets.Server.WINDOW_ITEMS);
StructureModifier<ItemStack[]> itemAccess = windowItems.getItemArrayModifier();
ItemStack[] itemArray = new ItemStack[] {
new ItemStack(Material.GOLD_AXE),
new ItemStack(Material.DIAMOND_AXE)
};
assertNull(itemAccess.read(0));
// Insert and check that it was succesful
itemAccess.write(0, itemArray);
// Read back array
ItemStack[] comparision = itemAccess.read(0);
assertEquals(itemArray.length, comparision.length);
// Check that it is equivalent
for (int i = 0; i < itemArray.length; i++) {
assertTrue(String.format("Array element %s is not the same: %s != %s",
i, itemArray[i], comparision[i]), equivalentItem(itemArray[i], comparision[i]));
}
}
private boolean equivalentItem(ItemStack first, ItemStack second) {
if (first == null) {
return second == null;
} else if (second == null) {
return false;
} else {
return first.getType().equals(second.getType());
}
}
@Test
public void testGetWorldTypeModifier() {
PacketContainer loginPacket = new PacketContainer(Packets.Server.LOGIN);
StructureModifier<WorldType> worldAccess = loginPacket.getWorldTypeModifier();
WorldType testValue = WorldType.LARGE_BIOMES;
assertNull(worldAccess.read(0));
// Insert and read back
worldAccess.write(0, testValue);
assertEquals(testValue, worldAccess.read(0));
}
@Test
public void testGetDataWatcherModifier() {
PacketContainer mobSpawnPacket = new PacketContainer(Packets.Server.MOB_SPAWN);
StructureModifier<WrappedDataWatcher> watcherAccessor = mobSpawnPacket.getDataWatcherModifier();
WrappedDataWatcher dataWatcher = new WrappedDataWatcher();
dataWatcher.setObject(1, 100);
dataWatcher.setObject(2, 125);
assertNull(watcherAccessor.read(0));
// Insert and read back
watcherAccessor.write(0, dataWatcher);
assertEquals(dataWatcher, watcherAccessor.read(0));
}
// Unfortunately, it might be too difficult to mock this one
//
// @Test
// public void testGetEntityModifier() { }
// No packet expose this type directly.
//
// @Test
// public void testGetPositionModifier() { }
@Test
public void testGetPositionCollectionModifier() {
PacketContainer explosionPacket = new PacketContainer(Packets.Server.EXPLOSION);
StructureModifier<List<ChunkPosition>> positionAccessor = explosionPacket.getPositionCollectionModifier();
assertNull(positionAccessor.read(0));
List<ChunkPosition> positions = Lists.newArrayList();
positions.add(new ChunkPosition(1, 2, 3));
positions.add(new ChunkPosition(3, 4, 5));
// Insert and read back
positionAccessor.write(0, positions);
assertEquals(positions, positionAccessor.read(0));
}
@Test
public void testGetWatchableCollectionModifier() {
PacketContainer entityMetadata = new PacketContainer(Packets.Server.ENTITY_METADATA);
StructureModifier<List<WrappedWatchableObject>> watchableAccessor =
entityMetadata.getWatchableCollectionModifier();
assertNull(watchableAccessor.read(0));
WrappedDataWatcher watcher = new WrappedDataWatcher();
watcher.setObject(1, 10);
watcher.setObject(8, 10);
List<WrappedWatchableObject> list = watcher.getWatchableObjects();
// Insert and read back
watchableAccessor.write(0, list);
assertEquals(list, watchableAccessor.read(0));
}
@Test
public void testDeepClone() {
// Try constructing all the packets
for (Integer id : Iterables.concat(
Packets.getClientRegistry().values(),
Packets.getServerRegistry().values() )) {
// Whether or not this packet has been registered
boolean registered = Packets.Server.isSupported(id) ||
Packets.Client.isSupported(id);
try {
PacketContainer constructed = new PacketContainer(id);
if (!registered) {
fail("Expected IllegalArgumentException(Packet " + id + " not registered");
}
// Make sure these packets contains fields as well
assertTrue("Constructed packet with no known fields (" + id + ")",
constructed.getModifier().size() > 0);
// Initialize default values
constructed.getModifier().writeDefaults();
// Clone the packet
PacketContainer cloned = constructed.deepClone();
// Make sure they're equivalent
StructureModifier<Object> firstMod = constructed.getModifier(), secondMod = cloned.getModifier();
assertEquals(firstMod.size(), secondMod.size());
// Make sure all the fields are equivalent
for (int i = 0; i < firstMod.size(); i++) {
if (firstMod.getField(i).getType().isArray())
assertArrayEquals(getArray(firstMod.read(i)), getArray(secondMod.read(i)));
else
testEquality(firstMod.read(i), secondMod.read(i));
}
} catch (IllegalArgumentException e) {
if (!registered) {
// Check the same
assertEquals(e.getMessage(), "The packet ID " + id + " is not registered.");
} else {
// Something is very wrong
throw e;
}
}
}
}
// Convert to objects that support equals()
private void testEquality(Object a, Object b) {
if (a != null && b != null) {
if (MinecraftReflection.isDataWatcher(a)) {
a = watchConvert.getSpecific(a);
b = watchConvert.getSpecific(b);
} else if (MinecraftReflection.isItemStack(a)) {
a = itemConvert.getSpecific(a);
b = itemConvert.getSpecific(b);
}
}
assertEquals(a, b);
}
/**
* Get the underlying array as an object array.
* @param val - array wrapped as an Object.
* @return An object array.
*/
private Object[] getArray(Object val) {
if (val instanceof Object[])
return (Object[]) val;
if (val == null)
return null;
int arrlength = Array.getLength(val);
Object[] outputArray = new Object[arrlength];
for (int i = 0; i < arrlength; ++i)
outputArray[i] = Array.get(val, i);
return outputArray;
}
}