package kcauldron; import java.util.HashSet; import java.util.Iterator; import java.util.NavigableSet; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import gnu.trove.iterator.TLongObjectIterator; import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; import net.minecraft.block.Block; import net.minecraft.world.NextTickListEntry; import net.minecraft.world.WorldServer; import net.minecraft.world.chunk.Chunk; public final class BlockUpdatesTracker implements Iterable { private long lastEntryId = Long.MIN_VALUE; private final WorldServer world; private final NavigableSet sortedTree = new TreeSet(); private final TLongObjectMap trackerMap = new TLongObjectHashMap(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); public BlockUpdatesTracker(WorldServer world) { this.world = world; } public BlockUpdateEntry allocateEntry(int x, int y, int z, int priority, long time, Block block) { return allocateEntry(x, y, z, priority, time, block, true); } public BlockUpdateEntry allocateEntry(int x, int y, int z, int priority, long time, Block block, boolean allowChunkTracking) { lock.writeLock().lock(); try { Chunk chunk = allowChunkTracking ? world.getChunkIfLoaded(x >> 4, z >> 4) : null; BlockUpdateEntry entry = new BlockUpdateEntry(x, y, z, priority, time, block, lastEntryId++); BlockUpdateEntry oldEntry = trackerMap.remove(entry.key); if (oldEntry != null) { sortedTree.remove(oldEntry); if (chunk != null) chunk.blockUpdates.remove(oldEntry); } trackerMap.put(entry.key, entry); sortedTree.add(entry); if (chunk != null) chunk.blockUpdates.add(entry); return entry; } finally { lock.writeLock().unlock(); } } public BlockUpdateEntry removeEntry(int x, int y, int z) { return removeEntry(x, y, z, true); } public BlockUpdateEntry removeEntry(int x, int y, int z, boolean allowChunkTracking) { lock.writeLock().lock(); try { Chunk chunk = allowChunkTracking ? world.getChunkIfLoaded(x >> 4, z >> 4) : null; BlockUpdateEntry entry = trackerMap.remove(new BlockCoords(x, y, z).key); if (entry != null) { sortedTree.remove(entry); if (chunk != null) chunk.blockUpdates.remove(entry); } return entry; } finally { lock.writeLock().unlock(); } } public BlockUpdateEntry removeEntry(BlockUpdateEntry entry, boolean allowChunkTracking) { lock.writeLock().lock(); try { Chunk chunk = allowChunkTracking ? world.getChunkIfLoaded(entry.x >> 4, entry.z >> 4) : null; trackerMap.remove(entry.key); if (entry != null) { sortedTree.remove(entry); if (chunk != null) chunk.blockUpdates.remove(entry); } return entry; } finally { lock.writeLock().unlock(); } } public BlockUpdateEntry getEntry(int x, int y, int z) { lock.readLock().lock(); try { return trackerMap.get(new BlockCoords(x, y, z).key); } finally { lock.readLock().unlock(); } } public BlockUpdateEntry peek() { lock.readLock().lock(); try { return sortedTree.first(); } finally { lock.readLock().unlock(); } } public boolean isEmpty() { lock.readLock().lock(); try { return trackerMap.isEmpty(); } finally { lock.readLock().unlock(); } } public int size() { lock.readLock().lock(); try { return trackerMap.size(); } finally { lock.readLock().unlock(); } } @Override public Iterator iterator() { return new Iterator() { private final Iterator iterator = sortedTree.iterator();; private BlockUpdateEntry next; @Override public boolean hasNext() { next = null; return iterator.hasNext(); } @Override public BlockUpdateEntry next() { return next = iterator.next(); } @Override public void remove() { if (next == null) next(); if (next != null) removeEntry(next, true); } }; } private final VanillaWrapper wrapper = new VanillaWrapper(); public final HashSet hashSet = new VanillaHashSetWrapper(); public final TreeSet treeSet = new VanillaTreeSetWrapper(); private final class VanillaWrapper { private Set add = new HashSet(); private Set remove = new HashSet(); public boolean add(NextTickListEntry e) { if (add.remove(e)) { allocateEntry(e.xCoord, e.yCoord, e.zCoord, e.priority, e.scheduledTime, e.func_151351_a()); return true; } return add.add(e); } public boolean remove(NextTickListEntry e) { if (remove.remove(e)) { removeEntry(e.xCoord, e.yCoord, e.zCoord); return true; } return remove.add(e); } public boolean contains(NextTickListEntry e, boolean allowInterscan) { if (allowInterscan) { if (add.contains(e)) return true; if (remove.contains(e)) return false; } return getEntry(e.xCoord, e.yCoord, e.zCoord) != null; } } private final class VanillaHashSetWrapper extends HashSet { @Override public Iterator iterator() { return new Iterator() { private final TLongObjectIterator iterator = trackerMap.iterator();; @Override public boolean hasNext() { return iterator.hasNext(); } @Override public NextTickListEntry next() { iterator.advance(); return iterator.value().asMCEntry(); } @Override public void remove() { removeEntry(iterator.value(), true); } }; } @Override public boolean add(NextTickListEntry e) { return wrapper.add(e); } @Override public boolean remove(Object o) { return wrapper.remove((NextTickListEntry) o); } @Override public boolean contains(Object o) { return wrapper.contains((NextTickListEntry) o, true); } @Override public boolean isEmpty() { return BlockUpdatesTracker.this.sortedTree.isEmpty(); } @Override public int size() { return BlockUpdatesTracker.this.sortedTree.size(); } } private final class VanillaTreeSetWrapper extends TreeSet { @Override public Iterator iterator() { return new Iterator() { private final Iterator iterator = sortedTree.iterator();; private BlockUpdateEntry next; @Override public boolean hasNext() { next = null; return iterator.hasNext(); } @Override public NextTickListEntry next() { return (next = iterator.next()).asMCEntry(); } @Override public void remove() { if (next == null) next(); if (next != null) removeEntry(next, true); } }; } @Override public boolean add(NextTickListEntry e) { return wrapper.add(e); } @Override public boolean remove(Object o) { return wrapper.remove((NextTickListEntry) o); } @Override public boolean contains(Object o) { return wrapper.contains((NextTickListEntry) o, false); } @Override public boolean isEmpty() { return BlockUpdatesTracker.this.sortedTree.isEmpty(); } @Override public int size() { return BlockUpdatesTracker.this.sortedTree.size(); } } }