diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index aaf73006..70d5d1a9 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -491,6 +491,8 @@ public class FaweBukkit implements IFawe, Listener { v1_10_R1, v1_11_R1, v1_12_R1, + v1_12_R2, + v1_13_R1, NONE, } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitChunk_All.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitChunk_All.java index 569470e5..ee1ba91c 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitChunk_All.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitChunk_All.java @@ -208,7 +208,7 @@ public class BukkitChunk_All extends CharFaweChunk { int x = cacheX[m]; int z = cacheZ[m]; int id = combined >> 4; - if (FaweCache.hasNBT(id) && parent.adapter != null) { + if (FaweCache.hasNBT(id) && parent.getAdapter() != null) { CompoundTag nbt = getTile(x, y, z); if (nbt != null) { if (mutableLoc == null) mutableLoc = new Location(world, 0, 0, 0); @@ -216,7 +216,7 @@ public class BukkitChunk_All extends CharFaweChunk { mutableLoc.setY(y); mutableLoc.setZ(bz + z); synchronized (BukkitChunk_All.this) { - parent.adapter.setBlock(mutableLoc, new BaseBlock(id, combined & 0xF, nbt), false); + parent.getAdapter().setBlock(mutableLoc, new BaseBlock(id, combined & 0xF, nbt), false); } continue; } @@ -276,10 +276,10 @@ public class BukkitChunk_All extends CharFaweChunk { int x = cacheX[j]; int z = cacheZ[j]; int y = cacheY[j]; - if (FaweCache.hasNBT(id) && parent.adapter != null) { + if (FaweCache.hasNBT(id) && parent.getAdapter() != null) { CompoundTag tile = getTile(x, y, z); if (tile != null) { - parent.adapter.setBlock(new Location(world, bx + x, y, bz + z), new BaseBlock(id, combined & 0xF, tile), false); + parent.getAdapter().setBlock(new Location(world, bx + x, y, bz + z), new BaseBlock(id, combined & 0xF, tile), false); break; } } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java index 8ae633d4..993b4b45 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java @@ -12,11 +12,13 @@ import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.visitor.FaweChunkVisitor; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.TaskManager; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.world.biome.BaseBiome; import java.io.File; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.Map; @@ -37,9 +39,10 @@ import org.bukkit.event.world.WorldInitEvent; public abstract class BukkitQueue_0 extends NMSMappedFaweQueue implements Listener { - public static BukkitImplAdapter adapter; - public static Method methodToNative; - public static Method methodFromNative; + private static BukkitImplAdapter adapter; + private static FaweAdapter_All backupAdaper; + private static Method methodToNative; + private static Method methodFromNative; private static boolean setupAdapter = false; public BukkitQueue_0(final com.sk89q.worldedit.world.World world) { @@ -62,9 +65,38 @@ public abstract class BukkitQueue_0 extends NMSMa public static BukkitImplAdapter getAdapter() { if (adapter == null) setupAdapter(null); + if (adapter == null) return backupAdaper; return adapter; } + public static Tag toNative(Object tag) { + BukkitImplAdapter adapter = getAdapter(); + if (adapter == null) { + if (backupAdaper != null) return backupAdaper.toNative(tag); + return null; + } + try { + return (Tag) methodToNative.invoke(adapter, tag); + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + public static Object fromNative(Tag tag) { + BukkitImplAdapter adapter = getAdapter(); + if (adapter == null) { + if (backupAdaper != null) return backupAdaper.fromNative(tag); + return null; + } + try { + return methodFromNative.invoke(adapter, tag); + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + @Override public File getSaveFolder() { return new File(Bukkit.getWorldContainer(), getWorldName() + File.separator + "region"); @@ -152,37 +184,46 @@ public abstract class BukkitQueue_0 extends NMSMa public static void setupAdapter(BukkitImplAdapter adapter) { try { - if (setupAdapter == (setupAdapter = true)) { + if (adapter == null && setupAdapter == (setupAdapter = true)) { return; } WorldEditPlugin instance = (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"); Field fieldAdapter = WorldEditPlugin.class.getDeclaredField("bukkitAdapter"); fieldAdapter.setAccessible(true); - if ((BukkitQueue_0.adapter = adapter) != null) { + if (adapter != null) { + BukkitQueue_0.adapter = adapter; fieldAdapter.set(instance, adapter); } else { BukkitQueue_0.adapter = adapter = (BukkitImplAdapter) fieldAdapter.get(instance); } - for (Method method : adapter.getClass().getDeclaredMethods()) { - switch (method.getName()) { - case "toNative": - methodToNative = method; - methodToNative.setAccessible(true); - break; - case "fromNative": - methodFromNative = method; - methodFromNative.setAccessible(true); - break; + if (adapter != null) { + for (Method method : adapter.getClass().getDeclaredMethods()) { + switch (method.getName()) { + case "toNative": + methodToNative = method; + methodToNative.setAccessible(true); + break; + case "fromNative": + methodFromNative = method; + methodFromNative.setAccessible(true); + break; + } + } + } + return; + } catch (Throwable ignore) { + ignore.printStackTrace(); + } + if (BukkitQueue_0.adapter == null) { + if (backupAdaper == null) { + try { + backupAdaper = new FaweAdapter_All(); + Fawe.debug("Native adapter failed. Backup adapter is functional."); + } catch (Throwable ignore) { + Fawe.debug("Native and backup adapter failed! (Try updating the plugin)"); + ignore.printStackTrace(); } } - } catch (Throwable e) { - setupAdapter = false; - Fawe.debug("====== NO NATIVE WORLDEDIT ADAPTER ======"); - e.printStackTrace(); - Fawe.debug("Try updating WorldEdit: "); - Fawe.debug(" - http://builds.enginehub.org/job/worldedit?branch=master"); - Fawe.debug("See also: http://wiki.sk89q.com/wiki/WorldEdit/Bukkit_adapters"); - Fawe.debug("========================================="); } } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java index 7c846efc..c536f8dc 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java @@ -3,11 +3,20 @@ package com.boydti.fawe.bukkit.v0; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.RegionWrapper; +import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.util.SetQueue; +import com.boydti.fawe.util.TaskManager; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.blocks.BaseBlock; +import java.io.File; +import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayDeque; +import java.util.Map; import org.bukkit.Chunk; import org.bukkit.ChunkSnapshot; import org.bukkit.Location; @@ -37,6 +46,138 @@ public class BukkitQueue_All extends BukkitQueue_0 classRegionFileCache; + private static Class classRegionFile; + private static Class classCraftChunk; + private static Class classCraftWorld; + private static Class classNMSChunk; + private static Class classNMSWorld; + private static Class classChunkProviderServer; + private static Class classIChunkLoader; + private static Class classChunkRegionLoader; + private static Class classIChunkProvider; + private static Method methodGetHandleChunk; + private static Method methodGetHandleWorld; + private static Method methodFlush; + private static Method methodNeedsSaving; + private static Field fieldChunkProvider; + private static Field fieldChunkLoader; + private static Field fieldRegionMap; + private static Field fieldRegionRAF; + + static { + try { + ReflectionUtils.init(); + classRegionFileCache = ReflectionUtils.getNmsClass("RegionFileCache"); + classRegionFile = ReflectionUtils.getNmsClass("RegionFile"); + classCraftChunk = ReflectionUtils.getCbClass("CraftChunk"); + classNMSChunk = ReflectionUtils.getNmsClass("Chunk"); + classCraftWorld = ReflectionUtils.getCbClass("CraftWorld"); + classNMSWorld = ReflectionUtils.getNmsClass("World"); + classChunkProviderServer = ReflectionUtils.getNmsClass("ChunkProviderServer"); + classIChunkProvider = ReflectionUtils.getNmsClass("IChunkProvider"); + classIChunkLoader = ReflectionUtils.getNmsClass("IChunkLoader"); + classChunkRegionLoader = ReflectionUtils.getNmsClass("ChunkRegionLoader"); + + methodGetHandleChunk = ReflectionUtils.setAccessible(classCraftChunk.getDeclaredMethod("getHandle")); + methodGetHandleWorld = ReflectionUtils.setAccessible(classCraftWorld.getDeclaredMethod("getHandle")); + methodFlush = ReflectionUtils.findMethod(classChunkRegionLoader, boolean.class); + methodNeedsSaving = ReflectionUtils.findMethod(classNMSChunk, boolean.class, boolean.class); + + fieldChunkProvider = ReflectionUtils.findField(classNMSWorld, classIChunkProvider); + fieldChunkLoader = ReflectionUtils.findField(classChunkProviderServer, classIChunkLoader); + + fieldRegionMap = ReflectionUtils.findField(classRegionFileCache, Map.class); + fieldRegionRAF = ReflectionUtils.findField(classRegionFile, RandomAccessFile.class); + } catch (Throwable ignore) { + ignore.printStackTrace(); + } + } + + @Override + public boolean setMCA(int mcaX, int mcaZ, RegionWrapper allowed, Runnable whileLocked, boolean load) { + if (classRegionFileCache == null) { + return super.setMCA(mcaX, mcaZ, allowed, whileLocked, load); + } + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + long start = System.currentTimeMillis(); + long last = start; + synchronized (classRegionFileCache) { + try { + World world = getWorld(); + boolean autoSave = world.isAutoSave(); + + if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false); + + ArrayDeque unloaded = null; + if (load) { + int bcx = mcaX << 5; + int bcz = mcaZ << 5; + int tcx = bcx + 31; + int tcz = bcz + 31; + for (Chunk chunk : world.getLoadedChunks()) { + int cx = chunk.getX(); + int cz = chunk.getZ(); + if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) { + Object nmsChunk = methodGetHandleChunk.invoke(chunk); + boolean mustSave = (boolean) methodNeedsSaving.invoke(nmsChunk, false); + chunk.unload(mustSave, false); + if (unloaded == null) unloaded = new ArrayDeque(); + unloaded.add(chunk); + } + } + } else { + world.save(); + } + + Object nmsWorld = methodGetHandleWorld.invoke(world); + Object chunkProviderServer = fieldChunkProvider.get(nmsWorld); + Object chunkRegionLoader = fieldChunkLoader.get(chunkProviderServer); + while ((boolean) methodFlush.invoke(chunkRegionLoader)); + + if (unloaded != null) { + Map regionMap = (Map) fieldRegionMap.get(null); + File file = new File(world.getWorldFolder(), "region" + File.separator + "r." + mcaX + "." + mcaZ + ".mca"); + Object regionFile = regionMap.remove(file); + if (regionFile != null) { + RandomAccessFile raf = (RandomAccessFile) fieldRegionRAF.get(regionFile); + raf.close(); + } + } + + whileLocked.run(); + + if (load && unloaded != null) { + final ArrayDeque finalUnloaded = unloaded; + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + for (Chunk chunk : finalUnloaded) { + int cx = chunk.getX(); + int cz = chunk.getZ(); + if (world.isChunkLoaded(cx, cz)) continue; + SetQueue.IMP.addTask(() -> { + world.loadChunk(chunk.getX(), chunk.getZ(), false); + world.refreshChunk(chunk.getX(), chunk.getZ()); + }); + + } + } + }); + // load chunks + + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + }); + return true; + } + @Override public void setHeightMap(FaweChunk chunk, byte[] heightMap) { // Not supported @@ -69,7 +210,7 @@ public class BukkitQueue_All extends BukkitQueue_0 classCraftBlock; + private final Method biomeToBiomeBase; + private final Class classBiomeBase; + private final Method biomeBaseToTypeId; + private final Method getBiome; + private final Method biomeBaseToBiome; + private final Class classCraftWorld; + private final Method getHandleWorld; + private final Class classWorld; + private final Method getTileEntity1; + private final Method getTileEntity2; + private final Class classNBTTagCompound; + private final Constructor newNBTTagCompound; + private final Class classTileEntity; + private final Class classCraftEntity; + private final Method getHandleEntity; + private final Class classNBTTagInt; + private final Class classNBTBase; + private final Constructor newNBTTagInt; + private final Method setNBTTagCompound; + private Class classEntity; + private Method getBukkitEntity; + private Method addEntity; + private Method setLocation; + private Class classEntityTypes; + private Method getEntityId; + private Method createEntityFromId; + private Method readTagIntoEntity; + private Method readEntityIntoTag; + private Constructor newMinecraftKey; + private Class classMinecraftKey; + private Method readTagIntoTileEntity; + private Method readTileEntityIntoTag; + private Class classBlockPosition; + private Constructor newBlockPosition; + + private Map, NMSTagConstructor> WEToNMS = new ConcurrentHashMap<>(); + private Map NMSToWE = new ConcurrentHashMap<>(); + private Map, Integer> TagToId = new ConcurrentHashMap<>(); + + public FaweAdapter_All() throws Throwable { + ReflectionUtils.init(); + classCraftWorld = ReflectionUtils.getCbClass("CraftWorld"); + classCraftBlock = ReflectionUtils.getCbClass("block.CraftBlock"); + classCraftEntity = ReflectionUtils.getCbClass("entity.CraftEntity"); + classBiomeBase = ReflectionUtils.getNmsClass("BiomeBase"); + classWorld = ReflectionUtils.getNmsClass("World"); + classTileEntity = ReflectionUtils.getNmsClass("TileEntity"); + + biomeToBiomeBase = ReflectionUtils.setAccessible(classCraftBlock.getDeclaredMethod("biomeToBiomeBase", Biome.class)); + biomeBaseToBiome = ReflectionUtils.setAccessible(classCraftBlock.getDeclaredMethod("biomeBaseToBiome", classBiomeBase)); + getBiome = ReflectionUtils.setAccessible(classBiomeBase.getDeclaredMethod("getBiome", int.class)); + biomeBaseToTypeId = ReflectionUtils.findMethod(classBiomeBase, int.class, classBiomeBase); + getHandleWorld = ReflectionUtils.setAccessible(classCraftWorld.getDeclaredMethod("getHandle")); + getHandleEntity = ReflectionUtils.setAccessible(classCraftEntity.getDeclaredMethod("getHandle")); + try { + classBlockPosition = ReflectionUtils.getNmsClass("BlockPosition"); + } catch (Throwable ignore) { + } + if (classBlockPosition != null) { + getTileEntity1 = classWorld.getDeclaredMethod("getTileEntity", classBlockPosition); + getTileEntity2 = null; + newBlockPosition = ReflectionUtils.setAccessible(classBlockPosition.getConstructor(int.class, int.class, int.class)); + } else { + getTileEntity1 = null; + getTileEntity2 = ReflectionUtils.setAccessible(classWorld.getDeclaredMethod("getTileEntity", int.class, int.class, int.class)); + } + + classNBTTagCompound = ReflectionUtils.getNmsClass("NBTTagCompound"); + classNBTBase = ReflectionUtils.getNmsClass("NBTBase"); + classNBTTagInt = ReflectionUtils.getNmsClass("NBTTagInt"); + newNBTTagInt = ReflectionUtils.setAccessible(classNBTTagInt.getConstructor(int.class)); + setNBTTagCompound = ReflectionUtils.setAccessible(classNBTTagCompound.getDeclaredMethod("set", String.class, classNBTBase)); + newNBTTagCompound = ReflectionUtils.setAccessible(classNBTTagCompound.getConstructor()); + try { + readTileEntityIntoTag = ReflectionUtils.setAccessible(classTileEntity.getDeclaredMethod("save", classNBTTagCompound)); + } catch (Throwable ignore) { + readTileEntityIntoTag = ReflectionUtils.findMethod(classTileEntity, classNBTTagCompound, classNBTTagCompound); + if (readTileEntityIntoTag == null) { + readTileEntityIntoTag = ReflectionUtils.findMethod(classTileEntity, 1, Void.TYPE, classNBTTagCompound); + } + } + + + try { + readTagIntoTileEntity = ReflectionUtils.setAccessible(classTileEntity.getDeclaredMethod("load", classNBTTagCompound)); + } catch (Throwable ignore) { + readTagIntoTileEntity = ReflectionUtils.findMethod(classTileEntity, 0, Void.TYPE, classNBTTagCompound); + } + + + List nmsClasses = Arrays.asList("NBTTagCompound", "NBTTagByte", "NBTTagByteArray", "NBTTagDouble", "NBTTagFloat", "NBTTagInt", "NBTTagIntArray", "NBTTagList", "NBTTagEnd", "NBTTagString", "NBTTagShort", "NBTTagLong"); + List> weClasses = Arrays.asList(CompoundTag.class, ByteTag.class, ByteArrayTag.class, DoubleTag.class, FloatTag.class, IntTag.class, IntArrayTag.class, ListTag.class, EndTag.class, StringTag.class, ShortTag.class, LongTag.class); + int[] ids = new int[]{10, 1, 7, 6, 5, 3, 11, 9, 0, 8, 2, 4}; + + int noMods = Modifier.STATIC; + int hasMods = Modifier.PRIVATE; + for (int i = 0; i < nmsClasses.size(); i++) { + Class nmsClass = ReflectionUtils.getNmsClass(nmsClasses.get(i)); + Class weClass = weClasses.get(i); + TagToId.put(weClass, ids[i]); + + Constructor nmsConstructor = ReflectionUtils.setAccessible(nmsClass.getDeclaredConstructor()); + + if (weClass == EndTag.class) { + NMSToWE.put(nmsClass, value -> new EndTag()); + WEToNMS.put(weClass, value -> nmsConstructor.newInstance()); + } else if (weClass == CompoundTag.class) { + Field mapField = ReflectionUtils.findField(nmsClass, Map.class, hasMods, noMods); + Constructor weConstructor = ReflectionUtils.setAccessible(CompoundTag.class.getConstructor(Map.class)); + + NMSToWE.put(nmsClass, value -> { + Map map = (Map) mapField.get(value); + Map weMap = new HashMap(); + for (Map.Entry entry : map.entrySet()) { + weMap.put(entry.getKey(), toNative(entry.getValue())); + } + return new CompoundTag(weMap); + }); + + WEToNMS.put(weClass, value -> { + Map map = ReflectionUtils.getMap(((CompoundTag) value).getValue()); + Object nmsTag = nmsConstructor.newInstance(); + Map nmsMap = (Map) mapField.get(nmsTag); + for (Map.Entry entry : map.entrySet()) { + nmsMap.put(entry.getKey(), fromNative(entry.getValue())); + } + return nmsTag; + }); + } else if (weClass == ListTag.class) { + Field listField = ReflectionUtils.findField(nmsClass, List.class, hasMods, noMods); + Field typeField = ReflectionUtils.findField(nmsClass, byte.class, hasMods, noMods); + Constructor weConstructor = ReflectionUtils.setAccessible(ListTag.class.getConstructor(Class.class, List.class)); + + NMSToWE.put(nmsClass, tag -> { + int type = ((Number) typeField.get(tag)).intValue(); + List list = (List) listField.get(tag); + + Class weType = NBTConstants.getClassFromType(type); + ArrayList weList = new ArrayList<>(); + for (Object nmsTag : list) { + weList.add(toNative(nmsTag)); + } + return new ListTag(weType, weList); + }); + WEToNMS.put(weClass, tag -> { + ListTag lt = (ListTag) tag; + List list = ReflectionUtils.getList(lt.getValue()); + Class type = lt.getType(); + + int typeId = TagToId.get(type); + Object nmsTagList = nmsConstructor.newInstance(); + typeField.set(nmsTagList, (byte) typeId); + ArrayList nmsList = (ArrayList) listField.get(nmsTagList); + for (Tag weTag : list) { + nmsList.add(fromNative(weTag)); + } + return nmsTagList; + }); + } else { + Field typeField = ReflectionUtils.findField(nmsClass, null, hasMods, noMods); + Constructor weConstructor = ReflectionUtils.setAccessible(weClass.getConstructor(typeField.getType())); + + NMSToWE.put(nmsClass, tag -> { + Object value = typeField.get(tag); + return weConstructor.newInstance(value); + }); + + WEToNMS.put(weClass, tag -> { + Object nmsTag = nmsConstructor.newInstance(); + typeField.set(nmsTag, tag.getValue()); + return nmsTag; + }); + } + } + try { + classEntity = ReflectionUtils.getNmsClass("Entity"); + classEntityTypes = ReflectionUtils.getNmsClass("EntityTypes"); + + getBukkitEntity = ReflectionUtils.setAccessible(classEntity.getDeclaredMethod("getBukkitEntity")); + addEntity = ReflectionUtils.setAccessible(classWorld.getDeclaredMethod("addEntity", classEntity)); + setLocation = ReflectionUtils.setAccessible(classEntity.getDeclaredMethod("setLocation", double.class, double.class, double.class, float.class, float.class)); + + try { + classMinecraftKey = ReflectionUtils.getNmsClass("MinecraftKey"); + newMinecraftKey = classMinecraftKey.getConstructor(String.class); + } catch (Throwable ignore) { + } + if (classMinecraftKey != null) { + getEntityId = ReflectionUtils.findMethod(classEntityTypes, classMinecraftKey, classEntity); + createEntityFromId = ReflectionUtils.findMethod(classEntityTypes, classEntity, classMinecraftKey, classWorld); + } else { + getEntityId = ReflectionUtils.findMethod(classEntityTypes, String.class, classEntity); + createEntityFromId = ReflectionUtils.findMethod(classEntityTypes, classEntity, String.class, classWorld); + } + + noMods = Modifier.ABSTRACT | Modifier.PROTECTED | Modifier.PRIVATE; + try { + readEntityIntoTag = classEntity.getDeclaredMethod("save", classNBTTagCompound); + } catch (Throwable ignore) { + readEntityIntoTag = ReflectionUtils.findMethod(classEntity, classNBTTagCompound, classNBTTagCompound); + if (readEntityIntoTag == null) { + readEntityIntoTag = ReflectionUtils.findMethod(classEntity, 0, 0, noMods, Void.TYPE, classNBTTagCompound); + } + } + ReflectionUtils.setAccessible(readEntityIntoTag); + readTagIntoEntity = ReflectionUtils.findMethod(classEntity, 1, 0, noMods, Void.TYPE, classNBTTagCompound); + if (readTagIntoEntity == null) { + readTagIntoEntity = ReflectionUtils.findMethod(classEntity, 0, 0, noMods, Void.TYPE, classNBTTagCompound); + } + } catch (Throwable e) { + e.printStackTrace(); + classEntity = null; + } + } + + @Nullable + @Override + public BaseEntity getEntity(Entity entity) { + try { + if (classEntity == null) return null; + Object nmsEntity = getHandleEntity.invoke(entity); + + String id = getEntityId(nmsEntity); + + if (id != null) { + Object tag = newNBTTagCompound.newInstance(); + readEntityIntoTag.invoke(nmsEntity, tag); + return new BaseEntity(id, (CompoundTag) toNative(tag)); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + return null; + } + + private String getEntityId(Object entity) throws InvocationTargetException, IllegalAccessException { + Object res = getEntityId.invoke(null, entity); + return res == null ? null : res.toString(); + } + + private Object createEntityFromId(String id, Object world) throws InvocationTargetException, IllegalAccessException, InstantiationException { + if (classMinecraftKey != null) { + Object key = newMinecraftKey.newInstance(id); + return createEntityFromId.invoke(null, key, world); + } else { + return createEntityFromId.invoke(null, id, world); + } + } + + @Nullable + @Override + public Entity createEntity(Location location, BaseEntity state) { + try { + if (classEntity == null) return null; + World world = location.getWorld(); + Object nmsWorld = getHandleWorld.invoke(world); + + Object createdEntity = createEntityFromId(state.getTypeId(), nmsWorld); + + if (createdEntity != null) { + CompoundTag nativeTag = state.getNbtData(); + Map rawMap = ReflectionUtils.getMap(nativeTag.getValue()); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + rawMap.remove(name); + } + if (nativeTag != null) { + Object tag = fromNative(nativeTag); + readTagIntoEntity.invoke(createdEntity, tag); + } + + setLocation.invoke(createdEntity, location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + addEntity.invoke(nmsWorld, createdEntity, CreatureSpawnEvent.SpawnReason.CUSTOM); + return (Entity) getBukkitEntity.invoke(createdEntity); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + return null; + } + + public Tag toNative(Object nmsTag) { + try { + return NMSToWE.get(nmsTag.getClass()).construct(nmsTag); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public Object fromNative(Tag tag) { + try { + return WEToNMS.get(tag.getClass()).construct(tag); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public int getBlockId(Material material) { + return material.getId(); + } + + @Override + public Material getMaterial(int id) { + return Material.getMaterial(id); + } + + @Override + public int getBiomeId(Biome biome) { + try { + Object biomeBase = biomeToBiomeBase.invoke(null, biome); + if (biomeBase != null) return (int) biomeBaseToTypeId.invoke(null, biomeBase); + } catch (Throwable e) { + throw new RuntimeException(e); + } + return 0; + } + + @Override + public Biome getBiome(int id) { + try { + Object biomeBase = getBiome.invoke(null, id); + if (biomeBase != null) return (Biome) biomeBaseToBiome.invoke(null, biomeBase); + } catch (Throwable e) { + throw new RuntimeException(e); + } + return Biome.OCEAN; + } + + @Override + public BaseBlock getBlock(Location location) { + try { + World craftWorld = location.getWorld(); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + org.bukkit.block.Block bukkitBlock = location.getBlock(); + BaseBlock block = FaweCache.getBlock(bukkitBlock.getTypeId(), bukkitBlock.getData()); + + // Read the NBT data + Object nmsWorld = getHandleWorld.invoke(craftWorld); + Object tileEntity = getTileEntity(nmsWorld, x, y, z); + + if (tileEntity != null) { + block = new BaseBlock(block); + Object tag = newNBTTagCompound.newInstance(); + readTileEntityIntoTag.invoke(tileEntity, tag); + block.setNbtData((CompoundTag) toNative(tag)); + } + return block; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + public Object getTileEntity(Object nmsWorld, int x, int y, int z) { + try { + if (getTileEntity1 != null) { + Object pos = newBlockPosition.newInstance(x, y, z); + return getTileEntity1.invoke(nmsWorld, pos); + } else { + return getTileEntity2.invoke(nmsWorld, x, y, z); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean setBlock(Location location, BaseBlock block, boolean notifyAndLight) { + World craftWorld = location.getWorld(); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + boolean changed = location.getBlock().setTypeIdAndData(block.getId(), (byte) block.getData(), notifyAndLight); + + CompoundTag nativeTag = block.getNbtData(); + if (nativeTag != null) { + try { + Object nmsWorld = getHandleWorld.invoke(craftWorld); + Object tileEntity = getTileEntity(nmsWorld, x, y, z); + if (tileEntity != null) { + Object tag = fromNative(nativeTag); + + setNBTTagCompound.invoke(tag, "x", newNBTTagInt.newInstance(x)); + setNBTTagCompound.invoke(tag, "y", newNBTTagInt.newInstance(y)); + setNBTTagCompound.invoke(tag, "z", newNBTTagInt.newInstance(z)); + readTagIntoTileEntity.invoke(tileEntity, tag); // Load data + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + return changed; + } + + private interface NMSTagConstructor { + Object construct(Tag value) throws Exception; + } + + private interface WETagConstructor { + Tag construct(Object value) throws Exception; + } +} diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitChunk_1_10.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitChunk_1_10.java index 4e71d6bf..7926218c 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitChunk_1_10.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitChunk_1_10.java @@ -198,7 +198,7 @@ public class BukkitChunk_1_10 extends CharFaweChunk { if (id != null) { NBTTagCompound tag = new NBTTagCompound(); ent.e(tag); // readEntityIntoTag - CompoundTag nativeTag = (CompoundTag) getParent().methodToNative.invoke(getParent().adapter, tag); + CompoundTag nativeTag = (CompoundTag) getParent().toNative(tag); Map map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); setEntity(nativeTag); @@ -321,7 +321,7 @@ public class BukkitChunk_1_10 extends CharFaweChunk { entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); if (nativeTag != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_10.methodFromNative.invoke(BukkitQueue_1_10.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_10.fromNative(nativeTag); for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } @@ -474,7 +474,7 @@ public class BukkitChunk_1_10 extends CharFaweChunk { net.minecraft.server.v1_10_R1.BlockPosition pos = new net.minecraft.server.v1_10_R1.BlockPosition(x, y, z); // Set pos net.minecraft.server.v1_10_R1.TileEntity tileEntity = nmsWorld.getTileEntity(pos); if (tileEntity != null) { - net.minecraft.server.v1_10_R1.NBTTagCompound tag = (net.minecraft.server.v1_10_R1.NBTTagCompound) com.boydti.fawe.bukkit.v1_10.BukkitQueue_1_10.methodFromNative.invoke(com.boydti.fawe.bukkit.v1_10.BukkitQueue_1_10.adapter, nativeTag); + net.minecraft.server.v1_10_R1.NBTTagCompound tag = (net.minecraft.server.v1_10_R1.NBTTagCompound) com.boydti.fawe.bukkit.v1_10.BukkitQueue_1_10.fromNative(nativeTag); tag.set("x", new NBTTagInt(x)); tag.set("y", new NBTTagInt(y)); tag.set("z", new NBTTagInt(z)); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java index 03083a8e..e140a6e8 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java @@ -140,7 +140,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0 map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); previous.setEntity(nativeTag); @@ -829,7 +829,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0 map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); setEntity(nativeTag); @@ -330,7 +330,7 @@ public class BukkitChunk_1_11 extends CharFaweChunk map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); previous.setEntity(nativeTag); @@ -831,7 +831,7 @@ public class BukkitQueue_1_11 extends BukkitQueue_0 { } public boolean storeEntity(Entity ent) throws InvocationTargetException, IllegalAccessException { - if (ent instanceof EntityPlayer || BukkitQueue_0.adapter == null) { + if (ent instanceof EntityPlayer || BukkitQueue_0.getAdapter() == null) { return false; } int x = ((int) Math.round(ent.locX) & 15); @@ -90,7 +90,7 @@ public class BukkitChunk_1_12 extends CharFaweChunk { if (id != null) { NBTTagCompound tag = new NBTTagCompound(); ent.save(tag); // readEntityIntoTag - CompoundTag nativeTag = (CompoundTag) BukkitQueue_0.methodToNative.invoke(getParent().adapter, tag); + CompoundTag nativeTag = (CompoundTag) BukkitQueue_0.toNative(tag); Map map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); setEntity(nativeTag); @@ -330,7 +330,7 @@ public class BukkitChunk_1_12 extends CharFaweChunk { entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); if (nativeTag != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_12.methodFromNative.invoke(BukkitQueue_1_12.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_12.fromNative(nativeTag); for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } @@ -484,7 +484,7 @@ public class BukkitChunk_1_12 extends CharFaweChunk { BlockPosition pos = new BlockPosition(x, y, z); // Set pos TileEntity tileEntity = nmsWorld.getTileEntity(pos); if (tileEntity != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_12.methodFromNative.invoke(BukkitQueue_1_12.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_12.fromNative(nativeTag); tag.set("x", new NBTTagInt(x)); tag.set("y", new NBTTagInt(y)); tag.set("z", new NBTTagInt(z)); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java index 46252ee0..186c48c1 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java @@ -156,7 +156,7 @@ public class BukkitQueue_1_12 extends BukkitQueue_0 map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); previous.setEntity(nativeTag); @@ -846,7 +846,7 @@ public class BukkitQueue_1_12 extends BukkitQueue_0 { Entity entity = EntityTypes.createEntityByName(id, nmsWorld); if (entity != null) { if (nativeTag != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue17.methodFromNative.invoke(BukkitQueue17.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue17.fromNative(nativeTag); for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } @@ -446,7 +446,7 @@ public class BukkitChunk_1_7 extends CharFaweChunk { int z = (blockHash >> 8 & 0xF) + bz; TileEntity tileEntity = nmsWorld.getTileEntity(x, y, z); if (tileEntity != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue17.methodFromNative.invoke(BukkitQueue17.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue17.fromNative(nativeTag); tag.set("x", new NBTTagInt(x)); tag.set("y", new NBTTagInt(y)); tag.set("z", new NBTTagInt(z)); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java index 6c1acb77..56ad65ca 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java @@ -253,7 +253,7 @@ public class BukkitQueue17 extends BukkitQueue_0 map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); previous.setEntity(nativeTag); @@ -436,7 +436,7 @@ public class BukkitQueue17 extends BukkitQueue_0 { Entity entity = EntityTypes.createEntityByName(id, nmsWorld); if (entity != null) { if (nativeTag != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue18R3.methodFromNative.invoke(BukkitQueue18R3.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue18R3.fromNative(nativeTag); for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } @@ -307,7 +307,7 @@ public class BukkitChunk_1_8 extends CharFaweChunk { BlockPosition pos = new BlockPosition(x, y, z); // Set pos TileEntity tileEntity = nmsWorld.getTileEntity(pos); if (tileEntity != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue18R3.methodFromNative.invoke(BukkitQueue18R3.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue18R3.fromNative(nativeTag); tag.set("x", new NBTTagInt(x)); tag.set("y", new NBTTagInt(y)); tag.set("z", new NBTTagInt(z)); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java index e882142c..f194b324 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java @@ -251,7 +251,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0 map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); previous.setEntity(nativeTag); @@ -426,7 +426,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0 { entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); if (nativeTag != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_9_R1.methodFromNative.invoke(BukkitQueue_1_9_R1.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_9_R1.fromNative(nativeTag); for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } @@ -426,7 +426,7 @@ public class BukkitChunk_1_9 extends CharFaweChunk { BlockPosition pos = new BlockPosition(x, y, z); // Set pos TileEntity tileEntity = nmsWorld.getTileEntity(pos); if (tileEntity != null) { - NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_9_R1.methodFromNative.invoke(BukkitQueue_1_9_R1.adapter, nativeTag); + NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_9_R1.fromNative(nativeTag); tag.set("x", new NBTTagInt(x)); tag.set("y", new NBTTagInt(y)); tag.set("z", new NBTTagInt(z)); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java index a29b49aa..0b474520 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java @@ -122,9 +122,9 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0 map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); previous.setEntity(nativeTag); @@ -784,7 +784,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0 clazz, final Class type, int hasMods, int noMods) { + for (Field field : clazz.getDeclaredFields()) { + if (type == null || field.getType() == type) { + int mods = field.getModifiers(); + if ((mods & hasMods) == hasMods && (mods & noMods) == 0) { + return setAccessible(field); + } + } + } + return null; + } + + public static Field findField(final Class clazz, final Class type) { + for (Field field : clazz.getDeclaredFields()) { + if (field.getType() == type) { + return setAccessible(field); + } + } + return null; + } + + public static Method findMethod(final Class clazz, final Class returnType, Class... params) { + return findMethod(clazz, 0, returnType, params); + } + + public static Method findMethod(final Class clazz, int index, int hasMods, int noMods, final Class returnType, Class... params) { + outer: + for (Method method : sortMethods(clazz.getDeclaredMethods())) { + if (returnType == null || method.getReturnType() == returnType) { + Class[] mp = method.getParameterTypes(); + int mods = method.getModifiers(); + if ((mods & hasMods) != hasMods || (mods & noMods) != 0) continue; + if (params == null) { + if (index-- == 0) return setAccessible(method); + else { + continue; + } + } + if (mp.length == params.length) { + for (int i = 0; i < mp.length; i++) { + if (mp[i] != params[i]) continue outer; + } + if (index-- == 0) return setAccessible(method); + else { + continue; + } + } + } + } + return null; + } + + private static Method[] sortMethods(Method[] methods) { + Arrays.sort(methods, (o1, o2) -> o1.getName().compareTo(o2.getName())); + return methods; + } + + public static Method findMethod(final Class clazz, int index, final Class returnType, Class... params) { + return findMethod(clazz, index, 0, 0, returnType, params); + } + + public static T setAccessible(final T ao) { + ao.setAccessible(true); + return ao; + } + @SuppressWarnings("unchecked") public static T getField(final Field field, final Object instance) { if (field == null) {