mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-07-13 02:15:46 +00:00
f
This commit is contained in:
@@ -13,7 +13,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
name: Verify (core:check + spi:build)
|
||||
name: Verify (core, plugin, spi, probes)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -25,8 +25,8 @@ jobs:
|
||||
java-version: '25'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
- name: Run core checks and build spi
|
||||
run: ./gradlew :core:check :spi:build :probe:deserializationProbe --console=plain --stacktrace
|
||||
- name: Run verification gates
|
||||
run: ./gradlew :core:check :adapters:bukkit:plugin:test :spi:build :probe:deserializationProbe -PuseLocalVolmLib=false --console=plain --stacktrace
|
||||
|
||||
build-artifacts:
|
||||
name: Build ${{ matrix.platform }}
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
- name: Build ${{ matrix.platform }} artifact
|
||||
run: ./gradlew ${{ matrix.task }} --console=plain --stacktrace
|
||||
run: ./gradlew ${{ matrix.task }} -PuseLocalVolmLib=false --console=plain --stacktrace
|
||||
- name: Upload ${{ matrix.platform }} jar
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
release-bundle:
|
||||
name: Release bundle (buildAll)
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: verify
|
||||
needs: [verify, build-artifacts]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
- name: Build all platforms
|
||||
run: ./gradlew buildAll --console=plain --stacktrace
|
||||
run: ./gradlew buildAll --no-parallel -PuseLocalVolmLib=false --console=plain --stacktrace
|
||||
- name: Upload release bundle
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -24,3 +24,5 @@ adapters/*/run/
|
||||
.codegraph/
|
||||
|
||||
plans/
|
||||
|
||||
docs/
|
||||
|
||||
@@ -2,13 +2,12 @@ String apiVersion = providers.gradleProperty('minecraftVersion').get()
|
||||
def mainClass = 'art.arcane.iris.Iris'
|
||||
|
||||
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:cdf7e8bb880199734dacd386a6616bda799fcde1')
|
||||
.get()
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(':core'))
|
||||
compileOnly(volmLibCoordinate) {
|
||||
changing = true
|
||||
transitive = false
|
||||
}
|
||||
compileOnly(libs.spigot)
|
||||
@@ -18,7 +17,6 @@ dependencies {
|
||||
testImplementation(libs.sentry)
|
||||
testImplementation(project(':core'))
|
||||
testImplementation(volmLibCoordinate) {
|
||||
changing = true
|
||||
transitive = false
|
||||
}
|
||||
compileOnly(libs.placeholderApi)
|
||||
|
||||
+28
-11
@@ -34,6 +34,7 @@ import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
|
||||
import art.arcane.iris.util.project.context.IrisContext;
|
||||
import art.arcane.iris.util.project.matter.IrisMatterContext;
|
||||
import art.arcane.iris.util.common.director.DirectorExecutor;
|
||||
import art.arcane.volmlib.util.director.DirectorOrigin;
|
||||
import art.arcane.volmlib.util.director.annotations.Director;
|
||||
@@ -74,8 +75,14 @@ public class CommandDeveloper implements DirectorExecutor {
|
||||
@Director(description = "Send a test exception to sentry")
|
||||
public void Sentry() {
|
||||
Engine engine = engine();
|
||||
if (engine != null) IrisContext.getOr(engine);
|
||||
Iris.reportError(new Exception("This is a test"));
|
||||
Exception testException = new Exception("This is a test");
|
||||
if (engine == null) {
|
||||
Iris.reportError(testException);
|
||||
return;
|
||||
}
|
||||
try (IrisContext.Scope scope = IrisContext.open(engine, engine.getGenerationSessionId(), null)) {
|
||||
Iris.reportError(testException);
|
||||
}
|
||||
}
|
||||
|
||||
@Director(description = "Hash generated block output of a fixed area for determinism/identity testing", origin = DirectorOrigin.BOTH)
|
||||
@@ -221,18 +228,28 @@ public class CommandDeveloper implements DirectorExecutor {
|
||||
@Param(name = "name", description = "The dump file id under plugins/Iris/dump (pv.<id>.*)", defaultValue = "21474836474")
|
||||
String name
|
||||
) throws Throwable {
|
||||
var base = Iris.instance.getDataFile("dump", "pv." + name + ".ttp.lz4b.bin");
|
||||
var section = Iris.instance.getDataFile("dump", "pv." + name + ".section.bin");
|
||||
Engine activeEngine = engine();
|
||||
if (activeEngine == null) {
|
||||
sender().sendMessage(C.RED + "Target an Iris world before reading a mantle dump.");
|
||||
return;
|
||||
}
|
||||
File base = Iris.instance.getDataFile("dump", "pv." + name + ".ttp.lz4b.bin");
|
||||
File section = Iris.instance.getDataFile("dump", "pv." + name + ".section.bin");
|
||||
|
||||
if (plate) {
|
||||
try (var in = CountingDataInputStream.wrap(new BufferedInputStream(new FileInputStream(base)))) {
|
||||
TectonicPlate.read(1088, in, true, IrisEngineMantle.createRuntimeDataAdapter(), IrisEngineMantle.createRuntimeHooks());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
try (IrisMatterContext.Scope scope = IrisMatterContext.open(activeEngine.getData())) {
|
||||
if (plate) {
|
||||
try (CountingDataInputStream in = CountingDataInputStream.wrap(new BufferedInputStream(new FileInputStream(base)))) {
|
||||
TectonicPlate.read(1088, in, true, IrisEngineMantle.createRuntimeDataAdapter(activeEngine.getData()), IrisEngineMantle.createRuntimeHooks());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
Matter.read(section);
|
||||
}
|
||||
} else Matter.read(section);
|
||||
if (!TectonicPlate.hasError())
|
||||
}
|
||||
if (!TectonicPlate.hasError()) {
|
||||
Iris.info("Read " + (plate ? base : section).length() + " bytes from " + (plate ? base : section).getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Director(description = "Test")
|
||||
|
||||
+19
-4
@@ -42,21 +42,36 @@ public class CommandPregen implements DirectorExecutor {
|
||||
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
|
||||
Vector center,
|
||||
@Param(description = "Open the Iris pregen gui", defaultValue = "true")
|
||||
boolean gui
|
||||
boolean gui,
|
||||
@Param(name = "serial", description = "Generate only one chunk at a time", defaultValue = "false")
|
||||
boolean serial
|
||||
) {
|
||||
if (radius <= 0) {
|
||||
sender().sendMessage(C.RED + "Pregen radius must be greater than zero blocks.");
|
||||
return;
|
||||
}
|
||||
if (serial && !IrisToolbelt.supportsStrictSerialPregeneration()) {
|
||||
sender().sendMessage(C.RED + "Strict serial pregeneration requires Paper or a Paper-compatible server.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (sender().isPlayer() && access() == null) {
|
||||
sender().sendMessage(C.RED + "The engine access for this world is null!");
|
||||
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
|
||||
}
|
||||
radius = Math.max(radius, 1024);
|
||||
IrisToolbelt.pregenerate(PregenTask
|
||||
PregenTask task = PregenTask
|
||||
.builder()
|
||||
.center(new Position2(center.getBlockX(), center.getBlockZ()))
|
||||
.gui(gui)
|
||||
.radiusX(radius)
|
||||
.radiusZ(radius)
|
||||
.build(), world);
|
||||
.build();
|
||||
if (serial) {
|
||||
IrisToolbelt.pregenerateSerial(task, world);
|
||||
} else {
|
||||
IrisToolbelt.pregenerate(task, world);
|
||||
}
|
||||
String msg = C.GREEN + "Pregen started in " + C.GOLD + world.getName() + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
|
||||
sender().sendMessage(msg);
|
||||
Iris.info(msg);
|
||||
|
||||
@@ -31,6 +31,7 @@ file('../../gradle.properties').withInputStream { InputStream stream -> rootProp
|
||||
String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.0.0-26.2'))
|
||||
String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.2'))
|
||||
String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse(rootProperties.getProperty('fabricLoaderVersion', '0.19.3'))
|
||||
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:cdf7e8bb880199734dacd386a6616bda799fcde1'))
|
||||
Closure<String> irisArtifactName = { String platform, String targetVersion ->
|
||||
return "Iris v${project.version} [${platform}] ${targetVersion}.jar"
|
||||
}
|
||||
@@ -124,7 +125,7 @@ dependencies {
|
||||
List<Object> shared = [
|
||||
"art.arcane:core:${irisVersion}".toString(),
|
||||
"art.arcane:spi:${irisVersion}".toString(),
|
||||
'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT',
|
||||
volmLibCoordinate,
|
||||
libs.paralithic,
|
||||
libs.lru,
|
||||
libs.kotlin.stdlib,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
{ "file": "META-INF/jars/fabric-command-api-v2.jar" },
|
||||
{ "file": "META-INF/jars/fabric-events-interaction-v0.jar" },
|
||||
{ "file": "META-INF/jars/fabric-rendering-v1.jar" },
|
||||
{ "file": "META-INF/jars/fabric-transitive-access-wideners-v1.jar" },
|
||||
{ "file": "META-INF/jars/fabric-key-mapping-api-v1.jar" }
|
||||
],
|
||||
"depends": {
|
||||
|
||||
@@ -31,6 +31,7 @@ file('../../gradle.properties').withInputStream { InputStream stream -> rootProp
|
||||
String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.0.0-26.2'))
|
||||
String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.2'))
|
||||
String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse(rootProperties.getProperty('forgeVersion', '26.2-65.0.0'))
|
||||
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:cdf7e8bb880199734dacd386a6616bda799fcde1'))
|
||||
Closure<String> loaderDisplayVersion = { String loaderVersion ->
|
||||
String coordinatePrefix = "${minecraftVersion}-"
|
||||
if (loaderVersion.startsWith(coordinatePrefix)) {
|
||||
@@ -134,7 +135,7 @@ dependencies {
|
||||
List<Object> shared = [
|
||||
"art.arcane:core:${irisVersion}".toString(),
|
||||
"art.arcane:spi:${irisVersion}".toString(),
|
||||
'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT',
|
||||
volmLibCoordinate,
|
||||
libs.paralithic,
|
||||
libs.lru,
|
||||
libs.kotlin.stdlib,
|
||||
|
||||
+11
-17
@@ -62,6 +62,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -368,8 +369,8 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
for (int blockY = y; blockY < sectionEnd; blockY++) {
|
||||
int bufferY = blockY - dimMinY;
|
||||
int localY = blockY & 15;
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
if (blocks.isAir(x, bufferY, z)) {
|
||||
continue;
|
||||
}
|
||||
@@ -445,7 +446,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
Engine current = engineOrNull();
|
||||
return current == null ? 63 : current.getDimension().getFluidHeight();
|
||||
return current == null ? 63 : current.getMinHeight() + current.getDimension().getFluidHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -471,25 +472,18 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
Engine current = engineOrNull();
|
||||
BlockState airState = Blocks.AIR.defaultBlockState();
|
||||
if (current == null) {
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
states[i] = airState;
|
||||
}
|
||||
Arrays.fill(states, airState);
|
||||
return new NoiseColumn(minY, states);
|
||||
}
|
||||
int surface = current.getMinHeight() + current.getHeight(x, z, true);
|
||||
int fluid = current.getDimension().getFluidHeight();
|
||||
int fluid = current.getMinHeight() + current.getDimension().getFluidHeight();
|
||||
BlockState stone = Blocks.STONE.defaultBlockState();
|
||||
BlockState water = Blocks.WATER.defaultBlockState();
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
int y = minY + i;
|
||||
if (y <= surface) {
|
||||
states[i] = stone;
|
||||
} else if (y <= fluid) {
|
||||
states[i] = water;
|
||||
} else {
|
||||
states[i] = airState;
|
||||
}
|
||||
}
|
||||
int solidEnd = Math.max(0, Math.min(states.length, surface - minY + 1));
|
||||
int fluidEnd = Math.max(solidEnd, Math.max(0, Math.min(states.length, fluid - minY + 1)));
|
||||
Arrays.fill(states, 0, solidEnd, stone);
|
||||
Arrays.fill(states, solidEnd, fluidEnd, water);
|
||||
Arrays.fill(states, fluidEnd, states.length, airState);
|
||||
return new NoiseColumn(minY, states);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,9 @@ public final class ModdedProtocolHandler {
|
||||
transport = serverTransport;
|
||||
protocolServer = protocol;
|
||||
IrisServices.register(IrisProtocolServer.class, protocol);
|
||||
if (server.getPlayerList() == null) {
|
||||
return;
|
||||
}
|
||||
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
|
||||
sessionRegistry.register(new IrisSession(player.getUUID().toString(), serverTransport));
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ public final class ModdedGoldenHash {
|
||||
engineMode,
|
||||
true,
|
||||
false,
|
||||
"minecraft:plains");
|
||||
GoldenHashEngine.FALLBACK_BIOME_KEY);
|
||||
this.hashEngine = new GoldenHashEngine(engine, request, goldenDir, this::snapshot, feedback(), progress());
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -386,7 +386,7 @@ public final class ModdedObjectCommands {
|
||||
|
||||
IrisObjectPlacement placement = new IrisObjectPlacement();
|
||||
placement.setRotation(IrisObjectRotation.of(0, rotation, 0));
|
||||
ModdedObjectPlacer placer = new ModdedObjectPlacer(level);
|
||||
ModdedObjectPlacer placer = new ModdedObjectPlacer(level, engine);
|
||||
try {
|
||||
object.place(target.getX(), target.getY() + object.getCenter().getY(), target.getZ(), placer, placement, new RNG(), null);
|
||||
} catch (Throwable e) {
|
||||
|
||||
+5
-2
@@ -35,6 +35,7 @@ import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -45,14 +46,16 @@ final class ModdedObjectPlacer implements IObjectPlacer {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
|
||||
|
||||
private final ServerLevel level;
|
||||
private final Engine engine;
|
||||
private final Map<BlockPos, BlockState> undo = new HashMap<>();
|
||||
private int writes = 0;
|
||||
private int nonAirWrites = 0;
|
||||
private int skippedTiles = 0;
|
||||
private int restoredTiles = 0;
|
||||
|
||||
ModdedObjectPlacer(ServerLevel level) {
|
||||
ModdedObjectPlacer(ServerLevel level, @Nullable Engine engine) {
|
||||
this.level = level;
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
Map<BlockPos, BlockState> undoSnapshot() {
|
||||
@@ -183,6 +186,6 @@ final class ModdedObjectPlacer implements IObjectPlacer {
|
||||
|
||||
@Override
|
||||
public Engine getEngine() {
|
||||
return null;
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -186,6 +186,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
|
||||
listener.onChunkFailed(x, z);
|
||||
return;
|
||||
}
|
||||
markCompleted();
|
||||
listener.onChunkGenerated(x, z);
|
||||
cleanupMantleChunk(x, z);
|
||||
listener.onChunkCleaned(x, z);
|
||||
@@ -236,6 +237,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
|
||||
return;
|
||||
}
|
||||
onSuccess();
|
||||
markCompleted();
|
||||
listener.onChunkGenerated(x, z);
|
||||
cleanupMantleChunk(x, z);
|
||||
listener.onChunkCleaned(x, z);
|
||||
@@ -253,12 +255,15 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
|
||||
|
||||
private void markFinished() {
|
||||
inFlight.decrementAndGet();
|
||||
completed.incrementAndGet();
|
||||
synchronized (permitMonitor) {
|
||||
permitMonitor.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void markCompleted() {
|
||||
completed.incrementAndGet();
|
||||
}
|
||||
|
||||
private void onTimeout() {
|
||||
if (timeoutStreak.incrementAndGet() % ADAPTIVE_TIMEOUT_STEP == 0) {
|
||||
adjustAdaptiveLimit(-1);
|
||||
|
||||
+2
-1
@@ -161,6 +161,7 @@ public final class ModdedStructureCommands {
|
||||
return 0;
|
||||
}
|
||||
ServerLevel level = source.getLevel();
|
||||
Engine engine = IrisModdedCommands.engineFor(level);
|
||||
IrisData data = dataFor(source);
|
||||
if (data == null) {
|
||||
return 0;
|
||||
@@ -181,7 +182,7 @@ public final class ModdedStructureCommands {
|
||||
IrisModdedCommands.fail(source, "Structure '" + key + "' assembled 0 pieces");
|
||||
return 0;
|
||||
}
|
||||
ModdedObjectPlacer placer = new ModdedObjectPlacer(level);
|
||||
ModdedObjectPlacer placer = new ModdedObjectPlacer(level, engine);
|
||||
UUID owner = player.getUUID();
|
||||
try {
|
||||
for (PlacedStructurePiece piece : pieces) {
|
||||
|
||||
@@ -31,6 +31,7 @@ file('../../gradle.properties').withInputStream { InputStream stream -> rootProp
|
||||
String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.0.0-26.2'))
|
||||
String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.2'))
|
||||
String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse(rootProperties.getProperty('neoForgeVersion', '26.2.0.6-beta'))
|
||||
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:cdf7e8bb880199734dacd386a6616bda799fcde1'))
|
||||
Closure<String> irisArtifactName = { String platform, String targetVersion ->
|
||||
return "Iris v${project.version} [${platform}] ${targetVersion}.jar"
|
||||
}
|
||||
@@ -103,7 +104,7 @@ dependencies {
|
||||
List<Object> shared = [
|
||||
"art.arcane:core:${irisVersion}".toString(),
|
||||
"art.arcane:spi:${irisVersion}".toString(),
|
||||
'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT',
|
||||
volmLibCoordinate,
|
||||
libs.paralithic,
|
||||
libs.lru,
|
||||
libs.kotlin.stdlib,
|
||||
|
||||
@@ -35,8 +35,9 @@ public final class IrisNeoForgeClient {
|
||||
private IrisNeoForgeClient() {
|
||||
}
|
||||
|
||||
public static void registerClientbound(PayloadRegistrar registrar) {
|
||||
registrar.playToClient(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC,
|
||||
public static void registerBidirectional(PayloadRegistrar registrar) {
|
||||
registrar.playBidirectional(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC,
|
||||
NeoForgeProtocolNetworking::onInbound,
|
||||
(payload, context) -> context.enqueueWork(() -> IrisClient.onInbound(payload.data())));
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -55,14 +55,14 @@ public final class NeoForgeProtocolNetworking {
|
||||
private static void onRegisterPayloads(RegisterPayloadHandlersEvent event) {
|
||||
PayloadRegistrar registrar = event.registrar("1").optional();
|
||||
if (FMLEnvironment.getDist() == Dist.CLIENT) {
|
||||
IrisNeoForgeClient.registerClientbound(registrar);
|
||||
IrisNeoForgeClient.registerBidirectional(registrar);
|
||||
} else {
|
||||
registrar.playToClient(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC);
|
||||
registrar.playBidirectional(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC,
|
||||
NeoForgeProtocolNetworking::onInbound);
|
||||
}
|
||||
registrar.playToServer(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC, NeoForgeProtocolNetworking::onInbound);
|
||||
}
|
||||
|
||||
private static void onInbound(ModdedIrisPayload payload, IPayloadContext context) {
|
||||
static void onInbound(ModdedIrisPayload payload, IPayloadContext context) {
|
||||
if (context.player() instanceof ServerPlayer player) {
|
||||
ModdedProtocolHandler.onInbound(player, payload.data());
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
./gradlew buildAll "$@"
|
||||
./gradlew buildAll --no-parallel -PuseLocalVolmLib=false "$@"
|
||||
|
||||
+35
-11
@@ -52,8 +52,10 @@ String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').get
|
||||
String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse('26.2-65.0.0')
|
||||
String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse('26.2.0.6-beta')
|
||||
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:cdf7e8bb880199734dacd386a6616bda799fcde1')
|
||||
.get()
|
||||
String useLocalVolmLib = providers.gradleProperty('useLocalVolmLib').getOrElse('true')
|
||||
String localVolmLibDirectory = providers.gradleProperty('localVolmLibDirectory').getOrNull()
|
||||
Closure<String> loaderDisplayVersion = { String loaderVersion ->
|
||||
String coordinatePrefix = "${minecraftVersion}-"
|
||||
if (loaderVersion.startsWith(coordinatePrefix)) {
|
||||
@@ -118,7 +120,6 @@ nmsBindings.each { key, value ->
|
||||
dependencies {
|
||||
compileOnly(project(':core'))
|
||||
compileOnly(volmLibCoordinate) {
|
||||
changing = true
|
||||
transitive = false
|
||||
}
|
||||
compileOnly(rootProject.libs.annotations)
|
||||
@@ -172,14 +173,20 @@ tasks.register('fabricJar', Exec) {
|
||||
group = 'iris'
|
||||
workingDir = layout.projectDirectory.dir('adapters/fabric').asFile
|
||||
String wrapperScript = System.getProperty('os.name').toLowerCase().contains('windows') ? 'gradlew.bat' : 'gradlew'
|
||||
commandLine(
|
||||
List<String> command = [
|
||||
layout.projectDirectory.file(wrapperScript).asFile.absolutePath,
|
||||
'shadowJar',
|
||||
'--console=plain',
|
||||
"-PirisVersion=${project.version}",
|
||||
"-PminecraftVersion=${minecraftVersion}",
|
||||
"-PfabricLoaderVersion=${fabricLoaderVersion}"
|
||||
)
|
||||
"-PfabricLoaderVersion=${fabricLoaderVersion}",
|
||||
"-PuseLocalVolmLib=${useLocalVolmLib}",
|
||||
"-PvolmLibCoordinate=${volmLibCoordinate}"
|
||||
]
|
||||
if (localVolmLibDirectory != null && !localVolmLibDirectory.isBlank()) {
|
||||
command.add("-PlocalVolmLibDirectory=${localVolmLibDirectory}")
|
||||
}
|
||||
commandLine(command)
|
||||
}
|
||||
|
||||
tasks.register('buildFabric', Copy) {
|
||||
@@ -196,14 +203,20 @@ tasks.register('forgeJar', Exec) {
|
||||
group = 'iris'
|
||||
workingDir = layout.projectDirectory.dir('adapters/forge').asFile
|
||||
String wrapperScript = System.getProperty('os.name').toLowerCase().contains('windows') ? 'gradlew.bat' : 'gradlew'
|
||||
commandLine(
|
||||
List<String> command = [
|
||||
layout.projectDirectory.file(wrapperScript).asFile.absolutePath,
|
||||
'shadowJar',
|
||||
'--console=plain',
|
||||
"-PirisVersion=${project.version}",
|
||||
"-PminecraftVersion=${minecraftVersion}",
|
||||
"-PforgeVersion=${forgeVersion}"
|
||||
)
|
||||
"-PforgeVersion=${forgeVersion}",
|
||||
"-PuseLocalVolmLib=${useLocalVolmLib}",
|
||||
"-PvolmLibCoordinate=${volmLibCoordinate}"
|
||||
]
|
||||
if (localVolmLibDirectory != null && !localVolmLibDirectory.isBlank()) {
|
||||
command.add("-PlocalVolmLibDirectory=${localVolmLibDirectory}")
|
||||
}
|
||||
commandLine(command)
|
||||
}
|
||||
|
||||
tasks.register('buildForge', Copy) {
|
||||
@@ -220,14 +233,20 @@ tasks.register('neoforgeJar', Exec) {
|
||||
group = 'iris'
|
||||
workingDir = layout.projectDirectory.dir('adapters/neoforge').asFile
|
||||
String wrapperScript = System.getProperty('os.name').toLowerCase().contains('windows') ? 'gradlew.bat' : 'gradlew'
|
||||
commandLine(
|
||||
List<String> command = [
|
||||
layout.projectDirectory.file(wrapperScript).asFile.absolutePath,
|
||||
'shadowJar',
|
||||
'--console=plain',
|
||||
"-PirisVersion=${project.version}",
|
||||
"-PminecraftVersion=${minecraftVersion}",
|
||||
"-PneoForgeVersion=${neoForgeVersion}"
|
||||
)
|
||||
"-PneoForgeVersion=${neoForgeVersion}",
|
||||
"-PuseLocalVolmLib=${useLocalVolmLib}",
|
||||
"-PvolmLibCoordinate=${volmLibCoordinate}"
|
||||
]
|
||||
if (localVolmLibDirectory != null && !localVolmLibDirectory.isBlank()) {
|
||||
command.add("-PlocalVolmLibDirectory=${localVolmLibDirectory}")
|
||||
}
|
||||
commandLine(command)
|
||||
}
|
||||
|
||||
tasks.register('buildNeoforge', Copy) {
|
||||
@@ -244,6 +263,11 @@ tasks.named('forgeJar').configure {
|
||||
mustRunAfter(tasks.named('fabricJar'))
|
||||
}
|
||||
|
||||
tasks.named('fabricJar').configure {
|
||||
mustRunAfter(tasks.named('buildBukkit'))
|
||||
mustRunAfter(project(':spi').tasks.named('jar'))
|
||||
}
|
||||
|
||||
tasks.named('neoforgeJar').configure {
|
||||
mustRunAfter(tasks.named('forgeJar'))
|
||||
}
|
||||
|
||||
+1
-2
@@ -41,7 +41,7 @@ plugins {
|
||||
|
||||
def lib = 'art.arcane.iris.util'
|
||||
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:cdf7e8bb880199734dacd386a6616bda799fcde1')
|
||||
.get()
|
||||
String sentryAuthToken = findProperty('sentry.auth.token') as String ?: System.getenv('SENTRY_AUTH_TOKEN')
|
||||
boolean hasSentryAuthToken = sentryAuthToken != null && !sentryAuthToken.isBlank()
|
||||
@@ -85,7 +85,6 @@ dependencies {
|
||||
// Shaded
|
||||
implementation('de.crazydev22.slimjar.helper:spigot:2.1.9')
|
||||
implementation(volmLibCoordinate) {
|
||||
changing = true
|
||||
transitive = false
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
private ResourceLoader<IrisObject> objectLoader;
|
||||
private ResourceLoader<IrisMatterObject> matterLoader;
|
||||
private ResourceLoader<IrisImage> imageLoader;
|
||||
private ResourceLoader<IrisMatterObject> matterObjectLoader;
|
||||
private ResourceLoader<IrisStructure> structureLoader;
|
||||
private ResourceLoader<IrisJigsawPool> jigsawPoolLoader;
|
||||
private ResourceLoader<IrisJigsawPiece> jigsawPieceLoader;
|
||||
@@ -337,7 +336,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
this.expressionLoader = registerLoader(IrisExpression.class);
|
||||
this.objectLoader = registerLoader(IrisObject.class);
|
||||
this.imageLoader = registerLoader(IrisImage.class);
|
||||
this.matterObjectLoader = registerLoader(IrisMatterObject.class);
|
||||
this.matterLoader = registerLoader(IrisMatterObject.class);
|
||||
this.structureLoader = registerLoader(IrisStructure.class);
|
||||
this.jigsawPoolLoader = registerLoader(IrisJigsawPool.class);
|
||||
this.jigsawPieceLoader = registerLoader(IrisJigsawPiece.class);
|
||||
|
||||
@@ -53,7 +53,7 @@ public class MatterObjectResourceLoader extends ResourceLoader<IrisMatterObject>
|
||||
protected IrisMatterObject loadFile(File j, String name) {
|
||||
try {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
IrisMatterObject t = IrisMatterObject.from(j);
|
||||
IrisMatterObject t = IrisMatterObject.from(j, manager);
|
||||
t.setLoadKey(name);
|
||||
t.setLoader(manager);
|
||||
t.setLoadFile(j);
|
||||
|
||||
@@ -204,22 +204,22 @@ public class IrisPregenerator {
|
||||
ticker.start();
|
||||
checkRegions();
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
boolean completed = false;
|
||||
try {
|
||||
int[] regionBounds = task.regionBounds();
|
||||
generator.onRegionBounds(regionBounds[0], regionBounds[1], regionBounds[2], regionBounds[3]);
|
||||
task.iterateRegions((x, z) -> visitRegion(x, z, true));
|
||||
task.iterateRegions((x, z) -> visitRegion(x, z, false));
|
||||
long failedCount = failed.get();
|
||||
if (failedCount > 0) {
|
||||
IrisLogging.warn("Pregen finished with " + Form.f(failedCount) + " failed chunk(s); failures are not cached, rerun to fill them");
|
||||
}
|
||||
IrisLogging.info("Pregen took " + Form.duration((long) p.getMilliseconds()));
|
||||
completed = true;
|
||||
} catch (Throwable e) {
|
||||
IrisLogging.reportError(e);
|
||||
IrisLogging.error("Pregen aborted after " + Form.duration((long) p.getMilliseconds()) + " due to " + e.getClass().getSimpleName() + ": " + e.getMessage());
|
||||
} finally {
|
||||
shutdown();
|
||||
}
|
||||
if (completed) {
|
||||
logSuccessfulCompletion(p);
|
||||
}
|
||||
if (benchmarking == null) {
|
||||
IrisLogging.info(C.IRIS + "Pregen stopped.");
|
||||
} else {
|
||||
@@ -227,6 +227,17 @@ public class IrisPregenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void logSuccessfulCompletion(PrecisionStopwatch stopwatch) {
|
||||
long failedCount = failed.get();
|
||||
if (failedCount > 0) {
|
||||
IrisLogging.warn("Pregen finished with " + Form.f(failedCount) + " failed chunk(s); failures are not cached, rerun to fill them");
|
||||
}
|
||||
IrisLogging.info("Pregen finished: generated=" + Form.f(generated.get())
|
||||
+ " total=" + Form.f(totalChunks.get())
|
||||
+ " failed=" + Form.f(failedCount)
|
||||
+ " duration=" + Form.duration((long) stopwatch.getMilliseconds()));
|
||||
}
|
||||
|
||||
private void checkRegions() {
|
||||
task.iterateRegions(this::checkRegion);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@ public class PregenTask {
|
||||
private final Bounds bounds = new Bounds();
|
||||
|
||||
protected PregenTask(boolean gui, Position2 center, int radiusX, int radiusZ) {
|
||||
if (radiusX <= 0 || radiusZ <= 0) {
|
||||
throw new IllegalArgumentException("Pregen radii must be greater than zero blocks.");
|
||||
}
|
||||
|
||||
this.gui = gui;
|
||||
this.center = new ProxiedPos(center);
|
||||
this.radiusX = radiusX;
|
||||
|
||||
+12
@@ -35,6 +35,18 @@ public class AsyncOrMedievalPregenMethod implements PregeneratorMethod {
|
||||
}
|
||||
}
|
||||
|
||||
private AsyncOrMedievalPregenMethod(PregeneratorMethod method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public static AsyncOrMedievalPregenMethod strictSerial(World world) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
throw new UnsupportedOperationException("Strict serial pregeneration requires Paper or a Paper-compatible server.");
|
||||
}
|
||||
|
||||
return new AsyncOrMedievalPregenMethod(AsyncPregenMethod.strictSerial(world));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
method.init();
|
||||
|
||||
+13
-1
@@ -103,6 +103,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
private final PregenMantleBackpressure backpressure;
|
||||
|
||||
public AsyncPregenMethod(World world, int unusedThreads) {
|
||||
this(world, false);
|
||||
}
|
||||
|
||||
private AsyncPregenMethod(World world, boolean strictSerial) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
|
||||
}
|
||||
@@ -138,7 +142,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
int configuredThreads = foliaRuntime
|
||||
? computeFoliaRecommendedCap(workerThreadsForCap)
|
||||
: computePaperLikeRecommendedCap(workerThreadsForCap);
|
||||
this.threads = Math.max(1, configuredThreads);
|
||||
this.threads = selectConcurrencyCap(configuredThreads, strictSerial);
|
||||
this.workerPoolThreads = detectedWorkerPoolThreads;
|
||||
this.runtimeCpuThreads = detectedCpuThreads;
|
||||
this.effectiveWorkerThreads = workerThreadsForCap;
|
||||
@@ -168,6 +172,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
this::metricsSnapshot);
|
||||
}
|
||||
|
||||
public static AsyncPregenMethod strictSerial(World world) {
|
||||
return new AsyncPregenMethod(world, true);
|
||||
}
|
||||
|
||||
private IrisPaperLikeBackendMode resolvePaperLikeBackendMode(IrisSettings.IrisSettingsPregen pregen) {
|
||||
IrisPaperLikeBackendMode configuredMode = pregen.getPaperLikeBackendMode();
|
||||
if (configuredMode != IrisPaperLikeBackendMode.AUTO) {
|
||||
@@ -540,6 +548,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
return recommendedCap;
|
||||
}
|
||||
|
||||
static int selectConcurrencyCap(int recommendedCap, boolean strictSerial) {
|
||||
return strictSerial ? 1 : Math.max(1, recommendedCap);
|
||||
}
|
||||
|
||||
static int resolvePaperLikeConcurrencyWorkerThreads(int detectedWorkerPoolThreads, int detectedCpuThreads, int configuredWorldGenThreads) {
|
||||
int provisionedWorkerThreads = Math.max(1, configuredWorldGenThreads);
|
||||
if (detectedWorkerPoolThreads > 0) {
|
||||
|
||||
+9
-1
@@ -28,8 +28,16 @@ public class HybridPregenMethod implements PregeneratorMethod {
|
||||
private final World world;
|
||||
|
||||
public HybridPregenMethod(World world, int threads) {
|
||||
this(world, new AsyncOrMedievalPregenMethod(world, threads));
|
||||
}
|
||||
|
||||
private HybridPregenMethod(World world, PregeneratorMethod inWorld) {
|
||||
this.world = world;
|
||||
inWorld = new AsyncOrMedievalPregenMethod(world, threads);
|
||||
this.inWorld = inWorld;
|
||||
}
|
||||
|
||||
public static HybridPregenMethod strictSerial(World world) {
|
||||
return new HybridPregenMethod(world, AsyncOrMedievalPregenMethod.strictSerial(world));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,6 +46,8 @@ import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public final class GoldenHashEngine {
|
||||
public static final String FALLBACK_BIOME_KEY = "minecraft:plains";
|
||||
|
||||
public enum Mode {
|
||||
AUTO,
|
||||
CAPTURE,
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class GoldenHashScanner {
|
||||
GoldenHashEngine.Mode.AUTO,
|
||||
resetMantle,
|
||||
deep,
|
||||
"null");
|
||||
GoldenHashEngine.FALLBACK_BIOME_KEY);
|
||||
this.hashEngine = new GoldenHashEngine(engine, request, IrisPlatforms.get().dataFolder("golden"), this::snapshot, feedback(), progress());
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.framework.MeteredCache;
|
||||
import art.arcane.iris.engine.framework.PreservationRegistry;
|
||||
import art.arcane.iris.util.project.context.IrisContext;
|
||||
import art.arcane.volmlib.util.data.KCache;
|
||||
import art.arcane.volmlib.util.format.Form;
|
||||
import art.arcane.iris.util.common.plugin.IrisService;
|
||||
@@ -52,7 +51,7 @@ public class PreservationSVC implements IrisService, PreservationRegistry {
|
||||
}
|
||||
|
||||
public void printCaches() {
|
||||
var c = getCaches();
|
||||
List<MeteredCache> c = getCaches();
|
||||
long s = 0;
|
||||
long m = 0;
|
||||
double p = 0;
|
||||
@@ -68,7 +67,6 @@ public class PreservationSVC implements IrisService, PreservationRegistry {
|
||||
}
|
||||
|
||||
public void dereference() {
|
||||
IrisContext.dereference();
|
||||
IrisData.dereference();
|
||||
threads.removeIf((i) -> !i.isAlive());
|
||||
services.removeIf(ExecutorService::isShutdown);
|
||||
@@ -122,7 +120,7 @@ public class PreservationSVC implements IrisService, PreservationRegistry {
|
||||
|
||||
public void updateCaches() {
|
||||
caches.removeIf(ref -> {
|
||||
var c = ref.get();
|
||||
MeteredCache c = ref.get();
|
||||
return c == null || c.isClosed();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class IrisSplashComposer {
|
||||
public static final String RELEASE_TAG = "RC.1.1.6";
|
||||
private static final String SPLASH_PADDING = " ".repeat(4);
|
||||
|
||||
private IrisSplashComposer() {
|
||||
@@ -20,7 +19,7 @@ public final class IrisSplashComposer {
|
||||
String prefix = style.linePrefix();
|
||||
return new String[]{
|
||||
"",
|
||||
prefix + style.title(" Iris, ") + style.subtitle("Dimension Engine ") + style.tag("[" + releaseTrain + " " + RELEASE_TAG + "]"),
|
||||
prefix + style.title(" Iris, ") + style.subtitle("Dimension Engine ") + style.tag("[" + releaseTrain + "]"),
|
||||
prefix + style.label(" Version: ") + style.value(version),
|
||||
prefix + style.label(" By: ") + style.value("Volmit Software (Arcane Arts)"),
|
||||
prefix + style.label(" Server: ") + style.value(serverLine),
|
||||
|
||||
@@ -264,6 +264,10 @@ public class IrisToolbelt {
|
||||
PregenPerformanceProfile.apply(engine);
|
||||
}
|
||||
|
||||
public static boolean supportsStrictSerialPregeneration() {
|
||||
return PaperLib.isPaper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a pregenerator task. If the supplied generator is headless, headless mode is used,
|
||||
* otherwise Hybrid mode is used.
|
||||
@@ -273,8 +277,16 @@ public class IrisToolbelt {
|
||||
* @return the pregenerator job (already started)
|
||||
*/
|
||||
public static PregeneratorJob pregenerate(PregenTask task, PlatformChunkGenerator gen) {
|
||||
return pregenerate(task, new HybridPregenMethod(gen.getEngine().getWorld().realWorld(),
|
||||
IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())), gen.getEngine());
|
||||
World world = gen.getEngine().getWorld().realWorld();
|
||||
int threads = IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism());
|
||||
PregeneratorMethod method = new HybridPregenMethod(world, threads);
|
||||
return pregenerate(task, method, gen.getEngine());
|
||||
}
|
||||
|
||||
public static PregeneratorJob pregenerateSerial(PregenTask task, PlatformChunkGenerator gen) {
|
||||
World world = gen.getEngine().getWorld().realWorld();
|
||||
PregeneratorMethod method = HybridPregenMethod.strictSerial(world);
|
||||
return pregenerate(task, method, gen.getEngine());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,7 +302,18 @@ public class IrisToolbelt {
|
||||
return pregenerate(task, access(world));
|
||||
}
|
||||
|
||||
return pregenerate(task, new HybridPregenMethod(world, IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())), null);
|
||||
int threads = IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism());
|
||||
PregeneratorMethod method = new HybridPregenMethod(world, threads);
|
||||
return pregenerate(task, method, null);
|
||||
}
|
||||
|
||||
public static PregeneratorJob pregenerateSerial(PregenTask task, World world) {
|
||||
if (isIrisWorld(world)) {
|
||||
return pregenerateSerial(task, access(world));
|
||||
}
|
||||
|
||||
PregeneratorMethod method = HybridPregenMethod.strictSerial(world);
|
||||
return pregenerate(task, method, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,6 @@ import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.PlatformBiome;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.iris.util.project.context.IrisContext;
|
||||
import art.arcane.iris.util.common.data.DataProvider;
|
||||
import art.arcane.volmlib.util.math.M;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
@@ -164,7 +163,7 @@ public class IrisComplex implements DataProvider {
|
||||
.cache2D("regionStream", engine, cacheSize).waste("Region Stream");
|
||||
regionIDStream = regionIdentityStream.convertCached((i) -> new UUID(Double.doubleToLongBits(i),
|
||||
String.valueOf(i * 38445).hashCode() * 3245556666L)).waste("Region ID Stream");
|
||||
caveBiomeStream = regionStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getRegion().get(x, z))
|
||||
caveBiomeStream = regionStream.contextInjecting(engine, (c, x, z) -> c.getRegion().get(x, z))
|
||||
.convert((r)
|
||||
-> engine.getDimension().getCaveBiomeStyle().create(rng.nextParallelRNG(InferredType.CAVE.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
@@ -173,7 +172,7 @@ public class IrisComplex implements DataProvider {
|
||||
.onNull(emptyBiome)
|
||||
).convertAware2D(ProceduralStream::get).cache2D("caveBiomeStream", engine, cacheSize).waste("Cave Biome Stream");
|
||||
inferredStreams.put(InferredType.CAVE, caveBiomeStream);
|
||||
landBiomeStream = regionStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getRegion().get(x, z))
|
||||
landBiomeStream = regionStream.contextInjecting(engine, (c, x, z) -> c.getRegion().get(x, z))
|
||||
.convert((r)
|
||||
-> engine.getDimension().getLandBiomeStyle().create(rng.nextParallelRNG(InferredType.LAND.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
@@ -183,7 +182,7 @@ public class IrisComplex implements DataProvider {
|
||||
).convertAware2D(ProceduralStream::get)
|
||||
.cache2D("landBiomeStream", engine, cacheSize).waste("Land Biome Stream");
|
||||
inferredStreams.put(InferredType.LAND, landBiomeStream);
|
||||
seaBiomeStream = regionStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getRegion().get(x, z))
|
||||
seaBiomeStream = regionStream.contextInjecting(engine, (c, x, z) -> c.getRegion().get(x, z))
|
||||
.convert((r)
|
||||
-> engine.getDimension().getSeaBiomeStyle().create(rng.nextParallelRNG(InferredType.SEA.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
@@ -193,7 +192,7 @@ public class IrisComplex implements DataProvider {
|
||||
).convertAware2D(ProceduralStream::get)
|
||||
.cache2D("seaBiomeStream", engine, cacheSize).waste("Sea Biome Stream");
|
||||
inferredStreams.put(InferredType.SEA, seaBiomeStream);
|
||||
shoreBiomeStream = regionStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getRegion().get(x, z))
|
||||
shoreBiomeStream = regionStream.contextInjecting(engine, (c, x, z) -> c.getRegion().get(x, z))
|
||||
.convert((r)
|
||||
-> engine.getDimension().getShoreBiomeStyle().create(rng.nextParallelRNG(InferredType.SHORE.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
@@ -216,37 +215,37 @@ public class IrisComplex implements DataProvider {
|
||||
IrisBiome b = focusBiome != null ? focusBiome : baseBiomeStream.get(x, z);
|
||||
return getHeight(engine, b, x, z, engine.getSeedManager().getHeight());
|
||||
}, Interpolated.DOUBLE).cache2DDouble("heightStream", engine, cacheSize).waste("Height Stream");
|
||||
roundedHeighteightStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().getDouble(x, z))
|
||||
roundedHeighteightStream = heightStream.contextInjecting(engine, (c, x, z) -> c.getHeight().getDouble(x, z))
|
||||
.round().waste("Rounded Height Stream");
|
||||
slopeStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().getDouble(x, z))
|
||||
slopeStream = heightStream.contextInjecting(engine, (c, x, z) -> c.getHeight().getDouble(x, z))
|
||||
.slope(3).cache2DDouble("slopeStream", engine, cacheSize).waste("Slope Stream");
|
||||
trueBiomeStream = focusBiome != null ? ProceduralStream.of((x, y) -> focusBiome, Interpolated.of(a -> 0D,
|
||||
b -> focusBiome))
|
||||
.cache2D("trueBiomeStream-focus", engine, cacheSize) : heightStream
|
||||
.convertAware2D((h, x, z) ->
|
||||
fixBiomeType(h, baseBiomeStream.get(x, z),
|
||||
regionStream.contextInjecting((c, xx, zz) -> IrisContext.getOr(engine).getChunkContext().getRegion().get(xx, zz)).get(x, z), x, z, fluidHeight))
|
||||
regionStream.contextInjecting(engine, (c, xx, zz) -> c.getRegion().get(xx, zz)).get(x, z), x, z, fluidHeight))
|
||||
.cache2D("trueBiomeStream", engine, cacheSize).waste("True Biome Stream");
|
||||
trueBiomeDerivativeStream = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
|
||||
trueBiomeDerivativeStream = trueBiomeStream.contextInjecting(engine, (c, x, z) -> c.getBiome().get(x, z))
|
||||
.convert((b) -> IrisPlatforms.get().registries().biome(b.getDerivativeKey())).cache2D("trueBiomeDerivativeStream", engine, cacheSize).waste("True Biome Derivative Stream");
|
||||
heightFluidStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().getDouble(x, z))
|
||||
heightFluidStream = heightStream.contextInjecting(engine, (c, x, z) -> c.getHeight().getDouble(x, z))
|
||||
.max(fluidHeight).cache2DDouble("heightFluidStream", engine, cacheSize).waste("Height Fluid Stream");
|
||||
maxHeightStream = ProceduralStream.ofDouble((x, z) -> height).waste("Max Height Stream");
|
||||
terrainSurfaceDecoration = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
|
||||
terrainSurfaceDecoration = trueBiomeStream.contextInjecting(engine, (c, x, z) -> c.getBiome().get(x, z))
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.NONE)).cache2D("terrainSurfaceDecoration", engine, cacheSize).waste("Surface Decoration Stream");
|
||||
terrainCeilingDecoration = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
|
||||
terrainCeilingDecoration = trueBiomeStream.contextInjecting(engine, (c, x, z) -> c.getBiome().get(x, z))
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.CEILING)).cache2D("terrainCeilingDecoration", engine, cacheSize).waste("Ceiling Decoration Stream");
|
||||
terrainCaveSurfaceDecoration = caveBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getCave().get(x, z))
|
||||
terrainCaveSurfaceDecoration = caveBiomeStream.contextInjecting(engine, (c, x, z) -> c.getCave().get(x, z))
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.NONE)).cache2D("terrainCaveSurfaceDecoration", engine, cacheSize).waste("Cave Surface Stream");
|
||||
terrainCaveCeilingDecoration = caveBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getCave().get(x, z))
|
||||
terrainCaveCeilingDecoration = caveBiomeStream.contextInjecting(engine, (c, x, z) -> c.getCave().get(x, z))
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.CEILING)).cache2D("terrainCaveCeilingDecoration", engine, cacheSize).waste("Cave Ceiling Stream");
|
||||
shoreSurfaceDecoration = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
|
||||
shoreSurfaceDecoration = trueBiomeStream.contextInjecting(engine, (c, x, z) -> c.getBiome().get(x, z))
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SHORE_LINE)).cache2D("shoreSurfaceDecoration", engine, cacheSize).waste("Shore Surface Stream");
|
||||
seaSurfaceDecoration = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
|
||||
seaSurfaceDecoration = trueBiomeStream.contextInjecting(engine, (c, x, z) -> c.getBiome().get(x, z))
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SEA_SURFACE)).cache2D("seaSurfaceDecoration", engine, cacheSize).waste("Sea Surface Stream");
|
||||
seaFloorDecoration = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
|
||||
seaFloorDecoration = trueBiomeStream.contextInjecting(engine, (c, x, z) -> c.getBiome().get(x, z))
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SEA_FLOOR)).cache2D("seaFloorDecoration", engine, cacheSize).waste("Sea Floor Stream");
|
||||
baseBiomeIDStream = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
|
||||
baseBiomeIDStream = trueBiomeStream.contextInjecting(engine, (c, x, z) -> c.getBiome().get(x, z))
|
||||
.convertAware2D((b, x, z) -> {
|
||||
UUID d = regionIDStream.get(x, z);
|
||||
return new UUID(b.getLoadKey().hashCode() * 818223L,
|
||||
|
||||
@@ -81,8 +81,6 @@ import art.arcane.volmlib.util.scheduling.ChronoLatch;
|
||||
import art.arcane.iris.util.common.scheduling.J;
|
||||
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -94,8 +92,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(exclude = "context")
|
||||
@ToString(exclude = "context")
|
||||
public class IrisEngine implements Engine {
|
||||
private final AtomicInteger bud;
|
||||
private final AtomicInteger buds;
|
||||
@@ -104,7 +100,6 @@ public class IrisEngine implements Engine {
|
||||
private final AtomicDouble perSecond;
|
||||
private final AtomicLong lastGPS;
|
||||
private final EngineTarget target;
|
||||
private final IrisContext context;
|
||||
private final EngineMantle mantle;
|
||||
private final ChronoLatch perSecondLatch;
|
||||
private final ChronoLatch perSecondBudLatch;
|
||||
@@ -156,7 +151,6 @@ public class IrisEngine implements Engine {
|
||||
long _t0 = M.ms();
|
||||
mantle = new IrisEngineMantle(this);
|
||||
IrisLogging.debug("[IrisEngine timing] new IrisEngineMantle=" + (M.ms() - _t0) + "ms");
|
||||
context = new IrisContext(this);
|
||||
cleaning = new AtomicBoolean(false);
|
||||
modeFallbackLogged = new AtomicBoolean(false);
|
||||
prefetchSaveStarted = new AtomicBoolean(false);
|
||||
@@ -167,7 +161,6 @@ public class IrisEngine implements Engine {
|
||||
getTarget().setDimension(getData().getDimensionLoader().load(getDimension().getLoadKey()));
|
||||
IrisLogging.debug("[IrisEngine timing] dump+clearLists+reload=" + (M.ms() - _t0) + "ms");
|
||||
}
|
||||
context.touch();
|
||||
getData().setEngine(this);
|
||||
_t0 = M.ms();
|
||||
getData().loadPrefetch(this);
|
||||
@@ -717,9 +710,8 @@ public class IrisEngine implements Engine {
|
||||
throw new GenerationSessionException("Generation session is closed for world \"" + getWorld().name() + "\".", true);
|
||||
}
|
||||
|
||||
try (GenerationSessionLease lease = acquireGenerationLease("chunk_generate")) {
|
||||
context.touch();
|
||||
context.setGenerationSessionId(lease.sessionId());
|
||||
try (GenerationSessionLease lease = acquireGenerationLease("chunk_generate");
|
||||
IrisContext.Scope generationScope = IrisContext.open(this, lease.sessionId(), null)) {
|
||||
getEngineData().getStatistics().generatedChunk();
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
Hunk<PlatformBlockState> blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t));
|
||||
@@ -733,7 +725,7 @@ public class IrisEngine implements Engine {
|
||||
}
|
||||
} else {
|
||||
EngineMode activeMode = ensureMode();
|
||||
activeMode.generate(x, z, blocks, vbiomes, multicore);
|
||||
activeMode.generate(x, z, blocks, vbiomes, multicore, lease.sessionId());
|
||||
}
|
||||
|
||||
boolean skipRealFlag = J.isFolia() && getWorld().hasRealWorld() && IrisToolbelt.isWorldMaintenanceBypassingMantleStages(getWorld().realWorld());
|
||||
|
||||
@@ -20,6 +20,7 @@ package art.arcane.iris.engine;
|
||||
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.tools.WorldMaintenance;
|
||||
import art.arcane.iris.engine.EnginePanic;
|
||||
import art.arcane.iris.core.nms.container.Pair;
|
||||
@@ -34,6 +35,7 @@ import art.arcane.iris.engine.mantle.components.MantleObjectComponent;
|
||||
import art.arcane.iris.engine.mantle.components.IrisStructureComponent;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
import art.arcane.iris.util.project.matter.IrisMatterContext;
|
||||
import art.arcane.iris.util.project.matter.IrisMatterSupport;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
@@ -182,7 +184,7 @@ public class IrisEngineMantle implements EngineMantle {
|
||||
IrisMatterSupport.ensureRegistered();
|
||||
File dataFolder = new File(engine.getWorld().worldFolder(), "mantle");
|
||||
int worldHeight = engine.getTarget().getHeight();
|
||||
MantleDataAdapter<Matter> adapter = createRuntimeDataAdapter();
|
||||
MantleDataAdapter<Matter> adapter = createRuntimeDataAdapter(engine.getData());
|
||||
MantleHooks hooks = createRuntimeHooks();
|
||||
art.arcane.volmlib.util.mantle.Mantle.RegionIO<TectonicPlate<Matter>> regionIO =
|
||||
createRegionIO(dataFolder, worldHeight, adapter, hooks);
|
||||
@@ -198,15 +200,15 @@ public class IrisEngineMantle implements EngineMantle {
|
||||
);
|
||||
}
|
||||
|
||||
public static MantleDataAdapter<Matter> createRuntimeDataAdapter() {
|
||||
return createDataAdapter();
|
||||
public static MantleDataAdapter<Matter> createRuntimeDataAdapter(IrisData data) {
|
||||
return createDataAdapter(data);
|
||||
}
|
||||
|
||||
public static MantleHooks createRuntimeHooks() {
|
||||
return createHooks();
|
||||
}
|
||||
|
||||
private static MantleDataAdapter<Matter> createDataAdapter() {
|
||||
private static MantleDataAdapter<Matter> createDataAdapter(IrisData data) {
|
||||
return new MantleDataAdapter<>() {
|
||||
@Override
|
||||
public Matter createSection() {
|
||||
@@ -215,7 +217,9 @@ public class IrisEngineMantle implements EngineMantle {
|
||||
|
||||
@Override
|
||||
public Matter readSection(art.arcane.volmlib.util.io.CountingDataInputStream din) throws IOException {
|
||||
return Matter.readDin(din);
|
||||
try (IrisMatterContext.Scope scope = IrisMatterContext.open(data)) {
|
||||
return Matter.readDin(din);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -51,7 +51,6 @@ import art.arcane.iris.spi.PlatformBlockState;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.iris.util.project.context.ChunkContext;
|
||||
import art.arcane.iris.util.project.context.IrisContext;
|
||||
import art.arcane.iris.util.common.data.DataProvider;
|
||||
import art.arcane.iris.util.common.data.B;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
@@ -111,8 +110,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
return isClosed();
|
||||
}
|
||||
|
||||
IrisContext getContext();
|
||||
|
||||
double getMaxBiomeObjectDensity();
|
||||
|
||||
double getMaxBiomeDecoratorDensity();
|
||||
@@ -214,7 +211,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
Color ibc = biome.getColor(this, RenderType.BIOME);
|
||||
Color rc = irc != null ? irc : Color.GREEN.darker();
|
||||
Color bc = ibc != null ? ibc : biome.isAquatic() ? Color.BLUE : Color.YELLOW;
|
||||
Color f = IrisColor.blend(rc, bc, bc, Color.getHSBColor(0, 0, (float) heightFactor));
|
||||
|
||||
return IrisColor.blend(rc, bc, bc, Color.getHSBColor(0, 0, (float) heightFactor));
|
||||
}
|
||||
|
||||
@@ -51,7 +51,11 @@ public interface EngineMode extends Staged {
|
||||
e.setMulticore(multicore);
|
||||
|
||||
for (EngineStage i : stages) {
|
||||
e.queue(() -> i.generate(x, z, blocks, biomes, multicore, ctx));
|
||||
e.queue(() -> {
|
||||
try (IrisContext.Scope stageScope = IrisContext.open(getEngine(), ctx.getGenerationSessionId(), ctx)) {
|
||||
i.generate(x, z, blocks, biomes, multicore, ctx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
e.complete();
|
||||
@@ -71,19 +75,19 @@ public interface EngineMode extends Staged {
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
default void generate(int x, int z, Hunk<PlatformBlockState> blocks, Hunk<PlatformBiome> biomes, boolean multicore) {
|
||||
IrisContext context = IrisContext.getOr(getEngine());
|
||||
default void generate(int x, int z, Hunk<PlatformBlockState> blocks, Hunk<PlatformBiome> biomes, boolean multicore, long generationSessionId) {
|
||||
boolean cacheContext = true;
|
||||
if (J.isFolia() && getEngine().getWorld().hasRealWorld() && shouldDisableContextCacheForMaintenance()) {
|
||||
cacheContext = false;
|
||||
}
|
||||
ChunkContext.PrefillPlan prefillPlan = cacheContext ? ChunkContext.PrefillPlan.NO_CAVE : ChunkContext.PrefillPlan.NONE;
|
||||
ChunkContext ctx = new ChunkContext(x, z, getComplex(), context.getGenerationSessionId(), cacheContext, prefillPlan, getEngine().getMetrics());
|
||||
context.setChunkContext(ctx);
|
||||
ChunkContext ctx = new ChunkContext(x, z, getComplex(), generationSessionId, cacheContext, prefillPlan, getEngine().getMetrics());
|
||||
|
||||
EngineStage[] stages = getStages().toArray(new EngineStage[0]);
|
||||
for (EngineStage i : stages) {
|
||||
i.generate(x, z, blocks, biomes, multicore, ctx);
|
||||
try (IrisContext.Scope chunkScope = IrisContext.open(getEngine(), generationSessionId, ctx)) {
|
||||
for (EngineStage i : stages) {
|
||||
i.generate(x, z, blocks, biomes, multicore, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,8 +80,10 @@ public interface Locator<T> {
|
||||
return (e, c) -> {
|
||||
AtomicBoolean found = new AtomicBoolean(false);
|
||||
try (GenerationSessionLease lease = e.acquireGenerationLease("locator_generate_matter")) {
|
||||
IrisContext.getOr(e).setGenerationSessionId(lease.sessionId());
|
||||
e.generateMatter(c.getX(), c.getZ(), true, new ChunkContext(c.getX() << 4, c.getZ() << 4, e.getComplex(), lease.sessionId(), false, ChunkContext.PrefillPlan.NONE, null));
|
||||
ChunkContext chunkContext = new ChunkContext(c.getX() << 4, c.getZ() << 4, e.getComplex(), lease.sessionId(), false, ChunkContext.PrefillPlan.NONE, null);
|
||||
try (IrisContext.Scope locatorScope = IrisContext.open(e, lease.sessionId(), chunkContext)) {
|
||||
e.generateMatter(c.getX(), c.getZ(), true, chunkContext);
|
||||
}
|
||||
} catch (GenerationSessionException sessionException) {
|
||||
throw new IllegalStateException(sessionException);
|
||||
}
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ public class HeightmapObjectPlacer implements IObjectPlacer {
|
||||
private final IrisObjectPlacement config;
|
||||
private final IObjectPlacer oplacer;
|
||||
|
||||
public HeightmapObjectPlacer(Engine engine, RNG rng, int x, int yv, int z, IrisObjectPlacement config, IObjectPlacer oplacer) {
|
||||
public HeightmapObjectPlacer(RNG rng, int x, int yv, int z, IrisObjectPlacement config, IObjectPlacer oplacer) {
|
||||
s = rng.nextLong() + yv + z - x;
|
||||
this.config = config;
|
||||
this.oplacer = oplacer;
|
||||
@@ -94,6 +94,6 @@ public class HeightmapObjectPlacer implements IObjectPlacer {
|
||||
|
||||
@Override
|
||||
public Engine getEngine() {
|
||||
return null;
|
||||
return oplacer.getEngine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class IrisPerfectionModifier extends EngineAssignedModifier<PlatformBlockState> {
|
||||
private static final class States {
|
||||
@@ -88,17 +87,14 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<PlatformBlock
|
||||
hideOres(output, multicore);
|
||||
}
|
||||
AtomicBoolean changed = new AtomicBoolean(true);
|
||||
int passes = 0;
|
||||
AtomicInteger changes = new AtomicInteger();
|
||||
List<Integer> surfaces = new ArrayList<>();
|
||||
List<Integer> ceilings = new ArrayList<>();
|
||||
BurstExecutor burst = burst().burst(multicore);
|
||||
while (changed.get()) {
|
||||
passes++;
|
||||
changed.set(false);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int finalI = i;
|
||||
burst.queue(() -> {
|
||||
List<Integer> surfaces = new ArrayList<>();
|
||||
List<Integer> ceilings = new ArrayList<>();
|
||||
for (int j = 0; j < 16; j++) {
|
||||
surfaces.clear();
|
||||
ceilings.clear();
|
||||
@@ -148,11 +144,9 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<PlatformBlock
|
||||
|
||||
if (remove) {
|
||||
changed.set(true);
|
||||
changes.getAndIncrement();
|
||||
output.set(finalI, k, j, States.AIR);
|
||||
|
||||
if (remove2) {
|
||||
changes.getAndIncrement();
|
||||
output.set(finalI, k - 1, j, States.AIR);
|
||||
}
|
||||
}
|
||||
@@ -161,6 +155,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<PlatformBlock
|
||||
}
|
||||
});
|
||||
}
|
||||
burst.complete();
|
||||
}
|
||||
|
||||
getEngine().getMetrics().getPerfection().put(p.getMilliseconds());
|
||||
|
||||
@@ -30,8 +30,6 @@ import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class IrisPostModifier extends EngineAssignedModifier<PlatformBlockState> {
|
||||
private static final class States {
|
||||
private static final PlatformBlockState AIR = B.getState("AIR");
|
||||
@@ -48,14 +46,12 @@ public class IrisPostModifier extends EngineAssignedModifier<PlatformBlockState>
|
||||
@Override
|
||||
public void onModify(int x, int z, Hunk<PlatformBlockState> output, boolean multicore, ChunkContext context) {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
AtomicInteger i = new AtomicInteger();
|
||||
AtomicInteger j = new AtomicInteger();
|
||||
Hunk<PlatformBlockState> sync = output.synchronize();
|
||||
for (i.set(0); i.get() < output.getWidth(); i.getAndIncrement()) {
|
||||
for (j.set(0); j.get() < output.getDepth(); j.getAndIncrement()) {
|
||||
int ii = i.get();
|
||||
int jj = j.get();
|
||||
post(ii, jj, sync, ii + x, jj + z, context);
|
||||
int width = output.getWidth();
|
||||
int depth = output.getDepth();
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < depth; j++) {
|
||||
post(i, j, sync, i + x, j + z, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.iris.util.project.context.IrisContext;
|
||||
import art.arcane.iris.util.common.data.B;
|
||||
import art.arcane.iris.util.common.data.VectorMap;
|
||||
import art.arcane.volmlib.util.format.Form;
|
||||
@@ -676,7 +675,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
}
|
||||
|
||||
public int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer<BlockPosition, PlatformBlockState> listener, CarveResult c, IrisData rdata) {
|
||||
IObjectPlacer placer = (config.getHeightmap() != null) ? new HeightmapObjectPlacer(oplacer.getEngine() == null ? IrisContext.get().getEngine() : oplacer.getEngine(), rng, x, yv, z, config, oplacer) : oplacer;
|
||||
IObjectPlacer placer = config.getHeightmap() != null ? new HeightmapObjectPlacer(rng, x, yv, z, config, oplacer) : oplacer;
|
||||
|
||||
if (rdata != null) {
|
||||
// Slope condition
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package art.arcane.iris.engine.object.matter;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.loader.IrisRegistrant;
|
||||
import art.arcane.iris.engine.object.IrisObject;
|
||||
import art.arcane.iris.util.project.matter.IrisMatterContext;
|
||||
import art.arcane.iris.util.project.matter.IrisMatterSupport;
|
||||
import art.arcane.volmlib.util.json.JSONObject;
|
||||
import art.arcane.volmlib.util.matter.IrisMatter;
|
||||
@@ -35,9 +37,11 @@ public class IrisMatterObject extends IrisRegistrant {
|
||||
return new IrisMatterObject(IrisMatterSupport.from(object));
|
||||
}
|
||||
|
||||
public static IrisMatterObject from(File j) throws IOException, ClassNotFoundException {
|
||||
public static IrisMatterObject from(File j, IrisData data) throws IOException {
|
||||
IrisMatterSupport.ensureRegistered();
|
||||
return new IrisMatterObject(Matter.read(j));
|
||||
try (IrisMatterContext.Scope scope = IrisMatterContext.open(data)) {
|
||||
return new IrisMatterObject(Matter.read(j));
|
||||
}
|
||||
}
|
||||
|
||||
private static Matter createMatter(int w, int h, int d) {
|
||||
|
||||
@@ -381,12 +381,20 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
|
||||
public void withExclusiveControl(Runnable r) {
|
||||
J.a(() -> {
|
||||
boolean acquired = false;
|
||||
try {
|
||||
loadLock.acquire(LOAD_LOCKS);
|
||||
acquired = true;
|
||||
r.run();
|
||||
loadLock.release(LOAD_LOCKS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
IrisLogging.reportError(e);
|
||||
} catch (Throwable e) {
|
||||
IrisLogging.reportError(e);
|
||||
} finally {
|
||||
if (acquired) {
|
||||
loadLock.release(LOAD_LOCKS);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -394,14 +402,21 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
public CompletableFuture<Void> withExclusiveControlFuture(Runnable r) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
J.a(() -> {
|
||||
boolean acquired = false;
|
||||
try {
|
||||
loadLock.acquire(LOAD_LOCKS);
|
||||
acquired = true;
|
||||
r.run();
|
||||
future.complete(null);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
future.completeExceptionally(e);
|
||||
} catch (Throwable e) {
|
||||
future.completeExceptionally(e);
|
||||
} finally {
|
||||
loadLock.release(LOAD_LOCKS);
|
||||
if (acquired) {
|
||||
loadLock.release(LOAD_LOCKS);
|
||||
}
|
||||
}
|
||||
});
|
||||
return future;
|
||||
|
||||
@@ -121,6 +121,10 @@ public class ChunkContext {
|
||||
return complex;
|
||||
}
|
||||
|
||||
public long getGenerationSessionId() {
|
||||
return generationSessionId;
|
||||
}
|
||||
|
||||
public ChunkedDoubleDataCache getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@@ -20,70 +20,55 @@ package art.arcane.iris.util.project.context;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.IrisComplex;
|
||||
import art.arcane.iris.engine.mantle.EngineMantle;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.volmlib.util.scheduling.ChronoLatch;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class IrisContext {
|
||||
private static final KMap<Thread, IrisContext> context = new KMap<>();
|
||||
private static final ChronoLatch cl = new ChronoLatch(60000);
|
||||
import java.util.Objects;
|
||||
|
||||
public final class IrisContext {
|
||||
private static final ThreadLocal<IrisContext> CONTEXT = new ThreadLocal<>();
|
||||
private final Engine engine;
|
||||
private ChunkContext chunkContext;
|
||||
private long generationSessionId;
|
||||
private final ChunkContext chunkContext;
|
||||
private final long generationSessionId;
|
||||
|
||||
public IrisContext(Engine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
public static IrisContext getOr(Engine engine) {
|
||||
IrisContext c = get();
|
||||
|
||||
if (c == null) {
|
||||
c = new IrisContext(engine);
|
||||
touch(c);
|
||||
}
|
||||
|
||||
return c;
|
||||
private IrisContext(Engine engine, long generationSessionId, ChunkContext chunkContext) {
|
||||
this.engine = Objects.requireNonNull(engine);
|
||||
this.generationSessionId = generationSessionId;
|
||||
this.chunkContext = chunkContext;
|
||||
}
|
||||
|
||||
public static IrisContext get() {
|
||||
return context.get(Thread.currentThread());
|
||||
return CONTEXT.get();
|
||||
}
|
||||
|
||||
public static void touch(IrisContext c) {
|
||||
context.put(Thread.currentThread(), c);
|
||||
|
||||
if (!cl.couldFlip()) return;
|
||||
synchronized (cl) {
|
||||
if (cl.flip()) {
|
||||
dereference();
|
||||
}
|
||||
public static IrisContext require() {
|
||||
IrisContext current = get();
|
||||
if (current == null) {
|
||||
throw new IllegalStateException("No Iris execution context is bound to thread " + Thread.currentThread().getName() + ".");
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
public static synchronized void dereference() {
|
||||
var it = context.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
var entry = it.next();
|
||||
var thread = entry.getKey();
|
||||
var context = entry.getValue();
|
||||
if (thread == null || context == null) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!thread.isAlive() || context.engine.isClosed()) {
|
||||
IrisLogging.debug("Dereferenced Context<Engine> " + thread.getName() + " " + thread.threadId());
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
public static Scope open(Engine engine, long generationSessionId, ChunkContext chunkContext) {
|
||||
Thread thread = Thread.currentThread();
|
||||
IrisContext previous = CONTEXT.get();
|
||||
IrisContext installed = new IrisContext(engine, generationSessionId, chunkContext);
|
||||
CONTEXT.set(installed);
|
||||
return new Scope(thread, previous, installed);
|
||||
}
|
||||
|
||||
public void touch() {
|
||||
IrisContext.touch(this);
|
||||
public Engine getEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
public ChunkContext getChunkContext() {
|
||||
return chunkContext;
|
||||
}
|
||||
|
||||
public long getGenerationSessionId() {
|
||||
return generationSessionId;
|
||||
}
|
||||
|
||||
public IrisData getData() {
|
||||
@@ -95,9 +80,9 @@ public class IrisContext {
|
||||
}
|
||||
|
||||
public KMap<String, Object> asContext() {
|
||||
var hash32 = engine.getHash32().getNow(null);
|
||||
var dimension = engine.getDimension();
|
||||
var mantle = engine.getMantle();
|
||||
Long hash32 = engine.getHash32().getNow(null);
|
||||
IrisDimension dimension = engine.getDimension();
|
||||
EngineMantle mantle = engine.getMantle();
|
||||
return new KMap<String, Object>()
|
||||
.qput("studio", engine.isStudio())
|
||||
.qput("closed", engine.isClosed())
|
||||
@@ -111,4 +96,37 @@ public class IrisContext {
|
||||
.qput("loaded", mantle.getLoadedRegionCount())
|
||||
.qput("queued", mantle.getUnloadRegionCount()));
|
||||
}
|
||||
|
||||
public static final class Scope implements AutoCloseable {
|
||||
private final Thread owner;
|
||||
private final IrisContext previous;
|
||||
private final IrisContext installed;
|
||||
private boolean closed;
|
||||
|
||||
private Scope(Thread owner, IrisContext previous, IrisContext installed) {
|
||||
this.owner = owner;
|
||||
this.previous = previous;
|
||||
this.installed = installed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
if (Thread.currentThread() != owner) {
|
||||
throw new IllegalStateException("Iris execution context scope closed from a different thread.");
|
||||
}
|
||||
IrisContext current = CONTEXT.get();
|
||||
if (current != installed) {
|
||||
throw new IllegalStateException("Iris execution context scopes must close in LIFO order.");
|
||||
}
|
||||
if (previous == null) {
|
||||
CONTEXT.remove();
|
||||
} else {
|
||||
CONTEXT.set(previous);
|
||||
}
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package art.arcane.iris.util.project.matter;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class IrisMatterContext {
|
||||
private static final ThreadLocal<IrisData> DATA = new ThreadLocal<>();
|
||||
|
||||
private IrisMatterContext() {
|
||||
}
|
||||
|
||||
public static Scope open(IrisData data) {
|
||||
Thread owner = Thread.currentThread();
|
||||
IrisData previous = DATA.get();
|
||||
IrisData installed = Objects.requireNonNull(data);
|
||||
DATA.set(installed);
|
||||
return new Scope(owner, previous, installed);
|
||||
}
|
||||
|
||||
public static IrisData require() {
|
||||
IrisData data = DATA.get();
|
||||
if (data == null) {
|
||||
throw new IllegalStateException("No Iris matter data context is bound to thread " + Thread.currentThread().getName() + ".");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static final class Scope implements AutoCloseable {
|
||||
private final Thread owner;
|
||||
private final IrisData previous;
|
||||
private final IrisData installed;
|
||||
private boolean closed;
|
||||
|
||||
private Scope(Thread owner, IrisData previous, IrisData installed) {
|
||||
this.owner = owner;
|
||||
this.previous = previous;
|
||||
this.installed = installed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
if (Thread.currentThread() != owner) {
|
||||
throw new IllegalStateException("Iris matter data scope closed from a different thread.");
|
||||
}
|
||||
if (DATA.get() != installed) {
|
||||
throw new IllegalStateException("Iris matter data scopes must close in LIFO order.");
|
||||
}
|
||||
if (previous == null) {
|
||||
DATA.remove();
|
||||
} else {
|
||||
DATA.set(previous);
|
||||
}
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,10 @@
|
||||
|
||||
package art.arcane.iris.util.project.matter.slices;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.loader.IrisRegistrant;
|
||||
import art.arcane.iris.util.project.context.IrisContext;
|
||||
import art.arcane.iris.core.loader.ResourceLoader;
|
||||
import art.arcane.iris.util.project.matter.IrisMatterContext;
|
||||
import art.arcane.volmlib.util.data.palette.Palette;
|
||||
import art.arcane.volmlib.util.matter.slices.RawMatter;
|
||||
|
||||
@@ -44,7 +46,12 @@ public class RegistryMatter<T extends IrisRegistrant> extends RawMatter<T> {
|
||||
|
||||
@Override
|
||||
public T readNode(DataInputStream din) throws IOException {
|
||||
IrisContext context = IrisContext.get();
|
||||
return (T) context.getData().getLoaders().get(getType()).load(din.readUTF());
|
||||
String key = din.readUTF();
|
||||
IrisData data = IrisMatterContext.require();
|
||||
ResourceLoader<? extends IrisRegistrant> loader = data.getLoaders().get(getType());
|
||||
if (loader == null) {
|
||||
throw new IOException("No Iris registry loader is available for matter type " + getType().getName() + " while reading key " + key + ".");
|
||||
}
|
||||
return (T) loader.load(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,10 @@ public interface ProceduralStream<T> extends ProceduralLayer, Interpolated<T> {
|
||||
return new ContextInjectingStream<>(this, contextAccessor);
|
||||
}
|
||||
|
||||
default ProceduralStream<T> contextInjecting(Engine engine, Function3<ChunkContext, Integer, Integer, T> contextAccessor) {
|
||||
return new ContextInjectingStream<>(this, engine, contextAccessor);
|
||||
}
|
||||
|
||||
default ProceduralStream<T> add(ProceduralStream<Double> a) {
|
||||
return add2D((x, z) -> a.get(x, z));
|
||||
}
|
||||
|
||||
+16
-2
@@ -2,14 +2,21 @@ package art.arcane.iris.util.project.stream.utility;
|
||||
|
||||
import art.arcane.iris.util.project.context.ChunkContext;
|
||||
import art.arcane.iris.util.project.context.IrisContext;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.volmlib.util.function.Function3;
|
||||
import art.arcane.iris.util.project.stream.BasicStream;
|
||||
import art.arcane.iris.util.project.stream.ProceduralStream;
|
||||
public class ContextInjectingStream<T> extends BasicStream<T> {
|
||||
private final Engine engine;
|
||||
private final Function3<ChunkContext, Integer, Integer, T> contextAccessor;
|
||||
|
||||
public ContextInjectingStream(ProceduralStream<T> stream, Function3<ChunkContext, Integer, Integer, T> contextAccessor) {
|
||||
this(stream, null, contextAccessor);
|
||||
}
|
||||
|
||||
public ContextInjectingStream(ProceduralStream<T> stream, Engine engine, Function3<ChunkContext, Integer, Integer, T> contextAccessor) {
|
||||
super(stream);
|
||||
this.engine = engine;
|
||||
this.contextAccessor = contextAccessor;
|
||||
}
|
||||
|
||||
@@ -19,9 +26,16 @@ public class ContextInjectingStream<T> extends BasicStream<T> {
|
||||
|
||||
if (context != null) {
|
||||
ChunkContext chunkContext = context.getChunkContext();
|
||||
int blockX = (int) x;
|
||||
int blockZ = (int) z;
|
||||
|
||||
if (chunkContext != null && (int) x >> 4 == chunkContext.getX() >> 4 && (int) z >> 4 == chunkContext.getZ() >> 4) {
|
||||
T t = contextAccessor.apply(chunkContext, (int) x & 15, (int) z & 15);
|
||||
if (chunkContext != null
|
||||
&& (engine == null || context.getEngine() == engine)
|
||||
&& context.getGenerationSessionId() == chunkContext.getGenerationSessionId()
|
||||
&& (engine == null || context.getGenerationSessionId() == engine.getGenerationSessionId())
|
||||
&& blockX >> 4 == chunkContext.getX() >> 4
|
||||
&& blockZ >> 4 == chunkContext.getZ() >> 4) {
|
||||
T t = contextAccessor.apply(chunkContext, blockX & 15, blockZ & 15);
|
||||
|
||||
if (t != null) {
|
||||
return t;
|
||||
|
||||
@@ -5,10 +5,14 @@ import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||
import art.arcane.volmlib.util.math.Position2;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisPregeneratorInitTest {
|
||||
@Test
|
||||
@@ -35,6 +39,44 @@ public class IrisPregeneratorInitTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completionSummaryWaitsForAsyncCloseDrain() {
|
||||
String output = runCompletionOnClose(false);
|
||||
|
||||
assertTrue(output.contains("Pregen finished: generated=9 total=9 failed=0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completionSummaryIncludesFailureDrainedOnClose() {
|
||||
String output = runCompletionOnClose(true);
|
||||
|
||||
assertTrue(output.contains("Pregen finished: generated=8 total=9 failed=1"));
|
||||
}
|
||||
|
||||
private String runCompletionOnClose(boolean failLast) {
|
||||
IrisSettings previousSettings = IrisSettings.settings;
|
||||
PrintStream previousOut = System.out;
|
||||
IrisSettings.settings = new IrisSettings();
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
|
||||
CompletionOnCloseMethod method = new CompletionOnCloseMethod(failLast);
|
||||
PregenTask task = PregenTask.builder()
|
||||
.center(new Position2(0, 0))
|
||||
.radiusX(1)
|
||||
.radiusZ(1)
|
||||
.build();
|
||||
try {
|
||||
IrisPregenerator pregenerator = new IrisPregenerator(task, method, new NoOpPregenListener());
|
||||
|
||||
pregenerator.start();
|
||||
|
||||
return output.toString(StandardCharsets.UTF_8);
|
||||
} finally {
|
||||
System.setOut(previousOut);
|
||||
IrisSettings.settings = previousSettings;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TrackingPregeneratorMethod implements PregeneratorMethod {
|
||||
private final AtomicInteger initCalls = new AtomicInteger();
|
||||
private final AtomicInteger saveCalls = new AtomicInteger();
|
||||
@@ -77,6 +119,60 @@ public class IrisPregeneratorInitTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CompletionOnCloseMethod implements PregeneratorMethod {
|
||||
private final boolean failLast;
|
||||
private int pending;
|
||||
private PregenListener listener;
|
||||
|
||||
private CompletionOnCloseMethod(boolean failLast) {
|
||||
this.failLast = failLast;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
for (int index = 0; index < pending; index++) {
|
||||
if (failLast && index == pending - 1) {
|
||||
listener.onChunkFailed(0, 0);
|
||||
} else {
|
||||
listener.onChunkGenerated(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRegions(int x, int z, PregenListener listener) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod(int x, int z) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateRegion(int x, int z, PregenListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateChunk(int x, int z, PregenListener listener) {
|
||||
pending++;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mantle getMantle() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NoOpPregenListener implements PregenListener {
|
||||
@Override
|
||||
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
|
||||
|
||||
+30
@@ -8,6 +8,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
public class PregenTaskInterleavedTraversalTest {
|
||||
@Test
|
||||
@@ -38,6 +39,35 @@ public class PregenTaskInterleavedTraversalTest {
|
||||
assertEquals(asSet(baseline), asSet(firstInterleaved));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockRadius352CoversExactly2025Chunks() {
|
||||
PregenTask task = PregenTask.builder()
|
||||
.center(new Position2(0, 0))
|
||||
.radiusX(352)
|
||||
.radiusZ(352)
|
||||
.build();
|
||||
KList<Long> chunks = new KList<>();
|
||||
|
||||
task.iterateAllChunks((x, z) -> chunks.add(asKey(x, z)));
|
||||
|
||||
assertEquals(2025, chunks.size());
|
||||
assertEquals(2025, asSet(chunks).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonpositiveRadiusIsRejected() {
|
||||
assertThrows(IllegalArgumentException.class, () -> PregenTask.builder()
|
||||
.center(new Position2(0, 0))
|
||||
.radiusX(0)
|
||||
.radiusZ(352)
|
||||
.build());
|
||||
assertThrows(IllegalArgumentException.class, () -> PregenTask.builder()
|
||||
.center(new Position2(0, 0))
|
||||
.radiusX(352)
|
||||
.radiusZ(-1)
|
||||
.build());
|
||||
}
|
||||
|
||||
private Set<Long> asSet(KList<Long> values) {
|
||||
Set<Long> set = new HashSet<>();
|
||||
for (Long value : values) {
|
||||
|
||||
+7
@@ -35,4 +35,11 @@ public class AsyncPregenMethodConcurrencyCapTest {
|
||||
assertEquals(32, AsyncPregenMethod.resolveFoliaConcurrencyWorkerThreads(4, 16, 32));
|
||||
assertEquals(16, AsyncPregenMethod.resolveFoliaConcurrencyWorkerThreads(-1, 16, 12));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void strictSerialOverridesRecommendedConcurrencyCap() {
|
||||
assertEquals(1, AsyncPregenMethod.selectConcurrencyCap(128, true));
|
||||
assertEquals(128, AsyncPregenMethod.selectConcurrencyCap(128, false));
|
||||
assertEquals(1, AsyncPregenMethod.selectConcurrencyCap(0, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ public class GoldenHashEngineTest {
|
||||
private PlatformBlockState stone;
|
||||
private PlatformBlockState dirt;
|
||||
private PlatformBiome plains;
|
||||
private PlatformBiome forest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -49,6 +50,8 @@ public class GoldenHashEngineTest {
|
||||
when(dirt.key()).thenReturn("minecraft:dirt");
|
||||
plains = mock(PlatformBiome.class);
|
||||
when(plains.key()).thenReturn("minecraft:plains");
|
||||
forest = mock(PlatformBiome.class);
|
||||
when(forest.key()).thenReturn("minecraft:forest");
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -80,6 +83,26 @@ public class GoldenHashEngineTest {
|
||||
assertEquals("#combined=" + referenceCombined(expectedBody), lines.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullBiomeUsesCanonicalPlainsFallback() throws Exception {
|
||||
RecordingFeedback feedback = new RecordingFeedback();
|
||||
GoldenHashEngine hashEngine = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.AUTO), goldenDir, source(dirt, 3, 2, 1, null), feedback, progress());
|
||||
|
||||
assertTrue(hashEngine.run());
|
||||
List<String> lines = Files.readAllLines(hashEngine.getGoldenFile().toPath(), StandardCharsets.UTF_8);
|
||||
assertEquals(referenceLine(0, 0, 3, 2, 1), lines.get(8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonNullBiomeKeyRemainsUnchanged() throws Exception {
|
||||
RecordingFeedback feedback = new RecordingFeedback();
|
||||
GoldenHashEngine hashEngine = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.AUTO), goldenDir, source(dirt, 3, 2, 1, forest), feedback, progress());
|
||||
|
||||
assertTrue(hashEngine.run());
|
||||
List<String> lines = Files.readAllLines(hashEngine.getGoldenFile().toPath(), StandardCharsets.UTF_8);
|
||||
assertEquals(referenceLine(0, 0, 3, 2, 1, "minecraft:forest"), lines.get(8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyMatchesGoldenCapture() {
|
||||
RecordingFeedback captureFeedback = new RecordingFeedback();
|
||||
@@ -130,7 +153,7 @@ public class GoldenHashEngineTest {
|
||||
}
|
||||
|
||||
private GoldenHashEngine.Request request(GoldenHashEngine.Mode mode) {
|
||||
return new GoldenHashEngine.Request("testworld", 1234L, "test-1.0", MIN_Y, MAX_Y, 0, 0, 0, 1, mode, false, false, "null");
|
||||
return new GoldenHashEngine.Request("testworld", 1234L, "test-1.0", MIN_Y, MAX_Y, 0, 0, 0, 1, mode, false, false, GoldenHashEngine.FALLBACK_BIOME_KEY);
|
||||
}
|
||||
|
||||
private GoldenHashEngine.Progress progress() {
|
||||
@@ -139,6 +162,10 @@ public class GoldenHashEngineTest {
|
||||
}
|
||||
|
||||
private GoldenHashEngine.ChunkSource source(PlatformBlockState special, int sx, int sy, int sz) {
|
||||
return source(special, sx, sy, sz, plains);
|
||||
}
|
||||
|
||||
private GoldenHashEngine.ChunkSource source(PlatformBlockState special, int sx, int sy, int sz, PlatformBiome biome) {
|
||||
return (int chunkX, int chunkZ) -> new GoldenHashEngine.ChunkSnapshot() {
|
||||
@Override
|
||||
public int minY() {
|
||||
@@ -157,12 +184,16 @@ public class GoldenHashEngineTest {
|
||||
|
||||
@Override
|
||||
public PlatformBiome biome(int x, int y, int z) {
|
||||
return plains;
|
||||
return biome;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private String referenceLine(int chunkX, int chunkZ, int sx, int sy, int sz) throws Exception {
|
||||
return referenceLine(chunkX, chunkZ, sx, sy, sz, "minecraft:plains");
|
||||
}
|
||||
|
||||
private String referenceLine(int chunkX, int chunkZ, int sx, int sy, int sz, String biomeKey) throws Exception {
|
||||
MessageDigest blockDigest = MessageDigest.getInstance("SHA-256");
|
||||
MessageDigest biomeDigest = MessageDigest.getInstance("SHA-256");
|
||||
for (int x = 0; x < 16; x++) {
|
||||
@@ -176,7 +207,7 @@ public class GoldenHashEngineTest {
|
||||
for (int x = 0; x < 16; x += 4) {
|
||||
for (int z = 0; z < 16; z += 4) {
|
||||
for (int y = MIN_Y; y < MAX_Y; y += 4) {
|
||||
biomeDigest.update("minecraft:plains\n".getBytes(StandardCharsets.UTF_8));
|
||||
biomeDigest.update((biomeKey + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package art.arcane.iris.core.splash;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class IrisSplashComposerTest {
|
||||
@Test
|
||||
public void composeInfoUsesCurrentVersionWithoutStaleReleaseTag() {
|
||||
String[] info = IrisSplashComposer.composeInfo("4.0.0-26.2", "Paper 26.2", IrisSplashComposer.InfoStyle.PLAIN);
|
||||
|
||||
assertEquals(" Iris, Dimension Engine [4.0]", info[1]);
|
||||
assertEquals(" Version: 4.0.0-26.2", info[2]);
|
||||
assertFalse(String.join("\n", info).contains("RC.1.1.6"));
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package art.arcane.iris.engine.framework.placer;
|
||||
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IObjectPlacer;
|
||||
import art.arcane.iris.engine.object.IrisObjectPlacement;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class HeightmapObjectPlacerTest {
|
||||
@Test
|
||||
public void engineOwnershipDelegatesWithoutAmbientContext() {
|
||||
Engine engine = mock(Engine.class);
|
||||
IObjectPlacer ownedDelegate = mock(IObjectPlacer.class);
|
||||
when(ownedDelegate.getEngine()).thenReturn(engine);
|
||||
HeightmapObjectPlacer owned = new HeightmapObjectPlacer(mock(RNG.class), 1, 2, 3, new IrisObjectPlacement(), ownedDelegate);
|
||||
assertSame(engine, owned.getEngine());
|
||||
|
||||
IObjectPlacer unownedDelegate = mock(IObjectPlacer.class);
|
||||
HeightmapObjectPlacer unowned = new HeightmapObjectPlacer(mock(RNG.class), 1, 2, 3, new IrisObjectPlacement(), unownedDelegate);
|
||||
assertNull(unowned.getEngine());
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
package art.arcane.iris.util.project.context;
|
||||
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class IrisContextIsolationTest {
|
||||
@Test
|
||||
public void scopedContextsIsolateStatePerThread() throws Exception {
|
||||
Engine engine = mock(Engine.class);
|
||||
ChunkContext leftChunk = mock(ChunkContext.class);
|
||||
ChunkContext rightChunk = mock(ChunkContext.class);
|
||||
CyclicBarrier writesComplete = new CyclicBarrier(2);
|
||||
AtomicReference<IrisContext> leftBound = new AtomicReference<>();
|
||||
AtomicReference<IrisContext> rightBound = new AtomicReference<>();
|
||||
AtomicReference<ChunkContext> leftObserved = new AtomicReference<>();
|
||||
AtomicReference<ChunkContext> rightObserved = new AtomicReference<>();
|
||||
AtomicReference<IrisContext> leftAfterClose = new AtomicReference<>();
|
||||
AtomicReference<IrisContext> rightAfterClose = new AtomicReference<>();
|
||||
AtomicReference<Throwable> failure = new AtomicReference<>();
|
||||
|
||||
Thread leftThread = new Thread(() -> exerciseContext(engine, 17L, leftChunk, writesComplete, leftBound, leftObserved, leftAfterClose, failure), "iris-context-left");
|
||||
Thread rightThread = new Thread(() -> exerciseContext(engine, 17L, rightChunk, writesComplete, rightBound, rightObserved, rightAfterClose, failure), "iris-context-right");
|
||||
leftThread.start();
|
||||
rightThread.start();
|
||||
leftThread.join(5000L);
|
||||
rightThread.join(5000L);
|
||||
|
||||
assertFalse("Left context worker did not finish.", leftThread.isAlive());
|
||||
assertFalse("Right context worker did not finish.", rightThread.isAlive());
|
||||
assertNull(failure.get());
|
||||
assertNotSame(leftBound.get(), rightBound.get());
|
||||
assertSame(leftChunk, leftObserved.get());
|
||||
assertSame(rightChunk, rightObserved.get());
|
||||
assertNull(leftAfterClose.get());
|
||||
assertNull(rightAfterClose.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scopeRebindsWorkerWhenEngineChanges() throws Exception {
|
||||
Engine firstEngine = mock(Engine.class);
|
||||
Engine secondEngine = mock(Engine.class);
|
||||
AtomicReference<IrisContext> first = new AtomicReference<>();
|
||||
AtomicReference<IrisContext> second = new AtomicReference<>();
|
||||
|
||||
Thread worker = new Thread(() -> {
|
||||
try (IrisContext.Scope firstScope = IrisContext.open(firstEngine, 1L, null)) {
|
||||
first.set(IrisContext.require());
|
||||
}
|
||||
try (IrisContext.Scope secondScope = IrisContext.open(secondEngine, 2L, null)) {
|
||||
second.set(IrisContext.require());
|
||||
}
|
||||
}, "iris-context-reused-worker");
|
||||
worker.start();
|
||||
worker.join(5000L);
|
||||
|
||||
assertFalse("Reused context worker did not finish.", worker.isAlive());
|
||||
assertSame(firstEngine, first.get().getEngine());
|
||||
assertSame(secondEngine, second.get().getEngine());
|
||||
assertNotSame(first.get(), second.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedScopeRestoresOuterContext() {
|
||||
Engine outerEngine = mock(Engine.class);
|
||||
Engine innerEngine = mock(Engine.class);
|
||||
ChunkContext outerChunk = mock(ChunkContext.class);
|
||||
ChunkContext innerChunk = mock(ChunkContext.class);
|
||||
|
||||
try (IrisContext.Scope outerScope = IrisContext.open(outerEngine, 11L, outerChunk)) {
|
||||
IrisContext outer = IrisContext.require();
|
||||
try (IrisContext.Scope innerScope = IrisContext.open(innerEngine, 12L, innerChunk)) {
|
||||
IrisContext inner = IrisContext.require();
|
||||
assertSame(innerEngine, inner.getEngine());
|
||||
assertSame(innerChunk, inner.getChunkContext());
|
||||
}
|
||||
assertSame(outer, IrisContext.require());
|
||||
}
|
||||
|
||||
assertNull(IrisContext.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedScopeRestoresOuterContextAfterEngineCloses() {
|
||||
Engine engine = mock(Engine.class);
|
||||
when(engine.isClosed()).thenReturn(false);
|
||||
|
||||
try (IrisContext.Scope outerScope = IrisContext.open(engine, 11L, null)) {
|
||||
IrisContext outer = IrisContext.require();
|
||||
when(engine.isClosed()).thenReturn(true);
|
||||
assertTrue(engine.isClosed());
|
||||
try (IrisContext.Scope innerScope = IrisContext.open(engine, 11L, null)) {
|
||||
assertNotSame(outer, IrisContext.require());
|
||||
}
|
||||
assertSame(outer, IrisContext.require());
|
||||
}
|
||||
|
||||
assertNull(IrisContext.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void closingScopesOutOfOrderFails() {
|
||||
Engine outerEngine = mock(Engine.class);
|
||||
Engine innerEngine = mock(Engine.class);
|
||||
IrisContext.Scope outerScope = IrisContext.open(outerEngine, 21L, null);
|
||||
IrisContext.Scope innerScope = IrisContext.open(innerEngine, 22L, null);
|
||||
|
||||
assertThrows(IllegalStateException.class, outerScope::close);
|
||||
innerScope.close();
|
||||
outerScope.close();
|
||||
assertNull(IrisContext.get());
|
||||
}
|
||||
|
||||
private void exerciseContext(
|
||||
Engine engine,
|
||||
long generationSessionId,
|
||||
ChunkContext chunk,
|
||||
CyclicBarrier writesComplete,
|
||||
AtomicReference<IrisContext> bound,
|
||||
AtomicReference<ChunkContext> observed,
|
||||
AtomicReference<IrisContext> afterClose,
|
||||
AtomicReference<Throwable> failure
|
||||
) {
|
||||
try {
|
||||
try (IrisContext.Scope scope = IrisContext.open(engine, generationSessionId, chunk)) {
|
||||
bound.set(IrisContext.require());
|
||||
await(writesComplete);
|
||||
observed.set(IrisContext.require().getChunkContext());
|
||||
}
|
||||
afterClose.set(IrisContext.get());
|
||||
} catch (Throwable e) {
|
||||
failure.compareAndSet(null, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void await(CyclicBarrier barrier) throws BrokenBarrierException, InterruptedException, TimeoutException {
|
||||
barrier.await(5L, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package art.arcane.iris.util.project.matter;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class IrisMatterContextTest {
|
||||
@Test
|
||||
public void nestedScopesRestoreAndClearData() {
|
||||
IrisData outerData = mock(IrisData.class);
|
||||
IrisData innerData = mock(IrisData.class);
|
||||
|
||||
assertThrows(IllegalStateException.class, IrisMatterContext::require);
|
||||
try (IrisMatterContext.Scope outerScope = IrisMatterContext.open(outerData)) {
|
||||
assertSame(outerData, IrisMatterContext.require());
|
||||
try (IrisMatterContext.Scope innerScope = IrisMatterContext.open(innerData)) {
|
||||
assertSame(innerData, IrisMatterContext.require());
|
||||
}
|
||||
assertSame(outerData, IrisMatterContext.require());
|
||||
}
|
||||
assertThrows(IllegalStateException.class, IrisMatterContext::require);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void closingScopesOutOfOrderFails() {
|
||||
IrisMatterContext.Scope outerScope = IrisMatterContext.open(mock(IrisData.class));
|
||||
IrisMatterContext.Scope innerScope = IrisMatterContext.open(mock(IrisData.class));
|
||||
|
||||
assertThrows(IllegalStateException.class, outerScope::close);
|
||||
innerScope.close();
|
||||
outerScope.close();
|
||||
assertThrows(IllegalStateException.class, IrisMatterContext::require);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package art.arcane.iris.util.project.matter;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.loader.IrisRegistrant;
|
||||
import art.arcane.iris.core.loader.ResourceLoader;
|
||||
import art.arcane.iris.engine.object.IrisSpawner;
|
||||
import art.arcane.iris.util.project.matter.slices.SpawnerMatter;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class RegistryMatterContextTest {
|
||||
@Test
|
||||
public void registrySliceResolvesThroughExplicitDataOnIoWorker() throws Exception {
|
||||
IrisSpawner firstResolved = new IrisSpawner();
|
||||
IrisSpawner secondResolved = new IrisSpawner();
|
||||
IrisData firstData = dataReturning(firstResolved);
|
||||
IrisData secondData = dataReturning(secondResolved);
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
try {
|
||||
Future<IrisSpawner> firstRead = executor.submit(() -> readSpawner(firstData));
|
||||
Future<IrisSpawner> secondRead = executor.submit(() -> readSpawner(secondData));
|
||||
assertSame(firstResolved, firstRead.get());
|
||||
assertSame(secondResolved, secondRead.get());
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private IrisSpawner readSpawner(IrisData data) throws Exception {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
try (DataOutputStream output = new DataOutputStream(bytes)) {
|
||||
output.writeUTF("test-spawner");
|
||||
}
|
||||
try (IrisMatterContext.Scope scope = IrisMatterContext.open(data);
|
||||
DataInputStream input = new DataInputStream(new ByteArrayInputStream(bytes.toByteArray()))) {
|
||||
return new SpawnerMatter().readNode(input);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private IrisData dataReturning(IrisSpawner resolved) {
|
||||
ResourceLoader<IrisSpawner> loader = mock(ResourceLoader.class);
|
||||
when(loader.load("test-spawner")).thenReturn(resolved);
|
||||
KMap<Class<? extends IrisRegistrant>, ResourceLoader<? extends IrisRegistrant>> loaders = new KMap<>();
|
||||
loaders.put(IrisSpawner.class, loader);
|
||||
IrisData data = mock(IrisData.class);
|
||||
when(data.getLoaders()).thenReturn(loaders);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package art.arcane.iris.util.project.stream.utility;
|
||||
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.util.project.context.ChunkContext;
|
||||
import art.arcane.iris.util.project.context.IrisContext;
|
||||
import art.arcane.iris.util.project.stream.ProceduralStream;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.anyDouble;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ContextInjectingStreamTest {
|
||||
@Test
|
||||
public void matchingOwnerSessionAndChunkUsesContextCache() {
|
||||
Engine engine = mock(Engine.class);
|
||||
when(engine.getGenerationSessionId()).thenReturn(41L);
|
||||
ChunkContext chunkContext = chunkContext(32, 48, 41L);
|
||||
ProceduralStream<String> source = sourceStream();
|
||||
ContextInjectingStream<String> stream = new ContextInjectingStream<>(source, engine,
|
||||
(context, x, z) -> "cache-" + x + "-" + z);
|
||||
|
||||
try (IrisContext.Scope scope = IrisContext.open(engine, 41L, chunkContext)) {
|
||||
assertEquals("cache-3-3", stream.get(35D, 51D));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mismatchedOwnerSessionChunkOrCoordinatesFallsBackToSource() {
|
||||
Engine engine = mock(Engine.class);
|
||||
Engine otherEngine = mock(Engine.class);
|
||||
when(engine.getGenerationSessionId()).thenReturn(41L);
|
||||
when(otherEngine.getGenerationSessionId()).thenReturn(41L);
|
||||
ProceduralStream<String> source = sourceStream();
|
||||
ContextInjectingStream<String> stream = new ContextInjectingStream<>(source, engine,
|
||||
(context, x, z) -> "cache");
|
||||
|
||||
try (IrisContext.Scope scope = IrisContext.open(otherEngine, 41L, chunkContext(32, 48, 41L))) {
|
||||
assertEquals("source", stream.get(35D, 51D));
|
||||
}
|
||||
try (IrisContext.Scope scope = IrisContext.open(engine, 40L, chunkContext(32, 48, 40L))) {
|
||||
assertEquals("source", stream.get(35D, 51D));
|
||||
}
|
||||
try (IrisContext.Scope scope = IrisContext.open(engine, 41L, chunkContext(32, 48, 41L))) {
|
||||
assertEquals("source", stream.get(64D, 80D));
|
||||
}
|
||||
try (IrisContext.Scope scope = IrisContext.open(engine, 41L, null)) {
|
||||
assertEquals("source", stream.get(35D, 51D));
|
||||
}
|
||||
}
|
||||
|
||||
private ChunkContext chunkContext(int x, int z, long generationSessionId) {
|
||||
ChunkContext context = mock(ChunkContext.class);
|
||||
when(context.getX()).thenReturn(x);
|
||||
when(context.getZ()).thenReturn(z);
|
||||
when(context.getGenerationSessionId()).thenReturn(generationSessionId);
|
||||
return context;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ProceduralStream<String> sourceStream() {
|
||||
ProceduralStream<String> source = mock(ProceduralStream.class);
|
||||
when(source.get(anyDouble(), anyDouble())).thenReturn("source");
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
# Minecraft Version Bump Checklist
|
||||
|
||||
`gradle.properties` `minecraftVersion` is the single source of truth for the target Minecraft
|
||||
version. Most build outputs derive from it. This document lists every edit required to move Iris
|
||||
to a new Minecraft version, in order.
|
||||
|
||||
## Source of truth
|
||||
|
||||
`gradle.properties`:
|
||||
|
||||
- `minecraftVersion` — target MC version (e.g. `26.2`). Drives the Bukkit plugin `api-version`,
|
||||
`BuildConstants.MINECRAFT_VERSION`, the `com.mojang:minecraft` coordinate, all mod-metadata
|
||||
minecraft ranges, and every dist/jar artifact name.
|
||||
- `fabricLoaderVersion` — Fabric Loader version.
|
||||
- `forgeVersion` — Forge version (`<mc>-<forge>`).
|
||||
- `neoForgeVersion` — NeoForge version.
|
||||
- `irisVersion` — bump the trailing `-<mc>` suffix to match (e.g. `4.0.0-26.2` -> `4.0.0-27.0`).
|
||||
|
||||
## Ordered steps
|
||||
|
||||
1. Edit `gradle.properties`: update `minecraftVersion`, `fabricLoaderVersion`, `forgeVersion`,
|
||||
`neoForgeVersion`, and the `irisVersion` suffix.
|
||||
|
||||
2. Edit `gradle/libs.versions.toml`:
|
||||
- `spigot` — the Spigot/Paper API pin used to compile against (`<mc>-R0.1-SNAPSHOT`).
|
||||
- `fabricApi-*` — the six Fabric API module versions, if the new MC requires different
|
||||
Fabric API builds. Each module is versioned independently (`<version>+<build-hash>`).
|
||||
|
||||
3. Edit `core/src/main/java/art/arcane/iris/core/nms/datapack/DataVersion.java` (manual, structural):
|
||||
- Append a new enum constant `V<major>_<minor>("<mc>", <packFormat>, <DataFixer>::new)`.
|
||||
- `packFormat` comes from https://minecraft.wiki/w/Pack_format.
|
||||
- `getLatest()` returns the last enum constant, so append; do not reorder.
|
||||
- Add a matching `IDataFixer` implementation under `core/src/main/java/art/arcane/iris/core/nms/datapack/`
|
||||
if the datapack format changed.
|
||||
|
||||
4. Register the new Bukkit NMS binding module:
|
||||
- `settings.gradle` — add `include(':adapters:bukkit:nms:v<major>_<minor>_R<rev>')`.
|
||||
- `build.gradle` — add the binding to the `nmsBindings` map:
|
||||
`v<major>_<minor>_R<rev>: '<spigot-nms-build-version>'` (e.g. `'26.2.build.25-alpha'`).
|
||||
- Create the binding sources under `adapters/bukkit/nms/v<major>_<minor>_R<rev>/`.
|
||||
|
||||
5. Update loader version-range metadata (manual floors/ranges only; the `minecraft` ranges are
|
||||
templated from `minecraftVersion` and need no edit):
|
||||
- `adapters/fabric/src/main/resources/fabric.mod.json` — `minecraft` is `~${minecraftVersion}`
|
||||
(auto). Update the `fabricloader` floor (`>=0.19.0`) if the loader minimum changes, and the
|
||||
`jars` list if the bundled Fabric API modules change.
|
||||
- `adapters/forge/src/main/resources/META-INF/mods.toml` — `minecraft` versionRange is
|
||||
`[${minecraftVersion}]` (auto). Update `loaderVersion` (`[64,)`) and the `forge` dependency
|
||||
versionRange for the new Forge line.
|
||||
- `adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml` — `minecraft` versionRange
|
||||
is `[${minecraftVersion}]` (auto). Update `loaderVersion` and the `neoforge` dependency range
|
||||
if the loader minimum changes.
|
||||
|
||||
6. Build and verify:
|
||||
- `./gradlew :core:check`
|
||||
- `./gradlew buildBukkit`
|
||||
- `./gradlew buildFabric`
|
||||
- `./gradlew buildForge`
|
||||
- `./gradlew buildNeoforge`
|
||||
|
||||
## Derived automatically (do not hand-edit on a version bump)
|
||||
|
||||
- Bukkit plugin `api-version` — `adapters/bukkit/plugin/build.gradle` reads `minecraftVersion`.
|
||||
- `BuildConstants.MINECRAFT_VERSION` — stamped by the `generateTemplates` task in
|
||||
`core/build.gradle` from `minecraftVersion`; consumed by `Tasks.supportedVersions`.
|
||||
- Mod-metadata `minecraft` version ranges — templated from `minecraftVersion` at `processResources`.
|
||||
- Dist/jar artifact names and the `com.mojang:minecraft` coordinate — composed from
|
||||
`minecraftVersion` in the build scripts.
|
||||
|
||||
## Notes
|
||||
|
||||
- `build.gradle`, the adapter `build.gradle` files, and `settings.gradle` carry `.getOrElse('26.2')`
|
||||
defensive defaults for the version properties. `gradle.properties` always overrides them, so a
|
||||
bump does not require touching those fallbacks; refresh them only if the checked-in default should
|
||||
track the current release.
|
||||
- The Java literal `"26.2"` intentionally remains in `DataVersion.java` (structural enum constant),
|
||||
`core/src/test/java/art/arcane/iris/core/nms/MinecraftVersionTest.java`, and
|
||||
`core/src/test/java/art/arcane/iris/core/lifecycle/PaperLibBootstrapTest.java`. The test files use
|
||||
MC version strings as parser fixtures, not as a version source; update them only when the version
|
||||
string formats they exercise change.
|
||||
@@ -1,78 +0,0 @@
|
||||
# Iris Release Checklist
|
||||
|
||||
Manual release procedure. There is no release automation by design: every step below is run by
|
||||
a person and verified by eye. Work top to bottom; do not skip the verify gates.
|
||||
|
||||
Reference values below assume the current `gradle.properties`: `irisVersion=4.0.0-26.2`,
|
||||
`minecraftVersion=26.2`, `fabricLoaderVersion=0.19.3`, `forgeVersion=26.2-65.0.0`,
|
||||
`neoForgeVersion=26.2.0.6-beta`. For a Minecraft version bump, do `docs/mc-version-bump.md` first,
|
||||
then start this checklist.
|
||||
|
||||
## a. Preflight
|
||||
|
||||
- [ ] Working tree clean on the exact commit you intend to tag (`git status` shows nothing to commit).
|
||||
- [ ] CI is green on that commit. The `verify` job (`.github/workflows/ci.yml`) runs
|
||||
`./gradlew :core:check :spi:build :probe:deserializationProbe` on JDK 25; the `build-artifacts`
|
||||
matrix builds all four platform jars. Do not release on a red or stale run.
|
||||
- [ ] `MasterChangelog.MD` Iris section is coherent: one consolidated entry set, deduplicated, no
|
||||
date-sliced headers, and it describes the current shipped state (not superseded intermediate work).
|
||||
- [ ] Version fields correct in `gradle.properties`: `irisVersion` is the release version and its
|
||||
trailing `-<mc>` suffix matches `minecraftVersion`. For a Minecraft bump, confirm every step in
|
||||
`docs/mc-version-bump.md` is done (loader ranges, `DataVersion`, NMS binding).
|
||||
- [ ] JDK 25 is the active toolchain locally (`java -version` reports 25).
|
||||
|
||||
## b. Build
|
||||
|
||||
- [ ] From the Iris project root: `./gradlew buildAll`
|
||||
- [ ] `dist/` contains the four platform jars (exact names for this release):
|
||||
- [ ] `Iris v4.0.0-26.2 [CraftBukkit] 26.2.jar` (Bukkit/Paper/Purpur/Spigot/Folia plugin)
|
||||
- [ ] `Iris v4.0.0-26.2 [Fabric] 26.2+0.19.3.jar`
|
||||
- [ ] `Iris v4.0.0-26.2 [Forge] 26.2+65.0.0.jar`
|
||||
- [ ] `Iris v4.0.0-26.2 [NeoForge] 26.2+26.2.0.6-beta.jar`
|
||||
- Naming pattern: `Iris v<irisVersion> [<Platform>] <mc>[+<loaderDisplay>].jar`.
|
||||
- [ ] The developer SPI jar is built by the same run at `spi/build/libs/iris-spi-4.0.0-26.2.jar`.
|
||||
It is the platform-API artifact for downstream developers and is not copied into `dist/`; it is
|
||||
not uploaded to the mod portals (see publish).
|
||||
- [ ] Each mod jar bundles core + spi + shaded libs; each plugin/mod jar is self-contained.
|
||||
|
||||
## c. Verify (release gates)
|
||||
|
||||
- [ ] `:core:check` and `:probe:deserializationProbe` passed in CI on the tag commit (a. covers this).
|
||||
- [ ] Golden-hash determinism VERIFY passes on all four platforms and matches the same hash:
|
||||
- [ ] Bukkit plugin: `/iris goldenhash verify <radius> <threads>`
|
||||
- [ ] Fabric mod: `/iris goldenhash verify <radius> <threads>`
|
||||
- [ ] Forge mod: `/iris goldenhash verify <radius> <threads>`
|
||||
- [ ] NeoForge mod: `/iris goldenhash verify <radius> <threads>`
|
||||
- The hash is interchangeable across platforms: all four MUST report identical output for the same
|
||||
pack and seed. Any mismatch blocks the release.
|
||||
- [ ] Live modded content-mod gate: on each loader, boot the mod jar alongside a real content mod
|
||||
(e.g. Create) and generate an Iris world. Confirm no load-time rejection, no class-loader crash,
|
||||
and that modded blocks/items/entities author and generate.
|
||||
- [ ] Fabric + content mod
|
||||
- [ ] Forge + content mod
|
||||
- [ ] NeoForge + content mod
|
||||
- [ ] Client-mod matrix: install the mod on the client (keybind `H` toggles the pregen HUD) and confirm:
|
||||
- [ ] Modded server + modded client: HUD receives pregen progress over `irisworldgen:main`.
|
||||
- [ ] Modded server + vanilla client: server generates normally; vanilla client is unaffected.
|
||||
- [ ] Paper (Bukkit) server + modded client: HUD receives pregen progress over vanilla plugin messaging.
|
||||
- [ ] Folia smoke: plugin loads and an Iris world generates on Folia.
|
||||
- [ ] Non-Iris server + modded client: client is inert, no errors.
|
||||
|
||||
## d. Publish (all manual, no automation)
|
||||
|
||||
- [ ] Modrinth: upload the three mod jars and the plugin jar. Tag loaders `fabric` / `forge` /
|
||||
`neoforge` on the mod files; mark the environment server + client; set game version 26.2.
|
||||
- [ ] CurseForge: upload the three mod jars with the matching loader tags and game version 26.2.
|
||||
- [ ] Existing plugin distribution channels: publish the plugin jar
|
||||
(`Iris v4.0.0-26.2 [CraftBukkit] 26.2.jar`) where the plugin already ships.
|
||||
- [ ] Sentry: add a release note / mark the release so incoming reports map to this version
|
||||
(the mod version string is the Sentry release tag).
|
||||
- [ ] Storepage / `listing.json` staleness review: check the listing copy for pre-4.0 content
|
||||
(Bukkit-only framing, old feature lists, screenshots). Flag anything stale for update before or
|
||||
right after launch. (Review only; this checklist does not change store copy.)
|
||||
|
||||
## e. Post
|
||||
|
||||
- [ ] Tag the release commit (`v<irisVersion>`) and push the tag. The `release-bundle` CI job
|
||||
(tag-gated, `refs/tags/v*`) runs `buildAll` and uploads the `dist/` bundle for the record.
|
||||
- [ ] Announce the release on the community channels once the portals show the new files live.
|
||||
@@ -28,3 +28,4 @@ minecraftVersion=26.2
|
||||
fabricLoaderVersion=0.19.3
|
||||
forgeVersion=26.2-65.0.0
|
||||
neoForgeVersion=26.2.0.6-beta
|
||||
volmLibCoordinate=com.github.VolmitSoftware:VolmLib:cdf7e8bb880199734dacd386a6616bda799fcde1
|
||||
|
||||
+1
-2
@@ -10,14 +10,13 @@ application {
|
||||
}
|
||||
|
||||
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:cdf7e8bb880199734dacd386a6616bda799fcde1')
|
||||
.get()
|
||||
|
||||
dependencies {
|
||||
implementation(project(':core'))
|
||||
compileOnly(libs.spigot)
|
||||
compileOnly(volmLibCoordinate) {
|
||||
changing = true
|
||||
transitive = false
|
||||
}
|
||||
runtimeOnly(libs.paralithic)
|
||||
|
||||
Reference in New Issue
Block a user