mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-12 03:24:14 +00:00
Adapter and papi fixes
This commit is contained in:
@@ -184,6 +184,19 @@ returns `---`. A real zero returns `0`.
|
||||
| `%iris_world.available%` | `true` when the reading player is in an Iris world and a reading exists |
|
||||
| `%iris_world.biome%` | Surface biome display name at the player, e.g. `Hot Desert Dunes` |
|
||||
| `%iris_world.biome-key%` | Surface biome load key, e.g. `desert/hot-dunes` |
|
||||
| `%iris_world.biome-custom%` | `true` when the surface biome defines custom derivatives, otherwise `false` |
|
||||
| `%iris_world.biome-custom-id%` | Authored `customDerivitives[].id` when exactly one derivative is defined, for example `golden-dunes`; `---` for none or multiple |
|
||||
| `%iris_world.biome-custom-ids%` | All authored custom derivative IDs, comma-and-space separated in definition order; `---` for none |
|
||||
| `%iris_world.biome-custom-key%` | Namespaced Minecraft registry key for the sole custom derivative, such as `iris:biomes/<hash>`; `---` for none, multiple, or unavailable mapping |
|
||||
| `%iris_world.biome-custom-keys%` | Registry keys for all custom derivatives in definition order, comma-and-space separated; `---` for none or any unavailable mapping |
|
||||
| `%iris_world.biome-custom-count%` | Number of authored custom derivatives; `0` for a vanilla-only biome |
|
||||
| `%iris_world.biome-derivative%` | Configured base derivative registry key, for example `minecraft:desert` |
|
||||
| `%iris_world.biome-vanilla-derivative%` | Effective vanilla derivative registry key used when a vanilla biome is required |
|
||||
| `%iris_world.biome-type%` | Inferred biome category: `land`, `sea`, `shore`, or `cave`; `---` when unavailable |
|
||||
| `%iris_world.min-height%` | World minimum absolute Y, inclusive |
|
||||
| `%iris_world.max-height%` | World maximum absolute Y, exclusive |
|
||||
| `%iris_world.height%` | World vertical span, equal to maximum minus minimum height |
|
||||
| `%iris_world.fluid-height%` | Dimension fluid level in absolute world Y; this is configuration, not a measurement of water at the player |
|
||||
| `%iris_world.region%` | Region display name at the player |
|
||||
| `%iris_world.region-key%` | Region load key |
|
||||
| `%iris_world.dimension%` | Dimension (pack) load key of the player's world |
|
||||
@@ -197,7 +210,9 @@ returns `---`. A real zero returns `0`.
|
||||
| `%iris_pregen.chunks-per-second%` | Current rate |
|
||||
| `%iris_pregen.paused%` | `true` while the job is paused |
|
||||
|
||||
The world values are the surface reading at the player's block column. Walking refreshes them at most
|
||||
Custom derivative IDs are authored metadata. A singular ID or registry key is available only when the biome defines one derivative; plural keys list all definitions. Saved columns retain their historical biome definitions.
|
||||
|
||||
Biome and region values describe the surface at the player's block column. Height values describe world configuration. Walking refreshes them at most
|
||||
once per second per player, so a whole board of `world.*` keys costs one refresh per player per
|
||||
second no matter how many of them are on it, and a value may lag a sprinting player by up to a
|
||||
second. A jump that is not walking - joining, respawning, changing worlds, stepping through a portal,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package art.arcane.iris.api.terrain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record IrisBiomeInfo(
|
||||
String key,
|
||||
String name,
|
||||
String regionKey,
|
||||
String regionName,
|
||||
String derivativeKey,
|
||||
String vanillaDerivativeKey,
|
||||
String type,
|
||||
List<IrisCustomBiomeInfo> customDerivatives) {
|
||||
public IrisBiomeInfo {
|
||||
key = key == null ? "" : key;
|
||||
name = name == null ? "" : name;
|
||||
regionKey = regionKey == null ? "" : regionKey;
|
||||
regionName = regionName == null ? "" : regionName;
|
||||
derivativeKey = derivativeKey == null ? "" : derivativeKey;
|
||||
vanillaDerivativeKey = vanillaDerivativeKey == null ? "" : vanillaDerivativeKey;
|
||||
type = type == null ? "" : type;
|
||||
customDerivatives = List.copyOf(customDerivatives);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package art.arcane.iris.api.terrain;
|
||||
|
||||
public record IrisCustomBiomeInfo(String id, String registryKey) {
|
||||
public IrisCustomBiomeInfo {
|
||||
id = id == null ? "" : id;
|
||||
registryKey = registryKey == null ? "" : registryKey;
|
||||
}
|
||||
}
|
||||
+2
@@ -18,6 +18,8 @@ public interface IrisTerrainService {
|
||||
|
||||
Optional<String> surfaceBiomeName(World world, int blockX, int blockZ);
|
||||
|
||||
Optional<IrisBiomeInfo> surfaceBiomeInfo(World world, int blockX, int blockZ);
|
||||
|
||||
Optional<String> biomeKey(World world, int blockX, int blockY, int blockZ);
|
||||
|
||||
Optional<String> regionKey(World world, int blockX, int blockZ);
|
||||
|
||||
+13
@@ -42,6 +42,19 @@ public final class IrisPapiExpansion extends VolmitPlaceholderExpansion {
|
||||
.key("world.available", state::worldAvailable)
|
||||
.key("world.biome", state::biome)
|
||||
.key("world.biome-key", state::biomeKey)
|
||||
.key("world.biome-custom", state::biomeCustom)
|
||||
.key("world.biome-custom-id", state::biomeCustomId)
|
||||
.key("world.biome-custom-ids", state::biomeCustomIds)
|
||||
.key("world.biome-custom-key", state::biomeCustomKey)
|
||||
.key("world.biome-custom-keys", state::biomeCustomKeys)
|
||||
.key("world.biome-custom-count", state::biomeCustomCount)
|
||||
.key("world.biome-derivative", state::biomeDerivative)
|
||||
.key("world.biome-vanilla-derivative", state::biomeVanillaDerivative)
|
||||
.key("world.biome-type", state::biomeType)
|
||||
.key("world.min-height", state::minHeight)
|
||||
.key("world.max-height", state::maxHeight)
|
||||
.key("world.height", state::height)
|
||||
.key("world.fluid-height", state::fluidHeight)
|
||||
.key("world.region", state::region)
|
||||
.key("world.region-key", state::regionKey)
|
||||
.key("world.dimension", state::dimension)
|
||||
|
||||
+67
-6
@@ -3,7 +3,6 @@ package art.arcane.iris.integration;
|
||||
import art.arcane.iris.api.pregen.IrisPregenPhase;
|
||||
import art.arcane.iris.api.pregen.IrisPregenProgress;
|
||||
import art.arcane.iris.api.terrain.IrisTerrainService;
|
||||
import art.arcane.iris.api.terrain.IrisWorldInfo;
|
||||
import art.arcane.volmlib.util.bukkit.papi.PlaceholderSnapshot;
|
||||
import art.arcane.volmlib.util.bukkit.papi.PlaceholderValues;
|
||||
import art.arcane.volmlib.util.bukkit.papi.PlayerSnapshotStore;
|
||||
@@ -103,6 +102,71 @@ public final class IrisPapiState {
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.biomeKey();
|
||||
}
|
||||
|
||||
public String biomeCustom(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().custom();
|
||||
}
|
||||
|
||||
public String biomeCustomId(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().customId();
|
||||
}
|
||||
|
||||
public String biomeCustomIds(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().customIds();
|
||||
}
|
||||
|
||||
public String biomeCustomKey(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().customKey();
|
||||
}
|
||||
|
||||
public String biomeCustomKeys(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().customKeys();
|
||||
}
|
||||
|
||||
public String biomeCustomCount(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().customCount();
|
||||
}
|
||||
|
||||
public String biomeDerivative(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().derivative();
|
||||
}
|
||||
|
||||
public String biomeVanillaDerivative(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().vanillaDerivative();
|
||||
}
|
||||
|
||||
public String biomeType(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.details().type();
|
||||
}
|
||||
|
||||
public String minHeight(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.heights().minimum();
|
||||
}
|
||||
|
||||
public String maxHeight(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.heights().maximum();
|
||||
}
|
||||
|
||||
public String height(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.heights().height();
|
||||
}
|
||||
|
||||
public String fluidHeight(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.heights().fluid();
|
||||
}
|
||||
|
||||
public String region(UUID playerId) {
|
||||
IrisPapiWorldView view = viewOf(playerId);
|
||||
return view == null ? PlaceholderValues.UNAVAILABLE : view.region();
|
||||
@@ -195,10 +259,7 @@ public final class IrisPapiState {
|
||||
return IrisPapiWorldView.present(
|
||||
position,
|
||||
now,
|
||||
service.surfaceBiomeName(world, blockX, blockZ),
|
||||
service.surfaceBiomeKey(world, blockX, blockZ),
|
||||
service.regionName(world, blockX, blockZ),
|
||||
service.regionKey(world, blockX, blockZ),
|
||||
service.worldInfo(world).map(IrisWorldInfo::dimensionKey));
|
||||
service.surfaceBiomeInfo(world, blockX, blockZ),
|
||||
service.worldInfo(world));
|
||||
}
|
||||
}
|
||||
|
||||
+72
-12
@@ -1,8 +1,13 @@
|
||||
package art.arcane.iris.integration;
|
||||
|
||||
import art.arcane.iris.api.terrain.IrisBiomeInfo;
|
||||
import art.arcane.iris.api.terrain.IrisCustomBiomeInfo;
|
||||
import art.arcane.iris.api.terrain.IrisWorldInfo;
|
||||
import art.arcane.volmlib.util.bukkit.papi.PlaceholderValues;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public record IrisPapiWorldView(
|
||||
IrisPapiPosition position,
|
||||
@@ -12,7 +17,9 @@ public record IrisPapiWorldView(
|
||||
String biomeKey,
|
||||
String region,
|
||||
String regionKey,
|
||||
String dimension) {
|
||||
String dimension,
|
||||
BiomeDetails details,
|
||||
Heights heights) {
|
||||
public static IrisPapiWorldView absent(IrisPapiPosition position, long builtAtMs) {
|
||||
return new IrisPapiWorldView(
|
||||
position,
|
||||
@@ -22,29 +29,82 @@ public record IrisPapiWorldView(
|
||||
PlaceholderValues.UNAVAILABLE,
|
||||
PlaceholderValues.UNAVAILABLE,
|
||||
PlaceholderValues.UNAVAILABLE,
|
||||
PlaceholderValues.UNAVAILABLE);
|
||||
PlaceholderValues.UNAVAILABLE,
|
||||
BiomeDetails.absent(),
|
||||
Heights.absent());
|
||||
}
|
||||
|
||||
public static IrisPapiWorldView present(
|
||||
IrisPapiPosition position,
|
||||
long builtAtMs,
|
||||
Optional<String> biome,
|
||||
Optional<String> biomeKey,
|
||||
Optional<String> region,
|
||||
Optional<String> regionKey,
|
||||
Optional<String> dimension) {
|
||||
Optional<IrisBiomeInfo> biome,
|
||||
Optional<IrisWorldInfo> world) {
|
||||
return new IrisPapiWorldView(
|
||||
position,
|
||||
builtAtMs,
|
||||
PlaceholderValues.TRUE,
|
||||
text(biome),
|
||||
text(biomeKey),
|
||||
text(region),
|
||||
text(regionKey),
|
||||
text(dimension));
|
||||
text(biome.map(IrisBiomeInfo::name)),
|
||||
text(biome.map(IrisBiomeInfo::key)),
|
||||
text(biome.map(IrisBiomeInfo::regionName)),
|
||||
text(biome.map(IrisBiomeInfo::regionKey)),
|
||||
text(world.map(IrisWorldInfo::dimensionKey)),
|
||||
biome.map(BiomeDetails::of).orElseGet(BiomeDetails::absent),
|
||||
world.map(Heights::of).orElseGet(Heights::absent));
|
||||
}
|
||||
|
||||
static String text(Optional<String> value) {
|
||||
return value == null || value.isEmpty() ? PlaceholderValues.UNAVAILABLE : PlaceholderValues.text(value.get());
|
||||
}
|
||||
|
||||
public record BiomeDetails(
|
||||
String custom,
|
||||
String customId,
|
||||
String customIds,
|
||||
String customKey,
|
||||
String customKeys,
|
||||
String customCount,
|
||||
String derivative,
|
||||
String vanillaDerivative,
|
||||
String type) {
|
||||
static BiomeDetails absent() {
|
||||
String unavailable = PlaceholderValues.UNAVAILABLE;
|
||||
return new BiomeDetails(unavailable, unavailable, unavailable, unavailable, unavailable,
|
||||
unavailable, unavailable, unavailable, unavailable);
|
||||
}
|
||||
|
||||
static BiomeDetails of(IrisBiomeInfo biome) {
|
||||
List<IrisCustomBiomeInfo> derivatives = biome.customDerivatives();
|
||||
StringJoiner ids = new StringJoiner(", ");
|
||||
StringJoiner keys = new StringJoiner(", ");
|
||||
boolean keysComplete = true;
|
||||
for (IrisCustomBiomeInfo derivative : derivatives) {
|
||||
ids.add(derivative.id());
|
||||
keys.add(derivative.registryKey());
|
||||
keysComplete &= !derivative.registryKey().isBlank();
|
||||
}
|
||||
IrisCustomBiomeInfo single = derivatives.size() == 1 ? derivatives.getFirst() : null;
|
||||
return new BiomeDetails(
|
||||
PlaceholderValues.bool(!derivatives.isEmpty()),
|
||||
single == null ? PlaceholderValues.UNAVAILABLE : PlaceholderValues.text(single.id()),
|
||||
PlaceholderValues.text(ids.toString()),
|
||||
single == null ? PlaceholderValues.UNAVAILABLE : PlaceholderValues.text(single.registryKey()),
|
||||
keysComplete ? PlaceholderValues.text(keys.toString()) : PlaceholderValues.UNAVAILABLE,
|
||||
Integer.toString(derivatives.size()),
|
||||
PlaceholderValues.text(biome.derivativeKey()),
|
||||
PlaceholderValues.text(biome.vanillaDerivativeKey()),
|
||||
PlaceholderValues.text(biome.type()));
|
||||
}
|
||||
}
|
||||
|
||||
public record Heights(String minimum, String maximum, String height, String fluid) {
|
||||
static Heights absent() {
|
||||
String unavailable = PlaceholderValues.UNAVAILABLE;
|
||||
return new Heights(unavailable, unavailable, unavailable, unavailable);
|
||||
}
|
||||
|
||||
static Heights of(IrisWorldInfo world) {
|
||||
return new Heights(Integer.toString(world.minHeight()), Integer.toString(world.maxHeight()),
|
||||
Integer.toString(world.height()), Integer.toString(world.fluidHeight()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -1,5 +1,6 @@
|
||||
package art.arcane.iris.platform.bukkit.api;
|
||||
|
||||
import art.arcane.iris.api.terrain.IrisBiomeInfo;
|
||||
import art.arcane.iris.api.terrain.IrisColumnField;
|
||||
import art.arcane.iris.api.terrain.IrisColumnQuery;
|
||||
import art.arcane.iris.api.terrain.IrisColumnSample;
|
||||
@@ -10,6 +11,7 @@ import art.arcane.iris.api.terrain.IrisTerrainService;
|
||||
import art.arcane.iris.api.terrain.IrisWorldInfo;
|
||||
import art.arcane.iris.configuration.IrisSettings;
|
||||
import art.arcane.iris.platform.bukkit.terrain.IrisApiFaultGuard;
|
||||
import art.arcane.iris.platform.bukkit.terrain.IrisBiomeInfoFactory;
|
||||
import art.arcane.iris.platform.bukkit.terrain.IrisColumnWalk;
|
||||
import art.arcane.iris.platform.bukkit.terrain.IrisSampleLimits;
|
||||
import art.arcane.iris.platform.bukkit.terrain.IrisSurfaceClassifier;
|
||||
@@ -25,6 +27,7 @@ import art.arcane.iris.platform.bukkit.BukkitPlatform;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.IrisServices;
|
||||
import art.arcane.iris.platform.bukkit.plugin.IrisService;
|
||||
import art.arcane.iris.world.history.SavedBiomeUnavailableException;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
@@ -150,6 +153,22 @@ public class IrisTerrainSVC implements IrisService, IrisTerrainService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IrisBiomeInfo> surfaceBiomeInfo(World world, int blockX, int blockZ) {
|
||||
Engine engine = liveEngineOf(world);
|
||||
if (engine == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
return Optional.of(IrisBiomeInfoFactory.surface(engine, blockX, blockZ,
|
||||
error -> reportQueryFault("surfaceBiomeInfo", world, error)));
|
||||
} catch (Throwable error) {
|
||||
reportQueryFault("surfaceBiomeInfo", world, error);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> biomeKey(World world, int blockX, int blockY, int blockZ) {
|
||||
Engine engine = liveEngineOf(world);
|
||||
@@ -371,6 +390,10 @@ public class IrisTerrainSVC implements IrisService, IrisTerrainService {
|
||||
}
|
||||
|
||||
private void reportQueryFault(String operation, World world, Throwable error) {
|
||||
if (error instanceof SavedBiomeUnavailableException unavailable
|
||||
&& unavailable.isLoading() && unavailable.getSuppressed().length == 0) {
|
||||
return;
|
||||
}
|
||||
if (queryFaults.record(System.currentTimeMillis())) {
|
||||
IrisLogging.reportError("Iris terrain API query \"" + operation + "\" failed for world \""
|
||||
+ (world == null ? "null" : world.getName()) + "\" (" + queryFaults.faults()
|
||||
@@ -379,6 +402,10 @@ public class IrisTerrainSVC implements IrisService, IrisTerrainService {
|
||||
}
|
||||
|
||||
private void reportSinkFault(World world, Throwable error) {
|
||||
if (error instanceof SavedBiomeUnavailableException unavailable
|
||||
&& unavailable.isLoading() && unavailable.getSuppressed().length == 0) {
|
||||
return;
|
||||
}
|
||||
if (sinkFaults.record(System.currentTimeMillis())) {
|
||||
IrisLogging.reportError("Iris terrain API column sample failed for world \""
|
||||
+ (world == null ? "null" : world.getName()) + "\" (" + sinkFaults.faults()
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package art.arcane.iris.platform.bukkit.terrain;
|
||||
|
||||
import art.arcane.iris.api.terrain.IrisBiomeInfo;
|
||||
import art.arcane.iris.api.terrain.IrisCustomBiomeInfo;
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.biome.IrisBiomeCustom;
|
||||
import art.arcane.iris.generation.runtime.BiomeEnvironment;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.terrain.InferredType;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class IrisBiomeInfoFactory {
|
||||
private IrisBiomeInfoFactory() {
|
||||
}
|
||||
|
||||
public static IrisBiomeInfo surface(Engine engine, int blockX, int blockZ, Consumer<Throwable> reportFailure) {
|
||||
Objects.requireNonNull(reportFailure, "reportFailure");
|
||||
BiomeEnvironment environment = engine.getSurfaceBiomeEnvironment(blockX, blockZ);
|
||||
IrisBiome biome = environment.biome();
|
||||
InferredType inferredType = biome.getInferredType();
|
||||
return new IrisBiomeInfo(
|
||||
biome.getLoadKey(),
|
||||
biome.getName(),
|
||||
environment.region().getLoadKey(),
|
||||
environment.region().getName(),
|
||||
biome.getDerivativeKey(),
|
||||
biome.getVanillaDerivativeKey(),
|
||||
inferredType == null ? "" : inferredType.name().toLowerCase(Locale.ROOT),
|
||||
customDerivatives(environment, reportFailure));
|
||||
}
|
||||
|
||||
private static List<IrisCustomBiomeInfo> customDerivatives(
|
||||
BiomeEnvironment environment,
|
||||
Consumer<Throwable> reportFailure) {
|
||||
KList<IrisBiomeCustom> customBiomes = environment.biome().getCustomDerivitives();
|
||||
if (customBiomes == null || customBiomes.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<IrisCustomBiomeInfo> derivatives = new ArrayList<>(customBiomes.size());
|
||||
for (IrisBiomeCustom customBiome : customBiomes) {
|
||||
String registryKey = "";
|
||||
try {
|
||||
registryKey = environment.data().customBiomeResourceKey(environment.dimension(), customBiome);
|
||||
} catch (RuntimeException failure) {
|
||||
reportFailure.accept(new IllegalStateException(
|
||||
"Unable to resolve the registry key for Iris custom biome '" + customBiome.getId() + "'.", failure));
|
||||
}
|
||||
derivatives.add(new IrisCustomBiomeInfo(customBiome.getId(), registryKey));
|
||||
}
|
||||
return derivatives;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package art.arcane.iris.api.terrain;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
public class IrisBiomeInfoTest {
|
||||
@Test
|
||||
public void snapshotsDoNotExposeMutableDerivativeLists() {
|
||||
List<IrisCustomBiomeInfo> derivatives = new ArrayList<>();
|
||||
derivatives.add(new IrisCustomBiomeInfo("amber", "iris:biomes/amber"));
|
||||
IrisBiomeInfo info = new IrisBiomeInfo(
|
||||
"forests/amber", "Amber Forest", "temperate", "Temperate",
|
||||
"minecraft:forest", "minecraft:forest", "land", derivatives);
|
||||
|
||||
derivatives.clear();
|
||||
|
||||
assertEquals(1, info.customDerivatives().size());
|
||||
assertThrows(UnsupportedOperationException.class, () -> info.customDerivatives().clear());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unavailableStringsAreEmpty() {
|
||||
IrisBiomeInfo info = new IrisBiomeInfo(null, null, null, null, null, null, null, List.of());
|
||||
IrisCustomBiomeInfo custom = new IrisCustomBiomeInfo(null, null);
|
||||
|
||||
assertEquals("", info.key());
|
||||
assertEquals("", info.name());
|
||||
assertEquals("", info.regionKey());
|
||||
assertEquals("", info.regionName());
|
||||
assertEquals("", info.derivativeKey());
|
||||
assertEquals("", info.vanillaDerivativeKey());
|
||||
assertEquals("", info.type());
|
||||
assertEquals("", custom.id());
|
||||
assertEquals("", custom.registryKey());
|
||||
}
|
||||
}
|
||||
+29
-11
@@ -1,5 +1,7 @@
|
||||
package art.arcane.iris.integration;
|
||||
|
||||
import art.arcane.iris.api.terrain.IrisBiomeInfo;
|
||||
import art.arcane.iris.api.terrain.IrisCustomBiomeInfo;
|
||||
import art.arcane.iris.api.terrain.IrisColumnQuery;
|
||||
import art.arcane.iris.api.terrain.IrisColumnSink;
|
||||
import art.arcane.iris.api.terrain.IrisSurfaceKind;
|
||||
@@ -8,6 +10,7 @@ import art.arcane.iris.api.terrain.IrisWorldInfo;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
@@ -20,6 +23,8 @@ final class FakeIrisTerrainService implements IrisTerrainService {
|
||||
private String biomeKey = "desert/hot-dunes";
|
||||
private String regionName = "Scorched Expanse";
|
||||
private String regionKey = "scorched";
|
||||
private List<IrisCustomBiomeInfo> derivatives = List.of(new IrisCustomBiomeInfo("golden-dunes", "iris:biomes/" + "a".repeat(64)));
|
||||
private boolean biomeAvailable = true;
|
||||
private int builds;
|
||||
private int lastBlockX;
|
||||
private int lastBlockZ;
|
||||
@@ -36,6 +41,26 @@ final class FakeIrisTerrainService implements IrisTerrainService {
|
||||
this.regionKey = regionKey;
|
||||
}
|
||||
|
||||
void derivatives(List<IrisCustomBiomeInfo> derivatives) {
|
||||
this.derivatives = derivatives;
|
||||
}
|
||||
|
||||
void biomeAvailable(boolean available) {
|
||||
biomeAvailable = available;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IrisBiomeInfo> surfaceBiomeInfo(World world, int blockX, int blockZ) {
|
||||
if (!isIrisWorld(world) || !biomeAvailable) {
|
||||
return Optional.empty();
|
||||
}
|
||||
builds++;
|
||||
lastBlockX = blockX;
|
||||
lastBlockZ = blockZ;
|
||||
return Optional.of(new IrisBiomeInfo(biomeKey, biomeName, regionKey, regionName,
|
||||
"minecraft:desert", "minecraft:desert", "land", derivatives));
|
||||
}
|
||||
|
||||
int builds() {
|
||||
return builds;
|
||||
}
|
||||
@@ -70,19 +95,12 @@ final class FakeIrisTerrainService implements IrisTerrainService {
|
||||
|
||||
@Override
|
||||
public Optional<String> surfaceBiomeKey(World world, int blockX, int blockZ) {
|
||||
return isIrisWorld(world) ? Optional.ofNullable(biomeKey) : Optional.empty();
|
||||
throw new AssertionError("placeholders must use a single biome metadata snapshot");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> surfaceBiomeName(World world, int blockX, int blockZ) {
|
||||
if (!isIrisWorld(world)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
builds++;
|
||||
lastBlockX = blockX;
|
||||
lastBlockZ = blockZ;
|
||||
return Optional.ofNullable(biomeName);
|
||||
throw new AssertionError("placeholders must use a single biome metadata snapshot");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,12 +110,12 @@ final class FakeIrisTerrainService implements IrisTerrainService {
|
||||
|
||||
@Override
|
||||
public Optional<String> regionKey(World world, int blockX, int blockZ) {
|
||||
return isIrisWorld(world) ? Optional.ofNullable(regionKey) : Optional.empty();
|
||||
throw new AssertionError("placeholders must use a single biome metadata snapshot");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> regionName(World world, int blockX, int blockZ) {
|
||||
return isIrisWorld(world) ? Optional.ofNullable(regionName) : Optional.empty();
|
||||
throw new AssertionError("placeholders must use a single biome metadata snapshot");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+26
@@ -36,8 +36,21 @@ public class IrisPapiExpansionTest {
|
||||
"pregen.world",
|
||||
"world.available",
|
||||
"world.biome",
|
||||
"world.biome-custom",
|
||||
"world.biome-custom-count",
|
||||
"world.biome-custom-id",
|
||||
"world.biome-custom-ids",
|
||||
"world.biome-custom-key",
|
||||
"world.biome-custom-keys",
|
||||
"world.biome-derivative",
|
||||
"world.biome-key",
|
||||
"world.biome-type",
|
||||
"world.biome-vanilla-derivative",
|
||||
"world.dimension",
|
||||
"world.fluid-height",
|
||||
"world.height",
|
||||
"world.max-height",
|
||||
"world.min-height",
|
||||
"world.region",
|
||||
"world.region-key");
|
||||
|
||||
@@ -102,6 +115,19 @@ public class IrisPapiExpansionTest {
|
||||
assertEquals("true", expansion.onRequest(player, "world.available"));
|
||||
assertEquals("Hot Desert Dunes", expansion.onRequest(player, "world.biome"));
|
||||
assertEquals("desert/hot-dunes", expansion.onRequest(player, "world.biome-key"));
|
||||
assertEquals("true", expansion.onRequest(player, "world.biome-custom"));
|
||||
assertEquals("golden-dunes", expansion.onRequest(player, "world.biome-custom-id"));
|
||||
assertEquals("golden-dunes", expansion.onRequest(player, "world.biome-custom-ids"));
|
||||
assertEquals("iris:biomes/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", expansion.onRequest(player, "world.biome-custom-key"));
|
||||
assertEquals("iris:biomes/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", expansion.onRequest(player, "world.biome-custom-keys"));
|
||||
assertEquals("1", expansion.onRequest(player, "world.biome-custom-count"));
|
||||
assertEquals("minecraft:desert", expansion.onRequest(player, "world.biome-derivative"));
|
||||
assertEquals("minecraft:desert", expansion.onRequest(player, "world.biome-vanilla-derivative"));
|
||||
assertEquals("land", expansion.onRequest(player, "world.biome-type"));
|
||||
assertEquals("-64", expansion.onRequest(player, "world.min-height"));
|
||||
assertEquals("320", expansion.onRequest(player, "world.max-height"));
|
||||
assertEquals("384", expansion.onRequest(player, "world.height"));
|
||||
assertEquals("63", expansion.onRequest(player, "world.fluid-height"));
|
||||
assertEquals("Scorched Expanse", expansion.onRequest(player, "world.region"));
|
||||
assertEquals("scorched", expansion.onRequest(player, "world.region-key"));
|
||||
assertEquals("overworld", expansion.onRequest(player, "world.dimension"));
|
||||
|
||||
+127
@@ -1,5 +1,6 @@
|
||||
package art.arcane.iris.integration;
|
||||
|
||||
import art.arcane.iris.api.terrain.IrisCustomBiomeInfo;
|
||||
import art.arcane.iris.api.pregen.IrisPregenPhase;
|
||||
import art.arcane.iris.api.pregen.IrisPregenProgress;
|
||||
import art.arcane.iris.api.terrain.IrisTerrainService;
|
||||
@@ -110,6 +111,19 @@ public class IrisPapiStateTest {
|
||||
state.region(PLAYER);
|
||||
state.regionKey(PLAYER);
|
||||
state.dimension(PLAYER);
|
||||
state.biomeCustom(PLAYER);
|
||||
state.biomeCustomId(PLAYER);
|
||||
state.biomeCustomIds(PLAYER);
|
||||
state.biomeCustomKey(PLAYER);
|
||||
state.biomeCustomKeys(PLAYER);
|
||||
state.biomeCustomCount(PLAYER);
|
||||
state.biomeDerivative(PLAYER);
|
||||
state.biomeVanillaDerivative(PLAYER);
|
||||
state.biomeType(PLAYER);
|
||||
state.minHeight(PLAYER);
|
||||
state.maxHeight(PLAYER);
|
||||
state.height(PLAYER);
|
||||
state.fluidHeight(PLAYER);
|
||||
}
|
||||
|
||||
assertEquals(1, terrain.builds());
|
||||
@@ -391,4 +405,117 @@ public class IrisPapiStateTest {
|
||||
assertEquals("false", state.worldAvailable(PLAYER));
|
||||
assertEquals(DASH, state.biome(PLAYER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleCustomDerivativesExposeAllDefinitionsWithoutGuessingTheSelectedOne() {
|
||||
FakeIrisTerrainService terrain = new FakeIrisTerrainService();
|
||||
World world = IrisPapiTestSupport.world("sandbox");
|
||||
terrain.addIrisWorld(world);
|
||||
terrain.derivatives(List.of(
|
||||
new IrisCustomBiomeInfo("golden-dunes", "iris:biomes/first"),
|
||||
new IrisCustomBiomeInfo("red-dunes", "iris:biomes/second")));
|
||||
IrisPapiState state = new IrisPapiState(() -> terrain, new IrisPapiTestSupport.Clock());
|
||||
state.trackPosition(PLAYER, world, 10, 10);
|
||||
|
||||
assertEquals("true", state.biomeCustom(PLAYER));
|
||||
assertEquals("2", state.biomeCustomCount(PLAYER));
|
||||
assertEquals("golden-dunes, red-dunes", state.biomeCustomIds(PLAYER));
|
||||
assertEquals("iris:biomes/first, iris:biomes/second", state.biomeCustomKeys(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustomId(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustomKey(PLAYER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vanillaBiomesExposeFalseAndARealZeroCustomCount() {
|
||||
FakeIrisTerrainService terrain = new FakeIrisTerrainService();
|
||||
World world = IrisPapiTestSupport.world("sandbox");
|
||||
terrain.addIrisWorld(world);
|
||||
terrain.derivatives(List.of());
|
||||
IrisPapiState state = new IrisPapiState(() -> terrain, new IrisPapiTestSupport.Clock());
|
||||
state.trackPosition(PLAYER, world, 10, 10);
|
||||
|
||||
assertEquals("false", state.biomeCustom(PLAYER));
|
||||
assertEquals("0", state.biomeCustomCount(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustomId(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustomIds(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustomKeys(PLAYER));
|
||||
assertEquals("minecraft:desert", state.biomeVanillaDerivative(PLAYER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnavailableRegistryKeyPreservesAuthoredIdsWithoutPublishingPartialKeyLists() {
|
||||
FakeIrisTerrainService terrain = new FakeIrisTerrainService();
|
||||
World world = IrisPapiTestSupport.world("sandbox");
|
||||
terrain.addIrisWorld(world);
|
||||
terrain.derivatives(List.of(new IrisCustomBiomeInfo("golden-dunes", "")));
|
||||
IrisPapiTestSupport.Clock clock = new IrisPapiTestSupport.Clock();
|
||||
IrisPapiState state = new IrisPapiState(() -> terrain, clock);
|
||||
state.trackPosition(PLAYER, world, 10, 10);
|
||||
|
||||
assertEquals("golden-dunes", state.biomeCustomId(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustomKey(PLAYER));
|
||||
terrain.derivatives(List.of(new IrisCustomBiomeInfo("golden-dunes", "iris:biomes/first"),
|
||||
new IrisCustomBiomeInfo("red-dunes", "")));
|
||||
clock.advance(IrisPapiState.VIEW_TTL_MS);
|
||||
assertEquals("golden-dunes, red-dunes", state.biomeCustomIds(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustomKeys(PLAYER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingBiomeMetadataDoesNotHideKnownDimensionHeights() {
|
||||
FakeIrisTerrainService terrain = new FakeIrisTerrainService();
|
||||
World world = IrisPapiTestSupport.world("sandbox");
|
||||
terrain.addIrisWorld(world);
|
||||
terrain.biomeAvailable(false);
|
||||
IrisPapiState state = new IrisPapiState(() -> terrain, new IrisPapiTestSupport.Clock());
|
||||
state.trackPosition(PLAYER, world, 10, 10);
|
||||
|
||||
assertEquals("true", state.worldAvailable(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustom(PLAYER));
|
||||
assertEquals(DASH, state.biomeCustomCount(PLAYER));
|
||||
assertEquals(DASH, state.biomeDerivative(PLAYER));
|
||||
assertEquals("overworld", state.dimension(PLAYER));
|
||||
assertEquals("-64", state.minHeight(PLAYER));
|
||||
assertEquals("320", state.maxHeight(PLAYER));
|
||||
assertEquals("384", state.height(PLAYER));
|
||||
assertEquals("63", state.fluidHeight(PLAYER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingBiomeMetadataIsRetriedAfterTheViewExpiresWithoutPlayerMovement() {
|
||||
FakeIrisTerrainService terrain = new FakeIrisTerrainService();
|
||||
World world = IrisPapiTestSupport.world("sandbox");
|
||||
terrain.addIrisWorld(world);
|
||||
terrain.biomeAvailable(false);
|
||||
IrisPapiTestSupport.Clock clock = new IrisPapiTestSupport.Clock();
|
||||
IrisPapiState state = new IrisPapiState(() -> terrain, clock);
|
||||
state.trackPosition(PLAYER, world, 10, 10);
|
||||
|
||||
assertEquals(DASH, state.biomeCustomId(PLAYER));
|
||||
assertEquals("true", state.worldAvailable(PLAYER));
|
||||
terrain.biomeAvailable(true);
|
||||
clock.advance(IrisPapiState.VIEW_TTL_MS - 1L);
|
||||
assertEquals(DASH, state.biomeCustomId(PLAYER));
|
||||
clock.advance(1L);
|
||||
assertEquals("golden-dunes", state.biomeCustomId(PLAYER));
|
||||
assertEquals("Hot Desert Dunes", state.biome(PLAYER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customDerivativeIdsAreScrubbedAndRefreshWithTheCachedView() {
|
||||
FakeIrisTerrainService terrain = new FakeIrisTerrainService();
|
||||
World world = IrisPapiTestSupport.world("sandbox");
|
||||
terrain.addIrisWorld(world);
|
||||
IrisPapiTestSupport.Clock clock = new IrisPapiTestSupport.Clock();
|
||||
IrisPapiState state = new IrisPapiState(() -> terrain, clock);
|
||||
state.trackPosition(PLAYER, world, 10, 10);
|
||||
assertEquals("golden-dunes", state.biomeCustomId(PLAYER));
|
||||
|
||||
terrain.derivatives(List.of(new IrisCustomBiomeInfo("§6red%dunes", "iris:biomes/second")));
|
||||
assertEquals("golden-dunes", state.biomeCustomIds(PLAYER));
|
||||
clock.advance(IrisPapiState.VIEW_TTL_MS);
|
||||
assertEquals("reddunes", state.biomeCustomId(PLAYER));
|
||||
assertEquals("reddunes", state.biomeCustomIds(PLAYER));
|
||||
assertEquals(2, terrain.builds());
|
||||
}
|
||||
}
|
||||
|
||||
+142
@@ -5,23 +5,43 @@ import art.arcane.iris.api.terrain.IrisColumnQuery;
|
||||
import art.arcane.iris.api.terrain.IrisRiverState;
|
||||
import art.arcane.iris.api.terrain.IrisSurfaceKind;
|
||||
import art.arcane.iris.api.terrain.IrisTerrainService;
|
||||
import art.arcane.iris.configuration.IrisSettings;
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.runtime.BiomeEnvironment;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.terrain.IrisDimension;
|
||||
import art.arcane.iris.generation.terrain.IrisRegion;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.platform.generation.PlatformChunkGenerator;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.world.history.SavedBiomeUnavailableException;
|
||||
import art.arcane.iris.generation.hydrology.HydrologyColumnLayer;
|
||||
import art.arcane.iris.generation.hydrology.HydrologyColumnSample;
|
||||
import art.arcane.iris.generation.hydrology.HydrologyFeatureRef;
|
||||
import art.arcane.iris.generation.hydrology.HydrologyFeatureType;
|
||||
import art.arcane.iris.platform.bukkit.plugin.IrisService;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.junit.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.contains;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
public class IrisTerrainSVCTest {
|
||||
private static final IrisColumnQuery SMALL = IrisColumnQuery.rect(
|
||||
@@ -53,6 +73,7 @@ public class IrisTerrainSVCTest {
|
||||
assertEquals(IrisSurfaceKind.UNKNOWN, service.surfaceKind(null, 0, 0));
|
||||
assertTrue(service.surfaceBiomeKey(null, 0, 0).isEmpty());
|
||||
assertTrue(service.surfaceBiomeName(null, 0, 0).isEmpty());
|
||||
assertTrue(service.surfaceBiomeInfo(null, 0, 0).isEmpty());
|
||||
assertTrue(service.biomeKey(null, 0, 64, 0).isEmpty());
|
||||
assertTrue(service.regionKey(null, 0, 0).isEmpty());
|
||||
assertTrue(service.regionName(null, 0, 0).isEmpty());
|
||||
@@ -66,6 +87,9 @@ public class IrisTerrainSVCTest {
|
||||
assertEquals(Optional.class,
|
||||
IrisTerrainService.class.getMethod("regionName", World.class, int.class, int.class)
|
||||
.getReturnType());
|
||||
assertEquals(Optional.class,
|
||||
IrisTerrainService.class.getMethod("surfaceBiomeInfo", World.class, int.class, int.class)
|
||||
.getReturnType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,6 +118,124 @@ public class IrisTerrainSVCTest {
|
||||
assertEquals(IrisRiverState.DRY, IrisTerrainSVC.riverState(river(false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingBiomeMetadataReturnsAbsenceAndResolvesOnRetryWithoutLogging() throws Exception {
|
||||
Engine engine = mock(Engine.class);
|
||||
World world = world(engine);
|
||||
IrisTerrainSVC service = enabledService();
|
||||
IrisBiome biome = mock(IrisBiome.class);
|
||||
when(biome.getLoadKey()).thenReturn("desert/dunes");
|
||||
BiomeEnvironment environment = new BiomeEnvironment(1L, biome, mock(IrisRegion.class),
|
||||
mock(IrisDimension.class), mock(IrisData.class));
|
||||
when(engine.getSurfaceBiomeEnvironment(-17, 32))
|
||||
.thenThrow(new SavedBiomeUnavailableException("Saved biome information is loading", true))
|
||||
.thenReturn(environment);
|
||||
|
||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||
assertTrue(service.surfaceBiomeInfo(world, -17, 32).isEmpty());
|
||||
assertEquals("desert/dunes", service.surfaceBiomeInfo(world, -17, 32).orElseThrow().key());
|
||||
logging.verifyNoInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingReadsDoNotConsumeTheFirstRealFailureReport() throws Exception {
|
||||
Engine engine = mock(Engine.class);
|
||||
World world = world(engine);
|
||||
IrisTerrainSVC service = enabledService();
|
||||
SavedBiomeUnavailableException failure = new SavedBiomeUnavailableException(
|
||||
"Saved biome data could not be read", new IllegalStateException("Invalid saved data"));
|
||||
when(engine.getSurfaceBiomeEnvironment(-17, 32))
|
||||
.thenThrow(new SavedBiomeUnavailableException("Saved biome information is loading", true))
|
||||
.thenThrow(failure);
|
||||
|
||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||
assertTrue(service.surfaceBiomeInfo(world, -17, 32).isEmpty());
|
||||
logging.verifyNoInteractions();
|
||||
assertTrue(service.surfaceBiomeInfo(world, -17, 32).isEmpty());
|
||||
logging.verify(() -> IrisLogging.reportError(contains("(1 terrain API query faults"), same(failure)));
|
||||
logging.verifyNoMoreInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void biomeAndRegionReadsTreatLoadingAsTemporaryAbsence() throws Exception {
|
||||
Engine engine = mock(Engine.class);
|
||||
World world = world(engine);
|
||||
IrisTerrainSVC service = enabledService();
|
||||
SavedBiomeUnavailableException pending = new SavedBiomeUnavailableException("Loading saved biomes", true);
|
||||
when(engine.getSurfaceBiome(0, 0)).thenThrow(pending);
|
||||
when(engine.getBiome(0, 64, 0)).thenThrow(pending);
|
||||
when(engine.getRegion(0, 0)).thenThrow(pending);
|
||||
|
||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||
assertTrue(service.surfaceBiomeKey(world, 0, 0).isEmpty());
|
||||
assertTrue(service.surfaceBiomeName(world, 0, 0).isEmpty());
|
||||
assertTrue(service.biomeKey(world, 0, 64, 0).isEmpty());
|
||||
assertTrue(service.regionKey(world, 0, 0).isEmpty());
|
||||
assertTrue(service.regionName(world, 0, 0).isEmpty());
|
||||
logging.verifyNoInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aLoadingReadWithASuppressedFailureIsStillReported() throws Exception {
|
||||
Engine engine = mock(Engine.class);
|
||||
World world = world(engine);
|
||||
IrisTerrainSVC service = enabledService();
|
||||
SavedBiomeUnavailableException failure = new SavedBiomeUnavailableException("Loading saved biomes", true);
|
||||
failure.addSuppressed(new IllegalStateException("Failed to release saved definitions"));
|
||||
when(engine.getSurfaceBiomeEnvironment(0, 0)).thenThrow(failure);
|
||||
|
||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||
assertTrue(service.surfaceBiomeInfo(world, 0, 0).isEmpty());
|
||||
logging.verify(() -> IrisLogging.reportError(contains("surfaceBiomeInfo"), same(failure)));
|
||||
logging.verifyNoMoreInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingBatchReadDoesNotHideSubsequentSinkFailures() throws Exception {
|
||||
Engine engine = mock(Engine.class);
|
||||
World world = world(engine);
|
||||
IrisTerrainSVC service = enabledService();
|
||||
IrisColumnQuery query = IrisColumnQuery.rect(0, 0, 0, 0, 1, EnumSet.of(IrisColumnField.BIOME_KEY));
|
||||
when(engine.getDimension()).thenReturn(mock(IrisDimension.class));
|
||||
when(engine.getSurfaceBiome(0, 0))
|
||||
.thenThrow(new SavedBiomeUnavailableException("Loading saved biomes", true))
|
||||
.thenReturn(mock(IrisBiome.class));
|
||||
AtomicInteger sinkCalls = new AtomicInteger();
|
||||
IllegalStateException failure = new IllegalStateException("Consumer failed");
|
||||
|
||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class);
|
||||
MockedStatic<IrisSettings> settings = mockStatic(IrisSettings.class)) {
|
||||
settings.when(IrisSettings::get).thenReturn(new IrisSettings());
|
||||
assertFalse(service.sampleColumns(world, query, sample -> sinkCalls.incrementAndGet()));
|
||||
assertEquals(0, sinkCalls.get());
|
||||
logging.verifyNoInteractions();
|
||||
assertFalse(service.sampleColumns(world, query, sample -> { throw failure; }));
|
||||
logging.verify(() -> IrisLogging.reportError(contains("(1 terrain API sample faults"), same(failure)));
|
||||
logging.verifyNoMoreInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
private static World world(Engine engine) {
|
||||
World world = mock(World.class);
|
||||
ChunkGenerator generator = mock(ChunkGenerator.class, withSettings().extraInterfaces(PlatformChunkGenerator.class));
|
||||
when(world.getGenerator()).thenReturn(generator);
|
||||
when(world.getName()).thenReturn("sandbox");
|
||||
when(((PlatformChunkGenerator) generator).getEngine()).thenReturn(engine);
|
||||
return world;
|
||||
}
|
||||
|
||||
private static IrisTerrainSVC enabledService() throws ReflectiveOperationException {
|
||||
IrisTerrainSVC service = new IrisTerrainSVC();
|
||||
Field enabled = IrisTerrainSVC.class.getDeclaredField("serviceEnabled");
|
||||
enabled.setAccessible(true);
|
||||
((AtomicBoolean) enabled.get(service)).set(true);
|
||||
return service;
|
||||
}
|
||||
|
||||
private static HydrologyColumnSample river(boolean connectedFluid) {
|
||||
HydrologyFeatureRef feature = new HydrologyFeatureRef(
|
||||
1L,
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package art.arcane.iris.platform.bukkit.terrain;
|
||||
|
||||
import art.arcane.iris.api.terrain.IrisBiomeInfo;
|
||||
import art.arcane.iris.api.terrain.IrisCustomBiomeInfo;
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.biome.IrisBiomeCustom;
|
||||
import art.arcane.iris.generation.runtime.BiomeEnvironment;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.terrain.InferredType;
|
||||
import art.arcane.iris.generation.terrain.IrisDimension;
|
||||
import art.arcane.iris.generation.terrain.IrisRegion;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class IrisBiomeInfoFactoryTest {
|
||||
@Test
|
||||
public void historicalMetadataUsesTheRetainedEnvironmentInsteadOfTheActivePack() {
|
||||
IrisBiomeCustom custom = new IrisBiomeCustom().setId("amber-forest");
|
||||
Fixture fixture = fixture(List.of(custom));
|
||||
IrisData activeData = mock(IrisData.class);
|
||||
when(fixture.engine().getData()).thenReturn(activeData);
|
||||
when(fixture.data().customBiomeResourceKey(fixture.dimension(), custom)).thenReturn("iris:biomes/retained");
|
||||
List<Throwable> failures = new ArrayList<>();
|
||||
|
||||
IrisBiomeInfo info = IrisBiomeInfoFactory.surface(fixture.engine(), -17, 32, failures::add);
|
||||
|
||||
assertEquals("forests/amber", info.key());
|
||||
assertEquals("Amber Forest", info.name());
|
||||
assertEquals("temperate", info.regionKey());
|
||||
assertEquals("Temperate Region", info.regionName());
|
||||
assertEquals("minecraft:forest", info.derivativeKey());
|
||||
assertEquals("minecraft:birch_forest", info.vanillaDerivativeKey());
|
||||
assertEquals("land", info.type());
|
||||
assertEquals(List.of(new IrisCustomBiomeInfo("amber-forest", "iris:biomes/retained")), info.customDerivatives());
|
||||
assertTrue(failures.isEmpty());
|
||||
verify(fixture.engine()).getSurfaceBiomeEnvironment(-17, 32);
|
||||
verifyNoMoreInteractions(fixture.engine());
|
||||
verify(fixture.data()).customBiomeResourceKey(fixture.dimension(), custom);
|
||||
verifyNoInteractions(activeData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allCustomDerivativesRemainInAuthoredOrderWithoutSelectingAVariant() {
|
||||
IrisBiomeCustom first = new IrisBiomeCustom().setId("amber");
|
||||
IrisBiomeCustom second = new IrisBiomeCustom().setId("copper");
|
||||
Fixture fixture = fixture(List.of(first, second));
|
||||
when(fixture.data().customBiomeResourceKey(fixture.dimension(), first)).thenReturn("iris:biomes/first");
|
||||
when(fixture.data().customBiomeResourceKey(fixture.dimension(), second)).thenReturn("iris:biomes/second");
|
||||
|
||||
IrisBiomeInfo info = IrisBiomeInfoFactory.surface(fixture.engine(), -17, 32, failure -> {
|
||||
throw new AssertionError(failure);
|
||||
});
|
||||
|
||||
assertEquals(List.of(
|
||||
new IrisCustomBiomeInfo("amber", "iris:biomes/first"),
|
||||
new IrisCustomBiomeInfo("copper", "iris:biomes/second")), info.customDerivatives());
|
||||
verify(fixture.engine()).getSurfaceBiomeEnvironment(-17, 32);
|
||||
verifyNoMoreInteractions(fixture.engine());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aVanillaBiomeDoesNotResolveAnyCustomRegistryKeys() {
|
||||
Fixture fixture = fixture(List.of());
|
||||
|
||||
IrisBiomeInfo info = IrisBiomeInfoFactory.surface(fixture.engine(), -17, 32, failure -> {
|
||||
throw new AssertionError(failure);
|
||||
});
|
||||
|
||||
assertEquals("Amber Forest", info.name());
|
||||
assertTrue(info.customDerivatives().isEmpty());
|
||||
verifyNoInteractions(fixture.data());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aMissingMappingPreservesTheAuthoredIdAndOtherMetadata() {
|
||||
IrisBiomeCustom first = new IrisBiomeCustom().setId("amber");
|
||||
IrisBiomeCustom second = new IrisBiomeCustom().setId("copper");
|
||||
Fixture fixture = fixture(List.of(first, second));
|
||||
IllegalStateException missing = new IllegalStateException("Missing historical registry mapping");
|
||||
when(fixture.data().customBiomeResourceKey(fixture.dimension(), first)).thenThrow(missing);
|
||||
when(fixture.data().customBiomeResourceKey(fixture.dimension(), second)).thenReturn("iris:biomes/second");
|
||||
List<Throwable> failures = new ArrayList<>();
|
||||
|
||||
IrisBiomeInfo info = IrisBiomeInfoFactory.surface(fixture.engine(), -17, 32, failures::add);
|
||||
|
||||
assertEquals("Amber Forest", info.name());
|
||||
assertEquals(List.of(
|
||||
new IrisCustomBiomeInfo("amber", ""),
|
||||
new IrisCustomBiomeInfo("copper", "iris:biomes/second")), info.customDerivatives());
|
||||
assertEquals(1, failures.size());
|
||||
assertSame(missing, failures.getFirst().getCause());
|
||||
assertTrue(failures.getFirst().getMessage().contains("amber"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnclassifiedBiomeReportsAnEmptyType() {
|
||||
Fixture fixture = fixture(List.of());
|
||||
when(fixture.biome().getInferredType()).thenReturn(null);
|
||||
|
||||
IrisBiomeInfo info = IrisBiomeInfoFactory.surface(fixture.engine(), -17, 32, failure -> {
|
||||
throw new AssertionError(failure);
|
||||
});
|
||||
|
||||
assertEquals("", info.type());
|
||||
}
|
||||
|
||||
private static Fixture fixture(List<IrisBiomeCustom> customBiomes) {
|
||||
Engine engine = mock(Engine.class);
|
||||
IrisBiome biome = mock(IrisBiome.class);
|
||||
IrisRegion region = mock(IrisRegion.class);
|
||||
IrisDimension dimension = mock(IrisDimension.class);
|
||||
IrisData data = mock(IrisData.class);
|
||||
KList<IrisBiomeCustom> derivatives = new KList<>();
|
||||
derivatives.addAll(customBiomes);
|
||||
when(biome.getLoadKey()).thenReturn("forests/amber");
|
||||
when(biome.getName()).thenReturn("Amber Forest");
|
||||
when(biome.getDerivativeKey()).thenReturn("minecraft:forest");
|
||||
when(biome.getVanillaDerivativeKey()).thenReturn("minecraft:birch_forest");
|
||||
when(biome.getInferredType()).thenReturn(InferredType.LAND);
|
||||
when(biome.getCustomDerivitives()).thenReturn(derivatives);
|
||||
when(region.getLoadKey()).thenReturn("temperate");
|
||||
when(region.getName()).thenReturn("Temperate Region");
|
||||
when(engine.getSurfaceBiomeEnvironment(-17, 32)).thenReturn(
|
||||
new BiomeEnvironment(4L, biome, region, dimension, data));
|
||||
return new Fixture(engine, biome, dimension, data);
|
||||
}
|
||||
|
||||
private record Fixture(Engine engine, IrisBiome biome, IrisDimension dimension, IrisData data) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user