Add support for the Netty Spigot build.
This required extensive reworking of the inner packet injection system in ProtocolLib. I've also fixed a minior bug in the fuzzy member contract class.
Dieser Commit ist enthalten in:
Ursprung
e8112dba0e
Commit
901ab1fdda
@ -78,9 +78,9 @@ class CleanupStaticMembers {
|
|||||||
"com.comphenix.protocol.injector.player.PlayerInjector",
|
"com.comphenix.protocol.injector.player.PlayerInjector",
|
||||||
"com.comphenix.protocol.injector.player.TemporaryPlayerFactory",
|
"com.comphenix.protocol.injector.player.TemporaryPlayerFactory",
|
||||||
"com.comphenix.protocol.injector.EntityUtilities",
|
"com.comphenix.protocol.injector.EntityUtilities",
|
||||||
"com.comphenix.protocol.injector.MinecraftRegistry",
|
"com.comphenix.protocol.injector.packet.PacketRegistry",
|
||||||
"com.comphenix.protocol.injector.PacketInjector",
|
"com.comphenix.protocol.injector.packet.PacketInjector",
|
||||||
"com.comphenix.protocol.injector.ReadPacketModifier",
|
"com.comphenix.protocol.injector.packet.ReadPacketModifier",
|
||||||
"com.comphenix.protocol.injector.StructureCache",
|
"com.comphenix.protocol.injector.StructureCache",
|
||||||
"com.comphenix.protocol.reflect.compiler.BoxingHelper",
|
"com.comphenix.protocol.reflect.compiler.BoxingHelper",
|
||||||
"com.comphenix.protocol.reflect.compiler.MethodDescriptor"
|
"com.comphenix.protocol.reflect.compiler.MethodDescriptor"
|
||||||
|
@ -44,7 +44,7 @@ public class IntegerSet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether or not the given element exists in the set.
|
* Determine whether or not the given element exists in the set.
|
||||||
* @param value - the element to check. Must be in the range [0, count).
|
* @param element - the element to check. Must be in the range [0, count).
|
||||||
* @return TRUE if the given element exists, FALSE otherwise.
|
* @return TRUE if the given element exists, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean contains(int element) {
|
public boolean contains(int element) {
|
||||||
|
@ -56,6 +56,7 @@ import com.comphenix.protocol.injector.packet.PacketInjectorBuilder;
|
|||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
|
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
|
||||||
|
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
@ -142,6 +143,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
// Whether or not plugins are using the send/receive methods
|
// Whether or not plugins are using the send/receive methods
|
||||||
private AtomicBoolean packetCreation = new AtomicBoolean();
|
private AtomicBoolean packetCreation = new AtomicBoolean();
|
||||||
|
|
||||||
|
// Spigot listener, if in use
|
||||||
|
private SpigotPacketInjector spigotInjector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only create instances of this class if protocol lib is disabled.
|
* Only create instances of this class if protocol lib is disabled.
|
||||||
* @param unhookTask
|
* @param unhookTask
|
||||||
@ -181,22 +185,30 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize injection mangers
|
// Spigot
|
||||||
this.playerInjection = PlayerInjectorBuilder.newBuilder().
|
if (SpigotPacketInjector.canUseSpigotListener()) {
|
||||||
invoker(this).
|
spigotInjector = new SpigotPacketInjector(classLoader, reporter, this, server);
|
||||||
server(server).
|
this.playerInjection = spigotInjector.getPlayerHandler();
|
||||||
reporter(reporter).
|
this.packetInjector = spigotInjector.getPacketInjector();
|
||||||
classLoader(classLoader).
|
|
||||||
packetListeners(packetListeners).
|
} else {
|
||||||
injectionFilter(isInjectionNecessary).
|
// Initialize standard injection mangers
|
||||||
buildHandler();
|
this.playerInjection = PlayerInjectorBuilder.newBuilder().
|
||||||
|
invoker(this).
|
||||||
this.packetInjector = PacketInjectorBuilder.newBuilder().
|
server(server).
|
||||||
invoker(this).
|
reporter(reporter).
|
||||||
reporter(reporter).
|
classLoader(classLoader).
|
||||||
classLoader(classLoader).
|
packetListeners(packetListeners).
|
||||||
playerInjection(playerInjection).
|
injectionFilter(isInjectionNecessary).
|
||||||
buildInjector();
|
buildHandler();
|
||||||
|
|
||||||
|
this.packetInjector = PacketInjectorBuilder.newBuilder().
|
||||||
|
invoker(this).
|
||||||
|
reporter(reporter).
|
||||||
|
classLoader(classLoader).
|
||||||
|
playerInjection(playerInjection).
|
||||||
|
buildInjector();
|
||||||
|
}
|
||||||
|
|
||||||
this.asyncFilterManager = new AsyncFilterManager(reporter, server.getScheduler(), this);
|
this.asyncFilterManager = new AsyncFilterManager(reporter, server.getScheduler(), this);
|
||||||
|
|
||||||
@ -618,6 +630,8 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
|||||||
* @param plugin - the parent plugin.
|
* @param plugin - the parent plugin.
|
||||||
*/
|
*/
|
||||||
public void registerEvents(PluginManager manager, final Plugin plugin) {
|
public void registerEvents(PluginManager manager, final Plugin plugin) {
|
||||||
|
if (spigotInjector != null && !spigotInjector.register(plugin))
|
||||||
|
throw new IllegalArgumentException("Spigot has already been registered.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
manager.registerEvents(new Listener() {
|
manager.registerEvents(new Listener() {
|
||||||
|
@ -28,6 +28,7 @@ import net.sf.cglib.proxy.MethodProxy;
|
|||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.bukkit.Server;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
@ -38,13 +39,14 @@ import com.comphenix.protocol.events.PacketListener;
|
|||||||
import com.comphenix.protocol.injector.GamePhase;
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
|
import com.comphenix.protocol.injector.player.TemporaryPlayerFactory.InjectContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
|
* Injection method that overrides the NetworkHandler itself, and it's queue-method.
|
||||||
*
|
*
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class NetworkObjectInjector extends PlayerInjector {
|
public class NetworkObjectInjector extends PlayerInjector {
|
||||||
// Determine if we're listening
|
// Determine if we're listening
|
||||||
private IntegerSet sendingFilters;
|
private IntegerSet sendingFilters;
|
||||||
|
|
||||||
@ -54,6 +56,9 @@ class NetworkObjectInjector extends PlayerInjector {
|
|||||||
// Shared callback filter - avoid creating a new class every time
|
// Shared callback filter - avoid creating a new class every time
|
||||||
private static CallbackFilter callbackFilter;
|
private static CallbackFilter callbackFilter;
|
||||||
|
|
||||||
|
// Temporary player factory
|
||||||
|
private static volatile TemporaryPlayerFactory tempPlayerFactory;
|
||||||
|
|
||||||
public NetworkObjectInjector(ClassLoader classLoader, ErrorReporter reporter, Player player,
|
public NetworkObjectInjector(ClassLoader classLoader, ErrorReporter reporter, Player player,
|
||||||
ListenerInvoker invoker, IntegerSet sendingFilters) throws IllegalAccessException {
|
ListenerInvoker invoker, IntegerSet sendingFilters) throws IllegalAccessException {
|
||||||
super(reporter, player, invoker);
|
super(reporter, player, invoker);
|
||||||
@ -66,6 +71,21 @@ class NetworkObjectInjector extends PlayerInjector {
|
|||||||
return sendingFilters.contains(packetID);
|
return sendingFilters.contains(packetID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a temporary player for use during login.
|
||||||
|
* @param server - Bukkit server.
|
||||||
|
* @return The temporary player.
|
||||||
|
*/
|
||||||
|
public Player createTemporaryPlayer(Server server) {
|
||||||
|
if (tempPlayerFactory == null)
|
||||||
|
tempPlayerFactory = new TemporaryPlayerFactory();
|
||||||
|
|
||||||
|
// Create and associate this fake player with this network injector
|
||||||
|
Player player = tempPlayerFactory.createTemporaryPlayer(server);
|
||||||
|
((InjectContainer) player).setInjector(this);
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
|
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
|
||||||
Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue();
|
Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue();
|
||||||
|
@ -52,7 +52,7 @@ import com.google.common.collect.Maps;
|
|||||||
*
|
*
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class NetworkServerInjector extends PlayerInjector {
|
class NetworkServerInjector extends PlayerInjector {
|
||||||
|
|
||||||
private volatile static CallbackFilter callbackFilter;
|
private volatile static CallbackFilter callbackFilter;
|
||||||
|
|
||||||
|
@ -58,10 +58,10 @@ abstract class PlayerInjector {
|
|||||||
protected static Field proxyServerField;
|
protected static Field proxyServerField;
|
||||||
|
|
||||||
protected static Field networkManagerField;
|
protected static Field networkManagerField;
|
||||||
protected static Field inputField;
|
|
||||||
protected static Field netHandlerField;
|
protected static Field netHandlerField;
|
||||||
protected static Field socketField;
|
protected static Field socketField;
|
||||||
|
|
||||||
|
private static Field inputField;
|
||||||
private static Field entityPlayerField;
|
private static Field entityPlayerField;
|
||||||
|
|
||||||
// Whether or not we're using a proxy type
|
// Whether or not we're using a proxy type
|
||||||
@ -206,11 +206,6 @@ abstract class PlayerInjector {
|
|||||||
if (queueMethod == null)
|
if (queueMethod == null)
|
||||||
queueMethod = FuzzyReflection.fromClass(reference.getType()).
|
queueMethod = FuzzyReflection.fromClass(reference.getType()).
|
||||||
getMethodByParameters("queue", MinecraftReflection.getPacketClass());
|
getMethodByParameters("queue", MinecraftReflection.getPacketClass());
|
||||||
|
|
||||||
// And the data input stream that we'll use to identify a player
|
|
||||||
if (inputField == null)
|
|
||||||
inputField = FuzzyReflection.fromObject(networkManager, true).
|
|
||||||
getFieldByType("java\\.io\\.DataInputStream");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -250,9 +245,9 @@ abstract class PlayerInjector {
|
|||||||
public Socket getSocket() throws IllegalAccessException {
|
public Socket getSocket() throws IllegalAccessException {
|
||||||
try {
|
try {
|
||||||
if (socketField == null)
|
if (socketField == null)
|
||||||
socketField = FuzzyReflection.fromObject(networkManager).getFieldListByType(Socket.class).get(0);
|
socketField = FuzzyReflection.fromObject(networkManager, true).getFieldListByType(Socket.class).get(0);
|
||||||
if (socket == null)
|
if (socket == null)
|
||||||
socket = (Socket) FieldUtils.readField(socketField, networkManager);
|
socket = (Socket) FieldUtils.readField(socketField, networkManager, true);
|
||||||
return socket;
|
return socket;
|
||||||
|
|
||||||
} catch (IndexOutOfBoundsException e) {
|
} catch (IndexOutOfBoundsException e) {
|
||||||
@ -570,11 +565,13 @@ abstract class PlayerInjector {
|
|||||||
* @return The player's input stream.
|
* @return The player's input stream.
|
||||||
*/
|
*/
|
||||||
public DataInputStream getInputStream(boolean cache) {
|
public DataInputStream getInputStream(boolean cache) {
|
||||||
if (inputField == null)
|
// And the data input stream that we'll use to identify a player
|
||||||
throw new IllegalStateException("Input field is NULL.");
|
|
||||||
if (networkManager == null)
|
if (networkManager == null)
|
||||||
throw new IllegalStateException("Network manager is NULL.");
|
throw new IllegalStateException("Network manager is NULL.");
|
||||||
|
if (inputField == null)
|
||||||
|
inputField = FuzzyReflection.fromObject(networkManager, true).
|
||||||
|
getFieldByType("java\\.io\\.DataInputStream");
|
||||||
|
|
||||||
// Get the associated input stream
|
// Get the associated input stream
|
||||||
try {
|
try {
|
||||||
if (cache && cachedInput != null)
|
if (cache && cachedInput != null)
|
||||||
@ -604,6 +601,16 @@ abstract class PlayerInjector {
|
|||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the hooked player.
|
||||||
|
* <p>
|
||||||
|
* Should only be called during the creation of the injector.
|
||||||
|
* @param player - the new hooked player.
|
||||||
|
*/
|
||||||
|
public void setPlayer(Player player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object that can invoke the packet events.
|
* Object that can invoke the packet events.
|
||||||
* @return Packet event invoker.
|
* @return Packet event invoker.
|
||||||
@ -622,4 +629,12 @@ abstract class PlayerInjector {
|
|||||||
else
|
else
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the real Bukkit player that we will use.
|
||||||
|
* @param updatedPlayer - the real Bukkit player.
|
||||||
|
*/
|
||||||
|
public void setUpdatedPlayer(Player updatedPlayer) {
|
||||||
|
this.updatedPlayer = updatedPlayer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,8 @@ public class PlayerInjectorBuilder {
|
|||||||
// Fill any default fields
|
// Fill any default fields
|
||||||
initializeDefaults();
|
initializeDefaults();
|
||||||
|
|
||||||
return new ProxyPlayerInjectionHandler(classLoader, reporter, injectionFilter, invoker,
|
return new ProxyPlayerInjectionHandler(
|
||||||
packetListeners, server);
|
classLoader, reporter, injectionFilter,
|
||||||
|
invoker, packetListeners, server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ class TemporaryPlayerFactory {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* Note that the player a player has not been assigned a name yet, and thus cannot be
|
* Note that the player a player has not been assigned a name yet, and thus cannot be
|
||||||
* uniquely identified. Use the
|
* uniquely identified. Use the address instead.
|
||||||
* @param injector - the player injector used.
|
* @param injector - the player injector used.
|
||||||
* @param server - the current server.
|
* @param server - the current server.
|
||||||
* @return A temporary player instance.
|
* @return A temporary player instance.
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.comphenix.protocol.injector.spigot;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||||
|
|
||||||
|
public class DummyPacketInjector implements PacketInjector {
|
||||||
|
private SpigotPacketInjector injector;
|
||||||
|
private IntegerSet reveivedFilters;
|
||||||
|
|
||||||
|
public DummyPacketInjector(SpigotPacketInjector injector, IntegerSet reveivedFilters) {
|
||||||
|
this.injector = injector;
|
||||||
|
this.reveivedFilters = reveivedFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoCancel(Integer id, Object packet) {
|
||||||
|
// Do nothing yet
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addPacketHandler(int packetID) {
|
||||||
|
reveivedFilters.add(packetID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removePacketHandler(int packetID) {
|
||||||
|
reveivedFilters.remove(packetID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPacketHandler(int packetID) {
|
||||||
|
return reveivedFilters.contains(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getPacketHandlers() {
|
||||||
|
return reveivedFilters.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketEvent packetRecieved(PacketContainer packet, Player client) {
|
||||||
|
return injector.packetReceived(packet, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanupAll() {
|
||||||
|
reveivedFilters.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
package com.comphenix.protocol.injector.spigot;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.comphenix.protocol.injector.GamePhase;
|
||||||
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||||
|
|
||||||
|
class DummyPlayerHandler implements PlayerInjectionHandler {
|
||||||
|
private SpigotPacketInjector injector;
|
||||||
|
private IntegerSet sendingFilters;
|
||||||
|
|
||||||
|
public DummyPlayerHandler(SpigotPacketInjector injector, IntegerSet sendingFilters) {
|
||||||
|
this.injector = injector;
|
||||||
|
this.sendingFilters = sendingFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean uninjectPlayer(InetSocketAddress address) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean uninjectPlayer(Player player) {
|
||||||
|
injector.uninjectPlayer(player);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlayerHook(GamePhase phase, PlayerInjectHooks playerHook) {
|
||||||
|
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||||
|
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scheduleDataInputRefresh(Player player) {
|
||||||
|
// Fine
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPacketHandler(int packetID) {
|
||||||
|
sendingFilters.add(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePacketHandler(int packetID) {
|
||||||
|
sendingFilters.remove(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getSendingFilters() {
|
||||||
|
return sendingFilters.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
sendingFilters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
||||||
|
injector.sendServerPacket(reciever, packet, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
injector.processPacket(player, mcPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectPlayer(Player player) {
|
||||||
|
injector.injectPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleDisconnect(Player player) {
|
||||||
|
// Just ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlayerInjectHooks getPlayerHook(GamePhase phase) {
|
||||||
|
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlayerInjectHooks getPlayerHook() {
|
||||||
|
// Pretend that we do
|
||||||
|
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Player getPlayerByConnection(DataInputStream inputStream, long playerTimeout, TimeUnit unit) throws InterruptedException {
|
||||||
|
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Player getPlayerByConnection(DataInputStream inputStream) throws InterruptedException {
|
||||||
|
throw new UnsupportedOperationException("This is not needed in Spigot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkListener(PacketListener listener) {
|
||||||
|
// They're all fine!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkListener(Set<PacketListener> listeners) {
|
||||||
|
// Yes, really
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,447 @@
|
|||||||
|
package com.comphenix.protocol.injector.spigot;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import net.sf.cglib.proxy.Callback;
|
||||||
|
import net.sf.cglib.proxy.CallbackFilter;
|
||||||
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
|
import net.sf.cglib.proxy.MethodInterceptor;
|
||||||
|
import net.sf.cglib.proxy.MethodProxy;
|
||||||
|
import net.sf.cglib.proxy.NoOp;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.Packets;
|
||||||
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
|
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||||
|
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||||
|
import com.comphenix.protocol.injector.player.NetworkObjectInjector;
|
||||||
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||||
|
import com.comphenix.protocol.reflect.MethodInfo;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offload all the work to Spigot, if possible.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class SpigotPacketInjector implements SpigotPacketListener {
|
||||||
|
// Lazily retrieve the spigot listener class
|
||||||
|
private static volatile Class<?> spigotListenerClass;
|
||||||
|
private static volatile boolean classChecked;
|
||||||
|
|
||||||
|
// Packets that are not to be processed by the filters
|
||||||
|
private Set<Object> ignoredPackets = Collections.newSetFromMap(new MapMaker().weakKeys().<Object, Boolean>makeMap());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of ticks to wait before removing all traces of a player.
|
||||||
|
*/
|
||||||
|
private static final int CLEANUP_DELAY = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the spigot packet listener class.
|
||||||
|
* @return The listener class.
|
||||||
|
*/
|
||||||
|
private static Class<?> getSpigotListenerClass() {
|
||||||
|
if (!classChecked) {
|
||||||
|
try {
|
||||||
|
spigotListenerClass = SpigotPacketInjector.class.getClassLoader().loadClass("org.spigotmc.netty.PacketListener");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
// We've given it a try now
|
||||||
|
classChecked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spigotListenerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the register packet listener method.
|
||||||
|
* @return The method used to register a packet listener.
|
||||||
|
*/
|
||||||
|
private static Method getRegisterMethod() {
|
||||||
|
Class<?> clazz = getSpigotListenerClass();
|
||||||
|
|
||||||
|
if (clazz != null) {
|
||||||
|
try {
|
||||||
|
return clazz.getMethod("register", clazz, Plugin.class);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
// If this happens, then ... we're doomed
|
||||||
|
throw new RuntimeException("Reflection is not allowed.", e);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new IllegalStateException("Cannot find register() method in " + clazz, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also bad
|
||||||
|
throw new IllegalStateException("Spigot could not be found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if there is a Spigot packet listener.
|
||||||
|
* @return Spigot packet listener.
|
||||||
|
*/
|
||||||
|
public static boolean canUseSpigotListener() {
|
||||||
|
return getSpigotListenerClass() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The listener we will register on Spigot.
|
||||||
|
// Unfortunately, due to the use of PlayerConnection, INetworkManager and Packet, we're
|
||||||
|
// unable to reference it directly. But with CGLib, it shouldn't cost us much.
|
||||||
|
private Object dynamicListener;
|
||||||
|
|
||||||
|
// Reference to ProtocolLib
|
||||||
|
private Plugin plugin;
|
||||||
|
|
||||||
|
// Different sending filters
|
||||||
|
private IntegerSet queuedFilters;
|
||||||
|
private IntegerSet reveivedFilters;
|
||||||
|
|
||||||
|
// NetworkManager to injector and player
|
||||||
|
private ConcurrentMap<Object, NetworkObjectInjector> networkManagerInjector = new ConcurrentHashMap<Object, NetworkObjectInjector>();
|
||||||
|
|
||||||
|
// Player to injector
|
||||||
|
private ConcurrentMap<Player, NetworkObjectInjector> playerInjector = new ConcurrentHashMap<Player, NetworkObjectInjector>();
|
||||||
|
|
||||||
|
// Responsible for informing the PL packet listeners
|
||||||
|
private ListenerInvoker invoker;
|
||||||
|
private ErrorReporter reporter;
|
||||||
|
private Server server;
|
||||||
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new spigot injector.
|
||||||
|
*/
|
||||||
|
public SpigotPacketInjector(ClassLoader classLoader, ErrorReporter reporter, ListenerInvoker invoker, Server server) {
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
this.reporter = reporter;
|
||||||
|
this.invoker = invoker;
|
||||||
|
this.server = server;
|
||||||
|
this.queuedFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
|
||||||
|
this.reveivedFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean register(Plugin plugin) {
|
||||||
|
if (hasRegistered())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Save the plugin too
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
final Callback[] callbacks = new Callback[3];
|
||||||
|
final boolean[] found = new boolean[3];
|
||||||
|
|
||||||
|
// Packets received from the clients
|
||||||
|
callbacks[0] = new MethodInterceptor() {
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
|
return SpigotPacketInjector.this.packetReceived(args[0], args[1], args[2]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Packet sent/queued
|
||||||
|
callbacks[1] = new MethodInterceptor() {
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
|
return SpigotPacketInjector.this.packetQueued(args[0], args[1], args[2]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't care for everything else
|
||||||
|
callbacks[2] = NoOp.INSTANCE;
|
||||||
|
|
||||||
|
Enhancer enhancer = new Enhancer();
|
||||||
|
enhancer.setClassLoader(classLoader);
|
||||||
|
enhancer.setSuperclass(getSpigotListenerClass());
|
||||||
|
enhancer.setCallbacks(callbacks);
|
||||||
|
enhancer.setCallbackFilter(new CallbackFilter() {
|
||||||
|
@Override
|
||||||
|
public int accept(Method method) {
|
||||||
|
// We'll be pretty stringent
|
||||||
|
if (matchMethod("packetReceived", method)) {
|
||||||
|
found[0] = true;
|
||||||
|
return 0;
|
||||||
|
} else if (matchMethod("packetQueued", method)) {
|
||||||
|
found[1] = true;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
found[2] = true;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dynamicListener = enhancer.create();
|
||||||
|
|
||||||
|
// Verify methods
|
||||||
|
if (!found[0])
|
||||||
|
throw new IllegalStateException("Unable to find a valid packet receiver in Spigot.");
|
||||||
|
if (!found[1])
|
||||||
|
throw new IllegalStateException("Unable to find a valid packet queue in Spigot.");
|
||||||
|
|
||||||
|
// Lets register it too
|
||||||
|
try {
|
||||||
|
getRegisterMethod().invoke(null, dynamicListener, plugin);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Cannot register Spigot packet listener.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we succeed
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given method is a valid packet receiver or queued method.
|
||||||
|
* @param methodName - the expected name of the method.
|
||||||
|
* @param method - the method we're testing.
|
||||||
|
* @return TRUE if this is a correct method, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
private boolean matchMethod(String methodName, Method method) {
|
||||||
|
return FuzzyMethodContract.newBuilder().
|
||||||
|
nameExact(methodName).
|
||||||
|
parameterCount(3).
|
||||||
|
parameterSuperOf(MinecraftReflection.getNetHandlerClass(), 1).
|
||||||
|
parameterSuperOf(MinecraftReflection.getPacketClass(), 2).
|
||||||
|
returnTypeExact(MinecraftReflection.getPacketClass()).
|
||||||
|
build().
|
||||||
|
isMatch(MethodInfo.fromMethod(method), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasRegistered() {
|
||||||
|
return dynamicListener != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerInjectionHandler getPlayerHandler() {
|
||||||
|
return new DummyPlayerHandler(this, queuedFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketInjector getPacketInjector() {
|
||||||
|
return new DummyPacketInjector(this, reveivedFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the currently registered injector for the given player.
|
||||||
|
* @param player - injected player.
|
||||||
|
* @return The injector.
|
||||||
|
*/
|
||||||
|
NetworkObjectInjector getInjector(Player player) {
|
||||||
|
return playerInjector.get(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve or create a registered injector for the given network manager and connection.
|
||||||
|
* @param networkManager - a INetworkManager object.
|
||||||
|
* @param connection - a Connection (PlayerConnection, PendingConnection) object.
|
||||||
|
* @return The created NetworkObjectInjector with a temporary player.
|
||||||
|
*/
|
||||||
|
NetworkObjectInjector getInjector(Object networkManager, Object connection) {
|
||||||
|
NetworkObjectInjector dummyInjector = networkManagerInjector.get(networkManager);
|
||||||
|
|
||||||
|
if (dummyInjector == null) {
|
||||||
|
// Inject the network manager
|
||||||
|
try {
|
||||||
|
NetworkObjectInjector created = new NetworkObjectInjector(classLoader, reporter, null, invoker, null);
|
||||||
|
|
||||||
|
created.initializeLogin(connection);
|
||||||
|
created.setPlayer(created.createTemporaryPlayer(server));
|
||||||
|
dummyInjector = saveInjector(networkManager, created);
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Cannot create dummy injector.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dummyInjector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a given player injector for later.
|
||||||
|
* @param networkManager - the associated network manager.
|
||||||
|
* @param created - the created network object creator.
|
||||||
|
* @return Any other network injector that came before us.
|
||||||
|
*/
|
||||||
|
private NetworkObjectInjector saveInjector(Object networkManager, NetworkObjectInjector created) {
|
||||||
|
// Concurrency - use the same injector!
|
||||||
|
NetworkObjectInjector result = networkManagerInjector.putIfAbsent(networkManager, created);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
result = created;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the player as well
|
||||||
|
playerInjector.put(created.getPlayer(), created);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object packetReceived(Object networkManager, Object connection, Object packet) {
|
||||||
|
Integer id = invoker.getPacketID(packet);
|
||||||
|
|
||||||
|
if (id != null && reveivedFilters.contains(id)) {
|
||||||
|
// Check for ignored packets
|
||||||
|
if (ignoredPackets.remove(packet)) {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player sender = getInjector(networkManager, connection).getUpdatedPlayer();
|
||||||
|
PacketContainer container = new PacketContainer(id, packet);
|
||||||
|
PacketEvent event = packetReceived(container, sender);
|
||||||
|
|
||||||
|
if (!event.isCancelled())
|
||||||
|
return event.getPacket().getHandle();
|
||||||
|
else
|
||||||
|
return null; // Cancel
|
||||||
|
}
|
||||||
|
// Don't change anything
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object packetQueued(Object networkManager, Object connection, Object packet) {
|
||||||
|
Integer id = invoker.getPacketID(packet);
|
||||||
|
|
||||||
|
if (id != null & queuedFilters.contains(id)) {
|
||||||
|
// Check for ignored packets
|
||||||
|
if (ignoredPackets.remove(packet)) {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player reciever = getInjector(networkManager, connection).getUpdatedPlayer();
|
||||||
|
PacketContainer container = new PacketContainer(id, packet);
|
||||||
|
PacketEvent event = packetQueued(container, reciever);
|
||||||
|
|
||||||
|
if (!event.isCancelled())
|
||||||
|
return event.getPacket().getHandle();
|
||||||
|
else
|
||||||
|
return null; // Cancel
|
||||||
|
}
|
||||||
|
// Don't change anything
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to inform the event listeners of a queued packet.
|
||||||
|
* @param packet - the packet that is to be sent.
|
||||||
|
* @param reciever - the reciever of this packet.
|
||||||
|
* @return The packet event that was used.
|
||||||
|
*/
|
||||||
|
PacketEvent packetQueued(PacketContainer packet, Player reciever) {
|
||||||
|
PacketEvent event = PacketEvent.fromServer(this, packet, reciever);
|
||||||
|
|
||||||
|
invoker.invokePacketSending(event);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to inform the event listeners of a received packet.
|
||||||
|
* @param packet - the packet that has been receieved.
|
||||||
|
* @param sender - the client packet.
|
||||||
|
* @return The packet event that was used.
|
||||||
|
*/
|
||||||
|
PacketEvent packetReceived(PacketContainer packet, Player sender) {
|
||||||
|
PacketEvent event = PacketEvent.fromClient(this, packet, sender);
|
||||||
|
|
||||||
|
invoker.invokePacketRecieving(event);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a player has logged in properly.
|
||||||
|
* @param player - the player that has logged in.
|
||||||
|
*/
|
||||||
|
void injectPlayer(Player player) {
|
||||||
|
try {
|
||||||
|
NetworkObjectInjector dummy = new NetworkObjectInjector(classLoader, reporter, player, invoker, null);
|
||||||
|
dummy.initializePlayer(player);
|
||||||
|
|
||||||
|
// Save this player for the network manager
|
||||||
|
NetworkObjectInjector realInjector = networkManagerInjector.get(dummy.getNetworkManager());
|
||||||
|
|
||||||
|
if (realInjector != null) {
|
||||||
|
// Update all future references
|
||||||
|
realInjector.setUpdatedPlayer(player);
|
||||||
|
playerInjector.put(player, realInjector);
|
||||||
|
} else {
|
||||||
|
// Ah - in that case, save this injector
|
||||||
|
saveInjector(dummy.getNetworkManager(), dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Cannot inject " + player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninject the given player.
|
||||||
|
* @param player - the player to uninject.
|
||||||
|
*/
|
||||||
|
void uninjectPlayer(Player player) {
|
||||||
|
final NetworkObjectInjector injector = getInjector(player);
|
||||||
|
|
||||||
|
if (player != null) {
|
||||||
|
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Clean up
|
||||||
|
playerInjector.remove(injector.getPlayer());
|
||||||
|
playerInjector.remove(injector.getUpdatedPlayer());
|
||||||
|
networkManagerInjector.remove(injector);
|
||||||
|
}
|
||||||
|
}, CLEANUP_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a plugin wants to sent a packet.
|
||||||
|
* @param reciever - the packet receiver.
|
||||||
|
* @param packet - the packet to transmit.
|
||||||
|
* @param filters - whether or not to invoke the packet listeners.
|
||||||
|
* @throws InvocationTargetException If anything went wrong.
|
||||||
|
*/
|
||||||
|
void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
||||||
|
NetworkObjectInjector networkObject = getInjector(reciever);
|
||||||
|
|
||||||
|
// If TRUE, process this packet like any other
|
||||||
|
if (filters)
|
||||||
|
ignoredPackets.remove(packet.getHandle());
|
||||||
|
else
|
||||||
|
ignoredPackets.add(packet.getHandle());
|
||||||
|
|
||||||
|
if (networkObject != null)
|
||||||
|
networkObject.sendServerPacket(packet.getHandle(), filters);
|
||||||
|
else
|
||||||
|
throw new PlayerLoggedOutException("Player " + reciever + " has logged out");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a plugin wants to simulate receiving a packet.
|
||||||
|
* @param player - the supposed sender.
|
||||||
|
* @param mcPacket - the packet to receieve.
|
||||||
|
* @throws IllegalAccessException Reflection is not permitted.
|
||||||
|
* @throws InvocationTargetException Minecraft threw an exception.
|
||||||
|
*/
|
||||||
|
void processPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
NetworkObjectInjector networkObject = getInjector(player);
|
||||||
|
|
||||||
|
// We will always ignore this packet
|
||||||
|
ignoredPackets.add(mcPacket);
|
||||||
|
|
||||||
|
if (networkObject != null)
|
||||||
|
networkObject.processPacket(mcPacket);
|
||||||
|
else
|
||||||
|
throw new PlayerLoggedOutException("Player " + player + " has logged out");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.comphenix.protocol.injector.spigot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a proxy for a Spigot packet listener.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
interface SpigotPacketListener {
|
||||||
|
/**
|
||||||
|
* Called when a packet has been received and is about to be handled by the
|
||||||
|
* current Connection.
|
||||||
|
* <p>
|
||||||
|
* The returned packet will be the packet passed on for handling, or in the case of
|
||||||
|
* null being returned, not handled at all.
|
||||||
|
*
|
||||||
|
* @param networkManager the NetworkManager receiving the packet
|
||||||
|
* @param connection the connection which will handle the packet
|
||||||
|
* @param packet the received packet
|
||||||
|
* @return the packet to be handled, or null to cancel
|
||||||
|
*/
|
||||||
|
public Object packetReceived(Object networkManager, Object connection, Object packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a packet is queued to be sent.The returned packet will be
|
||||||
|
* the packet sent. In the case of null being returned, the packet will not
|
||||||
|
* be sent.
|
||||||
|
*
|
||||||
|
* @param networkManager the NetworkManager which will send the packet
|
||||||
|
* @param connection the connection which queued the packet
|
||||||
|
* @param packet the queue packet
|
||||||
|
* @return the packet to be sent, or null if the packet will not be sent.
|
||||||
|
*/
|
||||||
|
public Object packetQueued(Object networkManager, Object connection, Object packet);
|
||||||
|
}
|
@ -153,10 +153,7 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
|
|||||||
* Use this to prepare any special values.
|
* Use this to prepare any special values.
|
||||||
*/
|
*/
|
||||||
protected void prepareBuild() {
|
protected void prepareBuild() {
|
||||||
// Permit any modifier if we havent's specified anything
|
// No need to prepare anything
|
||||||
if (modifiersRequired == 0) {
|
|
||||||
modifiersRequired = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone a given contract
|
// Clone a given contract
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren