Last active
September 16, 2024 07:56
-
-
Save aadnk/5443172 to your computer and use it in GitHub Desktop.
Thread-safe tile entity processor that splits up the work in smaller portions.
This file contains hidden or 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.comphenix.example; | |
import java.util.ArrayDeque; | |
import java.util.Arrays; | |
import java.util.Queue; | |
import java.util.concurrent.TimeUnit; | |
import org.bukkit.Bukkit; | |
import org.bukkit.Chunk; | |
import org.bukkit.block.BlockState; | |
import org.bukkit.plugin.Plugin; | |
import org.bukkit.scheduler.BukkitScheduler; | |
import org.bukkit.scheduler.BukkitTask; | |
import com.google.common.base.Preconditions; | |
// Simple tile entity processor. EXAMPLE: | |
// | |
// public class ExampleMod extends JavaPlugin { | |
// @Override | |
// public void onEnable() { | |
// TileEntityProcessor processor = new TileEntityProcessor(10, TimeUnit.MILLISECONDS) { | |
// @Override | |
// protected void processTileEntity(BlockState state) { | |
// // Whatever you need to do per tile entity! | |
// if (state instanceof Chest) { | |
// Chest chest = (Chest) state; | |
// Inventory inventory = chest.getInventory(); | |
// | |
// for (ItemStack stack : inventory.getContents()) { | |
// if (stack != null && stack.getType() == Material.CHAINMAIL_CHESTPLATE) { | |
// inventory.remove(stack); | |
// } | |
// } | |
// } | |
// } | |
// }; | |
// processor.start(this); | |
// } | |
//} | |
public abstract class TileEntityProcessor { | |
private static final int TICK_REPEAT_DELAY = 1; | |
private static final int TICK_DELAY = 1; | |
private Queue<Chunk> chunks; | |
private Queue<BlockState> tileEntities; | |
private final long tickProcessingTimeNano; | |
private BukkitTask task; | |
public TileEntityProcessor(long tickProcessingTime, TimeUnit tickProcessingUnit) { | |
Preconditions.checkNotNull(tickProcessingUnit, "Tick processing unit cannot be NULL."); | |
this.chunks = new ArrayDeque<Chunk>(); | |
this.tickProcessingTimeNano = tickProcessingUnit.toNanos(tickProcessingTime); | |
} | |
/** | |
* Process a given tile entity. | |
* @param state - the entity to process. | |
*/ | |
protected abstract void processTileEntity(BlockState state); | |
/** | |
* Determine if the entity processor is running. | |
* @return TRUE if it is, FALSE otherwise. | |
*/ | |
public final boolean isRunning() { | |
return task != null; | |
} | |
/** | |
* Begin processing tile entities. | |
* @param plugin - the current plugin. | |
*/ | |
public final void start(Plugin plugin) { | |
start(Bukkit.getScheduler(), plugin); | |
} | |
/** | |
* Begin processing tile entities. | |
* @param scheduler - the current Bukkit scehduler. | |
* @param plugin - the current plugin. | |
*/ | |
public final void start(BukkitScheduler scheduler, Plugin plugin) { | |
Preconditions.checkState(!isRunning(), "Cannot start a processor twice."); | |
Preconditions.checkNotNull(scheduler, "Scheduler cannot be NULL."); | |
Preconditions.checkNotNull(plugin, "Plugin cannot be NULL."); | |
scheduler.runTaskTimer(plugin, getRunnable(), TICK_DELAY, TICK_REPEAT_DELAY); | |
} | |
/** | |
* Stop processing tile entities. | |
* | |
*/ | |
public final void stop() { | |
Preconditions.checkState(isRunning(), "Cannot stop a stopped processor."); | |
task.cancel(); | |
task = null; | |
} | |
private Runnable getRunnable() { | |
return new Runnable() { | |
@Override | |
public void run() { | |
long start = System.nanoTime(); | |
while (true) { | |
if (increment()) { | |
// Handle timeout | |
if (!processTileEntities(start)) | |
break; | |
} else { | |
// Don't repeat | |
stop(); | |
break; | |
} | |
} | |
} | |
}; | |
} | |
private boolean processTileEntities(long start) { | |
while (!tileEntities.isEmpty()) { | |
// Timeout? | |
if (System.nanoTime() > start + tickProcessingTimeNano) | |
return false; | |
BlockState state = tileEntities.poll(); | |
processTileEntity(state); | |
} | |
// Process more! | |
return true; | |
} | |
private boolean increment() { | |
if (isEmpty(tileEntities)) { | |
// Process next chunk | |
if (!isEmpty(chunks)) { | |
tileEntities = new ArrayDeque<BlockState>( | |
Arrays.asList(chunks.poll().getTileEntities()) | |
); | |
return true; | |
} | |
} | |
return false; | |
} | |
private boolean isEmpty(Queue<?> queue) { | |
return queue == null || queue.isEmpty(); | |
} | |
} |
How is the ArrayDeque variable 'chunks' populated?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You need to assign that TaskTimer at line 90 to the task variable, don't you?