9
0
mirror of https://github.com/VolmitSoftware/Iris.git synced 2025-12-19 23:19:21 +00:00

Mob spawning fixes (#1169)

* fix cooldown being 0 in most cases

* fix max entity count for spawners
This commit is contained in:
Julian Krings
2025-02-11 23:06:47 +01:00
committed by GitHub
parent d7270f66e1
commit ac03a977aa
6 changed files with 155 additions and 202 deletions

View File

@@ -62,6 +62,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -299,28 +300,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
energy += 1.2; energy += 1.2;
} }
//@builder
IrisBiome biome = IrisSettings.get().getWorld().isAnbientEntitySpawningSystem()
? getEngine().getSurfaceBiome(c) : null;
IrisEntitySpawn v = IrisSettings.get().getWorld().isAnbientEntitySpawningSystem()
? spawnRandomly(Stream.concat(getData().getSpawnerLoader()
.loadAll(getDimension().getEntitySpawners())
.shuffleCopy(RNG.r).stream()
.filter(this::canSpawn)
.filter((i) -> i.isValid(biome))
.flatMap((i) -> stream(i, initial)),
Stream.concat(getData().getSpawnerLoader()
.loadAll(getEngine().getRegion(c.getX() << 4, c.getZ() << 4).getEntitySpawners())
.shuffleCopy(RNG.r).stream().filter(this::canSpawn)
.flatMap((i) -> stream(i, initial)),
getData().getSpawnerLoader()
.loadAll(getEngine().getSurfaceBiome(c.getX() << 4, c.getZ() << 4).getEntitySpawners())
.shuffleCopy(RNG.r).stream().filter(this::canSpawn)
.flatMap((i) -> stream(i, initial))))
.collect(Collectors.toList()))
.popRandom(RNG.r) : null;
//@done
if (IrisSettings.get().getWorld().isMarkerEntitySpawningSystem()) { if (IrisSettings.get().getWorld().isMarkerEntitySpawningSystem()) {
getSpawnersFromMarkers(c).forEach((blockf, spawners) -> { getSpawnersFromMarkers(c).forEach((blockf, spawners) -> {
if (spawners.isEmpty()) { if (spawners.isEmpty()) {
@@ -335,16 +314,39 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}); });
} }
if (v != null && v.getReferenceSpawner() != null) { if (!IrisSettings.get().getWorld().isAnbientEntitySpawningSystem()) {
int maxEntCount = v.getReferenceSpawner().getMaxEntitiesPerChunk();
for (Entity i : c.getEntities()) {
if (i instanceof LivingEntity) {
if (-maxEntCount <= 0) {
return; return;
} }
}
} //@builder
Predicate<IrisSpawner> filter = i -> i.canSpawn(getEngine(), c.getX(), c.getZ());
ChunkCounter counter = new ChunkCounter(c.getEntities());
IrisBiome biome = getEngine().getSurfaceBiome(c);
IrisEntitySpawn v = spawnRandomly(Stream.concat(getData().getSpawnerLoader()
.loadAll(getDimension().getEntitySpawners())
.shuffleCopy(RNG.r)
.stream()
.filter(filter)
.filter((i) -> i.isValid(biome)),
Stream.concat(getData()
.getSpawnerLoader()
.loadAll(getEngine().getRegion(c.getX() << 4, c.getZ() << 4).getEntitySpawners())
.shuffleCopy(RNG.r)
.stream()
.filter(filter),
getData().getSpawnerLoader()
.loadAll(getEngine().getSurfaceBiome(c.getX() << 4, c.getZ() << 4).getEntitySpawners())
.shuffleCopy(RNG.r)
.stream()
.filter(filter)))
.filter(counter)
.flatMap((i) -> stream(i, initial))
.collect(Collectors.toList()))
.getRandom();
//@done
if (v == null || v.getReferenceSpawner() == null)
return;
try { try {
spawn(c, v); spawn(c, v);
@@ -352,77 +354,27 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
J.s(() -> spawn(c, v)); J.s(() -> spawn(c, v));
} }
} }
}
private void spawn(Chunk c, IrisEntitySpawn i) { private void spawn(Chunk c, IrisEntitySpawn i) {
boolean allow = true; IrisSpawner ref = i.getReferenceSpawner();
if (!i.getReferenceSpawner().getMaximumRatePerChunk().isInfinite()) {
allow = false;
IrisEngineChunkData cd = getEngine().getEngineData().getChunk(c.getX(), c.getZ());
IrisEngineSpawnerCooldown sc = null;
for (IrisEngineSpawnerCooldown j : cd.getCooldowns()) {
if (j.getSpawner().equals(i.getReferenceSpawner().getLoadKey())) {
sc = j;
break;
}
}
if (sc == null) {
sc = new IrisEngineSpawnerCooldown();
sc.setSpawner(i.getReferenceSpawner().getLoadKey());
cd.getCooldowns().add(sc);
}
if (sc.canSpawn(i.getReferenceSpawner().getMaximumRatePerChunk())) {
sc.spawn(getEngine());
allow = true;
}
}
if (allow) {
int s = i.spawn(getEngine(), c, RNG.r); int s = i.spawn(getEngine(), c, RNG.r);
actuallySpawned += s; actuallySpawned += s;
if (s > 0) { if (s > 0) {
getCooldown(i.getReferenceSpawner()).spawn(getEngine()); ref.spawn(getEngine(), c.getX(), c.getZ());
energy -= s * ((i.getEnergyMultiplier() * i.getReferenceSpawner().getEnergyMultiplier() * 1)); energy -= s * ((i.getEnergyMultiplier() * ref.getEnergyMultiplier() * 1));
}
}
}
private void spawn(IrisPosition c, IrisEntitySpawn i) {
boolean allow = true;
if (!i.getReferenceSpawner().getMaximumRatePerChunk().isInfinite()) {
allow = false;
IrisEngineChunkData cd = getEngine().getEngineData().getChunk(c.getX() >> 4, c.getZ() >> 4);
IrisEngineSpawnerCooldown sc = null;
for (IrisEngineSpawnerCooldown j : cd.getCooldowns()) {
if (j.getSpawner().equals(i.getReferenceSpawner().getLoadKey())) {
sc = j;
break;
} }
} }
if (sc == null) { private void spawn(IrisPosition pos, IrisEntitySpawn i) {
sc = new IrisEngineSpawnerCooldown(); IrisSpawner ref = i.getReferenceSpawner();
sc.setSpawner(i.getReferenceSpawner().getLoadKey()); if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ()))
cd.getCooldowns().add(sc); return;
}
if (sc.canSpawn(i.getReferenceSpawner().getMaximumRatePerChunk())) { int s = i.spawn(getEngine(), pos, RNG.r);
sc.spawn(getEngine());
allow = true;
}
}
if (allow) {
int s = i.spawn(getEngine(), c, RNG.r);
actuallySpawned += s; actuallySpawned += s;
if (s > 0) { if (s > 0) {
getCooldown(i.getReferenceSpawner()).spawn(getEngine()); ref.spawn(getEngine(), pos.getX() >> 4, pos.getZ() >> 4);
energy -= s * ((i.getEnergyMultiplier() * i.getReferenceSpawner().getEnergyMultiplier() * 1)); energy -= s * ((i.getEnergyMultiplier() * ref.getEnergyMultiplier() * 1));
}
} }
} }
@@ -450,31 +402,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
return rarityTypes; return rarityTypes;
} }
public boolean canSpawn(IrisSpawner i) {
return i.isValid(getEngine().getWorld().realWorld())
&& getCooldown(i).canSpawn(i.getMaximumRate());
}
private IrisEngineSpawnerCooldown getCooldown(IrisSpawner i) {
IrisEngineData ed = getEngine().getEngineData();
IrisEngineSpawnerCooldown cd = null;
for (IrisEngineSpawnerCooldown j : ed.getSpawnerCooldowns().copy()) {
if (j.getSpawner().equals(i.getLoadKey())) {
cd = j;
}
}
if (cd == null) {
cd = new IrisEngineSpawnerCooldown();
cd.setSpawner(i.getLoadKey());
cd.setLastSpawn(M.ms() - i.getMaximumRate().getInterval());
ed.getSpawnerCooldowns().add(cd);
}
return cd;
}
@Override @Override
public void onTick() { public void onTick() {
@@ -708,4 +635,27 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
return (double) entityCount / (getEngine().getWorld().realWorld().getLoadedChunks().length + 1) * 1.28; return (double) entityCount / (getEngine().getWorld().realWorld().getLoadedChunks().length + 1) * 1.28;
} }
@Data
private static class ChunkCounter implements Predicate<IrisSpawner> {
private final Entity[] entities;
private transient int index = 0;
private transient int count = 0;
@Override
public boolean test(IrisSpawner spawner) {
int max = spawner.getMaxEntitiesPerChunk();
if (max <= count)
return false;
while (index < entities.length) {
if (entities[index++] instanceof LivingEntity) {
if (++count >= max)
return false;
}
}
return true;
}
}
} }

