diff --git a/pom.xml b/pom.xml index ffa2031b..32f00459 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ UTF-8 FastAsyncWorldEdit - 1.3.1 + 1.3.2 FastAsyncWorldEdit jar @@ -213,7 +213,7 @@ com.intellectualcrafters.plot plotsquared - 3.2.24 + 3.3.0 com.worldcretornica diff --git a/src/main/java/com/boydti/fawe/Fawe.java b/src/main/java/com/boydti/fawe/Fawe.java index 9d66523c..8fd33782 100644 --- a/src/main/java/com/boydti/fawe/Fawe.java +++ b/src/main/java/com/boydti/fawe/Fawe.java @@ -133,6 +133,10 @@ public class Fawe { TaskManager.IMP.repeat(lag, 100); } + public boolean checkVersion(final int[] version, final int major, final int minor, final int minor2) { + return (version[0] > major) || ((version[0] == major) && (version[1] > minor)) || ((version[0] == major) && (version[1] == minor) && (version[2] >= minor2)); + } + private void setupEvents() { WorldEdit.getInstance().getEventBus().register(new WESubscriber()); if (Settings.COMMAND_PROCESSOR) { diff --git a/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index f2df650e..faef3937 100644 --- a/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -24,6 +24,7 @@ import com.boydti.fawe.bukkit.regions.TownyFeature; import com.boydti.fawe.bukkit.regions.Worldguard; import com.boydti.fawe.bukkit.v1_8.BukkitEditSessionWrapper_1_8; import com.boydti.fawe.bukkit.v1_8.BukkitQueue_1_8; +import com.boydti.fawe.bukkit.v1_9.BukkitQueue_1_9; import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; @@ -133,9 +134,38 @@ public class FaweBukkit extends JavaPlugin implements IFawe { @Override public FaweQueue getQueue() { + if (Fawe.get().checkVersion(getServerVersion(), 1, 9, 0)) { + try { + return new BukkitQueue_1_9(); + } catch (Throwable e) { + e.printStackTrace(); + } + } return new BukkitQueue_1_8(); } + private int[] version; + + public int[] getServerVersion() { + if (version == null) { + try { + version = new int[3]; + final String[] split = Bukkit.getBukkitVersion().split("-")[0].split("\\."); + version[0] = Integer.parseInt(split[0]); + version[1] = Integer.parseInt(split[1]); + if (split.length == 3) { + version[2] = Integer.parseInt(split[2]); + } + } catch (NumberFormatException e) { + e.printStackTrace(); + Fawe.debug(StringMan.getString(Bukkit.getBukkitVersion())); + Fawe.debug(StringMan.getString(Bukkit.getBukkitVersion().split("-")[0].split("\\."))); + return new int[] { Integer.MAX_VALUE, 0, 0 }; + } + } + return version; + } + @Override public EditSessionWrapper getEditSessionWrapper(final EditSession session) { return new BukkitEditSessionWrapper_1_8(session); diff --git a/src/main/java/com/boydti/fawe/bukkit/regions/PlotSquaredFeature.java b/src/main/java/com/boydti/fawe/bukkit/regions/PlotSquaredFeature.java index d6fe60be..071f7124 100644 --- a/src/main/java/com/boydti/fawe/bukkit/regions/PlotSquaredFeature.java +++ b/src/main/java/com/boydti/fawe/bukkit/regions/PlotSquaredFeature.java @@ -15,7 +15,6 @@ import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.PlotId; import com.intellectualcrafters.plot.object.PlotPlayer; -import com.intellectualcrafters.plot.util.MainUtil; import com.plotsquared.bukkit.BukkitMain; public class PlotSquaredFeature extends BukkitMaskManager implements Listener { @@ -37,7 +36,7 @@ public class PlotSquaredFeature extends BukkitMaskManager implements Listener { final String world = loc.getWorld(); int min = Integer.MAX_VALUE; for (final Plot p : pp.getPlots()) { - if (p.world.equals(world)) { + if (p.getArea().worldname.equals(world)) { final double d = p.getHome().getEuclideanDistanceSquared(loc); if (d < min) { min = (int) d; @@ -57,8 +56,8 @@ public class PlotSquaredFeature extends BukkitMaskManager implements Listener { } if (hasPerm) { final World world = fp.parent.getWorld(); - final com.intellectualcrafters.plot.object.RegionWrapper region = MainUtil.getLargestRegion(plot); - final HashSet regions = MainUtil.getRegions(plot); + final com.intellectualcrafters.plot.object.RegionWrapper region = plot.getLargestRegion(); + final HashSet regions = plot.getRegions(); final Location pos1 = new Location(world, region.minX, 0, region.minZ); final Location pos2 = new Location(world, region.maxX, 256, region.maxZ); diff --git a/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java b/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java new file mode 100644 index 00000000..4a005668 --- /dev/null +++ b/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java @@ -0,0 +1,237 @@ +package com.boydti.fawe.bukkit.v1_9; + +import java.util.Arrays; + +import org.bukkit.Bukkit; +import org.bukkit.Chunk; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.ChunkLoc; +import com.boydti.fawe.object.FaweChunk; +import com.sk89q.worldedit.world.biome.BaseBiome; + +public class BukkitChunk_1_9 extends FaweChunk { + + private int[][] ids; + + private final short[] count; + private final short[] air; + private final short[] relight; + public int[][] biomes; + + public Chunk chunk; + + /** + * A FaweSections object represents a chunk and the blocks that you wish to change in it. + */ + protected BukkitChunk_1_9(final ChunkLoc chunk) { + super(chunk); + ids = new int[16][]; + count = new short[16]; + air = new short[16]; + relight = new short[16]; + } + + @Override + public Chunk getChunk() { + if (chunk == null) { + final ChunkLoc cl = getChunkLoc(); + chunk = Bukkit.getWorld(cl.world).getChunkAt(cl.x, cl.z); + } + return chunk; + } + + @Override + public void setChunkLoc(final ChunkLoc loc) { + super.setChunkLoc(loc); + chunk = null; + } + + /** + * Get the number of block changes in a specified section + * @param i + * @return + */ + public int getCount(final int i) { + return count[i]; + } + + public int getAir(final int i) { + return air[i]; + } + + public void setCount(int i, short value) { + count[i] = value; + } + + /** + * Get the number of block changes in a specified section + * @param i + * @return + */ + public int getRelight(final int i) { + return relight[i]; + } + + public int getTotalCount() { + int total = 0; + for (int i = 0; i < 16; i++) { + total += count[i]; + } + return total; + } + + public int getTotalRelight() { + if (getTotalCount() == 0 && biomes == null) { + Arrays.fill(count, (short) 1); + Arrays.fill(relight, Short.MAX_VALUE); + return Short.MAX_VALUE; + } + int total = 0; + for (int i = 0; i < 16; i++) { + total += relight[i]; + } + return total; + } + + /** + * Get the raw data for a section + * @param i + * @return + */ + public int[] getIdArray(final int i) { + return ids[i]; + } + + public int[][] getIdArrays() { + return ids; + } + + public void clear() { + ids = null; + biomes = null; + } + public int[][] getBiomeArray() { + return biomes; + } + + @Override + public void setBlock(final int x, final int y, final int z, final int id, byte data) { + final int i = FaweCache.CACHE_I[y][x][z]; + final int j = FaweCache.CACHE_J[y][x][z]; + int[] vs = ids[i]; + if (vs == null) { + vs = ids[i] = new int[4096]; + count[i]++; + } else if (vs[j] == 0) { + count[i]++; + } + switch (id) { + case 0: + air[i]++; + vs[j] = -1; + return; + case 10: + case 11: + case 39: + case 40: + case 51: + case 74: + case 89: + case 122: + case 124: + case 138: + case 169: + relight[i]++; + case 2: + case 4: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 30: + case 32: + case 37: + case 41: + case 42: + case 45: + case 46: + case 47: + case 48: + case 49: + case 55: + case 56: + case 57: + case 58: + case 60: + case 7: + case 8: + case 9: + case 73: + + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 85: + case 87: + case 88: + case 101: + case 102: + case 103: + case 110: + case 112: + case 113: + case 121: + case 129: + case 133: + case 165: + case 166: + case 170: + case 172: + case 173: + case 174: + case 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: + vs[j] = (id); + return; + case 130: + case 76: + case 62: + relight[i]++; + case 54: + case 146: + case 61: + case 65: + case 68: + case 50: + if (data < 2) { + data = 2; + } + default: + vs[j] = id + (data << 12); + return; + } + } + + @Override + public void setBiome(int x, int z, BaseBiome biome) { + if (biomes == null) { + biomes = new int[16][]; + } + int[] index = biomes[x]; + if (index == null) { + index = biomes[x] = new int[16]; + } + index[z] = biome.getId(); + } +} diff --git a/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9.java b/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9.java new file mode 100644 index 00000000..241253dc --- /dev/null +++ b/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9.java @@ -0,0 +1,699 @@ +package com.boydti.fawe.bukkit.v1_9; + +import static com.boydti.fawe.util.ReflectionUtils.getRefClass; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.block.Biome; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.v0.BukkitQueue_0; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.ChunkLoc; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.IntegerPair; +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.util.MemUtil; +import com.boydti.fawe.util.ReflectionUtils.RefClass; +import com.boydti.fawe.util.ReflectionUtils.RefConstructor; +import com.boydti.fawe.util.ReflectionUtils.RefField; +import com.boydti.fawe.util.ReflectionUtils.RefMethod; +import com.boydti.fawe.util.ReflectionUtils.RefMethod.RefExecutor; +import com.sk89q.worldedit.LocalSession; + +public class BukkitQueue_1_9 extends BukkitQueue_0 { + + private final RefClass classEntityPlayer = getRefClass("{nms}.EntityPlayer"); + private final RefClass classMapChunk = getRefClass("{nms}.PacketPlayOutMapChunk"); + private final RefClass classPacket = getRefClass("{nms}.Packet"); + private final RefClass classConnection = getRefClass("{nms}.PlayerConnection"); + private final RefClass classChunk = getRefClass("{nms}.Chunk"); + private final RefClass classCraftPlayer = getRefClass("{cb}.entity.CraftPlayer"); + private final RefClass classCraftChunk = getRefClass("{cb}.CraftChunk"); + private final RefClass classWorld = getRefClass("{nms}.World"); + private final RefField mustSave = classChunk.getField("mustSave"); + private final RefClass classBlockPosition = getRefClass("{nms}.BlockPosition"); + private final RefClass classChunkSection = getRefClass("{nms}.ChunkSection"); + private final RefClass classBlock = getRefClass("{nms}.Block"); + private final RefClass classIBlockData = getRefClass("{nms}.IBlockData"); + private final RefMethod methodGetHandleChunk; + private final RefConstructor MapChunk; + private final RefMethod methodInitLighting; + private final RefConstructor classBlockPositionConstructor; + private final RefConstructor classChunkSectionConstructor; + private final RefMethod methodW; + private final RefMethod methodAreNeighborsLoaded; + private final RefField fieldSections; + private final RefField fieldWorld; + private final RefMethod methodGetBlocks; + private final RefMethod methodGetType; + private final RefMethod methodSetType; + private final RefMethod methodGetCombinedId; + private final RefMethod methodGetByCombinedId; + private final Object air; + + private final RefMethod methodGetHandlePlayer; + private final RefField connection; + private final RefMethod send; + + public BukkitQueue_1_9() throws NoSuchMethodException, RuntimeException { + methodGetHandlePlayer = classCraftPlayer.getMethod("getHandle"); + connection = classEntityPlayer.getField("playerConnection"); + send = classConnection.getMethod("sendPacket", classPacket.getRealClass()); + methodGetHandleChunk = classCraftChunk.getMethod("getHandle"); + methodInitLighting = classChunk.getMethod("initLighting"); + MapChunk = classMapChunk.getConstructor(classChunk.getRealClass(), boolean.class, int.class); + classBlockPositionConstructor = classBlockPosition.getConstructor(int.class, int.class, int.class); + methodW = classWorld.getMethod("w", classBlockPosition.getRealClass()); + fieldSections = classChunk.getField("sections"); + fieldWorld = classChunk.getField("world"); + methodGetCombinedId = classBlock.getMethod("getCombinedId", classIBlockData.getRealClass()); + methodGetByCombinedId = classBlock.getMethod("getByCombinedId", int.class); + methodGetBlocks = classChunkSection.getMethod("getBlocks"); + methodGetType = classChunkSection.getMethod("getType", int.class, int.class, int.class); + methodSetType = classChunkSection.getMethod("setType", int.class, int.class, int.class, classIBlockData.getRealClass()); + methodAreNeighborsLoaded = classChunk.getMethod("areNeighborsLoaded", int.class); + classChunkSectionConstructor = classChunkSection.getConstructor(int.class, boolean.class, char[].class); + air = methodGetByCombinedId.call(0); + } + + @Override + public Collection> sendChunk(final Collection> fcs) { + final HashMap, Object> packets = new HashMap<>(); + final HashMap>> map = new HashMap<>(); + + for (final FaweChunk fc : fcs) { + String world = fc.getChunkLoc().world; + ArrayList> list = map.get(world); + if (list == null) { + list = new ArrayList<>(); + map.put(world, list); + } + list.add(fc); + } + final int view = Bukkit.getServer().getViewDistance(); + for (final Player player : Bukkit.getOnlinePlayers()) { + final String world = player.getWorld().getName(); + final ArrayList> list = map.get(world); + if (list == null) { + continue; + } + final Location loc = player.getLocation(); + final int cx = loc.getBlockX() >> 4; + final int cz = loc.getBlockZ() >> 4; + final Object entity = methodGetHandlePlayer.of(player).call(); + + for (final FaweChunk fc : list) { + final int dx = Math.abs(cx - fc.getChunkLoc().x); + final int dz = Math.abs(cz - fc.getChunkLoc().z); + if ((dx > view) || (dz > view)) { + continue; + } + RefExecutor con = send.of(connection.of(entity).get()); + Object packet = packets.get(fc); + if (packet == null) { + final Object c = methodGetHandleChunk.of(fc.getChunk()).call(); + packet = MapChunk.create(c, true, 65535); + packets.put(fc, packet); + con.call(packet); + } else { + con.call(packet); + } + } + } + final HashSet> chunks = new HashSet>(); + for (FaweChunk fc : fcs) { + Chunk chunk = fc.getChunk(); + chunk.unload(true, false); + chunk.load(); + ChunkLoc loc = fc.getChunkLoc(); + chunk.getWorld().refreshChunk(loc.x, loc.z); + if (!fixLighting(fc, Settings.FIX_ALL_LIGHTING)) { + chunks.add(fc); + } + } + return chunks; + } + + @Override + public boolean fixLighting(final FaweChunk pc, boolean fixAll) { + try { + BukkitChunk_1_9 bc = (BukkitChunk_1_9) pc; + final Chunk chunk = bc.getChunk(); + if (!chunk.isLoaded()) { + chunk.load(false); + } else { + chunk.unload(true, false); + chunk.load(false); + } + + // Initialize lighting + final Object c = methodGetHandleChunk.of(chunk).call(); + + if (fixAll && !(boolean) methodAreNeighborsLoaded.of(c).call(1)) { + World world = chunk.getWorld(); + ChunkLoc wrapper = bc.getChunkLoc(); + String worldname = wrapper.world; + for (int x = wrapper.x - 1; x <= wrapper.x + 1; x++) { + for (int z = wrapper.z - 1; z <= wrapper.z + 1; z++) { + if (x != 0 && z != 0) { + Chunk other = world.getChunkAt(x, z); + while (!other.isLoaded()) { + other.load(true); + } + } + } + } + } + + methodInitLighting.of(c).call(); + + if ((bc.getTotalRelight() == 0 && !fixAll)) { + return true; + } + + final Object[] sections = (Object[]) fieldSections.of(c).get(); + final Object w = fieldWorld.of(c).get(); + + final int X = chunk.getX() << 4; + final int Z = chunk.getZ() << 4; + + RefExecutor relight = methodW.of(w); + for (int j = 0; j < sections.length; j++) { + final Object section = sections[j]; + if (section == null) { + continue; + } + if ((bc.getRelight(j) == 0 && !fixAll) || bc.getCount(j) == 0 || (bc.getCount(j) >= 4096 && bc.getAir(j) == 0)) { + continue; + } + final int[] array = bc.getIdArray(j); + int l = PseudoRandom.random.random(2); + for (int k = 0; k < array.length; k++) { + final int i = array[k]; + if (i < 16) { + continue; + } + final short id = (short) (i >> 4); + switch (id) { // Lighting + default: + if (!fixAll) { + continue; + } + if ((k & 1) == l) { + l = 1 - l; + continue; + } + case 10: + case 11: + case 39: + case 40: + case 50: + case 51: + case 62: + case 74: + case 76: + case 89: + case 122: + case 124: + case 130: + case 138: + case 169: + final int x = FaweCache.CACHE_X[j][k]; + final int y = FaweCache.CACHE_Y[j][k]; + final int z = FaweCache.CACHE_Z[j][k]; + if (isSurrounded(bc.getIdArrays(), x, y, z)) { + continue; + } + final Object pos = classBlockPositionConstructor.create(X + x, y, Z + z); + relight.call(pos); + } + } + } + return true; + } catch (final Throwable e) { + e.printStackTrace(); + } + return false; + } + + public boolean isSurrounded(int[][] sections, int x, int y, int z) { + return isSolid(getId(sections, x, y + 1, z)) + && isSolid(getId(sections, x + 1, y - 1, z)) + && isSolid(getId(sections, x - 1, y, z)) + && isSolid(getId(sections, x, y, z + 1)) + && isSolid(getId(sections, x, y, z - 1)); + } + + public boolean isSolid(int i) { + return i != 0 && Material.getMaterial(i).isOccluding(); + } + + public int getId(int[][] sections, int x, int y, int z) { + if (x < 0 || x > 15 || z < 0 || z > 15) { + return 1; + } + if (y < 0 || y > 255) { + return 1; + } + int i = FaweCache.CACHE_I[y][x][z]; + int[] section = sections[i]; + if (section == null) { + return 0; + } + int j = FaweCache.CACHE_J[y][x][z]; + return section[j]; + } + + public Object getBlocks(final Object obj) { + return methodGetBlocks.of(obj).call(); + } + + @Override + public boolean setComponents(final FaweChunk pc) { + BukkitChunk_1_9 fs = (BukkitChunk_1_9) pc; + Chunk chunk = pc.getChunk(); + final World world = chunk.getWorld(); + ChunkLoc wrapper = pc.getChunkLoc(); + chunk.load(true); + try { + final boolean flag = world.getEnvironment() == Environment.NORMAL; + + // Sections + final Method getHandele = chunk.getClass().getDeclaredMethod("getHandle"); + final Object c = getHandele.invoke(chunk); + final Class clazz = c.getClass(); + final Field sf = clazz.getDeclaredField("sections"); + sf.setAccessible(true); + final Field tf = clazz.getDeclaredField("tileEntities"); + final Field ef = clazz.getDeclaredField("entitySlices"); + + final Object[] sections = (Object[]) sf.get(c); + final HashMap tiles = (HashMap) tf.get(c); + final List[] entities = (List[]) ef.get(c); + + Method xm = null; + Method ym = null; + Method zm = null; + + // Trim tiles + final Set> entryset = (Set>) (Set) tiles.entrySet(); + final Iterator> iter = entryset.iterator(); + while (iter.hasNext()) { + final Entry tile = iter.next(); + final Object pos = tile.getKey(); + if (xm == null) { + final Class clazz2 = pos.getClass().getSuperclass(); + xm = clazz2.getDeclaredMethod("getX"); + ym = clazz2.getDeclaredMethod("getY"); + zm = clazz2.getDeclaredMethod("getZ"); + } + final int lx = (int) xm.invoke(pos) & 15; + final int ly = (int) ym.invoke(pos); + final int lz = (int) zm.invoke(pos) & 15; + final int j = FaweCache.CACHE_I[ly][lx][lz]; + final int k = FaweCache.CACHE_J[ly][lx][lz]; + final int[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + if (array[k] != 0) { + iter.remove(); + } + } + + // Trim entities + for (int i = 0; i < 16; i++) { + if ((entities[i] != null) && (fs.getCount(i) >= 4096)) { + entities[i].clear(); + } + } + + // Efficiently merge sections + for (int j = 0; j < sections.length; j++) { + if (fs.getCount(j) == 0) { + continue; + } + final int[] newArray = fs.getIdArray(j); + if (newArray == null) { + continue; + } + Object section = sections[j]; + if ((section == null) || (fs.getCount(j) >= 4096)) { + char[] array = new char[4096]; + for (int i = 0; i < newArray.length; i++) { + int combined = newArray[i]; + int id = combined & 4095; + int data = combined >> 12; + array[i] = (char) ((id << 4) + data); + } + section = sections[j] = newChunkSection(j << 4, flag, array); + continue; + } + final Object currentArray = getBlocks(section); + RefExecutor setType = methodSetType.of(section); + boolean fill = true; + for (int k = 0; k < newArray.length; k++) { + final int n = newArray[k]; + switch (n) { + case 0: + fill = false; + continue; + case -1: { + fill = false; + int x = FaweCache.CACHE_X[j][k]; + int y = FaweCache.CACHE_Y[j][k]; + int z = FaweCache.CACHE_Z[j][k]; + setType.call(x, y & 15, z, air); + continue; + } + default: { + int x = FaweCache.CACHE_X[j][k]; + int y = FaweCache.CACHE_Y[j][k]; + int z = FaweCache.CACHE_Z[j][k]; + int id = n; + Object iblock = methodGetByCombinedId.call(n); + setType.call(x, y & 15, z, iblock); + continue; + } + } + } + if (fill) { + fs.setCount(j, Short.MAX_VALUE); + } + } + // Clear + } catch (IllegalAccessException | IllegalArgumentException | NoSuchMethodException | SecurityException | InvocationTargetException | NoSuchFieldException e) { + e.printStackTrace(); + } + int[][] biomes = fs.biomes; + Biome[] values = Biome.values(); + if (biomes != null) { + for (int x = 0; x < 16; x++) { + int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + int biome = array[z]; + if (biome == 0) { + continue; + } + chunk.getBlock(x, 0, z).setBiome(values[biome]); + } + } + } + return true; + } + + @Override + public void clear() { + super.clear(); + ArrayDeque toUnload = new ArrayDeque<>(); + final int distance = Bukkit.getViewDistance() + 2; + HashMap> players = new HashMap<>(); + for (final Player player : Bukkit.getOnlinePlayers()) { + // Clear history + FawePlayer fp = FawePlayer.wrap(player); + LocalSession s = fp.getSession(); + if (s != null) { + s.clearHistory(); + s.setClipboard(null); + } + final Location loc = player.getLocation(); + final World worldObj = loc.getWorld(); + final String world = worldObj.getName(); + HashMap map = players.get(world); + if (map == null) { + map = new HashMap<>(); + players.put(world, map); + } + final IntegerPair origin = new IntegerPair(loc.getBlockX() >> 4, loc.getBlockZ() >> 4); + Integer val = map.get(origin); + int check; + if (val != null) { + if (val == distance) { + continue; + } + check = distance - val; + } else { + check = distance; + map.put(origin, distance); + } + for (int x = -distance; x <= distance; x++) { + if ((x >= check) || (-x >= check)) { + continue; + } + for (int z = -distance; z <= distance; z++) { + if ((z >= check) || (-z >= check)) { + continue; + } + final int weight = distance - Math.max(Math.abs(x), Math.abs(z)); + final IntegerPair chunk = new IntegerPair(x + origin.x, z + origin.z); + val = map.get(chunk); + if ((val == null) || (val < weight)) { + map.put(chunk, weight); + } + } + } + } + Fawe.get().getWorldEdit().clearSessions(); + for (final World world : Bukkit.getWorlds()) { + final String name = world.getName(); + final HashMap map = players.get(name); + if ((map == null) || (map.size() == 0)) { + final boolean save = world.isAutoSave(); + world.setAutoSave(false); + for (final Chunk chunk : world.getLoadedChunks()) { + unloadChunk(name, chunk); + } + world.setAutoSave(save); + continue; + } + final Chunk[] chunks = world.getLoadedChunks(); + for (final Chunk chunk : chunks) { + final int x = chunk.getX(); + final int z = chunk.getZ(); + if (!map.containsKey(new IntegerPair(x, z))) { + toUnload.add(chunk); + } else if (chunk.getEntities().length > 4096) { + for (final Entity ent : chunk.getEntities()) { + ent.remove(); + } + } + } + } + // GC again + System.gc(); + System.gc(); + // If still critical memory + int free = MemUtil.calculateMemory(); + if (free <= 1) { + for (final Chunk chunk : toUnload) { + unloadChunk(chunk.getWorld().getName(), chunk); + } + } else if (free == Integer.MAX_VALUE) { + for (final Chunk chunk : toUnload) { + chunk.unload(true, false); + } + } else { + return; + } + toUnload = null; + players = null; + System.gc(); + System.gc(); + free = MemUtil.calculateMemory(); + if (free > 1) { + return; + } + Collection online = Bukkit.getOnlinePlayers(); + if (online.size() > 0) { + online.iterator().next().kickPlayer("java.lang.OutOfMemoryError"); + } + online = null; + System.gc(); + System.gc(); + free = MemUtil.calculateMemory(); + if ((free > 1) || (Bukkit.getOnlinePlayers().size() > 0)) { + return; + } + for (final World world : Bukkit.getWorlds()) { + final String name = world.getName(); + for (final Chunk chunk : world.getLoadedChunks()) { + unloadChunk(name, chunk); + } + } + System.gc(); + System.gc(); + } + + public Object newChunkSection(final int i, final boolean flag, final char[] ids) { + return classChunkSectionConstructor.create(i, flag, ids); + } + + @Override + public FaweChunk getChunk(final ChunkLoc wrap) { + return new BukkitChunk_1_9(wrap); + } + + public boolean unloadChunk(final String world, final Chunk chunk) { + final Object c = methodGetHandleChunk.of(chunk).call(); + mustSave.of(c).set(false); + if (chunk.isLoaded()) { + chunk.unload(false, false); + } + return true; + } + + public ChunkGenerator setGenerator(final World world, final ChunkGenerator newGen) { + try { + final ChunkGenerator gen = world.getGenerator(); + final Class clazz = world.getClass(); + final Field generator = clazz.getDeclaredField("generator"); + generator.setAccessible(true); + generator.set(world, newGen); + + final Field wf = clazz.getDeclaredField("world"); + wf.setAccessible(true); + final Object w = wf.get(world); + final Class clazz2 = w.getClass().getSuperclass(); + final Field generator2 = clazz2.getDeclaredField("generator"); + generator2.set(w, newGen); + + return gen; + } catch (final Exception e) { + e.printStackTrace(); + } + return null; + } + + public List setPopulator(final World world, final List newPop) { + try { + final List pop = world.getPopulators(); + final Field populators = world.getClass().getDeclaredField("populators"); + populators.setAccessible(true); + populators.set(world, newPop); + return pop; + } catch (final Exception e) { + e.printStackTrace(); + } + return null; + } + + public void setEntitiesAndTiles(final Chunk chunk, final List[] entities, final Map tiles) { + try { + final Class clazz = chunk.getClass(); + final Method handle = clazz.getMethod("getHandle"); + final Object c = handle.invoke(chunk); + final Class clazz2 = c.getClass(); + + if (tiles.size() > 0) { + final Field tef = clazz2.getDeclaredField("tileEntities"); + final Map te = (Map) tef.get(c); + final Method put = te.getClass().getMethod("putAll", Map.class); + put.invoke(te, tiles); + } + + final Field esf = clazz2.getDeclaredField("entitySlices"); + esf.setAccessible(true); + esf.set(c, entities); + } catch (final Exception e) { + e.printStackTrace(); + } + } + + public Object getProvider(final World world) { + try { + // Provider 1 + final Class clazz = world.getClass(); + final Field wf = clazz.getDeclaredField("world"); + wf.setAccessible(true); + final Object w = wf.get(world); + final Field provider = w.getClass().getSuperclass().getDeclaredField("chunkProvider"); + provider.setAccessible(true); + // ChunkProviderServer + final Class clazz2 = w.getClass(); + final Field wpsf = clazz2.getDeclaredField("chunkProviderServer"); + // Store old provider server + final Object worldProviderServer = wpsf.get(w); + // Store the old provider + final Field cp = worldProviderServer.getClass().getDeclaredField("chunkProvider"); + return cp.get(worldProviderServer); + } catch (final Exception e) { + e.printStackTrace(); + } + return null; + } + + public Object setProvider(final World world, Object newProvider) { + try { + // Provider 1 + final Class clazz = world.getClass(); + final Field wf = clazz.getDeclaredField("world"); + wf.setAccessible(true); + final Object w = wf.get(world); + // ChunkProviderServer + final Class clazz2 = w.getClass(); + final Field wpsf = clazz2.getDeclaredField("chunkProviderServer"); + // Store old provider server + final Object worldProviderServer = wpsf.get(w); + // Store the old provider + final Field cp = worldProviderServer.getClass().getDeclaredField("chunkProvider"); + final Object oldProvider = cp.get(worldProviderServer); + // Provider 2 + final Class clazz3 = worldProviderServer.getClass(); + final Field provider2 = clazz3.getDeclaredField("chunkProvider"); + // If the provider needs to be calculated + if (newProvider == null) { + Method k; + try { + k = clazz2.getDeclaredMethod("k"); + } catch (final Throwable e) { + try { + k = clazz2.getDeclaredMethod("j"); + } catch (final Throwable e2) { + e2.printStackTrace(); + return null; + } + } + k.setAccessible(true); + final Object tempProviderServer = k.invoke(w); + newProvider = cp.get(tempProviderServer); + // Restore old provider + wpsf.set(w, worldProviderServer); + } + // Set provider for provider server + provider2.set(worldProviderServer, newProvider); + // Return the previous provider + return oldProvider; + } catch (final Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/com/boydti/fawe/object/PseudoRandom.java b/src/main/java/com/boydti/fawe/object/PseudoRandom.java index f941e87a..1285056c 100644 --- a/src/main/java/com/boydti/fawe/object/PseudoRandom.java +++ b/src/main/java/com/boydti/fawe/object/PseudoRandom.java @@ -1,6 +1,9 @@ package com.boydti.fawe.object; public class PseudoRandom { + + public static PseudoRandom random = new PseudoRandom(); + private long state; public PseudoRandom() {