Created
January 15, 2016 16:06
-
-
Save Jikoo/008de1c007b6087c3ff0 to your computer and use it in GitHub Desktop.
Force queued block updates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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