View File

@@ -1,43 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import lombok.Data;
@Data
public class IrisEngineChunkData {
private long chunk;
private KList<IrisEngineSpawnerCooldown> cooldowns = new KList<>();
public void cleanup(Engine engine) {
for (IrisEngineSpawnerCooldown i : getCooldowns().copy()) {
IrisSpawner sp = engine.getData().getSpawnerLoader().load(i.getSpawner());
if (sp == null || i.canSpawn(sp.getMaximumRate())) {
getCooldowns().remove(i);
}
}
}
public boolean isEmpty() {
return cooldowns.isEmpty();
}
}

View File

@@ -20,51 +20,31 @@ package com.volmit.iris.engine.object;
import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
@Data @Data
public class IrisEngineData { @EqualsAndHashCode(callSuper = true)
public class IrisEngineData extends IrisSpawnerCooldowns {
private IrisEngineStatistics statistics = new IrisEngineStatistics(); private IrisEngineStatistics statistics = new IrisEngineStatistics();
private KList<IrisEngineSpawnerCooldown> spawnerCooldowns = new KList<>(); private KMap<Long, IrisSpawnerCooldowns> chunks = new KMap<>();
private KList<IrisEngineChunkData> chunks = new KList<>();
private Long seed = null; private Long seed = null;
public void removeChunk(int x, int z) { public void removeChunk(int x, int z) {
long k = Cache.key(x, z); chunks.remove(Cache.key(x, z));
chunks.removeWhere((i) -> i.getChunk() == k);
} }
public IrisEngineChunkData getChunk(int x, int z) { public IrisSpawnerCooldowns getChunk(int x, int z) {
long k = Cache.key(x, z); return chunks.computeIfAbsent(Cache.key(x, z), k -> new IrisSpawnerCooldowns());
for (IrisEngineChunkData i : chunks) {
if (i.getChunk() == k) {
return i;
}
}
IrisEngineChunkData c = new IrisEngineChunkData();
c.setChunk(k);
chunks.add(c);
return c;
} }
public void cleanup(Engine engine) { public void cleanup(Engine engine) {
for (IrisEngineSpawnerCooldown i : getSpawnerCooldowns().copy()) { super.cleanup(engine);
IrisSpawner sp = engine.getData().getSpawnerLoader().load(i.getSpawner());
if (sp == null || i.canSpawn(sp.getMaximumRate())) { chunks.values().removeIf(chunk -> {
getSpawnerCooldowns().remove(i); chunk.cleanup(engine);
} return chunk.isEmpty();
} });
for (IrisEngineChunkData i : chunks.copy()) {
i.cleanup(engine);
if (i.isEmpty()) {
getChunks().remove(i);
}
}
} }
} }

