Initial commit.
Dieser Commit ist enthalten in:
Commit
c65f6b006f
14
ProtocolLib/.classpath
Normale Datei
14
ProtocolLib/.classpath
Normale Datei
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" output="class_test" path="test"/>
|
||||||
|
<classpathentry kind="lib" path="D:/Games/Minecraft/Server Mods/Server/craftbukkit-1.3.1-R2.0.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/cglib-nodep-2.2.3.jar">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="javadoc_location" value="jar:file:/D:/Kristian/Programmer/Binaries/cglib.2.2.3/cglib-docs-2.2.3.jar!/"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jre6"/>
|
||||||
|
<classpathentry kind="output" path="class"/>
|
||||||
|
</classpath>
|
17
ProtocolLib/.project
Normale Datei
17
ProtocolLib/.project
Normale Datei
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>ProtocolLib</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
11
ProtocolLib/.settings/org.eclipse.jdt.core.prefs
Normale Datei
11
ProtocolLib/.settings/org.eclipse.jdt.core.prefs
Normale Datei
@ -0,0 +1,11 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.6
|
14
ProtocolLib/Java.xml
Normale Datei
14
ProtocolLib/Java.xml
Normale Datei
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<project default="compile" name="Project ItemDisguise" basedir=".">
|
||||||
|
<!--ANT 1.7 is required -->
|
||||||
|
<target name="compile">
|
||||||
|
<jar destfile="bin/ProtocolLib.jar" filesetmanifest="mergewithoutmain" excludes="test/*">
|
||||||
|
<manifest>
|
||||||
|
<attribute name="Main-Class" value="com.comphenix.itemdisguise.Application"/>
|
||||||
|
<attribute name="Class-Path" value="."/>
|
||||||
|
</manifest>
|
||||||
|
<fileset dir="class"/>
|
||||||
|
<zipfileset excludes="META-INF/*.SF" src="lib/cglib-nodep-2.2.3.jar"/>
|
||||||
|
</jar>
|
||||||
|
</target>
|
||||||
|
</project>
|
BIN
ProtocolLib/lib/cglib-nodep-2.2.3.jar
Normale Datei
BIN
ProtocolLib/lib/cglib-nodep-2.2.3.jar
Normale Datei
Binäre Datei nicht angezeigt.
8
ProtocolLib/src/com/comphenix/protocol/Application.java
Normale Datei
8
ProtocolLib/src/com/comphenix/protocol/Application.java
Normale Datei
@ -0,0 +1,8 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
public class Application {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// For now, though we might consider making a proper application
|
||||||
|
System.out.println("This is a Bukkit library. Place it in the plugin-folder and restart the server!");
|
||||||
|
}
|
||||||
|
}
|
63
ProtocolLib/src/com/comphenix/protocol/ProtocolLibrary.java
Normale Datei
63
ProtocolLib/src/com/comphenix/protocol/ProtocolLibrary.java
Normale Datei
@ -0,0 +1,63 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.injector.PacketFilterManager;
|
||||||
|
|
||||||
|
public class ProtocolLibrary extends JavaPlugin {
|
||||||
|
|
||||||
|
// There should only be one protocol manager, so we'll make it static
|
||||||
|
private static PacketFilterManager protocolManager;
|
||||||
|
|
||||||
|
// Error logger
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
logger = getLoggerSafely();
|
||||||
|
protocolManager = new PacketFilterManager(getClassLoader(), logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
Server server = getServer();
|
||||||
|
PluginManager manager = server.getPluginManager();
|
||||||
|
|
||||||
|
// Player login and logout events
|
||||||
|
protocolManager.registerEvents(manager, this);
|
||||||
|
protocolManager.initializePlayers(server.getOnlinePlayers());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
protocolManager.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the packet protocol manager.
|
||||||
|
* @return Packet protocol manager.
|
||||||
|
*/
|
||||||
|
public static ProtocolManager getProtocolManager() {
|
||||||
|
return protocolManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Bukkit logger first, before we try to create our own
|
||||||
|
private Logger getLoggerSafely() {
|
||||||
|
|
||||||
|
Logger log = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log = getLogger();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// We'll handle it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log == null)
|
||||||
|
log = Logger.getLogger("Minecraft");
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
}
|
90
ProtocolLib/src/com/comphenix/protocol/ProtocolManager.java
Normale Datei
90
ProtocolLib/src/com/comphenix/protocol/ProtocolManager.java
Normale Datei
@ -0,0 +1,90 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
public interface ProtocolManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of every registered packet listener.
|
||||||
|
* @return Every registered packet listener.
|
||||||
|
*/
|
||||||
|
public abstract ImmutableSet<PacketListener> getPacketListeners();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a packet listener.
|
||||||
|
* @param listener - new packet listener.
|
||||||
|
*/
|
||||||
|
public abstract void addPacketListener(PacketListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a given packet listener.
|
||||||
|
* @param listener - the packet listener to remove.
|
||||||
|
*/
|
||||||
|
public abstract void removePacketListener(PacketListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet to the given player.
|
||||||
|
* @param reciever - the reciever.
|
||||||
|
* @param packet - packet to send.
|
||||||
|
* @throws InvocationTargetException - if an error occured when sending the packet.
|
||||||
|
*/
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet)
|
||||||
|
throws InvocationTargetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet to the given player.
|
||||||
|
* @param reciever - the reciever.
|
||||||
|
* @param packet - packet to send.
|
||||||
|
* @param filters - whether or not to invoke any packet filters.
|
||||||
|
* @throws InvocationTargetException - if an error occured when sending the packet.
|
||||||
|
*/
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters)
|
||||||
|
throws InvocationTargetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate recieving a certain packet from a given player.
|
||||||
|
* @param sender - the sender.
|
||||||
|
* @param packet - the packet that was sent.
|
||||||
|
* @throws InvocationTargetException If the reflection machinery failed.
|
||||||
|
* @throws IllegalAccessException If the underlying method caused an error.
|
||||||
|
*/
|
||||||
|
public void recieveClientPacket(Player sender, PacketContainer packet)
|
||||||
|
throws IllegalAccessException, InvocationTargetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate recieving a certain packet from a given player.
|
||||||
|
* @param sender - the sender.
|
||||||
|
* @param packet - the packet that was sent.
|
||||||
|
* @param filters - whether or not to invoke any packet filters.
|
||||||
|
* @throws InvocationTargetException If the reflection machinery failed.
|
||||||
|
* @throws IllegalAccessException If the underlying method caused an error.
|
||||||
|
*/
|
||||||
|
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
|
||||||
|
throws IllegalAccessException, InvocationTargetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new encapsulated Minecraft packet with the given ID.
|
||||||
|
* @param id - packet ID.
|
||||||
|
* @return New encapsulated Minecraft packet.
|
||||||
|
*/
|
||||||
|
public PacketContainer createPacket(int id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retieves a set of every enabled packet.
|
||||||
|
* @return Every packet filter.
|
||||||
|
*/
|
||||||
|
public Set<Integer> getPacketFilters();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not is protocol mananger has been disabled.
|
||||||
|
* @return TRUE if it has, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isClosed();
|
||||||
|
}
|
31
ProtocolLib/src/com/comphenix/protocol/events/ConnectionSide.java
Normale Datei
31
ProtocolLib/src/com/comphenix/protocol/events/ConnectionSide.java
Normale Datei
@ -0,0 +1,31 @@
|
|||||||
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to set a packet filter.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public enum ConnectionSide {
|
||||||
|
/**
|
||||||
|
* Listen for server side packets that will invoke onPacketSending().
|
||||||
|
*/
|
||||||
|
SERVER_SIDE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for client side packets that will invoke onPacketReceiving().
|
||||||
|
*/
|
||||||
|
CLIENT_SIDE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for both client and server side packets.
|
||||||
|
*/
|
||||||
|
BOTH;
|
||||||
|
|
||||||
|
public boolean isForClient() {
|
||||||
|
return this == CLIENT_SIDE || this == BOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForServer() {
|
||||||
|
return this == SERVER_SIDE || this == BOTH;
|
||||||
|
}
|
||||||
|
}
|
60
ProtocolLib/src/com/comphenix/protocol/events/PacketAdapter.java
Normale Datei
60
ProtocolLib/src/com/comphenix/protocol/events/PacketAdapter.java
Normale Datei
@ -0,0 +1,60 @@
|
|||||||
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a packet listener with useful constructors.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public abstract class PacketAdapter implements PacketListener {
|
||||||
|
|
||||||
|
protected JavaPlugin plugin;
|
||||||
|
protected Set<Integer> packetsID;
|
||||||
|
protected ConnectionSide connectionSide;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a packet listener.
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
public PacketAdapter(JavaPlugin plugin, ConnectionSide connectionSide, Integer... packets) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.connectionSide = connectionSide;
|
||||||
|
this.packetsID = Sets.newHashSet(packets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketReceiving(PacketEvent event) {
|
||||||
|
// Default is to do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketSending(PacketEvent event) {
|
||||||
|
// And here too
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionSide getConnectionSide() {
|
||||||
|
return connectionSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getPacketsID() {
|
||||||
|
return packetsID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// This is used by the error reporter
|
||||||
|
return String.format("PacketAdapter[plugin=%s, side=%s, packets=%s]",
|
||||||
|
plugin.getName(), getConnectionSide().name(),
|
||||||
|
StringUtils.join(packetsID, ", "));
|
||||||
|
}
|
||||||
|
}
|
126
ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java
Normale Datei
126
ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java
Normale Datei
@ -0,0 +1,126 @@
|
|||||||
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.NullArgumentException;
|
||||||
|
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.injector.StructureCache;
|
||||||
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Minecraft packet indirectly.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class PacketContainer {
|
||||||
|
|
||||||
|
protected Packet handle;
|
||||||
|
protected int id;
|
||||||
|
|
||||||
|
// Current structure modifier
|
||||||
|
protected StructureModifier<Object> structureModifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packet container for a new packet.
|
||||||
|
* @param id - ID of the packet to create.
|
||||||
|
*/
|
||||||
|
public PacketContainer(int id) {
|
||||||
|
this(id, StructureCache.newPacket(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packet container for an existing packet.
|
||||||
|
* @param id - ID of the given packet.
|
||||||
|
* @param handle - contained packet.
|
||||||
|
*/
|
||||||
|
public PacketContainer(int id, Packet handle) {
|
||||||
|
this(id, handle, StructureCache.getStructure(id).withTarget(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a packet container for an existing packet.
|
||||||
|
* @param id - ID of the given packet.
|
||||||
|
* @param handle - contained packet.
|
||||||
|
* @param structure - structure modifier.
|
||||||
|
*/
|
||||||
|
public PacketContainer(int id, Packet handle, StructureModifier<Object> structure) {
|
||||||
|
if (handle == null)
|
||||||
|
throw new NullArgumentException("handle");
|
||||||
|
|
||||||
|
this.id = id;
|
||||||
|
this.handle = handle;
|
||||||
|
this.structureModifier = structure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the underlying Minecraft packet.
|
||||||
|
* @return Underlying Minecraft packet.
|
||||||
|
*/
|
||||||
|
public Packet getHandle() {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the generic structure modifier for this packet.
|
||||||
|
* @return Structure modifier.
|
||||||
|
*/
|
||||||
|
public StructureModifier<Object> getModifier() {
|
||||||
|
return structureModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> StructureModifier<T> getPrimitiveModifier(Class<T> primitiveType) {
|
||||||
|
return structureModifier.withType(primitiveType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StructureModifier<ItemStack> getItemModifier() {
|
||||||
|
// Convert from and to the Bukkit wrapper
|
||||||
|
return structureModifier.<ItemStack>withType(net.minecraft.server.ItemStack.class, new EquivalentConverter<ItemStack>() {
|
||||||
|
public Object getGeneric(ItemStack specific) {
|
||||||
|
return ((CraftItemStack) specific).getHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getSpecific(Object generic) {
|
||||||
|
return new CraftItemStack((net.minecraft.server.ItemStack) generic);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public StructureModifier<ItemStack[]> getItemArrayModifier() {
|
||||||
|
// Convert to and from the Bukkit wrapper
|
||||||
|
return structureModifier.<ItemStack[]>withType(net.minecraft.server.ItemStack[].class, new EquivalentConverter<ItemStack[]>() {
|
||||||
|
public Object getGeneric(ItemStack[] specific) {
|
||||||
|
net.minecraft.server.ItemStack[] result = new net.minecraft.server.ItemStack[specific.length];
|
||||||
|
|
||||||
|
// Unwrap every item
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = ((CraftItemStack) specific[i]).getHandle();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack[] getSpecific(Object generic) {
|
||||||
|
net.minecraft.server.ItemStack[] input = (net.minecraft.server.ItemStack[]) generic;
|
||||||
|
ItemStack[] result = new ItemStack[input.length];
|
||||||
|
|
||||||
|
// Add the wrapper
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = new CraftItemStack(input[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the ID of this packet.
|
||||||
|
* @return Packet ID.
|
||||||
|
*/
|
||||||
|
public int getID() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
119
ProtocolLib/src/com/comphenix/protocol/events/PacketEvent.java
Normale Datei
119
ProtocolLib/src/com/comphenix/protocol/events/PacketEvent.java
Normale Datei
@ -0,0 +1,119 @@
|
|||||||
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class PacketEvent extends EventObject {
|
||||||
|
/**
|
||||||
|
* Automatically generated by Eclipse.
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -5360289379097430620L;
|
||||||
|
|
||||||
|
private PacketContainer packet;
|
||||||
|
private Player sender;
|
||||||
|
private Player reciever;
|
||||||
|
private boolean cancel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the static constructors to create instances of this event.
|
||||||
|
* @param source - the event source.
|
||||||
|
*/
|
||||||
|
public PacketEvent(Object source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PacketEvent(Object source, PacketContainer packet, Player sender, Player reciever) {
|
||||||
|
super(source);
|
||||||
|
this.packet = packet;
|
||||||
|
this.sender = sender;
|
||||||
|
this.reciever = reciever;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event representing a client packet transmission.
|
||||||
|
* @param source - the event source.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @param client - the client that sent the packet.
|
||||||
|
* @return The event.
|
||||||
|
*/
|
||||||
|
public static PacketEvent fromClient(Object source, PacketContainer packet, Player client) {
|
||||||
|
return new PacketEvent(source, packet, client, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event representing a server packet transmission.
|
||||||
|
* @param source - the event source.
|
||||||
|
* @param packet - the packet.
|
||||||
|
* @param recipient - the client that will receieve the packet.
|
||||||
|
* @return The event.
|
||||||
|
*/
|
||||||
|
public static PacketEvent fromServer(Object source, PacketContainer packet, Player recipient) {
|
||||||
|
return new PacketEvent(source, packet, null, recipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the packet that will be sent to the player.
|
||||||
|
* @return Packet to send to the player.
|
||||||
|
*/
|
||||||
|
public PacketContainer getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the packet that will be sent to the player.
|
||||||
|
* @param packet - the packet that will be sent instead.
|
||||||
|
*/
|
||||||
|
public void setPacket(PacketContainer packet) {
|
||||||
|
this.packet = packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the packet ID.
|
||||||
|
* @return The current packet ID.
|
||||||
|
*/
|
||||||
|
public int getPacketID() {
|
||||||
|
return packet.getID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves whether or not the packet should be cancelled.
|
||||||
|
* @return TRUE if it should be cancelled, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not the packet should be cancelled.
|
||||||
|
* @param cancel - TRUE if it should be cancelled, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public void setCancelled(boolean cancel) {
|
||||||
|
this.cancel = cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the player that has sent the packet.
|
||||||
|
* @return The sender, or NULL if the server is sending the packet.
|
||||||
|
*/
|
||||||
|
public Player getSender() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the player that will recieve the packet.
|
||||||
|
* @return The reciever, or NULL if the server is recieving the packet.
|
||||||
|
*/
|
||||||
|
public Player getReciever() {
|
||||||
|
return reciever;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not this packet was created by the server.
|
||||||
|
* @return TRUE if the packet was created by the server, FALSE if it was created by a client.
|
||||||
|
*/
|
||||||
|
public boolean isServerPacket() {
|
||||||
|
return getReciever() != null;
|
||||||
|
}
|
||||||
|
}
|
34
ProtocolLib/src/com/comphenix/protocol/events/PacketListener.java
Normale Datei
34
ProtocolLib/src/com/comphenix/protocol/events/PacketListener.java
Normale Datei
@ -0,0 +1,34 @@
|
|||||||
|
package com.comphenix.protocol.events;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
public interface PacketListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked right before a packet is transmitted from the server to the client.
|
||||||
|
* <p>
|
||||||
|
* Note that the packet may be replaced, if needed.
|
||||||
|
*
|
||||||
|
* @param event - the packet that should be sent.
|
||||||
|
*/
|
||||||
|
public void onPacketSending(PacketEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked right before a recieved packet from a client is being processed.
|
||||||
|
* @param event - the packet that has been recieved.
|
||||||
|
*/
|
||||||
|
public void onPacketReceiving(PacketEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve whether or not we're listening for client or server packets.
|
||||||
|
* @return The type of packets we expect.
|
||||||
|
*/
|
||||||
|
public ConnectionSide getConnectionSide();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of packet ids we expect to recieve.
|
||||||
|
* @return Packets IDs.
|
||||||
|
*/
|
||||||
|
public Set<Integer> getPacketsID();
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.ObjectUtils;
|
||||||
|
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static registries in Minecraft.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
class MinecraftRegistry {
|
||||||
|
|
||||||
|
// The packet class to packet ID translator
|
||||||
|
private static Map<Class, Integer> packetToID;
|
||||||
|
|
||||||
|
// New proxy values
|
||||||
|
private static Map<Integer, Class> overwrittenPackets = new HashMap<Integer, Class>();
|
||||||
|
|
||||||
|
// Vanilla packets
|
||||||
|
private static Map<Integer, Class> previousValues = new HashMap<Integer, Class>();
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked" })
|
||||||
|
public static Map<Class, Integer> getPacketToID() {
|
||||||
|
// Initialize it, if we haven't already
|
||||||
|
if (packetToID == null) {
|
||||||
|
try {
|
||||||
|
Field packetsField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType("java\\.util\\.Map");
|
||||||
|
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packetToID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, Class> getOverwrittenPackets() {
|
||||||
|
return overwrittenPackets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, Class> getPreviousPackets() {
|
||||||
|
return previousValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the correct packet class from a given packet ID.
|
||||||
|
* @param packetID - the packet ID.
|
||||||
|
* @return The associated class.
|
||||||
|
*/
|
||||||
|
public static Class getPacketClassFromID(int packetID) {
|
||||||
|
return getPacketClassFromID(packetID, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the correct packet class from a given packet ID.
|
||||||
|
* @param packetID - the packet ID.
|
||||||
|
* @param vanilla - whether or not to look for vanilla classes, not injected classes.
|
||||||
|
* @return The associated class.
|
||||||
|
*/
|
||||||
|
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
|
||||||
|
|
||||||
|
Map<Integer, Class> lookup = forceVanilla ? previousValues : overwrittenPackets;
|
||||||
|
|
||||||
|
// Optimized lookup
|
||||||
|
if (lookup.containsKey(packetToID)) {
|
||||||
|
return lookup.get(packetToID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will most likely not be used
|
||||||
|
for (Map.Entry<Class, Integer> entry : getPacketToID().entrySet()) {
|
||||||
|
if (ObjectUtils.equals(entry.getValue(), packetID)) {
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("The packet ID " + packetID + " is not registered.");
|
||||||
|
}
|
||||||
|
}
|
336
ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java
Normale Datei
336
ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java
Normale Datei
@ -0,0 +1,336 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.NullArgumentException;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
|
import com.comphenix.protocol.events.ConnectionSide;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
public final class PacketFilterManager implements ProtocolManager {
|
||||||
|
|
||||||
|
private Set<PacketListener> packetListeners = new CopyOnWriteArraySet<PacketListener>();
|
||||||
|
|
||||||
|
// Player injection
|
||||||
|
private Map<DataInputStream, Player> connectionLookup = new HashMap<DataInputStream, Player>();
|
||||||
|
private Map<Player, PlayerInjector> playerInjection = new HashMap<Player, PlayerInjector>();
|
||||||
|
|
||||||
|
// Packet injection
|
||||||
|
private PacketInjector packetInjector;
|
||||||
|
|
||||||
|
// Enabled packet filters
|
||||||
|
private Set<Integer> packetFilters = new HashSet<Integer>();
|
||||||
|
|
||||||
|
// Whether or not this class has been closed
|
||||||
|
private boolean hasClosed;
|
||||||
|
|
||||||
|
// Error logger
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only create instances of this class if protocol lib is disabled.
|
||||||
|
*/
|
||||||
|
public PacketFilterManager(ClassLoader classLoader, Logger logger) {
|
||||||
|
if (logger == null)
|
||||||
|
throw new NullArgumentException("logger");
|
||||||
|
if (classLoader == null)
|
||||||
|
throw new NullArgumentException("classLoader");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Initialize values
|
||||||
|
this.logger = logger;
|
||||||
|
this.packetInjector = new PacketInjector(classLoader, this, connectionLookup);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
logger.log(Level.SEVERE, "Unable to initialize packet injector.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Logger getLogger() {
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableSet<PacketListener> getPacketListeners() {
|
||||||
|
return ImmutableSet.copyOf(packetListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPacketListener(PacketListener listener) {
|
||||||
|
if (listener == null)
|
||||||
|
throw new NullArgumentException("listener");
|
||||||
|
|
||||||
|
packetListeners.add(listener);
|
||||||
|
enablePacketFilters(listener.getConnectionSide(),
|
||||||
|
listener.getPacketsID());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePacketListener(PacketListener listener) {
|
||||||
|
if (listener == null)
|
||||||
|
throw new NullArgumentException("listener");
|
||||||
|
|
||||||
|
packetListeners.remove(listener);
|
||||||
|
disablePacketFilters(listener.getConnectionSide(),
|
||||||
|
listener.getPacketsID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the given packet event for every registered listener.
|
||||||
|
* @param event - the packet event to invoke.
|
||||||
|
*/
|
||||||
|
public void invokePacketRecieving(PacketEvent event) {
|
||||||
|
for (PacketListener listener : packetListeners) {
|
||||||
|
try {
|
||||||
|
if (canHandlePacket(listener, event))
|
||||||
|
listener.onPacketReceiving(event);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Minecraft doesn't want your Exception.
|
||||||
|
logger.log(Level.SEVERE, "Exception occured in onPacketReceiving() for " + listener.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the given packet event for every registered listener.
|
||||||
|
* @param event - the packet event to invoke.
|
||||||
|
*/
|
||||||
|
public void invokePacketSending(PacketEvent event) {
|
||||||
|
for (PacketListener listener : packetListeners) {
|
||||||
|
try {
|
||||||
|
if (canHandlePacket(listener, event))
|
||||||
|
listener.onPacketSending(event);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.SEVERE, "Exception occured in onPacketReceiving() for " + listener.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canHandlePacket(PacketListener listener, PacketEvent event) {
|
||||||
|
// Make sure the listener is looking for this packet
|
||||||
|
if (!listener.getPacketsID().contains(event.getPacket().getID()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// And this type of packet
|
||||||
|
if (event.isServerPacket())
|
||||||
|
return listener.getConnectionSide().isForServer();
|
||||||
|
else
|
||||||
|
return listener.getConnectionSide().isForClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables packet events for a given packet ID.
|
||||||
|
* <p>
|
||||||
|
* Note that all packets are disabled by default.
|
||||||
|
*
|
||||||
|
* @param side - which side the event will arrive from.
|
||||||
|
* @param packets - the packet id(s).
|
||||||
|
*/
|
||||||
|
private void enablePacketFilters(ConnectionSide side, Set<Integer> packets) {
|
||||||
|
if (side == null)
|
||||||
|
throw new NullArgumentException("side");
|
||||||
|
|
||||||
|
for (int packetID : packets) {
|
||||||
|
if (side.isForServer())
|
||||||
|
packetFilters.add(packetID);
|
||||||
|
if (side.isForClient() && packetInjector != null)
|
||||||
|
packetInjector.addPacketHandler(packetID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables packet events from a given packet ID.
|
||||||
|
* @param packets - the packet id(s).
|
||||||
|
* @param side - which side the event no longer should arrive from.
|
||||||
|
*/
|
||||||
|
private void disablePacketFilters(ConnectionSide side, Set<Integer> packets) {
|
||||||
|
if (side == null)
|
||||||
|
throw new NullArgumentException("side");
|
||||||
|
|
||||||
|
for (int packetID : packets) {
|
||||||
|
if (side.isForServer())
|
||||||
|
packetFilters.remove(packetID);
|
||||||
|
if (side.isForClient() && packetInjector != null)
|
||||||
|
packetInjector.removePacketHandler(packetID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet) throws InvocationTargetException {
|
||||||
|
sendServerPacket(reciever, packet, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
||||||
|
if (reciever == null)
|
||||||
|
throw new NullArgumentException("reciever");
|
||||||
|
if (packet == null)
|
||||||
|
throw new NullArgumentException("packet");
|
||||||
|
|
||||||
|
getInjector(reciever).sendServerPacket(packet.getHandle(), filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recieveClientPacket(Player sender, PacketContainer packet) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
recieveClientPacket(sender, packet, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
|
||||||
|
if (sender == null)
|
||||||
|
throw new NullArgumentException("sender");
|
||||||
|
if (packet == null)
|
||||||
|
throw new NullArgumentException("packet");
|
||||||
|
|
||||||
|
PlayerInjector injector = getInjector(sender);
|
||||||
|
Packet mcPacket = packet.getHandle();
|
||||||
|
|
||||||
|
if (filters) {
|
||||||
|
mcPacket = injector.handlePacketRecieved(mcPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
injector.processPacket(mcPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketContainer createPacket(int id) {
|
||||||
|
return new PacketContainer(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Integer> getPacketFilters() {
|
||||||
|
if (packetInjector != null)
|
||||||
|
return Sets.union(packetFilters, packetInjector.getPacketHandlers());
|
||||||
|
else
|
||||||
|
return packetFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the packet injection for every player.
|
||||||
|
* @param players - list of players to inject.
|
||||||
|
*/
|
||||||
|
public void initializePlayers(Player[] players) {
|
||||||
|
for (Player player : players)
|
||||||
|
injectPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void injectPlayer(Player player) {
|
||||||
|
// Don't inject if the class has closed
|
||||||
|
if (!hasClosed && player != null && !playerInjection.containsKey(player)) {
|
||||||
|
try {
|
||||||
|
PlayerInjector injector = new PlayerInjector(player, this, packetFilters);
|
||||||
|
|
||||||
|
injector.injectManager();
|
||||||
|
playerInjection.put(player, injector);
|
||||||
|
connectionLookup.put(injector.getInputStream(false), player);
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
// Mark this injection attempt as a failure
|
||||||
|
playerInjection.put(player, null);
|
||||||
|
logger.log(Level.SEVERE, "Unable to access fields.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register this protocol manager on Bukkit.
|
||||||
|
* @param manager - Bukkit plugin manager that provides player join/leave events.
|
||||||
|
* @param plugin - the parent plugin.
|
||||||
|
*/
|
||||||
|
public void registerEvents(PluginManager manager, Plugin plugin) {
|
||||||
|
manager.registerEvents(new Listener() {
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
injectPlayer(event.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
uninjectPlayer(event.getPlayer());
|
||||||
|
}
|
||||||
|
}, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uninjectPlayer(Player player) {
|
||||||
|
if (!hasClosed && player != null) {
|
||||||
|
|
||||||
|
PlayerInjector injector = playerInjection.get(player);
|
||||||
|
DataInputStream input = injector.getInputStream(true);
|
||||||
|
|
||||||
|
if (injector != null) {
|
||||||
|
injector.cleanupAll();
|
||||||
|
|
||||||
|
playerInjection.remove(injector);
|
||||||
|
connectionLookup.remove(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerInjector getInjector(Player player) {
|
||||||
|
if (!playerInjection.containsKey(player)) {
|
||||||
|
// What? Try to inject again.
|
||||||
|
injectPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerInjector injector = playerInjection.get(player);
|
||||||
|
|
||||||
|
// Check that the injector was sucessfully added
|
||||||
|
if (injector != null)
|
||||||
|
return injector;
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Player has no injected handler.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return hasClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
// Guard
|
||||||
|
if (hasClosed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Remove everything
|
||||||
|
for (PlayerInjector injection : playerInjection.values()) {
|
||||||
|
injection.cleanupAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove packet handlers
|
||||||
|
if (packetInjector != null)
|
||||||
|
packetInjector.cleanupAll();
|
||||||
|
|
||||||
|
playerInjection.clear();
|
||||||
|
connectionLookup.clear();
|
||||||
|
hasClosed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
177
ProtocolLib/src/com/comphenix/protocol/injector/PacketInjector.java
Normale Datei
177
ProtocolLib/src/com/comphenix/protocol/injector/PacketInjector.java
Normale Datei
@ -0,0 +1,177 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
import net.sf.cglib.proxy.Callback;
|
||||||
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for adding or removing proxy objects that intercepts recieved packets.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class PacketInjector {
|
||||||
|
|
||||||
|
// The "put" method that associates a packet ID with a packet class
|
||||||
|
private static Method putMethod;
|
||||||
|
private static Object intHashMap;
|
||||||
|
|
||||||
|
// The packet filter manager
|
||||||
|
private PacketFilterManager manager;
|
||||||
|
|
||||||
|
// Allows us to determine the sender
|
||||||
|
private Map<DataInputStream, Player> playerLookup;
|
||||||
|
|
||||||
|
// Class loader
|
||||||
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
|
public PacketInjector(ClassLoader classLoader, PacketFilterManager manager,
|
||||||
|
Map<DataInputStream, Player> playerLookup) throws IllegalAccessException {
|
||||||
|
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
this.manager = manager;
|
||||||
|
this.playerLookup = playerLookup;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
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(Packet.class).getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
intHashMap = intHashMapField.get(null);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new RuntimeException("Minecraft is incompatible.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, get the "put" method.
|
||||||
|
putMethod = FuzzyReflection.fromObject(intHashMap).getMethodByParameters("put", int.class, Object.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public boolean addPacketHandler(int packetID) {
|
||||||
|
if (hasPacketHandler(packetID))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Enhancer ex = new Enhancer();
|
||||||
|
|
||||||
|
// 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 = MinecraftRegistry.getOverwrittenPackets();
|
||||||
|
Map<Integer, Class> previous = MinecraftRegistry.getPreviousPackets();
|
||||||
|
Map<Class, Integer> registry = MinecraftRegistry.getPacketToID();
|
||||||
|
Class old = MinecraftRegistry.getPacketClassFromID(packetID);
|
||||||
|
|
||||||
|
// Check for previous injections
|
||||||
|
if (!old.getName().startsWith("net.minecraft.")) {
|
||||||
|
throw new IllegalStateException("Packet " + packetID + " has already been injected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subclass the specific packet class
|
||||||
|
ex.setSuperclass(old);
|
||||||
|
ex.setCallbackType(ReadPacketModifier.class);
|
||||||
|
ex.setUseCache(false);
|
||||||
|
ex.setClassLoader(classLoader);
|
||||||
|
Class proxy = ex.createClass();
|
||||||
|
|
||||||
|
// Add a static reference
|
||||||
|
Enhancer.registerStaticCallbacks(proxy, new Callback[] {
|
||||||
|
new ReadPacketModifier(packetID, this)
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Override values
|
||||||
|
putMethod.invoke(intHashMap, packetID, proxy);
|
||||||
|
previous.put(packetID, old);
|
||||||
|
registry.put(proxy, packetID);
|
||||||
|
overwritten.put(packetID, proxy);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new RuntimeException("Illegal argument.", e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Cannot access method.", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new RuntimeException("Exception occured in IntHashMap.put.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public boolean removePacketHandler(int packetID) {
|
||||||
|
if (!hasPacketHandler(packetID))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Map<Class, Integer> registry = MinecraftRegistry.getPacketToID();
|
||||||
|
Map<Integer, Class> previous = MinecraftRegistry.getPreviousPackets();
|
||||||
|
Map<Integer, Class> overwritten = MinecraftRegistry.getOverwrittenPackets();
|
||||||
|
|
||||||
|
// Use the old class definition
|
||||||
|
try {
|
||||||
|
Class old = previous.get(packetID);
|
||||||
|
Class proxy = MinecraftRegistry.getPacketClassFromID(packetID);
|
||||||
|
|
||||||
|
putMethod.invoke(intHashMap, packetID, old);
|
||||||
|
previous.remove(packetID);
|
||||||
|
registry.remove(proxy);
|
||||||
|
overwritten.remove(packetID);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Handle some problems
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return false;
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Cannot access method.", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new RuntimeException("Exception occured in IntHashMap.put.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPacketHandler(int packetID) {
|
||||||
|
return MinecraftRegistry.getPreviousPackets().containsKey(packetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getPacketHandlers() {
|
||||||
|
return MinecraftRegistry.getPreviousPackets().keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from the ReadPacketModified monitor
|
||||||
|
PacketEvent packetRecieved(PacketContainer packet, DataInputStream input) {
|
||||||
|
|
||||||
|
Player client = playerLookup.get(input);
|
||||||
|
PacketEvent event = PacketEvent.fromClient((Object) manager, packet, client);
|
||||||
|
|
||||||
|
manager.invokePacketRecieving(event);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public void cleanupAll() {
|
||||||
|
Map<Integer, Class> overwritten = MinecraftRegistry.getOverwrittenPackets();
|
||||||
|
Map<Integer, Class> previous = MinecraftRegistry.getPreviousPackets();
|
||||||
|
|
||||||
|
// Remove every packet handler
|
||||||
|
for (Integer id : previous.keySet()) {
|
||||||
|
removePacketHandler(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
overwritten.clear();
|
||||||
|
previous.clear();
|
||||||
|
}
|
||||||
|
}
|
261
ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java
Normale Datei
261
ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java
Normale Datei
@ -0,0 +1,261 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.minecraft.server.EntityPlayer;
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
|
||||||
|
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.VolatileField;
|
||||||
|
|
||||||
|
class PlayerInjector {
|
||||||
|
|
||||||
|
// Cache previously retrieved fields
|
||||||
|
private static Field serverHandlerField;
|
||||||
|
private static Field networkManagerField;
|
||||||
|
private static Field inputField;
|
||||||
|
private static Field netHandlerField;
|
||||||
|
|
||||||
|
// And methods
|
||||||
|
private static Method queueMethod;
|
||||||
|
private static Method processMethod;
|
||||||
|
|
||||||
|
private Player player;
|
||||||
|
private boolean hasInitialized;
|
||||||
|
|
||||||
|
// Reference to the player's network manager
|
||||||
|
private VolatileField networkManager;
|
||||||
|
|
||||||
|
// The packet manager and filters
|
||||||
|
private PacketFilterManager manager;
|
||||||
|
private Set<Integer> packetFilters;
|
||||||
|
|
||||||
|
// Previous data input
|
||||||
|
private DataInputStream cachedInput;
|
||||||
|
|
||||||
|
// Current net handler
|
||||||
|
private Object netHandler;
|
||||||
|
|
||||||
|
public PlayerInjector(Player player, PacketFilterManager manager, Set<Integer> packetFilters) throws IllegalAccessException {
|
||||||
|
this.player = player;
|
||||||
|
this.manager = manager;
|
||||||
|
this.packetFilters = packetFilters;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize() throws IllegalAccessException {
|
||||||
|
|
||||||
|
CraftPlayer craft = (CraftPlayer) player;
|
||||||
|
EntityPlayer notchEntity = craft.getHandle();
|
||||||
|
|
||||||
|
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");
|
||||||
|
Object serverHandler = FieldUtils.readField(serverHandlerField, notchEntity);
|
||||||
|
|
||||||
|
// Next, get the network manager
|
||||||
|
if (networkManagerField == null)
|
||||||
|
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(".*NetworkManager");
|
||||||
|
networkManager = new VolatileField(networkManagerField, serverHandler);
|
||||||
|
|
||||||
|
// And the queue method
|
||||||
|
if (queueMethod == null)
|
||||||
|
queueMethod = FuzzyReflection.fromClass(networkManagerField.getType()).
|
||||||
|
getMethodByParameters("queue", Packet.class );
|
||||||
|
|
||||||
|
// And the data input stream that we'll use to identify a player
|
||||||
|
if (inputField == null)
|
||||||
|
inputField = FuzzyReflection.fromObject(networkManager.getOldValue(), true).
|
||||||
|
getFieldByType("java\\.io\\.DataInputStream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current net handler for this player.
|
||||||
|
* @return Current net handler.
|
||||||
|
* @throws IllegalAccessException Unable to find or retrieve net handler.
|
||||||
|
*/
|
||||||
|
private Object getNetHandler() throws IllegalAccessException {
|
||||||
|
|
||||||
|
// What a mess
|
||||||
|
try {
|
||||||
|
if (netHandlerField == null)
|
||||||
|
netHandlerField = FuzzyReflection.fromClass(networkManagerField.getType(), true).
|
||||||
|
getFieldByType("net\\.minecraft\\.NetHandler");
|
||||||
|
} catch (RuntimeException e1) {
|
||||||
|
try {
|
||||||
|
// Well, that sucks. Try just Minecraft objects then.
|
||||||
|
netHandlerField = FuzzyReflection.fromClass(networkManagerField.getType(), true).
|
||||||
|
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
|
||||||
|
|
||||||
|
} catch (RuntimeException e2) {
|
||||||
|
return new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the handler
|
||||||
|
if (netHandler != null)
|
||||||
|
netHandler = FieldUtils.readField(netHandlerField, networkManager.getOldValue(), true);
|
||||||
|
return 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(Packet packet) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
|
||||||
|
Object netHandler = getNetHandler();
|
||||||
|
|
||||||
|
// Get the process method
|
||||||
|
if (processMethod == null) {
|
||||||
|
try {
|
||||||
|
processMethod = FuzzyReflection.fromClass(Packet.class).
|
||||||
|
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 filtered - whether or not the packet will be filtered by our listeners.
|
||||||
|
* @param InvocationTargetException If an error occured when sending the packet.
|
||||||
|
*/
|
||||||
|
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
|
||||||
|
Object networkDelegate = filtered ? networkManager.getValue() : networkManager.getOldValue();
|
||||||
|
|
||||||
|
if (networkDelegate != null) {
|
||||||
|
try {
|
||||||
|
// 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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectManager() {
|
||||||
|
|
||||||
|
if (networkManager != null) {
|
||||||
|
final Class<?> networkInterface = networkManagerField.getType();
|
||||||
|
final Object networkDelegate = networkManager.getOldValue();
|
||||||
|
|
||||||
|
// Create our proxy object
|
||||||
|
Object networkProxy = Proxy.newProxyInstance(networkInterface.getClassLoader(),
|
||||||
|
new Class<?>[] { networkInterface }, new InvocationHandler() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
// OH OH! The queue method!
|
||||||
|
if (method.equals(queueMethod)) {
|
||||||
|
Packet packet = (Packet) args[0];
|
||||||
|
|
||||||
|
if (packet != null) {
|
||||||
|
packet = handlePacketRecieved(packet);
|
||||||
|
|
||||||
|
// A NULL packet indicate cancelling
|
||||||
|
if (packet != null)
|
||||||
|
args[0] = packet;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate to our underlying class
|
||||||
|
try {
|
||||||
|
return method.invoke(networkDelegate, args);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw e.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inject it, if we can.
|
||||||
|
networkManager.setValue(networkProxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows a packet to be recieved by the listeners.
|
||||||
|
* @param packet - packet to recieve.
|
||||||
|
* @return The given packet, or the packet replaced by the listeners.
|
||||||
|
*/
|
||||||
|
Packet handlePacketRecieved(Packet packet) {
|
||||||
|
// Get the packet ID too
|
||||||
|
Integer id = MinecraftRegistry.getPacketToID().get(packet.getClass());
|
||||||
|
|
||||||
|
// Make sure we're listening
|
||||||
|
if (packetFilters.contains(id)) {
|
||||||
|
// A packet has been sent guys!
|
||||||
|
PacketContainer container = new PacketContainer(id, packet);
|
||||||
|
PacketEvent event = PacketEvent.fromServer(manager, container, player);
|
||||||
|
manager.invokePacketSending(event);
|
||||||
|
|
||||||
|
// Cancelling is pretty simple. Just ignore the packet.
|
||||||
|
if (event.isCancelled())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Right, remember to replace the packet again
|
||||||
|
return event.getPacket().getHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataInputStream getInputStream(boolean cache) {
|
||||||
|
// Get the associated input stream
|
||||||
|
try {
|
||||||
|
if (cache && cachedInput != null)
|
||||||
|
return cachedInput;
|
||||||
|
|
||||||
|
// Save to cache
|
||||||
|
cachedInput = (DataInputStream) FieldUtils.readField(inputField, networkManager.getOldValue(), true);
|
||||||
|
return cachedInput;
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Unable to read input stream.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanupAll() {
|
||||||
|
// Clean up
|
||||||
|
networkManager.revertValue();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.ArrayUtils;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
import net.sf.cglib.proxy.MethodInterceptor;
|
||||||
|
import net.sf.cglib.proxy.MethodProxy;
|
||||||
|
|
||||||
|
class ReadPacketModifier implements MethodInterceptor {
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static Class[] parameters = { DataInputStream.class };
|
||||||
|
|
||||||
|
// Common for all packets of the same type
|
||||||
|
private PacketInjector packetInjector;
|
||||||
|
private int packetID;
|
||||||
|
|
||||||
|
// Whether or not a packet has been cancelled
|
||||||
|
private static WeakHashMap<Object, Object> override = new WeakHashMap<Object, Object>();
|
||||||
|
|
||||||
|
public ReadPacketModifier(int packetID, PacketInjector packetInjector) {
|
||||||
|
this.packetID = packetID;
|
||||||
|
this.packetInjector = packetInjector;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object thisObj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
|
|
||||||
|
Object returnValue = null;
|
||||||
|
String methodName = method.getName();
|
||||||
|
|
||||||
|
// We always pass these down (otherwise, we'll end up with a infinite loop)
|
||||||
|
if (methodName.equals("hashCode") || methodName.equals("equals") || methodName.equals("toString")) {
|
||||||
|
return proxy.invokeSuper(thisObj, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override.containsKey(thisObj)) {
|
||||||
|
Object overridenObject = override.get(thisObj);
|
||||||
|
|
||||||
|
// Cancel EVERYTHING, including "processPacket"
|
||||||
|
if (overridenObject == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
returnValue = proxy.invokeSuper(overridenObject, args);
|
||||||
|
} else {
|
||||||
|
returnValue = proxy.invokeSuper(thisObj, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this a readPacketData method?
|
||||||
|
if (returnValue == null &&
|
||||||
|
ArrayUtils.isEquals(method.getParameterTypes(), parameters)) {
|
||||||
|
|
||||||
|
// We need this in order to get the correct player
|
||||||
|
DataInputStream input = (DataInputStream) args[0];
|
||||||
|
|
||||||
|
// Let the people know
|
||||||
|
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj);
|
||||||
|
PacketEvent event = packetInjector.packetRecieved(container, input);
|
||||||
|
Packet result = event.getPacket().getHandle();
|
||||||
|
|
||||||
|
// Handle override
|
||||||
|
if (event != null) {
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
override.put(thisObj, null);
|
||||||
|
} else if (!objectEquals(thisObj, result)) {
|
||||||
|
override.put(thisObj, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean objectEquals(Object a, Object b) {
|
||||||
|
return System.identityHashCode(a) != System.identityHashCode(b);
|
||||||
|
}
|
||||||
|
}
|
46
ProtocolLib/src/com/comphenix/protocol/injector/StructureCache.java
Normale Datei
46
ProtocolLib/src/com/comphenix/protocol/injector/StructureCache.java
Normale Datei
@ -0,0 +1,46 @@
|
|||||||
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
|
||||||
|
public class StructureCache {
|
||||||
|
// Structure modifiers
|
||||||
|
private static Map<Integer, StructureModifier<Object>> structureModifiers = new HashMap<Integer, StructureModifier<Object>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty Minecraft packet of the given ID.
|
||||||
|
* @param id - packet ID.
|
||||||
|
* @return Created packet.
|
||||||
|
*/
|
||||||
|
public static Packet newPacket(int id) {
|
||||||
|
try {
|
||||||
|
return (Packet) MinecraftRegistry.getPacketClassFromID(id, true).newInstance();
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
return null;
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Access denied.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a cached structure modifier for the given packet id.
|
||||||
|
* @param id - packet ID.
|
||||||
|
* @return A structure modifier.
|
||||||
|
*/
|
||||||
|
public static StructureModifier<Object> getStructure(int id) {
|
||||||
|
|
||||||
|
StructureModifier<Object> result = structureModifiers.get(id);
|
||||||
|
|
||||||
|
// Use the vanilla class definition
|
||||||
|
if (result == null) {
|
||||||
|
result = new StructureModifier<Object>(MinecraftRegistry.getPacketClassFromID(id, true));
|
||||||
|
structureModifiers.put(id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.comphenix.protocol.reflect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that converts generic objects into types and back.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
* @param <TType> The specific type.
|
||||||
|
*/
|
||||||
|
public interface EquivalentConverter<TType> {
|
||||||
|
public TType getSpecific(Object generic);
|
||||||
|
public Object getGeneric(TType specific);
|
||||||
|
}
|
473
ProtocolLib/src/com/comphenix/protocol/reflect/FieldUtils.java
Normale Datei
473
ProtocolLib/src/com/comphenix/protocol/reflect/FieldUtils.java
Normale Datei
@ -0,0 +1,473 @@
|
|||||||
|
package com.comphenix.protocol.reflect;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.ClassUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for working with fields by reflection. Adapted and refactored from
|
||||||
|
* the dormant [reflect] Commons sandbox component.
|
||||||
|
* <p>
|
||||||
|
* The ability is provided to break the scoping restrictions coded by the
|
||||||
|
* programmer. This can allow fields to be changed that shouldn't be. This
|
||||||
|
* facility should be used with care.
|
||||||
|
*
|
||||||
|
* @author Apache Software Foundation
|
||||||
|
* @author Matt Benson
|
||||||
|
* @since 2.5
|
||||||
|
* @version $Id: FieldUtils.java 1057009 2011-01-09 19:48:06Z niallp $
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class FieldUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FieldUtils instances should NOT be constructed in standard programming.
|
||||||
|
* <p>
|
||||||
|
* This constructor is public to permit tools that require a JavaBean
|
||||||
|
* instance to operate.
|
||||||
|
*/
|
||||||
|
public FieldUtils() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an accessible <code>Field</code> by name respecting scope.
|
||||||
|
* Superclasses/interfaces will be considered.
|
||||||
|
*
|
||||||
|
* @param cls the class to reflect, must not be null
|
||||||
|
* @param fieldName the field name to obtain
|
||||||
|
* @return the Field object
|
||||||
|
* @throws IllegalArgumentException if the class or field name is null
|
||||||
|
*/
|
||||||
|
public static Field getField(Class cls, String fieldName) {
|
||||||
|
Field field = getField(cls, fieldName, false);
|
||||||
|
MemberUtils.setAccessibleWorkaround(field);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an accessible <code>Field</code> by name breaking scope if
|
||||||
|
* requested. Superclasses/interfaces will be considered.
|
||||||
|
*
|
||||||
|
* @param cls the class to reflect, must not be null
|
||||||
|
* @param fieldName the field name to obtain
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method. <code>False</code> will
|
||||||
|
* only match public fields.
|
||||||
|
* @return the Field object
|
||||||
|
* @throws IllegalArgumentException if the class or field name is null
|
||||||
|
*/
|
||||||
|
public static Field getField(final Class cls, String fieldName, boolean forceAccess) {
|
||||||
|
if (cls == null) {
|
||||||
|
throw new IllegalArgumentException("The class must not be null");
|
||||||
|
}
|
||||||
|
if (fieldName == null) {
|
||||||
|
throw new IllegalArgumentException("The field name must not be null");
|
||||||
|
}
|
||||||
|
// Sun Java 1.3 has a bugged implementation of getField hence we write
|
||||||
|
// the
|
||||||
|
// code ourselves
|
||||||
|
|
||||||
|
// getField() will return the Field object with the declaring class
|
||||||
|
// set correctly to the class that declares the field. Thus requesting
|
||||||
|
// the
|
||||||
|
// field on a subclass will return the field from the superclass.
|
||||||
|
//
|
||||||
|
// priority order for lookup:
|
||||||
|
// searchclass private/protected/package/public
|
||||||
|
// superclass protected/package/public
|
||||||
|
// private/different package blocks access to further superclasses
|
||||||
|
// implementedinterface public
|
||||||
|
|
||||||
|
// check up the superclass hierarchy
|
||||||
|
for (Class acls = cls; acls != null; acls = acls.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
Field field = acls.getDeclaredField(fieldName);
|
||||||
|
// getDeclaredField checks for non-public scopes as well
|
||||||
|
// and it returns accurate results
|
||||||
|
if (!Modifier.isPublic(field.getModifiers())) {
|
||||||
|
if (forceAccess) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException ex) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check the public interface case. This must be manually searched for
|
||||||
|
// incase there is a public supersuperclass field hidden by a
|
||||||
|
// private/package
|
||||||
|
// superclass field.
|
||||||
|
Field match = null;
|
||||||
|
for (Iterator intf = ClassUtils.getAllInterfaces(cls).iterator(); intf.hasNext();) {
|
||||||
|
try {
|
||||||
|
Field test = ((Class) intf.next()).getField(fieldName);
|
||||||
|
if (match != null) {
|
||||||
|
throw new IllegalArgumentException("Reference to field " + fieldName
|
||||||
|
+ " is ambiguous relative to " + cls
|
||||||
|
+ "; a matching field exists on two or more implemented interfaces.");
|
||||||
|
}
|
||||||
|
match = test;
|
||||||
|
} catch (NoSuchFieldException ex) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an accessible static Field.
|
||||||
|
*
|
||||||
|
* @param field to read
|
||||||
|
* @return the field value
|
||||||
|
* @throws IllegalArgumentException if the field is null or not static
|
||||||
|
* @throws IllegalAccessException if the field is not accessible
|
||||||
|
*/
|
||||||
|
public static Object readStaticField(Field field) throws IllegalAccessException {
|
||||||
|
return readStaticField(field, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a static Field.
|
||||||
|
*
|
||||||
|
* @param field to read
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method.
|
||||||
|
* @return the field value
|
||||||
|
* @throws IllegalArgumentException if the field is null or not static
|
||||||
|
* @throws IllegalAccessException if the field is not made accessible
|
||||||
|
*/
|
||||||
|
public static Object readStaticField(Field field, boolean forceAccess)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
if (field == null) {
|
||||||
|
throw new IllegalArgumentException("The field must not be null");
|
||||||
|
}
|
||||||
|
if (!Modifier.isStatic(field.getModifiers())) {
|
||||||
|
throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
|
||||||
|
}
|
||||||
|
return readField(field, (Object) null, forceAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the named public static field. Superclasses will be considered.
|
||||||
|
*
|
||||||
|
* @param cls the class to reflect, must not be null
|
||||||
|
* @param fieldName the field name to obtain
|
||||||
|
* @return the value of the field
|
||||||
|
* @throws IllegalArgumentException if the class or field name is null
|
||||||
|
* @throws IllegalAccessException if the field is not accessible
|
||||||
|
*/
|
||||||
|
public static Object readStaticField(Class cls, String fieldName) throws IllegalAccessException {
|
||||||
|
return readStaticField(cls, fieldName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the named static field. Superclasses will be considered.
|
||||||
|
*
|
||||||
|
* @param cls the class to reflect, must not be null
|
||||||
|
* @param fieldName the field name to obtain
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method. <code>False</code> will
|
||||||
|
* only match public fields.
|
||||||
|
* @return the Field object
|
||||||
|
* @throws IllegalArgumentException if the class or field name is null
|
||||||
|
* @throws IllegalAccessException if the field is not made accessible
|
||||||
|
*/
|
||||||
|
public static Object readStaticField(Class cls, String fieldName, boolean forceAccess)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
Field field = getField(cls, fieldName, forceAccess);
|
||||||
|
if (field == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
|
||||||
|
}
|
||||||
|
// already forced access above, don't repeat it here:
|
||||||
|
return readStaticField(field, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an accessible Field.
|
||||||
|
*
|
||||||
|
* @param field the field to use
|
||||||
|
* @param target the object to call on, may be null for static fields
|
||||||
|
* @return the field value
|
||||||
|
* @throws IllegalArgumentException if the field is null
|
||||||
|
* @throws IllegalAccessException if the field is not accessible
|
||||||
|
*/
|
||||||
|
public static Object readField(Field field, Object target) throws IllegalAccessException {
|
||||||
|
return readField(field, target, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a Field.
|
||||||
|
*
|
||||||
|
* @param field the field to use
|
||||||
|
* @param target the object to call on, may be null for static fields
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method.
|
||||||
|
* @return the field value
|
||||||
|
* @throws IllegalArgumentException if the field is null
|
||||||
|
* @throws IllegalAccessException if the field is not made accessible
|
||||||
|
*/
|
||||||
|
public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException {
|
||||||
|
if (field == null)
|
||||||
|
throw new IllegalArgumentException("The field must not be null");
|
||||||
|
|
||||||
|
if (forceAccess && !field.isAccessible()) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
} else {
|
||||||
|
MemberUtils.setAccessibleWorkaround(field);
|
||||||
|
}
|
||||||
|
return field.get(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the named public field. Superclasses will be considered.
|
||||||
|
*
|
||||||
|
* @param target the object to reflect, must not be null
|
||||||
|
* @param fieldName the field name to obtain
|
||||||
|
* @return the value of the field
|
||||||
|
* @throws IllegalArgumentException if the class or field name is null
|
||||||
|
* @throws IllegalAccessException if the named field is not public
|
||||||
|
*/
|
||||||
|
public static Object readField(Object target, String fieldName) throws IllegalAccessException {
|
||||||
|
return readField(target, fieldName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the named field. Superclasses will be considered.
|
||||||
|
*
|
||||||
|
* @param target the object to reflect, must not be null
|
||||||
|
* @param fieldName the field name to obtain
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method. <code>False</code> will
|
||||||
|
* only match public fields.
|
||||||
|
* @return the field value
|
||||||
|
* @throws IllegalArgumentException if the class or field name is null
|
||||||
|
* @throws IllegalAccessException if the named field is not made accessible
|
||||||
|
*/
|
||||||
|
public static Object readField(Object target, String fieldName, boolean forceAccess)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
if (target == null) {
|
||||||
|
throw new IllegalArgumentException("target object must not be null");
|
||||||
|
}
|
||||||
|
Class cls = target.getClass();
|
||||||
|
Field field = getField(cls, fieldName, forceAccess);
|
||||||
|
if (field == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
|
||||||
|
}
|
||||||
|
// already forced access above, don't repeat it here:
|
||||||
|
return readField(field, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a public static Field.
|
||||||
|
*
|
||||||
|
* @param field to write
|
||||||
|
* @param value to set
|
||||||
|
* @throws IllegalArgumentException if the field is null or not static
|
||||||
|
* @throws IllegalAccessException if the field is not public or is final
|
||||||
|
*/
|
||||||
|
public static void writeStaticField(Field field, Object value) throws IllegalAccessException {
|
||||||
|
writeStaticField(field, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a static Field.
|
||||||
|
*
|
||||||
|
* @param field to write
|
||||||
|
* @param value to set
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method. <code>False</code> will
|
||||||
|
* only match public fields.
|
||||||
|
* @throws IllegalArgumentException if the field is null or not static
|
||||||
|
* @throws IllegalAccessException if the field is not made accessible or is
|
||||||
|
* final
|
||||||
|
*/
|
||||||
|
public static void writeStaticField(Field field, Object value, boolean forceAccess)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
if (field == null) {
|
||||||
|
throw new IllegalArgumentException("The field must not be null");
|
||||||
|
}
|
||||||
|
if (!Modifier.isStatic(field.getModifiers())) {
|
||||||
|
throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
|
||||||
|
}
|
||||||
|
writeField(field, (Object) null, value, forceAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a named public static Field. Superclasses will be considered.
|
||||||
|
*
|
||||||
|
* @param cls Class on which the Field is to be found
|
||||||
|
* @param fieldName to write
|
||||||
|
* @param value to set
|
||||||
|
* @throws IllegalArgumentException if the field cannot be located or is not
|
||||||
|
* static
|
||||||
|
* @throws IllegalAccessException if the field is not public or is final
|
||||||
|
*/
|
||||||
|
public static void writeStaticField(Class cls, String fieldName, Object value)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
writeStaticField(cls, fieldName, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a named static Field. Superclasses will be considered.
|
||||||
|
*
|
||||||
|
* @param cls Class on which the Field is to be found
|
||||||
|
* @param fieldName to write
|
||||||
|
* @param value to set
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method. <code>False</code> will
|
||||||
|
* only match public fields.
|
||||||
|
* @throws IllegalArgumentException if the field cannot be located or is not
|
||||||
|
* static
|
||||||
|
* @throws IllegalAccessException if the field is not made accessible or is
|
||||||
|
* final
|
||||||
|
*/
|
||||||
|
public static void writeStaticField(Class cls, String fieldName, Object value,
|
||||||
|
boolean forceAccess) throws IllegalAccessException {
|
||||||
|
Field field = getField(cls, fieldName, forceAccess);
|
||||||
|
if (field == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
|
||||||
|
}
|
||||||
|
// already forced access above, don't repeat it here:
|
||||||
|
writeStaticField(field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an accessible field.
|
||||||
|
*
|
||||||
|
* @param field to write
|
||||||
|
* @param target the object to call on, may be null for static fields
|
||||||
|
* @param value to set
|
||||||
|
* @throws IllegalArgumentException if the field is null
|
||||||
|
* @throws IllegalAccessException if the field is not accessible or is final
|
||||||
|
*/
|
||||||
|
public static void writeField(Field field, Object target, Object value)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
writeField(field, target, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a field.
|
||||||
|
*
|
||||||
|
* @param field to write
|
||||||
|
* @param target the object to call on, may be null for static fields
|
||||||
|
* @param value to set
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method. <code>False</code> will
|
||||||
|
* only match public fields.
|
||||||
|
* @throws IllegalArgumentException if the field is null
|
||||||
|
* @throws IllegalAccessException if the field is not made accessible or is
|
||||||
|
* final
|
||||||
|
*/
|
||||||
|
public static void writeField(Field field, Object target, Object value, boolean forceAccess)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
if (field == null) {
|
||||||
|
throw new IllegalArgumentException("The field must not be null");
|
||||||
|
}
|
||||||
|
if (forceAccess && !field.isAccessible()) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
} else {
|
||||||
|
MemberUtils.setAccessibleWorkaround(field);
|
||||||
|
}
|
||||||
|
field.set(target, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a public field. Superclasses will be considered.
|
||||||
|
*
|
||||||
|
* @param target the object to reflect, must not be null
|
||||||
|
* @param fieldName the field name to obtain
|
||||||
|
* @param value to set
|
||||||
|
* @throws IllegalArgumentException if <code>target</code> or
|
||||||
|
* <code>fieldName</code> is null
|
||||||
|
* @throws IllegalAccessException if the field is not accessible
|
||||||
|
*/
|
||||||
|
public static void writeField(Object target, String fieldName, Object value)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
writeField(target, fieldName, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a field. Superclasses will be considered.
|
||||||
|
*
|
||||||
|
* @param target the object to reflect, must not be null
|
||||||
|
* @param fieldName the field name to obtain
|
||||||
|
* @param value to set
|
||||||
|
* @param forceAccess whether to break scope restrictions using the
|
||||||
|
* <code>setAccessible</code> method. <code>False</code> will
|
||||||
|
* only match public fields.
|
||||||
|
* @throws IllegalArgumentException if <code>target</code> or
|
||||||
|
* <code>fieldName</code> is null
|
||||||
|
* @throws IllegalAccessException if the field is not made accessible
|
||||||
|
*/
|
||||||
|
public static void writeField(Object target, String fieldName, Object value, boolean forceAccess)
|
||||||
|
throws IllegalAccessException {
|
||||||
|
if (target == null) {
|
||||||
|
throw new IllegalArgumentException("target object must not be null");
|
||||||
|
}
|
||||||
|
Class cls = target.getClass();
|
||||||
|
Field field = getField(cls, fieldName, forceAccess);
|
||||||
|
if (field == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot locate declared field " + cls.getName()
|
||||||
|
+ "." + fieldName);
|
||||||
|
}
|
||||||
|
// already forced access above, don't repeat it here:
|
||||||
|
writeField(field, target, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Useful member methods
|
||||||
|
private static class MemberUtils {
|
||||||
|
|
||||||
|
private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED
|
||||||
|
| Modifier.PRIVATE;
|
||||||
|
|
||||||
|
public static void setAccessibleWorkaround(AccessibleObject o) {
|
||||||
|
if (o == null || o.isAccessible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Member m = (Member) o;
|
||||||
|
if (Modifier.isPublic(m.getModifiers())
|
||||||
|
&& isPackageAccess(m.getDeclaringClass().getModifiers())) {
|
||||||
|
try {
|
||||||
|
o.setAccessible(true);
|
||||||
|
} catch (SecurityException e) { // NOPMD
|
||||||
|
// ignore in favor of subsequent IllegalAccessException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a given set of modifiers implies package access.
|
||||||
|
*
|
||||||
|
* @param modifiers to test
|
||||||
|
* @return true unless package/protected/private modifier detected
|
||||||
|
*/
|
||||||
|
public static boolean isPackageAccess(int modifiers) {
|
||||||
|
return (modifiers & ACCESS_TEST) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
218
ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java
Normale Datei
218
ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java
Normale Datei
@ -0,0 +1,218 @@
|
|||||||
|
package com.comphenix.protocol.reflect;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.ArrayUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves fields and methods by signature, not just name.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class FuzzyReflection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches a Minecraft object.
|
||||||
|
*/
|
||||||
|
public static String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+";
|
||||||
|
|
||||||
|
// The class we're actually representing
|
||||||
|
private Class<?> source;
|
||||||
|
|
||||||
|
// Whether or not to lookup private members
|
||||||
|
private boolean forceAccess;
|
||||||
|
|
||||||
|
public FuzzyReflection(Class<?> source, boolean forceAccess) {
|
||||||
|
this.source = source;
|
||||||
|
this.forceAccess = forceAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a fuzzy reflection instance from a given class.
|
||||||
|
* @param source - the class we'll use.
|
||||||
|
* @return A fuzzy reflection instance.
|
||||||
|
*/
|
||||||
|
public static FuzzyReflection fromClass(Class<?> source) {
|
||||||
|
return fromClass(source, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a fuzzy reflection instance from a given class.
|
||||||
|
* @param source - the class we'll use.
|
||||||
|
* @param forceAccess - whether or not to override scope restrictions.
|
||||||
|
* @return A fuzzy reflection instance.
|
||||||
|
*/
|
||||||
|
public static FuzzyReflection fromClass(Class<?> source, boolean forceAccess) {
|
||||||
|
return new FuzzyReflection(source, forceAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a fuzzy reflection instance from an object.
|
||||||
|
* @param reference - the object we'll use.
|
||||||
|
* @return A fuzzy reflection instance that uses the class of the given object.
|
||||||
|
*/
|
||||||
|
public static FuzzyReflection fromObject(Object reference) {
|
||||||
|
return new FuzzyReflection(reference.getClass(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a fuzzy reflection instance from an object.
|
||||||
|
* @param reference - the object we'll use.
|
||||||
|
* @param forceAccess - whether or not to override scope restrictions.
|
||||||
|
* @return A fuzzy reflection instance that uses the class of the given object.
|
||||||
|
*/
|
||||||
|
public static FuzzyReflection fromObject(Object reference, boolean forceAccess) {
|
||||||
|
return new FuzzyReflection(reference.getClass(), forceAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the underlying class.
|
||||||
|
*/
|
||||||
|
public Class<?> getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a method by looking at its name.
|
||||||
|
* @param nameRegex - regular expression that will match method names.
|
||||||
|
* @return The first method that satisfies the regular expression.
|
||||||
|
*/
|
||||||
|
public Method getMethodByName(String nameRegex) {
|
||||||
|
|
||||||
|
Pattern match = Pattern.compile(nameRegex);
|
||||||
|
|
||||||
|
for (Method method : getMethods()) {
|
||||||
|
if (match.matcher(method.getName()).matches()) {
|
||||||
|
// Right - this is probably it.
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Unable to find a method with the pattern " +
|
||||||
|
nameRegex + " in " + source.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a method by looking at the parameter types only.
|
||||||
|
* @param name - potential name of the method. Only used by the error mechanism.
|
||||||
|
* @param args - parameter types of the method to find.
|
||||||
|
* @return The first method that satisfies the parameter types.
|
||||||
|
*/
|
||||||
|
public Method getMethodByParameters(String name, Class<?>... args) {
|
||||||
|
|
||||||
|
// Find the correct method to call
|
||||||
|
for (Method method : getMethods()) {
|
||||||
|
if (ArrayUtils.isEquals(method.getParameterTypes(), args)) {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// That sucks
|
||||||
|
throw new RuntimeException("Unable to find " + name + " in " + source.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a field by name.
|
||||||
|
* @param nameRegex - regular expression that will match a field name.
|
||||||
|
* @return The first field to match the given expression.
|
||||||
|
*/
|
||||||
|
public Field getFieldByName(String nameRegex) {
|
||||||
|
|
||||||
|
Pattern match = Pattern.compile(nameRegex);
|
||||||
|
|
||||||
|
for (Field field : getFields()) {
|
||||||
|
if (match.matcher(field.getName()).matches()) {
|
||||||
|
// Right - this is probably it.
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks like we're outdated. Too bad.
|
||||||
|
throw new RuntimeException("Unable to find a field with the pattern " +
|
||||||
|
nameRegex + " in " + source.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a field by type.
|
||||||
|
* <p>
|
||||||
|
* Note that the type is matched using the full canonical representation, i.e.:
|
||||||
|
* <ul>
|
||||||
|
* <li>java.util.List</li>
|
||||||
|
* <li>net.comphenix.xp.ExperienceMod</li>
|
||||||
|
* </ul>
|
||||||
|
* @param typeRegex - regular expression that will match the field type.
|
||||||
|
* @return The first field with a type that matches the given regular expression.
|
||||||
|
*/
|
||||||
|
public Field getFieldByType(String typeRegex) {
|
||||||
|
|
||||||
|
Pattern match = Pattern.compile(typeRegex);
|
||||||
|
|
||||||
|
// Like above, only here we test the field type
|
||||||
|
for (Field field : getFields()) {
|
||||||
|
if (match.matcher(field.getType().getName()).matches()) {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks like we're outdated. Too bad.
|
||||||
|
throw new RuntimeException("Unable to find a field with the type " +
|
||||||
|
typeRegex + " in " + source.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all private and public fields in declared order (after JDK 1.5).
|
||||||
|
* @return Every field.
|
||||||
|
*/
|
||||||
|
public Set<Field> getFields() {
|
||||||
|
// We will only consider private fields in the declared class
|
||||||
|
if (forceAccess)
|
||||||
|
return setUnion(source.getDeclaredFields(), source.getFields());
|
||||||
|
else
|
||||||
|
return setUnion(source.getFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all private and public methods in declared order (after JDK 1.5).
|
||||||
|
* @return Every method.
|
||||||
|
*/
|
||||||
|
public Set<Method> getMethods() {
|
||||||
|
// We will only consider private methods in the declared class
|
||||||
|
if (forceAccess)
|
||||||
|
return setUnion(source.getDeclaredMethods(), source.getMethods());
|
||||||
|
else
|
||||||
|
return setUnion(source.getMethods());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent duplicate fields
|
||||||
|
private static <T> Set<T> setUnion(T[]... array) {
|
||||||
|
Set<T> result = new LinkedHashSet<T>();
|
||||||
|
|
||||||
|
for (T[] elements : array) {
|
||||||
|
for (T element : elements) {
|
||||||
|
result.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves whether or not not to override any scope restrictions.
|
||||||
|
* @return TRUE if we override scope, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isForceAccess() {
|
||||||
|
return forceAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not not to override any scope restrictions.
|
||||||
|
* @param forceAccess - TRUE if we override scope, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public void setForceAccess(boolean forceAccess) {
|
||||||
|
this.forceAccess = forceAccess;
|
||||||
|
}
|
||||||
|
}
|
1325
ProtocolLib/src/com/comphenix/protocol/reflect/MethodUtils.java
Normale Datei
1325
ProtocolLib/src/com/comphenix/protocol/reflect/MethodUtils.java
Normale Datei
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
218
ProtocolLib/src/com/comphenix/protocol/reflect/StructureModifier.java
Normale Datei
218
ProtocolLib/src/com/comphenix/protocol/reflect/StructureModifier.java
Normale Datei
@ -0,0 +1,218 @@
|
|||||||
|
package com.comphenix.protocol.reflect;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class StructureModifier<TField> {
|
||||||
|
|
||||||
|
// Object and its type
|
||||||
|
private Class targetType;
|
||||||
|
private Object target;
|
||||||
|
|
||||||
|
// Converter. May be NULL.
|
||||||
|
private EquivalentConverter<TField> converter;
|
||||||
|
|
||||||
|
// The fields to read in order
|
||||||
|
private Class fieldType;
|
||||||
|
private List<Field> data = new ArrayList<Field>();
|
||||||
|
|
||||||
|
// Cache of previous types
|
||||||
|
private Map<Class, StructureModifier> subtypeCache;
|
||||||
|
|
||||||
|
public StructureModifier(Class targetType) {
|
||||||
|
this(targetType, Object.class, getFields(targetType), null, new HashMap<Class, StructureModifier>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureModifier(Class targetType, Class fieldType, List<Field> data,
|
||||||
|
EquivalentConverter<TField> converter, Map<Class, StructureModifier> subTypeCache) {
|
||||||
|
this.targetType = targetType;
|
||||||
|
this.fieldType = fieldType;
|
||||||
|
this.data = data;
|
||||||
|
this.converter = converter;
|
||||||
|
this.subtypeCache = subTypeCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureModifier(StructureModifier<TField> other, Object target) {
|
||||||
|
this(other.targetType, other.fieldType, other.data, other.converter, other.subtypeCache);
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the value of a field given its index.
|
||||||
|
* @param fieldIndex - index of the field.
|
||||||
|
* @return Value of the field.
|
||||||
|
* @throws IllegalAccessException If we're unable to read the field due to a security limitation.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public TField read(int fieldIndex) throws IllegalAccessException {
|
||||||
|
if (fieldIndex < 0 || fieldIndex >= data.size())
|
||||||
|
throw new IllegalArgumentException("Field index must be within 0 - count");
|
||||||
|
if (target == null)
|
||||||
|
throw new IllegalStateException("Cannot read from a NULL target.");
|
||||||
|
|
||||||
|
Object result = FieldUtils.readField(data.get(fieldIndex), target, true);
|
||||||
|
|
||||||
|
// Use the converter, if we have it
|
||||||
|
if (converter != null)
|
||||||
|
return converter.getSpecific(result);
|
||||||
|
else
|
||||||
|
return (TField) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the value of a field given its index.
|
||||||
|
* @param fieldIndex - index of the field.
|
||||||
|
* @param value - new value of the field.
|
||||||
|
* @return This structure modifier - for chaining.
|
||||||
|
* @throws IllegalAccessException If we're unable to write to the field due to a security limitation.
|
||||||
|
*/
|
||||||
|
public StructureModifier<TField> write(int fieldIndex, TField value) throws IllegalAccessException {
|
||||||
|
if (fieldIndex < 0 || fieldIndex >= data.size())
|
||||||
|
throw new IllegalArgumentException("Field index must be within 0 - count");
|
||||||
|
if (target == null)
|
||||||
|
throw new IllegalStateException("Cannot write to a NULL target.");
|
||||||
|
|
||||||
|
// Use the converter, if it exists
|
||||||
|
Object obj = converter != null ? converter.getGeneric(value) : value;
|
||||||
|
FieldUtils.writeField(data.get(fieldIndex), target, obj, true);
|
||||||
|
|
||||||
|
// Make this method chainable
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correctly modifies the value of a field.
|
||||||
|
* @param fieldIndex - index of the field to modify.
|
||||||
|
* @param select - the function that modifies the field value.
|
||||||
|
* @return This structure modifier - for chaining.
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
public StructureModifier<TField> modify(int fieldIndex, Function<TField, TField> select) throws IllegalAccessException {
|
||||||
|
TField value = read(fieldIndex);
|
||||||
|
return write(fieldIndex, select.apply(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a structure modifier that only reads and writes fields of a given type.
|
||||||
|
* @param fieldType - the type, or supertype, of every field to modify.
|
||||||
|
* @return A structure modifier for fields of this type.
|
||||||
|
*/
|
||||||
|
public <T> StructureModifier<T> withType(Class fieldType) {
|
||||||
|
return withType(fieldType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a structure modifier that only reads and writes fields of a given type.
|
||||||
|
* @param fieldType - the type, or supertype, of every field to modify.
|
||||||
|
* @param converter - converts objects into the given type.
|
||||||
|
* @return A structure modifier for fields of this type.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> StructureModifier<T> withType(Class fieldType, EquivalentConverter<T> converter) {
|
||||||
|
|
||||||
|
StructureModifier<T> result = subtypeCache.get(fieldType);
|
||||||
|
|
||||||
|
if (fieldType.equals(this.fieldType)) {
|
||||||
|
|
||||||
|
// We're dealing with the exact field type.
|
||||||
|
return withConverter(converter);
|
||||||
|
|
||||||
|
} else if (result == null) {
|
||||||
|
List<Field> filtered = new ArrayList<Field>();
|
||||||
|
|
||||||
|
for (Field field : data) {
|
||||||
|
if (fieldType.isAssignableFrom(field.getType())) {
|
||||||
|
filtered.add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache structure modifiers
|
||||||
|
result = new StructureModifier<T>(targetType, fieldType, filtered,
|
||||||
|
converter, new HashMap<Class, StructureModifier>());
|
||||||
|
subtypeCache.put(fieldType, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the target too
|
||||||
|
return result.withTarget(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the common type of each field.
|
||||||
|
* @return Common type of each field.
|
||||||
|
*/
|
||||||
|
public Class getFieldType() {
|
||||||
|
return fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the type of the object we're modifying.
|
||||||
|
* @return Type of the object.
|
||||||
|
*/
|
||||||
|
public Class getTargetType() {
|
||||||
|
return targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the object we're currently modifying.
|
||||||
|
* @return Object we're modifying.
|
||||||
|
*/
|
||||||
|
public Object getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the number of readable types.
|
||||||
|
* @return Readable types.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a structure modifier of the same type for a different object target.
|
||||||
|
* @param target - different target of the same type.
|
||||||
|
* @return Structure modifier with the new target.
|
||||||
|
*/
|
||||||
|
public StructureModifier<TField> withTarget(Object target) {
|
||||||
|
return new StructureModifier<TField>(this, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a structure modifier with the same type and target, but using a new object converter.
|
||||||
|
* @param converter- the object converter to use.
|
||||||
|
* @return Structure modifier with the new converter.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> StructureModifier<T> withConverter(EquivalentConverter<T> converter) {
|
||||||
|
StructureModifier copy = new StructureModifier(this, target);
|
||||||
|
|
||||||
|
copy.converter = converter;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to filter out irrelevant fields
|
||||||
|
private static List<Field> getFields(Class type) {
|
||||||
|
List<Field> result = new ArrayList<Field>();
|
||||||
|
|
||||||
|
// Retrieve every private and public field
|
||||||
|
for (Field field : FuzzyReflection.fromClass(type, true).getFields()) {
|
||||||
|
int mod = field.getModifiers();
|
||||||
|
|
||||||
|
// Ignore static, final and "abstract packet" fields
|
||||||
|
if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod) && !field.getDeclaringClass().equals(Packet.class)) {
|
||||||
|
result.add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
158
ProtocolLib/src/com/comphenix/protocol/reflect/VolatileField.java
Normale Datei
158
ProtocolLib/src/com/comphenix/protocol/reflect/VolatileField.java
Normale Datei
@ -0,0 +1,158 @@
|
|||||||
|
package com.comphenix.protocol.reflect;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a field that will revert to its original state when this class is garbaged collected.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class VolatileField {
|
||||||
|
|
||||||
|
private Field field;
|
||||||
|
private Object container;
|
||||||
|
|
||||||
|
// The current and previous values
|
||||||
|
private Object previous;
|
||||||
|
private Object current;
|
||||||
|
|
||||||
|
// Whether or not we must reset or load
|
||||||
|
private boolean previousLoaded;
|
||||||
|
private boolean currentSet;
|
||||||
|
|
||||||
|
// Whether or not to break access restrictions
|
||||||
|
private boolean forceAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a volatile field with an associated object.
|
||||||
|
* @param field - the field.
|
||||||
|
* @param container - the object this field belongs to.
|
||||||
|
*/
|
||||||
|
public VolatileField(Field field, Object container) {
|
||||||
|
this.field = field;
|
||||||
|
this.container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a volatile field with an associated object.
|
||||||
|
* @param field - the field.
|
||||||
|
* @param container - the object this field belongs to.
|
||||||
|
* @param forceAccess - whether or not to override any scope restrictions.
|
||||||
|
*/
|
||||||
|
public VolatileField(Field field, Object container, boolean forceAccess) {
|
||||||
|
this.field = field;
|
||||||
|
this.container = container;
|
||||||
|
this.forceAccess = forceAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current field.
|
||||||
|
* @return The stored field.
|
||||||
|
*/
|
||||||
|
public Field getField() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the object the field is stored.
|
||||||
|
* @return The reference object.
|
||||||
|
*/
|
||||||
|
public Object getContainer() {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves whether or not not to override any scope restrictions.
|
||||||
|
* @return TRUE if we override scope, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isForceAccess() {
|
||||||
|
return forceAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not not to override any scope restrictions.
|
||||||
|
* @param forceAccess - TRUE if we override scope, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public void setForceAccess(boolean forceAccess) {
|
||||||
|
this.forceAccess = forceAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current field value.
|
||||||
|
* @return The current field value.
|
||||||
|
*/
|
||||||
|
public Object getValue() {
|
||||||
|
// Retrieve the correct value
|
||||||
|
if (!currentSet) {
|
||||||
|
ensureLoaded();
|
||||||
|
return previous;
|
||||||
|
} else {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the field value before the previous setValue(), unless saveValue() has been called.
|
||||||
|
* @return Previous value.
|
||||||
|
*/
|
||||||
|
public Object getOldValue() {
|
||||||
|
ensureLoaded();
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current value. This will be reverted unless saveValue() is called.
|
||||||
|
* @param newValue - new field value.
|
||||||
|
*/
|
||||||
|
public void setValue(Object newValue) {
|
||||||
|
|
||||||
|
// Remember to safe the previous value
|
||||||
|
ensureLoaded();
|
||||||
|
|
||||||
|
try {
|
||||||
|
FieldUtils.writeField(field, container, newValue, forceAccess);
|
||||||
|
current = newValue;
|
||||||
|
currentSet = true;
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Unable to read field " + field.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that the current value is still set after this class has been garbaged collected.
|
||||||
|
*/
|
||||||
|
public void saveValue() {
|
||||||
|
previous = current;
|
||||||
|
currentSet = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revert to the previously set value.
|
||||||
|
*/
|
||||||
|
public void revertValue() {
|
||||||
|
// Reset value.
|
||||||
|
if (currentSet) {
|
||||||
|
setValue(previous);
|
||||||
|
currentSet = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureLoaded() {
|
||||||
|
// Load the value if we haven't already
|
||||||
|
if (!previousLoaded) {
|
||||||
|
try {
|
||||||
|
previous = FieldUtils.readField(field, container, forceAccess);
|
||||||
|
previousLoaded = true;
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Unable to read field " + field.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
revertValue();
|
||||||
|
}
|
||||||
|
}
|
8
ProtocolLib/src/plugin.yml
Normale Datei
8
ProtocolLib/src/plugin.yml
Normale Datei
@ -0,0 +1,8 @@
|
|||||||
|
name: ProtocolLib
|
||||||
|
version: 1.0.0
|
||||||
|
description: Provides read/write access to the Minecraft protocol.
|
||||||
|
author: Comphenix
|
||||||
|
website: http://www.comphenix.net/ProtocolLib
|
||||||
|
|
||||||
|
main: com.comphenix.protocol.ProtocolLibrary
|
||||||
|
database: false
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.comphenix.protocol.reflect;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import net.minecraft.server.Packet103SetSlot;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
|
||||||
|
public class StructureModifierTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws IllegalAccessException {
|
||||||
|
|
||||||
|
Packet103SetSlot move = new Packet103SetSlot();
|
||||||
|
StructureModifier<Object> modifier = new StructureModifier<Object>(Packet103SetSlot.class);
|
||||||
|
|
||||||
|
move.a = 1;
|
||||||
|
int value = (Integer) modifier.withTarget(move).withType(int.class).read(0);
|
||||||
|
|
||||||
|
assertEquals(1, value);
|
||||||
|
}
|
||||||
|
}
|
In neuem Issue referenzieren
Einen Benutzer sperren