Fixed the "moved too quickly" error that would cause players to

fall out into the void on 1.3.2.
This commit is contained in:
Kristian S. Stangeland 2012-10-02 03:54:50 +02:00
parent 0470b2335c
commit 2fceaa803e
4 changed files with 107 additions and 33 deletions

View File

@ -7,6 +7,8 @@ import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.cglib.proxy.Factory;
import org.bukkit.Server;
import com.comphenix.protocol.reflect.FieldUtils;
@ -134,18 +136,33 @@ class InjectedServerConnection {
if (list instanceof ReplacedArrayList) {
replacedLists.add((ReplacedArrayList<Object>) list);
} else {
replacedLists.add(new ReplacedArrayList<Object>(list));
replacedLists.add(createReplacement(list));
listFieldRef.setValue(replacedLists.get(0));
listFields.add(listFieldRef);
}
}
// Hack to avoid the "moved to quickly" error
private ReplacedArrayList<Object> createReplacement(List<Object> list) {
return new ReplacedArrayList<Object>(list) {
@Override
protected void onReplacing(Object inserting, Object replacement) {
// Is this a normal Minecraft object?
if (!(inserting instanceof Factory)) {
// If so, copy the content of the old element to the new
ObjectCloner.copyTo(inserting, replacement, inserting.getClass());
}
}
};
}
/**
* Replace the server handler instance kept by the "keep alive" object.
* @param oldHandler - old server handler.
* @param newHandler - new, proxied server handler.
*/
public void replaceServerHandler(Object oldHandler, Object newHandler) {
if (!hasAttempted) {
injectList();
}

View File

@ -13,10 +13,8 @@ import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.CollectionGenerator;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
@ -30,8 +28,6 @@ import com.comphenix.protocol.reflect.instances.PrimitiveGenerator;
public class NetworkServerInjector extends PlayerInjector {
private static Method sendPacketMethod;
private StructureModifier<Object> serverHandlerModifier;
private InjectedServerConnection serverInjection;
public NetworkServerInjector(Player player, PacketFilterManager manager,
@ -48,8 +44,6 @@ public class NetworkServerInjector extends PlayerInjector {
if (hasInitialized) {
if (sendPacketMethod == null)
sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*");
if (serverHandlerModifier == null)
serverHandlerModifier = new StructureModifier<Object>(serverHandler.getClass(), null, false);
}
}
@ -119,41 +113,23 @@ public class NetworkServerInjector extends PlayerInjector {
CollectionGenerator.INSTANCE);
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
serverInjection.replaceServerHandler(serverHandler, proxyObject);
// Inject it now
if (proxyObject != null) {
copyTo(serverHandler, proxyObject);
// This will be done by InjectedServerConnection instead
//copyTo(serverHandler, proxyObject);
serverInjection.replaceServerHandler(serverHandler, proxyObject);
serverHandlerRef.setValue(proxyObject);
} else {
throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
}
}
/**
* Copy every field in server handler A to server handler B.
* @param source - fields to copy.
* @param destination - fields to copy to.
*/
private void copyTo(Object source, Object destination) {
StructureModifier<Object> modifierSource = serverHandlerModifier.withTarget(source);
StructureModifier<Object> modifierDest = serverHandlerModifier.withTarget(destination);
// Copy every field
try {
for (int i = 0; i < modifierSource.size(); i++) {
modifierDest.write(i, modifierSource.read(i));
}
} catch (FieldAccessException e) {
throw new RuntimeException("Unable to copy fields from NetServerHandler.", e);
}
}
@Override
public void cleanupAll() {
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue());
ObjectCloner.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue();
}

View File

@ -0,0 +1,64 @@
package com.comphenix.protocol.injector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier;
/**
* Can copy an object field by field.
*
* @author Kristian
*/
class ObjectCloner {
// Cache structure modifiers
@SuppressWarnings("rawtypes")
private static ConcurrentMap<Class, StructureModifier<Object>> cache =
new ConcurrentHashMap<Class, StructureModifier<Object>>();
/**
* Copy every field in object A to object B.
* <p>
* The two objects must have the same number of fields of the same type.
* @param source - fields to copy.
* @param destination - fields to copy to.
* @param commonType - type containing each field to copy.
*/
public static void copyTo(Object source, Object destination, Class<?> commonType) {
if (source == null)
throw new IllegalArgumentException("Source cannot be NULL");
if (destination == null)
throw new IllegalArgumentException("Destination cannot be NULL");
StructureModifier<Object> modifier = cache.get(commonType);
// Create the structure modifier if we haven't already
if (modifier == null) {
StructureModifier<Object> value = new StructureModifier<Object>(commonType, null, false);
modifier = cache.putIfAbsent(commonType, value);
if (modifier == null)
modifier = value;
}
// Add target
StructureModifier<Object> modifierSource = modifier.withTarget(source);
StructureModifier<Object> modifierDest = modifier.withTarget(destination);
// Copy every field
try {
for (int i = 0; i < modifierSource.size(); i++) {
Object value = modifierSource.read(i);
modifierDest.write(i, value);
// System.out.println(String.format("Writing value %s to %s",
// value, modifier.getFields().get(i).getName()));
}
} catch (FieldAccessException e) {
throw new RuntimeException("Unable to copy fields from " + commonType.getName(), e);
}
}
}

View File

@ -24,10 +24,21 @@ class ReplacedArrayList<TKey> extends ForwardingList<TKey> {
this.underlyingList = underlyingList;
}
/**
* Invoked when a element inserted is replaced.
* @param inserting - the element inserted.
* @param replacement - the element that it should replace.
*/
protected void onReplacing(TKey inserting, TKey replacement) {
// Default is to do nothing.
}
@Override
public boolean add(TKey element) {
if (replaceMap.containsKey(element)) {
return super.add(replaceMap.get(element));
TKey replacement = replaceMap.get(element);
onReplacing(element, replacement);
return super.add(replacement);
} else {
return super.add(element);
}
@ -36,7 +47,9 @@ class ReplacedArrayList<TKey> extends ForwardingList<TKey> {
@Override
public void add(int index, TKey element) {
if (replaceMap.containsKey(element)) {
super.add(index, replaceMap.get(element));
TKey replacement = replaceMap.get(element);
onReplacing(element, replacement);
super.add(index, replacement);
} else {
super.add(index, element);
}
@ -101,8 +114,10 @@ class ReplacedArrayList<TKey> extends ForwardingList<TKey> {
*/
public synchronized void replaceAll(TKey find, TKey replace) {
for (int i = 0; i < underlyingList.size(); i++) {
if (Objects.equal(underlyingList.get(i), find))
if (Objects.equal(underlyingList.get(i), find)) {
onReplacing(find, replace);
underlyingList.set(i, replace);
}
}
}
@ -121,7 +136,9 @@ class ReplacedArrayList<TKey> extends ForwardingList<TKey> {
TKey replaced = underlyingList.get(i);
if (inverse.containsKey(replaced)) {
underlyingList.set(i, inverse.get(replaced));
TKey original = inverse.get(replaced);
onReplacing(replaced, original);
underlyingList.set(i, original);
}
}