mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
dwa
This commit is contained in:
+8
-4
@@ -57,6 +57,7 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
private volatile KMap<String, Holder<Biome>> customBiomes;
|
||||
private volatile Map<Biome, Holder<Biome>> vanillaSpawnBiomes;
|
||||
private volatile IrisDimension cacheDimension;
|
||||
private volatile int cacheRuntimeId;
|
||||
|
||||
public CustomBiomeSource(long seed, Engine engine, World world) {
|
||||
this.engine = engine;
|
||||
@@ -67,6 +68,7 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
this.customBiomes = fillCustomBiomes(this.biomeCustomRegistry, engine, this.fallbackBiome);
|
||||
this.vanillaSpawnBiomes = fillVanillaSpawnBiomes(this.biomeCustomRegistry, this.biomeRegistry, engine);
|
||||
this.cacheDimension = engine.getDimension();
|
||||
this.cacheRuntimeId = engine.getCacheID();
|
||||
}
|
||||
|
||||
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
|
||||
@@ -246,11 +248,11 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
}
|
||||
GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_spawn_biome");
|
||||
if (lease == null) {
|
||||
throw new IllegalStateException("Iris spawn biome lookup was rejected during an engine transition");
|
||||
return null;
|
||||
}
|
||||
try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
|
||||
if (!isRuntimeAvailable()) {
|
||||
throw new IllegalStateException("Iris spawn biome lookup has no active engine runtime");
|
||||
return null;
|
||||
}
|
||||
ensureCachesCurrent();
|
||||
return vanillaSpawnBiomes.get(biome.value());
|
||||
@@ -434,11 +436,12 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
|
||||
private void ensureCachesCurrent() {
|
||||
IrisDimension dimension = engine.getDimension();
|
||||
if (cacheDimension == dimension) {
|
||||
int runtimeId = engine.getCacheID();
|
||||
if (cacheDimension == dimension && cacheRuntimeId == runtimeId) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (cacheDimension == dimension) {
|
||||
if (cacheDimension == dimension && cacheRuntimeId == runtimeId) {
|
||||
return;
|
||||
}
|
||||
KMap<String, Holder<Biome>> refreshedCustomBiomes = fillCustomBiomes(
|
||||
@@ -451,6 +454,7 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
customBiomes = refreshedCustomBiomes;
|
||||
vanillaSpawnBiomes = refreshedSpawnBiomes;
|
||||
cacheDimension = dimension;
|
||||
cacheRuntimeId = runtimeId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -57,6 +57,7 @@ final class ImportedFeatureStage {
|
||||
private final Engine engine;
|
||||
private volatile FeatureTable featureTable;
|
||||
private volatile IrisDimension inertDimension;
|
||||
private volatile int settledRuntimeId;
|
||||
|
||||
ImportedFeatureStage(Engine engine) {
|
||||
this.engine = engine;
|
||||
@@ -86,18 +87,23 @@ final class ImportedFeatureStage {
|
||||
*/
|
||||
void prepare(WorldGenLevel level) {
|
||||
IrisDimension dimension = engine.getDimension();
|
||||
if (settled(dimension)) {
|
||||
int runtimeId = engine.getCacheID();
|
||||
if (settled(dimension, runtimeId)) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (settled(dimension)) {
|
||||
if (settled(dimension, runtimeId)) {
|
||||
return;
|
||||
}
|
||||
build(level, dimension);
|
||||
settledRuntimeId = runtimeId;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean settled(IrisDimension dimension) {
|
||||
private boolean settled(IrisDimension dimension, int runtimeId) {
|
||||
if (settledRuntimeId != runtimeId) {
|
||||
return false;
|
||||
}
|
||||
FeatureTable current = featureTable;
|
||||
if (current != null && current.dimension() == dimension) {
|
||||
return true;
|
||||
|
||||
+18
-4
@@ -128,6 +128,8 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private final int runtimeHeight;
|
||||
private final int runtimeSeaLevel;
|
||||
private final ConcurrentHashMap<SpawnTableKey, WeightedList<MobSpawnSettings.SpawnerData>> mergedSpawnTables = new ConcurrentHashMap<>();
|
||||
private volatile IrisDimension spawnTableDimension;
|
||||
private volatile int spawnTableRuntimeId;
|
||||
private final ImportedFeatureStage importedFeatures;
|
||||
private final AtomicReference<StudioStructureState> retainedStudioStructureState = new AtomicReference<>();
|
||||
private volatile ReachableStructureCache reachableStructureCache;
|
||||
@@ -310,18 +312,19 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
private Set<String> reachableStructureKeys(ServerLevel level) {
|
||||
IrisDimension dimension = engine.getDimension();
|
||||
int runtimeId = engine.getCacheID();
|
||||
ReachableStructureCache cached = reachableStructureCache;
|
||||
if (cached != null && cached.dimension() == dimension) {
|
||||
if (cached != null && cached.dimension() == dimension && cached.runtimeId() == runtimeId) {
|
||||
return cached.keys();
|
||||
}
|
||||
synchronized (this) {
|
||||
cached = reachableStructureCache;
|
||||
if (cached != null && cached.dimension() == dimension) {
|
||||
if (cached != null && cached.dimension() == dimension && cached.runtimeId() == runtimeId) {
|
||||
return cached.keys();
|
||||
}
|
||||
Set<String> reachable = Set.copyOf(
|
||||
VanillaStructureBiomes.reachableStructureKeys(level, customBiomeSource));
|
||||
reachableStructureCache = new ReachableStructureCache(dimension, reachable);
|
||||
reachableStructureCache = new ReachableStructureCache(dimension, runtimeId, reachable);
|
||||
return reachable;
|
||||
}
|
||||
}
|
||||
@@ -738,6 +741,17 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
return explicitSpawns;
|
||||
}
|
||||
|
||||
IrisDimension spawnDimension = engine.getDimension();
|
||||
int spawnRuntimeId = engine.getCacheID();
|
||||
if (spawnTableDimension != spawnDimension || spawnTableRuntimeId != spawnRuntimeId) {
|
||||
synchronized (mergedSpawnTables) {
|
||||
if (spawnTableDimension != spawnDimension || spawnTableRuntimeId != spawnRuntimeId) {
|
||||
mergedSpawnTables.clear();
|
||||
spawnTableDimension = spawnDimension;
|
||||
spawnTableRuntimeId = spawnRuntimeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
SpawnTableKey key = new SpawnTableKey(holder.value(), enumcreaturetype);
|
||||
return mergedSpawnTables.computeIfAbsent(key, ignored -> mergeSpawnTables(vanillaSpawns, explicitSpawns));
|
||||
}
|
||||
@@ -1160,7 +1174,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private record SpawnTableKey(Biome biome, MobCategory category) {
|
||||
}
|
||||
|
||||
private record ReachableStructureCache(IrisDimension dimension, Set<String> keys) {
|
||||
private record ReachableStructureCache(IrisDimension dimension, int runtimeId, Set<String> keys) {
|
||||
}
|
||||
|
||||
private record StructureStepCache(Registry<Structure> registry, List<List<Structure>> structures) {
|
||||
|
||||
+3
-2
@@ -26,8 +26,9 @@ public class CustomBiomeSourceStructureContractTest {
|
||||
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(\"bukkit_possible_biomes\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(\"bukkit_spawn_biome\")"));
|
||||
assertTrue(source.contains("Iris spawn biome lookup was rejected during an engine transition"));
|
||||
assertTrue(source.contains("Iris spawn biome lookup has no active engine runtime"));
|
||||
assertFalse(source.contains("Iris spawn biome lookup was rejected during an engine transition"));
|
||||
assertFalse(source.contains("Iris spawn biome lookup has no active engine runtime"));
|
||||
assertTrue(source.contains("vanillaSpawnBiomes.get(biome.value())"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(\"bukkit_structure_biome\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(\"bukkit_biomes_within\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(\"bukkit_visible_biome\")"));
|
||||
|
||||
@@ -613,18 +613,16 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
// (listeners, shutdown hook, replacement journals) are the safety-critical ones.
|
||||
// Only services that actually enabled get listeners and a later onDisable.
|
||||
enabledServices.clear();
|
||||
IrisService firstFailedService = null;
|
||||
for (IrisService service : orderedServices) {
|
||||
try {
|
||||
service.onEnable();
|
||||
enabledServices.add(service);
|
||||
} catch (Throwable e) {
|
||||
if (firstFailedService == null) {
|
||||
firstFailedService = service;
|
||||
}
|
||||
// A service failure is NOT a datapack validation failure: the admission gate
|
||||
// must never lock every login over a broken cosmetic service. Log loudly,
|
||||
// continue degraded, and clean up whatever the partial onEnable started
|
||||
// (a failed service is excluded from the teardown loop).
|
||||
Iris.reportError("Failed to enable " + service.getClass().getSimpleName() + "; continuing with a degraded runtime.", e);
|
||||
// A failed service is excluded from the teardown loop, so clean up whatever
|
||||
// its partial onEnable started right here, best-effort.
|
||||
try {
|
||||
service.onDisable();
|
||||
} catch (Throwable cleanup) {
|
||||
@@ -632,11 +630,6 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstFailedService != null) {
|
||||
IrisStartupValidation.markDatapacksInvalid("Iris service "
|
||||
+ firstFailedService.getClass().getSimpleName()
|
||||
+ " failed to enable; world creation and player admission are locked. Check the log above.");
|
||||
}
|
||||
for (IrisService service : enabledServices) {
|
||||
try {
|
||||
registerListener(service);
|
||||
|
||||
+15
-1
@@ -468,7 +468,21 @@ public final class PendingWorldReplacementManager implements Listener {
|
||||
}
|
||||
|
||||
private List<Transaction> loadTransactions() throws IOException {
|
||||
return WorldReplacementJournal.load(dataDirectory(), IrisWorldStorage.levelRoot().toPath());
|
||||
Path levelRoot = IrisWorldStorage.levelRoot().toPath();
|
||||
List<Transaction> transactions = WorldReplacementJournal.load(dataDirectory(), levelRoot);
|
||||
List<Transaction> applicable = new ArrayList<>(transactions.size());
|
||||
for (Transaction transaction : transactions) {
|
||||
// A journal staged against another level root is not this server's transaction;
|
||||
// skip it (the bootstrap already told the operator how to resolve it).
|
||||
if (WorldReplacementJournal.appliesTo(transaction, levelRoot)) {
|
||||
applicable.add(transaction);
|
||||
} else {
|
||||
Iris.warn("Ignoring pending world replacement " + transaction.id() + " for "
|
||||
+ transaction.worldKey() + "; it was staged against level root "
|
||||
+ transaction.levelRoot() + ".");
|
||||
}
|
||||
}
|
||||
return applicable;
|
||||
}
|
||||
|
||||
private void writeTransaction(Transaction transaction) throws IOException {
|
||||
|
||||
@@ -263,6 +263,10 @@ public class CommandPack implements DirectorExecutor {
|
||||
for (File packDirectory : packDirectories) {
|
||||
PackValidationResult result = PackValidationRegistry.get(packDirectory.getName());
|
||||
if (result == null) {
|
||||
// The boot cache only loads when it covers every pack, so a partial write is
|
||||
// pointless - but say so instead of silently skipping the persist forever.
|
||||
Iris.warn("Pack validation cache not written: \"" + packDirectory.getName()
|
||||
+ "\" has no registered result. Run /iris pack validate all to repopulate it.");
|
||||
return;
|
||||
}
|
||||
results.add(result);
|
||||
|
||||
+4
-4
@@ -329,8 +329,7 @@ public class CommandStudio implements DirectorExecutor {
|
||||
return;
|
||||
}
|
||||
if (radius <= 0 || radius > 2048) {
|
||||
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_ONLY_WORKS_IRIS_WORLD));
|
||||
sender().sendMessage("Radius must be between 1 and 2048 chunks.");
|
||||
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_REGIONS_RADIUS_OUT_OF_RANGE));
|
||||
return;
|
||||
}
|
||||
var sender = sender();
|
||||
@@ -393,7 +392,8 @@ public class CommandStudio implements DirectorExecutor {
|
||||
data.forEach((k, v) -> sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_MESSAGE, MessageArgument.untrusted("k", k), MessageArgument.untrusted("value", loader.load(k).getRarity()), MessageArgument.untrusted("value2", Form.f((double) v.get() / totalTasks * 100, 2)))));
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
sender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_ONLY_WORKS_IRIS_WORLD));
|
||||
sender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_REGIONS_SCAN_FAILED,
|
||||
MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
|
||||
} finally {
|
||||
if (c != -1) {
|
||||
J.car(c);
|
||||
@@ -712,7 +712,7 @@ public class CommandStudio implements DirectorExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
@Director(aliases = "find-objects", description = "Capture an IGenData chunk report for nearby chunks", descriptionKey = "iris.director.commandstudio.director.capture_igendata_chunk_report_nearby_chunks")
|
||||
@Director(aliases = "find-objects", description = "Capture an IGenData chunk report for nearby chunks", descriptionKey = "iris.director.commandstudio.director.capture_igendata_chunk_report_nearby_chunks", origin = DirectorOrigin.PLAYER)
|
||||
public void objects() {
|
||||
if (!IrisToolbelt.isIrisWorld(player().getWorld())) {
|
||||
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_MUST_BE_IRIS_WORLD));
|
||||
|
||||
Reference in New Issue
Block a user