mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-11-01 08:50:06 +00:00
Restore natural mob spawning
- Restore cave spawning and correct floor placement across platforms. - Enforce category population limits and clamp ambient spawn batches. - Honor mob-spawning rules and entity-ticking chunk admission. - Separate chunk saving from configurable distance-despawn retention. - Add spawning regressions and Folia gameplay acceptance coverage.
This commit is contained in:
1 parent
4ef6fc7cc6
commit
a765e76aa3
31 files changed
+1712
-87
No files matched your search
+6
-6
@@ -61,11 +61,12 @@ public final class ModdedEntitySpawner {
|
||||
}
|
||||
|
||||
double x = blockX + 0.5;
|
||||
double y = blockY + 0.5;
|
||||
double verticalOffset = irisEntity.getSurface().isFluid() ? 0.5 : 0;
|
||||
double y = blockY + verticalOffset;
|
||||
double z = blockZ + 0.5;
|
||||
boolean riseEffectActive = irisEntity.isSpawnEffectRiseOutOfGround() && level.hasPlayersNearby(new NativeEntityRuntime.Position(x, y, z), PLAYER_EFFECT_RADIUS);
|
||||
int spawnBlockY = riseEffectActive ? blockY - 5 : blockY;
|
||||
double spawnY = spawnBlockY + 0.5;
|
||||
double spawnY = spawnBlockY + verticalOffset;
|
||||
|
||||
NativeSpawnedEntity created = create(irisEntity, level, x, spawnY, z);
|
||||
if (created == null) {
|
||||
@@ -98,8 +99,7 @@ public final class ModdedEntitySpawner {
|
||||
String customName = irisEntity.getCustomName();
|
||||
entity.applyBase(irisEntity, customName == null ? null : colorize(customName));
|
||||
|
||||
boolean persistent = irisEntity.isKeepEntity() || forcePersist();
|
||||
applyPersistence(entity, persistent);
|
||||
applyPersistence(irisEntity, entity, forcePersist());
|
||||
|
||||
applyPassengers(engine, irisEntity, entity, level, blockX, blockY, blockZ, rng);
|
||||
|
||||
@@ -273,8 +273,8 @@ public final class ModdedEntitySpawner {
|
||||
return IrisSettings.get().getWorld().isForcePersistEntities();
|
||||
}
|
||||
|
||||
private static void applyPersistence(NativeSpawnedEntity entity, boolean persistent) {
|
||||
if (!entity.persistence(persistent)) {
|
||||
static void applyPersistence(IrisEntity options, NativeSpawnedEntity entity, boolean forcePersistence) {
|
||||
if (!entity.persistence(options.isKeepEntity() || forcePersistence)) {
|
||||
String type = entity.typeKey();
|
||||
if (WARNED_PERSISTENCE_TYPES.add(type)) {
|
||||
IrisLogging.warn("Iris entity: vanilla cannot persist non-serializable entity type '" + type + "'");
|
||||
|
||||
+19
-14
@@ -55,6 +55,7 @@ import art.arcane.volmlib.util.matter.Matter;
|
||||
import art.arcane.volmlib.util.matter.MatterMarker;
|
||||
import art.arcane.volmlib.util.matter.slices.MarkerMatter;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
@@ -511,13 +512,16 @@ public final class ModdedWorldManager implements EngineWorldManager {
|
||||
}
|
||||
|
||||
private void spawnAmbient(NativeWorld level, int chunkX, int chunkZ, boolean initial, BiomeEnvironment environment) {
|
||||
if (!NativeSpawnQueries.ambientAllowed(level, chunkX, chunkZ, initial)) {
|
||||
return;
|
||||
}
|
||||
IrisComplex complex = engine.getComplex();
|
||||
if (complex == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisBiome biome = environment.biome();
|
||||
int chunkMobs = countChunkLivingEntities(level, chunkX, chunkZ);
|
||||
Map<String, Integer> chunkMobs = NativeSpawnQueries.livingEntityCategories(level, chunkX, chunkZ);
|
||||
|
||||
KList<IrisEntitySpawn> pool = new KList<>();
|
||||
collectSpawns(pool, environment.data().getSpawnerLoader().loadAll(environment.dimension().getEntitySpawners()), biome, chunkX, chunkZ, chunkMobs, initial);
|
||||
@@ -535,20 +539,18 @@ public final class ModdedWorldManager implements EngineWorldManager {
|
||||
if (!canSpawn(spawner, chunkX, chunkZ)) {
|
||||
return;
|
||||
}
|
||||
int spawned = spawnEntry(level, chosen, spawner, chunkX, chunkZ);
|
||||
int spawned = spawnEntry(level, chosen, spawner, chunkX, chunkZ,
|
||||
remainingCapacity(chosen, spawner, chunkMobs));
|
||||
if (spawned > 0) {
|
||||
spawner.spawn(engine, chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectSpawns(KList<IrisEntitySpawn> pool, KList<IrisSpawner> spawners, IrisBiome biomeFilter, int chunkX, int chunkZ, int chunkMobs, boolean initial) {
|
||||
private void collectSpawns(KList<IrisEntitySpawn> pool, KList<IrisSpawner> spawners, IrisBiome biomeFilter, int chunkX, int chunkZ, Map<String, Integer> chunkMobs, boolean initial) {
|
||||
for (IrisSpawner spawner : spawners) {
|
||||
if (spawner == null) {
|
||||
continue;
|
||||
}
|
||||
if (spawner.getMaxEntitiesPerChunk() <= chunkMobs) {
|
||||
continue;
|
||||
}
|
||||
if (biomeFilter != null && !spawner.isValid(biomeFilter)) {
|
||||
continue;
|
||||
}
|
||||
@@ -559,12 +561,19 @@ public final class ModdedWorldManager implements EngineWorldManager {
|
||||
for (IrisEntitySpawn entry : spawns) {
|
||||
entry.setReferenceSpawner(spawner);
|
||||
entry.setReferenceMarker(spawner.getReferenceMarker());
|
||||
pool.add(entry);
|
||||
if (remainingCapacity(entry, spawner, chunkMobs) > 0) {
|
||||
pool.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int spawnEntry(NativeWorld level, IrisEntitySpawn entry, IrisSpawner spawner, int chunkX, int chunkZ) {
|
||||
private int remainingCapacity(IrisEntitySpawn entry, IrisSpawner spawner, Map<String, Integer> counts) {
|
||||
IrisEntity entity = entry.getRealEntity(engine);
|
||||
return spawner.remainingCapacity(entity, counts);
|
||||
}
|
||||
|
||||
private int spawnEntry(NativeWorld level, IrisEntitySpawn entry, IrisSpawner spawner, int chunkX, int chunkZ, int remainingCapacity) {
|
||||
IrisEntity irisEntity = entry.getRealEntity(engine);
|
||||
if (irisEntity == null) {
|
||||
return 0;
|
||||
@@ -572,7 +581,7 @@ public final class ModdedWorldManager implements EngineWorldManager {
|
||||
|
||||
int min = entry.getMinSpawns();
|
||||
int max = entry.getMaxSpawns();
|
||||
int count = LootResolver.inclusive(RNG.r, min, max);
|
||||
int count = Math.min(Math.max(0, remainingCapacity), LootResolver.inclusive(RNG.r, min, max));
|
||||
if (count <= 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -593,7 +602,7 @@ public final class ModdedWorldManager implements EngineWorldManager {
|
||||
}
|
||||
IrisPosition caveFloor = caveFloors.getRandom(RNG.r);
|
||||
worldX = caveFloor.getX();
|
||||
worldY = caveFloor.getY() + 1;
|
||||
worldY = caveFloor.getY();
|
||||
worldZ = caveFloor.getZ();
|
||||
} else {
|
||||
worldX = (chunkX << 4) + RNG.r.i(16);
|
||||
@@ -708,10 +717,6 @@ public final class ModdedWorldManager implements EngineWorldManager {
|
||||
|| NativeSpawnQueries.solid(level.getBlock(worldX, worldY + 2, worldZ));
|
||||
}
|
||||
|
||||
private int countChunkLivingEntities(NativeWorld level, int chunkX, int chunkZ) {
|
||||
return NativeSpawnQueries.livingEntities(level, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerChunkCache.tickChunks rebuilds NaturalSpawner.SpawnState every tick from every entity in the
|
||||
* level, so read that instead of walking all entities again on an Iris timer. The count is the natural
|
||||
|
||||
Reference in new issue
Block a user