View File

@@ -43,7 +43,7 @@ public class IrisRate {
} }
public long getInterval() { public long getInterval() {
long t = per.getMilliseconds() / (amount == 0 ? 1 : amount); long t = per.toMilliseconds() / (amount == 0 ? 1 : amount);
return Math.abs(t <= 0 ? 1 : t); return Math.abs(t <= 0 ? 1 : t);
} }

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.engine.object; package com.volmit.iris.engine.object;
import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.annotations.ArrayType; import com.volmit.iris.engine.object.annotations.ArrayType;
import com.volmit.iris.engine.object.annotations.Desc; import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
@@ -95,6 +96,37 @@ public class IrisSpawner extends IrisRegistrant {
return timeBlock.isWithin(world) && weather.is(world); return timeBlock.isWithin(world) && weather.is(world);
} }
public boolean canSpawn(Engine engine) {
if (!isValid(engine.getWorld().realWorld()))
return false;
var rate = getMaximumRate();
return rate.isInfinite() || engine.getEngineData().getCooldown(this).canSpawn(rate);
}
public boolean canSpawn(Engine engine, int x, int z) {
if (!canSpawn(engine))
return false;
var rate = getMaximumRatePerChunk();
return rate.isInfinite() || engine.getEngineData().getChunk(x, z).getCooldown(this).canSpawn(rate);
}
public void spawn(Engine engine) {
if (getMaximumRate().isInfinite())
return;
engine.getEngineData().getCooldown(this).spawn(engine);
}
public void spawn(Engine engine, int x, int z) {
spawn(engine);
if (getMaximumRatePerChunk().isInfinite())
return;
engine.getEngineData().getChunk(x, z).getCooldown(this).spawn(engine);
}
@Override @Override
public String getFolderName() { public String getFolderName() {
return "spawners"; return "spawners";

View File

@@ -0,0 +1,34 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
@EqualsAndHashCode
public class IrisSpawnerCooldowns {
private final KMap<String, IrisEngineSpawnerCooldown> cooldowns = new KMap<>();
public IrisEngineSpawnerCooldown getCooldown(@NonNull IrisSpawner spawner) {
return getCooldown(spawner.getLoadKey());
}
public IrisEngineSpawnerCooldown getCooldown(@NonNull String loadKey) {
return cooldowns.computeIfAbsent(loadKey, k -> {
IrisEngineSpawnerCooldown cd = new IrisEngineSpawnerCooldown();
cd.setSpawner(loadKey);
return cd;
});
}
public void cleanup(Engine engine) {
cooldowns.values().removeIf(cd -> {
IrisSpawner sp = engine.getData().getSpawnerLoader().load(cd.getSpawner());
return sp == null || cd.canSpawn(sp.getMaximumRate());
});
}
public boolean isEmpty() {
return cooldowns.isEmpty();
}
}