package com.intellectualcrafters.plot.util.block; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.RunnableVal; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.MathMan; import com.intellectualcrafters.plot.util.TaskManager; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; public abstract class BasicLocalBlockQueue<T> extends LocalBlockQueue { private final String world; private long modified; private final ConcurrentHashMap<Long, LocalChunk> blocks = new ConcurrentHashMap<>(); private final ConcurrentLinkedDeque<LocalChunk> chunks = new ConcurrentLinkedDeque<>(); public BasicLocalBlockQueue(String world) { super(world); this.world = world; this.modified = System.currentTimeMillis(); } public abstract LocalChunk getLocalChunk(int x, int z); @Override public abstract PlotBlock getBlock(int x, int y, int z); public abstract void setComponents(LocalChunk<T> lc); @Override public final String getWorld() { return world; } private LocalChunk lastWrappedChunk; private int lastX = Integer.MIN_VALUE; private int lastZ = Integer.MIN_VALUE; @Override public final boolean next() { lastX = Integer.MIN_VALUE; lastZ = Integer.MIN_VALUE; try { if (this.blocks.size() == 0) { return false; } synchronized (blocks) { LocalChunk chunk = chunks.poll(); if (chunk != null) { blocks.remove(chunk.longHash()); this.execute(chunk); return true; } } } catch (Throwable e) { e.printStackTrace(); } return false; } public final boolean execute(final LocalChunk<T> lc) { if (lc == null) { return false; } this.setComponents(lc); return true; } @Override public void startSet(boolean parallel) { // Do nothing } @Override public void endSet(boolean parallel) { // Do nothing } @Override public final int size() { return chunks.size(); } @Override public final void setModified(long modified) { this.modified = modified; } @Override public final long getModified() { return modified; } @Override public final boolean setBlock(int x, int y, int z, int id, int data) { if ((y > 255) || (y < 0)) { return false; } int cx = x >> 4; int cz = z >> 4; if (cx != lastX || cz != lastZ) { lastX = cx; lastZ = cz; long pair = (long) (cx) << 32 | (cz) & 0xFFFFFFFFL; lastWrappedChunk = this.blocks.get(pair); if (lastWrappedChunk == null) { lastWrappedChunk = this.getLocalChunk(x >> 4, z >> 4); lastWrappedChunk.setBlock(x & 15, y, z & 15, id, data); LocalChunk previous = this.blocks.put(pair, lastWrappedChunk); if (previous == null) { chunks.add(lastWrappedChunk); return true; } this.blocks.put(pair, previous); lastWrappedChunk = previous; } } lastWrappedChunk.setBlock(x & 15, y, z & 15, id, data); return true; } @Override public final boolean setBiome(int x, int z, String biome) { long pair = (long) (x >> 4) << 32 | (z >> 4) & 0xFFFFFFFFL; LocalChunk result = this.blocks.get(pair); if (result == null) { result = this.getLocalChunk(x >> 4, z >> 4); LocalChunk previous = this.blocks.put(pair, result); if (previous != null) { this.blocks.put(pair, previous); result = previous; } else { chunks.add(result); } } result.setBiome(x & 15, z & 15, biome); return true; } public final void setChunk(LocalChunk<T> chunk) { LocalChunk previous = this.blocks.put(chunk.longHash(), (LocalChunk) chunk); if (previous != null) { chunks.remove(previous); } chunks.add((LocalChunk) chunk); } public abstract class LocalChunk<T> { public final BasicLocalBlockQueue parent; public final int z; public final int x; public T[] blocks; public String[][] biomes; public LocalChunk(BasicLocalBlockQueue<T> parent, int x, int z) { this.parent = parent; this.x = x; this.z = z; } /** * Get the parent queue this chunk belongs to * @return */ public BasicLocalBlockQueue getParent() { return parent; } public int getX() { return x; } public int getZ() { return z; } /** * Add the chunk to the queue */ public void addToQueue() { parent.setChunk(this); } public void fill(int id, int data) { fillCuboid(0, 15, 0, 255, 0, 15, id, data); } /** * Fill a cuboid in this chunk with a block * @param x1 * @param x2 * @param y1 * @param y2 * @param z1 * @param z2 * @param id * @param data */ public void fillCuboid(int x1, int x2, int y1, int y2, int z1, int z2, int id, int data) { for (int x = x1; x <= x2; x++) { for (int y = y1; y <= y2; y++) { for (int z = z1; z <= z2; z++) { setBlock(x, y, z, id, data); } } } } public abstract void setBlock(final int x, final int y, final int z, final int id, final int data); public void setBiome(int x, int z, String biome) { if (this.biomes == null) { this.biomes = new String[16][]; } String[] index = this.biomes[x]; if (index == null) { index = this.biomes[x] = new String[16]; } index[z] = biome; } public long longHash() { return MathMan.pairInt(x, z); } @Override public int hashCode() { return MathMan.pair((short) x, (short) z); } } public class BasicLocalChunk extends LocalChunk<PlotBlock[]> { public BasicLocalChunk(BasicLocalBlockQueue parent, int x, int z) { super(parent, x, z); blocks = new PlotBlock[16][]; } public void setBlock(final int x, final int y, final int z, final int id, final int data) { PlotBlock block = PlotBlock.get(id, data); int i = MainUtil.CACHE_I[y][x][z]; int j = MainUtil.CACHE_J[y][x][z]; PlotBlock[] array = blocks[i]; if (array == null) { array = (blocks[i] = new PlotBlock[4096]); } array[j] = block; } } public class CharLocalChunk extends LocalChunk<char[]> { public CharLocalChunk(BasicLocalBlockQueue parent, int x, int z) { super(parent, x, z); blocks = new char[16][]; } public void setBlock(final int x, final int y, final int z, final int id, final int data) { PlotBlock block = PlotBlock.get(id, data); int i = MainUtil.CACHE_I[y][x][z]; int j = MainUtil.CACHE_J[y][x][z]; char[] array = blocks[i]; if (array == null) { array = (blocks[i] = new char[4096]); } array[j] = (char) ((block.id << 4) + block.data); } } @Override public void flush() { GlobalBlockQueue.IMP.dequeue(this); TaskManager.IMP.sync(new RunnableVal<Object>() { @Override public void run(Object value) { while (next()); } }); } }