mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-27 13:15:52 +01:00
Remove a bunch of legacy (<1.8) code
Shouldn't break any servers running 1.8+, but this version is all about code cleanup baby
This commit is contained in:
parent
7ac4ac696f
commit
fdd30a7b87
6
pom.xml
6
pom.xml
@ -16,7 +16,7 @@
|
||||
<project.build.number></project.build.number>
|
||||
<project.fullVersion>${project.version}</project.fullVersion>
|
||||
|
||||
<powermock.version>2.0.4</powermock.version>
|
||||
<powermock.version>2.0.7</powermock.version>
|
||||
<spigot.version>1.15.2-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
@ -302,13 +302,13 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<version>4.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>3.3.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -42,83 +42,63 @@ public interface AsynchronousManager {
|
||||
* @param listener - the packet listener that will receive these asynchronous events.
|
||||
* @return An asynchronous handler.
|
||||
*/
|
||||
public abstract AsyncListenerHandler registerAsyncHandler(PacketListener listener);
|
||||
AsyncListenerHandler registerAsyncHandler(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters and closes the given asynchronous handler.
|
||||
* @param handler - asynchronous handler.
|
||||
*/
|
||||
public abstract void unregisterAsyncHandler(AsyncListenerHandler handler);
|
||||
void unregisterAsyncHandler(AsyncListenerHandler handler);
|
||||
|
||||
/**
|
||||
* Unregisters and closes the first asynchronous handler associated with the given listener.
|
||||
* @param listener - asynchronous listener
|
||||
*/
|
||||
public abstract void unregisterAsyncHandler(PacketListener listener);
|
||||
void unregisterAsyncHandler(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters every asynchronous handler associated with this plugin.
|
||||
* @param plugin - the original plugin.
|
||||
*/
|
||||
public void unregisterAsyncHandlers(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the sent server packets that will be
|
||||
* observed by the asynchronous listeners.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getSendingTypes()} instead.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract Set<Integer> getSendingFilters();
|
||||
void unregisterAsyncHandlers(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the types of the sent server packets that will be
|
||||
* observed by the asynchronous listeners.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
public abstract Set<PacketType> getSendingTypes();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the recieved client packets that will be
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getReceivingTypes()} instead.
|
||||
* observed by the asynchronous listeners.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract Set<Integer> getReceivingFilters();
|
||||
|
||||
Set<PacketType> getSendingTypes();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the types of the received client packets that will be
|
||||
* observed by the asynchronous listeners.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
public abstract Set<PacketType> getReceivingTypes();
|
||||
Set<PacketType> getReceivingTypes();
|
||||
|
||||
/**
|
||||
* Determine if a given synchronous packet has asynchronous listeners.
|
||||
* @param packet - packet to test.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean hasAsynchronousListeners(PacketEvent packet);
|
||||
boolean hasAsynchronousListeners(PacketEvent packet);
|
||||
|
||||
/**
|
||||
* Retrieve the default packet stream.
|
||||
* @return Default packet stream.
|
||||
*/
|
||||
public abstract PacketStream getPacketStream();
|
||||
PacketStream getPacketStream();
|
||||
|
||||
/**
|
||||
* Retrieve the default error reporter.
|
||||
* @return Default reporter.
|
||||
*/
|
||||
public abstract ErrorReporter getErrorReporter();
|
||||
ErrorReporter getErrorReporter();
|
||||
|
||||
/**
|
||||
* Remove listeners, close threads and transmit every delayed packet.
|
||||
*/
|
||||
public abstract void cleanupAll();
|
||||
void cleanupAll();
|
||||
|
||||
/**
|
||||
* Signal that a packet is ready to be transmitted.
|
||||
@ -127,29 +107,29 @@ public interface AsynchronousManager {
|
||||
* has been called previously.
|
||||
* @param packet - packet to signal.
|
||||
*/
|
||||
public abstract void signalPacketTransmission(PacketEvent packet);
|
||||
void signalPacketTransmission(PacketEvent packet);
|
||||
|
||||
/**
|
||||
* Register a synchronous listener that handles packets when they time out.
|
||||
* @param listener - synchronous listener that will handle timed out packets.
|
||||
*/
|
||||
public abstract void registerTimeoutHandler(PacketListener listener);
|
||||
void registerTimeoutHandler(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters a given timeout listener.
|
||||
* @param listener - the timeout listener to unregister.
|
||||
*/
|
||||
public abstract void unregisterTimeoutHandler(PacketListener listener);
|
||||
void unregisterTimeoutHandler(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Get a immutable set of every registered timeout handler.
|
||||
* @return Set of every registered timeout handler.
|
||||
*/
|
||||
public abstract Set<PacketListener> getTimeoutHandlers();
|
||||
Set<PacketListener> getTimeoutHandlers();
|
||||
|
||||
/**
|
||||
* Get an immutable set of every registered asynchronous packet listener.
|
||||
* @return Set of every asynchronous packet listener.
|
||||
*/
|
||||
public abstract Set<PacketListener> getAsyncHandlers();
|
||||
Set<PacketListener> getAsyncHandlers();
|
||||
}
|
||||
|
@ -1,170 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.comphenix.protocol.async.AsyncListenerHandler;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.MethodUtils;
|
||||
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;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.reflect.instances.PrimitiveGenerator;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.ChunkPosition;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
|
||||
|
||||
/**
|
||||
* Used to fix ClassLoader leaks that may lead to filling up the permanent generation.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class CleanupStaticMembers {
|
||||
// Reports
|
||||
public final static ReportType REPORT_CANNOT_RESET_FIELD = new ReportType("Unable to reset field %s: %s");
|
||||
public final static ReportType REPORT_CANNOT_UNLOAD_CLASS = new ReportType("Unable to unload class %s.");
|
||||
|
||||
private ClassLoader loader;
|
||||
private ErrorReporter reporter;
|
||||
|
||||
public CleanupStaticMembers(ClassLoader loader, ErrorReporter reporter) {
|
||||
this.loader = loader;
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the previous ClassLoader is not leaking.
|
||||
*/
|
||||
public void resetAll() {
|
||||
// This list must always be updated
|
||||
@SuppressWarnings("deprecation")
|
||||
Class<?>[] publicClasses = {
|
||||
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
|
||||
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
|
||||
PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class,
|
||||
BackgroundCompiler.class, StructureCompiler.class,
|
||||
ObjectWriter.class, Packets.Server.class, Packets.Client.class,
|
||||
ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class,
|
||||
AbstractInputStreamLookup.class, TemporaryPlayerFactory.class, SpigotPacketInjector.class,
|
||||
MinecraftReflection.class, NbtBinarySerializer.class
|
||||
};
|
||||
|
||||
String[] internalClasses = {
|
||||
"com.comphenix.protocol.events.SerializedOfflinePlayer",
|
||||
"com.comphenix.protocol.injector.player.InjectedServerConnection",
|
||||
"com.comphenix.protocol.injector.player.NetworkFieldInjector",
|
||||
"com.comphenix.protocol.injector.player.NetworkObjectInjector",
|
||||
"com.comphenix.protocol.injector.player.NetworkServerInjector",
|
||||
"com.comphenix.protocol.injector.player.PlayerInjector",
|
||||
"com.comphenix.protocol.injector.EntityUtilities",
|
||||
"com.comphenix.protocol.injector.packet.PacketRegistry",
|
||||
"com.comphenix.protocol.injector.packet.PacketInjector",
|
||||
"com.comphenix.protocol.injector.packet.ReadPacketModifier",
|
||||
"com.comphenix.protocol.injector.StructureCache",
|
||||
"com.comphenix.protocol.reflect.compiler.BoxingHelper",
|
||||
"com.comphenix.protocol.reflect.compiler.MethodDescriptor",
|
||||
"com.comphenix.protocol.wrappers.nbt.WrappedElement",
|
||||
};
|
||||
|
||||
resetClasses(publicClasses);
|
||||
resetClasses(getClasses(loader, internalClasses));
|
||||
}
|
||||
|
||||
private void resetClasses(Class<?>[] classes) {
|
||||
// Reset each class one by one
|
||||
for (Class<?> clazz : classes) {
|
||||
resetClass(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetClass(Class<?> clazz) {
|
||||
for (Field field : clazz.getFields()) {
|
||||
Class<?> type = field.getType();
|
||||
|
||||
// Only check static non-primitive fields. We also skip strings.
|
||||
if (Modifier.isStatic(field.getModifiers()) &&
|
||||
!type.isPrimitive() && !type.equals(String.class) &&
|
||||
!type.equals(ReportType.class)) {
|
||||
|
||||
try {
|
||||
setFinalStatic(field, null);
|
||||
} catch (IllegalAccessException e) {
|
||||
// Just inform the player
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_RESET_FIELD).error(e).messageParam(field.getName(), e.getMessage())
|
||||
);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HACK! HAACK!
|
||||
private static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException {
|
||||
int modifier = field.getModifiers();
|
||||
boolean isFinal = Modifier.isFinal(modifier);
|
||||
|
||||
Field modifiersField = isFinal ? FieldUtils.getField(Field.class, "modifiers", true) : null;
|
||||
|
||||
// We have to remove the final field first
|
||||
if (isFinal) {
|
||||
FieldUtils.writeField(modifiersField, field, modifier & ~Modifier.FINAL, true);
|
||||
}
|
||||
|
||||
// Now we can safely modify the field
|
||||
FieldUtils.writeStaticField(field, newValue, true);
|
||||
|
||||
// Revert modifier
|
||||
if (isFinal) {
|
||||
FieldUtils.writeField(modifiersField, field, modifier, true);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?>[] getClasses(ClassLoader loader, String[] names) {
|
||||
List<Class<?>> output = new ArrayList<Class<?>>();
|
||||
|
||||
for (String name : names) {
|
||||
try {
|
||||
output.add(loader.loadClass(name));
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Warn the user
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_UNLOAD_CLASS).error(e).messageParam(name));
|
||||
}
|
||||
}
|
||||
|
||||
return output.toArray(new Class<?>[0]);
|
||||
}
|
||||
}
|
@ -6,8 +6,6 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
|
||||
@ -15,15 +13,14 @@ import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
/**
|
||||
* Represents the type of a packet in a specific protocol.
|
||||
@ -953,34 +950,29 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
||||
* Note that the registration will be performed on the main thread.
|
||||
* @param type - the type to register.
|
||||
* @param name - the name of the packet.
|
||||
* @return A future telling us if our instance was registered.
|
||||
*/
|
||||
public static Future<Boolean> scheduleRegister(final PacketType type, final String name) {
|
||||
Callable<Boolean> callable = new Callable<Boolean>() {
|
||||
public static void scheduleRegister(final PacketType type, final String name) {
|
||||
BukkitRunnable runnable = new BukkitRunnable() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
public void run() {
|
||||
PacketTypeEnum objEnum;
|
||||
|
||||
// A bit ugly, but performance is critical
|
||||
objEnum = getObjectEnum(type);
|
||||
|
||||
if (objEnum.registerMember(type, name)) {
|
||||
getLookup().addPacketTypes(Arrays.asList(type));
|
||||
return true;
|
||||
getLookup().addPacketTypes(Collections.singletonList(type));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Execute in the main thread if possible
|
||||
if (Bukkit.getServer() == null || Bukkit.isPrimaryThread()) {
|
||||
try {
|
||||
return Futures.immediateFuture(callable.call());
|
||||
} catch (Exception e) {
|
||||
return Futures.immediateFailedFuture(e);
|
||||
}
|
||||
runnable.run();
|
||||
} catch (Exception ignored) { }
|
||||
} else {
|
||||
runnable.runTaskLater(ProtocolLibrary.getPlugin(), 0);
|
||||
}
|
||||
return ProtocolLibrary.getExecutorSync().submit(callable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,300 +0,0 @@
|
||||
/*
|
||||
* 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 com.comphenix.protocol.reflect.IntEnum;
|
||||
|
||||
/**
|
||||
* List of known packet IDs since 1.3.2.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @author Kristian
|
||||
*/
|
||||
@Deprecated
|
||||
public final class Packets {
|
||||
|
||||
/**
|
||||
* The highest possible packet ID. It's unlikely that this value will ever change.
|
||||
*/
|
||||
public static final int MAXIMUM_PACKET_ID = 255;
|
||||
|
||||
/**
|
||||
* The maximum number of unique packet IDs. It's unlikely this will ever change.
|
||||
*/
|
||||
public static final int PACKET_COUNT = 256;
|
||||
|
||||
/**
|
||||
* List of packets sent only by the server.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @author Kristian
|
||||
*/
|
||||
@Deprecated
|
||||
public static final class Server extends IntEnum {
|
||||
/**
|
||||
* The singleton instance. Can also be retrieved from the parent class.
|
||||
*/
|
||||
private static Server INSTANCE = new Server();
|
||||
|
||||
public static final int KEEP_ALIVE = 0;
|
||||
public static final int LOGIN = 1;
|
||||
public static final int CHAT = 3;
|
||||
public static final int UPDATE_TIME = 4;
|
||||
public static final int ENTITY_EQUIPMENT = 5;
|
||||
public static final int SPAWN_POSITION = 6;
|
||||
public static final int UPDATE_HEALTH = 8;
|
||||
public static final int RESPAWN = 9;
|
||||
public static final int FLYING = 10;
|
||||
public static final int PLAYER_POSITION = 11;
|
||||
public static final int PLAYER_LOOK = 12;
|
||||
public static final int PLAYER_LOOK_MOVE = 13;
|
||||
/**
|
||||
* Made bi-directional in 1.4.6.
|
||||
*/
|
||||
public static final int BLOCK_ITEM_SWITCH = 16;
|
||||
public static final int ENTITY_LOCATION_ACTION = 17;
|
||||
public static final int ARM_ANIMATION = 18;
|
||||
public static final int NAMED_ENTITY_SPAWN = 20;
|
||||
/**
|
||||
* Removed in 1.4.6 and replaced with VEHICLE_SPAWN.
|
||||
* @see <a href="http://www.wiki.vg/Protocol_History#2012-12-20">Protocol History - MinecraftCoalition</a>
|
||||
*/
|
||||
@Deprecated()
|
||||
public static final int PICKUP_SPAWN = 21;
|
||||
public static final int COLLECT = 22;
|
||||
public static final int VEHICLE_SPAWN = 23;
|
||||
public static final int MOB_SPAWN = 24;
|
||||
public static final int ENTITY_PAINTING = 25;
|
||||
public static final int ADD_EXP_ORB = 26;
|
||||
public static final int ENTITY_VELOCITY = 28;
|
||||
public static final int DESTROY_ENTITY = 29;
|
||||
public static final int ENTITY = 30;
|
||||
public static final int REL_ENTITY_MOVE = 31;
|
||||
public static final int ENTITY_LOOK = 32;
|
||||
public static final int REL_ENTITY_MOVE_LOOK = 33;
|
||||
public static final int ENTITY_TELEPORT = 34;
|
||||
public static final int ENTITY_HEAD_ROTATION = 35;
|
||||
public static final int ENTITY_STATUS = 38;
|
||||
public static final int ATTACH_ENTITY = 39;
|
||||
|
||||
/**
|
||||
* Sent when an entities DataWatcher is updated.
|
||||
* <p>
|
||||
* Remember to clone the packet if you are modifying it.
|
||||
*/
|
||||
public static final int ENTITY_METADATA = 40;
|
||||
public static final int MOB_EFFECT = 41;
|
||||
public static final int REMOVE_MOB_EFFECT = 42;
|
||||
public static final int SET_EXPERIENCE = 43;
|
||||
public static final int UPDATE_ATTRIBUTES = 44;
|
||||
public static final int MAP_CHUNK = 51;
|
||||
public static final int MULTI_BLOCK_CHANGE = 52;
|
||||
public static final int BLOCK_CHANGE = 53;
|
||||
public static final int PLAY_NOTE_BLOCK = 54;
|
||||
public static final int BLOCK_BREAK_ANIMATION = 55;
|
||||
public static final int MAP_CHUNK_BULK = 56;
|
||||
public static final int EXPLOSION = 60;
|
||||
public static final int WORLD_EVENT = 61;
|
||||
public static final int NAMED_SOUND_EFFECT = 62;
|
||||
public static final int WORLD_PARTICLES = 63;
|
||||
public static final int BED = 70;
|
||||
public static final int WEATHER = 71;
|
||||
public static final int OPEN_WINDOW = 100;
|
||||
public static final int CLOSE_WINDOW = 101;
|
||||
public static final int SET_SLOT = 103;
|
||||
public static final int WINDOW_ITEMS = 104;
|
||||
public static final int CRAFT_PROGRESS_BAR = 105;
|
||||
public static final int TRANSACTION = 106;
|
||||
public static final int SET_CREATIVE_SLOT = 107;
|
||||
public static final int UPDATE_SIGN = 130;
|
||||
public static final int ITEM_DATA = 131;
|
||||
|
||||
/**
|
||||
* Sent the first time a tile entity (chest inventory, etc.) is withing range of the player, or has been updated.
|
||||
* <p>
|
||||
* Remember to clone the packet if you are modifying it.
|
||||
*/
|
||||
public static final int TILE_ENTITY_DATA = 132;
|
||||
public static final int OPEN_TILE_ENTITY = 133;
|
||||
public static final int STATISTIC = 200;
|
||||
public static final int PLAYER_INFO = 201;
|
||||
public static final int ABILITIES = 202;
|
||||
public static final int TAB_COMPLETE = 203;
|
||||
public static final int SCOREBOARD_OBJECTIVE = 206;
|
||||
public static final int UPDATE_SCORE = 207;
|
||||
public static final int DISPLAY_SCOREBOARD = 208;
|
||||
public static final int TEAMS = 209;
|
||||
public static final int CUSTOM_PAYLOAD = 250;
|
||||
public static final int KEY_RESPONSE = 252;
|
||||
public static final int KEY_REQUEST = 253;
|
||||
public static final int KICK_DISCONNECT = 255;
|
||||
|
||||
/**
|
||||
* This packet was introduced in 1.7.2.
|
||||
*/
|
||||
public static final int PING_TIME = 230;
|
||||
|
||||
/**
|
||||
* This packet was introduced in 1.7.2.
|
||||
*/
|
||||
public static final int LOGIN_SUCCESS = 232;
|
||||
|
||||
/**
|
||||
* A registry that parses between names and packet IDs.
|
||||
* @return The current server registry.
|
||||
*/
|
||||
public static Server getRegistry() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
// We only allow a single instance of this class
|
||||
private Server() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of packets sent by the client.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @author Kristian
|
||||
*/
|
||||
@Deprecated
|
||||
public static class Client extends IntEnum {
|
||||
/**
|
||||
* The singleton instance. Can also be retrieved from the parent class.
|
||||
*/
|
||||
private static Client INSTANCE = new Client();
|
||||
|
||||
public static final int KEEP_ALIVE = 0;
|
||||
public static final int LOGIN = 1;
|
||||
public static final int HANDSHAKE = 2;
|
||||
public static final int CHAT = 3;
|
||||
public static final int USE_ENTITY = 7;
|
||||
|
||||
/**
|
||||
* Since 1.3.1, the client no longer sends a respawn packet. Moved to CLIENT_COMMAND.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int RESPAWN = 9;
|
||||
|
||||
public static final int FLYING = 10;
|
||||
public static final int PLAYER_POSITION = 11;
|
||||
public static final int PLAYER_LOOK = 12;
|
||||
public static final int PLAYER_LOOK_MOVE = 13;
|
||||
public static final int BLOCK_DIG = 14;
|
||||
public static final int PLACE = 15;
|
||||
public static final int BLOCK_ITEM_SWITCH = 16;
|
||||
public static final int ARM_ANIMATION = 18;
|
||||
public static final int ENTITY_ACTION = 19;
|
||||
public static final int PLAYER_INPUT = 27;
|
||||
public static final int CLOSE_WINDOW = 101;
|
||||
public static final int WINDOW_CLICK = 102;
|
||||
public static final int TRANSACTION = 106;
|
||||
public static final int SET_CREATIVE_SLOT = 107;
|
||||
public static final int BUTTON_CLICK = 108;
|
||||
public static final int UPDATE_SIGN = 130;
|
||||
public static final int ABILITIES = 202;
|
||||
public static final int TAB_COMPLETE = 203;
|
||||
public static final int LOCALE_AND_VIEW_DISTANCE = 204;
|
||||
public static final int CLIENT_COMMAND = 205;
|
||||
public static final int CUSTOM_PAYLOAD = 250;
|
||||
public static final int KEY_RESPONSE = 252;
|
||||
public static final int GET_INFO = 254;
|
||||
public static final int KICK_DISCONNECT = 255;
|
||||
|
||||
/**
|
||||
* This packet was introduced in 1.7.2.
|
||||
*/
|
||||
public static final int PING_TIME = 230;
|
||||
|
||||
/**
|
||||
* This packet was introduced in 1.7.2.
|
||||
*/
|
||||
public static final int LOGIN_START = 231;
|
||||
|
||||
/**
|
||||
* A registry that parses between names and packet IDs.
|
||||
* @return The current client registry.
|
||||
*/
|
||||
public static Client getRegistry() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
// Like above
|
||||
private Client() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A registry that parses between names and packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @return The current client registry.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Server getServerRegistry() {
|
||||
return Server.getRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* A registry that parses between names and packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @return The current server registry.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Client getClientRegistry() {
|
||||
return Client.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a packet by name. Must be capitalized and use underscores.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @param name - name of packet to find.
|
||||
* @return The packet ID found.
|
||||
*/
|
||||
@Deprecated
|
||||
public static int valueOf(String name) {
|
||||
Integer serverAttempt = Server.INSTANCE.valueOf(name);
|
||||
|
||||
if (serverAttempt != null)
|
||||
return serverAttempt;
|
||||
else
|
||||
return Client.INSTANCE.valueOf(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of a packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link PacketType} instead.
|
||||
* @param packetID - packet to retrieve name.
|
||||
* @return The name, or NULL if unable to find such a packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getDeclaredName(int packetID) {
|
||||
String serverAttempt = Server.INSTANCE.getDeclaredName(packetID);
|
||||
|
||||
if (serverAttempt != null)
|
||||
return serverAttempt;
|
||||
else
|
||||
return Client.INSTANCE.getDeclaredName(packetID);
|
||||
}
|
||||
}
|
@ -26,21 +26,8 @@ import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.comphenix.protocol.executors.BukkitExecutors;
|
||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||
import com.comphenix.protocol.error.BasicErrorReporter;
|
||||
import com.comphenix.protocol.error.DelegatedErrorReporter;
|
||||
import com.comphenix.protocol.error.DetailedErrorReporter;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.error.*;
|
||||
import com.comphenix.protocol.injector.DelayedSingleTask;
|
||||
import com.comphenix.protocol.injector.InternalManager;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
@ -55,7 +42,13 @@ import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* The main entry point for ProtocolLib.
|
||||
@ -112,10 +105,6 @@ public class ProtocolLib extends JavaPlugin {
|
||||
// Metrics and statistics
|
||||
private Statistics statistics;
|
||||
|
||||
// Executors
|
||||
private static ListeningScheduledExecutorService executorAsync;
|
||||
private static ListeningScheduledExecutorService executorSync;
|
||||
|
||||
// Structure compiler
|
||||
private BackgroundCompiler backgroundCompiler;
|
||||
|
||||
@ -157,10 +146,6 @@ public class ProtocolLib extends JavaPlugin {
|
||||
// Initialize enhancer factory
|
||||
EnhancerFactory.getInstance().setClassLoader(getClassLoader());
|
||||
|
||||
// Initialize executors
|
||||
executorAsync = BukkitExecutors.newAsynchronous(this);
|
||||
executorSync = BukkitExecutors.newSynchronous(this);
|
||||
|
||||
// Add global parameters
|
||||
DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this);
|
||||
reporter = getFilteredReporter(detailedReporter);
|
||||
@ -213,7 +198,7 @@ public class ProtocolLib extends JavaPlugin {
|
||||
.build();
|
||||
|
||||
// Initialize the API
|
||||
ProtocolLibrary.init(this, config, protocolManager, reporter, executorAsync, executorSync);
|
||||
ProtocolLibrary.init(this, config, protocolManager, reporter);
|
||||
|
||||
// Setup error reporter
|
||||
detailedReporter.addGlobalParameter("manager", protocolManager);
|
||||
|
@ -19,12 +19,11 @@ package com.comphenix.protocol;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.error.BasicErrorReporter;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* The main entry point for ProtocolLib.
|
||||
@ -56,21 +55,15 @@ public class ProtocolLibrary {
|
||||
private static ProtocolManager manager;
|
||||
private static ErrorReporter reporter = new BasicErrorReporter();
|
||||
|
||||
private static ListeningScheduledExecutorService executorAsync;
|
||||
private static ListeningScheduledExecutorService executorSync;
|
||||
|
||||
private static boolean updatesDisabled;
|
||||
private static boolean initialized;
|
||||
|
||||
protected static void init(Plugin plugin, ProtocolConfig config, ProtocolManager manager, ErrorReporter reporter,
|
||||
ListeningScheduledExecutorService executorAsync, ListeningScheduledExecutorService executorSync) {
|
||||
protected static void init(Plugin plugin, ProtocolConfig config, ProtocolManager manager, ErrorReporter reporter) {
|
||||
Validate.isTrue(!initialized, "ProtocolLib has already been initialized.");
|
||||
ProtocolLibrary.plugin = plugin;
|
||||
ProtocolLibrary.config = config;
|
||||
ProtocolLibrary.manager = manager;
|
||||
ProtocolLibrary.reporter = reporter;
|
||||
ProtocolLibrary.executorAsync = executorAsync;
|
||||
ProtocolLibrary.executorSync = executorSync;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@ -106,26 +99,6 @@ public class ProtocolLibrary {
|
||||
return reporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an executor service for performing asynchronous tasks on the behalf of ProtocolLib.
|
||||
* <p>
|
||||
* Note that this service is NULL if ProtocolLib has not been initialized yet.
|
||||
* @return The executor service, or NULL.
|
||||
*/
|
||||
public static ListeningScheduledExecutorService getExecutorAsync() {
|
||||
return executorAsync;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an executor service for performing synchronous tasks (main thread) on the behalf of ProtocolLib.
|
||||
* <p>
|
||||
* Note that this service is NULL if ProtocolLib has not been initialized yet.
|
||||
* @return The executor service, or NULL.
|
||||
*/
|
||||
public static ListeningScheduledExecutorService getExecutorSync() {
|
||||
return executorSync;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the ProtocolLib update checker.
|
||||
*/
|
||||
|
@ -49,7 +49,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @param player - the player.
|
||||
* @return The associated protocol version, or {@link Integer#MIN_VALUE} if unknown.
|
||||
*/
|
||||
public int getProtocolVersion(Player player);
|
||||
int getProtocolVersion(Player player);
|
||||
|
||||
/**
|
||||
* Send a packet to the given player.
|
||||
@ -63,7 +63,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @throws InvocationTargetException - if an error occurred when sending the packet.
|
||||
*/
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
|
||||
void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
|
||||
throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
@ -79,7 +79,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @throws IllegalAccessException If the underlying method caused an error.
|
||||
*/
|
||||
@Override
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
|
||||
void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
|
||||
throws IllegalAccessException, InvocationTargetException;
|
||||
|
||||
/**
|
||||
@ -87,7 +87,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @param packet - the packet to broadcast.
|
||||
* @throws FieldAccessException If we were unable to send the packet due to reflection problems.
|
||||
*/
|
||||
public void broadcastServerPacket(PacketContainer packet);
|
||||
void broadcastServerPacket(PacketContainer packet);
|
||||
|
||||
/**
|
||||
* Broadcast a packet to every player that is receiving information about a given entity.
|
||||
@ -99,7 +99,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @param includeTracker - whether or not to also transmit the packet to the entity, if it is a tracker.
|
||||
* @throws FieldAccessException If we were unable to send the packet due to reflection problems.
|
||||
*/
|
||||
public void broadcastServerPacket(PacketContainer packet, Entity entity, boolean includeTracker);
|
||||
void broadcastServerPacket(PacketContainer packet, Entity entity, boolean includeTracker);
|
||||
|
||||
/**
|
||||
* Broadcast a packet to every player within the given maximum observer distance.
|
||||
@ -107,13 +107,13 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @param origin - the origin to consider when calculating the distance to each observer.
|
||||
* @param maxObserverDistance - the maximum distance to the origin.
|
||||
*/
|
||||
public void broadcastServerPacket(PacketContainer packet, Location origin, int maxObserverDistance);
|
||||
void broadcastServerPacket(PacketContainer packet, Location origin, int maxObserverDistance);
|
||||
|
||||
/**
|
||||
* Retrieves a list of every registered packet listener.
|
||||
* @return Every registered packet listener.
|
||||
*/
|
||||
public ImmutableSet<PacketListener> getPacketListeners();
|
||||
ImmutableSet<PacketListener> getPacketListeners();
|
||||
|
||||
/**
|
||||
* Adds a packet listener.
|
||||
@ -123,7 +123,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* can register it again.
|
||||
* @param listener - new packet listener.
|
||||
*/
|
||||
public void addPacketListener(PacketListener listener);
|
||||
void addPacketListener(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Removes a given packet listener.
|
||||
@ -131,47 +131,20 @@ public interface ProtocolManager extends PacketStream {
|
||||
* Attempting to remove a listener that doesn't exist has no effect.
|
||||
* @param listener - the packet listener to remove.
|
||||
*/
|
||||
public void removePacketListener(PacketListener listener);
|
||||
void removePacketListener(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Removes every listener associated with the given plugin.
|
||||
* @param plugin - the plugin to unload.
|
||||
*/
|
||||
public void removePacketListeners(Plugin plugin);
|
||||
void removePacketListeners(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #createPacket(PacketType)} instead.
|
||||
* @param id - packet ID.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id);
|
||||
|
||||
/**
|
||||
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||
* @param type - packet type.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
public PacketContainer createPacket(PacketType type);
|
||||
|
||||
/**
|
||||
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||
* <p>
|
||||
* If set to true, the <i>forceDefaults</i> option will force the system to automatically
|
||||
* give non-primitive fields in the packet sensible default values. For instance, certain
|
||||
* packets - like Packet60Explosion - require a List or Set to be non-null. If the
|
||||
* forceDefaults option is true, the List or Set will be automatically created.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #createPacket(PacketType, boolean)} instead.
|
||||
*
|
||||
* @param id - packet ID.
|
||||
* @param forceDefaults - TRUE to use sensible defaults in most fields, FALSE otherwise.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id, boolean forceDefaults);
|
||||
PacketContainer createPacket(PacketType type);
|
||||
|
||||
/**
|
||||
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||
@ -185,18 +158,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @param forceDefaults - TRUE to use sensible defaults in most fields, FALSE otherwise.
|
||||
* @return New encapsulated Minecraft packet.
|
||||
*/
|
||||
public PacketContainer createPacket(PacketType type, boolean forceDefaults);
|
||||
|
||||
/**
|
||||
* Construct a packet using the special builtin Minecraft constructors.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #createPacketConstructor(PacketType, Object...)} instead.
|
||||
* @param id - the packet ID.
|
||||
* @param arguments - arguments that will be passed to the constructor.
|
||||
* @return The packet constructor.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketConstructor createPacketConstructor(int id, Object... arguments);
|
||||
PacketContainer createPacket(PacketType type, boolean forceDefaults);
|
||||
|
||||
/**
|
||||
* Construct a packet using the special builtin Minecraft constructors.
|
||||
@ -204,7 +166,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @param arguments - arguments that will be passed to the constructor.
|
||||
* @return The packet constructor.
|
||||
*/
|
||||
public PacketConstructor createPacketConstructor(PacketType type, Object... arguments);
|
||||
PacketConstructor createPacketConstructor(PacketType type, Object... arguments);
|
||||
|
||||
/**
|
||||
* Completely resend an entity to a list of clients.
|
||||
@ -214,7 +176,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @param entity - entity to refresh.
|
||||
* @param observers - the clients to update.
|
||||
*/
|
||||
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException;
|
||||
void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException;
|
||||
|
||||
/**
|
||||
* Retrieve the associated entity.
|
||||
@ -223,7 +185,7 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @return The associated entity.
|
||||
* @throws FieldAccessException Reflection failed.
|
||||
*/
|
||||
public Entity getEntityFromID(World container, int id) throws FieldAccessException;
|
||||
Entity getEntityFromID(World container, int id) throws FieldAccessException;
|
||||
|
||||
/**
|
||||
* Retrieve every client that is receiving information about a given entity.
|
||||
@ -231,55 +193,37 @@ public interface ProtocolManager extends PacketStream {
|
||||
* @return Every client/player that is tracking the given entity.
|
||||
* @throws FieldAccessException If reflection failed.
|
||||
*/
|
||||
public List<Player> getEntityTrackers(Entity entity) throws FieldAccessException;
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the sent server packets that will be observed by listeners.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getSendingFilterTypes()} instead.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> getSendingFilters();
|
||||
|
||||
List<Player> getEntityTrackers(Entity entity) throws FieldAccessException;
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the type of the sent server packets that will be observed by listeners.
|
||||
* @return Every filtered server packet.
|
||||
*/
|
||||
public Set<PacketType> getSendingFilterTypes();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the ID of the received client packets that will be observed by listeners.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getReceivingFilterTypes()} instead.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> getReceivingFilters();
|
||||
|
||||
Set<PacketType> getSendingFilterTypes();
|
||||
|
||||
/**
|
||||
* Retrieves a immutable set containing the type of the received client packets that will be observed by listeners.
|
||||
* @return Every filtered client packet.
|
||||
*/
|
||||
public Set<PacketType> getReceivingFilterTypes();
|
||||
Set<PacketType> getReceivingFilterTypes();
|
||||
|
||||
/**
|
||||
* Retrieve the current Minecraft version.
|
||||
* @return The current version.
|
||||
*/
|
||||
public MinecraftVersion getMinecraftVersion();
|
||||
MinecraftVersion getMinecraftVersion();
|
||||
|
||||
/**
|
||||
* Determines whether or not this protocol manager has been disabled.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClosed();
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Retrieve the current asynchronous packet manager.
|
||||
* @return Asynchronous packet manager.
|
||||
*/
|
||||
public AsynchronousManager getAsynchronousManager();
|
||||
AsynchronousManager getAsynchronousManager();
|
||||
|
||||
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist);
|
||||
void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist);
|
||||
}
|
||||
|
@ -23,10 +23,6 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketStream;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
@ -37,12 +33,15 @@ import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.comphenix.protocol.injector.SortedPacketListenerList;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
/**
|
||||
* Represents a filter manager for asynchronous packets.
|
||||
* <p>
|
||||
@ -88,7 +87,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
// Initialize timeout listeners
|
||||
this.serverTimeoutListeners = new SortedPacketListenerList();
|
||||
this.clientTimeoutListeners = new SortedPacketListenerList();
|
||||
this.timeoutListeners = Sets.newSetFromMap(new ConcurrentHashMap<PacketListener, Boolean>());
|
||||
this.timeoutListeners = Sets.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
this.playerSendingHandler = new PlayerSendingHandler(reporter, serverTimeoutListeners, clientTimeoutListeners);
|
||||
this.serverProcessingQueue = new PacketProcessingQueue(playerSendingHandler);
|
||||
@ -327,22 +326,12 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
getProcessingQueue(syncPacket).enqueue(newEvent, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getSendingFilters() {
|
||||
return PacketRegistry.toLegacy(serverProcessingQueue.keySet());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getReceivingTypes() {
|
||||
return serverProcessingQueue.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getReceivingFilters() {
|
||||
return PacketRegistry.toLegacy(clientProcessingQueue.keySet());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getSendingTypes() {
|
||||
return clientProcessingQueue.keySet();
|
||||
|
@ -17,11 +17,7 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
@ -44,93 +40,21 @@ public class ListeningWhitelist {
|
||||
private final GamePhase gamePhase;
|
||||
private final Set<ListenerOptions> options;
|
||||
private final Set<PacketType> types;
|
||||
|
||||
// Cache whitelist
|
||||
private transient Set<Integer> intWhitelist;
|
||||
|
||||
|
||||
private ListeningWhitelist(Builder builder) {
|
||||
this.priority = builder.priority;
|
||||
this.types = builder.types;
|
||||
this.gamePhase = builder.gamePhase;
|
||||
this.options = builder.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - set of IDs to observe/enable.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist) {
|
||||
this(priority, whitelist, GamePhase.PLAYING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - set of IDs to observe/enable.
|
||||
* @param gamePhase - which game phase to receieve notifications on.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist, GamePhase gamePhase) {
|
||||
private ListeningWhitelist(ListenerPriority priority) {
|
||||
this.priority = priority;
|
||||
this.types = PacketRegistry.toPacketTypes(safeSet(whitelist));
|
||||
this.gamePhase = gamePhase;
|
||||
this.options = EnumSet.noneOf(ListenerOptions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist of a given priority for a list of packets.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - list of packet IDs to observe/enable.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer... whitelist) {
|
||||
this.priority = priority;
|
||||
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
|
||||
this.types = new HashSet<>();
|
||||
this.gamePhase = GamePhase.PLAYING;
|
||||
this.options = EnumSet.noneOf(ListenerOptions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - list of packet IDs to observe/enable.
|
||||
* @param gamePhase - which game phase to receieve notifications on.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer[] whitelist, GamePhase gamePhase) {
|
||||
this.priority = priority;
|
||||
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
|
||||
this.gamePhase = gamePhase;
|
||||
this.options = EnumSet.noneOf(ListenerOptions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs and options.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #newBuilder()} instead.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - list of packet IDs to observe/enable.
|
||||
* @param gamePhase - which game phase to receieve notifications on.
|
||||
* @param options - every special option associated with this whitelist.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer[] whitelist, GamePhase gamePhase, ListenerOptions... options) {
|
||||
this.priority = priority;
|
||||
this.types = PacketRegistry.toPacketTypes(Sets.newHashSet(whitelist));
|
||||
this.gamePhase = gamePhase;
|
||||
this.options = safeEnumSet(Arrays.asList(options), ListenerOptions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this whitelist has any enabled packets.
|
||||
* @return TRUE if there are any packets, FALSE otherwise.
|
||||
@ -147,19 +71,6 @@ public class ListeningWhitelist {
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of packets that will be observed by the listeners.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getTypes()} instead.
|
||||
* @return Packet whitelist.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> getWhitelist() {
|
||||
if (intWhitelist == null)
|
||||
intWhitelist = PacketRegistry.toLegacy(types);
|
||||
return intWhitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a set of the packets that will be observed by the listeners.
|
||||
* @return Packet whitelist.
|
||||
@ -189,23 +100,6 @@ public class ListeningWhitelist {
|
||||
return Objects.hashCode(priority, types, gamePhase, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any of the given IDs can be found in the whitelist.
|
||||
* @param whitelist - whitelist to test.
|
||||
* @param idList - list of packet IDs to find.
|
||||
* @return TRUE if any of the packets in the list can be found in the whitelist, FALSE otherwise.
|
||||
*/
|
||||
public static boolean containsAny(ListeningWhitelist whitelist, int... idList) {
|
||||
if (whitelist != null) {
|
||||
for (int i = 0; i < idList.length; i++) {
|
||||
if (whitelist.getWhitelist().contains(idList[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given whitelist is empty or not.
|
||||
* @param whitelist - the whitelist to test.
|
||||
@ -273,7 +167,7 @@ public class ListeningWhitelist {
|
||||
|
||||
/**
|
||||
* Construct a copy of a given set.
|
||||
* @param list - the set to copy.
|
||||
* @param set - the set to copy.
|
||||
* @return The copied set.
|
||||
*/
|
||||
private static <T> Set<T> safeSet(Collection<T> set) {
|
||||
@ -364,20 +258,7 @@ public class ListeningWhitelist {
|
||||
public Builder high() {
|
||||
return priority(ListenerPriority.HIGH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the whitelist of packet IDs to copy when constructing new whitelists.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #types(Collection)} instead.
|
||||
* @param whitelist - the whitelist of packets.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder whitelist(Collection<Integer> whitelist) {
|
||||
this.types = PacketRegistry.toPacketTypes(safeSet(whitelist));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the whitelist of packet types to copy when constructing new whitelists.
|
||||
* @param types - the whitelist of packets.
|
||||
|
@ -17,15 +17,11 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a listener that is notified of every sent and received packet.
|
||||
@ -45,26 +41,17 @@ public abstract class MonitorAdapter implements PacketListener {
|
||||
public MonitorAdapter(Plugin plugin, ConnectionSide side, Logger logger) {
|
||||
initialize(plugin, side, logger);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
||||
private void initialize(Plugin plugin, ConnectionSide side, Logger logger) {
|
||||
this.plugin = plugin;
|
||||
|
||||
// Recover in case something goes wrong
|
||||
try {
|
||||
if (side.isForServer())
|
||||
this.sending = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getServerPacketTypes()).gamePhaseBoth().build();
|
||||
if (side.isForClient())
|
||||
this.receiving = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getClientPacketTypes()).gamePhaseBoth().build();
|
||||
} catch (FieldAccessException e) {
|
||||
if (side.isForServer())
|
||||
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getRegistry().values(), GamePhase.BOTH);
|
||||
if (side.isForClient())
|
||||
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getRegistry().values(), GamePhase.BOTH);
|
||||
logger.log(Level.WARNING, "Defaulting to 1.3 packets.", e);
|
||||
}
|
||||
if (side.isForServer())
|
||||
this.sending = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getServerPacketTypes()).gamePhaseBoth().build();
|
||||
if (side.isForClient())
|
||||
this.receiving = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getClientPacketTypes()).gamePhaseBoth().build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a logger, even if we're running in a CraftBukkit version that doesn't support it.
|
||||
* @param plugin - the plugin to retrieve.
|
||||
|
@ -19,19 +19,17 @@ package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a packet listener with useful constructors.
|
||||
* <p>
|
||||
@ -103,151 +101,7 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
public PacketAdapter(Plugin plugin, ListenerPriority listenerPriority, PacketType... types) {
|
||||
this(params(plugin, types).listenerPriority(listenerPriority));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener with default priority.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, Integer... packets) {
|
||||
this(plugin, connectionSide, ListenerPriority.NORMAL, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Set<Integer> packets) {
|
||||
this(plugin, connectionSide, listenerPriority, GamePhase.PLAYING, packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optimize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, GamePhase gamePhase, Set<Integer> packets) {
|
||||
this(plugin, connectionSide, ListenerPriority.NORMAL, gamePhase, packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optimize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, GamePhase gamePhase, Set<Integer> packets) {
|
||||
this(plugin, connectionSide, listenerPriority, gamePhase, packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Integer... packets) {
|
||||
this(plugin, connectionSide, listenerPriority, GamePhase.PLAYING, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param options - which listener options to use.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerOptions[] options, Integer... packets) {
|
||||
this(plugin, connectionSide, ListenerPriority.NORMAL, GamePhase.PLAYING, options, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, GamePhase gamePhase, Integer... packets) {
|
||||
this(plugin, connectionSide, ListenerPriority.NORMAL, gamePhase, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optimize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, GamePhase gamePhase, Integer... packets) {
|
||||
this(plugin, connectionSide, listenerPriority, gamePhase, new ListenerOptions[0], packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optimize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* <p>
|
||||
* Listener options must be specified in order for {@link NetworkMarker#getInputBuffer()} to function correctly.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #params()} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param options - which listener options to use.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketAdapter(
|
||||
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
|
||||
GamePhase gamePhase, ListenerOptions[] options, Integer... packets) {
|
||||
|
||||
this(plugin, connectionSide, listenerPriority, gamePhase, options,
|
||||
PacketRegistry.toPacketTypes(Sets.newHashSet(packets), connectionSide.getSender()).toArray(new PacketType[0])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// For internal use only
|
||||
private PacketAdapter(
|
||||
Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority,
|
||||
@ -374,20 +228,6 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
public static AdapterParameteters params() {
|
||||
return new AdapterParameteters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a helper object for passing parameters to the packet adapter.
|
||||
* <p>
|
||||
* This is often simpler and better than passing them directly to each constructor.
|
||||
* Deprecated: Use {@link #params(Plugin, PacketType...)} instead.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
* @return Helper object.
|
||||
*/
|
||||
@Deprecated
|
||||
public static AdapterParameteters params(Plugin plugin, Integer... packets) {
|
||||
return new AdapterParameteters().plugin(plugin).packets(packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a helper object for passing parameters to the packet adapter.
|
||||
@ -532,17 +372,7 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
public AdapterParameteters optionIntercept() {
|
||||
return addOption(ListenerOptions.INTERCEPT_INPUT_BUFFER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener option to {@link ListenerOptions#DISABLE_GAMEPHASE_DETECTION}, causing ProtocolLib to ignore automatic game phase detection.
|
||||
* <p>
|
||||
* This is no longer relevant in 1.7.2.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public AdapterParameteters optionManualGamePhase() {
|
||||
return addOption(ListenerOptions.DISABLE_GAMEPHASE_DETECTION);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the listener option to {@link ListenerOptions#ASYNC}, indicating that our listener is thread safe.
|
||||
* <p>
|
||||
@ -552,40 +382,7 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
public AdapterParameteters optionAsync() {
|
||||
return addOption(ListenerOptions.ASYNC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet IDs of the packets the listener is looking for.
|
||||
* <p>
|
||||
* This parameter is required.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #types(PacketType...)} instead.
|
||||
* @param packets - the packet IDs to look for.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Deprecated
|
||||
public AdapterParameteters packets(@Nonnull Integer... packets) {
|
||||
Preconditions.checkNotNull(packets, "packets cannot be NULL");
|
||||
PacketType[] types = new PacketType[packets.length];
|
||||
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
types[i] = PacketType.findLegacy(packets[i]);
|
||||
}
|
||||
this.packets = types;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet IDs of the packets the listener is looking for.
|
||||
* <p>
|
||||
* This parameter is required.
|
||||
* @param packets - a set of the packet IDs to look for.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Deprecated
|
||||
public AdapterParameteters packets(@Nonnull Set<Integer> packets) {
|
||||
return packets(packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the packet types the listener is looking for.
|
||||
* <p>
|
||||
|
@ -68,6 +68,7 @@ import org.bukkit.util.Vector;
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class PacketContainer implements Serializable {
|
||||
private static final long serialVersionUID = 3;
|
||||
|
||||
@ -111,40 +112,6 @@ public class PacketContainer implements Serializable {
|
||||
private static final Set<PacketType> CLONING_UNSUPPORTED = Sets.newHashSet(
|
||||
PacketType.Play.Server.UPDATE_ATTRIBUTES, PacketType.Status.Server.SERVER_INFO);
|
||||
|
||||
/**
|
||||
* Creates a packet container for a new packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #PacketContainer(PacketType)} instead.
|
||||
* @param id - ID of the packet to create.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer(int id) {
|
||||
this(PacketType.findLegacy(id), StructureCache.newPacket(PacketType.findLegacy(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet container for an existing packet.
|
||||
* @param id - ID of the given packet.
|
||||
* @param handle - contained packet.
|
||||
* @deprecated Use {@link #PacketContainer(PacketType, Object)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer(int id, Object handle) {
|
||||
this(PacketType.findLegacy(id), handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet container for an existing packet.
|
||||
* @param id - ID of the given packet.
|
||||
* @param handle - contained packet.
|
||||
* @param structure - structure modifier.
|
||||
* @deprecated Use {@link #PacketContainer(PacketType, Object, StructureModifier)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketContainer(int id, Object handle, StructureModifier<Object> structure) {
|
||||
this(PacketType.findLegacy(id), handle, structure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet container for a new packet.
|
||||
* @param type - the type of the packet to create.
|
||||
@ -1035,16 +1002,13 @@ public class PacketContainer implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID of this packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getType()} instead.
|
||||
* @return Packet ID.
|
||||
* @deprecated Packet IDs are unreliable
|
||||
*/
|
||||
@Deprecated
|
||||
public int getID() {
|
||||
return type.getLegacyId();
|
||||
public int getId() {
|
||||
return type.getCurrentId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the packet type of this packet.
|
||||
* @return The packet type.
|
||||
@ -1230,61 +1194,6 @@ public class PacketContainer implements Serializable {
|
||||
PacketMetadata.remove(handle, key);
|
||||
}
|
||||
|
||||
// ---- Old Metadata API
|
||||
// Scheduled for removal in 4.5
|
||||
|
||||
/**
|
||||
* Gets the metadata value for a given key.
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @return Metadata value, or null if nonexistent.
|
||||
* @deprecated Replaced with {@link #getMeta(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getMetadata(String key) {
|
||||
return getMeta(key).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds metadata to this packet.
|
||||
* <p>
|
||||
* Note: Since metadata is lazily initialized, this may result in the creation of the metadata map.
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @param value Metadata value
|
||||
* @deprecated Replaced by {@link #setMeta(String, Object)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void addMetadata(String key, Object value) {
|
||||
setMeta(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes metadata from this packet.
|
||||
* <p>
|
||||
* Note: If this operation leaves the metadata map empty, the map will be set to null.
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @return The previous value, or null if nonexistant.
|
||||
* @deprecated Replaced by {@link #removeMeta(String)}. This one was pretty much just for naming consistency.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object removeMetadata(String key) {
|
||||
return PacketMetadata.remove(handle, key).orElseGet(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this packet has metadata for a given key.
|
||||
*
|
||||
* @param key Metadata key
|
||||
* @return True if this packet has metadata for the key, false if not.
|
||||
* @deprecated Replaced with {@code getMeta(key).isPresent()}
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean hasMetadata(String key) {
|
||||
return getMeta(key).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cached method concurrently.
|
||||
* @param lookup - a lazy lookup cache.
|
||||
|
@ -247,7 +247,7 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
*/
|
||||
@Deprecated
|
||||
public int getPacketID() {
|
||||
return packet.getID();
|
||||
return packet.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,142 +0,0 @@
|
||||
package com.comphenix.protocol.executors;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.util.concurrent.ListenableScheduledFuture;
|
||||
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
abstract class AbstractBukkitService
|
||||
extends AbstractListeningService implements BukkitScheduledExecutorService {
|
||||
|
||||
private static final long MILLISECONDS_PER_TICK = 50;
|
||||
private static final long NANOSECONDS_PER_TICK = 1000000 * MILLISECONDS_PER_TICK;
|
||||
|
||||
private volatile boolean shutdown;
|
||||
private final PendingTasks tasks;
|
||||
|
||||
public AbstractBukkitService(PendingTasks tasks) {
|
||||
this.tasks = tasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> RunnableAbstractFuture<T> newTaskFor(Runnable runnable, T value) {
|
||||
return newTaskFor(Executors.callable(runnable, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> RunnableAbstractFuture<T> newTaskFor(final Callable<T> callable) {
|
||||
validateState();
|
||||
return new CallableTask<T>(callable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
validateState();
|
||||
|
||||
if (command instanceof RunnableFuture) {
|
||||
tasks.add(getTask(command), (Future<?>) command);
|
||||
} else {
|
||||
// Submit it first
|
||||
submit(command);
|
||||
}
|
||||
}
|
||||
|
||||
// Bridge to Bukkit
|
||||
protected abstract BukkitTask getTask(Runnable command);
|
||||
protected abstract BukkitTask getLaterTask(Runnable task, long ticks);
|
||||
protected abstract BukkitTask getTimerTask(long ticksInitial, long ticksDelay, Runnable task);
|
||||
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
shutdown();
|
||||
tasks.cancel();
|
||||
|
||||
// We don't support this
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
private void validateState() {
|
||||
if (shutdown) {
|
||||
throw new RejectedExecutionException("Executor service has shut down. Cannot start new tasks.");
|
||||
}
|
||||
}
|
||||
|
||||
private long toTicks(long delay, TimeUnit unit) {
|
||||
return Math.round(unit.toMillis(delay) / (double)MILLISECONDS_PER_TICK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
|
||||
return schedule(Executors.callable(command), delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> ListenableScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
|
||||
long ticks = toTicks(delay, unit);
|
||||
|
||||
// Construct future task and Bukkit task
|
||||
CallableTask<V> task = new CallableTask<V>(callable);
|
||||
BukkitTask bukkitTask = getLaterTask(task, ticks);
|
||||
|
||||
tasks.add(bukkitTask, task);
|
||||
return task.getScheduledFuture(System.nanoTime() + delay * NANOSECONDS_PER_TICK, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
|
||||
long period, TimeUnit unit) {
|
||||
|
||||
long ticksInitial = toTicks(initialDelay, unit);
|
||||
long ticksDelay = toTicks(period, unit);
|
||||
|
||||
// Construct future task and Bukkit task
|
||||
CallableTask<?> task = new CallableTask<Object>(Executors.callable(command)) {
|
||||
protected void compute() {
|
||||
// Do nothing more. This future can only be finished by cancellation
|
||||
try {
|
||||
compute.call();
|
||||
} catch (Exception e) {
|
||||
// Let Bukkit handle this
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
BukkitTask bukkitTask = getTimerTask(ticksInitial, ticksDelay, task);
|
||||
|
||||
tasks.add(bukkitTask, task);
|
||||
return task.getScheduledFuture(
|
||||
System.nanoTime() + ticksInitial * NANOSECONDS_PER_TICK,
|
||||
ticksDelay * NANOSECONDS_PER_TICK);
|
||||
}
|
||||
|
||||
// Not supported!
|
||||
@Deprecated
|
||||
@Override
|
||||
public ListenableScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
|
||||
return scheduleAtFixedRate(command, initialDelay, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return tasks.awaitTermination(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return tasks.isTerminated();
|
||||
}
|
||||
}
|
@ -1,288 +0,0 @@
|
||||
package com.comphenix.protocol.executors;
|
||||
|
||||
/*
|
||||
* This file is a modified version of
|
||||
* http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/AbstractExecutorService.java?revision=1.35
|
||||
* which contained the following notice:
|
||||
*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the
|
||||
* public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* Rationale for copying:
|
||||
* Guava targets JDK5, whose AbstractExecutorService class lacks the newTaskFor protected
|
||||
* customization methods needed by MoreExecutors.listeningDecorator. This class is a copy of
|
||||
* AbstractExecutorService from the JSR166 CVS repository. It contains the desired methods.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RunnableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import com.google.common.util.concurrent.AbstractFuture;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListenableFutureTask;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
|
||||
/**
|
||||
* Provides default implementations of {@link ListeningExecutorService}
|
||||
* execution methods. This class implements the <tt>submit</tt>,
|
||||
* <tt>invokeAny</tt> and <tt>invokeAll</tt> methods using a
|
||||
* {@link ListenableFutureTask} returned by <tt>newTaskFor</tt>. For example,
|
||||
* the implementation of <tt>submit(Runnable)</tt> creates an associated
|
||||
* <tt>ListenableFutureTask</tt> that is executed and returned.
|
||||
*
|
||||
* @author Doug Lea
|
||||
*/
|
||||
abstract class AbstractListeningService implements ListeningExecutorService {
|
||||
/**
|
||||
* Represents a runnable abstract listenable future task.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <T>
|
||||
*/
|
||||
public static abstract class RunnableAbstractFuture<T>
|
||||
extends AbstractFuture<T> implements RunnableFuture<T> {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <tt>ListenableFutureTask</tt> for the given runnable and
|
||||
* default value.
|
||||
*
|
||||
* @param runnable - the runnable task being wrapped
|
||||
* @param value - the default value for the returned future
|
||||
* @return a <tt>ListenableFutureTask</tt> which when run will run the
|
||||
* underlying runnable and which, as a <tt>Future</tt>, will yield
|
||||
* the given value as its result and provide for cancellation of the
|
||||
* underlying task.
|
||||
*/
|
||||
protected abstract <T> RunnableAbstractFuture<T> newTaskFor(Runnable runnable, T value);
|
||||
|
||||
/**
|
||||
* Returns a <tt>ListenableFutureTask</tt> for the given callable task.
|
||||
*
|
||||
* @param callable - the callable task being wrapped
|
||||
* @return a <tt>ListenableFutureTask</tt> which when run will call the
|
||||
* underlying callable and which, as a <tt>Future</tt>, will yield
|
||||
* the callable's result as its result and provide for cancellation
|
||||
* of the underlying task.
|
||||
*/
|
||||
protected abstract <T> RunnableAbstractFuture<T> newTaskFor(Callable<T> callable);
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> submit(Runnable task) {
|
||||
if (task == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
RunnableAbstractFuture<Void> ftask = newTaskFor(task, null);
|
||||
execute(ftask);
|
||||
return ftask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> submit(Runnable task, T result) {
|
||||
if (task == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
RunnableAbstractFuture<T> ftask = newTaskFor(task, result);
|
||||
execute(ftask);
|
||||
return ftask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> submit(Callable<T> task) {
|
||||
if (task == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
RunnableAbstractFuture<T> ftask = newTaskFor(task);
|
||||
execute(ftask);
|
||||
return ftask;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main mechanics of invokeAny.
|
||||
*/
|
||||
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
if (tasks == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
int ntasks = tasks.size();
|
||||
if (ntasks == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
List<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
|
||||
ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this);
|
||||
|
||||
// For efficiency, especially in executors with limited
|
||||
// parallelism, check to see if previously submitted tasks are
|
||||
// done before submitting more of them. This interleaving
|
||||
// plus the exception mechanics account for messiness of main
|
||||
// loop.
|
||||
|
||||
try {
|
||||
// Record exceptions so that if we fail to obtain any
|
||||
// result, we can throw the last exception we got.
|
||||
ExecutionException ee = null;
|
||||
long lastTime = timed ? System.nanoTime() : 0;
|
||||
Iterator<? extends Callable<T>> it = tasks.iterator();
|
||||
|
||||
// Start one task for sure; the rest incrementally
|
||||
futures.add(ecs.submit(it.next()));
|
||||
--ntasks;
|
||||
int active = 1;
|
||||
|
||||
for (;;) {
|
||||
Future<T> f = ecs.poll();
|
||||
if (f == null) {
|
||||
if (ntasks > 0) {
|
||||
--ntasks;
|
||||
futures.add(ecs.submit(it.next()));
|
||||
++active;
|
||||
} else if (active == 0) {
|
||||
break;
|
||||
} else if (timed) {
|
||||
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
|
||||
if (f == null) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
long now = System.nanoTime();
|
||||
nanos -= now - lastTime;
|
||||
lastTime = now;
|
||||
} else {
|
||||
f = ecs.take();
|
||||
}
|
||||
}
|
||||
if (f != null) {
|
||||
--active;
|
||||
try {
|
||||
return f.get();
|
||||
} catch (ExecutionException eex) {
|
||||
ee = eex;
|
||||
} catch (RuntimeException rex) {
|
||||
ee = new ExecutionException(rex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ee == null) {
|
||||
ee = new ExecutionException(null);
|
||||
}
|
||||
throw ee;
|
||||
|
||||
} finally {
|
||||
for (Future<T> f : futures)
|
||||
f.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException,
|
||||
ExecutionException {
|
||||
try {
|
||||
return doInvokeAny(tasks, false, 0);
|
||||
} catch (TimeoutException cannotHappen) {
|
||||
// assert false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return doInvokeAny(tasks, true, unit.toNanos(timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
|
||||
throws InterruptedException {
|
||||
if (tasks == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
|
||||
boolean done = false;
|
||||
try {
|
||||
for (Callable<T> t : tasks) {
|
||||
RunnableAbstractFuture<T> f = newTaskFor(t);
|
||||
futures.add(f);
|
||||
execute(f);
|
||||
}
|
||||
for (Future<T> f : futures) {
|
||||
if (!f.isDone()) {
|
||||
try {
|
||||
f.get();
|
||||
} catch (CancellationException | ExecutionException ignore) { }
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
return futures;
|
||||
} finally {
|
||||
if (!done) {
|
||||
for (Future<T> f : futures)
|
||||
f.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
|
||||
TimeUnit unit) throws InterruptedException {
|
||||
if (tasks == null || unit == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
long nanos = unit.toNanos(timeout);
|
||||
List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
|
||||
boolean done = false;
|
||||
try {
|
||||
for (Callable<T> t : tasks)
|
||||
futures.add(newTaskFor(t));
|
||||
|
||||
long lastTime = System.nanoTime();
|
||||
|
||||
// Interleave time checks and calls to execute in case
|
||||
// executor doesn't have any/much parallelism.
|
||||
for (Future<T> future : futures) {
|
||||
execute((Runnable) future);
|
||||
long now = System.nanoTime();
|
||||
nanos -= now - lastTime;
|
||||
lastTime = now;
|
||||
if (nanos <= 0) {
|
||||
return futures;
|
||||
}
|
||||
}
|
||||
|
||||
for (Future<T> f : futures) {
|
||||
if (!f.isDone()) {
|
||||
if (nanos <= 0) {
|
||||
return futures;
|
||||
}
|
||||
try {
|
||||
f.get(nanos, TimeUnit.NANOSECONDS);
|
||||
} catch (CancellationException | ExecutionException ignore) {
|
||||
} catch (TimeoutException toe) {
|
||||
return futures;
|
||||
}
|
||||
long now = System.nanoTime();
|
||||
nanos -= now - lastTime;
|
||||
lastTime = now;
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
return futures;
|
||||
} finally {
|
||||
if (!done) {
|
||||
for (Future<T> f : futures)
|
||||
f.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
package com.comphenix.protocol.executors;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import com.comphenix.protocol.executors.AbstractBukkitService;
|
||||
import com.comphenix.protocol.executors.BukkitScheduledExecutorService;
|
||||
import com.comphenix.protocol.executors.PendingTasks;
|
||||
import com.comphenix.protocol.executors.PluginDisabledListener;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public class BukkitExecutors {
|
||||
private BukkitExecutors() {
|
||||
// Don't make it constructable
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a scheduled executor service for running tasks on the main thread.
|
||||
* @param plugin - plugin that is executing the given tasks.
|
||||
* @return Executor service.
|
||||
*/
|
||||
public static BukkitScheduledExecutorService newSynchronous(final Plugin plugin) {
|
||||
// Bridge destination
|
||||
final BukkitScheduler scheduler = getScheduler(plugin);
|
||||
Preconditions.checkNotNull(plugin, "plugin cannot be NULL");
|
||||
|
||||
BukkitScheduledExecutorService service = new com.comphenix.protocol.executors.AbstractBukkitService(new PendingTasks(plugin, scheduler)) {
|
||||
@Override
|
||||
protected BukkitTask getTask(Runnable command) {
|
||||
return scheduler.runTask(plugin, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BukkitTask getLaterTask(Runnable task, long ticks) {
|
||||
return scheduler.runTaskLater(plugin, task, ticks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BukkitTask getTimerTask(long ticksInitial, long ticksDelay, Runnable task) {
|
||||
return scheduler.runTaskTimer(plugin, task, ticksInitial, ticksDelay);
|
||||
}
|
||||
};
|
||||
|
||||
PluginDisabledListener.getListener(plugin).addService(service);
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a scheduled executor service for running asynchronous tasks.
|
||||
* @param plugin - plugin that is executing the given tasks.
|
||||
* @return Asynchronous executor service.
|
||||
*/
|
||||
public static BukkitScheduledExecutorService newAsynchronous(final Plugin plugin) {
|
||||
// Bridge destination
|
||||
final BukkitScheduler scheduler = getScheduler(plugin);
|
||||
Preconditions.checkNotNull(plugin, "plugin cannot be NULL");
|
||||
|
||||
BukkitScheduledExecutorService service = new com.comphenix.protocol.executors.AbstractBukkitService(new PendingTasks(plugin, scheduler)) {
|
||||
@Override
|
||||
protected BukkitTask getTask(Runnable command) {
|
||||
return scheduler.runTaskAsynchronously(plugin, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BukkitTask getLaterTask(Runnable task, long ticks) {
|
||||
return scheduler.runTaskLaterAsynchronously(plugin, task, ticks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BukkitTask getTimerTask(long ticksInitial, long ticksDelay, Runnable task) {
|
||||
return scheduler.runTaskTimerAsynchronously(plugin, task, ticksInitial, ticksDelay);
|
||||
}
|
||||
};
|
||||
|
||||
PluginDisabledListener.getListener(plugin).addService(service);
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current Bukkit scheduler.
|
||||
* @return Current scheduler.
|
||||
*/
|
||||
private static BukkitScheduler getScheduler(Plugin plugin) {
|
||||
BukkitScheduler scheduler = plugin.getServer().getScheduler();
|
||||
|
||||
if (scheduler != null) {
|
||||
return scheduler;
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to retrieve scheduler.");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package com.comphenix.protocol.executors;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import org.bukkit.event.*;
|
||||
import org.bukkit.plugin.EventExecutor;
|
||||
import org.bukkit.plugin.IllegalPluginAccessException;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredListener;
|
||||
|
||||
public class BukkitFutures {
|
||||
// Represents empty classes
|
||||
private static Listener EMPTY_LISTENER = new Listener() {};
|
||||
|
||||
/**
|
||||
* Retrieve a future representing the next invocation of the given event.
|
||||
* @param plugin - owner plugin.
|
||||
* @return Future event invocation.
|
||||
*/
|
||||
public static <TEvent extends Event> ListenableFuture<TEvent> nextEvent(Plugin plugin, Class<TEvent> eventClass) {
|
||||
return BukkitFutures.nextEvent(plugin, eventClass, EventPriority.NORMAL, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a future representing the next invocation of the given event.
|
||||
* @param plugin - owner plugin.
|
||||
* @return Future event invocation.
|
||||
*/
|
||||
public static <TEvent extends Event> ListenableFuture<TEvent> nextEvent(
|
||||
Plugin plugin, Class<TEvent> eventClass, EventPriority priority, boolean ignoreCancelled) {
|
||||
|
||||
// Event and future
|
||||
final HandlerList list = getHandlerList(eventClass);
|
||||
final SettableFuture<TEvent> future = SettableFuture.create();
|
||||
|
||||
EventExecutor executor = new EventExecutor() {
|
||||
private final AtomicBoolean once = new AtomicBoolean();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void execute(Listener listener, Event event) throws EventException {
|
||||
// Fire the future
|
||||
if (!future.isCancelled() && !once.getAndSet(true)) {
|
||||
future.set((TEvent) event);
|
||||
}
|
||||
}
|
||||
};
|
||||
RegisteredListener listener = new RegisteredListener(EMPTY_LISTENER, executor, priority, plugin, ignoreCancelled) {
|
||||
@Override
|
||||
public void callEvent(Event event) throws EventException {
|
||||
super.callEvent(event);
|
||||
list.unregister(this);
|
||||
}
|
||||
};
|
||||
|
||||
// Ensure that the future is cleaned up when the plugin is disabled
|
||||
PluginDisabledListener.getListener(plugin).addFuture(future);
|
||||
|
||||
// Add the listener
|
||||
list.register(listener);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a given event executor.
|
||||
* @param plugin - the owner plugin.
|
||||
* @param eventClass - the event to register.
|
||||
* @param priority - the event priority.
|
||||
* @param executor - the event executor.
|
||||
*/
|
||||
public static void registerEventExecutor(Plugin plugin, Class<? extends Event> eventClass, EventPriority priority, EventExecutor executor) {
|
||||
getHandlerList(eventClass).register(
|
||||
new RegisteredListener(EMPTY_LISTENER, executor, priority, plugin, false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the handler list associated with the given class.
|
||||
* @param clazz - given event class.
|
||||
* @return Associated handler list.
|
||||
*/
|
||||
private static HandlerList getHandlerList(Class<? extends Event> clazz) {
|
||||
// Class must have Event as its superclass
|
||||
while (clazz.getSuperclass() != null && Event.class.isAssignableFrom(clazz.getSuperclass())) {
|
||||
try {
|
||||
Method method = clazz.getDeclaredMethod("getHandlerList");
|
||||
method.setAccessible(true);
|
||||
return (HandlerList) method.invoke(null);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Keep on searching
|
||||
clazz = clazz.getSuperclass().asSubclass(Event.class);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalPluginAccessException(e.getMessage());
|
||||
}
|
||||
}
|
||||
throw new IllegalPluginAccessException("Unable to find handler list for event "
|
||||
+ clazz.getName());
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package com.comphenix.protocol.executors;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableScheduledFuture;
|
||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* Represents a listening scheduler service that returns {@link ListenableScheduledFuture} instead of {@link ScheduledFuture}.
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface BukkitScheduledExecutorService extends ListeningScheduledExecutorService {
|
||||
@Override
|
||||
public ListenableScheduledFuture<?> schedule(
|
||||
Runnable command, long delay, TimeUnit unit);
|
||||
|
||||
@Override
|
||||
public <V> ListenableScheduledFuture<V> schedule(
|
||||
Callable<V> callable, long delay, TimeUnit unit);
|
||||
|
||||
@Override
|
||||
public ListenableScheduledFuture<?> scheduleAtFixedRate(
|
||||
Runnable command, long initialDelay, long period, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* This is not supported by the underlying Bukkit scheduler.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public ListenableScheduledFuture<?> scheduleWithFixedDelay(
|
||||
Runnable command, long initialDelay, long delay, TimeUnit unit);
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
package com.comphenix.protocol.executors;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import com.comphenix.protocol.executors.AbstractListeningService.RunnableAbstractFuture;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.util.concurrent.ListenableScheduledFuture;
|
||||
|
||||
class CallableTask<T> extends RunnableAbstractFuture<T> {
|
||||
protected final Callable<T> compute;
|
||||
|
||||
public CallableTask(Callable<T> compute) {
|
||||
Preconditions.checkNotNull(compute, "compute cannot be NULL");
|
||||
|
||||
this.compute = compute;
|
||||
}
|
||||
|
||||
public ListenableScheduledFuture<T> getScheduledFuture(final long startTime, final long nextDelay) {
|
||||
return new ListenableScheduledFuture<T>() {
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return CallableTask.this.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException {
|
||||
return CallableTask.this.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout, TimeUnit unit) throws InterruptedException,
|
||||
ExecutionException, TimeoutException {
|
||||
return CallableTask.this.get(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return CallableTask.this.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return CallableTask.this.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Runnable listener, Executor executor) {
|
||||
CallableTask.this.addListener(listener, executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Delayed o) {
|
||||
return Long.valueOf(getDelay(TimeUnit.NANOSECONDS))
|
||||
.compareTo(o.getDelay(TimeUnit.NANOSECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDelay(TimeUnit unit) {
|
||||
long current = System.nanoTime();
|
||||
|
||||
// Calculate the correct delay
|
||||
if (current < startTime || !isPeriodic())
|
||||
return unit.convert(startTime - current, TimeUnit.NANOSECONDS);
|
||||
else
|
||||
return unit.convert(((current - startTime) % nextDelay), TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
// @Override
|
||||
public boolean isPeriodic() {
|
||||
return nextDelay > 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the thread responsible for computing this future.
|
||||
*/
|
||||
protected void compute() {
|
||||
try {
|
||||
// Save result
|
||||
if (!isCancelled()) {
|
||||
set(compute.call());
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
setException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
compute();
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
package com.comphenix.protocol.executors;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
class PendingTasks {
|
||||
/**
|
||||
* Represents a wrapper for a cancelable task.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
private interface CancelableFuture {
|
||||
void cancel();
|
||||
boolean isTaskCancelled();
|
||||
}
|
||||
|
||||
// Every pending task
|
||||
private final Set<CancelableFuture> pending = new HashSet<>();
|
||||
private final Object pendingLock = new Object();
|
||||
|
||||
// Handle arbitrary cancelation
|
||||
private final Plugin plugin;
|
||||
private final BukkitScheduler scheduler;
|
||||
private BukkitTask cancellationTask;
|
||||
|
||||
public PendingTasks(Plugin plugin, BukkitScheduler scheduler) {
|
||||
this.plugin = plugin;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
public void add(final BukkitTask task, final Future<?> future) {
|
||||
add(new CancelableFuture() {
|
||||
@Override
|
||||
public boolean isTaskCancelled() {
|
||||
// If completed, check its cancellation state
|
||||
if (future.isDone())
|
||||
return future.isCancelled();
|
||||
|
||||
return !(scheduler.isCurrentlyRunning(task.getTaskId()) ||
|
||||
scheduler.isQueued(task.getTaskId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
// Make sure
|
||||
task.cancel();
|
||||
future.cancel(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private CancelableFuture add(CancelableFuture task) {
|
||||
synchronized (pendingLock) {
|
||||
pending.add(task);
|
||||
pendingLock.notifyAll();
|
||||
beginCancellationTask();
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
private void beginCancellationTask() {
|
||||
if (cancellationTask == null) {
|
||||
cancellationTask = scheduler.runTaskTimer(plugin, () -> {
|
||||
// Check for cancellations
|
||||
synchronized (pendingLock) {
|
||||
boolean changed = false;
|
||||
|
||||
for (Iterator<CancelableFuture> it = pending.iterator(); it.hasNext(); ) {
|
||||
CancelableFuture future = it.next();
|
||||
|
||||
// Remove cancelled tasks
|
||||
if (future.isTaskCancelled()) {
|
||||
future.cancel();
|
||||
it.remove();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify waiting threads
|
||||
if (changed) {
|
||||
pendingLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
// Stop if we are out of tasks
|
||||
if (isTerminated()) {
|
||||
cancellationTask.cancel();
|
||||
cancellationTask = null;
|
||||
}
|
||||
}, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all pending tasks.
|
||||
*/
|
||||
public void cancel() {
|
||||
for (CancelableFuture task : pending) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until all pending tasks have completed.
|
||||
* @param timeout - the current timeout.
|
||||
* @param unit - unit of the timeout.
|
||||
* @return TRUE if every pending task has terminated, FALSE if we reached the timeout.
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
long expire = System.nanoTime() + unit.toNanos(timeout);
|
||||
|
||||
synchronized (pendingLock) {
|
||||
// Wait until the tasks have all terminated
|
||||
while (!isTerminated()) {
|
||||
// Check timeout
|
||||
if (expire < System.nanoTime())
|
||||
return false;
|
||||
unit.timedWait(pendingLock, timeout);
|
||||
}
|
||||
}
|
||||
// Timeout!
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if all tasks have completed executing.
|
||||
* @return TRUE if they have, FALSE otherwise.
|
||||
*/
|
||||
public boolean isTerminated() {
|
||||
return pending.isEmpty();
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
package com.comphenix.protocol.executors;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
class PluginDisabledListener implements Listener {
|
||||
private static final ConcurrentMap<Plugin, PluginDisabledListener> LISTENERS = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
// Objects that must be disabled
|
||||
private final Set<Future<?>> futures = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
private final Set<ExecutorService> services = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
private final Object setLock = new Object();
|
||||
|
||||
// The plugin we're looking for
|
||||
private final Plugin plugin;
|
||||
private boolean disabled;
|
||||
|
||||
private PluginDisabledListener(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated disabled listener.
|
||||
* @param plugin - the plugin.
|
||||
* @return Associated listener.
|
||||
*/
|
||||
public static PluginDisabledListener getListener(final Plugin plugin) {
|
||||
PluginDisabledListener result = LISTENERS.get(plugin);
|
||||
|
||||
if (result == null) {
|
||||
final PluginDisabledListener created = new PluginDisabledListener(plugin);
|
||||
result = LISTENERS.putIfAbsent(plugin, created);
|
||||
|
||||
if (result == null) {
|
||||
// Register listener - we can't use the normal method as the plugin might not be enabled yet
|
||||
BukkitFutures.registerEventExecutor(plugin, PluginDisableEvent.class, EventPriority.NORMAL,
|
||||
(listener, event) -> {
|
||||
if (event instanceof PluginDisableEvent) {
|
||||
created.onPluginDisabled((PluginDisableEvent) event);
|
||||
}
|
||||
});
|
||||
|
||||
result = created;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the given future will be cancelled when the plugin is disabled.
|
||||
* @param future - the future to cancel.
|
||||
*/
|
||||
public void addFuture(final ListenableFuture<?> future) {
|
||||
synchronized (setLock) {
|
||||
if (disabled) {
|
||||
processFuture(future);
|
||||
} else {
|
||||
futures.add(future);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the future when it has computed
|
||||
Futures.addCallback(future, new FutureCallback<Object>() {
|
||||
@Override
|
||||
public void onSuccess(Object value) {
|
||||
synchronized (setLock) {
|
||||
futures.remove(future);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable ex) {
|
||||
synchronized (setLock) {
|
||||
futures.remove(future);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a given service is shutdown when the plugin is disabled.
|
||||
* @param service - the service.
|
||||
*/
|
||||
public void addService(ExecutorService service) {
|
||||
synchronized (setLock) {
|
||||
if (disabled) {
|
||||
processService(service);
|
||||
} else {
|
||||
services.add(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Will be registered manually
|
||||
public void onPluginDisabled(PluginDisableEvent e) {
|
||||
if (e.getPlugin().equals(plugin)) {
|
||||
synchronized (setLock) {
|
||||
disabled = true;
|
||||
|
||||
// Cancel all unfinished futures
|
||||
for (Future<?> future : futures) {
|
||||
processFuture(future);
|
||||
}
|
||||
for (ExecutorService service : services) {
|
||||
processService(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processFuture(Future<?> future) {
|
||||
if (!future.isDone()) {
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void processService(ExecutorService service) {
|
||||
service.shutdownNow();
|
||||
}
|
||||
}
|
@ -1,518 +0,0 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.*;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
/**
|
||||
* A protocol manager that delays all packet listener registrations and unregistrations until
|
||||
* an underlying protocol manager can be constructed.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class DelayedPacketManager implements InternalManager {
|
||||
// Registering packet IDs that are not supported
|
||||
public static final ReportType REPORT_CANNOT_SEND_QUEUED_PACKET = new ReportType("Cannot send queued packet %s.");
|
||||
public static final ReportType REPORT_CANNOT_SEND_QUEUED_WIRE_PACKET = new ReportType("Cannot send queued wire packet %s.");
|
||||
public static final ReportType REPORT_CANNOT_REGISTER_QUEUED_LISTENER = new ReportType("Cannot register queued listener %s.");
|
||||
|
||||
private volatile InternalManager delegate;
|
||||
|
||||
// Queued actions
|
||||
private final List<Runnable> queuedActions = Collections.synchronizedList(Lists.<Runnable>newArrayList());
|
||||
private final List<PacketListener> queuedListeners = Collections.synchronizedList(Lists.<PacketListener>newArrayList());
|
||||
|
||||
private AsynchronousManager asyncManager;
|
||||
private ErrorReporter reporter;
|
||||
|
||||
// The current hook
|
||||
private PlayerInjectHooks hook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
|
||||
// If we have been closed
|
||||
private boolean closed;
|
||||
private boolean debug;
|
||||
|
||||
// Queued registration
|
||||
private PluginManager queuedManager;
|
||||
private Plugin queuedPlugin;
|
||||
|
||||
private MinecraftVersion version;
|
||||
|
||||
public DelayedPacketManager(@Nonnull ErrorReporter reporter, @Nonnull MinecraftVersion version) {
|
||||
Preconditions.checkNotNull(reporter, "reporter cannot be NULL.");
|
||||
Preconditions.checkNotNull(version, "version cannot be NULL.");
|
||||
|
||||
this.reporter = reporter;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying protocol manager.
|
||||
* @return The underlying manager.
|
||||
*/
|
||||
public InternalManager getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player player) {
|
||||
if (delegate != null)
|
||||
return delegate.getProtocolVersion(player);
|
||||
else
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftVersion getMinecraftVersion() {
|
||||
if (delegate != null)
|
||||
return delegate.getMinecraftVersion();
|
||||
else
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the delegate to the underlying manager.
|
||||
* <p>
|
||||
* This will prompt this packet manager to immediately transmit and
|
||||
* register all queued packets an listeners.
|
||||
* @param delegate - delegate to the new manager.
|
||||
*/
|
||||
protected void setDelegate(InternalManager delegate) {
|
||||
this.delegate = delegate;
|
||||
|
||||
if (delegate != null) {
|
||||
// Update the hook if needed
|
||||
if (!Objects.equal(delegate.getPlayerHook(), hook)) {
|
||||
delegate.setPlayerHook(hook);
|
||||
}
|
||||
// Register events as well
|
||||
if (queuedManager != null && queuedPlugin != null) {
|
||||
delegate.registerEvents(queuedManager, queuedPlugin);
|
||||
}
|
||||
// And update the debug mode
|
||||
delegate.setDebug(debug);
|
||||
|
||||
// Add any pending listeners
|
||||
synchronized (queuedListeners) {
|
||||
for (PacketListener listener : queuedListeners) {
|
||||
try {
|
||||
delegate.addPacketListener(listener);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Inform about this plugin error
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_REGISTER_QUEUED_LISTENER).
|
||||
callerParam(delegate).messageParam(listener).error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute any delayed actions
|
||||
synchronized (queuedActions) {
|
||||
for (Runnable action : queuedActions) {
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't keep this around anymore
|
||||
queuedListeners.clear();
|
||||
queuedActions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable queuedAddPacket(final ConnectionSide side, final Player player, final PacketContainer packet,
|
||||
final NetworkMarker marker, final boolean filtered) {
|
||||
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Attempt to send it now
|
||||
switch (side) {
|
||||
case CLIENT_SIDE:
|
||||
delegate.recieveClientPacket(player, packet, marker, filtered);
|
||||
break;
|
||||
case SERVER_SIDE:
|
||||
delegate.sendServerPacket(player, packet, marker, filtered);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("side cannot be " + side);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Inform about this plugin error
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_PACKET).
|
||||
callerParam(delegate).messageParam(packet).error(e));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||
this.hook = playerHook;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerInjectHooks getPlayerHook() {
|
||||
return hook;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet) throws InvocationTargetException {
|
||||
sendServerPacket(receiver, packet, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
||||
sendServerPacket(receiver, packet, null, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||
if (delegate != null) {
|
||||
delegate.sendServerPacket(receiver, packet, marker, filters);
|
||||
} else {
|
||||
queuedActions.add(queuedAddPacket(ConnectionSide.SERVER_SIDE, receiver, packet, marker, filters));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException {
|
||||
WirePacket packet = new WirePacket(id, bytes);
|
||||
sendWirePacket(receiver, packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWirePacket(final Player receiver, final WirePacket packet) throws InvocationTargetException {
|
||||
if (delegate != null) {
|
||||
delegate.sendWirePacket(receiver, packet);
|
||||
} else {
|
||||
queuedActions.add(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
delegate.sendWirePacket(receiver, packet);
|
||||
} catch (Throwable ex) {
|
||||
// Inform about this plugin error
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_WIRE_PACKET)
|
||||
.callerParam(delegate)
|
||||
.messageParam(packet)
|
||||
.error(ex));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet) throws IllegalAccessException, InvocationTargetException {
|
||||
recieveClientPacket(sender, packet, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException {
|
||||
recieveClientPacket(sender, packet, null, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters) throws IllegalAccessException, InvocationTargetException {
|
||||
if (delegate != null) {
|
||||
delegate.recieveClientPacket(sender, packet, marker, filters);
|
||||
} else {
|
||||
queuedActions.add(queuedAddPacket(ConnectionSide.CLIENT_SIDE, sender, packet, marker, filters));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastServerPacket(final PacketContainer packet, final Entity entity, final boolean includeTracker) {
|
||||
if (delegate != null) {
|
||||
delegate.broadcastServerPacket(packet, entity, includeTracker);
|
||||
} else {
|
||||
queuedActions.add(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegate.broadcastServerPacket(packet, entity, includeTracker);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastServerPacket(final PacketContainer packet, final Location origin, final int maxObserverDistance) {
|
||||
if (delegate != null) {
|
||||
delegate.broadcastServerPacket(packet, origin, maxObserverDistance);
|
||||
} else {
|
||||
queuedActions.add(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegate.broadcastServerPacket(packet, origin, maxObserverDistance);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastServerPacket(final PacketContainer packet) {
|
||||
if (delegate != null) {
|
||||
delegate.broadcastServerPacket(packet);
|
||||
} else {
|
||||
queuedActions.add(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegate.broadcastServerPacket(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<PacketListener> getPacketListeners() {
|
||||
if (delegate != null)
|
||||
return delegate.getPacketListeners();
|
||||
else
|
||||
return ImmutableSet.copyOf(queuedListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPacketListener(PacketListener listener) {
|
||||
if (delegate != null)
|
||||
delegate.addPacketListener(listener);
|
||||
else
|
||||
queuedListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketListener(PacketListener listener) {
|
||||
if (delegate != null)
|
||||
delegate.removePacketListener(listener);
|
||||
else
|
||||
queuedListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketListeners(Plugin plugin) {
|
||||
if (delegate != null) {
|
||||
delegate.removePacketListeners(plugin);
|
||||
} else {
|
||||
for (Iterator<PacketListener> it = queuedListeners.iterator(); it.hasNext(); ) {
|
||||
// Remove listeners of the same plugin
|
||||
if (Objects.equal(it.next().getPlugin(), plugin)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id) {
|
||||
if (delegate != null)
|
||||
return delegate.createPacket(id);
|
||||
return createPacket(id, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id, boolean forceDefaults) {
|
||||
if (delegate != null) {
|
||||
return delegate.createPacket(id);
|
||||
} else {
|
||||
// Fallback implementation
|
||||
PacketContainer packet = new PacketContainer(id);
|
||||
|
||||
// Use any default values if possible
|
||||
if (forceDefaults) {
|
||||
try {
|
||||
packet.getModifier().writeDefaults();
|
||||
} catch (FieldAccessException e) {
|
||||
throw new RuntimeException("Security exception.", e);
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
|
||||
if (delegate != null)
|
||||
return delegate.createPacketConstructor(id, arguments);
|
||||
else
|
||||
return PacketConstructor.DEFAULT.withPacket(id, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketConstructor createPacketConstructor(PacketType type, Object... arguments) {
|
||||
if (delegate != null)
|
||||
return delegate.createPacketConstructor(type, arguments);
|
||||
else
|
||||
return PacketConstructor.DEFAULT.withPacket(type, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Set<Integer> getSendingFilters() {
|
||||
if (delegate != null) {
|
||||
return delegate.getSendingFilters();
|
||||
} else {
|
||||
// Linear scan is fast enough here
|
||||
Set<Integer> sending = Sets.newHashSet();
|
||||
|
||||
for (PacketListener listener : queuedListeners) {
|
||||
sending.addAll(listener.getSendingWhitelist().getWhitelist());
|
||||
}
|
||||
return sending;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Set<Integer> getReceivingFilters() {
|
||||
if (delegate != null) {
|
||||
return delegate.getReceivingFilters();
|
||||
} else {
|
||||
Set<Integer> recieving = Sets.newHashSet();
|
||||
|
||||
for (PacketListener listener : queuedListeners) {
|
||||
recieving.addAll(listener.getReceivingWhitelist().getWhitelist());
|
||||
}
|
||||
return recieving;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketContainer createPacket(PacketType type) {
|
||||
return createPacket(type.getLegacyId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketContainer createPacket(PacketType type, boolean forceDefaults) {
|
||||
return createPacket(type.getLegacyId(), forceDefaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getSendingFilterTypes() {
|
||||
return PacketRegistry.toPacketTypes(getSendingFilters(), Sender.SERVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getReceivingFilterTypes() {
|
||||
return PacketRegistry.toPacketTypes(getReceivingFilters(), Sender.CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
|
||||
if (delegate != null)
|
||||
delegate.updateEntity(entity, observers);
|
||||
else
|
||||
EntityUtilities.getInstance().updateEntity(entity, observers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntityFromID(World container, int id) throws FieldAccessException {
|
||||
if (delegate != null)
|
||||
return delegate.getEntityFromID(container, id);
|
||||
else
|
||||
return EntityUtilities.getInstance().getEntityFromID(container, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Player> getEntityTrackers(Entity entity) throws FieldAccessException {
|
||||
if (delegate != null)
|
||||
return delegate.getEntityTrackers(entity);
|
||||
else
|
||||
return EntityUtilities.getInstance().getEntityTrackers(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed || (delegate != null && delegate.isClosed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsynchronousManager getAsynchronousManager() {
|
||||
if (delegate != null)
|
||||
return delegate.getAsynchronousManager();
|
||||
else
|
||||
return asyncManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
|
||||
if (delegate != null) {
|
||||
delegate.setDebug(debug);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the asynchronous manager. This must be set.
|
||||
* @param asyncManager - the asynchronous manager.
|
||||
*/
|
||||
public void setAsynchronousManager(AsynchronousManager asyncManager) {
|
||||
this.asyncManager = asyncManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEvents(PluginManager manager, Plugin plugin) {
|
||||
if (delegate != null) {
|
||||
delegate.registerEvents(manager, plugin);
|
||||
} else {
|
||||
queuedManager = manager;
|
||||
queuedPlugin = plugin;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != null)
|
||||
delegate.close();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist) {
|
||||
for (PacketType type : whitelist.getTypes()) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException(String.format("Packet type in in listener %s was NULL.",
|
||||
PacketAdapter.getPluginName(listener))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@ package com.comphenix.protocol.injector;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
||||
|
||||
/**
|
||||
* Represents an object that initiate the packet listeners.
|
||||
@ -32,13 +31,13 @@ public interface ListenerInvoker {
|
||||
* Invokes the given packet event for every registered listener.
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public abstract void invokePacketRecieving(PacketEvent event);
|
||||
void invokePacketRecieving(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public abstract void invokePacketSending(PacketEvent event);
|
||||
void invokePacketSending(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Retrieve the associated ID of a packet.
|
||||
@ -46,49 +45,12 @@ public interface ListenerInvoker {
|
||||
* @return The packet ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract int getPacketID(Object packet);
|
||||
int getPacketID(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the associated type of a packet.
|
||||
* @param packet - the packet.
|
||||
* @return The packet type.
|
||||
*/
|
||||
public abstract PacketType getPacketType(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the object responsible for intercepting write packets.
|
||||
* @return Object that intercepts write packets.
|
||||
*/
|
||||
public InterceptWritePacket getInterceptWritePacket();
|
||||
|
||||
/**
|
||||
* Determine if a given packet requires input buffering.
|
||||
* @param packetId - the packet to check.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean requireInputBuffer(int packetId);
|
||||
|
||||
/**
|
||||
* Associate a given class with the given packet ID. Internal method.
|
||||
* @param clazz - class to associate.
|
||||
*/
|
||||
public abstract void unregisterPacketClass(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Register a given class in the packet registry. Internal method.
|
||||
* @param clazz - class to register.
|
||||
* @param packetID - the the new associated packet ID.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void registerPacketClass(Class<?> clazz, int packetID);
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* @param packetID - the packet ID.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @return The associated class.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract Class<?> getPacketClassFromID(int packetID, boolean forceVanilla);
|
||||
PacketType getPacketType(Object packet);
|
||||
}
|
||||
|
@ -1,92 +0,0 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
/**
|
||||
* Packets that are known to be transmitted during login.
|
||||
* <p>
|
||||
* This may be dynamically extended later.
|
||||
* @author Kristian
|
||||
*/
|
||||
class LoginPackets {
|
||||
private IntegerSet clientSide = new IntegerSet(Packets.PACKET_COUNT);
|
||||
private IntegerSet serverSide = new IntegerSet(Packets.PACKET_COUNT);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public LoginPackets(MinecraftVersion version) {
|
||||
// Ordinary login
|
||||
clientSide.add(Packets.Client.HANDSHAKE);
|
||||
serverSide.add(Packets.Server.KEY_REQUEST);
|
||||
clientSide.add(Packets.Client.KEY_RESPONSE);
|
||||
serverSide.add(Packets.Server.KEY_RESPONSE);
|
||||
clientSide.add(Packets.Client.CLIENT_COMMAND);
|
||||
serverSide.add(Packets.Server.LOGIN);
|
||||
|
||||
// List ping
|
||||
clientSide.add(Packets.Client.GET_INFO);
|
||||
|
||||
// In 1.6.2, Minecraft started sending CUSTOM_PAYLOAD in the server list protocol
|
||||
// MCPC+/Cauldron contains Forge, which uses CUSTOM_PAYLOAD during login
|
||||
if (version.isAtLeast(MinecraftVersion.HORSE_UPDATE) || isCauldronOrMCPC()) {
|
||||
clientSide.add(Packets.Client.CUSTOM_PAYLOAD);
|
||||
}
|
||||
|
||||
if (isCauldronOrMCPC()) {
|
||||
serverSide.add(Packets.Server.CUSTOM_PAYLOAD);
|
||||
}
|
||||
|
||||
serverSide.add(Packets.Server.KICK_DISCONNECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are running MCPC or Cauldron.
|
||||
* @return TRUE if we are, FALSE otherwise.
|
||||
*/
|
||||
private static boolean isCauldronOrMCPC() {
|
||||
String version = Bukkit.getServer().getVersion();
|
||||
return version.contains("MCPC") || version.contains("Cauldron");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a packet may be sent during login from a given direction.
|
||||
* @param packetId - the ID of the packet.
|
||||
* @param side - the direction.
|
||||
* @return TRUE if it may, FALSE otherwise.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isLoginPacket(int packetId, ConnectionSide side) {
|
||||
switch (side) {
|
||||
case CLIENT_SIDE:
|
||||
return clientSide.contains(packetId);
|
||||
case SERVER_SIDE:
|
||||
return serverSide.contains(packetId);
|
||||
case BOTH:
|
||||
return clientSide.contains(packetId) ||
|
||||
serverSide.contains(packetId);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown connection side: " + side);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given packet may be sent during login.
|
||||
* @param type - the packet type.
|
||||
* @return TRUE if it may, FALSE otherwise.
|
||||
*/
|
||||
public boolean isLoginPacket(PacketType type) {
|
||||
if (!MinecraftReflection.isUsingNetty())
|
||||
return isLoginPacket(type.getLegacyId(), type.getSender().toSide());
|
||||
|
||||
return PacketType.Login.Client.getInstance().hasMember(type) ||
|
||||
PacketType.Login.Server.getInstance().hasMember(type) ||
|
||||
PacketType.Status.Client.getInstance().hasMember(type) ||
|
||||
PacketType.Status.Server.getInstance().hasMember(type);
|
||||
}
|
||||
}
|
@ -81,7 +81,7 @@ public class PacketConstructor {
|
||||
*/
|
||||
@Deprecated
|
||||
public int getPacketID() {
|
||||
return type.getLegacyId();
|
||||
return type.getCurrentId();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,22 +101,6 @@ public class PacketConstructor {
|
||||
return new PacketConstructor(type, constructorMethod, unwrappers, paramUnwrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a packet constructor that creates packets using the given ID.
|
||||
* <p>
|
||||
* Note that if you pass a Class as a value, it will use its type directly.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #withPacket(PacketType, Object[])} instead.
|
||||
* @param id - legacy (1.6.4) packet ID.
|
||||
* @param values - the values that will match each parameter in the desired constructor.
|
||||
* @return A packet constructor with these types.
|
||||
* @throws IllegalArgumentException If no packet constructor could be created with these types.
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketConstructor withPacket(int id, Object[] values) {
|
||||
return withPacket(PacketType.findLegacy(id), values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a packet constructor that creates packets using the given types.
|
||||
* <p>
|
||||
|
@ -2,26 +2,14 @@ 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.protocol.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;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class PacketFilterBuilder {
|
||||
public static final ReportType REPORT_TEMPORARY_EVENT_ERROR = new ReportType("Unable to register or handle temporary event.");
|
||||
public static final ReportType REPORT_SPIGOT_IS_DELAYING_INJECTOR = new ReportType("Delaying due to Spigot.");
|
||||
|
||||
private ClassLoader classLoader;
|
||||
private Server server;
|
||||
private Plugin library;
|
||||
@ -185,63 +173,10 @@ public class PacketFilterBuilder {
|
||||
|
||||
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, mcVersion);
|
||||
|
||||
// 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));
|
||||
}
|
||||
});
|
||||
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_SPIGOT_IS_DELAYING_INJECTOR));
|
||||
|
||||
// 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.
|
||||
|
@ -24,7 +24,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
@ -37,25 +37,21 @@ import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.*;
|
||||
import com.comphenix.protocol.injector.netty.ProtocolInjector;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
||||
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.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjector.ServerHandlerNull;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectorBuilder;
|
||||
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.Util;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
@ -72,8 +68,6 @@ import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public final class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
|
||||
public static final ReportType REPORT_CANNOT_LOAD_PACKET_LIST = new ReportType("Cannot load server and client packet list.");
|
||||
@ -106,8 +100,7 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
private DelayedSingleTask unhookTask;
|
||||
|
||||
// Create a concurrent set
|
||||
private Set<PacketListener> packetListeners =
|
||||
Collections.newSetFromMap(new ConcurrentHashMap<PacketListener, Boolean>());
|
||||
private Set<PacketListener> packetListeners = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
// Packet injection
|
||||
private PacketInjector packetInjector;
|
||||
@ -115,9 +108,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
// Different injection types per game phase
|
||||
private PlayerInjectionHandler playerInjection;
|
||||
|
||||
// Intercepting write packet methods
|
||||
private InterceptWritePacket interceptWritePacket;
|
||||
|
||||
// Whether or not a packet must be input buffered
|
||||
private volatile Set<PacketType> inputBufferedPackets = Sets.newHashSet();
|
||||
|
||||
@ -154,9 +144,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
// Whether or not plugins are using the send/receive methods
|
||||
private AtomicBoolean packetCreation = new AtomicBoolean();
|
||||
|
||||
// Spigot listener, if in use
|
||||
private SpigotPacketInjector spigotInjector;
|
||||
|
||||
// Netty injector (for 1.7.2)
|
||||
private ProtocolInjector nettyInjector;
|
||||
|
||||
@ -169,9 +156,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
// The current Minecraft version
|
||||
private MinecraftVersion minecraftVersion;
|
||||
|
||||
// Login packets
|
||||
private LoginPackets loginPackets;
|
||||
|
||||
// Debug mode
|
||||
private boolean debug;
|
||||
|
||||
@ -181,18 +165,15 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
*/
|
||||
public PacketFilterManager(PacketFilterBuilder builder) {
|
||||
// Used to determine if injection is needed
|
||||
Predicate<GamePhase> isInjectionNecessary = new Predicate<GamePhase>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable GamePhase phase) {
|
||||
boolean result = true;
|
||||
Predicate<GamePhase> isInjectionNecessary = phase -> {
|
||||
boolean result = true;
|
||||
|
||||
if (phase.hasLogin())
|
||||
result &= getPhaseLoginCount() > 0;
|
||||
// Note that we will still hook players if the unhooking has been delayed
|
||||
if (phase.hasPlaying())
|
||||
result &= getPhasePlayingCount() > 0 || unhookTask.isRunning();
|
||||
return result;
|
||||
}
|
||||
if (phase.hasLogin())
|
||||
result &= getPhaseLoginCount() > 0;
|
||||
// Note that we will still hook players if the unhooking has been delayed
|
||||
if (phase.hasPlaying())
|
||||
result &= getPhasePlayingCount() > 0 || unhookTask.isRunning();
|
||||
return result;
|
||||
};
|
||||
|
||||
// Listener containers
|
||||
@ -217,47 +198,11 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
|
||||
// Prepare version
|
||||
this.minecraftVersion = builder.getMinecraftVersion();
|
||||
this.loginPackets = new LoginPackets(minecraftVersion);
|
||||
|
||||
// The write packet interceptor
|
||||
this.interceptWritePacket = new InterceptWritePacket(reporter);
|
||||
this.nettyInjector = new ProtocolInjector(builder.getLibrary(), this, reporter);
|
||||
this.playerInjection = nettyInjector.getPlayerInjector();
|
||||
this.packetInjector = nettyInjector.getPacketInjector();
|
||||
|
||||
// Use the correct injection type
|
||||
if (MinecraftReflection.isUsingNetty()) {
|
||||
this.nettyInjector = new ProtocolInjector(builder.getLibrary(), this, reporter);
|
||||
this.playerInjection = nettyInjector.getPlayerInjector();
|
||||
this.packetInjector = nettyInjector.getPacketInjector();
|
||||
|
||||
} else if (builder.isNettyEnabled()) {
|
||||
this.spigotInjector = new SpigotPacketInjector(reporter, this, server);
|
||||
this.playerInjection = spigotInjector.getPlayerHandler();
|
||||
this.packetInjector = spigotInjector.getPacketInjector();
|
||||
|
||||
// Set real injector, in case we need it
|
||||
spigotInjector.setProxyPacketInjector(PacketInjectorBuilder.newBuilder().
|
||||
invoker(this).
|
||||
reporter(reporter).
|
||||
playerInjection(playerInjection).
|
||||
buildInjector()
|
||||
);
|
||||
|
||||
} else {
|
||||
// Initialize standard injection mangers
|
||||
this.playerInjection = PlayerInjectorBuilder.newBuilder().
|
||||
invoker(this).
|
||||
server(server).
|
||||
reporter(reporter).
|
||||
packetListeners(packetListeners).
|
||||
injectionFilter(isInjectionNecessary).
|
||||
version(builder.getMinecraftVersion()).
|
||||
buildHandler();
|
||||
|
||||
this.packetInjector = PacketInjectorBuilder.newBuilder().
|
||||
invoker(this).
|
||||
reporter(reporter).
|
||||
playerInjection(playerInjection).
|
||||
buildInjector();
|
||||
}
|
||||
this.asyncFilterManager = builder.getAsyncManager();
|
||||
this.library = builder.getLibrary();
|
||||
|
||||
@ -331,11 +276,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
return ImmutableSet.copyOf(packetListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterceptWritePacket getInterceptWritePacket() {
|
||||
return interceptWritePacket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn of common programming mistakes.
|
||||
* @param plugin - plugin to check.
|
||||
@ -410,13 +350,25 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given packet may be sent during login.
|
||||
* @param type - the packet type.
|
||||
* @return TRUE if it may, FALSE otherwise.
|
||||
*/
|
||||
public boolean isLoginPacket(PacketType type) {
|
||||
return PacketType.Login.Client.getInstance().hasMember(type) ||
|
||||
PacketType.Login.Server.getInstance().hasMember(type) ||
|
||||
PacketType.Status.Client.getInstance().hasMember(type) ||
|
||||
PacketType.Status.Server.getInstance().hasMember(type);
|
||||
}
|
||||
|
||||
private GamePhase processPhase(ListeningWhitelist whitelist) {
|
||||
// Determine if this is a login packet, ensuring that gamephase detection is enabled
|
||||
if (!whitelist.getGamePhase().hasLogin() &&
|
||||
!whitelist.getOptions().contains(ListenerOptions.DISABLE_GAMEPHASE_DETECTION)) {
|
||||
|
||||
for (PacketType type : whitelist.getTypes()) {
|
||||
if (loginPackets.isLoginPacket(type)) {
|
||||
if (isLoginPacket(type)) {
|
||||
return GamePhase.BOTH;
|
||||
}
|
||||
}
|
||||
@ -565,11 +517,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requireInputBuffer(int packetId) {
|
||||
return inputBufferedPackets.contains(PacketType.findLegacy(packetId, Sender.CLIENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a packet sending or receiving event.
|
||||
* <p>
|
||||
@ -858,23 +805,11 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id) {
|
||||
return createPacket(PacketType.findLegacy(id), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketContainer createPacket(PacketType type) {
|
||||
return createPacket(type, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PacketContainer createPacket(int id, boolean forceDefaults) {
|
||||
return createPacket(PacketType.findLegacy(id), forceDefaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketContainer createPacket(PacketType type, boolean forceDefaults) {
|
||||
PacketContainer packet = new PacketContainer(type);
|
||||
@ -891,27 +826,11 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
|
||||
return PacketConstructor.DEFAULT.withPacket(id, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketConstructor createPacketConstructor(PacketType type, Object... arguments) {
|
||||
return PacketConstructor.DEFAULT.withPacket(type, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Set<Integer> getSendingFilters() {
|
||||
return PacketRegistry.toLegacy(playerInjection.getSendingFilters());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getReceivingFilters() {
|
||||
return PacketRegistry.toLegacy(packetInjector.getPacketHandlers());
|
||||
}
|
||||
@Override
|
||||
public Set<PacketType> getSendingFilterTypes() {
|
||||
return Collections.unmodifiableSet(playerInjection.getSendingFilters());
|
||||
@ -964,8 +883,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
*/
|
||||
@Override
|
||||
public void registerEvents(PluginManager manager, final Plugin plugin) {
|
||||
if (spigotInjector != null && !spigotInjector.register(plugin))
|
||||
throw new IllegalArgumentException("Spigot has already been registered.");
|
||||
if (nettyInjector != null)
|
||||
nettyInjector.inject();
|
||||
|
||||
@ -1012,8 +929,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
// Let's clean up the other injection first.
|
||||
playerInjection.uninjectPlayer(event.getPlayer().getAddress());
|
||||
playerInjection.injectPlayer(event.getPlayer(), ConflictStrategy.OVERRIDE);
|
||||
} catch (ServerHandlerNull e) {
|
||||
// Caused by logged out players, or fake login events in MCPC+/Cauldron. Ignore it.
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(PacketFilterManager.this,
|
||||
Report.newBuilder(REPORT_CANNOT_INJECT_PLAYER).callerParam(event).error(e)
|
||||
@ -1087,44 +1002,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void registerPacketClass(Class<?> clazz, int packetID) {
|
||||
PacketRegistry.getPacketToID().put(clazz, packetID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void unregisterPacketClass(Class<?> clazz) {
|
||||
PacketRegistry.getPacketToID().remove(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Class<?> getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||
return PacketRegistry.getPacketClassFromID(packetID, forceVanilla);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet.
|
||||
* @return An immutable set of every known server packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Set<Integer> getServerPackets() throws FieldAccessException {
|
||||
return PacketRegistry.getServerPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported client packet.
|
||||
* @return An immutable set of every known client packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Set<Integer> getClientPackets() throws FieldAccessException {
|
||||
return PacketRegistry.getClientPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current plugin class loader.
|
||||
* @return Class loader.
|
||||
@ -1147,8 +1024,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
// Remove packet handlers
|
||||
if (packetInjector != null)
|
||||
packetInjector.cleanupAll();
|
||||
if (spigotInjector != null)
|
||||
spigotInjector.cleanupAll();
|
||||
if (nettyInjector != null)
|
||||
nettyInjector.close();
|
||||
|
||||
@ -1161,9 +1036,6 @@ public final class PacketFilterManager implements ListenerInvoker, InternalManag
|
||||
recievedListeners = null;
|
||||
sendingListeners = null;
|
||||
|
||||
// Also cleanup the interceptor for the write packet method
|
||||
interceptWritePacket.cleanup();
|
||||
|
||||
// Clean up async handlers. We have to do this last.
|
||||
asyncFilterManager.cleanupAll();
|
||||
}
|
||||
|
@ -37,22 +37,9 @@ import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
*/
|
||||
public class StructureCache {
|
||||
// Structure modifiers
|
||||
private static ConcurrentMap<PacketType, StructureModifier<Object>> structureModifiers =
|
||||
new ConcurrentHashMap<PacketType, StructureModifier<Object>>();
|
||||
private static ConcurrentMap<PacketType, StructureModifier<Object>> structureModifiers = new ConcurrentHashMap<>();
|
||||
|
||||
private static Set<PacketType> compiling = new HashSet<PacketType>();
|
||||
|
||||
/**
|
||||
* Creates an empty Minecraft packet of the given id.
|
||||
* <p>
|
||||
* Decreated: Use {@link #newPacket(PacketType)} instead.
|
||||
* @param legacyId - legacy (1.6.4) packet id.
|
||||
* @return Created packet.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Object newPacket(int legacyId) {
|
||||
return newPacket(PacketType.findLegacy(legacyId));
|
||||
}
|
||||
private static Set<PacketType> compiling = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Creates an empty Minecraft packet of the given type.
|
||||
@ -76,18 +63,6 @@ public class StructureCache {
|
||||
throw new IllegalArgumentException("Cannot find associated packet class: " + type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet id.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getStructure(PacketType)} instead.
|
||||
* @param legacyId - the legacy (1.6.4) packet ID.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
@Deprecated
|
||||
public static StructureModifier<Object> getStructure(int legacyId) {
|
||||
return getStructure(PacketType.findLegacy(legacyId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet type.
|
||||
* @param type - packet type.
|
||||
@ -119,19 +94,6 @@ public class StructureCache {
|
||||
return getStructure(PacketRegistry.getPacketType(packetType), compile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet ID.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getStructure(PacketType, boolean)} instead.
|
||||
* @param legacyId - the legacy (1.6.4) packet ID.
|
||||
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
@Deprecated
|
||||
public static StructureModifier<Object> getStructure(final int legacyId, boolean compile) {
|
||||
return getStructure(PacketType.findLegacy(legacyId), compile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet type.
|
||||
* @param type - packet type.
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.comphenix.protocol.injector.spigot;
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.util.Set;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.comphenix.protocol.injector.spigot;
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.util.Set;
|
@ -44,8 +44,6 @@ import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.injector.spigot.AbstractPacketInjector;
|
||||
import com.comphenix.protocol.injector.spigot.AbstractPlayerHandler;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
@ -1,64 +0,0 @@
|
||||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Represents an input stream that stores every read block of bytes in another output stream.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class CaptureInputStream extends FilterInputStream {
|
||||
protected OutputStream out;
|
||||
|
||||
public CaptureInputStream(InputStream in, OutputStream out) {
|
||||
super(in);
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int value = super.read();
|
||||
|
||||
// Write the byte
|
||||
if (value >= 0)
|
||||
out.write(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
out.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
int count = super.read(b);
|
||||
|
||||
if (count > 0 ) {
|
||||
out.write(b, 0, count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
int count = super.read(b, off, len);
|
||||
|
||||
if (count > 0 ) {
|
||||
out.write(b, off, count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the output stream that receives all the read information.
|
||||
* @return The output stream everything is copied to.
|
||||
*/
|
||||
public OutputStream getOutputStream() {
|
||||
return out;
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.io.DataOutput;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.reflect.MethodInfo;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Retrieve a packet instance that has its write method intercepted.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class InterceptWritePacket {
|
||||
public static final ReportType REPORT_CANNOT_FIND_WRITE_PACKET_METHOD = new ReportType("Cannot find write packet method in %s.");
|
||||
public static final ReportType REPORT_CANNOT_CONSTRUCT_WRITE_PROXY = new ReportType("Cannot construct write proxy packet %s.");
|
||||
|
||||
/**
|
||||
* Matches the readPacketData(DataInputStream) method in Packet.
|
||||
*/
|
||||
private static FuzzyMethodContract WRITE_PACKET = FuzzyMethodContract.newBuilder().
|
||||
returnTypeVoid().
|
||||
parameterDerivedOf(DataOutput.class).
|
||||
parameterCount(1).
|
||||
build();
|
||||
|
||||
private CallbackFilter filter;
|
||||
private boolean writePacketIntercepted;
|
||||
|
||||
private ConcurrentMap<Integer, Class<?>> proxyClasses = Maps.newConcurrentMap();
|
||||
private ErrorReporter reporter;
|
||||
|
||||
private WritePacketModifier modifierWrite;
|
||||
private WritePacketModifier modifierRest;
|
||||
|
||||
public InterceptWritePacket(ErrorReporter reporter) {
|
||||
this.reporter = reporter;
|
||||
|
||||
// Initialize modifiers
|
||||
this.modifierWrite = new WritePacketModifier(reporter, true);
|
||||
this.modifierRest = new WritePacketModifier(reporter, false);
|
||||
}
|
||||
|
||||
// TODO: PacketId should probably do something...
|
||||
private Class<?> createProxyClass() {
|
||||
// Construct the proxy object
|
||||
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
|
||||
|
||||
// Attempt to share callback filter
|
||||
if (filter == null) {
|
||||
filter = new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
// Skip methods defined in Object
|
||||
if (WRITE_PACKET.isMatch(MethodInfo.fromMethod(method), null)) {
|
||||
writePacketIntercepted = true;
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Subclass the generic packet class
|
||||
ex.setSuperclass(MinecraftReflection.getPacketClass());
|
||||
ex.setCallbackFilter(filter);
|
||||
ex.setUseCache(false);
|
||||
|
||||
ex.setCallbackTypes( new Class[] { WritePacketModifier.class, WritePacketModifier.class });
|
||||
Class<?> proxyClass = ex.createClass();
|
||||
|
||||
// Register write modifiers too
|
||||
Enhancer.registerStaticCallbacks(proxyClass, new Callback[] { modifierWrite, modifierRest });
|
||||
|
||||
if (proxyClass != null) {
|
||||
// Check that we found the read method
|
||||
if (!writePacketIntercepted) {
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_FIND_WRITE_PACKET_METHOD).
|
||||
messageParam(MinecraftReflection.getPacketClass()));
|
||||
}
|
||||
}
|
||||
return proxyClass;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private Class<?> getProxyClass(int packetId) {
|
||||
Class<?> stored = proxyClasses.get(packetId);
|
||||
|
||||
// Concurrent pattern
|
||||
if (stored == null) {
|
||||
final Class<?> created = createProxyClass();
|
||||
stored = proxyClasses.putIfAbsent(packetId, created);
|
||||
|
||||
// We won!
|
||||
if (stored == null) {
|
||||
stored = created;
|
||||
PacketRegistry.getPacketToID().put(stored, packetId);
|
||||
}
|
||||
}
|
||||
return stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new instance of the proxy object.
|
||||
* @param proxyObject - Object to construct proxy of
|
||||
* @param event - Packet event
|
||||
* @param marker - Network marker
|
||||
* @return New instance of the proxy, or null if we failed.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public Object constructProxy(Object proxyObject, PacketEvent event, NetworkMarker marker) {
|
||||
Class<?> proxyClass = null;
|
||||
|
||||
try {
|
||||
proxyClass = getProxyClass(event.getPacketID());
|
||||
Object generated = proxyClass.newInstance();
|
||||
|
||||
modifierWrite.register(generated, proxyObject, event, marker);
|
||||
modifierRest.register(generated, proxyObject, event, marker);
|
||||
return generated;
|
||||
|
||||
} catch (Exception e) {
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_CONSTRUCT_WRITE_PROXY).
|
||||
messageParam(proxyClass));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the write packet proxy class should be removed.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void cleanup() {
|
||||
// Remove all proxy classes from the registry
|
||||
for (Class<?> stored : proxyClasses.values()) {
|
||||
PacketRegistry.getPacketToID().remove(stored);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ForwardingMap;
|
||||
import com.google.common.collect.ForwardingMultimap;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
public class InverseMaps {
|
||||
private InverseMaps() {
|
||||
// Not constructable
|
||||
}
|
||||
|
||||
public static <K, V> Multimap<K, V> inverseMultimap(final Map<V, K> map, final Predicate<Map.Entry<V, K>> filter) {
|
||||
final MapContainer container = new MapContainer(map);
|
||||
|
||||
return new ForwardingMultimap<K, V>() {
|
||||
// The cached multimap
|
||||
private Multimap<K, V> inverseMultimap;
|
||||
|
||||
@Override
|
||||
protected Multimap<K, V> delegate() {
|
||||
if (container.hasChanged()) {
|
||||
inverseMultimap = HashMultimap.create();
|
||||
|
||||
// Construct the inverse map
|
||||
for (Map.Entry<V, K> entry : map.entrySet()) {
|
||||
if (filter.apply(entry)) {
|
||||
inverseMultimap.put(entry.getValue(), entry.getKey());
|
||||
}
|
||||
}
|
||||
container.setChanged(false);
|
||||
}
|
||||
return inverseMultimap;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> inverseMap(final Map<V, K> map, final Predicate<Map.Entry<V, K>> filter) {
|
||||
final MapContainer container = new MapContainer(map);
|
||||
|
||||
return new ForwardingMap<K, V>() {
|
||||
// The cached map
|
||||
private Map<K, V> inverseMap;
|
||||
|
||||
@Override
|
||||
protected Map<K, V> delegate() {
|
||||
if (container.hasChanged()) {
|
||||
inverseMap = Maps.newHashMap();
|
||||
|
||||
// Construct the inverse map
|
||||
for (Map.Entry<V, K> entry : map.entrySet()) {
|
||||
if (filter.apply(entry)) {
|
||||
inverseMap.put(entry.getValue(), entry.getKey());
|
||||
}
|
||||
}
|
||||
container.setChanged(false);
|
||||
}
|
||||
return inverseMap;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
/**
|
||||
* Represents a network marker for 1.6.4 and earlier.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class LegacyNetworkMarker extends NetworkMarker {
|
||||
public LegacyNetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, PacketType type) {
|
||||
super(side, inputBuffer, type);
|
||||
}
|
||||
|
||||
public LegacyNetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
|
||||
super(side, inputBuffer, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
|
||||
// This has already been done
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
|
||||
return ByteBuffer.wrap(Bytes.concat(new byte[] { (byte) type.getLegacyId() }, buffer.array()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream addHeader(final DataInputStream input, final PacketType type) {
|
||||
ByteSource header = new ByteSource() {
|
||||
@Override
|
||||
public InputStream openStream() throws IOException {
|
||||
byte[] data = new byte[] { (byte) type.getLegacyId() };
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
};
|
||||
|
||||
ByteSource data = new ByteSource() {
|
||||
@Override
|
||||
public InputStream openStream() throws IOException {
|
||||
return input;
|
||||
}
|
||||
};
|
||||
|
||||
// Combine them into a single stream
|
||||
try {
|
||||
return new DataInputStream(ByteSource.concat(header, data).openStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot add header.", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,334 +0,0 @@
|
||||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.TroveWrapper;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
class LegacyPacketRegistry {
|
||||
private static final int MIN_SERVER_PACKETS = 5;
|
||||
private static final int MIN_CLIENT_PACKETS = 5;
|
||||
|
||||
// Fuzzy reflection
|
||||
private FuzzyReflection packetRegistry;
|
||||
|
||||
// The packet class to packet ID translator
|
||||
private Map<Class, Integer> packetToID;
|
||||
|
||||
// Packet IDs to classes, grouped by whether or not they're vanilla or custom defined
|
||||
private Multimap<Integer, Class> customIdToPacket;
|
||||
private Map<Integer, Class> vanillaIdToPacket;
|
||||
|
||||
// Whether or not certain packets are sent by the client or the server
|
||||
private ImmutableSet<Integer> serverPackets;
|
||||
private ImmutableSet<Integer> clientPackets;
|
||||
|
||||
// The underlying sets
|
||||
private Set<Integer> serverPacketsRef;
|
||||
private Set<Integer> clientPacketsRef;
|
||||
|
||||
// New proxy values
|
||||
private Map<Integer, Class> overwrittenPackets = new HashMap<Integer, Class>();
|
||||
|
||||
// Vanilla packets
|
||||
private Map<Integer, Class> previousValues = new HashMap<Integer, Class>();
|
||||
|
||||
/**
|
||||
* Initialize the registry.
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public void initialize() {
|
||||
if (packetToID == null) {
|
||||
try {
|
||||
Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class);
|
||||
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Spigot 1.2.5 MCPC workaround
|
||||
try {
|
||||
packetToID = getSpigotWrapper();
|
||||
} catch (Exception e2) {
|
||||
// Very bad indeed
|
||||
throw new IllegalArgumentException(e.getMessage() + "; Spigot workaround failed.", e2);
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
||||
}
|
||||
|
||||
// Create the inverse maps
|
||||
customIdToPacket = InverseMaps.inverseMultimap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable Entry<Class, Integer> entry) {
|
||||
return !MinecraftReflection.isMinecraftClass(entry.getKey());
|
||||
}
|
||||
});
|
||||
|
||||
// And the vanilla pack - here we assume a unique ID to class mapping
|
||||
vanillaIdToPacket = InverseMaps.inverseMap(packetToID, new Predicate<Map.Entry<Class, Integer>>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable Entry<Class, Integer> entry) {
|
||||
return MinecraftReflection.isMinecraftClass(entry.getKey());
|
||||
}
|
||||
});
|
||||
}
|
||||
initializeSets();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initializeSets() throws FieldAccessException {
|
||||
if (serverPacketsRef == null || clientPacketsRef == null) {
|
||||
List<Field> sets = getPacketRegistry().getFieldListByType(Set.class);
|
||||
|
||||
try {
|
||||
if (sets.size() > 1) {
|
||||
serverPacketsRef = (Set<Integer>) FieldUtils.readStaticField(sets.get(0), true);
|
||||
clientPacketsRef = (Set<Integer>) FieldUtils.readStaticField(sets.get(1), true);
|
||||
|
||||
// Impossible
|
||||
if (serverPacketsRef == null || clientPacketsRef == null)
|
||||
throw new FieldAccessException("Packet sets are in an illegal state.");
|
||||
|
||||
// NEVER allow callers to modify the underlying sets
|
||||
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
||||
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
||||
|
||||
// Check sizes
|
||||
if (serverPackets.size() < MIN_SERVER_PACKETS)
|
||||
throw new InsufficientPacketsException("Insufficient server packets.", false, serverPackets.size());
|
||||
if (clientPackets.size() < MIN_CLIENT_PACKETS)
|
||||
throw new InsufficientPacketsException("Insufficient client packets.", true, clientPackets.size());
|
||||
|
||||
} else {
|
||||
throw new FieldAccessException("Cannot retrieve packet client/server sets.");
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot access field.", e);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Copy over again if it has changed
|
||||
if (serverPacketsRef != null && serverPacketsRef.size() != serverPackets.size())
|
||||
serverPackets = ImmutableSet.copyOf(serverPacketsRef);
|
||||
if (clientPacketsRef != null && clientPacketsRef.size() != clientPackets.size())
|
||||
clientPackets = ImmutableSet.copyOf(clientPacketsRef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet mapping.
|
||||
* @return The packet map.
|
||||
*/
|
||||
public Map<Class, Integer> getPacketToID() {
|
||||
// Initialize it, if we haven't already
|
||||
if (packetToID == null) {
|
||||
initialize();
|
||||
}
|
||||
return packetToID;
|
||||
}
|
||||
|
||||
private Map<Class, Integer> getSpigotWrapper() throws IllegalAccessException {
|
||||
// If it talks like a duck, etc.
|
||||
// Perhaps it would be nice to have a proper duck typing library as well
|
||||
FuzzyClassContract mapLike = FuzzyClassContract.newBuilder().
|
||||
method(FuzzyMethodContract.newBuilder().
|
||||
nameExact("size").returnTypeExact(int.class)).
|
||||
method(FuzzyMethodContract.newBuilder().
|
||||
nameExact("put").parameterCount(2)).
|
||||
method(FuzzyMethodContract.newBuilder().
|
||||
nameExact("get").parameterCount(1)).
|
||||
build();
|
||||
|
||||
Field packetsField = getPacketRegistry().getField(
|
||||
FuzzyFieldContract.newBuilder().typeMatches(mapLike).build());
|
||||
Object troveMap = FieldUtils.readStaticField(packetsField, true);
|
||||
|
||||
// Fix incorrect no entry values
|
||||
TroveWrapper.transformNoEntryValue(troveMap, new Function<Integer, Integer>() {
|
||||
public Integer apply(Integer value) {
|
||||
if (value >= 0 && value < 256) {
|
||||
// Someone forgot to set the no entry value. Let's help them.
|
||||
return -1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
// We'll assume this a Trove map
|
||||
return TroveWrapper.getDecoratedMap(troveMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cached fuzzy reflection instance allowing access to the packet registry.
|
||||
* @return Reflected packet registry.
|
||||
*/
|
||||
private FuzzyReflection getPacketRegistry() {
|
||||
if (packetRegistry == null)
|
||||
packetRegistry = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true);
|
||||
return packetRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the injected proxy classes handlig each packet ID.
|
||||
* @return Injected classes.
|
||||
*/
|
||||
public Map<Integer, Class> getOverwrittenPackets() {
|
||||
return overwrittenPackets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the vanilla classes handling each packet ID.
|
||||
* @return Vanilla classes.
|
||||
*/
|
||||
public Map<Integer, Class> getPreviousPackets() {
|
||||
return previousValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet.
|
||||
* @return An immutable set of every known server packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
||||
*/
|
||||
public Set<Integer> getServerPackets() throws FieldAccessException {
|
||||
initializeSets();
|
||||
|
||||
// Sanity check. This is impossible!
|
||||
if (serverPackets != null && serverPackets.size() < MIN_SERVER_PACKETS)
|
||||
throw new FieldAccessException("Server packet list is empty. Seems to be unsupported");
|
||||
return serverPackets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported client packet.
|
||||
* @return An immutable set of every known client packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
||||
*/
|
||||
public Set<Integer> getClientPackets() throws FieldAccessException {
|
||||
initializeSets();
|
||||
|
||||
// As above
|
||||
if (clientPackets != null && clientPackets.size() < MIN_CLIENT_PACKETS)
|
||||
throw new FieldAccessException("Client packet list is empty. Seems to be unsupported");
|
||||
return clientPackets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* @param packetID - the packet ID.
|
||||
* @return The associated class.
|
||||
*/
|
||||
public Class getPacketClassFromID(int packetID) {
|
||||
return getPacketClassFromID(packetID, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* @param packetID - the packet ID.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @return The associated class.
|
||||
*/
|
||||
public Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||
Map<Integer, Class> lookup = forceVanilla ? previousValues : overwrittenPackets;
|
||||
Class<?> result = null;
|
||||
|
||||
// Optimized lookup
|
||||
if (lookup.containsKey(packetID)) {
|
||||
return removeEnhancer(lookup.get(packetID), forceVanilla);
|
||||
}
|
||||
|
||||
// Refresh lookup tables
|
||||
getPacketToID();
|
||||
|
||||
// See if we can look for non-vanilla classes
|
||||
if (!forceVanilla) {
|
||||
result = Iterables.getFirst(customIdToPacket.get(packetID), null);
|
||||
}
|
||||
if (result == null) {
|
||||
result = vanillaIdToPacket.get(packetID);
|
||||
}
|
||||
|
||||
// See if we got it
|
||||
if (result != null)
|
||||
return result;
|
||||
else
|
||||
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 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.
|
||||
* @param remove - whether or not to skip enhanced (proxy) classes.
|
||||
* @return If remove is TRUE, the first superclass that is not a proxy.
|
||||
*/
|
||||
private static Class removeEnhancer(Class clazz, boolean remove) {
|
||||
if (remove) {
|
||||
// Get the underlying vanilla class
|
||||
while (Factory.class.isAssignableFrom(clazz) && !clazz.equals(Object.class)) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Occurs when we were unable to retrieve all the packets in the registry.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class InsufficientPacketsException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final boolean client;
|
||||
private final int packetCount;
|
||||
|
||||
private InsufficientPacketsException(String message, boolean client, int packetCount) {
|
||||
super(message);
|
||||
this.client = client;
|
||||
this.packetCount = packetCount;
|
||||
}
|
||||
|
||||
public boolean isClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public int getPacketCount() {
|
||||
return packetCount;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* A builder responsible for creating incoming packet injectors.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PacketInjectorBuilder {
|
||||
protected PacketInjectorBuilder() {
|
||||
// No need to construct this
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a new packet injector builder.
|
||||
* @return Injector builder.
|
||||
*/
|
||||
public static PacketInjectorBuilder newBuilder() {
|
||||
return new PacketInjectorBuilder();
|
||||
}
|
||||
|
||||
protected ListenerInvoker invoker;
|
||||
protected ErrorReporter reporter;
|
||||
protected PlayerInjectionHandler playerInjection;
|
||||
|
||||
/**
|
||||
* The error reporter used by the created injector.
|
||||
* @param reporter - new error reporter.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketInjectorBuilder reporter(@Nonnull ErrorReporter reporter) {
|
||||
Preconditions.checkNotNull(reporter, "reporter cannot be NULL");
|
||||
this.reporter = reporter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The packet stream invoker.
|
||||
* @param invoker - the invoker.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PacketInjectorBuilder invoker(@Nonnull ListenerInvoker invoker) {
|
||||
Preconditions.checkNotNull(invoker, "invoker cannot be NULL");
|
||||
this.invoker = invoker;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the player injection.
|
||||
* @param playerInjection - the injection.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Nonnull
|
||||
public PacketInjectorBuilder playerInjection(@Nonnull PlayerInjectionHandler playerInjection) {
|
||||
Preconditions.checkNotNull(playerInjection, "playerInjection cannot be NULL");
|
||||
this.playerInjection = playerInjection;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before an object is created with this builder.
|
||||
*/
|
||||
private void initializeDefaults() {
|
||||
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
// Initialize with default values if we can
|
||||
if (reporter == null)
|
||||
reporter = ProtocolLibrary.getErrorReporter();
|
||||
if (invoker == null)
|
||||
invoker = (PacketFilterManager) manager;
|
||||
if (playerInjection == null)
|
||||
throw new IllegalStateException("Player injection parameter must be initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a packet injector using the provided fields or the default values.
|
||||
* <p>
|
||||
* Note that any non-null builder parameters must be set.
|
||||
* @return The created injector.
|
||||
* @throws FieldAccessException If anything goes wrong in terms of reflection.
|
||||
*/
|
||||
public PacketInjector buildInjector() throws FieldAccessException {
|
||||
initializeDefaults();
|
||||
return new ProxyPacketInjector(invoker, playerInjection, reporter);
|
||||
}
|
||||
}
|
@ -17,22 +17,16 @@
|
||||
|
||||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
|
||||
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Static packet registry in Minecraft.
|
||||
@ -40,19 +34,9 @@ import com.google.common.collect.Sets;
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class PacketRegistry {
|
||||
public static final ReportType REPORT_CANNOT_CORRECT_TROVE_MAP = new ReportType("Unable to correct no entry value.");
|
||||
|
||||
public static final ReportType REPORT_INSUFFICIENT_SERVER_PACKETS = new ReportType("Too few server packets detected: %s");
|
||||
public static final ReportType REPORT_INSUFFICIENT_CLIENT_PACKETS = new ReportType("Too few client packets detected: %s");
|
||||
|
||||
// The Netty packet registry
|
||||
private static volatile ProtocolRegistry NETTY;
|
||||
|
||||
// Cached for Netty
|
||||
private static volatile Set<Integer> LEGACY_SERVER_PACKETS;
|
||||
private static volatile Set<Integer> LEGACY_CLIENT_PACKETS;
|
||||
private static volatile Map<Integer, Class> LEGACY_PREVIOUS_PACKETS;
|
||||
|
||||
// Whether or not the registry has been initialized
|
||||
private static volatile boolean INITIALIZED = false;
|
||||
|
||||
@ -93,13 +77,7 @@ public class PacketRegistry {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Class, Integer> result = (Map) Maps.transformValues(
|
||||
NETTY.getPacketClassLookup(),
|
||||
new Function<PacketType, Integer>() {
|
||||
@Override
|
||||
public Integer apply(PacketType type) {
|
||||
return type.getLegacyId();
|
||||
};
|
||||
});
|
||||
NETTY.getPacketClassLookup(), type -> type.getLegacyId());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -114,55 +92,7 @@ public class PacketRegistry {
|
||||
Map<Class, PacketType> result = (Map) NETTY.getPacketClassLookup();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the injected proxy classes handlig each packet ID.
|
||||
* <p>
|
||||
* This is not supported in 1.7.2 and later.
|
||||
* @return Injected classes.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<Integer, Class> getOverwrittenPackets() {
|
||||
initialize();
|
||||
throw new IllegalStateException("Not supported on Netty.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the vanilla classes handling each packet ID.
|
||||
* @return Vanilla classes.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Map<Integer, Class> getPreviousPackets() {
|
||||
initialize();
|
||||
|
||||
// Construct it first
|
||||
if (LEGACY_PREVIOUS_PACKETS == null) {
|
||||
Map<Integer, Class> map = Maps.newHashMap();
|
||||
|
||||
for (Entry<PacketType, Class<?>> entry : NETTY.getPacketTypeLookup().entrySet()) {
|
||||
map.put(entry.getKey().getLegacyId(), entry.getValue());
|
||||
}
|
||||
LEGACY_PREVIOUS_PACKETS = Collections.unmodifiableMap(map);
|
||||
}
|
||||
return LEGACY_PREVIOUS_PACKETS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getServerPacketTypes()} instead.
|
||||
* @return An immutable set of every known server packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Set<Integer> getServerPackets() throws FieldAccessException {
|
||||
if (LEGACY_SERVER_PACKETS == null) {
|
||||
LEGACY_SERVER_PACKETS = toLegacy(getServerPacketTypes());
|
||||
}
|
||||
|
||||
return LEGACY_SERVER_PACKETS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet type.
|
||||
* @return Every server packet type.
|
||||
@ -174,22 +104,6 @@ public class PacketRegistry {
|
||||
return NETTY.getServerPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported client packet.
|
||||
* <p>
|
||||
* Deprecated: Use {@link #getClientPacketTypes()} instead.
|
||||
* @return An immutable set of every known client packet.
|
||||
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Set<Integer> getClientPackets() throws FieldAccessException {
|
||||
if (LEGACY_CLIENT_PACKETS == null) {
|
||||
LEGACY_CLIENT_PACKETS = toLegacy(getClientPacketTypes());
|
||||
}
|
||||
|
||||
return LEGACY_CLIENT_PACKETS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet type.
|
||||
* @return Every server packet type.
|
||||
@ -200,43 +114,7 @@ public class PacketRegistry {
|
||||
|
||||
return NETTY.getClientPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a set of packet types to a set of integers based on the legacy packet ID.
|
||||
* @param types - packet type.
|
||||
* @return Set of integers.
|
||||
*/
|
||||
public static Set<Integer> toLegacy(Set<PacketType> types) {
|
||||
Set<Integer> result = Sets.newHashSet();
|
||||
|
||||
for (PacketType type : types)
|
||||
result.add(type.getLegacyId());
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a set of legacy packet IDs to packet types.
|
||||
* @param ids - legacy packet IDs.
|
||||
* @return Set of packet types.
|
||||
*/
|
||||
public static Set<PacketType> toPacketTypes(Set<Integer> ids) {
|
||||
return toPacketTypes(ids, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a set of legacy packet IDs to packet types.
|
||||
* @param ids - legacy packet IDs.
|
||||
* @param preference - the sender preference, if any.
|
||||
* @return Set of packet types.
|
||||
*/
|
||||
public static Set<PacketType> toPacketTypes(Set<Integer> ids, Sender preference) {
|
||||
Set<PacketType> result = Sets.newHashSet();
|
||||
|
||||
for (int id : ids)
|
||||
result.add(PacketType.fromLegacy(id, preference));
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the correct packet class from a given packet ID.
|
||||
* <p>
|
||||
|
@ -1,372 +0,0 @@
|
||||
/*
|
||||
* 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.packet;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
import net.sf.cglib.proxy.NoOp;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.MethodInfo;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedIntHashMap;
|
||||
|
||||
/**
|
||||
* This class is responsible for adding or removing proxy objects that intercepts received packets.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class ProxyPacketInjector implements PacketInjector {
|
||||
public static final ReportType REPORT_CANNOT_FIND_READ_PACKET_METHOD = new ReportType("Cannot find read packet method for ID %s.");
|
||||
public static final ReportType REPORT_UNKNOWN_ORIGIN_FOR_PACKET = new ReportType("Timeout: Unknown origin %s for packet %s. Are you using GamePhase.LOGIN?");
|
||||
|
||||
/**
|
||||
* Represents a way to update the packet ID to class lookup table.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static interface PacketClassLookup {
|
||||
public void setLookup(int packetID, Class<?> clazz);
|
||||
}
|
||||
|
||||
private static class IntHashMapLookup implements PacketClassLookup {
|
||||
private WrappedIntHashMap intHashMap;
|
||||
|
||||
public IntHashMapLookup() throws IllegalAccessException {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLookup(int packetID, Class<?> clazz) {
|
||||
intHashMap.put(packetID, clazz);
|
||||
}
|
||||
|
||||
private void initialize() throws IllegalAccessException {
|
||||
if (intHashMap == null) {
|
||||
// We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
|
||||
Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
|
||||
getFieldByType("packetIdMap", MinecraftReflection.getIntHashMapClass());
|
||||
|
||||
try {
|
||||
intHashMap = WrappedIntHashMap.fromHandle(
|
||||
FieldUtils.readField(intHashMapField, (Object) null, true));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Minecraft is incompatible.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArrayLookup implements PacketClassLookup {
|
||||
private Class<?>[] array;
|
||||
|
||||
public ArrayLookup() throws IllegalAccessException {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLookup(int packetID, Class<?> clazz) {
|
||||
array[packetID] = clazz;
|
||||
}
|
||||
|
||||
private void initialize() throws IllegalAccessException {
|
||||
FuzzyReflection reflection = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass());
|
||||
|
||||
// Is there a Class array with 256 elements instead?
|
||||
for (Field field : reflection.getFieldListByType(Class[].class)) {
|
||||
Class<?>[] test = (Class<?>[]) FieldUtils.readField(field, (Object)null);
|
||||
|
||||
if (test.length == 256) {
|
||||
array = test;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to find an array with the type " + Class[].class +
|
||||
" in " + MinecraftReflection.getPacketClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the readPacketData(DataInputStream) method in Packet.
|
||||
*/
|
||||
private static FuzzyMethodContract READ_PACKET = FuzzyMethodContract.newBuilder().
|
||||
returnTypeVoid().
|
||||
parameterDerivedOf(DataInput.class).
|
||||
parameterCount(1).
|
||||
build();
|
||||
|
||||
private static PacketClassLookup lookup;
|
||||
|
||||
// The packet filter manager
|
||||
private ListenerInvoker manager;
|
||||
|
||||
// Error reporter
|
||||
private ErrorReporter reporter;
|
||||
|
||||
// Allows us to determine the sender
|
||||
private PlayerInjectionHandler playerInjection;
|
||||
|
||||
// Share callback filter
|
||||
private CallbackFilter filter;
|
||||
|
||||
// Determine if the read packet method was found
|
||||
private boolean readPacketIntercepted = false;
|
||||
|
||||
public ProxyPacketInjector(ListenerInvoker manager, PlayerInjectionHandler playerInjection,
|
||||
ErrorReporter reporter) throws FieldAccessException {
|
||||
|
||||
this.manager = manager;
|
||||
this.playerInjection = playerInjection;
|
||||
this.reporter = reporter;
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled(Object packet) {
|
||||
return ReadPacketModifier.isCancelled(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(Object packet, boolean cancelled) {
|
||||
if (cancelled) {
|
||||
ReadPacketModifier.setOverride(packet, null);
|
||||
} else {
|
||||
ReadPacketModifier.removeOverride(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void initialize() throws FieldAccessException {
|
||||
if (lookup == null) {
|
||||
try {
|
||||
lookup = new IntHashMapLookup();
|
||||
} catch (Exception e1) {
|
||||
|
||||
try {
|
||||
lookup = new ArrayLookup();
|
||||
} catch (Exception e2) {
|
||||
// Wow
|
||||
throw new FieldAccessException(e1.getMessage() + ". Workaround failed too.", e2);
|
||||
}
|
||||
}
|
||||
|
||||
// Should work fine now
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputBuffersChanged(Set<PacketType> set) {
|
||||
// No need to do anything
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "deprecation"})
|
||||
public boolean addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
final int packetID = type.getLegacyId();
|
||||
|
||||
if (hasPacketHandler(type))
|
||||
return false;
|
||||
|
||||
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
|
||||
|
||||
// Unfortunately, we can't easily distinguish between these two functions:
|
||||
// * Object lookup(int par1)
|
||||
// * Object removeObject(int par1)
|
||||
|
||||
// So, we'll use the classMapToInt registry instead.
|
||||
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
|
||||
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
|
||||
Map<Class, Integer> registry = PacketRegistry.getPacketToID();
|
||||
Class old = PacketRegistry.getPacketClassFromType(type);
|
||||
|
||||
// If this packet is not known
|
||||
if (old == null) {
|
||||
throw new IllegalStateException("Packet ID " + type + " is not a valid packet type in this version.");
|
||||
}
|
||||
// Check for previous injections
|
||||
if (Factory.class.isAssignableFrom(old)) {
|
||||
throw new IllegalStateException("Packet " + type + " has already been injected.");
|
||||
}
|
||||
|
||||
if (filter == null) {
|
||||
readPacketIntercepted = false;
|
||||
|
||||
filter = new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
// Skip methods defined in Object
|
||||
if (method.getDeclaringClass().equals(Object.class)) {
|
||||
return 0;
|
||||
} else if (READ_PACKET.isMatch(MethodInfo.fromMethod(method), null)) {
|
||||
readPacketIntercepted = true;
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Subclass the specific packet class
|
||||
ex.setSuperclass(old);
|
||||
ex.setCallbackFilter(filter);
|
||||
ex.setCallbackTypes(new Class<?>[] { NoOp.class, ReadPacketModifier.class, ReadPacketModifier.class });
|
||||
Class proxy = ex.createClass();
|
||||
|
||||
// Create the proxy handlers
|
||||
ReadPacketModifier modifierReadPacket = new ReadPacketModifier(packetID, this, reporter, true);
|
||||
ReadPacketModifier modifierRest = new ReadPacketModifier(packetID, this, reporter, false);
|
||||
|
||||
// Add a static reference
|
||||
Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest });
|
||||
|
||||
// Check that we found the read method
|
||||
if (!readPacketIntercepted) {
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_CANNOT_FIND_READ_PACKET_METHOD).messageParam(packetID));
|
||||
}
|
||||
|
||||
// Override values
|
||||
previous.put(packetID, old);
|
||||
registry.put(proxy, packetID);
|
||||
overwritten.put(packetID, proxy);
|
||||
lookup.setLookup(packetID, proxy);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "deprecation"})
|
||||
public boolean removePacketHandler(PacketType type) {
|
||||
final int packetID = type.getLegacyId();
|
||||
|
||||
if (!hasPacketHandler(type))
|
||||
return false;
|
||||
|
||||
Map<Class, Integer> registry = PacketRegistry.getPacketToID();
|
||||
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
|
||||
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
|
||||
|
||||
Class old = previous.get(packetID);
|
||||
Class proxy = PacketRegistry.getPacketClassFromType(type);
|
||||
|
||||
lookup.setLookup(packetID, old);
|
||||
previous.remove(packetID);
|
||||
registry.remove(proxy);
|
||||
overwritten.remove(packetID);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the data a packet read must be buffered.
|
||||
* @param packetId - the packet to check.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean requireInputBuffers(int packetId) {
|
||||
return manager.requireInputBuffer(packetId);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean hasPacketHandler(PacketType type) {
|
||||
return PacketRegistry.getPreviousPackets().containsKey(type.getLegacyId());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Set<PacketType> getPacketHandlers() {
|
||||
return PacketRegistry.toPacketTypes(PacketRegistry.getPreviousPackets().keySet(), Sender.CLIENT);
|
||||
}
|
||||
|
||||
// Called from the ReadPacketModified monitor
|
||||
public PacketEvent packetRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
|
||||
if (playerInjection.canRecievePackets()) {
|
||||
return playerInjection.handlePacketRecieved(packet, input, buffered);
|
||||
}
|
||||
|
||||
try {
|
||||
Player client = playerInjection.getPlayerByConnection((DataInputStream) input);
|
||||
|
||||
// Never invoke a event if we don't know where it's from
|
||||
if (client != null) {
|
||||
return packetRecieved(packet, client, buffered);
|
||||
} else {
|
||||
// The timeout elapsed!
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_UNKNOWN_ORIGIN_FOR_PACKET).messageParam(input, packet.getType()));
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
// We will ignore this - it occurs when a player disconnects
|
||||
//reporter.reportDetailed(this, "Thread was interrupted.", e, packet, input);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||
NetworkMarker marker = buffered != null ? new LegacyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered, packet.getType()) : null;
|
||||
PacketEvent event = PacketEvent.fromClient(manager, packet, marker, client);
|
||||
|
||||
manager.invokePacketRecieving(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "deprecation"})
|
||||
public synchronized void cleanupAll() {
|
||||
Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
|
||||
Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
|
||||
|
||||
// Remove every packet handler
|
||||
for (Integer id : previous.keySet().toArray(new Integer[0])) {
|
||||
removePacketHandler(PacketType.findLegacy(id, Sender.CLIENT));
|
||||
removePacketHandler(PacketType.findLegacy(id, Sender.SERVER));
|
||||
}
|
||||
|
||||
overwritten.clear();
|
||||
previous.clear();
|
||||
}
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
/*
|
||||
* 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.packet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.NetworkProcessor;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
class ReadPacketModifier implements MethodInterceptor {
|
||||
public static final ReportType REPORT_CANNOT_HANDLE_CLIENT_PACKET = new ReportType("Cannot handle client packet.");
|
||||
|
||||
// A cancel marker
|
||||
private static final Object CANCEL_MARKER = new Object();
|
||||
|
||||
// Common for all packets of the same type
|
||||
private ProxyPacketInjector packetInjector;
|
||||
private int packetID;
|
||||
|
||||
// Report errors
|
||||
private ErrorReporter reporter;
|
||||
private NetworkProcessor processor;
|
||||
|
||||
// If this is a read packet data method
|
||||
private boolean isReadPacketDataMethod;
|
||||
|
||||
// Whether or not a packet has been cancelled
|
||||
private static Map<Object, Object> override = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
public ReadPacketModifier(int packetID, ProxyPacketInjector packetInjector, ErrorReporter reporter, boolean isReadPacketDataMethod) {
|
||||
this.packetID = packetID;
|
||||
this.packetInjector = packetInjector;
|
||||
this.reporter = reporter;
|
||||
this.processor = new NetworkProcessor(reporter);
|
||||
this.isReadPacketDataMethod = isReadPacketDataMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any packet overrides.
|
||||
* @param packet - the packet to rever
|
||||
*/
|
||||
public static void removeOverride(Object packet) {
|
||||
override.remove(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet that overrides the methods of the given packet.
|
||||
* @param packet - the given packet.
|
||||
* @return Overriden object.
|
||||
*/
|
||||
public static Object getOverride(Object packet) {
|
||||
return override.get(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet instance to delegate to instead, or mark the packet as cancelled.
|
||||
* <p>
|
||||
* To undo a override, use {@link #removeOverride(Object)}.
|
||||
* @param packet - the packet.
|
||||
* @param override - the override method. NULL to cancel this packet.
|
||||
*/
|
||||
public static void setOverride(Object packet, Object overridePacket) {
|
||||
override.put(packet, overridePacket != null ? overridePacket : CANCEL_MARKER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet has been cancelled before.
|
||||
* @param packet - the packet to check.
|
||||
* @return TRUE if it has been cancelled, FALSE otherwise.
|
||||
*/
|
||||
public static boolean isCancelled(Object packet) {
|
||||
return getOverride(packet) == CANCEL_MARKER;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
// Atomic retrieval
|
||||
Object overridenObject = override.get(thisObj);
|
||||
Object returnValue = null;
|
||||
|
||||
// We need this in order to get the correct player
|
||||
InputStream input = isReadPacketDataMethod ? (InputStream) args[0] : null;
|
||||
ByteArrayOutputStream bufferStream = null;
|
||||
|
||||
// See if we need to buffer the read data
|
||||
if (isReadPacketDataMethod && packetInjector.requireInputBuffers(packetID)) {
|
||||
CaptureInputStream captured = new CaptureInputStream(
|
||||
input, bufferStream = new ByteArrayOutputStream());
|
||||
|
||||
// Swap it with our custom stream
|
||||
args[0] = new DataInputStream(captured);
|
||||
}
|
||||
|
||||
if (overridenObject != null) {
|
||||
// This packet has been cancelled
|
||||
if (overridenObject == CANCEL_MARKER) {
|
||||
// So, cancel all void methods
|
||||
if (method.getReturnType().equals(Void.TYPE))
|
||||
return null;
|
||||
else // Revert to normal for everything else
|
||||
overridenObject = thisObj;
|
||||
}
|
||||
|
||||
returnValue = proxy.invokeSuper(overridenObject, args);
|
||||
} else {
|
||||
returnValue = proxy.invokeSuper(thisObj, args);
|
||||
}
|
||||
|
||||
// Is this a readPacketData method?
|
||||
if (isReadPacketDataMethod) {
|
||||
// Swap back custom stream
|
||||
args[0] = input;
|
||||
|
||||
try {
|
||||
byte[] buffer = bufferStream != null ? bufferStream.toByteArray() : null;
|
||||
|
||||
// Let the people know
|
||||
PacketType type = PacketType.findLegacy(packetID, Sender.CLIENT);
|
||||
PacketContainer container = new PacketContainer(type, thisObj);
|
||||
PacketEvent event = packetInjector.packetRecieved(container, input, buffer);
|
||||
|
||||
// Handle override
|
||||
if (event != null) {
|
||||
Object result = event.getPacket().getHandle();
|
||||
|
||||
if (event.isCancelled()) {
|
||||
override.put(thisObj, CANCEL_MARKER);
|
||||
return returnValue;
|
||||
} else if (!objectEquals(thisObj, result)) {
|
||||
override.put(thisObj, result);
|
||||
}
|
||||
|
||||
// This is fine - received packets are enqueued in any case
|
||||
NetworkMarker marker = NetworkMarker.getNetworkMarker(event);
|
||||
processor.invokePostEvent(event, marker);
|
||||
}
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Minecraft cannot handle this error
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_HANDLE_CLIENT_PACKET).callerParam(args[0]).error(e)
|
||||
);
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private boolean objectEquals(Object a, Object b) {
|
||||
return System.identityHashCode(a) != System.identityHashCode(b);
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
/*
|
||||
* 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.packet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.NetworkProcessor;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
public class WritePacketModifier implements MethodInterceptor {
|
||||
public static final ReportType REPORT_CANNOT_WRITE_SERVER_PACKET = new ReportType("Cannot write server packet.");
|
||||
|
||||
private static class ProxyInformation {
|
||||
// Marker that contains custom writers
|
||||
public final Object proxyObject;
|
||||
public final PacketEvent event;
|
||||
public final NetworkMarker marker;
|
||||
|
||||
public ProxyInformation(Object proxyObject, PacketEvent event, NetworkMarker marker) {
|
||||
this.proxyObject = proxyObject;
|
||||
this.event = event;
|
||||
this.marker = marker;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Object, ProxyInformation> proxyLookup = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
// Report errors
|
||||
private final ErrorReporter reporter;
|
||||
private final NetworkProcessor processor;
|
||||
|
||||
// Whether or not this represents the write method
|
||||
private boolean isWriteMethod;
|
||||
|
||||
public WritePacketModifier(ErrorReporter reporter, boolean isWriteMethod) {
|
||||
this.reporter = reporter;
|
||||
this.processor = new NetworkProcessor(reporter);
|
||||
this.isWriteMethod = isWriteMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the given generated instance of a class and the given parameteters.
|
||||
* @param generatedClass - the generated class.
|
||||
* @param proxyObject - the object to call from the generated class.
|
||||
* @param event - the packet event.
|
||||
* @param marker - the network marker.
|
||||
*/
|
||||
public void register(Object generatedClass, Object proxyObject, PacketEvent event, NetworkMarker marker) {
|
||||
proxyLookup.put(generatedClass, new ProxyInformation(proxyObject, event, marker));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
ProxyInformation information = proxyLookup.get(thisObj);
|
||||
|
||||
if (information == null) {
|
||||
// This is really bad - someone forgot to register the proxy
|
||||
throw new RuntimeException("Cannot find proxy information for " + thisObj);
|
||||
}
|
||||
|
||||
if (isWriteMethod) {
|
||||
// If every output handler has been removed - ignore everything
|
||||
if (!information.marker.getOutputHandlers().isEmpty()) {
|
||||
try {
|
||||
DataOutput output = (DataOutput) args[0];
|
||||
|
||||
// First - we need the initial buffer
|
||||
ByteArrayOutputStream outputBufferStream = new ByteArrayOutputStream();
|
||||
proxy.invoke(information.proxyObject, new Object[] { new DataOutputStream(outputBufferStream) });
|
||||
|
||||
// Let each handler prepare the actual output
|
||||
byte[] outputBuffer = processor.processOutput(information.event, information.marker,
|
||||
outputBufferStream.toByteArray());
|
||||
|
||||
// Write that output to the network stream
|
||||
output.write(outputBuffer);
|
||||
|
||||
// We're done
|
||||
processor.invokePostEvent(information.event, information.marker);
|
||||
return null;
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Minecraft cannot handle this error
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_WRITE_SERVER_PACKET).callerParam(args[0]).error(e)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke this write method first
|
||||
proxy.invoke(information.proxyObject, args);
|
||||
processor.invokePostEvent(information.event, information.marker);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Default to the super method
|
||||
return proxy.invoke(information.proxyObject, args);
|
||||
}
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
/**
|
||||
* The array list that notifies when packets are sent by the server.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class InjectedArrayList extends ArrayList<Object> {
|
||||
public static final ReportType REPORT_CANNOT_REVERT_CANCELLED_PACKET = new ReportType("Reverting cancelled packet failed.");
|
||||
|
||||
/**
|
||||
* Silly Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = -1173865905404280990L;
|
||||
|
||||
// Fake inverted proxy objects
|
||||
private static ConcurrentMap<Object, Object> delegateLookup = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private transient PlayerInjector injector;
|
||||
private transient Set<Object> ignoredPackets;
|
||||
|
||||
private transient InvertedIntegerCallback callback;
|
||||
|
||||
public InjectedArrayList(PlayerInjector injector, Set<Object> ignoredPackets) {
|
||||
this.injector = injector;
|
||||
this.ignoredPackets = ignoredPackets;
|
||||
this.callback = new InvertedIntegerCallback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Object packet) {
|
||||
|
||||
Object result = null;
|
||||
|
||||
// Check for fake packets and ignored packets
|
||||
if (packet instanceof FakePacket) {
|
||||
return true;
|
||||
} else if (ignoredPackets.contains(packet)) {
|
||||
// Don't send it to the filters
|
||||
result = ignoredPackets.remove(packet);
|
||||
} else {
|
||||
result = injector.handlePacketSending(packet);
|
||||
}
|
||||
|
||||
// A NULL packet indicate cancelling
|
||||
try {
|
||||
if (result != null) {
|
||||
super.add(result);
|
||||
} else {
|
||||
// We'll use the FakePacket marker instead of preventing the filters
|
||||
injector.sendServerPacket(createNegativePacket(packet), null, true);
|
||||
}
|
||||
|
||||
// Collection.add contract
|
||||
return true;
|
||||
|
||||
} catch (InvocationTargetException e) {
|
||||
// Prefer to report this to the user, instead of risking sending it to Minecraft
|
||||
ProtocolLibrary.getErrorReporter().reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_REVERT_CANCELLED_PACKET).error(e).callerParam(packet)
|
||||
);
|
||||
|
||||
// Failure
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by a hack that reverses the effect of a cancelled packet. Returns a packet
|
||||
* whereby every int method's return value is inverted (a => -a).
|
||||
*
|
||||
* @param source - packet to invert.
|
||||
* @return The inverted packet.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
Object createNegativePacket(Object source) {
|
||||
ListenerInvoker invoker = injector.getInvoker();
|
||||
|
||||
PacketType type = invoker.getPacketType(source);
|
||||
|
||||
// We want to subtract the byte amount that were added to the running
|
||||
// total of outstanding packets. Otherwise, cancelling too many packets
|
||||
// might cause a "disconnect.overflow" error.
|
||||
//
|
||||
// We do that by constructing a special packet of the same type that returns
|
||||
// a negative integer for all zero-parameter integer methods. This includes the
|
||||
// size() method, which is used by the queue method to count the number of
|
||||
// bytes to add.
|
||||
//
|
||||
// Essentially, we have:
|
||||
//
|
||||
// public class NegativePacket extends [a packet] {
|
||||
// @Override
|
||||
// public int size() {
|
||||
// return -super.size();
|
||||
// }
|
||||
// ect.
|
||||
// }
|
||||
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
|
||||
ex.setSuperclass(MinecraftReflection.getPacketClass());
|
||||
ex.setInterfaces(new Class[] { FakePacket.class } );
|
||||
ex.setUseCache(true);
|
||||
ex.setCallbackType(InvertedIntegerCallback.class);
|
||||
|
||||
Class<?> proxyClass = ex.createClass();
|
||||
Enhancer.registerCallbacks(proxyClass, new Callback[] { callback });
|
||||
|
||||
try {
|
||||
// Temporarily associate the fake packet class
|
||||
invoker.registerPacketClass(proxyClass, type.getLegacyId());
|
||||
Object proxy = proxyClass.newInstance();
|
||||
|
||||
InjectedArrayList.registerDelegate(proxy, source);
|
||||
return proxy;
|
||||
|
||||
} catch (Exception e) {
|
||||
// Don't pollute the throws tree
|
||||
throw new RuntimeException("Cannot create fake class.", e);
|
||||
} finally {
|
||||
// Remove this association
|
||||
invoker.unregisterPacketClass(proxyClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the inverted integer proxy uses the given object as source.
|
||||
* @param proxy - inverted integer proxy.
|
||||
* @param source - source object.
|
||||
*/
|
||||
private static void registerDelegate(Object proxy, Object source) {
|
||||
delegateLookup.put(proxy, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts the integer result of every integer method.
|
||||
* @author Kristian
|
||||
*/
|
||||
private class InvertedIntegerCallback implements MethodInterceptor {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
final Object delegate = delegateLookup.get(obj);
|
||||
|
||||
if (delegate == null) {
|
||||
throw new IllegalStateException("Unable to find delegate source for " + obj);
|
||||
}
|
||||
|
||||
if (method.getReturnType().equals(int.class) && args.length == 0) {
|
||||
Integer result = (Integer) proxy.invoke(delegate, args);
|
||||
return -result;
|
||||
} else {
|
||||
return proxy.invoke(delegate, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,442 +0,0 @@
|
||||
/*
|
||||
* 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.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.Server;
|
||||
|
||||
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;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
/**
|
||||
* Used to ensure that the 1.3 server is referencing the correct server handler.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class InjectedServerConnection {
|
||||
// A number of things can go wrong ...
|
||||
public static final ReportType REPORT_CANNOT_FIND_MINECRAFT_SERVER = new ReportType("Cannot extract minecraft server from Bukkit.");
|
||||
public static final ReportType REPORT_CANNOT_INJECT_SERVER_CONNECTION = new ReportType("Cannot inject into server connection. Bad things will happen.");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_FIND_LISTENER_THREAD = new ReportType("Cannot find listener thread in MinecraftServer.");
|
||||
public static final ReportType REPORT_CANNOT_READ_LISTENER_THREAD = new ReportType("Unable to read the listener thread.");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_FIND_SERVER_CONNECTION = new ReportType("Unable to retrieve server connection");
|
||||
public static final ReportType REPORT_UNEXPECTED_THREAD_COUNT = new ReportType("Unexpected number of threads in %s: %s");
|
||||
public static final ReportType REPORT_CANNOT_FIND_NET_HANDLER_THREAD = new ReportType("Unable to retrieve net handler thread.");
|
||||
public static final ReportType REPORT_INSUFFICENT_THREAD_COUNT = new ReportType("Unable to inject %s lists in %s.");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_COPY_OLD_TO_NEW = new ReportType("Cannot copy old %s to new.");
|
||||
|
||||
private static Field listenerThreadField;
|
||||
private static Field minecraftServerField;
|
||||
private static Field listField;
|
||||
private static Field dedicatedThreadField;
|
||||
|
||||
private static Method serverConnectionMethod;
|
||||
|
||||
private List<VolatileField> listFields;
|
||||
private List<ReplacedArrayList<Object>> replacedLists;
|
||||
|
||||
// The current detected server socket
|
||||
public enum ServerSocketType {
|
||||
SERVER_CONNECTION,
|
||||
LISTENER_THREAD,
|
||||
}
|
||||
|
||||
// Used to inject net handlers
|
||||
private NetLoginInjector netLoginInjector;
|
||||
|
||||
// Inject server connections
|
||||
private AbstractInputStreamLookup socketInjector;
|
||||
|
||||
// Detected by the initializer
|
||||
private ServerSocketType socketType;
|
||||
|
||||
private Server server;
|
||||
private ErrorReporter reporter;
|
||||
private boolean hasAttempted;
|
||||
private boolean hasSuccess;
|
||||
|
||||
private Object minecraftServer = null;
|
||||
|
||||
public InjectedServerConnection(ErrorReporter reporter, AbstractInputStreamLookup socketInjector, Server server, NetLoginInjector netLoginInjector) {
|
||||
this.listFields = new ArrayList<VolatileField>();
|
||||
this.replacedLists = new ArrayList<ReplacedArrayList<Object>>();
|
||||
this.reporter = reporter;
|
||||
this.server = server;
|
||||
this.socketInjector = socketInjector;
|
||||
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.
|
||||
*/
|
||||
public void initialize() {
|
||||
// Only execute this method once
|
||||
if (!hasAttempted)
|
||||
hasAttempted = true;
|
||||
else
|
||||
return;
|
||||
|
||||
if (minecraftServerField == null)
|
||||
minecraftServerField = FuzzyReflection.fromObject(server, true).
|
||||
getFieldByType("MinecraftServer", MinecraftReflection.getMinecraftServerClass());
|
||||
|
||||
try {
|
||||
minecraftServer = FieldUtils.readField(minecraftServerField, server, true);
|
||||
} catch (IllegalAccessException e1) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_FIND_MINECRAFT_SERVER));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (serverConnectionMethod == null)
|
||||
serverConnectionMethod = FuzzyReflection.fromClass(minecraftServerField.getType()).
|
||||
getMethodByParameters("getServerConnection",
|
||||
MinecraftReflection.getServerConnectionClass(), new Class[] {});
|
||||
// We're using Minecraft 1.3.1
|
||||
socketType = ServerSocketType.SERVER_CONNECTION;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Minecraft 1.2.5 or lower
|
||||
socketType = ServerSocketType.LISTENER_THREAD;
|
||||
|
||||
} catch (Exception e) {
|
||||
// Oh damn - inform the player
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_INJECT_SERVER_CONNECTION).error(e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the known server socket type.
|
||||
* <p>
|
||||
* This depends on the version of CraftBukkit we are using.
|
||||
* @return The server socket type.
|
||||
*/
|
||||
public ServerSocketType getServerSocketType() {
|
||||
return socketType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the connection interceptor into the correct server socket implementation.
|
||||
*/
|
||||
public void injectList() {
|
||||
initialize();
|
||||
|
||||
if (socketType == ServerSocketType.SERVER_CONNECTION) {
|
||||
injectServerConnection();
|
||||
} else if (socketType == ServerSocketType.LISTENER_THREAD) {
|
||||
injectListenerThread();
|
||||
} else {
|
||||
// Damn it
|
||||
throw new IllegalStateException("Unable to detected server connection.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the listener thread field.
|
||||
*/
|
||||
private void initializeListenerField() {
|
||||
if (listenerThreadField == null)
|
||||
listenerThreadField = FuzzyReflection.fromObject(minecraftServer).
|
||||
getFieldByType("networkListenThread", MinecraftReflection.getNetworkListenThreadClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the listener thread object, or NULL the server isn't using this socket implementation.
|
||||
* @return The listener thread, or NULL.
|
||||
* @throws IllegalAccessException Cannot access field.
|
||||
* @throws RuntimeException Unexpected class structure - the field doesn't exist.
|
||||
*/
|
||||
public Object getListenerThread() throws RuntimeException, IllegalAccessException {
|
||||
initialize();
|
||||
|
||||
if (socketType == ServerSocketType.LISTENER_THREAD) {
|
||||
initializeListenerField();
|
||||
return listenerThreadField.get(minecraftServer);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the server connection object, or NULL if the server isn't using it as the socket implementation.
|
||||
* @return The socket connection, or NULL.
|
||||
* @throws IllegalAccessException If the reflective operation failed.
|
||||
* @throws IllegalArgumentException If the reflective operation failed.
|
||||
* @throws InvocationTargetException If the reflective operation failed.
|
||||
*/
|
||||
public Object getServerConnection() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
initialize();
|
||||
|
||||
if (socketType == ServerSocketType.SERVER_CONNECTION)
|
||||
return serverConnectionMethod.invoke(minecraftServer);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private void injectListenerThread() {
|
||||
try {
|
||||
initializeListenerField();
|
||||
} catch (RuntimeException e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_FIND_LISTENER_THREAD).callerParam(minecraftServer).error(e)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Object listenerThread = null;
|
||||
|
||||
// Attempt to get the thread
|
||||
try {
|
||||
listenerThread = getListenerThread();
|
||||
} catch (Exception e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_READ_LISTENER_THREAD).error(e));
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject the server socket too
|
||||
injectServerSocket(listenerThread);
|
||||
|
||||
// Just inject every list field we can get
|
||||
injectEveryListField(listenerThread, 1);
|
||||
hasSuccess = true;
|
||||
}
|
||||
|
||||
private void injectServerConnection() {
|
||||
Object serverConnection = null;
|
||||
|
||||
// Careful - we might fail
|
||||
try {
|
||||
serverConnection = getServerConnection();
|
||||
} catch (Exception e) {
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_FIND_SERVER_CONNECTION).callerParam(minecraftServer).error(e)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (listField == null)
|
||||
listField = FuzzyReflection.fromClass(serverConnectionMethod.getReturnType(), true).
|
||||
getFieldByType("netServerHandlerList", List.class);
|
||||
if (dedicatedThreadField == null) {
|
||||
List<Field> matches = FuzzyReflection.fromObject(serverConnection, true).
|
||||
getFieldListByType(Thread.class);
|
||||
|
||||
// Verify the field count
|
||||
if (matches.size() != 1)
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_UNEXPECTED_THREAD_COUNT).messageParam(serverConnection.getClass(), matches.size())
|
||||
);
|
||||
else
|
||||
dedicatedThreadField = matches.get(0);
|
||||
}
|
||||
|
||||
// Next, try to get the dedicated thread
|
||||
try {
|
||||
if (dedicatedThreadField != null) {
|
||||
Object dedicatedThread = FieldUtils.readField(dedicatedThreadField, serverConnection, true);
|
||||
|
||||
// Inject server socket and NetServerHandlers.
|
||||
injectServerSocket(dedicatedThread);
|
||||
injectEveryListField(dedicatedThread, 1);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_FIND_NET_HANDLER_THREAD).error(e));
|
||||
}
|
||||
|
||||
injectIntoList(serverConnection, listField);
|
||||
hasSuccess = true;
|
||||
}
|
||||
|
||||
private void injectServerSocket(Object container) {
|
||||
socketInjector.inject(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically inject into every List-compatible public or private field of the given object.
|
||||
* @param container - container object with the fields to inject.
|
||||
* @param minimum - the minimum number of fields we expect exists.
|
||||
*/
|
||||
private void injectEveryListField(Object container, int minimum) {
|
||||
// Ok, great. Get every list field
|
||||
List<Field> lists = FuzzyReflection.fromObject(container, true).getFieldListByType(List.class);
|
||||
|
||||
for (Field list : lists) {
|
||||
injectIntoList(container, list);
|
||||
}
|
||||
|
||||
// Warn about unexpected errors
|
||||
if (lists.size() < minimum) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_INSUFFICENT_THREAD_COUNT).messageParam(minimum, container.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void injectIntoList(Object instance, Field field) {
|
||||
VolatileField listFieldRef = new VolatileField(field, instance, true);
|
||||
List<Object> list = (List<Object>) listFieldRef.getValue();
|
||||
|
||||
// Careful not to inject twice
|
||||
if (list instanceof ReplacedArrayList) {
|
||||
replacedLists.add((ReplacedArrayList<Object>) list);
|
||||
} else {
|
||||
ReplacedArrayList<Object> injectedList = createReplacement(list);
|
||||
|
||||
replacedLists.add(injectedList);
|
||||
listFieldRef.setValue(injectedList);
|
||||
listFields.add(listFieldRef);
|
||||
}
|
||||
}
|
||||
|
||||
// Hack to avoid the "moved to quickly" error
|
||||
private ReplacedArrayList<Object> createReplacement(List<Object> list) {
|
||||
return new ReplacedArrayList<Object>(list) {
|
||||
/**
|
||||
* 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) {
|
||||
// Is this a normal Minecraft object?
|
||||
if (!(inserting instanceof Factory)) {
|
||||
// If so, copy the content of the old element to the new
|
||||
try {
|
||||
writer.copyTo(inserting, replacement, inserting.getClass());
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(InjectedServerConnection.this,
|
||||
Report.newBuilder(REPORT_CANNOT_COPY_OLD_TO_NEW).messageParam(inserting).callerParam(inserting, replacement).error(e)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInserting(Object inserting) {
|
||||
// Ready for some login handler injection?
|
||||
if (MinecraftReflection.isLoginHandler(inserting)) {
|
||||
Object replaced = netLoginInjector.onNetLoginCreated(inserting);
|
||||
|
||||
// Only replace if it has changed
|
||||
if (inserting != replaced)
|
||||
addMapping(inserting, replaced, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemoved(Object removing) {
|
||||
// Clean up?
|
||||
if (MinecraftReflection.isLoginHandler(removing)) {
|
||||
netLoginInjector.cleanup(removing);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the server handler instance kept by the "keep alive" object.
|
||||
* @param oldHandler - old server handler.
|
||||
* @param newHandler - new, proxied server handler.
|
||||
*/
|
||||
public void replaceServerHandler(Object oldHandler, Object newHandler) {
|
||||
if (!hasAttempted) {
|
||||
injectList();
|
||||
}
|
||||
|
||||
if (hasSuccess) {
|
||||
for (ReplacedArrayList<Object> replacedList : replacedLists) {
|
||||
replacedList.addMapping(oldHandler, newHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert to the old vanilla server handler, if it has been replaced.
|
||||
* @param oldHandler - old vanilla server handler.
|
||||
*/
|
||||
public void revertServerHandler(Object oldHandler) {
|
||||
if (hasSuccess) {
|
||||
for (ReplacedArrayList<Object> replacedList : replacedLists) {
|
||||
replacedList.removeMapping(oldHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undoes everything.
|
||||
*/
|
||||
public void cleanupAll() {
|
||||
if (replacedLists.size() > 0) {
|
||||
// Repair the underlying lists
|
||||
for (ReplacedArrayList<Object> replacedList : replacedLists) {
|
||||
replacedList.revertAll();
|
||||
}
|
||||
for (VolatileField field : listFields) {
|
||||
field.revertValue();
|
||||
}
|
||||
|
||||
listFields.clear();
|
||||
replacedLists.clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Injects every NetLoginHandler created by the server.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class NetLoginInjector {
|
||||
public static final ReportType REPORT_CANNOT_HOOK_LOGIN_HANDLER = new ReportType("Unable to hook %s.");
|
||||
public static final ReportType REPORT_CANNOT_CLEANUP_LOGIN_HANDLER = new ReportType("Cannot cleanup %s.");
|
||||
|
||||
private ConcurrentMap<Object, PlayerInjector> injectedLogins = Maps.newConcurrentMap();
|
||||
|
||||
// Handles every hook
|
||||
private ProxyPlayerInjectionHandler injectionHandler;
|
||||
|
||||
// Create temporary players
|
||||
private TemporaryPlayerFactory playerFactory = new TemporaryPlayerFactory();
|
||||
|
||||
// The current error reporter
|
||||
private ErrorReporter reporter;
|
||||
private Server server;
|
||||
|
||||
public NetLoginInjector(ErrorReporter reporter, Server server, ProxyPlayerInjectionHandler injectionHandler) {
|
||||
this.reporter = reporter;
|
||||
this.server = server;
|
||||
this.injectionHandler = injectionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a NetLoginHandler has been created.
|
||||
* @param inserting - the new NetLoginHandler.
|
||||
* @return An injected NetLoginHandler, or the original object.
|
||||
*/
|
||||
public Object onNetLoginCreated(Object inserting) {
|
||||
try {
|
||||
// Make sure we actually need to inject during this phase
|
||||
if (!injectionHandler.isInjectionNecessary(GamePhase.LOGIN))
|
||||
return inserting;
|
||||
|
||||
Player temporary = playerFactory.createTemporaryPlayer(server);
|
||||
// Note that we bail out if there's an existing player injector
|
||||
PlayerInjector injector = injectionHandler.injectPlayer(
|
||||
temporary, inserting, ConflictStrategy.BAIL_OUT, GamePhase.LOGIN);
|
||||
|
||||
if (injector != null) {
|
||||
// Update injector as well
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(temporary, injector);
|
||||
injector.updateOnLogin = true;
|
||||
|
||||
// Save the login
|
||||
injectedLogins.putIfAbsent(inserting, injector);
|
||||
}
|
||||
|
||||
// NetServerInjector can never work (currently), so we don't need to replace the NetLoginHandler
|
||||
return inserting;
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Minecraft can't handle this, so we'll deal with it here
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_HOOK_LOGIN_HANDLER).
|
||||
messageParam(MinecraftReflection.getNetLoginHandlerName()).
|
||||
callerParam(inserting, injectionHandler).
|
||||
error(e)
|
||||
);
|
||||
return inserting;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a NetLoginHandler should be reverted.
|
||||
* @param inserting - the original NetLoginHandler.
|
||||
* @return An injected NetLoginHandler, or the original object.
|
||||
*/
|
||||
public synchronized void cleanup(Object removing) {
|
||||
PlayerInjector injected = injectedLogins.get(removing);
|
||||
|
||||
if (injected != null) {
|
||||
try {
|
||||
PlayerInjector newInjector = null;
|
||||
Player player = injected.getPlayer();
|
||||
|
||||
// Clean up list
|
||||
injectedLogins.remove(removing);
|
||||
|
||||
// No need to clean up twice
|
||||
if (injected.isClean())
|
||||
return;
|
||||
|
||||
// Hack to clean up other references
|
||||
newInjector = injectionHandler.getInjectorByNetworkHandler(injected.getNetworkManager());
|
||||
injectionHandler.uninjectPlayer(player);
|
||||
|
||||
// Update NetworkManager
|
||||
if (newInjector != null) {
|
||||
if (injected instanceof NetworkObjectInjector) {
|
||||
newInjector.setNetworkManager(injected.getNetworkManager(), true);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Don't leak this to Minecraft
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_CANNOT_CLEANUP_LOGIN_HANDLER).
|
||||
messageParam(MinecraftReflection.getNetLoginHandlerName()).
|
||||
callerParam(removing).
|
||||
error(e)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all injected hooks.
|
||||
*/
|
||||
public void cleanupAll() {
|
||||
for (PlayerInjector injector : injectedLogins.values()) {
|
||||
injector.cleanupAll();
|
||||
}
|
||||
|
||||
injectedLogins.clear();
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
/*
|
||||
* 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.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Injection hook that overrides the packet queue lists in NetworkHandler.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class NetworkFieldInjector extends PlayerInjector {
|
||||
|
||||
/**
|
||||
* Marker interface that indicates a packet is fake and should not be processed.
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface FakePacket {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
// After commit 336a4e00668fd2518c41242755ed6b3bdc3b0e6c (Update CraftBukkit to Minecraft 1.4.4.),
|
||||
// CraftBukkit stopped redirecting map chunk and map chunk bulk packets to a separate queue.
|
||||
// Thus, NetworkFieldInjector can safely handle every packet (though not perfectly - some packets
|
||||
// will be slightly processed).
|
||||
private MinecraftVersion safeVersion = new MinecraftVersion("1.4.4");
|
||||
|
||||
// Packets to ignore
|
||||
private Set<Object> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
|
||||
|
||||
// Overridden fields
|
||||
private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
|
||||
|
||||
// Sync field
|
||||
private static Field syncField;
|
||||
private Object syncObject;
|
||||
|
||||
// Determine if we're listening
|
||||
private IntegerSet sendingFilters;
|
||||
|
||||
public NetworkFieldInjector(ErrorReporter reporter, Player player, ListenerInvoker manager, IntegerSet sendingFilters) {
|
||||
super(reporter, player, manager);
|
||||
this.sendingFilters = sendingFilters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasListener(int packetID) {
|
||||
return sendingFilters.contains(packetID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void initialize(Object injectionSource) throws IllegalAccessException {
|
||||
super.initialize(injectionSource);
|
||||
|
||||
// Get the sync field as well
|
||||
if (hasInitialized) {
|
||||
if (syncField == null)
|
||||
syncField = FuzzyReflection.fromObject(networkManager, true).getFieldByType("java\\.lang\\.Object");
|
||||
syncObject = FieldUtils.readField(syncField, networkManager, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
|
||||
if (networkManager != null) {
|
||||
try {
|
||||
if (!filtered) {
|
||||
ignoredPackets.add(packet);
|
||||
}
|
||||
if (marker != null) {
|
||||
queuedMarkers.put(packet, marker);
|
||||
}
|
||||
|
||||
// Note that invocation target exception is a wrapper for a checked exception
|
||||
queueMethod.invoke(networkManager, packet);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Unable to access queue method.", e);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to load network mananager. Cannot send packet.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) {
|
||||
if (version != null && version.compareTo(safeVersion) > 0) {
|
||||
return null;
|
||||
|
||||
} else {
|
||||
@SuppressWarnings("deprecation")
|
||||
int[] unsupported = { Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK };
|
||||
|
||||
// Unfortunately, we don't support chunk packets
|
||||
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), unsupported)) {
|
||||
return new UnsupportedListener("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.", unsupported);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectManager() {
|
||||
if (networkManager != null) {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
StructureModifier<List> list = networkModifier.withType(List.class);
|
||||
|
||||
// Subclass both send queues
|
||||
for (Field field : list.getFields()) {
|
||||
VolatileField overwriter = new VolatileField(field, networkManager, true);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> minecraftList = (List<Object>) overwriter.getOldValue();
|
||||
|
||||
synchronized(syncObject) {
|
||||
// The list we'll be inserting
|
||||
List<Object> hackedList = new InjectedArrayList(this, ignoredPackets);
|
||||
|
||||
// Add every previously stored packet
|
||||
for (Object packet : minecraftList) {
|
||||
hackedList.add(packet);
|
||||
}
|
||||
|
||||
// Don' keep stale packets around
|
||||
minecraftList.clear();
|
||||
overwriter.setValue(Collections.synchronizedList(hackedList));
|
||||
}
|
||||
|
||||
overridenLists.add(overwriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void cleanHook() {
|
||||
// Clean up
|
||||
for (VolatileField overriden : overridenLists) {
|
||||
List<Object> minecraftList = (List<Object>) overriden.getOldValue();
|
||||
List<Object> hacketList = (List<Object>) overriden.getValue();
|
||||
|
||||
if (minecraftList == hacketList) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a lock before we modify the list
|
||||
synchronized(syncObject) {
|
||||
try {
|
||||
// Copy over current packets
|
||||
for (Object packet : (List<Object>) overriden.getValue()) {
|
||||
minecraftList.add(packet);
|
||||
}
|
||||
} finally {
|
||||
overriden.revertValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
overridenLists.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect() {
|
||||
// No need to do anything
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInject(GamePhase phase) {
|
||||
// All phases should work
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerInjectHooks getHookType() {
|
||||
return PlayerInjectHooks.NETWORK_HANDLER_FIELDS;
|
||||
}
|
||||
}
|
@ -1,230 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.LazyLoader;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
/**
|
||||
* Injection method that overrides the NetworkHandler itself, and its queue-method.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class NetworkObjectInjector extends PlayerInjector {
|
||||
// Determine if we're listening
|
||||
private IntegerSet sendingFilters;
|
||||
|
||||
// After commit 336a4e00668fd2518c41242755ed6b3bdc3b0e6c (Update CraftBukkit to Minecraft 1.4.4.),
|
||||
// CraftBukkit stopped redirecting map chunk and map chunk bulk packets to a separate queue.
|
||||
// Thus, NetworkFieldInjector can safely handle every packet (though not perfectly - some packets
|
||||
// will be slightly processed).
|
||||
private MinecraftVersion safeVersion = new MinecraftVersion("1.4.4");
|
||||
|
||||
// Shared callback filter - avoid creating a new class every time
|
||||
private volatile static CallbackFilter callbackFilter;
|
||||
|
||||
// Temporary player factory
|
||||
private static volatile TemporaryPlayerFactory tempPlayerFactory;
|
||||
|
||||
/**
|
||||
* Create a new network object injector.
|
||||
* <p>
|
||||
* Note: This class is intended to be internal. Do not use.
|
||||
* @param reporter - the error reporter.
|
||||
* @param player - the player Bukkit entity.
|
||||
* @param invoker - the packet invoker.
|
||||
* @param sendingFilters - list of permitted packet IDs.
|
||||
* @throws IllegalAccessException If reflection failed.
|
||||
*/
|
||||
public NetworkObjectInjector(ErrorReporter reporter, Player player,
|
||||
ListenerInvoker invoker, IntegerSet sendingFilters) throws IllegalAccessException {
|
||||
|
||||
super(reporter, player, invoker);
|
||||
this.sendingFilters = sendingFilters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasListener(int 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 the fake player with this network injector
|
||||
return tempPlayerFactory.createTemporaryPlayer(server, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
|
||||
Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue();
|
||||
|
||||
if (networkDelegate != null) {
|
||||
try {
|
||||
if (marker != null) {
|
||||
queuedMarkers.put(packet, marker);
|
||||
}
|
||||
|
||||
// Note that invocation target exception is a wrapper for a checked exception
|
||||
queueMethod.invoke(networkDelegate, packet);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Unable to access queue method.", e);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to load network mananager. Cannot send packet.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) {
|
||||
if (version != null && version.compareTo(safeVersion) > 0) {
|
||||
return null;
|
||||
|
||||
} else {
|
||||
@SuppressWarnings("deprecation")
|
||||
int[] unsupported = { Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK };
|
||||
|
||||
// Unfortunately, we don't support chunk packets
|
||||
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), unsupported)) {
|
||||
return new UnsupportedListener("The NETWORK_OBJECT_INJECTOR hook doesn't support map chunk listeners.", unsupported);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectManager() {
|
||||
|
||||
if (networkManager != null) {
|
||||
final Class<?> networkInterface = networkManagerRef.getField().getType();
|
||||
final Object networkDelegate = networkManagerRef.getOldValue();
|
||||
|
||||
if (!networkInterface.isInterface()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Must use CraftBukkit 1.3.0 or later to inject into into NetworkMananger.");
|
||||
}
|
||||
|
||||
Callback queueFilter = new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
Object packet = args[0];
|
||||
|
||||
if (packet != null) {
|
||||
packet = handlePacketSending(packet);
|
||||
|
||||
// A NULL packet indicate cancelling
|
||||
if (packet != null)
|
||||
args[0] = packet;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
// Delegate to our underlying class
|
||||
return proxy.invokeSuper(networkDelegate, args);
|
||||
}
|
||||
};
|
||||
Callback dispatch = new LazyLoader() {
|
||||
@Override
|
||||
public Object loadObject() throws Exception {
|
||||
return networkDelegate;
|
||||
}
|
||||
};
|
||||
|
||||
// Share callback filter - that way, we avoid generating a new class every time.
|
||||
if (callbackFilter == null) {
|
||||
callbackFilter = new CallbackFilter() {
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
if (method.equals(queueMethod))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Create our proxy object
|
||||
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
|
||||
ex.setSuperclass(networkInterface);
|
||||
ex.setCallbacks(new Callback[] { queueFilter, dispatch });
|
||||
ex.setCallbackFilter(callbackFilter);
|
||||
|
||||
// Inject it, if we can.
|
||||
networkManagerRef.setValue(ex.create());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanHook() {
|
||||
// Clean up
|
||||
if (networkManagerRef != null && networkManagerRef.isCurrentSet()) {
|
||||
networkManagerRef.revertValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect() {
|
||||
// No need to do anything
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInject(GamePhase phase) {
|
||||
// Works for all phases
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerInjectHooks getHookType() {
|
||||
return PlayerInjectHooks.NETWORK_MANAGER_OBJECT;
|
||||
}
|
||||
}
|
@ -1,367 +0,0 @@
|
||||
/*
|
||||
* 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.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
import net.sf.cglib.proxy.NoOp;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
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;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
/**
|
||||
* Represents a player hook into the NetServerHandler class.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class NetworkServerInjector extends PlayerInjector {
|
||||
// Disconnected field
|
||||
public static final ReportType REPORT_ASSUMING_DISCONNECT_FIELD = new ReportType("Unable to find 'disconnected' field. Assuming %s.");
|
||||
public static final ReportType REPORT_DISCONNECT_FIELD_MISSING = new ReportType("Cannot find disconnected field. Is ProtocolLib up to date?");
|
||||
public static final ReportType REPORT_DISCONNECT_FIELD_FAILURE = new ReportType("Unable to update disconnected field. Player quit event may be sent twice.");
|
||||
|
||||
private volatile static CallbackFilter callbackFilter;
|
||||
private volatile static boolean foundSendPacket;
|
||||
|
||||
private volatile static Field disconnectField;
|
||||
private InjectedServerConnection serverInjection;
|
||||
|
||||
// Determine if we're listening
|
||||
private IntegerSet sendingFilters;
|
||||
|
||||
// Whether or not the player has disconnected
|
||||
private boolean hasDisconnected;
|
||||
|
||||
// Used to copy fields
|
||||
private final ObjectWriter writer = new ObjectWriter();
|
||||
|
||||
public NetworkServerInjector(
|
||||
ErrorReporter reporter, Player player,
|
||||
ListenerInvoker invoker, IntegerSet sendingFilters,
|
||||
InjectedServerConnection serverInjection) {
|
||||
super(reporter, player, invoker);
|
||||
this.sendingFilters = sendingFilters;
|
||||
this.serverInjection = serverInjection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasListener(int packetID) {
|
||||
return sendingFilters.contains(packetID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
|
||||
Object serverDelegate = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue();
|
||||
|
||||
if (serverDelegate != null) {
|
||||
try {
|
||||
if (marker != null) {
|
||||
queuedMarkers.put(packet, marker);
|
||||
}
|
||||
|
||||
// Note that invocation target exception is a wrapper for a checked exception
|
||||
MinecraftMethods.getSendPacketMethod().invoke(serverDelegate, packet);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Unable to access send packet method.", e);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to load server handler. Cannot send packet.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectManager() {
|
||||
if (serverHandlerRef == null)
|
||||
throw new IllegalStateException("Cannot find server handler.");
|
||||
// Don't inject twice
|
||||
if (serverHandlerRef.getValue() instanceof Factory)
|
||||
return;
|
||||
|
||||
if (!tryInjectManager()) {
|
||||
Class<?> serverHandlerClass = MinecraftReflection.getPlayerConnectionClass();
|
||||
|
||||
// Try to override the proxied object
|
||||
if (proxyServerField != null) {
|
||||
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
|
||||
serverHandler = serverHandlerRef.getValue();
|
||||
|
||||
if (serverHandler == null)
|
||||
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
|
||||
else
|
||||
serverHandlerClass = serverHandler.getClass();
|
||||
|
||||
// Try again
|
||||
if (tryInjectManager()) {
|
||||
// It worked - probably
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"Cannot hook player: Unable to find a valid constructor for the "
|
||||
+ serverHandlerClass.getName() + " object.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryInjectManager() {
|
||||
Class<?> serverClass = serverHandler.getClass();
|
||||
|
||||
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
|
||||
Callback sendPacketCallback = new MethodInterceptor() {
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
Object packet = args[0];
|
||||
|
||||
if (packet != null) {
|
||||
packet = handlePacketSending(packet);
|
||||
|
||||
// A NULL packet indicate cancelling
|
||||
if (packet != null)
|
||||
args[0] = packet;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
// Call the method directly
|
||||
return proxy.invokeSuper(obj, args);
|
||||
};
|
||||
};
|
||||
Callback noOpCallback = NoOp.INSTANCE;
|
||||
|
||||
// Share callback filter - that way, we avoid generating a new class for
|
||||
// every logged in player.
|
||||
if (callbackFilter == null) {
|
||||
callbackFilter = new SendMethodFilter();
|
||||
}
|
||||
|
||||
ex.setSuperclass(serverClass);
|
||||
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
|
||||
ex.setCallbackFilter(callbackFilter);
|
||||
|
||||
// Find the Minecraft NetServerHandler superclass
|
||||
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
|
||||
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
|
||||
DefaultInstances serverInstances = null;
|
||||
|
||||
// Maybe the proxy instance can help?
|
||||
Object proxyInstance = getProxyServerHandler();
|
||||
|
||||
// Use the existing server proxy when we create one
|
||||
if (proxyInstance != null && proxyInstance != serverHandler) {
|
||||
serverInstances = DefaultInstances.fromArray(generator,
|
||||
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
|
||||
} else {
|
||||
serverInstances = DefaultInstances.fromArray(generator);
|
||||
}
|
||||
|
||||
serverInstances.setNonNull(true);
|
||||
serverInstances.setMaximumRecursion(1);
|
||||
|
||||
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
|
||||
|
||||
// Inject it now
|
||||
if (proxyObject != null) {
|
||||
// Did we override a sendPacket method?
|
||||
if (!foundSendPacket) {
|
||||
throw new IllegalArgumentException("Unable to find a sendPacket method in " + serverClass);
|
||||
}
|
||||
|
||||
serverInjection.replaceServerHandler(serverHandler, proxyObject);
|
||||
serverHandlerRef.setValue(proxyObject);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Object getProxyServerHandler() {
|
||||
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
|
||||
try {
|
||||
return FieldUtils.readField(proxyServerField, serverHandler, true);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Oh well
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
|
||||
if (MinecraftReflection.isMinecraftClass(clazz))
|
||||
return clazz;
|
||||
else if (clazz.equals(Object.class))
|
||||
return clazz;
|
||||
else
|
||||
return getFirstMinecraftSuperClass(clazz.getSuperclass());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanHook() {
|
||||
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
|
||||
writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
|
||||
serverHandlerRef.revertValue();
|
||||
|
||||
try {
|
||||
if (getNetHandler() != null) {
|
||||
// Restore packet listener
|
||||
try {
|
||||
FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true);
|
||||
} catch (IllegalAccessException e) {
|
||||
// Oh well
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Prevent the PlayerQuitEvent from being sent twice
|
||||
if (hasDisconnected) {
|
||||
setDisconnect(serverHandlerRef.getValue(), true);
|
||||
}
|
||||
}
|
||||
|
||||
serverInjection.revertServerHandler(serverHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect() {
|
||||
hasDisconnected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the disconnected field in a NetServerHandler.
|
||||
* @param handler - the NetServerHandler.
|
||||
* @param value - the new value.
|
||||
*/
|
||||
private void setDisconnect(Object handler, boolean value) {
|
||||
// Set it
|
||||
try {
|
||||
// Load the field
|
||||
if (disconnectField == null) {
|
||||
disconnectField = FuzzyReflection.fromObject(handler).getFieldByName("disconnected.*");
|
||||
}
|
||||
FieldUtils.writeField(disconnectField, handler, value);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Assume it's the first ...
|
||||
if (disconnectField == null) {
|
||||
disconnectField = FuzzyReflection.fromObject(handler).getFieldByType("disconnected", boolean.class);
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_ASSUMING_DISCONNECT_FIELD).messageParam(disconnectField));
|
||||
|
||||
// Try again
|
||||
if (disconnectField != null) {
|
||||
setDisconnect(handler, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This is really bad
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_MISSING).error(e));
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_FAILURE).error(e));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) {
|
||||
// We support everything
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInject(GamePhase phase) {
|
||||
// Doesn't work when logging in
|
||||
return phase == GamePhase.PLAYING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerInjectHooks getHookType() {
|
||||
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a CallbackFilter that only matches the SendPacket method.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static class SendMethodFilter implements CallbackFilter {
|
||||
private Method sendPacket = MinecraftMethods.getSendPacketMethod();
|
||||
|
||||
@Override
|
||||
public int accept(Method method) {
|
||||
if (isCallableEqual(sendPacket, method)) {
|
||||
NetworkServerInjector.foundSendPacket = true;
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the two methods are equal in terms of call semantics.
|
||||
* <p>
|
||||
* Two methods are equal if they have the same name, parameter types and return type.
|
||||
* @param first - first method.
|
||||
* @param second - second method.
|
||||
* @return TRUE if they are, FALSE otherwise.
|
||||
*/
|
||||
private boolean isCallableEqual(Method first, Method second) {
|
||||
return first.getName().equals(second.getName()) &&
|
||||
first.getReturnType().equals(second.getReturnType()) &&
|
||||
Arrays.equals(first.getParameterTypes(), second.getParameterTypes());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,760 +0,0 @@
|
||||
/*
|
||||
* 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.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
|
||||
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.VolatileField;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
public abstract class PlayerInjector implements SocketInjector {
|
||||
// Disconnect method related reports
|
||||
public static final ReportType REPORT_ASSUME_DISCONNECT_METHOD = new ReportType("Cannot find disconnect method by name. Assuming %s.");
|
||||
public static final ReportType REPORT_INVALID_ARGUMENT_DISCONNECT = new ReportType("Invalid argument passed to disconnect method: %s");
|
||||
public static final ReportType REPORT_CANNOT_ACCESS_DISCONNECT = new ReportType("Unable to access disconnect method.");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_CLOSE_SOCKET = new ReportType("Unable to close socket.");
|
||||
public static final ReportType REPORT_ACCESS_DENIED_CLOSE_SOCKET = new ReportType("Insufficient permissions. Cannot close socket.");
|
||||
|
||||
public static final ReportType REPORT_DETECTED_CUSTOM_SERVER_HANDLER =
|
||||
new ReportType("Detected server handler proxy type by another plugin. Conflict may occur!");
|
||||
public static final ReportType REPORT_CANNOT_PROXY_SERVER_HANDLER = new ReportType("Unable to load server handler from proxy type.");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_UPDATE_PLAYER = new ReportType("Cannot update player in PlayerEvent.");
|
||||
public static final ReportType REPORT_CANNOT_HANDLE_PACKET = new ReportType("Cannot handle server packet.");
|
||||
|
||||
public static final ReportType REPORT_INVALID_NETWORK_MANAGER = new ReportType("NetworkManager doesn't appear to be valid.");
|
||||
|
||||
// Net login handler stuff
|
||||
private static Field netLoginNetworkField;
|
||||
|
||||
// Different disconnect methods
|
||||
private static Method loginDisconnect;
|
||||
private static Method serverDisconnect;
|
||||
|
||||
// Cache previously retrieved fields
|
||||
protected static Field serverHandlerField;
|
||||
protected static Field proxyServerField;
|
||||
|
||||
protected static Field networkManagerField;
|
||||
protected static Field netHandlerField;
|
||||
protected static Field socketField;
|
||||
protected static Field socketAddressField;
|
||||
|
||||
private static Field inputField;
|
||||
private static Field entityPlayerField;
|
||||
|
||||
// Whether or not we're using a proxy type
|
||||
private static boolean hasProxyType;
|
||||
|
||||
// To add our injected array lists
|
||||
protected static StructureModifier<Object> networkModifier;
|
||||
|
||||
// And methods
|
||||
protected static Method queueMethod;
|
||||
protected static Method processMethod;
|
||||
|
||||
protected volatile Player player;
|
||||
protected boolean hasInitialized;
|
||||
|
||||
// Reference to the player's network manager
|
||||
protected VolatileField networkManagerRef;
|
||||
protected VolatileField serverHandlerRef;
|
||||
protected Object networkManager;
|
||||
|
||||
// Current net handler
|
||||
protected Object loginHandler;
|
||||
protected Object serverHandler;
|
||||
protected Object netHandler;
|
||||
|
||||
// Current socket and address
|
||||
protected Socket socket;
|
||||
protected SocketAddress socketAddress;
|
||||
|
||||
// The packet manager and filters
|
||||
protected ListenerInvoker invoker;
|
||||
|
||||
// Previous data input
|
||||
protected DataInputStream cachedInput;
|
||||
|
||||
// Handle errors
|
||||
protected ErrorReporter reporter;
|
||||
|
||||
// Previous markers
|
||||
protected Map<Object, NetworkMarker> queuedMarkers = new MapMaker().weakKeys().makeMap();
|
||||
protected InterceptWritePacket writePacketInterceptor;
|
||||
|
||||
// Whether or not the injector has been cleaned
|
||||
private boolean clean;
|
||||
|
||||
// Whether or not to update the current player on the first Packet1Login
|
||||
boolean updateOnLogin;
|
||||
volatile Player updatedPlayer;
|
||||
|
||||
public PlayerInjector(ErrorReporter reporter, Player player, ListenerInvoker invoker) {
|
||||
this.reporter = reporter;
|
||||
this.player = player;
|
||||
this.invoker = invoker;
|
||||
this.writePacketInterceptor = invoker.getInterceptWritePacket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the notch (NMS) entity player object.
|
||||
* @param player - the player to retrieve.
|
||||
* @return Notch player object.
|
||||
*/
|
||||
protected Object getEntityPlayer(Player player) {
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
return unwrapper.unwrapItem(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all fields for this player injector, if it hasn't already.
|
||||
* @param injectionSource - Injection source
|
||||
* @throws IllegalAccessException An error has occured.
|
||||
*/
|
||||
public void initialize(Object injectionSource) throws IllegalAccessException {
|
||||
if (injectionSource == null)
|
||||
throw new IllegalArgumentException("injectionSource cannot be NULL");
|
||||
|
||||
//Dispatch to the correct injection method
|
||||
if (injectionSource instanceof Player)
|
||||
initializePlayer((Player) injectionSource);
|
||||
else if (MinecraftReflection.isLoginHandler(injectionSource))
|
||||
initializeLogin(injectionSource);
|
||||
else
|
||||
throw new IllegalArgumentException("Cannot initialize a player hook using a " + injectionSource.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the player injector using an actual player instance.
|
||||
* @param player - the player to hook.
|
||||
*/
|
||||
public void initializePlayer(Player player) {
|
||||
Object notchEntity = getEntityPlayer(player);
|
||||
|
||||
// Save the player too
|
||||
this.player = player;
|
||||
|
||||
if (!hasInitialized) {
|
||||
// Do this first, in case we encounter an exception
|
||||
hasInitialized = true;
|
||||
|
||||
// Retrieve the server handler
|
||||
if (serverHandlerField == null) {
|
||||
serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(
|
||||
"NetServerHandler", MinecraftReflection.getPlayerConnectionClass());
|
||||
proxyServerField = getProxyField(notchEntity, serverHandlerField);
|
||||
}
|
||||
|
||||
// Yo dawg
|
||||
serverHandlerRef = new VolatileField(serverHandlerField, notchEntity);
|
||||
serverHandler = serverHandlerRef.getValue();
|
||||
|
||||
// Next, get the network manager
|
||||
if (networkManagerField == null)
|
||||
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(
|
||||
"networkManager", MinecraftReflection.getNetworkManagerClass());
|
||||
initializeNetworkManager(networkManagerField, serverHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the player injector from a NetLoginHandler.
|
||||
* @param netLoginHandler - the net login handler to inject.
|
||||
*/
|
||||
public void initializeLogin(Object netLoginHandler) {
|
||||
if (!hasInitialized) {
|
||||
// Just in case
|
||||
if (!MinecraftReflection.isLoginHandler(netLoginHandler))
|
||||
throw new IllegalArgumentException("netLoginHandler (" + netLoginHandler + ") is not a " +
|
||||
MinecraftReflection.getNetLoginHandlerName());
|
||||
|
||||
hasInitialized = true;
|
||||
loginHandler = netLoginHandler;
|
||||
|
||||
if (netLoginNetworkField == null)
|
||||
netLoginNetworkField = FuzzyReflection.fromObject(netLoginHandler).
|
||||
getFieldByType("networkManager", MinecraftReflection.getNetworkManagerClass());
|
||||
initializeNetworkManager(netLoginNetworkField, netLoginHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeNetworkManager(Field reference, Object container) {
|
||||
networkManagerRef = new VolatileField(reference, container);
|
||||
networkManager = networkManagerRef.getValue();
|
||||
|
||||
// No, don't do it
|
||||
if (networkManager instanceof Factory) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the network manager modifier from the actual object type
|
||||
if (networkManager != null && networkModifier == null)
|
||||
networkModifier = new StructureModifier<Object>(networkManager.getClass(), null, false);
|
||||
|
||||
// And the queue method
|
||||
if (queueMethod == null)
|
||||
queueMethod = FuzzyReflection.fromClass(reference.getType()).
|
||||
getMethodByParameters("queue", MinecraftReflection.getPacketClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve whether or not the server handler is a proxy object.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
protected boolean hasProxyServerHandler() {
|
||||
return hasProxyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current network manager.
|
||||
* @return Current network manager.
|
||||
*/
|
||||
public Object getNetworkManager() {
|
||||
return networkManagerRef.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current server handler (PlayerConnection).
|
||||
* @return Current server handler.
|
||||
*/
|
||||
public Object getServerHandler() {
|
||||
return serverHandlerRef.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current network manager.
|
||||
* @param value - new network manager.
|
||||
* @param force - whether or not to save this value.
|
||||
*/
|
||||
public void setNetworkManager(Object value, boolean force) {
|
||||
networkManagerRef.setValue(value);
|
||||
|
||||
if (force)
|
||||
networkManagerRef.saveValue();
|
||||
initializeNetworkManager(networkManagerField, serverHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated socket of this player.
|
||||
* @return The associated socket.
|
||||
* @throws IllegalAccessException If we're unable to read the socket field.
|
||||
*/
|
||||
@Override
|
||||
public Socket getSocket() throws IllegalAccessException {
|
||||
try {
|
||||
if (socketField == null)
|
||||
socketField = FuzzyReflection.fromObject(networkManager, true).
|
||||
getFieldListByType(Socket.class).get(0);
|
||||
if (socket == null)
|
||||
socket = (Socket) FieldUtils.readField(socketField, networkManager, true);
|
||||
return socket;
|
||||
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new IllegalAccessException("Unable to read the socket field.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the associated remote address of a player.
|
||||
* @return The associated remote address.
|
||||
* @throws IllegalAccessException If we're unable to read the socket address field.
|
||||
*/
|
||||
@Override
|
||||
public SocketAddress getAddress() throws IllegalAccessException {
|
||||
try {
|
||||
if (socketAddressField == null)
|
||||
socketAddressField = FuzzyReflection.fromObject(networkManager, true).
|
||||
getFieldListByType(SocketAddress.class).get(0);
|
||||
if (socketAddress == null)
|
||||
socketAddress = (SocketAddress) FieldUtils.readField(socketAddressField, networkManager, true);
|
||||
return socketAddress;
|
||||
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// Inform about the state of the network manager too
|
||||
reporter.reportWarning(
|
||||
this, Report.newBuilder(REPORT_INVALID_NETWORK_MANAGER).callerParam(networkManager).build());
|
||||
throw new IllegalAccessException("Unable to read the socket address field.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to disconnect the current client.
|
||||
* @param message - the message to display.
|
||||
* @throws InvocationTargetException If disconnection failed.
|
||||
*/
|
||||
@Override
|
||||
public void disconnect(String message) throws InvocationTargetException {
|
||||
// Get a non-null handler
|
||||
boolean usingNetServer = serverHandler != null;
|
||||
|
||||
Object handler = usingNetServer ? serverHandler : loginHandler;
|
||||
Method disconnect = usingNetServer ? serverDisconnect : loginDisconnect;
|
||||
|
||||
// Execute disconnect on it
|
||||
if (handler != null) {
|
||||
if (disconnect == null) {
|
||||
try {
|
||||
disconnect = FuzzyReflection.fromObject(handler).getMethodByName("disconnect.*");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Just assume it's the first String method
|
||||
disconnect = FuzzyReflection.fromObject(handler).getMethodByParameters("disconnect", String.class);
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_ASSUME_DISCONNECT_METHOD).messageParam(disconnect));
|
||||
}
|
||||
|
||||
// Save the method for later
|
||||
if (usingNetServer)
|
||||
serverDisconnect = disconnect;
|
||||
else
|
||||
loginDisconnect = disconnect;
|
||||
}
|
||||
|
||||
try {
|
||||
disconnect.invoke(handler, message);
|
||||
return;
|
||||
} catch (IllegalArgumentException e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_INVALID_ARGUMENT_DISCONNECT).error(e).messageParam(message).callerParam(handler));
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_ACCESS_DISCONNECT).error(e));
|
||||
}
|
||||
}
|
||||
|
||||
// Fuck it
|
||||
try {
|
||||
Socket socket = getSocket();
|
||||
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_CLOSE_SOCKET).error(e).callerParam(socket));
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_ACCESS_DENIED_CLOSE_SOCKET).error(e));
|
||||
}
|
||||
}
|
||||
|
||||
private Field getProxyField(Object notchEntity, Field serverField) {
|
||||
try {
|
||||
Object currentHandler = FieldUtils.readField(serverHandlerField, notchEntity, true);
|
||||
|
||||
// This is bad
|
||||
if (currentHandler == null)
|
||||
throw new ServerHandlerNull();
|
||||
|
||||
// See if this isn't a standard net handler class
|
||||
if (!isStandardMinecraftNetHandler(currentHandler)) {
|
||||
// This is our proxy object
|
||||
if (currentHandler instanceof Factory)
|
||||
return null;
|
||||
|
||||
hasProxyType = true;
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_DETECTED_CUSTOM_SERVER_HANDLER).callerParam(serverField));
|
||||
|
||||
// No? Is it a Proxy type?
|
||||
try {
|
||||
FuzzyReflection reflection = FuzzyReflection.fromObject(currentHandler, true);
|
||||
|
||||
// It might be
|
||||
return reflection.getFieldByType("NetServerHandler", MinecraftReflection.getPlayerConnectionClass());
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
// Damn
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_PROXY_SERVER_HANDLER).error(e).callerParam(notchEntity, serverField));
|
||||
}
|
||||
|
||||
// Nope, just go with it
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given object is a standard Minecraft net handler.
|
||||
* @param obj the object to test.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
private boolean isStandardMinecraftNetHandler(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
Class<?> clazz = obj.getClass();
|
||||
|
||||
return MinecraftReflection.getNetLoginHandlerClass().equals(clazz) ||
|
||||
MinecraftReflection.getPlayerConnectionClass().equals(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current net handler for this player.
|
||||
* @return Current net handler.
|
||||
* @throws IllegalAccessException Unable to find or retrieve net handler.
|
||||
*/
|
||||
protected Object getNetHandler() throws IllegalAccessException {
|
||||
return getNetHandler(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current net handler for this player.
|
||||
* @param refresh - Whether or not to refresh
|
||||
* @return Current net handler.
|
||||
* @throws IllegalAccessException Unable to find or retrieve net handler.
|
||||
* @return The current net handler for this player
|
||||
*/
|
||||
protected Object getNetHandler(boolean refresh) throws IllegalAccessException {
|
||||
// What a mess
|
||||
try {
|
||||
if (netHandlerField == null)
|
||||
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
|
||||
getFieldByType("NetHandler", MinecraftReflection.getNetHandlerClass());
|
||||
} catch (RuntimeException e1) {
|
||||
// Swallow it
|
||||
}
|
||||
|
||||
// Second attempt
|
||||
if (netHandlerField == null) {
|
||||
try {
|
||||
// Well, that sucks. Try just Minecraft objects then.
|
||||
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
|
||||
getFieldByType(MinecraftReflection.getMinecraftObjectRegex());
|
||||
|
||||
} catch (RuntimeException e2) {
|
||||
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Get the handler
|
||||
if (netHandler == null || refresh)
|
||||
netHandler = FieldUtils.readField(netHandlerField, networkManager, true);
|
||||
return netHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the stored entity player from a given NetHandler.
|
||||
* @param netHandler - the nethandler to retrieve it from.
|
||||
* @return The stored entity player.
|
||||
* @throws IllegalAccessException If the reflection failed.
|
||||
*/
|
||||
private Object getEntityPlayer(Object netHandler) throws IllegalAccessException {
|
||||
if (entityPlayerField == null)
|
||||
entityPlayerField = FuzzyReflection.fromObject(netHandler).getFieldByType(
|
||||
"EntityPlayer", MinecraftReflection.getEntityPlayerClass());
|
||||
return FieldUtils.readField(entityPlayerField, netHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the given packet as if it was transmitted by the current player.
|
||||
* @param packet - packet to process.
|
||||
* @throws IllegalAccessException If the reflection machinery failed.
|
||||
* @throws InvocationTargetException If the underlying method caused an error.
|
||||
*/
|
||||
public void processPacket(Object packet) throws IllegalAccessException, InvocationTargetException {
|
||||
|
||||
Object netHandler = getNetHandler();
|
||||
|
||||
// Get the process method
|
||||
if (processMethod == null) {
|
||||
try {
|
||||
processMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
|
||||
getMethodByParameters("processPacket", netHandlerField.getType());
|
||||
} catch (RuntimeException e) {
|
||||
throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// We're ready
|
||||
try {
|
||||
processMethod.invoke(packet, netHandler);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("Method " + processMethod.getName() + " is not compatible.");
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to the client.
|
||||
* @param packet - server packet to send.
|
||||
* @param marker - the network marker.
|
||||
* @param filtered - whether or not the packet will be filtered by our listeners.
|
||||
* @throws InvocationTargetException If an error occured when sending the packet.
|
||||
*/
|
||||
@Override
|
||||
public abstract void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Inject a hook to catch packets sent to the current player.
|
||||
*/
|
||||
public abstract void injectManager();
|
||||
|
||||
/**
|
||||
* Remove all hooks and modifications.
|
||||
*/
|
||||
public final void cleanupAll() {
|
||||
if (!clean) {
|
||||
cleanHook();
|
||||
writePacketInterceptor.cleanup();
|
||||
}
|
||||
clean = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up after the player has disconnected.
|
||||
*/
|
||||
public abstract void handleDisconnect();
|
||||
|
||||
/**
|
||||
* Override to add custom cleanup behavior.
|
||||
*/
|
||||
protected abstract void cleanHook();
|
||||
|
||||
/**
|
||||
* Determine whether or not this hook has already been cleaned.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public boolean isClean() {
|
||||
return clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this inject method can even be attempted.
|
||||
* @param state - Game phase
|
||||
* @return TRUE if can be attempted, though possibly with failure, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean canInject(GamePhase state);
|
||||
|
||||
/**
|
||||
* Retrieve the hook type this class represents.
|
||||
* @return Hook type this class represents.
|
||||
*/
|
||||
public abstract PlayerInjectHooks getHookType();
|
||||
|
||||
/**
|
||||
* Invoked before a new listener is registered.
|
||||
* <p>
|
||||
* The player injector should only return a non-null value if some or all of the packet IDs are unsupported.
|
||||
* @param version - the current Minecraft version, or NULL if unknown.
|
||||
* @param listener - the listener that is about to be registered.
|
||||
* @return A error message with the unsupported packet IDs, or NULL if this listener is valid.
|
||||
*/
|
||||
public abstract UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener);
|
||||
|
||||
/**
|
||||
* Allows a packet to be sent by the listeners.
|
||||
* @param packet - packet to sent.
|
||||
* @return The given packet, or the packet replaced by the listeners.
|
||||
*/
|
||||
@SuppressWarnings({ "deprecation", "null" })
|
||||
public Object handlePacketSending(Object packet) {
|
||||
try {
|
||||
// Get the packet ID too
|
||||
Integer id = invoker.getPacketID(packet);
|
||||
Player currentPlayer = player;
|
||||
|
||||
// Hack #1
|
||||
if (updateOnLogin) {
|
||||
if (updatedPlayer == null) {
|
||||
try {
|
||||
final Object handler = getNetHandler(true);
|
||||
|
||||
// Is this a net server class?
|
||||
if (MinecraftReflection.getPlayerConnectionClass().isAssignableFrom(handler.getClass())) {
|
||||
setUpdatedPlayer(
|
||||
(Player) MinecraftReflection.getBukkitEntity(getEntityPlayer(handler))
|
||||
);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_UPDATE_PLAYER).error(e).callerParam(packet));
|
||||
}
|
||||
}
|
||||
|
||||
// This will only occur in the NetLoginHandler injection
|
||||
if (updatedPlayer != null) {
|
||||
currentPlayer = updatedPlayer;
|
||||
updateOnLogin = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we're listening
|
||||
if (id != null && hasListener(id)) {
|
||||
NetworkMarker marker = queuedMarkers.remove(packet);
|
||||
|
||||
// A packet has been sent guys!
|
||||
PacketType type = PacketType.findLegacy(id, Sender.SERVER);
|
||||
PacketContainer container = new PacketContainer(type, packet);
|
||||
PacketEvent event = PacketEvent.fromServer(invoker, container, marker, currentPlayer);
|
||||
invoker.invokePacketSending(event);
|
||||
|
||||
// Cancelling is pretty simple. Just ignore the packet.
|
||||
if (event.isCancelled())
|
||||
return null;
|
||||
|
||||
// Right, remember to replace the packet again
|
||||
Object result = event.getPacket().getHandle();
|
||||
marker = NetworkMarker.getNetworkMarker(event);
|
||||
|
||||
// See if we need to proxy the write method
|
||||
if (result != null && (NetworkMarker.hasOutputHandlers(marker) || NetworkMarker.hasPostListeners(marker))) {
|
||||
result = writePacketInterceptor.constructProxy(result, event, marker);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_CANNOT_HANDLE_PACKET).error(e).callerParam(packet));
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given injector is listening for this packet ID.
|
||||
* @param packetID - packet ID to check.
|
||||
* @return TRUE if it is, FALSE oterhwise.
|
||||
*/
|
||||
protected abstract boolean hasListener(int packetID);
|
||||
|
||||
/**
|
||||
* Retrieve the current player's input stream.
|
||||
* @param cache - whether or not to cache the result of this method.
|
||||
* @return The player's input stream.
|
||||
*/
|
||||
public DataInputStream getInputStream(boolean cache) {
|
||||
// And the data input stream that we'll use to identify a player
|
||||
if (networkManager == 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
|
||||
try {
|
||||
if (cache && cachedInput != null)
|
||||
return cachedInput;
|
||||
|
||||
// Save to cache
|
||||
cachedInput = (DataInputStream) FieldUtils.readField(inputField, networkManager, true);
|
||||
return cachedInput;
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to read input stream.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the hooked player.
|
||||
*/
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
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.
|
||||
* @return Packet event invoker.
|
||||
*/
|
||||
public ListenerInvoker getInvoker() {
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the hooked player object OR the more up-to-date player instance.
|
||||
* @return The hooked player, or a more up-to-date instance.
|
||||
*/
|
||||
@Override
|
||||
public Player getUpdatedPlayer() {
|
||||
if (updatedPlayer != null)
|
||||
return updatedPlayer;
|
||||
else
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferState(SocketInjector delegate) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdatedPlayer(Player updatedPlayer) {
|
||||
this.updatedPlayer = updatedPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that a player's NetServerHandler or PlayerConnection was NULL.
|
||||
* <p>
|
||||
* This is usually because the player has just logged out, or due to it being a "fake" player in MCPC+/Cauldron.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static class ServerHandlerNull extends IllegalAccessError {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ServerHandlerNull() {
|
||||
super("Unable to fetch server handler: was NUll.");
|
||||
}
|
||||
|
||||
public ServerHandlerNull(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
package com.comphenix.protocol.injector.player;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
/**
|
||||
* Constructor for different player injectors.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PlayerInjectorBuilder {
|
||||
public static PlayerInjectorBuilder newBuilder() {
|
||||
return new PlayerInjectorBuilder();
|
||||
}
|
||||
|
||||
protected PlayerInjectorBuilder() {
|
||||
// Use the static method.
|
||||
}
|
||||
|
||||
protected ErrorReporter reporter;
|
||||
protected Predicate<GamePhase> injectionFilter;
|
||||
protected ListenerInvoker invoker;
|
||||
protected Set<PacketListener> packetListeners;
|
||||
protected Server server;
|
||||
protected MinecraftVersion version;
|
||||
|
||||
/**
|
||||
* The error reporter used by the created injector.
|
||||
* @param reporter - new error reporter.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PlayerInjectorBuilder reporter(@Nonnull ErrorReporter reporter) {
|
||||
Preconditions.checkNotNull(reporter, "reporter cannot be NULL");
|
||||
this.reporter = reporter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The injection filter that is used to determine if it is necessary to perform
|
||||
* injection during a certain phase.
|
||||
* @param injectionFilter - filter predicate.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Nonnull
|
||||
public PlayerInjectorBuilder injectionFilter(@Nonnull Predicate<GamePhase> injectionFilter) {
|
||||
Preconditions.checkNotNull(injectionFilter, "injectionFilter cannot be NULL");
|
||||
this.injectionFilter = injectionFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The packet stream invoker.
|
||||
* @param invoker - the invoker.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PlayerInjectorBuilder invoker(@Nonnull ListenerInvoker invoker) {
|
||||
Preconditions.checkNotNull(invoker, "invoker cannot be NULL");
|
||||
this.invoker = invoker;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the set of packet listeners.
|
||||
* @param packetListeners - packet listeners.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Nonnull
|
||||
public PlayerInjectorBuilder packetListeners(@Nonnull Set<PacketListener> packetListeners) {
|
||||
Preconditions.checkNotNull(packetListeners, "packetListeners cannot be NULL");
|
||||
this.packetListeners = packetListeners;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Bukkit server used for scheduling.
|
||||
* @param server - the Bukkit server.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PlayerInjectorBuilder server(@Nonnull Server server) {
|
||||
Preconditions.checkNotNull(server, "server cannot be NULL");
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current Minecraft version.
|
||||
* @param version - the current Minecraft version, or NULL if unknown.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public PlayerInjectorBuilder version(MinecraftVersion version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before an object is created with this builder.
|
||||
*/
|
||||
private void initializeDefaults() {
|
||||
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
// Initialize with default values if we can
|
||||
if (reporter == null)
|
||||
reporter = ProtocolLibrary.getErrorReporter();
|
||||
if (invoker == null)
|
||||
invoker = (PacketFilterManager) manager;
|
||||
if (server == null)
|
||||
server = Bukkit.getServer();
|
||||
if (injectionFilter == null)
|
||||
throw new IllegalStateException("injectionFilter must be initialized.");
|
||||
if (packetListeners == null)
|
||||
throw new IllegalStateException("packetListeners must be initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the injection handler.
|
||||
* <p>
|
||||
* Any builder parameter marked as NON-NULL is essential and must be initialized.
|
||||
* @return The constructed injection handler using the current parameters.
|
||||
*/
|
||||
public PlayerInjectionHandler buildHandler() {
|
||||
// Fill any default fields
|
||||
initializeDefaults();
|
||||
|
||||
return new ProxyPlayerInjectionHandler(
|
||||
reporter, injectionFilter,
|
||||
invoker, packetListeners, server, version);
|
||||
}
|
||||
}
|
@ -1,763 +0,0 @@
|
||||
/*
|
||||
* 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 io.netty.channel.Channel;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.concurrency.BlockingHashMap;
|
||||
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
|
||||
import com.comphenix.protocol.injector.server.BukkitSocketInjector;
|
||||
import com.comphenix.protocol.injector.server.InputStreamLookupBuilder;
|
||||
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.SafeCacheBuilder;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Responsible for injecting into a player's sendPacket method.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
||||
// Warnings and errors
|
||||
public static final ReportType REPORT_UNSUPPPORTED_LISTENER = new ReportType("Cannot fully register listener for %s: %s");
|
||||
|
||||
// Fallback to older player hook types
|
||||
public static final ReportType REPORT_PLAYER_HOOK_FAILED = new ReportType("Player hook %s failed.");
|
||||
public static final ReportType REPORT_SWITCHED_PLAYER_HOOK = new ReportType("Switching to %s instead.");
|
||||
|
||||
public static final ReportType REPORT_HOOK_CLEANUP_FAILED = new ReportType("Cleaing up after player hook failed.");
|
||||
public static final ReportType REPORT_CANNOT_REVERT_HOOK = new ReportType("Unable to fully revert old injector. May cause conflicts.");
|
||||
|
||||
// Server connection injection
|
||||
private InjectedServerConnection serverInjection;
|
||||
|
||||
// Server socket injection
|
||||
private AbstractInputStreamLookup inputStreamLookup;
|
||||
|
||||
// NetLogin injector
|
||||
private NetLoginInjector netLoginInjector;
|
||||
|
||||
// The last successful player hook
|
||||
private WeakReference<PlayerInjector> lastSuccessfulHook;
|
||||
|
||||
// Dummy injection
|
||||
private ConcurrentMap<Player, PlayerInjector> dummyInjectors =
|
||||
SafeCacheBuilder.newBuilder().
|
||||
expireAfterWrite(30, TimeUnit.SECONDS).
|
||||
build(BlockingHashMap.<Player, PlayerInjector>newInvalidCacheLoader());
|
||||
|
||||
// Player injection
|
||||
private Map<Player, PlayerInjector> playerInjection = Maps.newConcurrentMap();
|
||||
|
||||
// Player injection types
|
||||
private volatile PlayerInjectHooks loginPlayerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
private volatile PlayerInjectHooks playingPlayerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||
|
||||
// Error reporter
|
||||
private ErrorReporter reporter;
|
||||
|
||||
// Whether or not we're closing
|
||||
private boolean hasClosed;
|
||||
|
||||
// Used to invoke events
|
||||
private ListenerInvoker invoker;
|
||||
|
||||
// Current Minecraft version
|
||||
private MinecraftVersion version;
|
||||
|
||||
// Enabled packet filters
|
||||
private IntegerSet sendingFilters = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
|
||||
|
||||
// List of packet listeners
|
||||
private Set<PacketListener> packetListeners;
|
||||
|
||||
// Used to filter injection attempts
|
||||
private Predicate<GamePhase> injectionFilter;
|
||||
|
||||
public ProxyPlayerInjectionHandler(
|
||||
ErrorReporter reporter, Predicate<GamePhase> injectionFilter,
|
||||
ListenerInvoker invoker, Set<PacketListener> packetListeners, Server server, MinecraftVersion version) {
|
||||
|
||||
this.reporter = reporter;
|
||||
this.invoker = invoker;
|
||||
this.injectionFilter = injectionFilter;
|
||||
this.packetListeners = packetListeners;
|
||||
this.version = version;
|
||||
|
||||
this.inputStreamLookup = InputStreamLookupBuilder.newBuilder().
|
||||
server(server).
|
||||
reporter(reporter).
|
||||
build();
|
||||
|
||||
// Create net login injectors and the server connection injector
|
||||
this.netLoginInjector = new NetLoginInjector(reporter, server, this);
|
||||
this.serverInjection = new InjectedServerConnection(reporter, inputStreamLookup, server, netLoginInjector);
|
||||
serverInjection.injectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player player) {
|
||||
// Just use the server version
|
||||
return MinecraftProtocolVersion.getCurrentVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves how the server packets are read.
|
||||
* @return Injection method for reading server packets.
|
||||
*/
|
||||
@Override
|
||||
public PlayerInjectHooks getPlayerHook() {
|
||||
return getPlayerHook(GamePhase.PLAYING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves how the server packets are read.
|
||||
* @param phase - the current game phase.
|
||||
* @return Injection method for reading server packets.
|
||||
*/
|
||||
@Override
|
||||
public PlayerInjectHooks getPlayerHook(GamePhase phase) {
|
||||
switch (phase) {
|
||||
case LOGIN:
|
||||
return loginPlayerHook;
|
||||
case PLAYING:
|
||||
return playingPlayerHook;
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot retrieve injection hook for both phases at the same time.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(PacketType type) {
|
||||
return sendingFilters.contains(type.getLegacyId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how the server packets are read.
|
||||
* @param playerHook - the new injection method for reading server packets.
|
||||
*/
|
||||
@Override
|
||||
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||
setPlayerHook(GamePhase.PLAYING, playerHook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how the server packets are read.
|
||||
* @param phase - the current game phase.
|
||||
* @param playerHook - the new injection method for reading server packets.
|
||||
*/
|
||||
@Override
|
||||
public void setPlayerHook(GamePhase phase, PlayerInjectHooks playerHook) {
|
||||
if (phase.hasLogin())
|
||||
loginPlayerHook = playerHook;
|
||||
if (phase.hasPlaying())
|
||||
playingPlayerHook = playerHook;
|
||||
|
||||
// Make sure the current listeners are compatible
|
||||
checkListener(packetListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
sendingFilters.add(type.getLegacyId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketHandler(PacketType type) {
|
||||
sendingFilters.remove(type.getLegacyId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to construct a player hook.
|
||||
* @param player - the player to hook.
|
||||
* @param hook - the hook type.
|
||||
* @return A new player hoook
|
||||
* @throws IllegalAccessException Unable to do our reflection magic.
|
||||
*/
|
||||
private PlayerInjector getHookInstance(Player player, PlayerInjectHooks hook) throws IllegalAccessException {
|
||||
// Construct the correct player hook
|
||||
switch (hook) {
|
||||
case NETWORK_HANDLER_FIELDS:
|
||||
return new NetworkFieldInjector(reporter, player, invoker, sendingFilters);
|
||||
case NETWORK_MANAGER_OBJECT:
|
||||
return new NetworkObjectInjector(reporter, player, invoker, sendingFilters);
|
||||
case NETWORK_SERVER_OBJECT:
|
||||
return new NetworkServerInjector(reporter, player, invoker, sendingFilters, serverInjection);
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot construct a player injector.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a player by its DataInput connection.
|
||||
* @param inputStream - the associated DataInput connection.
|
||||
* @return The player we found.
|
||||
*/
|
||||
@Override
|
||||
public Player getPlayerByConnection(DataInputStream inputStream) {
|
||||
// Wait until the connection owner has been established
|
||||
SocketInjector injector = inputStreamLookup.waitSocketInjector(inputStream);
|
||||
|
||||
if (injector != null) {
|
||||
return injector.getPlayer();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that retrieves the injector type of a given player injector.
|
||||
* @param injector - injector type.
|
||||
* @return The injector type.
|
||||
*/
|
||||
private PlayerInjectHooks getInjectorType(PlayerInjector injector) {
|
||||
return injector != null ? injector.getHookType() : PlayerInjectHooks.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a player hook, allowing us to read server packets.
|
||||
* <p>
|
||||
* This call will be ignored if there's no listener that can receive the given events.
|
||||
* @param player - player to hook.
|
||||
* @param strategy - how to handle previous player injections.
|
||||
*/
|
||||
@Override
|
||||
public void injectPlayer(Player player, ConflictStrategy strategy) {
|
||||
// Inject using the player instance itself
|
||||
if (isInjectionNecessary(GamePhase.PLAYING)) {
|
||||
injectPlayer(player, player, strategy, GamePhase.PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if it's truly necessary to perform the given player injection.
|
||||
* @param phase - current game phase.
|
||||
* @return TRUE if we should perform the injection, FALSE otherwise.
|
||||
*/
|
||||
public boolean isInjectionNecessary(GamePhase phase) {
|
||||
return injectionFilter.apply(phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a player hook, allowing us to read server packets.
|
||||
* <p>
|
||||
* This method will always perform the instructed injection.
|
||||
*
|
||||
* @param player - player to hook.
|
||||
* @param injectionPoint - the object to use during the injection process.
|
||||
* @param phase - the current game phase.
|
||||
* @return The resulting player injector, or NULL if the injection failed.
|
||||
*/
|
||||
PlayerInjector injectPlayer(Player player, Object injectionPoint, ConflictStrategy stategy, GamePhase phase) {
|
||||
if (player == null)
|
||||
throw new IllegalArgumentException("Player cannot be NULL.");
|
||||
if (injectionPoint == null)
|
||||
throw new IllegalArgumentException("injectionPoint cannot be NULL.");
|
||||
if (phase == null)
|
||||
throw new IllegalArgumentException("phase cannot be NULL.");
|
||||
|
||||
// Unfortunately, due to NetLoginHandler, multiple threads may potentially call this method.
|
||||
synchronized (player) {
|
||||
return injectPlayerInternal(player, injectionPoint, stategy, phase);
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe variant of the above
|
||||
private PlayerInjector injectPlayerInternal(Player player, Object injectionPoint, ConflictStrategy stategy, GamePhase phase) {
|
||||
PlayerInjector injector = playerInjection.get(player);
|
||||
PlayerInjectHooks tempHook = getPlayerHook(phase);
|
||||
PlayerInjectHooks permanentHook = tempHook;
|
||||
|
||||
// The given player object may be fake, so be careful!
|
||||
|
||||
// See if we need to inject something else
|
||||
boolean invalidInjector = injector != null ? !injector.canInject(phase) : true;
|
||||
|
||||
// Don't inject if the class has closed
|
||||
if (!hasClosed && (tempHook != getInjectorType(injector) || invalidInjector)) {
|
||||
while (tempHook != PlayerInjectHooks.NONE) {
|
||||
// Whether or not the current hook method failed completely
|
||||
boolean hookFailed = false;
|
||||
|
||||
// Remove the previous hook, if any
|
||||
cleanupHook(injector);
|
||||
|
||||
try {
|
||||
injector = getHookInstance(player, tempHook);
|
||||
|
||||
// Make sure this injection method supports the current game phase
|
||||
if (injector.canInject(phase)) {
|
||||
injector.initialize(injectionPoint);
|
||||
|
||||
// Get socket and socket injector
|
||||
SocketAddress address = injector.getAddress();
|
||||
|
||||
// Ignore logged out players
|
||||
if (address == null)
|
||||
return null;
|
||||
|
||||
SocketInjector previous = inputStreamLookup.peekSocketInjector(address);
|
||||
Socket socket = injector.getSocket();
|
||||
|
||||
// Close any previously associated hooks before we proceed
|
||||
if (previous != null && !(player instanceof Factory)) {
|
||||
switch (stategy) {
|
||||
case OVERRIDE:
|
||||
uninjectPlayer(previous.getPlayer(), true);
|
||||
break;
|
||||
case BAIL_OUT:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
injector.injectManager();
|
||||
|
||||
saveAddressLookup(address, socket, injector);
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (PlayerLoggedOutException e) {
|
||||
throw e;
|
||||
|
||||
} catch (Exception e) {
|
||||
// Mark this injection attempt as a failure
|
||||
reporter.reportDetailed(this,
|
||||
Report.newBuilder(REPORT_PLAYER_HOOK_FAILED).messageParam(tempHook).callerParam(player, injectionPoint, phase).error(e)
|
||||
);
|
||||
hookFailed = true;
|
||||
}
|
||||
|
||||
// Choose the previous player hook type
|
||||
tempHook = PlayerInjectHooks.values()[tempHook.ordinal() - 1];
|
||||
|
||||
if (hookFailed)
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_SWITCHED_PLAYER_HOOK).messageParam(tempHook));
|
||||
|
||||
// Check for UTTER FAILURE
|
||||
if (tempHook == PlayerInjectHooks.NONE) {
|
||||
cleanupHook(injector);
|
||||
injector = null;
|
||||
hookFailed = true;
|
||||
}
|
||||
|
||||
// Should we set the default hook method too?
|
||||
if (hookFailed) {
|
||||
permanentHook = tempHook;
|
||||
}
|
||||
}
|
||||
|
||||
// Update values
|
||||
if (injector != null)
|
||||
lastSuccessfulHook = new WeakReference<PlayerInjector>(injector);
|
||||
if (permanentHook != getPlayerHook(phase))
|
||||
setPlayerHook(phase, tempHook);
|
||||
|
||||
// Save injector
|
||||
if (injector != null) {
|
||||
playerInjection.put(player, injector);
|
||||
}
|
||||
}
|
||||
|
||||
return injector;
|
||||
}
|
||||
|
||||
private void saveAddressLookup(SocketAddress address, Socket socket, SocketInjector injector) {
|
||||
SocketAddress socketAddress = socket != null ? socket.getRemoteSocketAddress() : null;
|
||||
|
||||
if (socketAddress != null && !Objects.equal(socketAddress, address)) {
|
||||
// Save this version as well
|
||||
inputStreamLookup.setSocketInjector(socketAddress, injector);
|
||||
}
|
||||
// Save injector
|
||||
inputStreamLookup.setSocketInjector(address, injector);
|
||||
}
|
||||
|
||||
private void cleanupHook(PlayerInjector injector) {
|
||||
// Clean up as much as possible
|
||||
try {
|
||||
if (injector != null)
|
||||
injector.cleanupAll();
|
||||
} catch (Exception ex) {
|
||||
reporter.reportDetailed(this, Report.newBuilder(REPORT_HOOK_CLEANUP_FAILED).callerParam(injector).error(ex));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke special routines for handling disconnect before a player is uninjected.
|
||||
* @param player - player to process.
|
||||
*/
|
||||
@Override
|
||||
public void handleDisconnect(Player player) {
|
||||
PlayerInjector injector = getInjector(player);
|
||||
|
||||
if (injector != null) {
|
||||
injector.handleDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePlayer(Player player) {
|
||||
SocketAddress address = player.getAddress();
|
||||
|
||||
// Ignore logged out players
|
||||
if (address != null) {
|
||||
SocketInjector injector = inputStreamLookup.peekSocketInjector(address);
|
||||
|
||||
if (injector != null) {
|
||||
injector.setUpdatedPlayer(player);
|
||||
} else {
|
||||
inputStreamLookup.setSocketInjector(player.getAddress(),
|
||||
new BukkitSocketInjector(player));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given player.
|
||||
* @param player - player to unregister.
|
||||
* @return TRUE if a player has been uninjected, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean uninjectPlayer(Player player) {
|
||||
return uninjectPlayer(player, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given player.
|
||||
* @param player - player to unregister.
|
||||
* @param prepareNextHook - whether or not we need to fix any lingering hooks.
|
||||
* @return TRUE if a player has been uninjected, FALSE otherwise.
|
||||
*/
|
||||
private boolean uninjectPlayer(Player player, boolean prepareNextHook) {
|
||||
if (!hasClosed && player != null) {
|
||||
|
||||
PlayerInjector injector = playerInjection.remove(player);
|
||||
|
||||
if (injector != null) {
|
||||
injector.cleanupAll();
|
||||
|
||||
// Remove the "hooked" network manager in our instance as well
|
||||
if (prepareNextHook && injector instanceof NetworkObjectInjector) {
|
||||
try {
|
||||
PlayerInjector dummyInjector = getHookInstance(player, PlayerInjectHooks.NETWORK_SERVER_OBJECT);
|
||||
dummyInjector.initializePlayer(player);
|
||||
dummyInjector.setNetworkManager(injector.getNetworkManager(), true);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
// Let the user know
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_REVERT_HOOK).error(e));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a player by the given address.
|
||||
* <p>
|
||||
* If the server handler has been created before we've gotten a chance to unject the player,
|
||||
* the method will try a workaround to remove the injected hook in the NetServerHandler.
|
||||
*
|
||||
* @param address - address of the player to unregister.
|
||||
* @return TRUE if a player has been uninjected, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean uninjectPlayer(InetSocketAddress address) {
|
||||
if (!hasClosed && address != null) {
|
||||
SocketInjector injector = inputStreamLookup.peekSocketInjector(address);
|
||||
|
||||
// Clean up
|
||||
if (injector != null)
|
||||
uninjectPlayer(injector.getPlayer(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the given packet to the given receiver.
|
||||
* @param receiver - the player receiver.
|
||||
* @param packet - the packet to send.
|
||||
* @param filters - whether or not to invoke the packet filters.
|
||||
* @throws InvocationTargetException If an error occured during sending.
|
||||
*/
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||
SocketInjector injector = getInjector(receiver);
|
||||
|
||||
// Send the packet, or drop it completely
|
||||
if (injector != null) {
|
||||
injector.sendServerPacket(packet.getHandle(), marker, filters);
|
||||
} else {
|
||||
throw new PlayerLoggedOutException(String.format(
|
||||
"Unable to send packet %s (%s): Player %s has logged out.",
|
||||
packet.getType(), packet, receiver
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recieve a packet as if it were sent by the given player.
|
||||
* @param player - the sender.
|
||||
* @param mcPacket - the packet to process.
|
||||
* @throws IllegalAccessException If the reflection machinery failed.
|
||||
* @throws InvocationTargetException If the underlying method caused an error.
|
||||
*/
|
||||
@Override
|
||||
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||
PlayerInjector injector = getInjector(player);
|
||||
|
||||
// Process the given packet, or simply give up
|
||||
if (injector != null)
|
||||
injector.processPacket(mcPacket);
|
||||
else
|
||||
throw new PlayerLoggedOutException(String.format(
|
||||
"Unable to receieve packet %s. Player %s has logged out.",
|
||||
mcPacket, player
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the injector associated with this player.
|
||||
* @param player - the player to find.
|
||||
* @return The injector, or NULL if not found.
|
||||
*/
|
||||
private PlayerInjector getInjector(Player player) {
|
||||
PlayerInjector injector = playerInjection.get(player);
|
||||
|
||||
if (injector == null) {
|
||||
// Try getting it from the player itself
|
||||
SocketAddress address = player.getAddress();
|
||||
|
||||
// Must have logged out - there's nothing we can do
|
||||
if (address == null)
|
||||
return null;
|
||||
|
||||
// Look that up without blocking
|
||||
SocketInjector result = inputStreamLookup.peekSocketInjector(address);
|
||||
|
||||
// Ensure that it is non-null and a player injector
|
||||
if (result instanceof PlayerInjector)
|
||||
return (PlayerInjector) result;
|
||||
else
|
||||
// Make a dummy injector them
|
||||
return createDummyInjector(player);
|
||||
|
||||
} else {
|
||||
return injector;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a simple dummy injector incase none has been constructed.
|
||||
* @param player - the CraftPlayer to construct for.
|
||||
* @return A dummy injector, or NULL if the given player is not a CraftPlayer.
|
||||
*/
|
||||
private PlayerInjector createDummyInjector(Player player) {
|
||||
if (!MinecraftReflection.getCraftPlayerClass().isAssignableFrom(player.getClass())) {
|
||||
// No - this is not safe
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
PlayerInjector dummyInjector = getHookInstance(player, PlayerInjectHooks.NETWORK_SERVER_OBJECT);
|
||||
dummyInjector.initializePlayer(player);
|
||||
|
||||
// This probably means the player has disconnected
|
||||
if (dummyInjector.getSocket() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
inputStreamLookup.setSocketInjector(dummyInjector.getAddress(), dummyInjector);
|
||||
dummyInjectors.put(player, dummyInjector);
|
||||
return dummyInjector;
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot access fields.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a player injector by looking for its NetworkManager.
|
||||
* @param networkManager - current network manager.
|
||||
* @return Related player injector.
|
||||
*/
|
||||
PlayerInjector getInjectorByNetworkHandler(Object networkManager) {
|
||||
// That's not legal
|
||||
if (networkManager == null)
|
||||
return null;
|
||||
|
||||
// O(n) is okay in this instance. This is only a backup solution.
|
||||
for (PlayerInjector injector : playerInjection.values()) {
|
||||
if (injector.getNetworkManager() == networkManager)
|
||||
return injector;
|
||||
}
|
||||
|
||||
// None found
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRecievePackets() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent handlePacketRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
|
||||
throw new UnsupportedOperationException("Proxy injection cannot handle received packets.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given listeners are valid.
|
||||
* @param listeners - listeners to check.
|
||||
*/
|
||||
@Override
|
||||
public void checkListener(Set<PacketListener> listeners) {
|
||||
// Make sure the current listeners are compatible
|
||||
if (getLastSuccessfulHook() != null) {
|
||||
for (PacketListener listener : listeners) {
|
||||
checkListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last successful hook.
|
||||
* <p>
|
||||
* May be NULL if the hook has been uninjected.
|
||||
* @return Last successful hook.
|
||||
*/
|
||||
private PlayerInjector getLastSuccessfulHook() {
|
||||
return lastSuccessfulHook != null ? lastSuccessfulHook.get() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a listener is valid or not.
|
||||
* <p>
|
||||
* If not, a warning will be printed to the console.
|
||||
* @param listener - listener to check.
|
||||
*/
|
||||
@Override
|
||||
public void checkListener(PacketListener listener) {
|
||||
PlayerInjector last = getLastSuccessfulHook();
|
||||
|
||||
if (last != null) {
|
||||
UnsupportedListener result = last.checkListener(version, listener);
|
||||
|
||||
// We won't prevent the listener, as it may still have valid packets
|
||||
if (result != null) {
|
||||
reporter.reportWarning(this,
|
||||
Report.newBuilder(REPORT_UNSUPPPORTED_LISTENER).messageParam(PacketAdapter.getPluginName(listener), result)
|
||||
);
|
||||
|
||||
// These are illegal
|
||||
for (int packetID : result.getPackets()) {
|
||||
removePacketHandler(PacketType.findLegacy(packetID, Sender.CLIENT));
|
||||
removePacketHandler(PacketType.findLegacy(packetID, Sender.SERVER));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current list of registered sending listeners.
|
||||
* @return List of the sending listeners's packet IDs.
|
||||
*/
|
||||
@Override
|
||||
public Set<PacketType> getSendingFilters() {
|
||||
return PacketRegistry.toPacketTypes(sendingFilters.toSet(), Sender.SERVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Guard
|
||||
if (hasClosed || playerInjection == null)
|
||||
return;
|
||||
|
||||
// Remove everything
|
||||
for (PlayerInjector injection : playerInjection.values()) {
|
||||
if (injection != null) {
|
||||
injection.cleanupAll();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove server handler
|
||||
if (inputStreamLookup != null)
|
||||
inputStreamLookup.cleanupAll();
|
||||
if (serverInjection != null)
|
||||
serverInjection.cleanupAll();
|
||||
if (netLoginInjector != null)
|
||||
netLoginInjector.cleanupAll();
|
||||
inputStreamLookup = null;
|
||||
serverInjection = null;
|
||||
netLoginInjector = null;
|
||||
hasClosed = true;
|
||||
|
||||
playerInjection.clear();
|
||||
invoker = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel(Player player) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -1,370 +0,0 @@
|
||||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
|
||||
/**
|
||||
* Represents an array list that wraps another list, while automatically replacing one element with another.
|
||||
* <p>
|
||||
* The replaced elements can be recovered.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <TKey> - type of the elements we're replacing.
|
||||
*/
|
||||
class ReplacedArrayList<TKey> extends ArrayList<TKey> {
|
||||
/**
|
||||
* Generated by Eclipse.
|
||||
*/
|
||||
private static final long serialVersionUID = 1008492765999744804L;
|
||||
|
||||
private BiMap<TKey, TKey> replaceMap = HashBiMap.create();
|
||||
private List<TKey> underlyingList;
|
||||
|
||||
public ReplacedArrayList(List<TKey> underlyingList) {
|
||||
this.underlyingList = underlyingList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a element inserted is replaced.
|
||||
* @param inserting - the element inserted.
|
||||
* @param replacement - the element that it should replace.
|
||||
*/
|
||||
protected void onReplacing(TKey inserting, TKey replacement) {
|
||||
// Default is to do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when an element is being inserted.
|
||||
* <p>
|
||||
* This should be used to add a "replace" map.
|
||||
* @param inserting - the element to insert.
|
||||
*/
|
||||
protected void onInserting(TKey inserting) {
|
||||
// Default is again nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when an element is being removed.
|
||||
* @param removing - the element being removed.
|
||||
*/
|
||||
protected void onRemoved(TKey removing) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(TKey element) {
|
||||
onInserting(element);
|
||||
|
||||
if (replaceMap.containsKey(element)) {
|
||||
TKey replacement = replaceMap.get(element);
|
||||
onReplacing(element, replacement);
|
||||
return delegate().add(replacement);
|
||||
} else {
|
||||
return delegate().add(element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, TKey element) {
|
||||
onInserting(element);
|
||||
|
||||
if (replaceMap.containsKey(element)) {
|
||||
TKey replacement = replaceMap.get(element);
|
||||
onReplacing(element, replacement);
|
||||
delegate().add(index, replacement);
|
||||
} else {
|
||||
delegate().add(index, element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends TKey> collection) {
|
||||
int oldSize = size();
|
||||
|
||||
for (TKey element : collection)
|
||||
add(element);
|
||||
return size() != oldSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends TKey> elements) {
|
||||
int oldSize = size();
|
||||
|
||||
for (TKey element : elements)
|
||||
add(index++, element);
|
||||
return size() != oldSize;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean remove(Object object) {
|
||||
boolean success = delegate().remove(object);
|
||||
|
||||
if (success)
|
||||
onRemoved((TKey) object);
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TKey remove(int index) {
|
||||
TKey removed = delegate().remove(index);
|
||||
|
||||
if (removed != null)
|
||||
onRemoved(removed);
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> collection) {
|
||||
int oldSize = size();
|
||||
|
||||
// Use the remove method
|
||||
for (Object element : collection)
|
||||
remove(element);
|
||||
return size() != oldSize;
|
||||
}
|
||||
|
||||
protected List<TKey> delegate() {
|
||||
return underlyingList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for (TKey element : delegate())
|
||||
onRemoved(element);
|
||||
|
||||
delegate().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return delegate().contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return delegate().containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TKey get(int index) {
|
||||
return delegate().get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return delegate().indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return delegate().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<TKey> iterator() {
|
||||
return delegate().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return delegate().lastIndexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<TKey> listIterator() {
|
||||
return delegate().listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<TKey> listIterator(int index) {
|
||||
return delegate().listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
int oldSize = size();
|
||||
|
||||
for (Iterator<TKey> it = delegate().iterator(); it.hasNext(); ) {
|
||||
TKey current = it.next();
|
||||
|
||||
// Remove elements that are not in the list
|
||||
if (!c.contains(current)) {
|
||||
it.remove();
|
||||
onRemoved(current);
|
||||
}
|
||||
}
|
||||
return size() != oldSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TKey set(int index, TKey element) {
|
||||
// Make sure to replace the element
|
||||
if (replaceMap.containsKey(element)) {
|
||||
TKey replacement = replaceMap.get(element);
|
||||
onReplacing(element, replacement);
|
||||
return delegate().set(index, replacement);
|
||||
} else {
|
||||
return delegate().set(index, element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TKey> subList(int fromIndex, int toIndex) {
|
||||
return delegate().subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return delegate().toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return delegate().toArray(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a replace rule.
|
||||
* <p>
|
||||
* This automatically replaces every existing element.
|
||||
* @param target - instance to find.
|
||||
* @param replacement - instance to replace with.
|
||||
*/
|
||||
public synchronized void addMapping(TKey target, TKey replacement) {
|
||||
addMapping(target, replacement, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the old value, if it exists.
|
||||
* @param target - the key.
|
||||
* @return The value that was replaced, or NULL.
|
||||
*/
|
||||
public TKey getMapping(TKey target) {
|
||||
return replaceMap.get(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a replace rule.
|
||||
* <p>
|
||||
* This automatically replaces every existing element.
|
||||
* @param target - instance to find.
|
||||
* @param replacement - instance to replace with.
|
||||
* @param ignoreExisting - whether or not to ignore the existing elements.
|
||||
*/
|
||||
public synchronized void addMapping(TKey target, TKey replacement, boolean ignoreExisting) {
|
||||
replaceMap.put(target, replacement);
|
||||
|
||||
// Replace existing elements
|
||||
if (!ignoreExisting) {
|
||||
replaceAll(target, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the given mapping.
|
||||
* @param target - the instance we replaced.
|
||||
* @return The old mapped value, or NULL if nothing was replaced.
|
||||
*/
|
||||
public synchronized TKey removeMapping(TKey target) {
|
||||
// Make sure the mapping exist
|
||||
if (replaceMap.containsKey(target)) {
|
||||
TKey replacement = replaceMap.get(target);
|
||||
replaceMap.remove(target);
|
||||
|
||||
// Revert existing elements
|
||||
replaceAll(replacement, target);
|
||||
return replacement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the new replaced value with its old value.
|
||||
* @param target - the instance we replaced.
|
||||
* @param The old mapped value, or NULL if nothing was swapped.
|
||||
*/
|
||||
public synchronized TKey swapMapping(TKey target) {
|
||||
// Make sure the mapping exist
|
||||
TKey replacement = removeMapping(target);
|
||||
|
||||
// Add the reverse
|
||||
if (replacement != null) {
|
||||
replaceMap.put(replacement, target);
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all instances of the given object.
|
||||
* @param find - object to find.
|
||||
* @param replace - object to replace it with.
|
||||
*/
|
||||
public synchronized void replaceAll(TKey find, TKey replace) {
|
||||
for (int i = 0; i < underlyingList.size(); i++) {
|
||||
if (Objects.equal(underlyingList.get(i), find)) {
|
||||
onReplacing(find, replace);
|
||||
underlyingList.set(i, replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo all replacements.
|
||||
*/
|
||||
public synchronized void revertAll() {
|
||||
|
||||
// No need to do anything else
|
||||
if (replaceMap.size() < 1)
|
||||
return;
|
||||
|
||||
BiMap<TKey, TKey> inverse = replaceMap.inverse();
|
||||
|
||||
for (int i = 0; i < underlyingList.size(); i++) {
|
||||
TKey replaced = underlyingList.get(i);
|
||||
|
||||
if (inverse.containsKey(replaced)) {
|
||||
TKey original = inverse.get(replaced);
|
||||
onReplacing(replaced, original);
|
||||
underlyingList.set(i, original);
|
||||
}
|
||||
}
|
||||
|
||||
replaceMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
revertAll();
|
||||
super.finalize();
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
/**
|
||||
* Represents an error message from a player injector.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class UnsupportedListener {
|
||||
private String message;
|
||||
private int[] packets;
|
||||
|
||||
/**
|
||||
* Create a new error message.
|
||||
* @param message - the message.
|
||||
* @param packets - unsupported packets.
|
||||
*/
|
||||
public UnsupportedListener(String message, int[] packets) {
|
||||
super();
|
||||
this.message = message;
|
||||
this.packets = packets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the error message.
|
||||
* @return Error message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all unsupported packets.
|
||||
* @return Unsupported packets.
|
||||
*/
|
||||
public int[] getPackets() {
|
||||
return packets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s (%s)", message, Joiner.on(", ").join(Arrays.asList(packets)));
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package com.comphenix.protocol.injector.spigot;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Dummy packet injector that simply delegates to its parent Spigot packet injector or receiving filters.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class DummyPacketInjector extends AbstractPacketInjector {
|
||||
private SpigotPacketInjector injector;
|
||||
private PacketTypeSet lastBufferedPackets = new PacketTypeSet();
|
||||
|
||||
public DummyPacketInjector(SpigotPacketInjector injector, PacketTypeSet reveivedFilters) {
|
||||
super(reveivedFilters);
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputBuffersChanged(Set<PacketType> set) {
|
||||
Set<PacketType> removed = Sets.difference(lastBufferedPackets.values(), set);
|
||||
Set<PacketType> added = Sets.difference(set, lastBufferedPackets.values());
|
||||
|
||||
// Update the proxy packet injector
|
||||
for (PacketType packet : removed) {
|
||||
injector.getProxyPacketInjector().removePacketHandler(packet);
|
||||
}
|
||||
for (PacketType packet : added) {
|
||||
injector.getProxyPacketInjector().addPacketHandler(packet, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||
return injector.packetReceived(packet, client, buffered);
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package com.comphenix.protocol.injector.spigot;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
|
||||
/**
|
||||
* Dummy player handler that simply delegates to its parent Spigot packet injector.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class DummyPlayerHandler extends AbstractPlayerHandler {
|
||||
private SpigotPacketInjector injector;
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player player) {
|
||||
// Just use the server version
|
||||
return MinecraftProtocolVersion.getCurrentVersion();
|
||||
}
|
||||
|
||||
public DummyPlayerHandler(SpigotPacketInjector injector, PacketTypeSet sendingFilters) {
|
||||
super(sendingFilters);
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uninjectPlayer(InetSocketAddress address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uninjectPlayer(Player player) {
|
||||
injector.uninjectPlayer(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||
injector.sendServerPacket(receiver, packet, marker, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recieveClientPacket(Player player, Object mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||
injector.processPacket(player, mcPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectPlayer(Player player, ConflictStrategy strategy) {
|
||||
// We don't care about strategy
|
||||
injector.injectPlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(PacketType type) {
|
||||
return sendingFilters.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect(Player player) {
|
||||
// Just ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent handlePacketRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
|
||||
// Associate this buffered data
|
||||
if (buffered != null) {
|
||||
injector.saveBuffered(packet.getHandle(), buffered);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePlayer(Player player) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel(Player player) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -1,672 +0,0 @@
|
||||
package com.comphenix.protocol.injector.spigot;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
import net.sf.cglib.proxy.NoOp;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.error.DelegatedErrorReporter;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||
import com.comphenix.protocol.injector.packet.LegacyNetworkMarker;
|
||||
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.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.MethodInfo;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Offload all the work to Spigot, if possible.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class SpigotPacketInjector implements SpigotPacketListener {
|
||||
public static final ReportType REPORT_CANNOT_CLEANUP_SPIGOT = new ReportType("Cannot cleanup Spigot listener.");
|
||||
|
||||
// Lazily retrieve the spigot listener class
|
||||
private static volatile Class<?> spigotListenerClass;
|
||||
private static volatile boolean classChecked;
|
||||
|
||||
// Retrieve the entity player from a PlayerConnection
|
||||
private static volatile Field playerConnectionPlayer;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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 PacketTypeSet queuedFilters;
|
||||
private PacketTypeSet reveivedFilters;
|
||||
|
||||
// NetworkManager to injector and player
|
||||
private ConcurrentMap<Object, NetworkObjectInjector> networkManagerInjector = Maps.newConcurrentMap();
|
||||
|
||||
// Player to injector
|
||||
private ConcurrentMap<Player, NetworkObjectInjector> playerInjector = Maps.newConcurrentMap();
|
||||
|
||||
// For handling read buffered packet data
|
||||
private Map<Object, byte[]> readBufferedPackets = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
// Responsible for informing the PL packet listeners
|
||||
private ListenerInvoker invoker;
|
||||
private ErrorReporter reporter;
|
||||
private Server server;
|
||||
|
||||
// The proxy packet injector
|
||||
private PacketInjector proxyPacketInjector;
|
||||
|
||||
// Background task
|
||||
private static final int BACKGROUND_DELAY = 30 * PacketFilterManager.TICKS_PER_SECOND;
|
||||
private int backgroundId;
|
||||
|
||||
/**
|
||||
* Create a new spigot injector.
|
||||
* @param reporter - error reporter
|
||||
* @param invoker - listener invoker
|
||||
* @param server - server
|
||||
*/
|
||||
public SpigotPacketInjector(ErrorReporter reporter, ListenerInvoker invoker, Server server) {
|
||||
this.reporter = reporter;
|
||||
this.invoker = invoker;
|
||||
this.server = server;
|
||||
this.queuedFilters = new PacketTypeSet();
|
||||
this.reveivedFilters = new PacketTypeSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying listener invoker.
|
||||
* @return The invoker.
|
||||
*/
|
||||
public ListenerInvoker getInvoker() {
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the real proxy packet injector.
|
||||
* @param proxyPacketInjector - the real injector.
|
||||
*/
|
||||
public void setProxyPacketInjector(PacketInjector proxyPacketInjector) {
|
||||
this.proxyPacketInjector = proxyPacketInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the real proxy packet injector.
|
||||
* @return The real injector.
|
||||
*/
|
||||
public PacketInjector getProxyPacketInjector() {
|
||||
return proxyPacketInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Spigot packet injector.
|
||||
* @param plugin - the parent plugin.
|
||||
* @return TRUE if we registered the plugin, FALSE otherwise.
|
||||
*/
|
||||
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 = EnhancerFactory.getInstance().createEnhancer();
|
||||
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);
|
||||
}
|
||||
|
||||
// Remember to register background task
|
||||
backgroundId = createBackgroundTask();
|
||||
|
||||
// If we succeed
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and register a background task.
|
||||
* @return The background task ID.
|
||||
*/
|
||||
private int createBackgroundTask() {
|
||||
return Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cleanupInjectors();
|
||||
}
|
||||
}, BACKGROUND_DELAY, BACKGROUND_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that all disconnected injectors are removed from memory.
|
||||
*/
|
||||
private void cleanupInjectors() {
|
||||
for (NetworkObjectInjector injector : networkManagerInjector.values()) {
|
||||
try {
|
||||
if (injector.getSocket() != null && injector.getSocket().isClosed()) {
|
||||
cleanupInjector(injector);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reporter.reportMinimal(plugin, "cleanupInjectors", e);
|
||||
|
||||
// What?
|
||||
cleanupInjector(injector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a given network object injector.
|
||||
* @param injector - the injector.
|
||||
*/
|
||||
private void cleanupInjector(final NetworkObjectInjector injector) {
|
||||
// Clean up
|
||||
playerInjector.remove(injector.getPlayer());
|
||||
playerInjector.remove(injector.getUpdatedPlayer());
|
||||
networkManagerInjector.remove(injector.getNetworkManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the Spigot packet listener has been registered.
|
||||
* @return TRUE if it has, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasRegistered() {
|
||||
return dynamicListener != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the dummy player injection handler.
|
||||
* @return Dummy player injection handler.
|
||||
*/
|
||||
public PlayerInjectionHandler getPlayerHandler() {
|
||||
return new DummyPlayerHandler(this, queuedFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the dummy packet injection handler.
|
||||
* @return Dummy packet injection handler.
|
||||
*/
|
||||
public PacketInjector getPacketInjector() {
|
||||
return new DummyPacketInjector(this, reveivedFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the currently registered injector for the given player.
|
||||
* @param player - injected player.
|
||||
* @param createNew - whether or not to create a new injector if the current is missing.
|
||||
* @return The injector.
|
||||
*/
|
||||
NetworkObjectInjector getInjector(Player player, boolean createNew) {
|
||||
NetworkObjectInjector injector = playerInjector.get(player);
|
||||
|
||||
if (injector == null && createNew) {
|
||||
// Check for temporary players ..
|
||||
if ((player instanceof Factory))
|
||||
throw new IllegalArgumentException("Cannot inject tempoary player " + player);
|
||||
|
||||
try {
|
||||
NetworkObjectInjector created = new NetworkObjectInjector(
|
||||
filterImpossibleWarnings(reporter), null, invoker, null);
|
||||
|
||||
created.initializePlayer(player);
|
||||
|
||||
if (created.getNetworkManager() == null)
|
||||
throw new PlayerLoggedOutException("Player " + player + " has logged out.");
|
||||
injector = saveInjector(created.getNetworkManager(), created);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot create dummy injector.", e);
|
||||
}
|
||||
}
|
||||
return injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
filterImpossibleWarnings(reporter), null, invoker, null);
|
||||
|
||||
if (MinecraftReflection.isLoginHandler(connection)) {
|
||||
created.initialize(connection);
|
||||
created.setPlayer(created.createTemporaryPlayer(server));
|
||||
} else if (MinecraftReflection.isServerHandler(connection)) {
|
||||
// Get the player instead
|
||||
if (playerConnectionPlayer == null)
|
||||
playerConnectionPlayer = FuzzyReflection.fromObject(connection).
|
||||
getFieldByType("player", MinecraftReflection.getEntityPlayerClass());
|
||||
Object entityPlayer = playerConnectionPlayer.get(connection);
|
||||
|
||||
created.initialize(MinecraftReflection.getBukkitEntity(entityPlayer));
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unregonized connection in NetworkManager.");
|
||||
}
|
||||
|
||||
dummyInjector = saveInjector(networkManager, created);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot create dummy injector.", e);
|
||||
}
|
||||
}
|
||||
|
||||
return dummyInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a delegated error reporter that ignores certain warnings that are irrelevant on Spigot.
|
||||
* @param reporter - error reporter to delegate.
|
||||
* @return The filtered error reporter.
|
||||
*/
|
||||
private ErrorReporter filterImpossibleWarnings(ErrorReporter reporter) {
|
||||
return new DelegatedErrorReporter(reporter) {
|
||||
@Override
|
||||
protected Report filterReport(Object sender, Report report, boolean detailed) {
|
||||
// This doesn't matter - ignore it
|
||||
if (report.getType() == NetworkObjectInjector.REPORT_DETECTED_CUSTOM_SERVER_HANDLER)
|
||||
return null;
|
||||
return report;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the buffered serialized input packet.
|
||||
* @param handle - the associated packet.
|
||||
* @param buffered - the buffere data to save.
|
||||
*/
|
||||
public void saveBuffered(Object handle, byte[] buffered) {
|
||||
readBufferedPackets.put(handle, buffered);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object packetReceived(Object networkManager, Object connection, Object packet) {
|
||||
if (reveivedFilters.contains(packet.getClass())) {
|
||||
@SuppressWarnings("deprecation")
|
||||
Integer id = invoker.getPacketID(packet);
|
||||
|
||||
// Check for ignored packets
|
||||
if (ignoredPackets.remove(packet)) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
Player sender = getInjector(networkManager, connection).getUpdatedPlayer();
|
||||
PacketType type = PacketType.findLegacy(id, Sender.CLIENT);
|
||||
PacketContainer container = new PacketContainer(type, packet);
|
||||
PacketEvent event = packetReceived(container, sender, readBufferedPackets.get(packet));
|
||||
|
||||
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) {
|
||||
if (queuedFilters.contains(packet.getClass())) {
|
||||
@SuppressWarnings("deprecation")
|
||||
Integer id = invoker.getPacketID(packet);
|
||||
|
||||
// Check for ignored packets
|
||||
if (ignoredPackets.remove(packet)) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
Player reciever = getInjector(networkManager, connection).getUpdatedPlayer();
|
||||
PacketType type = PacketType.findLegacy(id, Sender.SERVER);
|
||||
PacketContainer container = new PacketContainer(type, 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 receiver - the receiver of this packet.
|
||||
* @return The packet event that was used.
|
||||
*/
|
||||
PacketEvent packetQueued(PacketContainer packet, Player receiver) {
|
||||
PacketEvent event = PacketEvent.fromServer(this, packet, receiver);
|
||||
|
||||
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, byte[] buffered) {
|
||||
NetworkMarker marker = buffered != null ? new LegacyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered, packet.getType()) : null;
|
||||
PacketEvent event = PacketEvent.fromClient(this, packet, marker, 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(
|
||||
filterImpossibleWarnings(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, false);
|
||||
|
||||
if (player != null && injector != null) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cleanupInjector(injector);
|
||||
}
|
||||
}, CLEANUP_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a plugin wants to sent a packet.
|
||||
* @param receiver - the packet receiver.
|
||||
* @param packet - the packet to transmit.
|
||||
* @param marker - the network marker object.
|
||||
* @param filters - whether or not to invoke the packet listeners.
|
||||
* @throws InvocationTargetException If anything went wrong.
|
||||
*/
|
||||
void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
|
||||
NetworkObjectInjector networkObject = getInjector(receiver, true);
|
||||
|
||||
// If TRUE, process this packet like any other
|
||||
if (filters)
|
||||
ignoredPackets.remove(packet.getHandle());
|
||||
else
|
||||
ignoredPackets.add(packet.getHandle());
|
||||
|
||||
networkObject.sendServerPacket(packet.getHandle(), marker, filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, true);
|
||||
|
||||
// We will always ignore this packet
|
||||
ignoredPackets.add(mcPacket);
|
||||
networkObject.processPacket(mcPacket);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void cleanupListener() {
|
||||
Class<?> listenerClass = getSpigotListenerClass();
|
||||
|
||||
// Yeah ... there's no easy way to remove the listener
|
||||
synchronized (listenerClass) {
|
||||
try {
|
||||
Field listenersField = FieldUtils.getField(listenerClass, "listeners", true);
|
||||
Field bakedField = FieldUtils.getField(listenerClass, "baked", true);
|
||||
|
||||
Map<Object, Plugin> listenerMap = (Map<Object, Plugin>) listenersField.get(null);
|
||||
List<Object> listenerArray = Lists.newArrayList((Object[]) bakedField.get(null));
|
||||
|
||||
listenerMap.remove(dynamicListener);
|
||||
listenerArray.remove(dynamicListener);
|
||||
|
||||
// Save the array back
|
||||
bakedField.set(null, Iterables.toArray(listenerArray, (Class)listenerClass));
|
||||
|
||||
// Success
|
||||
dynamicListener = null;
|
||||
} catch (Exception e) {
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_CLEANUP_SPIGOT).
|
||||
callerParam(dynamicListener).error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the server is cleaning up.
|
||||
*/
|
||||
public void cleanupAll() {
|
||||
// Cleanup the Spigot listener
|
||||
if (dynamicListener != null) {
|
||||
cleanupListener();
|
||||
}
|
||||
if (backgroundId >= 0) {
|
||||
Bukkit.getScheduler().cancelTask(backgroundId);
|
||||
backgroundId = -1;
|
||||
}
|
||||
|
||||
// Cleanup network marker
|
||||
if (proxyPacketInjector != null) {
|
||||
proxyPacketInjector.cleanupAll();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
@ -1313,11 +1313,9 @@ public class MinecraftReflection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the WatchableObject class.
|
||||
* Retrieve the WatchableObject class. Replaced by {@link #getDataWatcherItemClass()}
|
||||
* @return The WatchableObject class.
|
||||
* @deprecated Replaced by {@link #getDataWatcherItemClass()}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Class<?> getWatchableObjectClass() {
|
||||
return getDataWatcherItemClass();
|
||||
}
|
||||
|
@ -257,11 +257,9 @@ public abstract class WrappedBlockData extends AbstractWrapper implements Clonab
|
||||
public abstract Material getType();
|
||||
|
||||
/**
|
||||
* Gets this BlockData's legacy data
|
||||
* Gets this BlockData's legacy data. Not recommended on 1.13+
|
||||
* @return The legacy data
|
||||
* @deprecated By the flattening in 1.13. Fine on lower versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract int getData();
|
||||
|
||||
/**
|
||||
@ -271,20 +269,16 @@ public abstract class WrappedBlockData extends AbstractWrapper implements Clonab
|
||||
public abstract void setType(Material material);
|
||||
|
||||
/**
|
||||
* Sets this BlockData's legacy data
|
||||
* Sets this BlockData's legacy data. Not recommended on 1.13+
|
||||
* @param data The new legacy data
|
||||
* @deprecated By the flattening in 1.13. Fine on lower versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void setData(int data);
|
||||
|
||||
/**
|
||||
* Sets this BlockData's type and legacy data
|
||||
* Sets this BlockData's type and legacy data. Not recommended on 1.13+
|
||||
* @param material The new Bukkit material
|
||||
* @param data The new legacy data
|
||||
* @deprecated By the flattening in 1.13. Fine on lower versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void setTypeAndData(Material material, int data);
|
||||
|
||||
public abstract WrappedBlockData deepClone();
|
||||
|
@ -16,7 +16,6 @@ import com.google.common.base.Preconditions;
|
||||
* @author Kristian
|
||||
* @deprecated IntHashMap no longer exists
|
||||
*/
|
||||
@Deprecated
|
||||
public class WrappedIntHashMap extends AbstractWrapper {
|
||||
private static Method PUT_METHOD;
|
||||
private static Method GET_METHOD;
|
||||
|
Loading…
Reference in New Issue
Block a user