Use a builder pattern instead of a constructor with 8 parameters.

Also make use of the fact that Spigot may have initialized its 
server connection (in the latest version).
This commit is contained in:
Kristian S. Stangeland 2013-07-07 14:03:07 +02:00
parent fb7f80b646
commit 6fe7fe46f3
4 changed files with 301 additions and 109 deletions

View File

@ -173,8 +173,14 @@ public class ProtocolLibrary extends JavaPlugin {
updater = new Updater(this, logger, "protocollib", getFile(), "protocol.info");
unhookTask = new DelayedSingleTask(this);
protocolManager = PacketFilterManager.createManager(
getClassLoader(), getServer(), this, version, unhookTask, reporter);
protocolManager = PacketFilterManager.newBuilder().
classLoader(getClassLoader()).
server(getServer()).
library(this).
minecraftVersion(version).
unhookTask(unhookTask).
reporter(reporter).
build();
// Setup error reporter
detailedReporter.addGlobalParameter("manager", protocolManager);

View File

@ -0,0 +1,255 @@
package com.comphenix.protocol.injector;
import javax.annotation.Nonnull;
import org.bukkit.Server;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.plugin.Plugin;
import com.comphenix.executors.BukkitFutures;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.player.InjectedServerConnection;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
public class PacketFilterBuilder {
public static final ReportType REPORT_TEMPORARY_EVENT_ERROR = new ReportType("Unable to register or handle temporary event.");
private ClassLoader classLoader;
private Server server;
private Plugin library;
private MinecraftVersion mcVersion;
private DelayedSingleTask unhookTask;
private ErrorReporter reporter;
// Whether or not we need to enable Netty
private AsyncFilterManager asyncManager;
private boolean nettyEnabled;
/**
* Update the current class loader.
* @param classLoader - current class loader.
* @return This builder, for chaining.
*/
public PacketFilterBuilder classLoader(@Nonnull ClassLoader classLoader) {
if (classLoader == null)
throw new IllegalArgumentException("classLoader cannot be NULL.");
this.classLoader = classLoader;
return this;
}
/**
* Set the current server.
* @param server - current server.
* @return This builder, for chaining.
*/
public PacketFilterBuilder server(@Nonnull Server server) {
if (server == null)
throw new IllegalArgumentException("server cannot be NULL.");
this.server = server;
return this;
}
/**
* Set a reference to the plugin instance of ProtocolLib.
* @param library - plugin instance.
* @return This builder, for chaining.
*/
public PacketFilterBuilder library(@Nonnull Plugin library) {
if (library == null)
throw new IllegalArgumentException("library cannot be NULL.");
this.library = library;
return this;
}
/**
* Set the current Minecraft version.
* @param mcVersion - Minecraft version.
* @return This builder, for chaining.
*/
public PacketFilterBuilder minecraftVersion(@Nonnull MinecraftVersion mcVersion) {
if (mcVersion == null)
throw new IllegalArgumentException("minecraftVersion cannot be NULL.");
this.mcVersion = mcVersion;
return this;
}
/**
* Set the task used to delay unhooking when ProtocolLib is no in use.
* @param unhookTask - the unhook task.
* @return This builder, for chaining.
*/
public PacketFilterBuilder unhookTask(@Nonnull DelayedSingleTask unhookTask) {
if (unhookTask == null)
throw new IllegalArgumentException("unhookTask cannot be NULL.");
this.unhookTask = unhookTask;
return this;
}
/**
* Set the error reporter.
* @param reporter - new error reporter.
* @return This builder, for chaining.
*/
public PacketFilterBuilder reporter(@Nonnull ErrorReporter reporter) {
if (reporter == null)
throw new IllegalArgumentException("reporter cannot be NULL.");
this.reporter = reporter;
return this;
}
/**
* Determine if we should prepare to hook Netty in Spigot.
* <p>
* This is calculated in the {@link #build()} method.
* @return TRUE if we should, FALSE otherwise.
*/
public boolean isNettyEnabled() {
return nettyEnabled;
}
/**
* Retrieve the class loader set in this builder.
* @return The class loader.
*/
public ClassLoader getClassLoader() {
return classLoader;
}
/**
* Retrieve the current CraftBukkit server.
* @return Current server.
*/
public Server getServer() {
return server;
}
/**
* Retrieve a reference to the current ProtocolLib instance.
* @return ProtocolLib.
*/
public Plugin getLibrary() {
return library;
}
/**
* Retrieve the current Minecraft version.
* @return Current version.
*/
public MinecraftVersion getMinecraftVersion() {
return mcVersion;
}
/**
* Retrieve the task that is used to delay unhooking when ProtocolLib is no in use.
* @return The unhook task.
*/
public DelayedSingleTask getUnhookTask() {
return unhookTask;
}
/**
* Retrieve the error reporter.
* @return Error reporter.
*/
public ErrorReporter getReporter() {
return reporter;
}
/**
* Retrieve the asynchronous manager.
* <p>
* This is first constructed the {@link #build()} method.
* @return The asynchronous manager.
*/
public AsyncFilterManager getAsyncManager() {
return asyncManager;
}
/**
* Create a new packet filter manager.
* @return A new packet filter manager.
*/
public InternalManager build() {
if (reporter == null)
throw new IllegalArgumentException("reporter cannot be NULL.");
if (classLoader == null)
throw new IllegalArgumentException("classLoader cannot be NULL.");
asyncManager = new AsyncFilterManager(reporter, server.getScheduler());
nettyEnabled = false;
// Spigot
if (SpigotPacketInjector.canUseSpigotListener()) {
// If the server hasn't loaded yet - wait
if (InjectedServerConnection.getServerConnection(reporter, server) == null) {
// We need to delay this until we know if Netty is enabled
final DelayedPacketManager delayed = new DelayedPacketManager(reporter);
// They must reference each other
delayed.setAsynchronousManager(asyncManager);
asyncManager.setManager(delayed);
Futures.addCallback(BukkitFutures.nextEvent(library, WorldInitEvent.class),
new FutureCallback<WorldInitEvent>() {
@Override
public void onSuccess(WorldInitEvent event) {
// Nevermind
if (delayed.isClosed())
return;
try {
registerSpigot(delayed);
} catch (Exception e) {
onFailure(e);
}
}
@Override
public void onFailure(Throwable error) {
reporter.reportWarning(PacketFilterBuilder.this, Report
.newBuilder(REPORT_TEMPORARY_EVENT_ERROR).error(error));
}
});
System.out.println("Delaying due to Spigot");
// Let plugins use this version instead
return delayed;
} else {
nettyEnabled = !MinecraftReflection.isMinecraftObject(
InjectedServerConnection.getServerConnection(reporter, server));
}
}
// Otherwise - construct the packet filter manager right away
return buildInternal();
}
private void registerSpigot(DelayedPacketManager delayed) {
// Use netty if we have a non-standard ServerConnection class
nettyEnabled = !MinecraftReflection.isMinecraftObject(
InjectedServerConnection.getServerConnection(reporter, server));
// Switch to the standard manager
delayed.setDelegate(buildInternal());
}
/**
* Construct a new packet filter manager without checking for Netty.
* @return A new packet filter manager.
*/
private PacketFilterManager buildInternal() {
PacketFilterManager manager = new PacketFilterManager(this);
// It's a cyclic reference, but it's too late to fix now
asyncManager.setManager(manager);
return manager;
}
}

View File

@ -22,7 +22,6 @@ import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@ -43,11 +42,9 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.comphenix.executors.BukkitFutures;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.async.AsyncFilterManager;
@ -59,7 +56,6 @@ import com.comphenix.protocol.events.*;
import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.player.InjectedServerConnection;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
@ -67,12 +63,9 @@ import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
public final class PacketFilterManager implements ProtocolManager, ListenerInvoker, InternalManager {
@ -94,8 +87,6 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
public static final ReportType REPORT_CANNOT_UNREGISTER_PLUGIN = new ReportType("Unable to handle disabled plugin.");
public static final ReportType REPORT_PLUGIN_VERIFIER_ERROR = new ReportType("Verifier error: %s");
public static final ReportType REPORT_TEMPORARY_EVENT_ERROR = new ReportType("Unable to register or handle temporary event.");
/**
* Sets the inject hook type. Different types allow for maximum compatibility.
* @author Kristian
@ -182,17 +173,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
/**
* Only create instances of this class if protocol lib is disabled.
*/
public PacketFilterManager(
ClassLoader classLoader, Server server, Plugin library,
AsyncFilterManager asyncManager, MinecraftVersion mcVersion,
final DelayedSingleTask unhookTask,
ErrorReporter reporter, boolean nettyEnabled) {
if (reporter == null)
throw new IllegalArgumentException("reporter cannot be NULL.");
if (classLoader == null)
throw new IllegalArgumentException("classLoader cannot be NULL.");
public PacketFilterManager(PacketFilterBuilder builder) {
// Used to determine if injection is needed
Predicate<GamePhase> isInjectionNecessary = new Predicate<GamePhase>() {
@Override
@ -213,16 +194,16 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
this.sendingListeners = new SortedPacketListenerList();
// References
this.unhookTask = unhookTask;
this.server = server;
this.classLoader = classLoader;
this.reporter = reporter;
this.unhookTask = builder.getUnhookTask();
this.server = builder.getServer();
this.classLoader = builder.getClassLoader();
this.reporter = builder.getReporter();
// The plugin verifier
this.pluginVerifier = new PluginVerifier(library);
this.pluginVerifier = new PluginVerifier(builder.getLibrary());
// Use the correct injection type
if (nettyEnabled) {
if (builder.isNettyEnabled()) {
spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
this.playerInjection = spigotInjector.getPlayerHandler();
this.packetInjector = spigotInjector.getPacketInjector();
@ -236,7 +217,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
classLoader(classLoader).
packetListeners(packetListeners).
injectionFilter(isInjectionNecessary).
version(mcVersion).
version(builder.getMinecraftVersion()).
buildHandler();
this.packetInjector = PacketInjectorBuilder.newBuilder().
@ -246,7 +227,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
playerInjection(playerInjection).
buildInjector();
}
this.asyncFilterManager = asyncManager;
this.asyncFilterManager = builder.getAsyncManager();
// Attempt to load the list of server and client packets
try {
@ -256,87 +237,15 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_PACKET_LIST).error(e));
}
}
public static InternalManager createManager(
final ClassLoader classLoader, final Server server, final Plugin library,
final MinecraftVersion mcVersion, final DelayedSingleTask unhookTask,
final ErrorReporter reporter) {
final AsyncFilterManager asyncManager = new AsyncFilterManager(reporter, server.getScheduler());
// Spigot
if (SpigotPacketInjector.canUseSpigotListener()) {
// We need to delay this until we know if Netty is enabled
final DelayedPacketManager delayed = new DelayedPacketManager(reporter);
// They must reference each other
delayed.setAsynchronousManager(asyncManager);
asyncManager.setManager(delayed);
final Callable<Object> registerSpigot = new Callable<Object>() {
@Override
public Object call() throws Exception {
// Now we are probably able to check for Netty
InjectedServerConnection inspector = new InjectedServerConnection(reporter, null, server, null);
Object connection = inspector.getServerConnection();
// Use netty if we have a non-standard ServerConnection class
boolean useNetty = !MinecraftReflection.isMinecraftObject(connection);
// Switch to the standard manager
delayed.setDelegate(new PacketFilterManager(
classLoader, server, library, asyncManager, mcVersion, unhookTask, reporter, useNetty)
);
// Reference this manager directly
asyncManager.setManager(delayed.getDelegate());
return null;
}
};
// If the server hasn't loaded yet - wait
if (server.getWorlds().size() == 0) {
Futures.addCallback(BukkitFutures.nextEvent(library, WorldInitEvent.class), new FutureCallback<WorldInitEvent>() {
@Override
public void onSuccess(WorldInitEvent event) {
// Nevermind
if (delayed.isClosed())
return;
try {
registerSpigot.call();
} catch (Exception e) {
onFailure(e);
}
}
@Override
public void onFailure(Throwable error) {
reporter.reportWarning(PacketFilterManager.class, Report.newBuilder(REPORT_TEMPORARY_EVENT_ERROR).error(error));
}
});
} else {
// Do it now
try {
registerSpigot.call();
} catch (Exception e) {
e.printStackTrace();
}
}
// Let plugins use this version instead
return delayed;
} else {
// The standard manager
PacketFilterManager manager = new PacketFilterManager(
classLoader, server, library, asyncManager, mcVersion, unhookTask, reporter, false);
asyncManager.setManager(manager);
return manager;
}
/**
* Construct a new packet filter builder.
* @return New builder.
*/
public static PacketFilterBuilder newBuilder() {
return new PacketFilterBuilder();
}
@Override
public AsynchronousManager getAsynchronousManager() {
return asyncFilterManager;

View File

@ -31,6 +31,7 @@ import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
@ -98,6 +99,27 @@ public class InjectedServerConnection {
this.netLoginInjector = netLoginInjector;
}
/**
* Retrieve the current server connection.
* @param reporter - error reproter.
* @param server - the current server.
* @return The current server connection, or NULL if it hasn't been initialized yet.
* @throws FieldAccessException Reflection error.
*/
public static Object getServerConnection(ErrorReporter reporter, Server server) {
try {
// Now we are probably able to check for Netty
InjectedServerConnection inspector = new InjectedServerConnection(reporter, null, server, null);
return inspector.getServerConnection();
} catch (IllegalAccessException e) {
throw new FieldAccessException("Reflection error.", e);
} catch (IllegalArgumentException e) {
throw new FieldAccessException("Corrupt data.", e);
} catch (InvocationTargetException e) {
throw new FieldAccessException("Minecraft error.", e);
}
}
/**
* Initial reflective detective work. Will be automatically called by most methods in this class.
*/