This commit is contained in:
Brian Neumann-Fopiano
2026-06-10 21:21:18 -04:00
parent c6c02a9040
commit 58d24ca55e
13 changed files with 115 additions and 110 deletions
+1 -1
View File
@@ -1 +1 @@
-1504154581
748295809
@@ -21,6 +21,7 @@ package art.arcane.iris.core.commands;
import art.arcane.iris.Iris;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.IrisWorlds;
import art.arcane.iris.core.ServerConfigurator;
import art.arcane.iris.core.lifecycle.WorldLifecycleService;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.service.StudioSVC;
@@ -468,6 +469,7 @@ public class CommandIris implements DirectorExecutor {
} else {
Iris.service(StudioSVC.class).downloadSearch(sender(), "IrisDimensions/" + pack + "/" + branch, overwrite);
}
ServerConfigurator.installDataPacksIfChanged(true);
}
@Director(description = "Get metrics for your world", aliases = "measure", origin = DirectorOrigin.PLAYER)
@@ -100,7 +100,7 @@ public class CommandStudio implements DirectorExecutor {
@Director(description = "Open a new studio world", aliases = "o", sync = true)
public void open(
@Param(defaultValue = "default", description = "The dimension to open a studio for", aliases = "dim", customHandler = DimensionHandler.class)
@Param(description = "The dimension pack to open a studio for", aliases = "dim", customHandler = DimensionHandler.class)
IrisDimension dimension,
@Param(defaultValue = "1337", description = "The seed to generate the studio with", aliases = "s")
long seed) {
@@ -186,7 +186,7 @@ public class CommandStudio implements DirectorExecutor {
@Director(description = "Create a new studio project", aliases = "+", sync = true)
public void create(
@Param(description = "The name of this new Iris Project.")
@Param(description = "The name of this new Iris Project.", defaultValue = "studio")
String name,
@Param(
description = "Copy the contents of an existing project in your packs folder and use it as a template in this new project.",
@@ -194,10 +194,19 @@ public class CommandStudio implements DirectorExecutor {
customHandler = NullableDimensionHandler.class
)
IrisDimension template) {
String projectName = name;
if (name.equals("studio")) {
File workspace = Iris.service(StudioSVC.class).getWorkspaceFolder();
int suffix = 2;
while (new File(workspace, projectName).exists()) {
projectName = "studio" + suffix++;
}
}
if (template != null) {
Iris.service(StudioSVC.class).create(sender(), name, template.getLoadKey());
Iris.service(StudioSVC.class).create(sender(), projectName, template.getLoadKey());
} else {
Iris.service(StudioSVC.class).create(sender(), name);
Iris.service(StudioSVC.class).create(sender(), projectName);
}
}
@@ -61,7 +61,7 @@ public enum Mode {
}
public void splash() {
String padd = Form.repeat(" ", 8);
String padd = Form.repeat(" ", 4);
String padd2 = Form.repeat(" ", 4);
String version = Iris.instance.getDescription().getVersion();
String releaseTrain = getReleaseTrain(version);
@@ -98,10 +98,8 @@ public enum Mode {
double headThickness = 3.7 + random.nextDouble() * 0.6;
double tipReach = 19.0 + random.nextDouble() * 4.0;
double notchX = 3.5 + random.nextDouble() * 1.5;
double streamReach = 22.0 + random.nextDouble() * 3.0;
double[][][] spines = splashFinSpines(tipReach);
double[][][] voids = splashNotchVoids(notchX);
double[][][] streams = splashStreamSpines(notchX, streamReach);
String[] lines = new String[SPLASH_HEIGHT];
for (int y = 0; y < SPLASH_HEIGHT; y++) {
StringBuilder line = new StringBuilder();
@@ -109,7 +107,7 @@ public enum Mode {
for (int x = 0; x < SPLASH_WIDTH; x++) {
char glyph = splashEyeGlyph(x, y);
if (glyph == 0) {
glyph = splashFinGlyph(x, y, spines, voids, streams, headThickness);
glyph = splashFinGlyph(x, y, spines, voids, headThickness);
}
if (glyph == 0 || glyph == ' ') {
line.append(' ');
@@ -160,7 +158,7 @@ public enum Mode {
return 0;
}
private char splashFinGlyph(int x, int y, double[][][] spines, double[][][] voids, double[][][] streams, double headThickness) {
private char splashFinGlyph(int x, int y, double[][][] spines, double[][][] voids, double headThickness) {
if (splashEyeRadius(x, y) <= 1.26) {
return 0;
}
@@ -181,7 +179,6 @@ public enum Mode {
}
double best = Double.MAX_VALUE;
double bestT = 0.0;
double bestSpineY = 0.0;
for (double[][] spine : spines) {
for (int i = 0; i < spine.length; i++) {
double t = i / (double) (spine.length - 1);
@@ -192,7 +189,6 @@ public enum Mode {
if (distance < best) {
best = distance;
bestT = t;
bestSpineY = spine[i][1];
}
}
}
@@ -200,41 +196,20 @@ public enum Mode {
+ Math.max(0.0, 3.0 - Math.min(x, SPLASH_WIDTH - 1 - x)) * 0.12
+ Math.max(0.0, 0.10 - bestT) * 1.5
+ Math.max(0.0, 1.30 - voidDistance) * 0.15;
if (shade <= 1.0) {
int level = shade <= 0.70 ? 0 : shade <= 0.90 ? 1 : 2;
if (bestT > 0.97) {
level += 2;
} else if (bestT > 0.88) {
level += 1;
}
return switch (Math.min(level, 2)) {
case 0 -> '#';
case 1 -> '=';
default -> '-';
};
if (shade > 1.0) {
return 0;
}
if (bestT < 0.25 && Math.abs(bestSpineY - SPLASH_CENTER_Y) > 2.5 && shade <= 1.25) {
return '-';
int level = shade <= 0.70 ? 0 : shade <= 0.90 ? 1 : 2;
if (bestT > 0.97) {
level += 2;
} else if (bestT > 0.88) {
level += 1;
}
if (bestT > 0.88 && shade <= 1.45) {
return '-';
}
double streamBest = Double.MAX_VALUE;
for (double[][] stream : streams) {
for (double[] sample : stream) {
double thickness = Math.max(0.4, 0.85 - 0.4 * sample[2]);
double dx = (x - sample[0]) / SPLASH_ASPECT;
double dy = y - sample[1];
double distance = Math.sqrt(dx * dx + dy * dy) / thickness;
if (distance < streamBest) {
streamBest = distance;
}
}
}
if (streamBest <= 1.0) {
return '-';
}
return 0;
return switch (Math.min(level, 2)) {
case 0 -> '#';
case 1 -> '=';
default -> '-';
};
}
private double splashEyeRadius(int x, int y) {
@@ -265,26 +240,6 @@ public enum Mode {
return new double[][][]{left, right};
}
private double[][][] splashStreamSpines(double notchX, double streamReach) {
double[][] control = {{notchX + 2.5, 10.2}, {13.0, 10.4}, {streamReach, 10.9}};
int samples = 41;
double[][] left = new double[samples][3];
double[][] right = new double[samples][3];
for (int i = 0; i < samples; i++) {
double t = i / (double) (samples - 1);
double u = 1.0 - t;
double bx = u * u * control[0][0] + 2.0 * u * t * control[1][0] + t * t * control[2][0];
double by = u * u * control[0][1] + 2.0 * u * t * control[1][1] + t * t * control[2][1];
left[i][0] = bx;
left[i][1] = by;
left[i][2] = t;
right[i][0] = (SPLASH_WIDTH - 1.0) - bx;
right[i][1] = (SPLASH_HEIGHT - 1.0) - by;
right[i][2] = t;
}
return new double[][][]{left, right};
}
private double[][][] splashFinSpines(double tipReach) {
double[][] control = {{-1.5, 9.8}, {0.4, 4.4}, {5.2, 1.0}, {tipReach, 0.4}};
int samples = 61;
@@ -107,14 +107,6 @@ public final class Tasks {
});
private static final Task INJECTION = Task.of("injection", () -> {
if (!isPaperPreferredServer() && !Agent.isInstalled()) {
return withDiagnostics(Mode.WARNING,
Diagnostic.Logger.WARN.create("Java Agent"),
Diagnostic.Logger.WARN.create("- Skipping dynamic Java agent attach on Spigot/Bukkit to avoid runtime agent warnings."),
Diagnostic.Logger.WARN.create("- For full runtime injection support, run with -javaagent:"
+ Agent.AGENT_JAR.getPath() + " or use Canvas/Folia/Paper/Purpur."));
}
if (!Agent.install()) {
return withDiagnostics(Mode.UNSTABLE,
Diagnostic.Logger.ERROR.create("Java Agent"),
@@ -193,15 +185,6 @@ public final class Tasks {
return Bukkit.getServer();
}
private static boolean isPaperPreferredServer() {
String name = server().getName().toLowerCase(Locale.ROOT);
return isCanvasServer()
|| name.contains("folia")
|| name.contains("paper")
|| name.contains("purpur")
|| name.contains("pufferfish");
}
private static boolean isCanvasServer() {
ClassLoader loader = server().getClass().getClassLoader();
try {
@@ -73,6 +73,7 @@ public class StudioSVC implements IrisService {
if (pack.equals("overworld")) {
Iris.info("Downloading Default Pack " + pack + " (latest on master)");
Iris.service(StudioSVC.class).downloadBranch(Iris.getSender(), "IrisDimensions/overworld", "master", false);
ServerConfigurator.installDataPacksIfChanged(true);
} else {
Iris.warn("Default pack '" + pack + "' is not installed. Please download it manually with /iris download " + pack);
}
@@ -223,6 +223,10 @@ public class IrisCreator {
if (J.isFolia() && containsCreateWorldUnsupportedOperation(e)) {
throw new IrisException("Runtime world creation is blocked and the selected world lifecycle backend could not create the world.", e);
}
if (containsMissingDimensionTypes(e)) {
throw new IrisException("The dimension types for pack \"" + dimension() + "\" are not loaded on this server yet. "
+ "Iris installed its datapack files - restart the server, then run the command again.", e);
}
throw new IrisException("Failed to create world with backend family " + WorldLifecycleService.get().capabilities().serverFamily().id() + "!", e);
}
@@ -389,6 +393,17 @@ public class IrisCreator {
return false;
}
private static boolean containsMissingDimensionTypes(Throwable throwable) {
Throwable cursor = throwable;
while (cursor != null) {
if (cursor instanceof IllegalStateException && String.valueOf(cursor.getMessage()).contains("Missing dimension types")) {
return true;
}
cursor = cursor.getCause();
}
return false;
}
private void addToBukkitYml() {
YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML);
String gen = "Iris:" + dimension;
@@ -60,6 +60,13 @@ public class CustomBiomeSource extends BiomeSource {
}
for (IrisBiome i : engine.getAllBiomes()) {
Holder<Biome> vanillaHolder = NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative());
if (vanillaHolder != null) {
biomes.add(vanillaHolder);
} else if (!i.isCustom() && fallback != null) {
biomes.add(fallback);
}
if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
Holder<Biome> customHolder = resolveCustomBiomeHolder(customRegistry, engine, j.getId());
@@ -69,13 +76,6 @@ public class CustomBiomeSource extends BiomeSource {
biomes.add(fallback);
}
}
} else {
Holder<Biome> vanillaHolder = NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative());
if (vanillaHolder != null) {
biomes.add(vanillaHolder);
} else if (fallback != null) {
biomes.add(fallback);
}
}
}
@@ -219,15 +219,15 @@ public class CustomBiomeSource extends BiomeSource {
return getFallbackBiome();
}
if (resolution.irisBiome.isCustom()) {
return resolveCustomHolder(resolution);
}
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, resolution.irisBiome.getVanillaDerivative());
if (holder != null) {
return holder;
}
if (resolution.irisBiome.isCustom()) {
return resolveCustomHolder(resolution);
}
return getFallbackBiome();
}
@@ -58,7 +58,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
}
private IrisChunkGenerator(ChunkGenerator delegate, Engine engine, World world, CustomBiomeSource customBiomeSource) {
super(((CraftWorld) world).getHandle(), edit(delegate, customBiomeSource), null);
super(((CraftWorld) world).getHandle(), edit(delegate, customBiomeSource), world.getGenerator());
this.delegate = delegate;
this.engine = engine;
this.customBiomeSource = customBiomeSource;
@@ -105,6 +105,8 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
try {
return delegate.findNearestMapStructure(level, reachable, pos, radius, findUnexplored);
} catch (Throwable e) {
Iris.error("Vanilla structure locate failed near " + pos.getX() + ", " + pos.getZ() + ": " + e);
Iris.reportError(e);
return null;
}
}
@@ -159,7 +161,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
if (!importedControl().active()) {
return;
}
delegate.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey);
super.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey);
}
private IrisImportedStructureControl importedControl() {
@@ -61,6 +61,7 @@ import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.levelgen.structure.StructureCheck;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.bukkit.*;
import org.bukkit.block.Biome;
@@ -624,11 +625,28 @@ public class NMSBinding implements INMSBinding {
Iris.error("Loaded world %s with unknown dimension type! expected=%s", world.getName(), expectedDimensionType);
}
IrisChunkGenerator irisGenerator = new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world);
var newContext = new WorldGenContext(
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
worldGenContext.level(), irisGenerator,
worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener());
worldGenContextField.set(chunkMap, newContext);
retargetStructureCheck(((CraftWorld) world).getHandle(), irisGenerator);
}
private static void retargetStructureCheck(ServerLevel level, IrisChunkGenerator generator) throws NoSuchFieldException, IllegalAccessException {
Field structureCheckField = getField(level.getClass(), StructureCheck.class);
structureCheckField.setAccessible(true);
Object structureCheck = structureCheckField.get(level);
if (structureCheck == null) {
return;
}
Field generatorField = getField(structureCheck.getClass(), net.minecraft.world.level.chunk.ChunkGenerator.class);
generatorField.setAccessible(true);
generatorField.set(structureCheck, generator);
Field biomeSourceField = getField(structureCheck.getClass(), BiomeSource.class);
biomeSourceField.setAccessible(true);
biomeSourceField.set(structureCheck, generator.getBiomeSource());
}
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
@@ -60,6 +60,13 @@ public class CustomBiomeSource extends BiomeSource {
}
for (IrisBiome i : engine.getAllBiomes()) {
Holder<Biome> vanillaHolder = NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative());
if (vanillaHolder != null) {
biomes.add(vanillaHolder);
} else if (!i.isCustom() && fallback != null) {
biomes.add(fallback);
}
if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
Holder<Biome> customHolder = resolveCustomBiomeHolder(customRegistry, engine, j.getId());
@@ -69,13 +76,6 @@ public class CustomBiomeSource extends BiomeSource {
biomes.add(fallback);
}
}
} else {
Holder<Biome> vanillaHolder = NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative());
if (vanillaHolder != null) {
biomes.add(vanillaHolder);
} else if (fallback != null) {
biomes.add(fallback);
}
}
}
@@ -219,15 +219,15 @@ public class CustomBiomeSource extends BiomeSource {
return getFallbackBiome();
}
if (resolution.irisBiome.isCustom()) {
return resolveCustomHolder(resolution);
}
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, resolution.irisBiome.getVanillaDerivative());
if (holder != null) {
return holder;
}
if (resolution.irisBiome.isCustom()) {
return resolveCustomHolder(resolution);
}
return getFallbackBiome();
}
@@ -58,7 +58,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
}
private IrisChunkGenerator(ChunkGenerator delegate, Engine engine, World world, CustomBiomeSource customBiomeSource) {
super(((CraftWorld) world).getHandle(), edit(delegate, customBiomeSource), null);
super(((CraftWorld) world).getHandle(), edit(delegate, customBiomeSource), world.getGenerator());
this.delegate = delegate;
this.engine = engine;
this.customBiomeSource = customBiomeSource;
@@ -105,6 +105,8 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
try {
return delegate.findNearestMapStructure(level, reachable, pos, radius, findUnexplored);
} catch (Throwable e) {
Iris.error("Vanilla structure locate failed near " + pos.getX() + ", " + pos.getZ() + ": " + e);
Iris.reportError(e);
return null;
}
}
@@ -159,7 +161,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
if (!importedControl().active()) {
return;
}
delegate.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey);
super.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey);
}
private IrisImportedStructureControl importedControl() {
@@ -65,6 +65,7 @@ import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
import net.minecraft.world.level.levelgen.structure.StructureCheck;
import net.minecraft.world.level.levelgen.feature.AbstractHugeMushroomFeature;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.FallenTreeFeature;
@@ -853,11 +854,28 @@ public class NMSBinding implements INMSBinding {
Iris.error("Loaded world %s with unknown dimension type! expected=%s", world.getName(), expectedDimensionType);
}
IrisChunkGenerator irisGenerator = new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world);
var newContext = new WorldGenContext(
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
worldGenContext.level(), irisGenerator,
worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener());
worldGenContextField.set(chunkMap, newContext);
retargetStructureCheck(((CraftWorld) world).getHandle(), irisGenerator);
}
private static void retargetStructureCheck(ServerLevel level, IrisChunkGenerator generator) throws NoSuchFieldException, IllegalAccessException {
Field structureCheckField = getField(level.getClass(), StructureCheck.class);
structureCheckField.setAccessible(true);
Object structureCheck = structureCheckField.get(level);
if (structureCheck == null) {
return;
}
Field generatorField = getField(structureCheck.getClass(), net.minecraft.world.level.chunk.ChunkGenerator.class);
generatorField.setAccessible(true);
generatorField.set(structureCheck, generator);
Field biomeSourceField = getField(structureCheck.getClass(), BiomeSource.class);
biomeSourceField.setAccessible(true);
biomeSourceField.set(structureCheck, generator.getBiomeSource());
}
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {