From a3d7269a99a77ed14443991af2d89810c9e52cf8 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Thu, 11 Jun 2026 20:30:20 -0400 Subject: [PATCH] Headless Classload --- .../iris/core/commands/CommandFind.java | 11 +- .../iris/core/commands/CommandStudio.java | 7 +- .../iris/core/commands/CommandWhat.java | 3 +- .../iris/core/link/IrisPapiExpansion.java | 7 +- core/purity-allowlist.txt | 7 +- .../core/runtime/InPlaceChunkRegenerator.java | 3 +- .../arcane/iris/engine/IrisWorldManager.java | 9 +- .../iris/engine/framework/BlockUpdater.java | 8 - .../arcane/iris/engine/framework/Engine.java | 495 +--------------- .../iris/engine/framework/EnginePlayer.java | 5 +- .../iris/engine/framework/LootProvider.java | 11 - .../framework/placer/WorldObjectPlacer.java | 5 +- .../arcane/iris/engine/object/IrisEffect.java | 8 +- .../arcane/iris/engine/object/IrisEntity.java | 68 ++- .../arcane/iris/engine/object/IrisObject.java | 22 +- .../engine/object/IrisObjectRotation.java | 10 +- .../iris/engine/object/LegacyTileData.java | 84 +-- .../iris/engine/platform/EngineBukkitOps.java | 539 ++++++++++++++++++ .../director/context/BiomeContextHandler.java | 3 +- .../context/GeneratorContextHandler.java | 3 +- .../context/RegionContextHandler.java | 3 +- .../art/arcane/iris/probe/ClassloadProbe.java | 8 + 22 files changed, 695 insertions(+), 624 deletions(-) create mode 100644 core/src/main/java/art/arcane/iris/engine/platform/EngineBukkitOps.java diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandFind.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandFind.java index caf15671a..1d4b68d53 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandFind.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandFind.java @@ -23,6 +23,7 @@ import art.arcane.iris.platform.bukkit.BukkitPlatform; import art.arcane.iris.core.service.ObjectStudioSaveService; import art.arcane.iris.engine.framework.Engine; import art.arcane.iris.engine.framework.IrisStructureLocator; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.framework.StructureReachability; import art.arcane.iris.engine.object.IrisBiome; import art.arcane.iris.engine.object.IrisRegion; @@ -57,7 +58,7 @@ public class CommandFind implements DirectorExecutor { return; } - e.gotoBiome(biome, player(), teleport); + EngineBukkitOps.gotoBiome(e, biome, player(), teleport); } @Director(description = "Find a region") @@ -74,7 +75,7 @@ public class CommandFind implements DirectorExecutor { return; } - e.gotoRegion(region, player(), teleport); + EngineBukkitOps.gotoRegion(e, region, player(), teleport); } @Director(description = "Find a point of interest.") @@ -90,7 +91,7 @@ public class CommandFind implements DirectorExecutor { return; } - e.gotoPOI(type, player(), teleport); + EngineBukkitOps.gotoPOI(e, type, player(), teleport); } @Director(description = "Find a structure (a vanilla key like minecraft:village_plains or minecraft:stronghold, or an imported iris structure key)") @@ -106,7 +107,7 @@ public class CommandFind implements DirectorExecutor { } if (IrisStructureLocator.isPlaced(e, structure)) { - e.gotoStructure(structure, player(), true); + EngineBukkitOps.gotoStructure(e, structure, player(), true); return; } @@ -180,7 +181,7 @@ public class CommandFind implements DirectorExecutor { } if (e.hasObjectPlacement(object)) { - e.gotoObject(object, player(), teleport); + EngineBukkitOps.gotoObject(e, object, player(), teleport); return; } diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandStudio.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandStudio.java index af68e9324..a02027550 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandStudio.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandStudio.java @@ -32,6 +32,7 @@ import art.arcane.iris.core.structure.StructureImporter; import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.framework.Engine; import art.arcane.iris.engine.object.*; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.platform.PlatformChunkGenerator; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; @@ -246,11 +247,11 @@ public class CommandStudio implements DirectorExecutor { ) { if (noStudio()) return; - KList tables = engine().getLootTables(RNG.r, player().getLocation().getBlock()); + KList tables = EngineBukkitOps.getLootTables(engine(), RNG.r, player().getLocation().getBlock()); Inventory inv = Bukkit.createInventory(null, 27 * 2); try { - engine().addItems(true, inv, RNG.r, tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1); + EngineBukkitOps.addItems(engine(), true, inv, RNG.r, tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1); } catch (Throwable e) { Iris.reportError(e); sender().sendMessage(C.RED + "Cannot add items to virtual inventory because of: " + e.getMessage()); @@ -277,7 +278,7 @@ public class CommandStudio implements DirectorExecutor { inv.clear(); } - engine.addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player.getWorld(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ(), 1); + EngineBukkitOps.addItems(engine, true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player.getWorld(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ(), 1); }, fast ? 5 : 35)); sender().sendMessage(C.GREEN + "Opening inventory now!"); diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandWhat.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandWhat.java index 1b59d8d50..2bf874afd 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandWhat.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/commands/CommandWhat.java @@ -26,6 +26,7 @@ import art.arcane.iris.core.edit.BlockSignal; import art.arcane.iris.core.nms.INMS; import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.framework.Engine; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.object.IrisBiome; import art.arcane.iris.engine.object.IrisRegion; import art.arcane.iris.util.common.director.DirectorExecutor; @@ -96,7 +97,7 @@ public class CommandWhat implements DirectorExecutor { public void region() { try { Chunk chunk = world().getChunkAt(player().getLocation().getBlockZ() / 16, player().getLocation().getBlockZ() / 16); - IrisRegion r = engine().getRegion(chunk); + IrisRegion r = EngineBukkitOps.getRegion(engine(), chunk); sender().sendMessage("IRegion: " + r.getLoadKey() + " (" + r.getName() + ")"); } catch (Throwable e) { diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/link/IrisPapiExpansion.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/link/IrisPapiExpansion.java index 10a694cb8..4b0c9db96 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/link/IrisPapiExpansion.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/core/link/IrisPapiExpansion.java @@ -21,6 +21,7 @@ package art.arcane.iris.core.link; import art.arcane.iris.Iris; import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.object.IrisBiome; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.platform.PlatformChunkGenerator; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.Location; @@ -73,15 +74,15 @@ public class IrisPapiExpansion extends PlaceholderExpansion { } } else if (p.equalsIgnoreCase("region_name")) { if (a != null) { - return a.getEngine().getRegion(l).getName(); + return EngineBukkitOps.getRegion(a.getEngine(), l).getName(); } } else if (p.equalsIgnoreCase("region_id")) { if (a != null) { - return a.getEngine().getRegion(l).getLoadKey(); + return EngineBukkitOps.getRegion(a.getEngine(), l).getLoadKey(); } } else if (p.equalsIgnoreCase("region_file")) { if (a != null) { - return a.getEngine().getRegion(l).getLoadFile().getPath(); + return EngineBukkitOps.getRegion(a.getEngine(), l).getLoadFile().getPath(); } } else if (p.equalsIgnoreCase("terrain_slope")) { if (a != null) { diff --git a/core/purity-allowlist.txt b/core/purity-allowlist.txt index 6c2d4e3a1..6aa991004 100644 --- a/core/purity-allowlist.txt +++ b/core/purity-allowlist.txt @@ -51,7 +51,6 @@ art/arcane/iris/core/runtime/ChunkClearer.java art/arcane/iris/core/runtime/ChunkJobReporter.java art/arcane/iris/core/runtime/GoldenHashScanner.java art/arcane/iris/core/runtime/InPlaceChunkRegenerator.java -art/arcane/iris/core/runtime/ObjectStudioLayout.java art/arcane/iris/core/runtime/PaperLikeRuntimeControlBackend.java art/arcane/iris/core/runtime/StudioOpenCoordinator.java art/arcane/iris/core/runtime/WorldRuntimeControlBackend.java @@ -83,12 +82,10 @@ art/arcane/iris/engine/actuator/IrisBiomeActuator.java art/arcane/iris/engine/data/chunk/LinkedTerrainChunk.java art/arcane/iris/engine/data/chunk/TerrainChunk.java art/arcane/iris/engine/decorator/DecoratorCore.java -art/arcane/iris/engine/framework/BlockUpdater.java art/arcane/iris/engine/framework/Engine.java art/arcane/iris/engine/framework/EngineAssignedWorldManager.java art/arcane/iris/engine/framework/EnginePlayer.java art/arcane/iris/engine/framework/EngineWorldManager.java -art/arcane/iris/engine/framework/LootProvider.java art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java art/arcane/iris/engine/mantle/EngineMantle.java art/arcane/iris/engine/mantle/MantleWriter.java @@ -123,7 +120,6 @@ art/arcane/iris/engine/object/IrisLootTable.java art/arcane/iris/engine/object/IrisObject.java art/arcane/iris/engine/object/IrisObjectPlacement.java art/arcane/iris/engine/object/IrisObjectRotation.java -art/arcane/iris/engine/object/IrisObjectTranslate.java art/arcane/iris/engine/object/IrisPotionEffect.java art/arcane/iris/engine/object/IrisProceduralBlocks.java art/arcane/iris/engine/object/IrisSurface.java @@ -151,6 +147,7 @@ art/arcane/iris/engine/object/tree/TreePlausibility.java art/arcane/iris/engine/platform/BukkitChunkGenerator.java art/arcane/iris/engine/platform/DummyBiomeProvider.java art/arcane/iris/engine/platform/DummyChunkGenerator.java +art/arcane/iris/engine/platform/EngineBukkitOps.java art/arcane/iris/engine/platform/studio/generators/ObjectStudioGenerator.java art/arcane/iris/platform/bukkit/BukkitBiome.java art/arcane/iris/platform/bukkit/BukkitBlockResolution.java @@ -162,7 +159,6 @@ art/arcane/iris/platform/bukkit/BukkitRegistries.java art/arcane/iris/platform/bukkit/BukkitScheduler.java art/arcane/iris/platform/bukkit/BukkitWorld.java art/arcane/iris/util/common/data/IrisCustomData.java -art/arcane/iris/util/common/data/VectorMap.java art/arcane/iris/util/common/data/registry/Attributes.java art/arcane/iris/util/common/data/registry/KeyedRegistry.java art/arcane/iris/util/common/data/registry/Materials.java @@ -183,7 +179,6 @@ art/arcane/iris/util/common/inventorygui/Window.java art/arcane/iris/util/common/inventorygui/WindowResolution.java art/arcane/iris/util/common/math/AxisAlignedBB.java art/arcane/iris/util/common/math/Direction.java -art/arcane/iris/util/common/math/Vector3i.java art/arcane/iris/util/common/math/VectorMath.java art/arcane/iris/util/common/misc/Bindings.java art/arcane/iris/util/common/nbt/mca/Chunk.java diff --git a/core/src/main/java/art/arcane/iris/core/runtime/InPlaceChunkRegenerator.java b/core/src/main/java/art/arcane/iris/core/runtime/InPlaceChunkRegenerator.java index 73673cbb4..09807ca6d 100644 --- a/core/src/main/java/art/arcane/iris/core/runtime/InPlaceChunkRegenerator.java +++ b/core/src/main/java/art/arcane/iris/core/runtime/InPlaceChunkRegenerator.java @@ -21,6 +21,7 @@ package art.arcane.iris.core.runtime; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.core.nms.INMS; import art.arcane.iris.engine.data.chunk.TerrainChunk; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.framework.Engine; import art.arcane.iris.engine.mantle.EngineMantle; import art.arcane.iris.spi.PlatformBiome; @@ -175,7 +176,7 @@ public final class InPlaceChunkRegenerator { } } - engine.updateChunk(chunk); + EngineBukkitOps.updateChunk(engine, chunk); world.refreshChunk(chunkX, chunkZ); } diff --git a/core/src/main/java/art/arcane/iris/engine/IrisWorldManager.java b/core/src/main/java/art/arcane/iris/engine/IrisWorldManager.java index 6a767423f..d9b1f5b68 100644 --- a/core/src/main/java/art/arcane/iris/engine/IrisWorldManager.java +++ b/core/src/main/java/art/arcane/iris/engine/IrisWorldManager.java @@ -25,6 +25,7 @@ import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.data.cache.Cache; import art.arcane.iris.engine.framework.Engine; import art.arcane.iris.engine.framework.EngineAssignedWorldManager; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.object.*; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.collection.KList; @@ -281,7 +282,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager { warmupMantleChunkAsync(chunkX, chunkZ); return; } - getEngine().updateChunk(chunk); + EngineBukkitOps.updateChunk(getEngine(), chunk); } if (!isEntitySpawningEnabledForCurrentWorld()) { @@ -585,7 +586,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager { Predicate filter = i -> i.canSpawn(getEngine(), c.getX(), c.getZ()); ChunkCounter counter = new ChunkCounter(c.getEntities()); - IrisBiome biome = getEngine().getSurfaceBiome(c); + IrisBiome biome = EngineBukkitOps.getSurfaceBiome(getEngine(), c); IrisEntitySpawn v = spawnRandomly(Stream.concat(getData().getSpawnerLoader() .loadAll(getDimension().getEntitySpawners()) .shuffleCopy(RNG.r) @@ -978,11 +979,11 @@ public class IrisWorldManager extends EngineAssignedWorldManager { }); KList d = new KList<>(); - IrisBiome b = getEngine().getBiome(e.getBlock().getLocation().clone().subtract(0, getEngine().getWorld().minHeight(), 0)); + IrisBiome b = EngineBukkitOps.getBiome(getEngine(), e.getBlock().getLocation().clone().subtract(0, getEngine().getWorld().minHeight(), 0)); List dropProviders = filterDrops(b.getBlockDrops(), e, getData()); if (dropProviders.stream().noneMatch(IrisBlockDrops::isSkipParents)) { - IrisRegion r = getEngine().getRegion(e.getBlock().getLocation()); + IrisRegion r = EngineBukkitOps.getRegion(getEngine(), e.getBlock().getLocation()); dropProviders.addAll(filterDrops(r.getBlockDrops(), e, getData())); dropProviders.addAll(filterDrops(getEngine().getDimension().getBlockDrops(), e, getData())); } diff --git a/core/src/main/java/art/arcane/iris/engine/framework/BlockUpdater.java b/core/src/main/java/art/arcane/iris/engine/framework/BlockUpdater.java index 7d6d1aa03..8cd73acbd 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/BlockUpdater.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/BlockUpdater.java @@ -19,16 +19,8 @@ package art.arcane.iris.engine.framework; import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.volmlib.util.mantle.runtime.MantleChunk; -import art.arcane.volmlib.util.math.RNG; -import art.arcane.volmlib.util.matter.Matter; -import org.bukkit.Chunk; public interface BlockUpdater { void catchBlockUpdates(int x, int y, int z, PlatformBlockState data); - - void updateChunk(Chunk c); - - void update(int x, int y, int z, Chunk c, MantleChunk mc, RNG rf); } diff --git a/core/src/main/java/art/arcane/iris/engine/framework/Engine.java b/core/src/main/java/art/arcane/iris/engine/framework/Engine.java index 684fb397c..d7efdef97 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/Engine.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/Engine.java @@ -20,30 +20,23 @@ package art.arcane.iris.engine.framework; import art.arcane.iris.platform.bukkit.BukkitBlockResolution; -import art.arcane.iris.spi.IrisServices; import art.arcane.iris.core.IrisSettings; -import art.arcane.iris.core.events.IrisLootEvent; import art.arcane.iris.engine.framework.render.RenderType; import art.arcane.iris.engine.framework.render.Renderer; import art.arcane.iris.core.gui.PregeneratorJob; -import art.arcane.iris.core.link.Identifier; import art.arcane.iris.core.loader.IrisData; import art.arcane.iris.core.loader.IrisRegistrant; import art.arcane.iris.core.nms.container.BlockPos; import art.arcane.iris.core.nms.container.Pair; -import art.arcane.iris.core.service.ExternalDataSVC; import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.IrisComplex; import art.arcane.iris.engine.UpperDimensionContext; -import art.arcane.iris.engine.data.cache.Cache; import art.arcane.iris.engine.data.chunk.TerrainChunk; import art.arcane.iris.engine.mantle.EngineMantle; import art.arcane.iris.engine.object.*; -import art.arcane.iris.platform.bukkit.BukkitBlockState; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.PlatformBiome; import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.util.project.matter.TileWrapper; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.iris.util.project.context.ChunkContext; @@ -52,48 +45,30 @@ import art.arcane.iris.util.common.data.DataProvider; import art.arcane.iris.util.common.data.IrisCustomData; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.documentation.ChunkCoordinates; -import art.arcane.iris.util.common.format.C; import art.arcane.volmlib.util.function.Function2; import art.arcane.iris.util.project.hunk.Hunk; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; import art.arcane.volmlib.util.mantle.flag.MantleFlag; -import art.arcane.volmlib.util.math.BlockPosition; -import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.M; -import art.arcane.volmlib.util.math.Position2; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.matter.Matter; import art.arcane.volmlib.util.matter.MatterCavern; -import art.arcane.volmlib.util.matter.MatterUpdate; import art.arcane.iris.util.common.parallel.BurstExecutor; import art.arcane.iris.util.common.parallel.MultiBurst; import art.arcane.iris.util.common.plugin.VolmitSender; -import art.arcane.iris.util.common.reflect.KeyedType; import art.arcane.iris.util.common.reflect.W; import art.arcane.volmlib.util.scheduling.ChronoLatch; import art.arcane.iris.util.common.scheduling.J; -import art.arcane.iris.util.common.scheduling.jobs.SingleJob; -import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; import art.arcane.iris.util.project.stream.ProceduralStream; -import io.papermc.lib.PaperLib; -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; +import org.bukkit.World; import org.bukkit.block.data.BlockData; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; import java.awt.Color; import java.util.Locale; import java.util.Set; import java.util.UUID; -import java.lang.reflect.Method; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -339,243 +314,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat void blockUpdatedMetric(); - @ChunkCoordinates - @Override - default void updateChunk(Chunk c) { - for (int x = -1; x <= 1; x++) { - for (int z = -1; z <= 1; z++) { - if (c.getWorld().isChunkLoaded(c.getX() + x, c.getZ() + z)) - continue; - var msg = "Chunk %s, %s [%s, %s] is not loaded".formatted(c.getX() + x, c.getZ() + z, x, z); - IrisLogging.debug(msg); - return; - } - } - var mantle = getMantle().getMantle(); - if (!mantle.isLoaded(c)) { - var msg = "Mantle Chunk " + c.getX() + "," + c.getZ() + " is not loaded"; - IrisLogging.debug(msg); - return; - } - - if (!J.isFolia() && !J.isPrimaryThread()) { - CompletableFuture scheduled = J.sfut(() -> updateChunk(c)); - if (scheduled != null) { - try { - scheduled.join(); - } catch (Throwable e) { - IrisLogging.reportError(e); - } - } - return; - } - - var chunk = mantle.getChunk(c).use(); - try { - Runnable tileTask = () -> { - chunk.iterate(TileWrapper.class, (x, y, z, v) -> { - Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15); - if (!TileData.setTileState(block, v.getData())) { - NamespacedKey blockTypeKey = KeyedType.getKey(block.getType()); - NamespacedKey tileTypeKey = KeyedType.getKey(v.getData().getMaterial()); - String blockType = blockTypeKey == null ? block.getType().name() : blockTypeKey.toString(); - String tileType = tileTypeKey == null ? v.getData().getMaterial().name() : tileTypeKey.toString(); - IrisLogging.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), blockType, tileType); - } - }); - }; - - Runnable customTask = () -> { - chunk.iterate(Identifier.class, (x, y, z, v) -> { - IrisServices.get(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v); - }); - }; - - Runnable updateTask = () -> { - PrecisionStopwatch p = PrecisionStopwatch.start(); - int[][] grid = new int[16][16]; - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - grid[x][z] = Integer.MIN_VALUE; - } - } - - RNG rng = new RNG(Cache.key(c.getX(), c.getZ())); - chunk.iterate(MatterCavern.class, (x, yf, z, v) -> { - int y = yf + getWorld().minHeight(); - x &= 15; - z &= 15; - Block block = c.getBlock(x, y, z); - if (!BukkitBlockResolution.isFluid(block.getBlockData())) { - return; - } - boolean u = BukkitBlockResolution.isAir(block.getRelative(BlockFace.DOWN).getBlockData()) - || BukkitBlockResolution.isAir(block.getRelative(BlockFace.WEST).getBlockData()) - || BukkitBlockResolution.isAir(block.getRelative(BlockFace.EAST).getBlockData()) - || BukkitBlockResolution.isAir(block.getRelative(BlockFace.SOUTH).getBlockData()) - || BukkitBlockResolution.isAir(block.getRelative(BlockFace.NORTH).getBlockData()); - - if (u) grid[x][z] = Math.max(grid[x][z], y); - }); - - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - if (grid[x][z] == Integer.MIN_VALUE) { - continue; - } - update(x, grid[x][z], z, c, chunk, rng); - } - } - - chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> { - int y = yf + getWorld().minHeight(); - if (v != null && v.isUpdate()) { - update(x, y, z, c, chunk, rng); - } - }); - chunk.deleteSlices(MatterUpdate.class); - getMetrics().getUpdates().put(p.getMilliseconds()); - }; - - if (shouldRunChunkUpdateInline(c)) { - chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> { - chunk.raiseFlagUnchecked(MantleFlag.TILE, tileTask); - chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, customTask); - chunk.raiseFlagUnchecked(MantleFlag.UPDATE, updateTask); - }); - return; - } - - Semaphore semaphore = new Semaphore(1024); - chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> { - chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, c, tileTask, 0)); - chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, c, customTask, 0)); - chunk.raiseFlagUnchecked(MantleFlag.UPDATE, run(semaphore, c, updateTask, RNG.r.i(1, 20))); - }); - - try { - semaphore.acquire(1024); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - IrisLogging.reportError(ex); - } - } finally { - chunk.release(); - } - } - - private static boolean shouldRunChunkUpdateInline(Chunk chunk) { - if (chunk == null) { - return false; - } - - if (!J.isFolia()) { - return true; - } - - return J.isOwnedByCurrentRegion(chunk.getWorld(), chunk.getX(), chunk.getZ()); - } - - private static Runnable run(Semaphore semaphore, Chunk contextChunk, Runnable runnable, int delay) { - return () -> { - try { - semaphore.acquire(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - int effectiveDelay = J.isFolia() ? 0 : delay; - boolean scheduled = J.runRegion(contextChunk.getWorld(), contextChunk.getX(), contextChunk.getZ(), () -> { - try { - runnable.run(); - } finally { - semaphore.release(); - } - }, effectiveDelay); - - if (!scheduled) { - try { - if (J.isPrimaryThread()) { - runnable.run(); - } - } finally { - semaphore.release(); - } - } - }; - } - - @BlockCoordinates - @Override - - default void update(int x, int y, int z, Chunk c, MantleChunk mc, RNG rf) { - Block block = c.getBlock(x, y, z); - BlockData data = block.getBlockData(); - blockUpdatedMetric(); - if (BukkitBlockResolution.isStorage(data)) { - RNG rx = rf.nextParallelRNG(BlockPosition.toLong(x, y, z)); - InventorySlotType slot = null; - - if (BukkitBlockResolution.isStorageChest(data)) { - slot = InventorySlotType.STORAGE; - } - - if (slot != null) { - KList tables = getLootTables(rx, block, mc); - - try { - Bukkit.getPluginManager().callEvent(new IrisLootEvent(this, block, slot, tables)); - if (tables.isEmpty()) return; - InventoryHolder m = (InventoryHolder) block.getState(); - addItems(false, m.getInventory(), rx, tables, slot, c.getWorld(), x, y, z, 15); - - } catch (Throwable e) { - IrisLogging.reportError(e); - } - } - } else { - block.setType(Material.AIR, false); - block.setBlockData(data, true); - } - } - - @Override - default void scramble(Inventory inventory, RNG rng) { - org.bukkit.inventory.ItemStack[] items = inventory.getContents(); - org.bukkit.inventory.ItemStack[] nitems = new org.bukkit.inventory.ItemStack[inventory.getSize()]; - System.arraycopy(items, 0, nitems, 0, items.length); - boolean packedFull = false; - - splitting: - for (int i = 0; i < nitems.length; i++) { - ItemStack is = nitems[i]; - - if (is != null && is.getAmount() > 1 && !packedFull) { - for (int j = 0; j < nitems.length; j++) { - if (nitems[j] == null) { - int take = rng.nextInt(is.getAmount()); - take = take == 0 ? 1 : take; - is.setAmount(is.getAmount() - take); - nitems[j] = is.clone(); - nitems[j].setAmount(take); - continue splitting; - } - } - - packedFull = true; - } - } - - for (int i = nitems.length; i > 1; i--) { - int j = rng.nextInt(i); - ItemStack tmp = nitems[i - 1]; - nitems[i - 1] = nitems[j]; - nitems[j] = tmp; - } - - inventory.setContents(nitems); - } - @Override default void injectTables(KList list, IrisLootReference r, boolean fallback) { if (r.getMode().equals(IrisLootMode.FALLBACK) && !fallback) @@ -588,101 +326,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat list.addAll(r.getLootTables(getComplex())); } - @BlockCoordinates - @Override - default KList getLootTables(RNG rng, Block b) { - MantleChunk mc = getMantle().getMantle().getChunk(b.getChunk()).use(); - try { - return getLootTables(rng, b, mc); - } finally { - mc.release(); - } - } - - @BlockCoordinates - default KList getLootTables(RNG rng, Block b, MantleChunk mc) { - int rx = b.getX(); - int rz = b.getZ(); - int ry = b.getY() - getWorld().minHeight(); - double he = getComplex().getHeightStream().get(rx, rz); - KList tables = new KList<>(); - - PlacedObject po = getObjectPlacement(rx, ry, rz, mc); - if (po != null && po.getPlacement() != null) { - if (BukkitBlockResolution.isStorageChest(b.getBlockData())) { - IrisLootTable table = po.getPlacement().getTable(BukkitBlockState.of(b.getBlockData()), getData()); - if (table != null) { - tables.add(table); - if (po.getPlacement().isOverrideGlobalLoot()) { - return new KList<>(table); - } - } - } - } - - IrisRegion region = getComplex().getRegionStream().get(rx, rz); - IrisBiome biomeSurface = getComplex().getTrueBiomeStream().get(rx, rz); - IrisBiome biomeUnder = ry < he ? getCaveBiome(rx, ry, rz) : biomeSurface; - - double multiplier = 1D * getDimension().getLoot().getMultiplier() * region.getLoot().getMultiplier() * biomeSurface.getLoot().getMultiplier() * biomeUnder.getLoot().getMultiplier(); - boolean fallback = tables.isEmpty(); - injectTables(tables, getDimension().getLoot(), fallback); - injectTables(tables, region.getLoot(), fallback); - injectTables(tables, biomeSurface.getLoot(), fallback); - injectTables(tables, biomeUnder.getLoot(), fallback); - - if (tables.isNotEmpty()) { - int target = (int) Math.round(tables.size() * multiplier); - - while (tables.size() < target && tables.isNotEmpty()) { - tables.add(tables.get(rng.i(tables.size() - 1))); - } - - while (tables.size() > target && tables.isNotEmpty()) { - tables.remove(rng.i(tables.size() - 1)); - } - } - - return tables; - } - - @Override - default void addItems(boolean debug, Inventory inv, RNG rng, KList tables, InventorySlotType slot, World world, int x, int y, int z, int mgf) { - KList items = new KList<>(); - - for (IrisLootTable i : tables) { - if (i == null) - continue; - items.addAll(i.getLoot(debug, rng, slot, world, x, y, z)); - } - if (IrisLootEvent.callLootEvent(items, inv, world, x, y, z)) - return; - - if (PaperLib.isPaper() && getWorld().hasRealWorld()) { - PaperLib.getChunkAtAsync(getWorld().realWorld(), x >> 4, z >> 4).thenAccept((c) -> { - Runnable r = () -> { - for (ItemStack i : items) { - inv.addItem(i); - } - - scramble(inv, rng); - }; - - if (Bukkit.isPrimaryThread()) { - r.run(); - } else { - J.s(r); - } - }); - } else { - for (ItemStack i : items) { - inv.addItem(i); - } - - scramble(inv, rng); - } - } - EngineEffects getEffects(); default MultiBurst burst() { @@ -693,16 +336,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat burst().lazy(() -> getMantle().trim(10)); } - @BlockCoordinates - default IrisBiome getBiome(Location l) { - return getBiome(l.getBlockX(), l.getBlockY() - getWorld().minHeight(), l.getBlockZ()); - } - - @BlockCoordinates - default IrisRegion getRegion(Location l) { - return getRegion(l.getBlockX(), l.getBlockZ()); - } - IrisBiome getFocus(); IrisRegion getFocusRegion(); @@ -710,14 +343,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat IrisEngineData getEngineData(); - default IrisBiome getSurfaceBiome(Chunk c) { - return getSurfaceBiome((c.getX() << 4) + 8, (c.getZ() << 4) + 8); - } - - default IrisRegion getRegion(Chunk c) { - return getRegion((c.getX() << 4) + 8, (c.getZ() << 4) + 8); - } - default KList getAllBiomes() { KMap v = new KMap<>(); @@ -981,107 +606,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat int getCacheID(); - default IrisBiome getBiomeOrMantle(Location l) { - return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ()); - } - - default void gotoBiome(IrisBiome biome, Player player, boolean teleport) { - find(Locator.surfaceBiome(biome.getLoadKey()), player, teleport, "Biome " + biome.getName()); - } - - private static void find(Locator locator, Player player, boolean teleport, String message) { - find(locator, player, 120_000, location -> { - if (location == null) { - player.sendMessage(C.RED + "Could not find " + message + " within search range."); - return; - } - if (teleport) { - J.runEntity(player, () -> teleportAsyncSafely(player, location)); - player.sendMessage(C.GREEN + "Teleporting to " + message + "..."); - } else { - player.sendMessage(C.GREEN + message + " at: " + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ()); - } - }); - } - - private static void find(Locator locator, Player player, long timeout, Consumer consumer) { - AtomicLong checks = new AtomicLong(); - long ms = M.ms(); - new SingleJob("Searching", () -> { - try { - World world = player.getWorld(); - Engine engine = IrisToolbelt.access(world).getEngine(); - Position2 at = locator.find(engine, new Position2(player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4), timeout, checks::set).get(); - - if (at != null) { - int bx = (at.getX() << 4) + 8; - int bz = (at.getZ() << 4) + 8; - consumer.accept(new Location(world, bx, - world.getHighestBlockYAt(bx, bz) + 2, - bz)); - } else { - consumer.accept(null); - } - } catch (WrongEngineBroException | InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - }) { - @Override - public String getName() { - return "Searched " + Form.f(checks.get()) + " Chunks"; - } - - @Override - public int getTotalWork() { - return (int) timeout; - } - - @Override - public int getWorkCompleted() { - return (int) Math.min(M.ms() - ms, timeout - 1); - } - }.execute(new VolmitSender(player)); - } - - private static void teleportAsyncSafely(Player player, Location location) { - if (player == null || location == null) { - return; - } - - if (invokeNativeTeleportAsync(player, location)) { - return; - } - - try { - CompletableFuture teleportFuture = PaperLib.teleportAsync(player, location); - if (teleportFuture != null) { - teleportFuture.exceptionally(throwable -> { - IrisLogging.reportError(throwable); - return false; - }); - } - } catch (Throwable throwable) { - IrisLogging.reportError(throwable); - } - } - - private static boolean invokeNativeTeleportAsync(Player player, Location location) { - try { - Method teleportAsyncMethod = player.getClass().getMethod("teleportAsync", Location.class); - teleportAsyncMethod.invoke(player, location); - return true; - } catch (NoSuchMethodException ignored) { - return false; - } catch (Throwable throwable) { - IrisLogging.reportError(throwable); - return false; - } - } - - default void gotoObject(String s, Player player, boolean teleport) { - find(Locator.object(s), player, teleport, "Object " + s); - } - default boolean hasObjectPlacement(String objectKey) { String normalizedObjectKey = normalizeObjectPlacementKey(objectKey); if (normalizedObjectKey.isBlank()) { @@ -1100,23 +624,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat return !regionKeys.isEmpty(); } - default void gotoRegion(IrisRegion r, Player player, boolean teleport) { - if (!getDimension().getRegions().contains(r.getLoadKey())) { - player.sendMessage(C.RED + r.getName() + " is not defined in the dimension!"); - return; - } - - find(Locator.region(r.getLoadKey()), player, teleport, "Region " + r.getName()); - } - - default void gotoPOI(String type, Player p, boolean teleport) { - find(Locator.poi(type), p, teleport, "POI " + type); - } - - default void gotoStructure(String key, Player player, boolean teleport) { - find(Locator.structure(key), player, teleport, "Structure " + key); - } - private static boolean containsObjectPlacement(KList placements, String normalizedObjectKey) { if (placements == null || placements.isEmpty() || normalizedObjectKey.isBlank()) { return false; diff --git a/core/src/main/java/art/arcane/iris/engine/framework/EnginePlayer.java b/core/src/main/java/art/arcane/iris/engine/framework/EnginePlayer.java index d6eb4f3b8..6b0f84f36 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/EnginePlayer.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/EnginePlayer.java @@ -19,6 +19,7 @@ package art.arcane.iris.engine.framework; import art.arcane.iris.core.IrisSettings; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.object.IrisBiome; import art.arcane.iris.engine.object.IrisEffect; import art.arcane.iris.engine.object.IrisRegion; @@ -87,8 +88,8 @@ public class EnginePlayer { if (ticksSinceLastSample() > 55 && current.distanceSquared(lastLocation) > 9 * 9) { lastLocation = current; lastSample = M.ms(); - biome = engine.getBiome(current); - region = engine.getRegion(current); + biome = EngineBukkitOps.getBiome(engine, current); + region = EngineBukkitOps.getRegion(engine, current); } return false; } catch (Throwable e) { diff --git a/core/src/main/java/art/arcane/iris/engine/framework/LootProvider.java b/core/src/main/java/art/arcane/iris/engine/framework/LootProvider.java index 5b8612934..f15394d98 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/LootProvider.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/LootProvider.java @@ -18,21 +18,10 @@ package art.arcane.iris.engine.framework; -import art.arcane.iris.engine.object.InventorySlotType; import art.arcane.iris.engine.object.IrisLootReference; import art.arcane.iris.engine.object.IrisLootTable; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.math.RNG; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.inventory.Inventory; public interface LootProvider { - void scramble(Inventory inventory, RNG rng); - void injectTables(KList list, IrisLootReference r, boolean fallback); - - KList getLootTables(RNG rng, Block b); - - void addItems(boolean debug, Inventory inv, RNG rng, KList tables, InventorySlotType slot, World world, int x, int y, int z, int mgf); } diff --git a/core/src/main/java/art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java b/core/src/main/java/art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java index 9fddc2d50..0f782a6e6 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java @@ -8,6 +8,7 @@ import art.arcane.iris.engine.data.cache.Cache; import art.arcane.iris.engine.framework.Engine; import art.arcane.iris.core.events.IrisLootEvent; import art.arcane.iris.engine.mantle.EngineMantle; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.object.IObjectPlacer; import art.arcane.iris.engine.object.InventorySlotType; import art.arcane.iris.engine.object.IrisLootTable; @@ -71,7 +72,7 @@ public class WorldObjectPlacer implements IObjectPlacer { if (slot != null) { RNG rx = new RNG(Cache.key(x, z)); - KList tables = engine.getLootTables(rx, block); + KList tables = EngineBukkitOps.getLootTables(engine, rx, block); try { Bukkit.getPluginManager().callEvent(new IrisLootEvent(engine, block, slot, tables)); @@ -83,7 +84,7 @@ public class WorldObjectPlacer implements IObjectPlacer { if (tables.isEmpty()) return; InventoryHolder m = (InventoryHolder) block.getState(); - engine.addItems(false, m.getInventory(), rx, tables, slot, world, x, y, z, 15); + EngineBukkitOps.addItems(engine, false, m.getInventory(), rx, tables, slot, world, x, y, z, 15); } catch (Throwable e) { IrisLogging.reportError(e); } diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisEffect.java b/core/src/main/java/art/arcane/iris/engine/object/IrisEffect.java index 3171940b6..279bdc430 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisEffect.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisEffect.java @@ -179,6 +179,12 @@ public class IrisEffect { }); } + private static final class BukkitFx { + private static void run(Player p, Runnable r) { + J.runEntity(p, r); + } + } + public void apply(Player p, Engine g) { if (!canTick()) { return; @@ -188,7 +194,7 @@ public class IrisEffect { return; } - J.runEntity(p, () -> { + BukkitFx.run(p, () -> { if (sound != null) { Location part = p.getLocation().clone().add(RNG.r.i(-soundDistance, soundDistance), RNG.r.i(-soundDistance, soundDistance), RNG.r.i(-soundDistance, soundDistance)); p.playSound(part, getSound(), (float) volume, (float) RNG.r.d(minPitch, maxPitch)); diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisEntity.java b/core/src/main/java/art/arcane/iris/engine/object/IrisEntity.java index 443d87d0c..e535f7b43 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisEntity.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisEntity.java @@ -24,6 +24,7 @@ import art.arcane.iris.core.link.Identifier; import art.arcane.iris.core.loader.IrisRegistrant; import art.arcane.iris.core.service.ExternalDataSVC; import art.arcane.iris.engine.framework.Engine; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.engine.object.annotations.*; import art.arcane.iris.platform.bukkit.BukkitPlatform; import art.arcane.iris.platform.bukkit.BukkitWorld; @@ -247,34 +248,7 @@ public class IrisEntity extends IrisRegistrant { Lootable l = (Lootable) e; if (getLoot().getTables().isNotEmpty()) { - Location finalAt = at; - l.setLootTable(new LootTable() { - @Override - public NamespacedKey getKey() { - return new NamespacedKey("iris", "loot-" + IrisEntity.this.hashCode()); - } - - @Override - public Collection populateLoot(Random random, LootContext context) { - KList items = new KList<>(); - - for (String fi : getLoot().getTables()) { - IrisLootTable i = gen.getData().getLootLoader().load(fi); - items.addAll(i.getLoot(gen.isStudio(), rng.nextParallelRNG(345911), InventorySlotType.STORAGE, finalAt.getWorld(), finalAt.getBlockX(), finalAt.getBlockY(), finalAt.getBlockZ())); - } - - return items; - } - - @Override - public void fillInventory(Inventory inventory, Random random, LootContext context) { - for (ItemStack i : populateLoot(random, context)) { - inventory.addItem(i); - } - - gen.scramble(inventory, rng); - } - }); + BukkitOps.bindLoot(this, gen, l, at, rng); } } @@ -326,7 +300,7 @@ public class IrisEntity extends IrisRegistrant { if (e instanceof Villager) { Villager villager = (Villager) e; villager.setRemoveWhenFarAway(false); - J.runEntity(villager, () -> villager.setPersistent(true), 1); + BukkitOps.persistVillager(villager); } if (e instanceof Mob) { @@ -458,6 +432,42 @@ public class IrisEntity extends IrisRegistrant { return specialType != null && !specialType.equals(""); } + private static final class BukkitOps { + private static void persistVillager(Villager villager) { + J.runEntity(villager, () -> villager.setPersistent(true), 1); + } + + private static void bindLoot(IrisEntity entity, Engine gen, Lootable l, Location finalAt, RNG rng) { + l.setLootTable(new LootTable() { + @Override + public NamespacedKey getKey() { + return new NamespacedKey("iris", "loot-" + entity.hashCode()); + } + + @Override + public Collection populateLoot(Random random, LootContext context) { + KList items = new KList<>(); + + for (String fi : entity.getLoot().getTables()) { + IrisLootTable i = gen.getData().getLootLoader().load(fi); + items.addAll(i.getLoot(gen.isStudio(), rng.nextParallelRNG(345911), InventorySlotType.STORAGE, finalAt.getWorld(), finalAt.getBlockX(), finalAt.getBlockY(), finalAt.getBlockZ())); + } + + return items; + } + + @Override + public void fillInventory(Inventory inventory, Random random, LootContext context) { + for (ItemStack i : populateLoot(random, context)) { + inventory.addItem(i); + } + + EngineBukkitOps.scramble(gen, inventory, rng); + } + }); + } + } + @Override public String getFolderName() { return "entities"; diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisObject.java b/core/src/main/java/art/arcane/iris/engine/object/IrisObject.java index 9c999f46f..41a0d05f0 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisObject.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisObject.java @@ -1059,8 +1059,8 @@ public class IrisObject extends IrisRegistrant { } if (placer.isPreventingDecay() && ((BlockData) data.nativeHandle()) instanceof Leaves && !((Leaves) ((BlockData) data.nativeHandle())).isPersistent()) { - Leaves leaves = (Leaves) ((BlockData) data.nativeHandle()).clone(); - leaves.setPersistent(true); + BlockData leaves = ((BlockData) data.nativeHandle()).clone(); + ((Leaves) leaves).setPersistent(true); data = BukkitBlockState.of(leaves); } @@ -1119,13 +1119,14 @@ public class IrisObject extends IrisRegistrant { } if (((BlockData) data.nativeHandle()) instanceof Waterlogged && shouldAutoWaterlogBlock(placer, config, yv, xx, yy, zz)) { - Waterlogged waterlogged = (Waterlogged) ((BlockData) data.nativeHandle()).clone(); - waterlogged.setWaterlogged(true); + BlockData waterlogged = ((BlockData) data.nativeHandle()).clone(); + ((Waterlogged) waterlogged).setWaterlogged(true); data = BukkitBlockState.of(waterlogged); } if (BukkitBlockResolution.isVineBlock((BlockData) data.nativeHandle())) { - MultipleFacing f = (MultipleFacing) ((BlockData) data.nativeHandle()).clone(); + BlockData fData = ((BlockData) data.nativeHandle()).clone(); + MultipleFacing f = (MultipleFacing) fData; boolean facesChanged = false; for (BlockFace face : f.getAllowedFaces()) { PlatformBlockState facingState = placer.get(xx + face.getModX(), yy + face.getModY(), zz + face.getModZ()); @@ -1136,7 +1137,7 @@ public class IrisObject extends IrisRegistrant { } } if (facesChanged) { - data = BukkitBlockState.of(f); + data = BukkitBlockState.of(fData); } } @@ -1344,8 +1345,8 @@ public class IrisObject extends IrisRegistrant { int highest = placer.getHighest(xx, zz, getLoader(), true); if (((BlockData) d.nativeHandle()) instanceof Waterlogged && shouldAutoWaterlogBlock(placer, config, yv, xx, highest, zz)) { - Waterlogged waterlogged = (Waterlogged) ((BlockData) d.nativeHandle()).clone(); - waterlogged.setWaterlogged(true); + BlockData waterlogged = ((BlockData) d.nativeHandle()).clone(); + ((Waterlogged) waterlogged).setWaterlogged(true); d = BukkitBlockState.of(waterlogged); } @@ -1385,7 +1386,8 @@ public class IrisObject extends IrisRegistrant { } if (BukkitBlockResolution.isVineBlock((BlockData) d.nativeHandle())) { - MultipleFacing f = (MultipleFacing) ((BlockData) d.nativeHandle()).clone(); + BlockData fData = ((BlockData) d.nativeHandle()).clone(); + MultipleFacing f = (MultipleFacing) fData; boolean facesChanged = false; for (BlockFace face : f.getAllowedFaces()) { PlatformBlockState facingState = placer.get(xx + face.getModX(), j + face.getModY(), zz + face.getModZ()); @@ -1396,7 +1398,7 @@ public class IrisObject extends IrisRegistrant { } } if (facesChanged) { - d = BukkitBlockState.of(f); + d = BukkitBlockState.of(fData); } } placer.set(xx, j, zz, d); diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisObjectRotation.java b/core/src/main/java/art/arcane/iris/engine/object/IrisObjectRotation.java index 7ca9d2f31..2df30ce79 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisObjectRotation.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisObjectRotation.java @@ -47,7 +47,9 @@ import java.util.Map; @Desc("Configures rotation for iris") @Data public class IrisObjectRotation { - private static final List WALL_FACES = List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST); + private static final class Faces { + private static final List WALL_FACES = List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST); + } @Desc("If this rotator is enabled or not") private boolean enabled = true; @@ -332,17 +334,17 @@ public class IrisObjectRotation { } else if (d instanceof Wall wall) { KMap faces = new KMap<>(); - for (BlockFace i : WALL_FACES) { + for (BlockFace i : Faces.WALL_FACES) { Wall.Height h = wall.getHeight(i); IrisBlockVector bv = new IrisBlockVector(i.getModX(), i.getModY(), i.getModZ()); bv = rotate(bv.clone(), spinx, spiny, spinz); BlockFace r = getFace(bv); - if (WALL_FACES.contains(r)) { + if (Faces.WALL_FACES.contains(r)) { faces.put(r, h); } } - for (BlockFace i : WALL_FACES) { + for (BlockFace i : Faces.WALL_FACES) { wall.setHeight(i, faces.getOrDefault(i, Wall.Height.NONE)); } } else if (d instanceof RedstoneWire wire) { diff --git a/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java b/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java index a9ab67af6..0ccae46e3 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java +++ b/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java @@ -36,18 +36,12 @@ import java.util.stream.StreamSupport; @ToString @EqualsAndHashCode(callSuper = false) public class LegacyTileData extends TileData { - private static final Map>> legacy = Map.of( - 0, new Pair<>(SignHandler::fromBukkit, SignHandler::new), - 1, new Pair<>(SpawnerHandler::fromBukkit, SpawnerHandler::new), - 2, new Pair<>(BannerHandler::fromBukkit, BannerHandler::new), - 3, new Pair<>(LootableHandler::fromBukkit, LootableHandler::new)); - private static final AtomicCache> SIGNS = new AtomicCache<>(); private final int id; private final Handler handler; public LegacyTileData(DataInputStream in) throws IOException { id = in.readShort(); - var factory = legacy.get(id); + Pair> factory = Handlers.LEGACY.get(id); if (factory == null) throw new IOException("Unknown tile type: " + id); handler = factory.getB().apply(in); @@ -60,16 +54,24 @@ public class LegacyTileData extends TileData { @Nullable public static LegacyTileData fromBukkit(@NonNull BlockState tileState) { - var type = tileState.getType(); - for (var id : legacy.keySet()) { - var factory = legacy.get(id); - var handler = factory.getA().apply(tileState, type); + Material type = tileState.getType(); + for (Integer id : Handlers.LEGACY.keySet()) { + Pair> factory = Handlers.LEGACY.get(id); + Handler handler = factory.getA().apply(tileState, type); if (handler != null) return new LegacyTileData(id, handler); } return null; } + private static final class Handlers { + private static final Map>> LEGACY = Map.of( + 0, new Pair<>(SignHandler::fromBukkit, SignHandler::new), + 1, new Pair<>(SpawnerHandler::fromBukkit, SpawnerHandler::new), + 2, new Pair<>(BannerHandler::fromBukkit, BannerHandler::new), + 3, new Pair<>(LootableHandler::fromBukkit, LootableHandler::new)); + } + @Override public @NonNull KMap getProperties() { return new KMap<>(); @@ -411,34 +413,42 @@ public class LegacyTileData extends TileData { } private static Tag signsTag() { - return SIGNS.aquire(() -> { - var signs = Bukkit.getTag("blocks", NamespacedKey.minecraft("all_signs"), Material.class); - if (signs != null) - return signs; - return new Tag<>() { - @Override - public boolean isTagged(@NotNull Material item) { - NamespacedKey key = KeyedType.getKey(item); - if (key != null) { - return key.getKey().endsWith("_sign"); + return SignTags.get(); + } + + private static final class SignTags { + private static final AtomicCache> SIGNS = new AtomicCache<>(); + + private static Tag get() { + return SIGNS.aquire(() -> { + Tag signs = Bukkit.getTag("blocks", NamespacedKey.minecraft("all_signs"), Material.class); + if (signs != null) + return signs; + return new Tag<>() { + @Override + public boolean isTagged(@NotNull Material item) { + NamespacedKey key = KeyedType.getKey(item); + if (key != null) { + return key.getKey().endsWith("_sign"); + } + return item.name().toLowerCase(Locale.ROOT).endsWith("_sign"); } - return item.name().toLowerCase(Locale.ROOT).endsWith("_sign"); - } - @NotNull - @Override - public Set getValues() { - return StreamSupport.stream(Registry.MATERIAL.spliterator(), false) - .filter(this::isTagged) - .collect(Collectors.toUnmodifiableSet()); - } + @NotNull + @Override + public Set getValues() { + return StreamSupport.stream(Registry.MATERIAL.spliterator(), false) + .filter(this::isTagged) + .collect(Collectors.toUnmodifiableSet()); + } - @NotNull - @Override - public NamespacedKey getKey() { - return NamespacedKey.minecraft("all_signs"); - } - }; - }); + @NotNull + @Override + public NamespacedKey getKey() { + return NamespacedKey.minecraft("all_signs"); + } + }; + }); + } } } diff --git a/core/src/main/java/art/arcane/iris/engine/platform/EngineBukkitOps.java b/core/src/main/java/art/arcane/iris/engine/platform/EngineBukkitOps.java new file mode 100644 index 000000000..4045d8d5a --- /dev/null +++ b/core/src/main/java/art/arcane/iris/engine/platform/EngineBukkitOps.java @@ -0,0 +1,539 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package art.arcane.iris.engine.platform; + +import art.arcane.iris.core.events.IrisLootEvent; +import art.arcane.iris.core.link.Identifier; +import art.arcane.iris.core.service.ExternalDataSVC; +import art.arcane.iris.core.tools.IrisToolbelt; +import art.arcane.iris.engine.data.cache.Cache; +import art.arcane.iris.engine.framework.Engine; +import art.arcane.iris.engine.framework.Locator; +import art.arcane.iris.engine.framework.PlacedObject; +import art.arcane.iris.engine.framework.WrongEngineBroException; +import art.arcane.iris.engine.object.InventorySlotType; +import art.arcane.iris.engine.object.IrisBiome; +import art.arcane.iris.engine.object.IrisLootTable; +import art.arcane.iris.engine.object.IrisRegion; +import art.arcane.iris.engine.object.TileData; +import art.arcane.iris.platform.bukkit.BukkitBlockResolution; +import art.arcane.iris.platform.bukkit.BukkitBlockState; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.iris.spi.IrisServices; +import art.arcane.iris.util.common.format.C; +import art.arcane.iris.util.common.plugin.VolmitSender; +import art.arcane.iris.util.common.reflect.KeyedType; +import art.arcane.iris.util.common.scheduling.J; +import art.arcane.iris.util.common.scheduling.jobs.SingleJob; +import art.arcane.iris.util.project.matter.TileWrapper; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.mantle.flag.MantleFlag; +import art.arcane.volmlib.util.mantle.runtime.Mantle; +import art.arcane.volmlib.util.mantle.runtime.MantleChunk; +import art.arcane.volmlib.util.math.BlockPosition; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.math.Position2; +import art.arcane.volmlib.util.math.RNG; +import art.arcane.volmlib.util.matter.Matter; +import art.arcane.volmlib.util.matter.MatterCavern; +import art.arcane.volmlib.util.matter.MatterUpdate; +import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; +import io.papermc.lib.PaperLib; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +import java.lang.reflect.Method; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + +public final class EngineBukkitOps { + private EngineBukkitOps() { + } + + public static void updateChunk(Engine engine, Chunk c) { + for (int x = -1; x <= 1; x++) { + for (int z = -1; z <= 1; z++) { + if (c.getWorld().isChunkLoaded(c.getX() + x, c.getZ() + z)) + continue; + String msg = "Chunk %s, %s [%s, %s] is not loaded".formatted(c.getX() + x, c.getZ() + z, x, z); + IrisLogging.debug(msg); + return; + } + } + Mantle mantle = engine.getMantle().getMantle(); + if (!mantle.isLoaded(c)) { + String msg = "Mantle Chunk " + c.getX() + "," + c.getZ() + " is not loaded"; + IrisLogging.debug(msg); + return; + } + + if (!J.isFolia() && !J.isPrimaryThread()) { + CompletableFuture scheduled = J.sfut(() -> updateChunk(engine, c)); + if (scheduled != null) { + try { + scheduled.join(); + } catch (Throwable e) { + IrisLogging.reportError(e); + } + } + return; + } + + MantleChunk chunk = mantle.getChunk(c).use(); + try { + Runnable tileTask = () -> { + chunk.iterate(TileWrapper.class, (x, y, z, v) -> { + Block block = c.getBlock(x & 15, y + engine.getWorld().minHeight(), z & 15); + if (!TileData.setTileState(block, v.getData())) { + NamespacedKey blockTypeKey = KeyedType.getKey(block.getType()); + NamespacedKey tileTypeKey = KeyedType.getKey(v.getData().getMaterial()); + String blockType = blockTypeKey == null ? block.getType().name() : blockTypeKey.toString(); + String tileType = tileTypeKey == null ? v.getData().getMaterial().name() : tileTypeKey.toString(); + IrisLogging.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), blockType, tileType); + } + }); + }; + + Runnable customTask = () -> { + chunk.iterate(Identifier.class, (x, y, z, v) -> { + IrisServices.get(ExternalDataSVC.class).processUpdate(engine, c.getBlock(x & 15, y + engine.getWorld().minHeight(), z & 15), v); + }); + }; + + Runnable updateTask = () -> { + PrecisionStopwatch p = PrecisionStopwatch.start(); + int[][] grid = new int[16][16]; + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + grid[x][z] = Integer.MIN_VALUE; + } + } + + RNG rng = new RNG(Cache.key(c.getX(), c.getZ())); + chunk.iterate(MatterCavern.class, (x, yf, z, v) -> { + int y = yf + engine.getWorld().minHeight(); + x &= 15; + z &= 15; + Block block = c.getBlock(x, y, z); + if (!BukkitBlockResolution.isFluid(block.getBlockData())) { + return; + } + boolean u = BukkitBlockResolution.isAir(block.getRelative(BlockFace.DOWN).getBlockData()) + || BukkitBlockResolution.isAir(block.getRelative(BlockFace.WEST).getBlockData()) + || BukkitBlockResolution.isAir(block.getRelative(BlockFace.EAST).getBlockData()) + || BukkitBlockResolution.isAir(block.getRelative(BlockFace.SOUTH).getBlockData()) + || BukkitBlockResolution.isAir(block.getRelative(BlockFace.NORTH).getBlockData()); + + if (u) grid[x][z] = Math.max(grid[x][z], y); + }); + + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + if (grid[x][z] == Integer.MIN_VALUE) { + continue; + } + update(engine, x, grid[x][z], z, c, chunk, rng); + } + } + + chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> { + int y = yf + engine.getWorld().minHeight(); + if (v != null && v.isUpdate()) { + update(engine, x, y, z, c, chunk, rng); + } + }); + chunk.deleteSlices(MatterUpdate.class); + engine.getMetrics().getUpdates().put(p.getMilliseconds()); + }; + + if (shouldRunChunkUpdateInline(c)) { + chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> { + chunk.raiseFlagUnchecked(MantleFlag.TILE, tileTask); + chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, customTask); + chunk.raiseFlagUnchecked(MantleFlag.UPDATE, updateTask); + }); + return; + } + + Semaphore semaphore = new Semaphore(1024); + chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> { + chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, c, tileTask, 0)); + chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, c, customTask, 0)); + chunk.raiseFlagUnchecked(MantleFlag.UPDATE, run(semaphore, c, updateTask, RNG.r.i(1, 20))); + }); + + try { + semaphore.acquire(1024); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + IrisLogging.reportError(ex); + } + } finally { + chunk.release(); + } + } + + private static boolean shouldRunChunkUpdateInline(Chunk chunk) { + if (chunk == null) { + return false; + } + + if (!J.isFolia()) { + return true; + } + + return J.isOwnedByCurrentRegion(chunk.getWorld(), chunk.getX(), chunk.getZ()); + } + + private static Runnable run(Semaphore semaphore, Chunk contextChunk, Runnable runnable, int delay) { + return () -> { + try { + semaphore.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + int effectiveDelay = J.isFolia() ? 0 : delay; + boolean scheduled = J.runRegion(contextChunk.getWorld(), contextChunk.getX(), contextChunk.getZ(), () -> { + try { + runnable.run(); + } finally { + semaphore.release(); + } + }, effectiveDelay); + + if (!scheduled) { + try { + if (J.isPrimaryThread()) { + runnable.run(); + } + } finally { + semaphore.release(); + } + } + }; + } + + public static void update(Engine engine, int x, int y, int z, Chunk c, MantleChunk mc, RNG rf) { + Block block = c.getBlock(x, y, z); + BlockData data = block.getBlockData(); + engine.blockUpdatedMetric(); + if (BukkitBlockResolution.isStorage(data)) { + RNG rx = rf.nextParallelRNG(BlockPosition.toLong(x, y, z)); + InventorySlotType slot = null; + + if (BukkitBlockResolution.isStorageChest(data)) { + slot = InventorySlotType.STORAGE; + } + + if (slot != null) { + KList tables = getLootTables(engine, rx, block, mc); + + try { + Bukkit.getPluginManager().callEvent(new IrisLootEvent(engine, block, slot, tables)); + if (tables.isEmpty()) return; + InventoryHolder m = (InventoryHolder) block.getState(); + addItems(engine, false, m.getInventory(), rx, tables, slot, c.getWorld(), x, y, z, 15); + + } catch (Throwable e) { + IrisLogging.reportError(e); + } + } + } else { + block.setType(Material.AIR, false); + block.setBlockData(data, true); + } + } + + public static void scramble(Engine engine, Inventory inventory, RNG rng) { + org.bukkit.inventory.ItemStack[] items = inventory.getContents(); + org.bukkit.inventory.ItemStack[] nitems = new org.bukkit.inventory.ItemStack[inventory.getSize()]; + System.arraycopy(items, 0, nitems, 0, items.length); + boolean packedFull = false; + + splitting: + for (int i = 0; i < nitems.length; i++) { + ItemStack is = nitems[i]; + + if (is != null && is.getAmount() > 1 && !packedFull) { + for (int j = 0; j < nitems.length; j++) { + if (nitems[j] == null) { + int take = rng.nextInt(is.getAmount()); + take = take == 0 ? 1 : take; + is.setAmount(is.getAmount() - take); + nitems[j] = is.clone(); + nitems[j].setAmount(take); + continue splitting; + } + } + + packedFull = true; + } + } + + for (int i = nitems.length; i > 1; i--) { + int j = rng.nextInt(i); + ItemStack tmp = nitems[i - 1]; + nitems[i - 1] = nitems[j]; + nitems[j] = tmp; + } + + inventory.setContents(nitems); + } + + public static KList getLootTables(Engine engine, RNG rng, Block b) { + MantleChunk mc = engine.getMantle().getMantle().getChunk(b.getChunk()).use(); + try { + return getLootTables(engine, rng, b, mc); + } finally { + mc.release(); + } + } + + public static KList getLootTables(Engine engine, RNG rng, Block b, MantleChunk mc) { + int rx = b.getX(); + int rz = b.getZ(); + int ry = b.getY() - engine.getWorld().minHeight(); + double he = engine.getComplex().getHeightStream().get(rx, rz); + KList tables = new KList<>(); + + PlacedObject po = engine.getObjectPlacement(rx, ry, rz, mc); + if (po != null && po.getPlacement() != null) { + if (BukkitBlockResolution.isStorageChest(b.getBlockData())) { + IrisLootTable table = po.getPlacement().getTable(BukkitBlockState.of(b.getBlockData()), engine.getData()); + if (table != null) { + tables.add(table); + if (po.getPlacement().isOverrideGlobalLoot()) { + return new KList<>(table); + } + } + } + } + + IrisRegion region = engine.getComplex().getRegionStream().get(rx, rz); + IrisBiome biomeSurface = engine.getComplex().getTrueBiomeStream().get(rx, rz); + IrisBiome biomeUnder = ry < he ? engine.getCaveBiome(rx, ry, rz) : biomeSurface; + + double multiplier = 1D * engine.getDimension().getLoot().getMultiplier() * region.getLoot().getMultiplier() * biomeSurface.getLoot().getMultiplier() * biomeUnder.getLoot().getMultiplier(); + boolean fallback = tables.isEmpty(); + engine.injectTables(tables, engine.getDimension().getLoot(), fallback); + engine.injectTables(tables, region.getLoot(), fallback); + engine.injectTables(tables, biomeSurface.getLoot(), fallback); + engine.injectTables(tables, biomeUnder.getLoot(), fallback); + + if (tables.isNotEmpty()) { + int target = (int) Math.round(tables.size() * multiplier); + + while (tables.size() < target && tables.isNotEmpty()) { + tables.add(tables.get(rng.i(tables.size() - 1))); + } + + while (tables.size() > target && tables.isNotEmpty()) { + tables.remove(rng.i(tables.size() - 1)); + } + } + + return tables; + } + + public static void addItems(Engine engine, boolean debug, Inventory inv, RNG rng, KList tables, InventorySlotType slot, World world, int x, int y, int z, int mgf) { + KList items = new KList<>(); + + for (IrisLootTable i : tables) { + if (i == null) + continue; + items.addAll(i.getLoot(debug, rng, slot, world, x, y, z)); + } + if (IrisLootEvent.callLootEvent(items, inv, world, x, y, z)) + return; + + if (PaperLib.isPaper() && engine.getWorld().hasRealWorld()) { + PaperLib.getChunkAtAsync(engine.getWorld().realWorld(), x >> 4, z >> 4).thenAccept((c) -> { + Runnable r = () -> { + for (ItemStack i : items) { + inv.addItem(i); + } + + scramble(engine, inv, rng); + }; + + if (Bukkit.isPrimaryThread()) { + r.run(); + } else { + J.s(r); + } + }); + } else { + for (ItemStack i : items) { + inv.addItem(i); + } + + scramble(engine, inv, rng); + } + } + + public static IrisBiome getBiome(Engine engine, Location l) { + return engine.getBiome(l.getBlockX(), l.getBlockY() - engine.getWorld().minHeight(), l.getBlockZ()); + } + + public static IrisRegion getRegion(Engine engine, Location l) { + return engine.getRegion(l.getBlockX(), l.getBlockZ()); + } + + public static IrisBiome getBiomeOrMantle(Engine engine, Location l) { + return engine.getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ()); + } + + public static IrisBiome getSurfaceBiome(Engine engine, Chunk c) { + return engine.getSurfaceBiome((c.getX() << 4) + 8, (c.getZ() << 4) + 8); + } + + public static IrisRegion getRegion(Engine engine, Chunk c) { + return engine.getRegion((c.getX() << 4) + 8, (c.getZ() << 4) + 8); + } + + public static void gotoBiome(Engine engine, IrisBiome biome, Player player, boolean teleport) { + find(Locator.surfaceBiome(biome.getLoadKey()), player, teleport, "Biome " + biome.getName()); + } + + public static void gotoObject(Engine engine, String s, Player player, boolean teleport) { + find(Locator.object(s), player, teleport, "Object " + s); + } + + public static void gotoRegion(Engine engine, IrisRegion r, Player player, boolean teleport) { + if (!engine.getDimension().getRegions().contains(r.getLoadKey())) { + player.sendMessage(C.RED + r.getName() + " is not defined in the dimension!"); + return; + } + + find(Locator.region(r.getLoadKey()), player, teleport, "Region " + r.getName()); + } + + public static void gotoPOI(Engine engine, String type, Player p, boolean teleport) { + find(Locator.poi(type), p, teleport, "POI " + type); + } + + public static void gotoStructure(Engine engine, String key, Player player, boolean teleport) { + find(Locator.structure(key), player, teleport, "Structure " + key); + } + + private static void find(Locator locator, Player player, boolean teleport, String message) { + find(locator, player, 120_000, location -> { + if (location == null) { + player.sendMessage(C.RED + "Could not find " + message + " within search range."); + return; + } + if (teleport) { + J.runEntity(player, () -> teleportAsyncSafely(player, location)); + player.sendMessage(C.GREEN + "Teleporting to " + message + "..."); + } else { + player.sendMessage(C.GREEN + message + " at: " + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ()); + } + }); + } + + private static void find(Locator locator, Player player, long timeout, Consumer consumer) { + AtomicLong checks = new AtomicLong(); + long ms = M.ms(); + new SingleJob("Searching", () -> { + try { + World world = player.getWorld(); + Engine engine = IrisToolbelt.access(world).getEngine(); + Position2 at = locator.find(engine, new Position2(player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4), timeout, checks::set).get(); + + if (at != null) { + int bx = (at.getX() << 4) + 8; + int bz = (at.getZ() << 4) + 8; + consumer.accept(new Location(world, bx, + world.getHighestBlockYAt(bx, bz) + 2, + bz)); + } else { + consumer.accept(null); + } + } catch (WrongEngineBroException | InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + }) { + @Override + public String getName() { + return "Searched " + Form.f(checks.get()) + " Chunks"; + } + + @Override + public int getTotalWork() { + return (int) timeout; + } + + @Override + public int getWorkCompleted() { + return (int) Math.min(M.ms() - ms, timeout - 1); + } + }.execute(new VolmitSender(player)); + } + + private static void teleportAsyncSafely(Player player, Location location) { + if (player == null || location == null) { + return; + } + + if (invokeNativeTeleportAsync(player, location)) { + return; + } + + try { + CompletableFuture teleportFuture = PaperLib.teleportAsync(player, location); + if (teleportFuture != null) { + teleportFuture.exceptionally(throwable -> { + IrisLogging.reportError(throwable); + return false; + }); + } + } catch (Throwable throwable) { + IrisLogging.reportError(throwable); + } + } + + private static boolean invokeNativeTeleportAsync(Player player, Location location) { + try { + Method teleportAsyncMethod = player.getClass().getMethod("teleportAsync", Location.class); + teleportAsyncMethod.invoke(player, location); + return true; + } catch (NoSuchMethodException ignored) { + return false; + } catch (Throwable throwable) { + IrisLogging.reportError(throwable); + return false; + } + } +} diff --git a/core/src/main/java/art/arcane/iris/util/common/director/context/BiomeContextHandler.java b/core/src/main/java/art/arcane/iris/util/common/director/context/BiomeContextHandler.java index 0ea7024f6..d5d90fd32 100644 --- a/core/src/main/java/art/arcane/iris/util/common/director/context/BiomeContextHandler.java +++ b/core/src/main/java/art/arcane/iris/util/common/director/context/BiomeContextHandler.java @@ -20,6 +20,7 @@ package art.arcane.iris.util.common.director.context; import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.object.IrisBiome; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.util.common.director.DirectorContextHandler; import art.arcane.iris.util.common.plugin.VolmitSender; @@ -32,7 +33,7 @@ public class BiomeContextHandler implements DirectorContextHandler { if (sender.isPlayer() && IrisToolbelt.isIrisWorld(sender.player().getWorld()) && IrisToolbelt.access(sender.player().getWorld()).getEngine() != null) { - return IrisToolbelt.access(sender.player().getWorld()).getEngine().getBiomeOrMantle(sender.player().getLocation()); + return EngineBukkitOps.getBiomeOrMantle(IrisToolbelt.access(sender.player().getWorld()).getEngine(), sender.player().getLocation()); } return null; diff --git a/core/src/main/java/art/arcane/iris/util/common/director/context/GeneratorContextHandler.java b/core/src/main/java/art/arcane/iris/util/common/director/context/GeneratorContextHandler.java index c3311a769..71303993c 100644 --- a/core/src/main/java/art/arcane/iris/util/common/director/context/GeneratorContextHandler.java +++ b/core/src/main/java/art/arcane/iris/util/common/director/context/GeneratorContextHandler.java @@ -21,6 +21,7 @@ package art.arcane.iris.util.common.director.context; import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.framework.Engine; import art.arcane.iris.engine.object.IrisGenerator; +import art.arcane.iris.engine.platform.EngineBukkitOps; import art.arcane.iris.util.common.director.DirectorContextHandler; import art.arcane.iris.util.common.plugin.VolmitSender; @@ -36,7 +37,7 @@ public class GeneratorContextHandler implements DirectorContextHandler if (sender.isPlayer() && IrisToolbelt.isIrisWorld(sender.player().getWorld()) && IrisToolbelt.access(sender.player().getWorld()).getEngine() != null) { - return IrisToolbelt.access(sender.player().getWorld()).getEngine().getRegion(sender.player().getLocation()); + return EngineBukkitOps.getRegion(IrisToolbelt.access(sender.player().getWorld()).getEngine(), sender.player().getLocation()); } return null; diff --git a/probe/src/main/java/art/arcane/iris/probe/ClassloadProbe.java b/probe/src/main/java/art/arcane/iris/probe/ClassloadProbe.java index 600d42302..9ff754466 100644 --- a/probe/src/main/java/art/arcane/iris/probe/ClassloadProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/ClassloadProbe.java @@ -47,6 +47,11 @@ public final class ClassloadProbe { "art.arcane.iris.util.common.misc.Bindings", "art.arcane.iris.util.common.misc.SlimJar", "art.arcane.iris.util.common.misc.ServerProperties", + "art.arcane.iris.util.common.data.IrisCustomData", + "art.arcane.iris.engine.IrisWorldManager", + "art.arcane.iris.engine.framework.EngineAssignedWorldManager", + "art.arcane.iris.engine.object.StudioMode", + "art.arcane.iris.engine.framework.placer.WorldObjectPlacer", }; public static void main(String[] args) throws IOException { @@ -87,6 +92,9 @@ public final class ClassloadProbe { Class.forName(name, true, ClassloadProbe.class.getClassLoader()); loaded++; } catch (Throwable failure) { + if (System.getenv("PROBE_TRACE") != null && name.contains(System.getenv("PROBE_TRACE"))) { + failure.printStackTrace(System.out); + } String cause = rootCause(failure); if (critical) { criticalFailures.put(name, cause);