diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java index 614abc6b6..9ea235e61 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java @@ -114,6 +114,10 @@ import java.util.regex.Pattern; public class Iris extends VolmitPlugin implements Listener, ReloadAware { private static final Queue syncJobs = new ShurikenQueue<>(); + static { + System.setProperty("iris.cache.fast", "true"); + } + public static Iris instance; public static Bindings.Adventure audiences; public static MultiverseCoreLink linkMultiverseCore; @@ -580,6 +584,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware { compat = IrisCompat.configured(getDataFile("compat.json")); IrisServices.register(IrisCompat.class, compat); ServerConfigurator.configure(); + IrisToolbelt.applyPregenPerformanceProfile(); validateAllPacks(); IrisSafeguard.execute(); getSender().setTag(getTag()); @@ -607,7 +612,6 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware { services.values().forEach(this::registerListener); addShutdownHook(); processPendingStartupWorldDeletes(); - IrisToolbelt.applyPregenPerformanceProfile(); WorldLifecycleService.get(); WorldRuntimeControlService.get(); diff --git a/core/src/main/java/art/arcane/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/art/arcane/iris/core/pregenerator/methods/AsyncPregenMethod.java index 5bc0c7cb1..7f3dec0a4 100644 --- a/core/src/main/java/art/arcane/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/art/arcane/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -34,8 +34,9 @@ import io.papermc.lib.PaperLib; import org.bukkit.Chunk; import org.bukkit.World; -import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -350,13 +351,13 @@ public class AsyncPregenMethod implements PregeneratorMethod { static int computePaperLikeRecommendedCap(int workerThreads) { int normalizedWorkers = Math.max(1, workerThreads); - int recommendedCap = normalizedWorkers * 2; - if (recommendedCap < 8) { - return 8; + int recommendedCap = normalizedWorkers * 4; + if (recommendedCap < 16) { + return 16; } - if (recommendedCap > 96) { - return 96; + if (recommendedCap > 128) { + return 128; } return recommendedCap; @@ -721,13 +722,18 @@ public class AsyncPregenMethod implements PregeneratorMethod { public static void increaseWorkerThreads() { THREAD_COUNT.updateAndGet(i -> { - if (i > 0) return 1; - var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads(); + if (i > 0) { + return 1; + } + + int adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads(); try { - var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); - var pool = field.get(null); - var threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length; - if (threads >= adjusted) return 0; + Field field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); + Object pool = field.get(null); + int threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length; + if (threads >= adjusted) { + return 0; + } pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted); return threads; @@ -745,11 +751,14 @@ public class AsyncPregenMethod implements PregeneratorMethod { public static void resetWorkerThreads() { THREAD_COUNT.updateAndGet(i -> { - if (i == 0) return 0; + if (i == 0) { + return 0; + } + try { - var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); - var pool = field.get(null); - var method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class); + Field field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); + Object pool = field.get(null); + Method method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class); method.invoke(pool, i); return 0; } catch (Throwable e) { diff --git a/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java b/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java index d8558f6bc..c4a2e8be5 100644 --- a/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java +++ b/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java @@ -25,6 +25,7 @@ import art.arcane.iris.engine.IrisComplex; import art.arcane.iris.engine.UpperDimensionContext; import art.arcane.iris.engine.object.IrisBiome; import art.arcane.iris.engine.object.IrisDimension; +import art.arcane.iris.engine.object.IrisOreGenerator; import art.arcane.iris.engine.object.IrisRegion; import art.arcane.volmlib.util.collection.KList; import art.arcane.iris.util.project.context.ChunkedDataCache; @@ -236,8 +237,14 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator biomeSurfaceOres = hideOres ? null : biome.getSurfaceOreGenerators(); + KList regionSurfaceOres = hideOres ? null : region.getSurfaceOreGenerators(); + KList dimensionSurfaceOres = hideOres ? null : dimension.getSurfaceOreGenerators(); + KList biomeUndergroundOres = hideOres ? null : biome.getUndergroundOreGenerators(); + KList regionUndergroundOres = hideOres ? null : region.getUndergroundOreGenerators(); + KList dimensionUndergroundOres = hideOres ? null : dimension.getUndergroundOreGenerators(); + boolean hasSurfaceOres = !hideOres && (!biomeSurfaceOres.isEmpty() || !regionSurfaceOres.isEmpty() || !dimensionSurfaceOres.isEmpty()); + boolean hasUndergroundOres = !hideOres && (!biomeUndergroundOres.isEmpty() || !regionUndergroundOres.isEmpty() || !dimensionUndergroundOres.isEmpty()); KList blocks = null; KList fblocks = null; @@ -250,9 +257,9 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator oreGenerators, int x, int y, int z, RNG rng, IrisData data) { + if (oreGenerators == null || oreGenerators.isEmpty()) { + return null; + } + + int oreCount = oreGenerators.size(); + for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { + IrisOreGenerator oreGenerator = oreGenerators.get(oreIndex); + PlatformBlockState ore = oreGenerator.generate(x, y, z, rng, data); + if (ore != null) { + return ore; + } + } + + return null; + } } diff --git a/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java b/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java index 1f2c1ab59..69b3fbf8a 100644 --- a/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java +++ b/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java @@ -15,10 +15,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; +import java.util.concurrent.ConcurrentHashMap; public interface MatterGenerator { - Executor DISPATCHER = MultiBurst.burst; + MultiBurst DISPATCHER = MultiBurst.burst; + ConcurrentHashMap> IN_FLIGHT_COMPONENTS = new ConcurrentHashMap<>(); Engine getEngine(); @@ -42,7 +43,7 @@ public interface MatterGenerator { try (MantleWriter writer = new MantleWriter(getEngine().getMantle(), getMantle(), x, z, writeRadius, multicore)) { for (Pair, Integer> pair : getComponents()) { int passRadius = pair.getB(); - List> launchedTasks = multicore ? new ArrayList<>() : null; + Set> launchedTasks = multicore ? new HashSet<>() : null; for (int i = -passRadius; i <= passRadius; i++) { for (int j = -passRadius; j <= passRadius; j++) { @@ -55,6 +56,7 @@ public interface MatterGenerator { continue; } + List eligibleComponents = new ArrayList<>(pair.getA().size()); for (MantleComponent component : pair.getA()) { if (!component.isEnabled()) { continue; @@ -88,18 +90,27 @@ public interface MatterGenerator { } } - int finalPassX = passX; - int finalPassZ = passZ; - MantleChunk finalChunk = chunk; - MantleComponent finalComponent = component; - Runnable task = () -> finalChunk.raiseFlagSuspend(finalComponent.getFlag(), - () -> finalComponent.generateLayer(writer, finalPassX, finalPassZ, context)); + eligibleComponents.add(component); + } - if (multicore) { - launchedTasks.add(CompletableFuture.runAsync(task, DISPATCHER)); - } else { - task.run(); + if (eligibleComponents.isEmpty()) { + continue; + } + + int finalPassX = passX; + int finalPassZ = passZ; + Runnable task = () -> { + for (MantleComponent component : eligibleComponents) { + runComponentInline(chunk, component, writer, finalPassX, finalPassZ, context); } + }; + + if (multicore) { + for (MantleComponent component : eligibleComponents) { + launchedTasks.add(runComponentAsync(chunk, component, writer, finalPassX, finalPassZ, context)); + } + } else { + task.run(); } } } @@ -128,4 +139,110 @@ public interface MatterGenerator { private static long chunkKey(int x, int z) { return (((long) x) << 32) ^ (z & 0xffffffffL); } + + private CompletableFuture runComponentAsync( + MantleChunk chunk, + MantleComponent component, + MantleWriter writer, + int chunkX, + int chunkZ, + ChunkContext context + ) { + MantleFlag flag = component.getFlag(); + if (chunk.isFlagged(flag)) { + return CompletableFuture.completedFuture(null); + } + + MatterTaskKey key = new MatterTaskKey(getMantle(), chunkX, chunkZ, flag.ordinal()); + CompletableFuture future = new CompletableFuture<>(); + CompletableFuture existing = IN_FLIGHT_COMPONENTS.putIfAbsent(key, future); + if (existing != null) { + return existing; + } + + try { + if (DISPATCHER.ownsCurrentThread()) { + completeComponentTask(future, key, chunk, component, writer, chunkX, chunkZ, context); + } else { + CompletableFuture.runAsync(() -> completeComponentTask(future, key, chunk, component, writer, chunkX, chunkZ, context), DISPATCHER); + } + } catch (Throwable throwable) { + IN_FLIGHT_COMPONENTS.remove(key, future); + future.completeExceptionally(throwable); + throw throwable; + } + + return future; + } + + private void completeComponentTask( + CompletableFuture future, + MatterTaskKey key, + MantleChunk chunk, + MantleComponent component, + MantleWriter writer, + int chunkX, + int chunkZ, + ChunkContext context + ) { + try { + runComponentInline(chunk, component, writer, chunkX, chunkZ, context); + future.complete(null); + } catch (Throwable throwable) { + future.completeExceptionally(throwable); + throw throwable; + } finally { + IN_FLIGHT_COMPONENTS.remove(key, future); + } + } + + private void runComponentInline( + MantleChunk chunk, + MantleComponent component, + MantleWriter writer, + int chunkX, + int chunkZ, + ChunkContext context + ) { + chunk.raiseFlagSuspend(component.getFlag(), () -> component.generateLayer(writer, chunkX, chunkZ, context)); + } + + final class MatterTaskKey { + private final Mantle mantle; + private final int chunkX; + private final int chunkZ; + private final int flagOrdinal; + + MatterTaskKey(Mantle mantle, int chunkX, int chunkZ, int flagOrdinal) { + this.mantle = mantle; + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.flagOrdinal = flagOrdinal; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (!(object instanceof MatterTaskKey other)) { + return false; + } + + return mantle == other.mantle + && chunkX == other.chunkX + && chunkZ == other.chunkZ + && flagOrdinal == other.flagOrdinal; + } + + @Override + public int hashCode() { + int result = System.identityHashCode(mantle); + result = 31 * result + chunkX; + result = 31 * result + chunkZ; + result = 31 * result + flagOrdinal; + return result; + } + } } diff --git a/core/src/main/java/art/arcane/iris/engine/modifier/IrisCarveModifier.java b/core/src/main/java/art/arcane/iris/engine/modifier/IrisCarveModifier.java index 44ab77993..50d131135 100644 --- a/core/src/main/java/art/arcane/iris/engine/modifier/IrisCarveModifier.java +++ b/core/src/main/java/art/arcane/iris/engine/modifier/IrisCarveModifier.java @@ -34,6 +34,7 @@ import art.arcane.iris.util.project.hunk.Hunk; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.mantle.runtime.Mantle; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; +import art.arcane.volmlib.util.mantle.runtime.TectonicPlate; import art.arcane.volmlib.util.math.BlockPosition; import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.math.PowerOfTwoCoordinates; @@ -229,55 +230,53 @@ public class IrisCarveModifier extends EngineAssignedModifier> 5), (chunkZ >> 5)); - boolean eastLoaded = mantle.hasTectonicPlate(((chunkX + 1) >> 5), (chunkZ >> 5)); - boolean northLoaded = mantle.hasTectonicPlate((chunkX >> 5), ((chunkZ - 1) >> 5)); - boolean southLoaded = mantle.hasTectonicPlate((chunkX >> 5), ((chunkZ + 1) >> 5)); - if (!westLoaded && !eastLoaded && !northLoaded && !southLoaded) { + MantleChunk west = existingMantleChunk(mantle, chunkX - 1, chunkZ); + MantleChunk east = existingMantleChunk(mantle, chunkX + 1, chunkZ); + MantleChunk north = existingMantleChunk(mantle, chunkX, chunkZ - 1); + MantleChunk south = existingMantleChunk(mantle, chunkX, chunkZ + 1); + if (west == null && east == null && north == null && south == null) { return; } for (int yy = 1; yy <= maxY; yy++) { for (int offset = 0; offset < 16; offset++) { - if (westLoaded) { - tryAddBoundaryWall(mantle, mc, walls, boundaryMasks, boundaryCaverns, 0, yy, offset, baseX, baseZ, -1, 0); + if (west != null) { + tryAddBoundaryWall(mc, west, walls, boundaryMasks, boundaryCaverns, 0, yy, offset, 15, offset); } - if (eastLoaded) { - tryAddBoundaryWall(mantle, mc, walls, boundaryMasks, boundaryCaverns, 15, yy, offset, baseX, baseZ, 1, 0); + if (east != null) { + tryAddBoundaryWall(mc, east, walls, boundaryMasks, boundaryCaverns, 15, yy, offset, 0, offset); } - if (northLoaded) { - tryAddBoundaryWall(mantle, mc, walls, boundaryMasks, boundaryCaverns, offset, yy, 0, baseX, baseZ, 0, -1); + if (north != null) { + tryAddBoundaryWall(mc, north, walls, boundaryMasks, boundaryCaverns, offset, yy, 0, offset, 15); } - if (southLoaded) { - tryAddBoundaryWall(mantle, mc, walls, boundaryMasks, boundaryCaverns, offset, yy, 15, baseX, baseZ, 0, 1); + if (south != null) { + tryAddBoundaryWall(mc, south, walls, boundaryMasks, boundaryCaverns, offset, yy, 15, offset, 0); } } } } private void tryAddBoundaryWall( - Mantle mantle, MantleChunk mc, + MantleChunk neighborChunk, PackedWallBuffer walls, ColumnMask[] boundaryMasks, MatterCavern[] boundaryCaverns, int localX, int yy, int localZ, - int baseX, - int baseZ, - int dx, - int dz + int neighborX, + int neighborZ ) { - int worldX = baseX + localX; - int worldZ = baseZ + localZ; - if (mc.get(worldX, yy, worldZ, MatterCavern.class) != null) { + if (mc.get(localX, yy, localZ, MatterCavern.class) != null) { return; } - MatterCavern neighbor = mantle.get(worldX + dx, yy, worldZ + dz, MatterCavern.class); + + MatterCavern neighbor = neighborChunk.get(neighborX, yy, neighborZ, MatterCavern.class); if (neighbor == null) { return; } + walls.put(localX, yy, localZ, neighbor); int columnIndex = PowerOfTwoCoordinates.packLocal16(localX, localZ); boundaryMasks[columnIndex].add(yy); @@ -286,6 +285,14 @@ public class IrisCarveModifier extends EngineAssignedModifier existingMantleChunk(Mantle mantle, int chunkX, int chunkZ) { + TectonicPlate plate = mantle.getLoadedRegions().get(Mantle.key(chunkX >> 5, chunkZ >> 5)); + if (plate == null || plate.isClosed()) { + return null; + } + return plate.get(chunkX & 31, chunkZ & 31); + } + private void processColumnFromMask( Hunk output, MantleChunk mc, diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisBiome.java b/core/src/main/java/art/arcane/iris/engine/object/IrisBiome.java index 735828160..20c1c82ee 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisBiome.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisBiome.java @@ -237,6 +237,14 @@ public class IrisBiome extends IrisRegistrant implements IRare { undergroundOreCache.reset(); } + public KList getSurfaceOreGenerators() { + return getOres(true); + } + + public KList getUndergroundOreGenerators() { + return getOres(false); + } + private KList getSurfaceOres() { return getOres(true); } diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisDimension.java b/core/src/main/java/art/arcane/iris/engine/object/IrisDimension.java index 4dc082a36..6c70e112e 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisDimension.java @@ -342,6 +342,14 @@ public class IrisDimension extends IrisRegistrant { undergroundOreCache.reset(); } + public KList getSurfaceOreGenerators() { + return getOres(true); + } + + public KList getUndergroundOreGenerators() { + return getOres(false); + } + private KList getSurfaceOres() { return getOres(true); } diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisRegion.java b/core/src/main/java/art/arcane/iris/engine/object/IrisRegion.java index 15b06bba1..bf77d8a69 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisRegion.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisRegion.java @@ -197,6 +197,14 @@ public class IrisRegion extends IrisRegistrant implements IRare { undergroundOreCache.reset(); } + public KList getSurfaceOreGenerators() { + return getOres(true); + } + + public KList getUndergroundOreGenerators() { + return getOres(false); + } + private KList getSurfaceOres() { return getOres(true); } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockState.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockState.java index d368b3106..2762908e1 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockState.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockState.java @@ -22,6 +22,7 @@ import art.arcane.iris.core.nms.INMS; import art.arcane.iris.spi.PlatformBlockState; import art.arcane.iris.util.common.data.IrisCustomData; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.block.data.BlockData; import java.util.LinkedHashMap; @@ -37,18 +38,65 @@ public final class BukkitBlockState implements PlatformBlockState { private final BlockData data; private final String key; private final String namespace; - private volatile Boolean air; - private volatile Boolean solid; - private volatile Boolean occluding; - private volatile Boolean fluid; - private volatile Boolean water; - private volatile Boolean foliage; - private volatile Boolean decorant; + private final Boolean air; + private final Boolean solid; + private final Boolean occluding; + private final Boolean fluid; + private final Boolean water; + private final Boolean waterLogged; + private final Boolean lit; + private final Boolean updatable; + private final Boolean foliage; + private final Boolean foliagePlantable; + private final Boolean decorant; + private final Boolean storage; + private final Boolean storageChest; + private final Boolean ore; + private final Boolean deepSlate; + private final Boolean vineBlock; + private volatile Boolean tileEntity; private BukkitBlockState(BlockData data, String key) { this.data = data; this.key = key; this.namespace = parseNamespace(key); + Material material = data.getMaterial(); + if (material == null) { + this.air = null; + this.solid = null; + this.occluding = null; + this.fluid = null; + this.water = null; + this.waterLogged = null; + this.lit = null; + this.updatable = null; + this.foliage = null; + this.foliagePlantable = null; + this.decorant = null; + this.storage = null; + this.storageChest = null; + this.ore = null; + this.deepSlate = null; + this.vineBlock = null; + return; + } + + this.air = BukkitBlockResolution.isAir(data); + this.solid = BukkitBlockResolution.isSolid(data); + this.occluding = material.isOccluding(); + this.fluid = BukkitBlockResolution.isFluid(data); + this.water = BukkitBlockResolution.isWater(data); + this.waterLogged = BukkitBlockResolution.isWaterLogged(data); + this.lit = BukkitBlockResolution.isLit(data); + this.updatable = BukkitBlockResolution.isUpdatable(data); + this.foliage = BukkitBlockResolution.isFoliage(data); + this.foliagePlantable = BukkitBlockResolution.isFoliagePlantable(data); + this.decorant = BukkitBlockResolution.isDecorant(data); + this.storage = BukkitBlockResolution.isStorage(data); + this.storageChest = BukkitBlockResolution.isStorageChest(data); + this.ore = BukkitBlockResolution.isOre(data); + this.deepSlate = BukkitBlockResolution.isDeepSlate(data); + this.vineBlock = BukkitBlockResolution.isVineBlock(data); } public static BukkitBlockState of(BlockData data) { @@ -125,32 +173,17 @@ public final class BukkitBlockState implements PlatformBlockState { @Override public boolean isAir() { - Boolean cached = air; - if (cached == null) { - cached = BukkitBlockResolution.isAir(data); - air = cached; - } - return cached; + return air != null ? air : BukkitBlockResolution.isAir(data); } @Override public boolean isSolid() { - Boolean cached = solid; - if (cached == null) { - cached = BukkitBlockResolution.isSolid(data); - solid = cached; - } - return cached; + return solid != null ? solid : BukkitBlockResolution.isSolid(data); } @Override public boolean isOccluding() { - Boolean cached = occluding; - if (cached == null) { - cached = data.getMaterial().isOccluding(); - occluding = cached; - } - return cached; + return occluding != null ? occluding : data.getMaterial().isOccluding(); } @Override @@ -160,87 +193,67 @@ public final class BukkitBlockState implements PlatformBlockState { @Override public boolean isFluid() { - Boolean cached = fluid; - if (cached == null) { - cached = BukkitBlockResolution.isFluid(data); - fluid = cached; - } - return cached; + return fluid != null ? fluid : BukkitBlockResolution.isFluid(data); } @Override public boolean isWater() { - Boolean cached = water; - if (cached == null) { - cached = BukkitBlockResolution.isWater(data); - water = cached; - } - return cached; + return water != null ? water : BukkitBlockResolution.isWater(data); } @Override public boolean isWaterLogged() { - return BukkitBlockResolution.isWaterLogged(data); + return waterLogged != null ? waterLogged : BukkitBlockResolution.isWaterLogged(data); } @Override public boolean isLit() { - return BukkitBlockResolution.isLit(data); + return lit != null ? lit : BukkitBlockResolution.isLit(data); } @Override public boolean isUpdatable() { - return BukkitBlockResolution.isUpdatable(data); + return updatable != null ? updatable : BukkitBlockResolution.isUpdatable(data); } @Override public boolean isFoliage() { - Boolean cached = foliage; - if (cached == null) { - cached = BukkitBlockResolution.isFoliage(data); - foliage = cached; - } - return cached; + return foliage != null ? foliage : BukkitBlockResolution.isFoliage(data); } @Override public boolean isFoliagePlantable() { - return BukkitBlockResolution.isFoliagePlantable(data); + return foliagePlantable != null ? foliagePlantable : BukkitBlockResolution.isFoliagePlantable(data); } @Override public boolean isDecorant() { - Boolean cached = decorant; - if (cached == null) { - cached = BukkitBlockResolution.isDecorant(data); - decorant = cached; - } - return cached; + return decorant != null ? decorant : BukkitBlockResolution.isDecorant(data); } @Override public boolean isStorage() { - return BukkitBlockResolution.isStorage(data); + return storage != null ? storage : BukkitBlockResolution.isStorage(data); } @Override public boolean isStorageChest() { - return BukkitBlockResolution.isStorageChest(data); + return storageChest != null ? storageChest : BukkitBlockResolution.isStorageChest(data); } @Override public boolean isOre() { - return BukkitBlockResolution.isOre(data); + return ore != null ? ore : BukkitBlockResolution.isOre(data); } @Override public boolean isDeepSlate() { - return BukkitBlockResolution.isDeepSlate(data); + return deepSlate != null ? deepSlate : BukkitBlockResolution.isDeepSlate(data); } @Override public boolean isVineBlock() { - return BukkitBlockResolution.isVineBlock(data); + return vineBlock != null ? vineBlock : BukkitBlockResolution.isVineBlock(data); } @Override @@ -255,7 +268,20 @@ public final class BukkitBlockState implements PlatformBlockState { @Override public boolean hasTileEntity() { - return INMS.get().hasTile(data.getMaterial()); + Boolean cached = tileEntity; + if (cached == null) { + cached = INMS.get().hasTile(data.getMaterial()); + tileEntity = cached; + } + return cached; + } + + @Override + public boolean isAirOrFluid() { + if (air != null && fluid != null) { + return air || fluid; + } + return PlatformBlockState.super.isAirOrFluid(); } @Override diff --git a/core/src/main/java/art/arcane/iris/util/common/data/B.java b/core/src/main/java/art/arcane/iris/util/common/data/B.java index c1bc517c4..4b36d0cfe 100644 --- a/core/src/main/java/art/arcane/iris/util/common/data/B.java +++ b/core/src/main/java/art/arcane/iris/util/common/data/B.java @@ -2,19 +2,22 @@ package art.arcane.iris.util.common.data; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.iris.spi.PlatformRegistries; import art.arcane.volmlib.util.collection.KList; public class B { + private static volatile PlatformRegistries registries; + public static PlatformBlockState getState(String bdxf) { - return IrisPlatforms.get().registries().block(bdxf); + return registries().block(bdxf); } public static PlatformBlockState getStateOrNull(String bdxf) { - return IrisPlatforms.get().registries().blockOrNull(bdxf); + return registries().blockOrNull(bdxf); } public static PlatformBlockState getStateOrNull(String bdxf, boolean warn) { - return IrisPlatforms.get().registries().blockOrNull(bdxf, warn); + return registries().blockOrNull(bdxf, warn); } public static KList getStates(KList find) { @@ -26,11 +29,11 @@ public class B { } public static PlatformBlockState getAirState() { - return IrisPlatforms.get().registries().air(); + return registries().air(); } public static PlatformBlockState toDeepSlateOre(PlatformBlockState block, PlatformBlockState ore) { - return IrisPlatforms.get().registries().deepSlateOre(block, ore); + return registries().deepSlateOre(block, ore); } public static boolean isAir(PlatformBlockState state) { @@ -108,4 +111,15 @@ public class B { public static boolean matches(PlatformBlockState filter, PlatformBlockState state) { return filter != null && state != null && filter.matches(state); } + + private static PlatformRegistries registries() { + PlatformRegistries cached = registries; + if (cached != null) { + return cached; + } + + PlatformRegistries resolved = IrisPlatforms.get().registries(); + registries = resolved; + return resolved; + } } diff --git a/core/src/main/java/art/arcane/iris/util/common/parallel/MultiBurst.java b/core/src/main/java/art/arcane/iris/util/common/parallel/MultiBurst.java index e2ff0d78a..9256b58a8 100644 --- a/core/src/main/java/art/arcane/iris/util/common/parallel/MultiBurst.java +++ b/core/src/main/java/art/arcane/iris/util/common/parallel/MultiBurst.java @@ -7,6 +7,8 @@ import art.arcane.volmlib.util.math.M; import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.ExecutorsKt; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; import java.util.function.IntSupplier; public class MultiBurst extends MultiBurstSupport { @@ -46,6 +48,20 @@ public class MultiBurst extends MultiBurstSupport { return dispatcher; } + public boolean ownsCurrentThread() { + Thread thread = Thread.currentThread(); + if (!(thread instanceof ForkJoinWorkerThread worker)) { + return false; + } + + ExecutorService service = service(); + if (!(service instanceof ForkJoinPool pool)) { + return false; + } + + return worker.getPool() == pool; + } + @Override public BurstExecutor burst(int estimate) { return new BurstExecutor(service(), estimate); diff --git a/core/src/main/java/art/arcane/iris/util/project/hunk/view/ChunkDataHunkHolder.java b/core/src/main/java/art/arcane/iris/util/project/hunk/view/ChunkDataHunkHolder.java index 4b895caec..6148d29f5 100644 --- a/core/src/main/java/art/arcane/iris/util/project/hunk/view/ChunkDataHunkHolder.java +++ b/core/src/main/java/art/arcane/iris/util/project/hunk/view/ChunkDataHunkHolder.java @@ -60,16 +60,47 @@ public class ChunkDataHunkHolder extends AtomicHunk { } public void apply() { - for (int i = 0; i < getHeight(); i++) { - for (int j = 0; j < getWidth(); j++) { - for (int k = 0; k < getDepth(); k++) { - PlatformBlockState b = super.getRaw(j, i, k); + int height = getHeight(); + for (int x = 0; x < getWidth(); x++) { + for (int z = 0; z < getDepth(); z++) { + BlockData activeBlock = null; + int runStart = -1; - if (b != null) { - chunk.setBlock(j, i + chunk.getMinHeight(), k, (BlockData) b.nativeHandle()); + for (int y = 0; y < height; y++) { + PlatformBlockState state = super.getRaw(x, y, z); + BlockData block = state == null ? null : (BlockData) state.nativeHandle(); + if (block == null) { + flushRun(x, z, runStart, y, activeBlock); + activeBlock = null; + runStart = -1; + continue; } + + if (activeBlock != null && activeBlock.equals(block)) { + continue; + } + + flushRun(x, z, runStart, y, activeBlock); + activeBlock = block; + runStart = y; } + + flushRun(x, z, runStart, height, activeBlock); } } } + + private void flushRun(int x, int z, int startY, int endY, BlockData block) { + if (block == null || startY < 0 || endY <= startY) { + return; + } + + int minY = chunk.getMinHeight(); + if (endY - startY == 1) { + chunk.setBlock(x, startY + minY, z, block); + return; + } + + chunk.setRegion(x, startY + minY, z, x + 1, endY + minY, z + 1, block); + } } diff --git a/core/src/test/java/art/arcane/iris/core/pregenerator/methods/AsyncPregenMethodConcurrencyCapTest.java b/core/src/test/java/art/arcane/iris/core/pregenerator/methods/AsyncPregenMethodConcurrencyCapTest.java index bc331216c..8dcca2a0b 100644 --- a/core/src/test/java/art/arcane/iris/core/pregenerator/methods/AsyncPregenMethodConcurrencyCapTest.java +++ b/core/src/test/java/art/arcane/iris/core/pregenerator/methods/AsyncPregenMethodConcurrencyCapTest.java @@ -7,10 +7,11 @@ import static org.junit.Assert.assertEquals; public class AsyncPregenMethodConcurrencyCapTest { @Test public void paperLikeRecommendedCapTracksWorkerThreads() { - assertEquals(8, AsyncPregenMethod.computePaperLikeRecommendedCap(1)); - assertEquals(8, AsyncPregenMethod.computePaperLikeRecommendedCap(4)); - assertEquals(24, AsyncPregenMethod.computePaperLikeRecommendedCap(12)); - assertEquals(96, AsyncPregenMethod.computePaperLikeRecommendedCap(80)); + assertEquals(16, AsyncPregenMethod.computePaperLikeRecommendedCap(1)); + assertEquals(16, AsyncPregenMethod.computePaperLikeRecommendedCap(4)); + assertEquals(48, AsyncPregenMethod.computePaperLikeRecommendedCap(12)); + assertEquals(128, AsyncPregenMethod.computePaperLikeRecommendedCap(80)); + assertEquals(128, AsyncPregenMethod.computePaperLikeRecommendedCap(128)); } @Test diff --git a/core/src/test/java/art/arcane/iris/util/common/parallel/BurstExecutorSupportReentrantTest.java b/core/src/test/java/art/arcane/iris/util/common/parallel/BurstExecutorSupportReentrantTest.java index 6353637d6..2f84ae190 100644 --- a/core/src/test/java/art/arcane/iris/util/common/parallel/BurstExecutorSupportReentrantTest.java +++ b/core/src/test/java/art/arcane/iris/util/common/parallel/BurstExecutorSupportReentrantTest.java @@ -11,6 +11,18 @@ import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.Assert.assertTrue; public class BurstExecutorSupportReentrantTest { + @Test + public void multiBurstRecognizesOwnedWorkerThread() throws Exception { + MultiBurst burst = new MultiBurst("Iris Test", Thread.NORM_PRIORITY, () -> 1); + + try { + Future future = burst.submit(burst::ownsCurrentThread); + assertTrue(future.get(5, TimeUnit.SECONDS)); + } finally { + burst.close(); + } + } + @Test public void runsNestedBurstInlineOnSameForkJoinPoolWorker() throws Exception { ForkJoinPool pool = new ForkJoinPool(1);