package com.boydti.fawe.bukkit.v0; import com.boydti.fawe.FaweCache; import com.boydti.fawe.bukkit.util.BukkitReflectionUtils; import com.boydti.fawe.config.Settings; import com.boydti.fawe.example.NullRelighter; import com.boydti.fawe.example.Relighter; 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.google.common.collect.MapMaker; 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 java.util.concurrent.ConcurrentMap; import org.bukkit.Chunk; import org.bukkit.ChunkSnapshot; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Biome; public class BukkitQueue_All extends BukkitQueue_0 { public static int ALLOCATE; private ConcurrentMap chunkCache = new MapMaker() .weakValues() .makeMap(); public BukkitQueue_All(com.sk89q.worldedit.world.World world) { super(world); if (Settings.IMP.QUEUE.EXTRA_TIME_MS != Integer.MIN_VALUE) { ALLOCATE = Settings.IMP.QUEUE.EXTRA_TIME_MS; Settings.IMP.QUEUE.EXTRA_TIME_MS = Integer.MIN_VALUE; Settings.IMP.QUEUE.PARALLEL_THREADS = 1; } } public BukkitQueue_All(String world) { super(world); if (Settings.IMP.QUEUE.EXTRA_TIME_MS != Integer.MIN_VALUE) { ALLOCATE = Settings.IMP.QUEUE.EXTRA_TIME_MS; Settings.IMP.QUEUE.EXTRA_TIME_MS = Integer.MIN_VALUE; Settings.IMP.QUEUE.PARALLEL_THREADS = 1; } } @Override public boolean queueChunkLoad(int cx, int cz, RunnableVal operation) { if (PAPER) { try { new PaperChunkCallback(getImpWorld(), cx, cz) { @Override public void onLoad(Chunk chunk) { try { ChunkSnapshot snapshot = chunk.getChunkSnapshot(); operation.run(snapshot); } catch (Throwable e) { PAPER = false; } } }; return true; } catch (Throwable ignore) { PAPER = false; } } return super.queueChunkLoad(cx, cz); } @Override public Relighter getRelighter() { return NullRelighter.INSTANCE; } private static Class 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 { BukkitReflectionUtils.init(); classRegionFileCache = BukkitReflectionUtils.getNmsClass("RegionFileCache"); classRegionFile = BukkitReflectionUtils.getNmsClass("RegionFile"); classCraftChunk = BukkitReflectionUtils.getCbClass("CraftChunk"); classNMSChunk = BukkitReflectionUtils.getNmsClass("Chunk"); classCraftWorld = BukkitReflectionUtils.getCbClass("CraftWorld"); classNMSWorld = BukkitReflectionUtils.getNmsClass("World"); classChunkProviderServer = BukkitReflectionUtils.getNmsClass("ChunkProviderServer"); classIChunkProvider = BukkitReflectionUtils.getNmsClass("IChunkProvider"); classIChunkLoader = BukkitReflectionUtils.getNmsClass("IChunkLoader"); classChunkRegionLoader = BukkitReflectionUtils.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 saveChunks, boolean load) { if (classRegionFileCache == null) { return super.setMCA(mcaX, mcaZ, allowed, whileLocked, saveChunks, 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 = saveChunks && (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 } @Override public void setSkyLight(ChunkSnapshot chunk, int x, int y, int z, int value) { // Not supported } @Override public void setBlockLight(ChunkSnapshot chunk, int x, int y, int z, int value) { // Not supported } @Override public int getCombinedId4Data(ChunkSnapshot chunk, int x, int y, int z) { if (chunk.isSectionEmpty(y >> 4)) { return 0; } int id = chunk.getBlockTypeId(x & 15, y, z & 15); if (FaweCache.hasData(id)) { int data = chunk.getBlockData(x & 15, y, z & 15); return (id << 4) + data; } else { return id << 4; } } @Override public int getBiome(ChunkSnapshot chunkSnapshot, int x, int z) { Biome biome = chunkSnapshot.getBiome(x & 15, z & 15); return getAdapter().getBiomeId(biome); } @Override public ChunkSnapshot getSections(ChunkSnapshot chunkSnapshot) { return chunkSnapshot; } @Override public ChunkSnapshot getCachedChunk(World world, int cx, int cz) { long pair = MathMan.pairInt(cx, cz); ChunkSnapshot cached = chunkCache.get(pair); if (cached != null) return cached; if (world.isChunkLoaded(cx, cz)) { Long originalKeep = keepLoaded.get(pair); keepLoaded.put(pair, Long.MAX_VALUE); if (world.isChunkLoaded(cx, cz)) { Chunk chunk = world.getChunkAt(cx, cz); ChunkSnapshot snapshot = getAndCacheChunk(chunk); if (originalKeep != null) { keepLoaded.put(pair, originalKeep); } else { keepLoaded.remove(pair); } return snapshot; } else { keepLoaded.remove(pair); return null; } } else { return null; } } @Override public int getEmmittedLight(final ChunkSnapshot chunk, int x, int y, int z) { return chunk.getBlockEmittedLight(x & 15, y, z & 15); } @Override public int getSkyLight(final ChunkSnapshot chunk, int x, int y, int z) { return chunk.getBlockSkyLight(x & 15, y, z & 15); } @Override public int getLight(final ChunkSnapshot chunk, int x, int y, int z) { x = x & 15; z = z & 15; return Math.max(chunk.getBlockEmittedLight(x, y, z), chunk.getBlockSkyLight(x, y, z)); } @Override public ChunkSnapshot loadChunk(World world, int x, int z, boolean generate) { Chunk chunk = world.getChunkAt(x, z); chunk.load(generate); return chunk.isLoaded() ? getAndCacheChunk(chunk) : null; } private ChunkSnapshot getAndCacheChunk(Chunk chunk) { ChunkSnapshot snapshot = chunk.getChunkSnapshot(false, true, false); chunkCache.put(MathMan.pairInt(chunk.getX(), chunk.getZ()), snapshot); return snapshot; } @Override public ChunkSnapshot getCachedSections(World impWorld, int cx, int cz) { return getCachedChunk(impWorld, cx, cz); } @Override public CompoundTag getTileEntity(ChunkSnapshot chunk, int x, int y, int z) { if (getAdapter() == null) { return null; } Location loc = new Location(getWorld(), x, y, z); BaseBlock block = getAdapter().getBlock(loc); return block != null ? block.getNbtData() : null; } @Override public FaweChunk getFaweChunk(int x, int z) { return new BukkitChunk_All(this, x, z); } @Override public boolean supports(Capability capability) { switch (capability) { case CHANGE_TASKS: return getAdapter() != null; } return super.supports(capability); } private int skip; @Override public void startSet(boolean parallel) { super.startSet(true); } private Field fieldNeighbors; private Method chunkGetHandle; /** * Exploiting a bug in the vanilla lighting algorithm for faster block placement * - Could have been achieved without reflection by force unloading specific chunks * - Much faster just setting the variable manually though * @param chunk * @return */ protected Object[] disableLighting(Chunk chunk) { try { if (chunkGetHandle == null) { chunkGetHandle = chunk.getClass().getDeclaredMethod("getHandle"); chunkGetHandle.setAccessible(true); } Object nmsChunk = chunkGetHandle.invoke(chunk); if (fieldNeighbors == null) { fieldNeighbors = nmsChunk.getClass().getDeclaredField("neighbors"); fieldNeighbors.setAccessible(true); } Object value = fieldNeighbors.get(nmsChunk); fieldNeighbors.set(nmsChunk, 0); return new Object[] {nmsChunk, value}; } catch (Throwable ignore) {} return null; } protected void disableLighting(Object[] disableResult) { if (disableResult != null) { try { fieldNeighbors.set(disableResult[0], 0); } catch (IllegalAccessException e) { e.printStackTrace(); } } } protected void resetLighting(Object[] disableResult) { if (disableResult != null) { try { fieldNeighbors.set(disableResult[0], disableResult[1]); } catch (Throwable ignore) { ignore.printStackTrace(); } } } protected void enableLighting(Object[] disableResult) { if (disableResult != null) { try { fieldNeighbors.set(disableResult[0], 0x739C0); } catch (Throwable ignore) {} } } @Override public void endSet(boolean parallel) { super.endSet(true); } }