diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java index caf451ce4..205eca913 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java @@ -24,10 +24,7 @@ import com.plotsquared.bukkit.listeners.*; import com.plotsquared.bukkit.listeners.worldedit.WEListener; import com.plotsquared.bukkit.titles.DefaultTitle; import com.plotsquared.bukkit.util.*; -import com.plotsquared.bukkit.util.block.FastQueue_1_7; -import com.plotsquared.bukkit.util.block.FastQueue_1_8; -import com.plotsquared.bukkit.util.block.FastQueue_1_8_3; -import com.plotsquared.bukkit.util.block.SlowQueue; +import com.plotsquared.bukkit.util.block.*; import com.plotsquared.bukkit.uuid.*; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import org.bukkit.*; @@ -366,6 +363,15 @@ public class BukkitMain extends JavaPlugin implements Listener, IPlotMain { e.printStackTrace(); MainUtil.canSendChunk = false; } + if (PS.get().checkVersion(getServerVersion(), 1, 9, 0)) { + try { + return new FastQueue_1_9(); + } + catch (Throwable e) { + e.printStackTrace(); + return new SlowQueue(); + } + } if (PS.get().checkVersion(getServerVersion(), 1, 8, 0)) { try { return new FastQueue_1_8_3(); diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_9.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_9.java index 66f674417..a47854654 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_9.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_9.java @@ -1,35 +1,250 @@ package com.plotsquared.bukkit.util.block; +import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue; +import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; import com.plotsquared.bukkit.util.BukkitUtil; +import org.bukkit.Bukkit; import org.bukkit.Chunk; +import java.util.Arrays; + public class FastChunk_1_9 extends PlotChunk { - public FastChunk_1_9(SetQueue.ChunkWrapper wrap) { - super(wrap); + public int[][] ids; + public short[] count; + public short[] air; + public short[] relight; + public int[][] biomes; + public Chunk chunk; + public FastChunk_1_9(final ChunkWrapper chunk) { + super(chunk); + ids = new int[16][]; + count = new short[16]; + air = new short[16]; + relight = new short[16]; } @Override public Chunk getChunkAbs() { - SetQueue.ChunkWrapper loc = getChunkWrapper(); + ChunkWrapper loc = getChunkWrapper(); return BukkitUtil.getWorld(loc.world).getChunkAt(loc.x, loc.z); } - @Override public void setBlock(int x, int y, int z, int id, byte data) { - + @Override + public Chunk getChunk() { + if (chunk == null) { + final ChunkWrapper cl = getChunkWrapper(); + chunk = Bukkit.getWorld(cl.world).getChunkAt(cl.x, cl.z); + } + return chunk; } - @Override public void setBiome(int x, int z, int biome) { - + @Override + public void setChunkWrapper(final ChunkWrapper loc) { + super.setChunkWrapper(loc); + chunk = null; } - @Override public PlotChunk clone() { - return null; + /** + * Get the number of block changes in a specified section + * @param i + * @return + */ + public int getCount(final int i) { + return count[i]; } - @Override public PlotChunk shallowClone() { - return null; + 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) { + 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; + } + + @Override + public void setBlock(final int x, final int y, final int z, final int id, byte data) { + final int i = MainUtil.CACHE_I[y][x][z]; + final int j = MainUtil.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 PlotChunk clone() { + FastChunk_1_9 toReturn = new FastChunk_1_9(getChunkWrapper()); + toReturn.air = air.clone(); + toReturn.count = count.clone(); + toReturn.relight = relight.clone(); + toReturn.ids = new int[ids.length][]; + for (int i = 0; i < ids.length; i++) { + int[] matrix = ids[i]; + if (matrix != null) { + toReturn.ids[i] = new int[matrix.length]; + System.arraycopy(matrix, 0, toReturn.ids[i], 0, matrix.length); + } + } + return toReturn; + } + + @Override + public PlotChunk shallowClone() { + FastChunk_1_9 toReturn = new FastChunk_1_9(getChunkWrapper()); + toReturn.air = air; + toReturn.count = count; + toReturn.relight = relight; + toReturn.ids = ids; + return toReturn; + } + + @Override + public void setBiome(int x, int z, int biome) { + if (biomes == null) { + biomes = new int[16][16]; + } + biomes[x][z] = biome; } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8_3.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8_3.java index 25f5a1005..f0dcb1341 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8_3.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8_3.java @@ -1,20 +1,14 @@ package com.plotsquared.bukkit.util.block; -import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; - import com.intellectualcrafters.plot.object.ChunkLoc; import com.intellectualcrafters.plot.object.PseudoRandom; -import com.intellectualcrafters.plot.util.ChunkManager; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; +import com.intellectualcrafters.plot.util.*; import com.intellectualcrafters.plot.util.ReflectionUtils.RefClass; import com.intellectualcrafters.plot.util.ReflectionUtils.RefConstructor; import com.intellectualcrafters.plot.util.ReflectionUtils.RefField; import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod; import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod.RefExecutor; -import com.intellectualcrafters.plot.util.SetQueue; import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.intellectualcrafters.plot.util.TaskManager; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.bukkit.util.SendChunk; import org.bukkit.Chunk; @@ -26,13 +20,10 @@ import org.bukkit.block.Biome; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; + +import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; public class FastQueue_1_8_3 extends SlowQueue { @@ -292,9 +283,9 @@ public class FastQueue_1_8_3 extends SlowQueue { } } } - if (!(boolean) methodAreNeighborsLoaded.of(c).call(1)) { - return false; - } +// if (!(boolean) methodAreNeighborsLoaded.of(c).call(1)) { +// return false; +// } } methodInitLighting.of(c).call(); diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_9.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_9.java index 0fd868a8b..f93e13ed0 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_9.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_9.java @@ -1,12 +1,455 @@ package com.plotsquared.bukkit.util.block; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue; +import com.intellectualcrafters.plot.object.ChunkLoc; +import com.intellectualcrafters.plot.object.PseudoRandom; +import com.intellectualcrafters.plot.util.*; +import com.intellectualcrafters.plot.util.ReflectionUtils.RefClass; +import com.intellectualcrafters.plot.util.ReflectionUtils.RefConstructor; +import com.intellectualcrafters.plot.util.ReflectionUtils.RefField; +import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod; +import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod.RefExecutor; +import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; +import com.plotsquared.bukkit.util.BukkitUtil; +import com.plotsquared.bukkit.util.SendChunk; import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.block.Biome; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.Map.Entry; + +import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; public class FastQueue_1_9 extends SlowQueue { - @Override public PlotChunk getChunk(SetQueue.ChunkWrapper wrap) { + public final SendChunk chunksender; + 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"); + public HashMap toUpdate = new HashMap<>(); + private RefMethod methodGetHandleChunk; + private RefConstructor MapChunk; + private RefMethod methodInitLighting; + private RefConstructor classBlockPositionConstructor; + private RefConstructor classChunkSectionConstructor; + private RefMethod methodW; + private RefMethod methodAreNeighborsLoaded; + private RefField fieldSections; + private RefField fieldWorld; + private RefMethod methodGetBlocks; + private RefMethod methodGetType; + private RefMethod methodSetType; + private RefMethod methodGetCombinedId; + private RefMethod methodGetByCombinedId; + final Object air; + + + public FastQueue_1_9() throws NoSuchMethodException, RuntimeException { + 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); + chunksender = new SendChunk(); + TaskManager.runTaskRepeat(new Runnable() { + @Override + public void run() { + if (toUpdate.isEmpty()) { + return; + } + int count = 0; + final ArrayList chunks = new ArrayList(); + final Iterator> i = toUpdate.entrySet().iterator(); + while (i.hasNext() && (count < 128)) { + chunks.add(i.next().getValue()); + i.remove(); + count++; + } + if (count == 0) { + return; + } + update(chunks); + } + }, 1); + MainUtil.initCache(); + } + + public void update(final Collection chunks) { + if (chunks.isEmpty()) { + return; + } + if (!MainUtil.canSendChunk) { + for (final Chunk chunk : chunks) { + chunk.getWorld().refreshChunk(chunk.getX(), chunk.getZ()); + chunk.unload(true, false); + chunk.load(); + } + return; + } + try { + chunksender.sendChunk(chunks); + } catch (final Throwable e) { + e.printStackTrace(); + MainUtil.canSendChunk = false; + } + } + + /** + * This should be overridden by any specialized queues + * @param pc + */ + @Override + public void execute(PlotChunk pc) { + FastChunk_1_9 fs = (FastChunk_1_9) pc; + Chunk chunk = pc.getChunk(); + final World world = chunk.getWorld(); + ChunkWrapper wrapper = pc.getChunkWrapper(); + if (!toUpdate.containsKey(wrapper)) { + toUpdate.put(wrapper, chunk); + } + 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 = MainUtil.CACHE_I[ly][lx][lz]; + final int k = MainUtil.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 = MainUtil.x_loc[j][k]; + int y = MainUtil.y_loc[j][k]; + int z = MainUtil.z_loc[j][k]; + setType.call(x, y & 15, z, air); + continue; + } + default: { + int x = MainUtil.x_loc[j][k]; + int y = MainUtil.y_loc[j][k]; + int z = MainUtil.z_loc[j][k]; + int id = (int) n; + Object iblock = methodGetByCombinedId.call((int) 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]); + } + } + } + } + + public Object newChunkSection(final int i, final boolean flag, final char[] ids) { + return classChunkSectionConstructor.create(i, flag, ids); + } + + public Object getBlocks(final Object obj) { + return methodGetBlocks.of(obj).call(); + } + + /** + * This should be overridden by any specialized queues + * @param wrap + */ + @Override + public PlotChunk getChunk(ChunkWrapper wrap) { return new FastChunk_1_9(wrap); } + + /** + * This should be overridden by any specialized queues + * @param pc + */ + @Override + public boolean fixLighting(PlotChunk pc, boolean fixAll) { + try { + FastChunk_1_9 bc = (FastChunk_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(); + ChunkWrapper wrapper = bc.getChunkWrapper(); + 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); + } + ChunkManager.manager.loadChunk(worldname, new ChunkLoc(x, z), 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 = MainUtil.x_loc[j][k]; + final int y = MainUtil.y_loc[j][k]; + final int z = MainUtil.z_loc[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 = MainUtil.CACHE_I[y][x][z]; + int[] section = sections[i]; + if (section == null) { + return 0; + } + int j = MainUtil.CACHE_J[y][x][z]; + return section[j]; + } + + public int getId(Object section, int x, int y, int z) { + int j = MainUtil.CACHE_J[y][x][z]; + Object iblock = methodGetType.of(section).call(x, y & 15, z); + return (int) methodGetCombinedId.call(iblock); + } + + public int getId(Object[] 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 = MainUtil.CACHE_I[y][x][z]; + Object section = sections[i]; + if (section == null) { + return 0; + } +// Object array = getBlocks(section); + return getId(section, x, y, z); + } + + /** + * This should be overridden by any specialized queues + * @param world + * @param locs + */ + @Override + public void sendChunk(String world, Collection locs) { + World worldObj = BukkitUtil.getWorld(world); + for (ChunkLoc loc : locs) { + ChunkWrapper wrapper = SetQueue.IMP.new ChunkWrapper(world, loc.x, loc.z); + if (!toUpdate.containsKey(wrapper)) { + toUpdate.put(wrapper, worldObj.getChunkAt(loc.x, loc.z)); + } + } + } }