diff --git a/src/main/java/com/sk89q/craftbook/core/st/SelfTriggeringManager.java b/src/main/java/com/sk89q/craftbook/core/st/SelfTriggeringManager.java index c207f30ec..893329584 100644 --- a/src/main/java/com/sk89q/craftbook/core/st/SelfTriggeringManager.java +++ b/src/main/java/com/sk89q/craftbook/core/st/SelfTriggeringManager.java @@ -91,31 +91,16 @@ public Collection getSelfTriggeringMechanics() { private Location[] registeredLocations; private boolean hasChanged = false; - private boolean areAdjacentChunksLoaded(Location loc) { + private static boolean areAdjacentChunksLoaded(Location loc) { World world = loc.getWorld(); final int CX = loc.getBlockX() >> 4; final int CZ = loc.getBlockZ() >> 4; - for (int x = -1; x <= 1; ++x) { - for (int z = -1; z <= 1; ++z) { - // Check only cardinal directions - if (x != 0 && z != 0) { - continue; - } - - // Don't test the current chunk - if (x == 0 && z == 0) { - continue; - } - - if (!world.isChunkLoaded(CX + x, CZ + z)) { - return false; - } - } - } - - return true; + return world.isChunkLoaded(CX - 1, CZ) + && world.isChunkLoaded(CX + 1, CZ) + && world.isChunkLoaded(CX, CZ - 1) + && world.isChunkLoaded(CX, CZ + 1); } /** diff --git a/src/main/java/com/sk89q/craftbook/mechanics/ic/ICMechanic.java b/src/main/java/com/sk89q/craftbook/mechanics/ic/ICMechanic.java index f930aaa7f..f95a8612e 100644 --- a/src/main/java/com/sk89q/craftbook/mechanics/ic/ICMechanic.java +++ b/src/main/java/com/sk89q/craftbook/mechanics/ic/ICMechanic.java @@ -16,6 +16,8 @@ package com.sk89q.craftbook.mechanics.ic; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.sk89q.craftbook.AbstractCraftBookMechanic; import com.sk89q.craftbook.ChangedSign; import com.sk89q.craftbook.CraftBookPlayer; @@ -37,6 +39,7 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -47,6 +50,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; /** @@ -303,10 +307,27 @@ public void onThink(SelfTriggerThinkEvent event) { if(!EventUtil.passesFilter(event)) return; + // Cached fast path: think without re-snapshotting and re-parsing the sign. + Location loc = event.getBlock().getLocation(); + ICFamily cachedFamily = thinkFastCache.getIfPresent(loc); + if (cachedFamily != null && ICManager.isCachedIC(loc)) { + IC cachedIC = ICManager.getCachedIC(loc); + if (cachedIC instanceof SelfTriggeredIC selfTriggeredIC) { + event.setHandled(true); + ChipState chipState = cachedFamily.detectSelfTriggered(BukkitAdapter.adapt(loc), cachedIC.getSign()); + selfTriggeredIC.think(chipState); + try { + cachedIC.getSign().update(false); + } catch (Throwable ignored) {} + return; + } + } + final Object[] icData = setupIC(event.getBlock(), true); if(icData != null && icData[2] instanceof SelfTriggeredIC ic) { event.setHandled(true); + thinkFastCache.put(loc, (ICFamily) icData[1]); ChipState chipState = ((ICFamily) icData[1]).detectSelfTriggered(BukkitAdapter.adapt(event.getBlock().getLocation()), ((IC) icData[2]).getSign()); ic.think(chipState); try { @@ -315,6 +336,17 @@ public void onThink(SelfTriggerThinkEvent event) { } } + /** + * Location -> ICFamily for the think fast path. The expiry is what reruns + * the full setupIC verification, so a sign edited in place is picked up + * within a second; eviction bounds the map on a server with more ICs than + * this. + */ + private final Cache thinkFastCache = CacheBuilder.newBuilder() + .maximumSize(4096) + .expireAfterWrite(1, TimeUnit.SECONDS) + .build(); + @EventHandler(priority = EventPriority.HIGH) public void onBlockBreak(BlockBreakEvent event) {