Skip to content

Instantly share code, notes, and snippets.

@Jikoo
Created January 15, 2016 16:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jikoo/008de1c007b6087c3ff0 to your computer and use it in GitHub Desktop.
Save Jikoo/008de1c007b6087c3ff0 to your computer and use it in GitHub Desktop.
Force queued block updates
package com.github.jikoo.blockupdate;
import java.util.Queue;
import com.github.jikoo.util.HashQueue;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_8_R3.util.CraftMagicNumbers;
import net.minecraft.server.v1_8_R3.BlockPosition;
/**
* Manager for queuing block updates to prevent redundant/excessive simultaneous updates.
*
* @author Jikoo
*/
public class BlockUpdateManager {
private final JavaPlugin plugin;
private final Queue<Block> pending;
private BukkitTask queueDrain;
private final BlockFace[] adjacent;
public BlockUpdateManager(JavaPlugin plugin) {
this.plugin = plugin;
this.pending = new HashQueue<>();
this.adjacent = new BlockFace[] { BlockFace.UP, BlockFace.DOWN, BlockFace.NORTH,
BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST };
}
public void queueBlock(Block block) {
// Only update blocks with adjacent non-air blocks
if (block.getType() == Material.AIR) {
boolean update = false;
for (BlockFace face : adjacent) {
update = block.getRelative(face).getType() != Material.AIR;
if (update) {
break;
}
}
if (!update) {
return;
}
}
pending.add(block);
startTask();
}
private void startTask() {
if (queueDrain == null) {
queueDrain = new QueueDrainRunnable().runTaskTimer(plugin, 0, 1L);
}
}
private class QueueDrainRunnable extends BukkitRunnable {
@SuppressWarnings("deprecation")
@Override
public void run() {
for (int i = 0; i < 50 && !pending.isEmpty(); i++) {
// Sadly, using the API does not work: pending.poll().getState().update(true, true);
// Blocks that are currently air cannot be updated at all to fix adjacent blocks,
// and certain other edge cases also will not trigger updates.
// Instead, we manually force an update using NMS.
Block block = pending.poll();
((CraftWorld) block.getWorld()).getHandle().applyPhysics(
new BlockPosition(block.getX(), block.getY(), block.getZ()),
CraftMagicNumbers.getBlock(block).fromLegacyData(block.getData()).getBlock());
}
if (pending.isEmpty()) {
this.cancel();
queueDrain = null;
}
}
}
}
package com.github.jikoo.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
/**
* A Queue which ensures that elements are unique.
*
* @author Jikoo
*/
public class HashQueue<E> implements Queue<E> {
private final Queue<E> queue = new LinkedList<>();
private final Set<E> set = new HashSet<>();
@Override
public int size() {
return queue.size();
}
@Override
public boolean isEmpty() {
return queue.isEmpty();
}
@Override
public boolean contains(Object o) {
return set.contains(o);
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
private final Iterator<E> queueIterator = queue.iterator();
@Override
public boolean hasNext() {
return queueIterator.hasNext();
}
@Override
public E next() {
return queueIterator.next();
}
};
}
@Override
public Object[] toArray() {
return queue.toArray();
}
@SuppressWarnings("unchecked")
@Override
public Object[] toArray(Object[] a) {
return queue.toArray(a);
}
@Override
public boolean remove(Object o) {
return set.remove(o) && queue.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
// This is a bad implementation, but this HashQueue is for my own usage only
// I don't actually ever call addAll. The only reason this doesn't throw an
// UnsupportedOperationException is because this is incredibly easy to do poorly.
boolean addAll = true;
for (E e : c) {
addAll = add(e) && addAll;
}
return addAll;
}
@Override
public boolean removeAll(Collection<?> c) {
return set.removeAll(c) || queue.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return set.retainAll(c) || queue.retainAll(c);
}
@Override
public void clear() {
set.clear();
queue.clear();
}
@Override
public boolean add(E e) {
return set.add(e) && queue.add(e);
}
@Override
public boolean offer(E e) {
return set.add(e) && queue.add(e);
}
@Override
public E remove() {
E e = queue.remove();
set.remove(e);
return e;
}
@Override
public E poll() {
E e = queue.poll();
set.remove(e);
return e;
}
@Override
public E element() {
return queue.element();
}
@Override
public E peek() {
return queue.peek();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment