mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-23 19:16:14 +01:00
parent
fa317c1167
commit
42bec5a858
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.injector;
|
package com.comphenix.protocol.injector;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@ -157,9 +158,21 @@ class EntityUtilities {
|
|||||||
Object playerChunkMap = chunkMapField.get(chunkProvider);
|
Object playerChunkMap = chunkMapField.get(chunkProvider);
|
||||||
|
|
||||||
if (trackedEntitiesField == null) {
|
if (trackedEntitiesField == null) {
|
||||||
trackedEntitiesField = Accessors.getFieldAccessor(
|
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||||
|
trackedEntitiesField = Accessors.getFieldAccessor(
|
||||||
|
FuzzyReflection.fromClass(playerChunkMap.getClass(), true).getField(
|
||||||
|
FuzzyFieldContract.newBuilder()
|
||||||
|
.banModifier(Modifier.STATIC)
|
||||||
|
.requirePublic()
|
||||||
|
.typeExact(org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.ints.Int2ObjectMap.class)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
trackedEntitiesField = Accessors.getFieldAccessor(
|
||||||
FuzzyReflection.fromClass(playerChunkMap.getClass(), false).getField(
|
FuzzyReflection.fromClass(playerChunkMap.getClass(), false).getField(
|
||||||
FuzzyFieldContract.newBuilder().typeDerivedOf(Map.class).nameExact("trackedEntities").build()));
|
FuzzyFieldContract.newBuilder().typeDerivedOf(Map.class).nameExact("trackedEntities").build()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Integer, Object> trackedEntities = (Map<Integer, Object>) trackedEntitiesField.get(playerChunkMap);
|
Map<Integer, Object> trackedEntities = (Map<Integer, Object>) trackedEntitiesField.get(playerChunkMap);
|
||||||
@ -229,14 +242,17 @@ class EntityUtilities {
|
|||||||
Object tracker = null;
|
Object tracker = null;
|
||||||
|
|
||||||
if (trackerEntry != null) {
|
if (trackerEntry != null) {
|
||||||
// plugins like citizens will use their own tracker
|
// plugins like citizens will use their own tracker class, so cache the result
|
||||||
FieldAccessor trackerField = trackerFields.computeIfAbsent(trackerEntry.getClass(), x -> {
|
FieldAccessor trackerField = trackerFields.computeIfAbsent(trackerEntry.getClass(), x -> {
|
||||||
|
// get the first entity field
|
||||||
try {
|
try {
|
||||||
return Accessors.getFieldAccessor(trackerEntry.getClass(), "tracker", true);
|
return Accessors.getFieldAccessor(FuzzyReflection.fromClass(trackerEntry.getClass(), true)
|
||||||
} catch (Exception e) {
|
.getField(FuzzyFieldContract.newBuilder().typeExact(MinecraftReflection.getEntityClass()).build()));
|
||||||
// Assume it's the first entity field then
|
} catch (Exception ex) {
|
||||||
return Accessors.getFieldAccessor(FuzzyReflection.fromObject(trackerEntry, true)
|
// try with the default class
|
||||||
.getFieldByType("tracker", MinecraftReflection.getEntityClass()));
|
Class<?> trackerEntryClass = MinecraftReflection.getEntityTrackerClass();
|
||||||
|
return Accessors.getFieldAccessor(FuzzyReflection.fromClass(trackerEntryClass, true)
|
||||||
|
.getField(FuzzyFieldContract.newBuilder().typeExact(MinecraftReflection.getEntityClass()).build()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import com.comphenix.protocol.reflect.FuzzyReflection;
|
|||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||||
import net.minecraft.server.level.ChunkProviderServer;
|
import net.minecraft.server.level.ChunkProviderServer;
|
||||||
import net.minecraft.server.level.EntityTrackerEntry;
|
import net.minecraft.server.level.EntityTrackerEntry;
|
||||||
@ -24,6 +25,8 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import static com.comphenix.protocol.utility.TestUtils.setFinalField;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@ -35,7 +38,7 @@ public class EntityUtilitiesTest {
|
|||||||
BukkitInitialization.initializeItemMeta();
|
BukkitInitialization.initializeItemMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test
|
@Test
|
||||||
public void testReflection() throws ReflectiveOperationException {
|
public void testReflection() throws ReflectiveOperationException {
|
||||||
CraftWorld bukkit = mock(CraftWorld.class);
|
CraftWorld bukkit = mock(CraftWorld.class);
|
||||||
WorldServer world = mock(WorldServer.class);
|
WorldServer world = mock(WorldServer.class);
|
||||||
@ -44,29 +47,25 @@ public class EntityUtilitiesTest {
|
|||||||
ChunkProviderServer provider = mock(ChunkProviderServer.class);
|
ChunkProviderServer provider = mock(ChunkProviderServer.class);
|
||||||
when(world.getChunkProvider()).thenReturn(provider);
|
when(world.getChunkProvider()).thenReturn(provider);
|
||||||
|
|
||||||
// TODO unsetting final doesn't work anymore
|
|
||||||
PlayerChunkMap chunkMap = mock(PlayerChunkMap.class);
|
PlayerChunkMap chunkMap = mock(PlayerChunkMap.class);
|
||||||
Field chunkMapField = FuzzyReflection.fromClass(ChunkProviderServer.class, true)
|
Field chunkMapField = FuzzyReflection.fromClass(ChunkProviderServer.class, true)
|
||||||
.getField(FuzzyFieldContract.newBuilder().typeExact(PlayerChunkMap.class).build());
|
.getField(FuzzyFieldContract.newBuilder().typeExact(PlayerChunkMap.class).build());
|
||||||
chunkMapField.setAccessible(true);
|
setFinalField(provider, chunkMapField, chunkMap);
|
||||||
chunkMapField.set(provider, chunkMap);
|
|
||||||
|
|
||||||
CraftEntity bukkitEntity = mock(CraftEntity.class);
|
CraftEntity bukkitEntity = mock(CraftEntity.class);
|
||||||
Entity fakeEntity = mock(Entity.class);
|
Entity fakeEntity = mock(Entity.class);
|
||||||
when(fakeEntity.getBukkitEntity()).thenReturn(bukkitEntity);
|
when(fakeEntity.getBukkitEntity()).thenReturn(bukkitEntity);
|
||||||
|
|
||||||
PlayerChunkMap.EntityTracker tracker = mock(PlayerChunkMap.EntityTracker.class);
|
EntityTracker tracker = mock(EntityTracker.class);
|
||||||
FuzzyReflection.fromClass(EntityTracker.class, true)
|
Field trackerField = FuzzyReflection.fromClass(EntityTracker.class, true)
|
||||||
.getField(FuzzyFieldContract.newBuilder().typeExact(EntityTrackerEntry.class).build())
|
.getField(FuzzyFieldContract.newBuilder().typeExact(Entity.class).build());
|
||||||
.set(tracker, fakeEntity);
|
setFinalField(tracker, trackerField, fakeEntity);
|
||||||
|
|
||||||
Int2ObjectMap<PlayerChunkMap.EntityTracker> trackerMap = new Int2ObjectOpenHashMap<>();
|
Int2ObjectMap<EntityTracker> trackerMap = new Int2ObjectOpenHashMap<>();
|
||||||
trackerMap.put(1, tracker);
|
trackerMap.put(1, tracker);
|
||||||
|
Field trackedEntitiesField = FuzzyReflection.fromClass(PlayerChunkMap.class, true)
|
||||||
new StructureModifier<>(PlayerChunkMap.class, true)
|
.getField(FuzzyFieldContract.newBuilder().typeExact(Int2ObjectMap.class).build());
|
||||||
.withTarget(chunkMap)
|
setFinalField(chunkMap, trackedEntitiesField, trackerMap);
|
||||||
.withParamType(Int2ObjectMap.class, null, EntityTracker.class)
|
|
||||||
.write(0, trackerMap);
|
|
||||||
|
|
||||||
assertEquals(bukkitEntity, EntityUtilities.getInstance().getEntityFromID(bukkit, 1));
|
assertEquals(bukkitEntity, EntityUtilities.getInstance().getEntityFromID(bukkit, 1));
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import sun.misc.Unsafe;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
@ -42,4 +44,13 @@ public class TestUtils {
|
|||||||
return first.getType().equals(second.getType());
|
return first.getType().equals(second.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setFinalField(Object obj, Field field, Object newValue) throws ReflectiveOperationException {
|
||||||
|
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
|
||||||
|
unsafeField.setAccessible(true);
|
||||||
|
Unsafe unsafe = (Unsafe) unsafeField.get(null);
|
||||||
|
|
||||||
|
long offset = unsafe.objectFieldOffset(field);
|
||||||
|
unsafe.putObject(obj, offset, newValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user