@@ -47,6 +47,7 @@ public class Utils {
4747 private static final String LOCKED_CONTAINER_PDC_KEY_DEFAULT_PATH = "locked_container" ;
4848 private static final Pattern HEX_COLOR_PATTERN = Pattern .compile ("(?i)&\\ #([0-9a-f]{6})" );
4949 private static final LockedContainerPdcAccess LOCKED_CONTAINER_PDC_ACCESS = LockedContainerPdcAccess .create ();
50+ private static final int PDC_TAG_REFRESH_TILE_ENTITY_BUDGET_PER_TICK = 128 ;
5051
5152 private static final LoadingCache <UUID , Block > selectedsign = CacheBuilder .newBuilder ()
5253 .expireAfterAccess (30 , TimeUnit .SECONDS )
@@ -56,6 +57,9 @@ public Block load(UUID key) {
5657 }
5758 });
5859 private static final Set <UUID > notified = new HashSet <>();
60+ private static final Deque <ChunkTagRefreshTask > queuedPdcTagRefreshTasks = new ArrayDeque <>();
61+ private static final Set <String > queuedPdcTagRefreshKeys = new HashSet <>();
62+ private static CompatibleTask queuedPdcTagRefreshWorker ;
5963
6064 // Helper functions
6165 public static Block putSignOn (Block block , BlockFace blockface , String line1 , String line2 , Material material ) {
@@ -252,22 +256,136 @@ public static void refreshLockedContainerPdcTagLater(Block block) {
252256 }
253257
254258 public static void refreshLockedContainerPdcTagsInChunk (Chunk chunk ) {
259+ if (chunk == null ) return ;
255260 for (BlockState blockState : chunk .getTileEntities ()) {
256- if (blockState instanceof Container ) {
257- ContainerPdcLockManager .refreshLockedContainerTag (blockState .getBlock ());
258- }
259- if (!(blockState instanceof Sign )) continue ;
260- Block signBlock = blockState .getBlock ();
261- if (!LocketteProAPI .isLockSign (signBlock )) continue ;
262- refreshLockedContainerPdcTag (LocketteProAPI .getAttachedBlock (signBlock ));
261+ refreshLockedContainerPdcTagByTileState (blockState );
263262 }
264263 }
265264
266265 public static void refreshLockedContainerPdcTagsInLoadedChunks () {
266+ queueRefreshLockedContainerPdcTagsInLoadedChunks ();
267+ }
268+
269+ public static void queueRefreshLockedContainerPdcTagsInChunk (Chunk chunk ) {
270+ if (chunk == null ) return ;
271+ startQueuedPdcTagRefreshWorker ();
272+ String key = toChunkQueueKey (chunk .getWorld ().getUID (), chunk .getX (), chunk .getZ ());
273+ if (!queuedPdcTagRefreshKeys .add (key )) return ;
274+ queuedPdcTagRefreshTasks .addLast (new ChunkTagRefreshTask (chunk .getWorld ().getUID (), chunk .getX (), chunk .getZ (), key ));
275+ }
276+
277+ public static void queueRefreshLockedContainerPdcTagsInLoadedChunks () {
278+ startQueuedPdcTagRefreshWorker ();
267279 for (World world : Bukkit .getWorlds ()) {
268280 for (Chunk chunk : world .getLoadedChunks ()) {
269- refreshLockedContainerPdcTagsInChunk (chunk );
281+ queueRefreshLockedContainerPdcTagsInChunk (chunk );
282+ }
283+ }
284+ }
285+
286+ public static void stopQueuedPdcTagRefreshWorker () {
287+ if (queuedPdcTagRefreshWorker != null ) {
288+ queuedPdcTagRefreshWorker .cancel ();
289+ queuedPdcTagRefreshWorker = null ;
290+ }
291+ queuedPdcTagRefreshTasks .clear ();
292+ queuedPdcTagRefreshKeys .clear ();
293+ }
294+
295+ private static void startQueuedPdcTagRefreshWorker () {
296+ if (queuedPdcTagRefreshWorker != null ) return ;
297+ queuedPdcTagRefreshWorker = CompatibleScheduler .runTaskTimer (
298+ LockettePro .getPlugin (),
299+ null ,
300+ Utils ::drainQueuedPdcTagRefreshTasks ,
301+ 1L ,
302+ 1L
303+ );
304+ }
305+
306+ private static void drainQueuedPdcTagRefreshTasks () {
307+ int budget = PDC_TAG_REFRESH_TILE_ENTITY_BUDGET_PER_TICK ;
308+ while (budget > 0 && !queuedPdcTagRefreshTasks .isEmpty ()) {
309+ ChunkTagRefreshTask task = queuedPdcTagRefreshTasks .peekFirst ();
310+ int processed = task .process (budget );
311+ if (task .isDone ()) {
312+ queuedPdcTagRefreshTasks .pollFirst ();
313+ queuedPdcTagRefreshKeys .remove (task .key );
314+ }
315+ if (processed <= 0 ) {
316+ processed = 1 ;
317+ }
318+ budget -= processed ;
319+ }
320+ }
321+
322+ private static void refreshLockedContainerPdcTagByTileState (BlockState blockState ) {
323+ if (blockState == null ) return ;
324+ if (blockState instanceof Container ) {
325+ ContainerPdcLockManager .refreshLockedContainerTag (blockState .getBlock ());
326+ }
327+ if (!(blockState instanceof Sign )) return ;
328+ Block signBlock = blockState .getBlock ();
329+ if (!LocketteProAPI .isLockSign (signBlock )) return ;
330+ refreshLockedContainerPdcTag (LocketteProAPI .getAttachedBlock (signBlock ));
331+ }
332+
333+ private static String toChunkQueueKey (UUID worldId , int chunkX , int chunkZ ) {
334+ return worldId + ":" + chunkX + ":" + chunkZ ;
335+ }
336+
337+ private static final class ChunkTagRefreshTask {
338+ private final UUID worldId ;
339+ private final int chunkX ;
340+ private final int chunkZ ;
341+ private final String key ;
342+ private BlockState [] tileEntities ;
343+ private int cursor ;
344+ private boolean done ;
345+
346+ private ChunkTagRefreshTask (UUID worldId , int chunkX , int chunkZ , String key ) {
347+ this .worldId = worldId ;
348+ this .chunkX = chunkX ;
349+ this .chunkZ = chunkZ ;
350+ this .key = key ;
351+ }
352+
353+ private int process (int budget ) {
354+ if (done ) return 0 ;
355+ if (!ensureTileEntitiesLoaded ()) return 0 ;
356+
357+ int processed = 0 ;
358+ while (cursor < tileEntities .length && processed < budget ) {
359+ refreshLockedContainerPdcTagByTileState (tileEntities [cursor ]);
360+ cursor ++;
361+ processed ++;
362+ }
363+ if (cursor >= tileEntities .length ) {
364+ done = true ;
270365 }
366+ return processed ;
367+ }
368+
369+ private boolean ensureTileEntitiesLoaded () {
370+ if (tileEntities != null ) {
371+ return true ;
372+ }
373+ World world = Bukkit .getWorld (worldId );
374+ if (world == null || !world .isChunkLoaded (chunkX , chunkZ )) {
375+ done = true ;
376+ return false ;
377+ }
378+ Chunk chunk = world .getChunkAt (chunkX , chunkZ );
379+ tileEntities = chunk .getTileEntities ();
380+ cursor = 0 ;
381+ if (tileEntities .length == 0 ) {
382+ done = true ;
383+ }
384+ return true ;
385+ }
386+
387+ private boolean isDone () {
388+ return done ;
271389 }
272390 }
273391
0 commit comments