Move the field cloning reponsibility to FieldCloner.

This commit is contained in:
Kristian S. Stangeland 2012-12-27 10:32:26 +01:00
parent 8b91e3034d
commit dea725e1e3
5 changed files with 49 additions and 37 deletions

View File

@ -213,13 +213,16 @@ class InjectedServerConnection {
*/ */
private static final long serialVersionUID = 2070481080950500367L; private static final long serialVersionUID = 2070481080950500367L;
// Object writer we'll use
private final ObjectWriter writer = new ObjectWriter();
@Override @Override
protected void onReplacing(Object inserting, Object replacement) { protected void onReplacing(Object inserting, Object replacement) {
// Is this a normal Minecraft object? // Is this a normal Minecraft object?
if (!(inserting instanceof Factory)) { if (!(inserting instanceof Factory)) {
// If so, copy the content of the old element to the new // If so, copy the content of the old element to the new
try { try {
ObjectWriter.copyTo(inserting, replacement, inserting.getClass()); writer.copyTo(inserting, replacement, inserting.getClass());
} catch (Throwable e) { } catch (Throwable e) {
reporter.reportDetailed(InjectedServerConnection.this, "Cannot copy old " + inserting + reporter.reportDetailed(InjectedServerConnection.this, "Cannot copy old " + inserting +
" to new.", e, inserting, replacement); " to new.", e, inserting, replacement);

View File

@ -66,6 +66,9 @@ public class NetworkServerInjector extends PlayerInjector {
// Whether or not the player has disconnected // Whether or not the player has disconnected
private boolean hasDisconnected; private boolean hasDisconnected;
// Used to copy fields
private final ObjectWriter writer = new ObjectWriter();
public NetworkServerInjector( public NetworkServerInjector(
ClassLoader classLoader, ErrorReporter reporter, Player player, ClassLoader classLoader, ErrorReporter reporter, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters, ListenerInvoker invoker, IntegerSet sendingFilters,
@ -248,7 +251,7 @@ public class NetworkServerInjector extends PlayerInjector {
@Override @Override
protected void cleanHook() { protected void cleanHook() {
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) { if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
ObjectWriter.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass()); writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue(); serverHandlerRef.revertValue();
try { try {

View File

@ -23,8 +23,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import com.comphenix.protocol.injector.StructureCache; import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.cloning.Cloner;
import com.comphenix.protocol.reflect.cloning.IdentityCloner;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
/** /**
@ -38,11 +36,6 @@ public class ObjectWriter {
private static ConcurrentMap<Class, StructureModifier<Object>> cache = private static ConcurrentMap<Class, StructureModifier<Object>> cache =
new ConcurrentHashMap<Class, StructureModifier<Object>>(); new ConcurrentHashMap<Class, StructureModifier<Object>>();
/**
* The default value cloner to use.
*/
private static final Cloner DEFAULT_CLONER = new IdentityCloner();
/** /**
* Retrieve a usable structure modifier for the given object type. * Retrieve a usable structure modifier for the given object type.
* <p> * <p>
@ -50,7 +43,7 @@ public class ObjectWriter {
* @param type - the type of the object we are modifying. * @param type - the type of the object we are modifying.
* @return A structure modifier for the given type. * @return A structure modifier for the given type.
*/ */
private static StructureModifier<Object> getModifier(Class<?> type) { private StructureModifier<Object> getModifier(Class<?> type) {
Class<?> packetClass = MinecraftReflection.getPacketClass(); Class<?> packetClass = MinecraftReflection.getPacketClass();
// Handle subclasses of the packet class with our custom structure cache // Handle subclasses of the packet class with our custom structure cache
@ -82,26 +75,24 @@ public class ObjectWriter {
* @param destination - fields to copy to. * @param destination - fields to copy to.
* @param commonType - type containing each field to copy. * @param commonType - type containing each field to copy.
*/ */
public static void copyTo(Object source, Object destination, Class<?> commonType) { public void copyTo(Object source, Object destination, Class<?> commonType) {
// Note that we indicate that public fields will be copied the first time around // Note that we indicate that public fields will be copied the first time around
copyToInternal(source, destination, commonType, DEFAULT_CLONER, true); copyToInternal(source, destination, commonType, true);
} }
/** /**
* Copy every field in object A to object B. Each value is copied using the supplied cloner. * Called for every non-static field that will be copied.
* <p> * @param modifierSource - modifier for the original object.
* The two objects must have the same number of fields of the same type. * @param modifierDest - modifier for the new cloned object.
* @param source - fields to copy. * @param fieldIndex - the current field index.
* @param destination - fields to copy to.
* @param commonType - type containing each field to copy.
* @param valueCloner - a object responsible for copying the content of each field.
*/ */
public static void copyTo(Object source, Object destination, Class<?> commonType, Cloner valueCloner) { protected void transformField(StructureModifier<Object> modifierSource, StructureModifier<Object> modifierDest, int fieldIndex) {
copyToInternal(source, destination, commonType, valueCloner, true); Object value = modifierSource.read(fieldIndex);
modifierDest.write(fieldIndex, value);
} }
// Internal method that will actually implement the recursion // Internal method that will actually implement the recursion
private static void copyToInternal(Object source, Object destination, Class<?> commonType, Cloner valueCloner, boolean copyPublic) { private void copyToInternal(Object source, Object destination, Class<?> commonType, boolean copyPublic) {
if (source == null) if (source == null)
throw new IllegalArgumentException("Source cannot be NULL"); throw new IllegalArgumentException("Source cannot be NULL");
if (destination == null) if (destination == null)
@ -119,10 +110,9 @@ public class ObjectWriter {
Field field = modifierSource.getField(i); Field field = modifierSource.getField(i);
int mod = field.getModifiers(); int mod = field.getModifiers();
// Skip static fields. We also get the "public" field fairly often, so we'll skip that. // Skip static fields. We also get the "public" fields fairly often, so we'll skip that.
if (!Modifier.isStatic(mod) && (!Modifier.isPublic(mod) || copyPublic)) { if (!Modifier.isStatic(mod) && (!Modifier.isPublic(mod) || copyPublic)) {
Object value = modifierSource.read(i); transformField(modifierSource, modifierDest, i);
modifierDest.write(i, valueCloner.clone(value));
} }
} }
@ -130,7 +120,7 @@ public class ObjectWriter {
Class<?> superclass = commonType.getSuperclass(); Class<?> superclass = commonType.getSuperclass();
if (!superclass.equals(Object.class)) { if (!superclass.equals(Object.class)) {
copyToInternal(source, destination, superclass, valueCloner, false); copyToInternal(source, destination, superclass, false);
} }
} catch (FieldAccessException e) { } catch (FieldAccessException e) {

View File

@ -1,6 +1,7 @@
package com.comphenix.protocol.reflect.cloning; package com.comphenix.protocol.reflect.cloning;
import com.comphenix.protocol.reflect.ObjectWriter; import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.InstanceProvider; import com.comphenix.protocol.reflect.instances.InstanceProvider;
/** /**
@ -12,6 +13,9 @@ public class FieldCloner implements Cloner {
private final Cloner defaultCloner; private final Cloner defaultCloner;
private final InstanceProvider instanceProvider; private final InstanceProvider instanceProvider;
// Used to clone objects
private final ObjectWriter writer;
/** /**
* Constructs a field cloner that copies objects by reading and writing the internal fields directly. * Constructs a field cloner that copies objects by reading and writing the internal fields directly.
* @param defaultCloner - the default cloner used while copying fields. * @param defaultCloner - the default cloner used while copying fields.
@ -20,6 +24,16 @@ public class FieldCloner implements Cloner {
public FieldCloner(Cloner defaultCloner, InstanceProvider instanceProvider) { public FieldCloner(Cloner defaultCloner, InstanceProvider instanceProvider) {
this.defaultCloner = defaultCloner; this.defaultCloner = defaultCloner;
this.instanceProvider = instanceProvider; this.instanceProvider = instanceProvider;
// Remember to clone the value too
this.writer = new ObjectWriter() {
@Override
protected void transformField(StructureModifier<Object> modifierSource,
StructureModifier<Object> modifierDest, int fieldIndex) {
Object value = modifierSource.read(fieldIndex);
modifierDest.write(fieldIndex, getDefaultCloner().clone(value));
}
};
} }
@Override @Override
@ -38,8 +52,8 @@ public class FieldCloner implements Cloner {
Object copy = instanceProvider.create(source.getClass()); Object copy = instanceProvider.create(source.getClass());
// Copy public and private fields alike. Skip static and transient fields. // Copy public and private fields alike. Skip static fields.
ObjectWriter.copyTo(source, copy, source.getClass(), defaultCloner); writer.copyTo(source, copy, source.getClass());
return copy; return copy;
} }

View File

@ -367,14 +367,16 @@ public class PacketContainerTest {
* @return An object array. * @return An object array.
*/ */
private Object[] getArray(Object val) { private Object[] getArray(Object val) {
if (val instanceof Object[]) if (val instanceof Object[])
return (Object[]) val; return (Object[]) val;
if (val == null)
return null;
int arrlength = Array.getLength(val); int arrlength = Array.getLength(val);
Object[] outputArray = new Object[arrlength]; Object[] outputArray = new Object[arrlength];
for (int i = 0; i < arrlength; ++i) for (int i = 0; i < arrlength; ++i)
outputArray[i] = Array.get(val, i); outputArray[i] = Array.get(val, i);
return outputArray; return outputArray;
} }
} }