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

initial rewrite of the ambient entity spawning

This commit is contained in:
Julian Krings
2025-04-15 15:50:38 +02:00
parent e6874a0e10
commit 9e0258089b
13 changed files with 480 additions and 152 deletions

View File

@@ -21,7 +21,6 @@ package com.volmit.iris.core.commands;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.pregenerator.ChunkUpdater;
import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisPackBenchmarking;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;

View File

@@ -44,6 +44,7 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.*;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -219,6 +220,10 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return s.map(this::load);
}
public Stream<T> streamAllPossible() {
return streamAll(Arrays.stream(getPossibleKeys()));
}
public KList<T> loadAll(KList<String> s) {
KList<T> m = new KList<>();

View File

@@ -27,7 +27,6 @@ import com.volmit.iris.engine.object.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.math.M;
@@ -72,7 +71,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
private final Looper looper;
private final int id;
private final KList<Runnable> updateQueue = new KList<>();
private final ChronoLatch cl;
private final ChronoLatch clw;
private final ChronoLatch ecl;
private final ChronoLatch cln;
@@ -83,12 +81,10 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
private long charge = 0;
private int actuallySpawned = 0;
private int cooldown = 0;
private List<Entity> precount = new KList<>();
private KSet<Position2> injectBiomes = new KSet<>();
public IrisWorldManager() {
super(null);
cl = null;
ecl = null;
cln = null;
clw = null;
@@ -103,7 +99,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
chunkUpdater = new ChronoLatch(3000);
chunkDiscovery = new ChronoLatch(5000);
cln = new ChronoLatch(60000);
cl = new ChronoLatch(3000);
ecl = new ChronoLatch(250);
clw = new ChronoLatch(1000, true);
id = engine.getCacheID();
@@ -151,27 +146,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
engine.getEngineData().cleanup(getEngine());
}
if (precount != null) {
entityCount = 0;
for (Entity i : precount) {
if (i instanceof LivingEntity) {
if (!i.isDead()) {
entityCount++;
}
}
}
precount = null;
}
if (energy < 650) {
if (ecl.flip()) {
energy *= 1 + (0.02 * M.clip((1D - getEntitySaturation()), 0D, 1D));
fixEnergy();
}
}
onAsyncTick();
}
return IrisSettings.get().getWorld().getAsyncTickIntervalMS();
@@ -214,7 +194,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
int finalZ = c.getZ() + z;
J.a(() -> getMantle().raiseFlag(finalX, finalZ, MantleFlag.INITIAL_SPAWNED_MARKER,
() -> {
J.a(() -> spawnIn(cx, true), RNG.r.i(5, 200));
J.a(() -> spawnIn(cx), RNG.r.i(5, 200));
getSpawnersFromMarkers(cx).forEach((blockf, spawners) -> {
if (spawners.isEmpty()) {
return;
@@ -222,7 +202,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
IrisPosition block = new IrisPosition(blockf.getX(), blockf.getY() + getEngine().getWorld().minHeight(), blockf.getZ());
IrisSpawner s = new KList<>(spawners).getRandom();
spawn(block, s, true);
spawn(block, s);
});
}));
}
@@ -232,73 +212,16 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}
}
private boolean onAsyncTick() {
if (getEngine().isClosed()) {
return false;
}
actuallySpawned = 0;
if (energy < 100) {
J.sleep(200);
return false;
}
if (!getEngine().getWorld().hasRealWorld()) {
Iris.debug("Can't spawn. No real world");
J.sleep(5000);
return false;
}
double epx = getEntitySaturation();
if (epx > IrisSettings.get().getWorld().getTargetSpawnEntitiesPerChunk()) {
Iris.debug("Can't spawn. The entity per chunk ratio is at " + Form.pc(epx, 2) + " > 100% (total entities " + entityCount + ")");
J.sleep(5000);
return false;
}
if (cl.flip()) {
try {
J.s(() -> precount = getEngine().getWorld().realWorld().getEntities());
} catch (Throwable e) {
close();
}
}
int spawnBuffer = RNG.r.i(2, 12);
Chunk[] cc = getEngine().getWorld().realWorld().getLoadedChunks();
while (spawnBuffer-- > 0) {
if (cc.length == 0) {
Iris.debug("Can't spawn. No chunks!");
return false;
}
Chunk c = cc[RNG.r.nextInt(cc.length)];
if (!c.isLoaded() || !Chunks.isSafe(c.getWorld(), c.getX(), c.getZ())) {
continue;
}
spawnIn(c, false);
}
energy -= (actuallySpawned / 2D);
return actuallySpawned > 0;
}
private void fixEnergy() {
energy = M.clip(energy, 1D, getDimension().getMaximumEnergy());
}
private void spawnIn(Chunk c, boolean initial) {
private void spawnIn(Chunk c) {
if (getEngine().isClosed()) {
return;
}
if (initial) {
energy += 1.2;
}
energy += 1.2;
if (IrisSettings.get().getWorld().isMarkerEntitySpawningSystem()) {
getSpawnersFromMarkers(c).forEach((blockf, spawners) -> {
@@ -308,9 +231,8 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
IrisPosition block = new IrisPosition(blockf.getX(), blockf.getY() + getEngine().getWorld().minHeight(), blockf.getZ());
IrisSpawner s = new KList<>(spawners).getRandom();
spawn(block, s, false);
J.a(() -> getMantle().raiseFlag(c.getX(), c.getZ(), MantleFlag.INITIAL_SPAWNED_MARKER,
() -> spawn(block, s, true)));
() -> spawn(block, s)));
});
}
@@ -341,7 +263,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
.stream()
.filter(filter)))
.filter(counter)
.flatMap((i) -> stream(i, initial))
.flatMap(this::stream)
.collect(Collectors.toList()))
.getRandom();
//@done
@@ -378,13 +300,13 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}
}
private Stream<IrisEntitySpawn> stream(IrisSpawner s, boolean initial) {
for (IrisEntitySpawn i : initial ? s.getInitialSpawns() : s.getSpawns()) {
private Stream<IrisEntitySpawn> stream(IrisSpawner s) {
for (IrisEntitySpawn i : s.getInitialSpawns()) {
i.setReferenceSpawner(s);
i.setReferenceMarker(s.getReferenceMarker());
}
return (initial ? s.getInitialSpawns() : s.getSpawns()).stream();
return (s.getInitialSpawns()).stream();
}
private KList<IrisEntitySpawn> spawnRandomly(List<IrisEntitySpawn> types) {
@@ -431,7 +353,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}
}
private void spawn(IrisPosition block, IrisSpawner spawner, boolean initial) {
private void spawn(IrisPosition block, IrisSpawner spawner) {
if (getEngine().isClosed()) {
return;
}
@@ -440,7 +362,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
return;
}
KList<IrisEntitySpawn> s = initial ? spawner.getInitialSpawns() : spawner.getSpawns();
KList<IrisEntitySpawn> s = spawner.getInitialSpawns();
if (s.isEmpty()) {
return;
}

View File

@@ -404,7 +404,7 @@ public class IrisEntity extends IrisRegistrant {
});
return e;
return e.isValid() ? e : null;
}
private int surfaceY(Location l) {

View File

@@ -28,19 +28,12 @@ import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.matter.MatterMarker;
import com.volmit.iris.util.matter.slices.MarkerMatter;
import io.lumine.mythic.bukkit.adapters.BukkitEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.util.BoundingBox;
@Snippet("entity-spawn")
@Accessors(chain = true)
@@ -69,6 +62,10 @@ public class IrisEntitySpawn implements IRare {
private transient IrisSpawner referenceSpawner;
private transient IrisMarker referenceMarker;
public boolean check(Engine eng, IrisPosition c, ChunkSnapshot snapshot) {
return getRealEntity(eng).getSurface().matches(snapshot.getBlockData(c.getX() & 15, c.getY(), c.getZ() & 15));
}
public int spawn(Engine gen, Chunk c, RNG rng) {
int spawns = minSpawns == maxSpawns ? minSpawns : rng.i(Math.min(minSpawns, maxSpawns), Math.max(minSpawns, maxSpawns));
int s = 0;
@@ -168,7 +165,7 @@ public class IrisEntitySpawn implements IRare {
return null;
}
if (!ignoreSurfaces && !irisEntity.getSurface().matches(at.clone().subtract(0, 1, 0).getBlock())) {
if (!ignoreSurfaces && !irisEntity.getSurface().matches(at.clone().subtract(0, 1, 0).getBlock().getBlockData())) {
return null;
}
@@ -183,6 +180,10 @@ public class IrisEntitySpawn implements IRare {
Entity e = irisEntity.spawn(g, at.add(0.5, 0.5, 0.5), rng.aquire(() -> new RNG(g.getSeedManager().getEntity())));
if (e != null) {
Iris.debug("Spawned " + C.DARK_AQUA + "Entity<" + getEntity() + "> " + C.GREEN + e.getType() + C.LIGHT_PURPLE + " @ " + C.GRAY + e.getLocation().getX() + ", " + e.getLocation().getY() + ", " + e.getLocation().getZ());
if (referenceSpawner != null) {
referenceSpawner.getConditions().apply(e);
}
}
return e;

View File

@@ -0,0 +1,58 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.entity.SpawnCategory;
import org.bukkit.persistence.PersistentDataType;
import java.util.UUID;
import java.util.function.BooleanSupplier;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Conditions for a spawner to be triggered")
@Data
public class IrisSpawnCondition {
private static final NamespacedKey CATEGORY_KEY = new NamespacedKey(Iris.instance, "spawn_category");
private SpawnCategory category = SpawnCategory.AMBIENT;
private int maxEntities = 60;
public boolean check(KMap<UUID, KMap<String, Boolean>> cache, KList<Entity> entities) {
int entityCount = 0;
for (Entity entity : entities) {
var map = cache.computeIfAbsent(entity.getUniqueId(), k -> new KMap<>());
if (check(map, "category_" + category.name(), () -> checkCategory(entity, category)) && ++entityCount >= maxEntities)
return false;
}
return true;
}
public void apply(Entity entity) {
var pdc = entity.getPersistentDataContainer();
pdc.set(CATEGORY_KEY, PersistentDataType.STRING, category.name());
}
private static boolean check(KMap<String, Boolean> cache, String key, BooleanSupplier predicate) {
return cache.computeIfAbsent(key, k -> predicate.getAsBoolean()) == Boolean.TRUE;
}
private static boolean checkCategory(Entity entity, SpawnCategory category) {
if (entity.getSpawnCategory() == category)
return true;
var pdc = entity.getPersistentDataContainer();
if (!pdc.has(CATEGORY_KEY, PersistentDataType.STRING))
return false;
return category.name().equals(pdc.get(CATEGORY_KEY, PersistentDataType.STRING));
}
}

View File

@@ -74,6 +74,9 @@ public class IrisSpawner extends IrisRegistrant {
@Desc("Where should these spawns be placed")
private IrisSpawnGroup group = IrisSpawnGroup.NORMAL;
@Desc("Conditions for this spawner to be triggered")
private IrisSpawnCondition conditions = new IrisSpawnCondition();
public boolean isValid(IrisBiome biome) {
return switch (group) {
case NORMAL -> switch (biome.getInferredType()) {

View File

@@ -20,7 +20,7 @@ package com.volmit.iris.engine.object;
import com.volmit.iris.engine.object.annotations.Desc;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
@Desc("The type of surface entities should spawn on")
@@ -47,8 +47,8 @@ public enum IrisSurface {
* @param state The blockstate
* @return True if it matches
*/
public boolean matches(Block state) {
Material type = state.getType();
public boolean matches(BlockData state) {
Material type = state.getMaterial();
if (type.isSolid()) {
return this == LAND || this == OVERWORLD || (this == ANIMAL
&& (type == Material.GRASS_BLOCK || type == Material.DIRT

View File

@@ -0,0 +1,305 @@
package com.volmit.iris.engine.service;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.engine.IrisWorldManager;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.MatterMarker;
import com.volmit.iris.util.parallel.Sync;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import io.papermc.lib.PaperLib;
import lombok.SneakyThrows;
import org.bukkit.ChunkSnapshot;
import org.bukkit.GameRule;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class EngineMobHandlerSVC extends IrisEngineService {
private static final List<String> CAVE_TAGS = List.of("cave_floor", "cave_ceiling");
private static final int SAFE_RADIUS = 16;
private static final int MAX_RADIUS = 128;
private final AtomicLong currentTick = new AtomicLong();
private final Sync<Long> sync = new Sync<>();
private final Set<Player> players = ConcurrentHashMap.newKeySet();
private KList<Entity> entities = new KList<>();
private Thread asyncTicker = null;
private Thread entityCollector = null;
private int task = -1;
public EngineMobHandlerSVC(Engine engine) {
super(engine);
}
@Override
public void onEnable(boolean hotload) {
if (task != -1) J.csr(task);
task = J.sr(() -> sync.advance(currentTick.getAndIncrement()), 0);
cancel(asyncTicker);
cancel(entityCollector);
asyncTicker = Thread.ofPlatform()
.name("Iris Async Mob Spawning - " + engine.getWorld().name())
.priority(9)
.start(() -> {
while (!engine.isClosed()) {
if (Thread.interrupted())
return;
try {
asyncTick();
} catch (Throwable e) {
Iris.error("Error in async tick for " + engine.getWorld().name());
e.printStackTrace();
J.sleep(100);
}
}
});
entityCollector = Thread.ofVirtual()
.name("Iris Async Entity Collector - " + engine.getWorld().name())
.start(() -> {
while (!engine.isClosed()) {
if (Thread.interrupted())
return;
try {
sync.next().join();
var world = engine.getWorld().realWorld();
if (world == null) continue;
J.s(() -> entities = new KList<>(world.getEntities()));
} catch (Throwable e) {
Iris.error("Error in async tick for " + engine.getWorld().name());
e.printStackTrace();
J.sleep(100);
}
}
});
}
@Override
public void onDisable(boolean hotload) {
J.csr(task);
cancel(asyncTicker);
cancel(entityCollector);
}
@SneakyThrows
private void asyncTick() {
long tick = sync.next().join();
var manager = (IrisWorldManager) engine.getWorldManager();
var world = engine.getWorld().realWorld();
if (world == null
|| noSpawning()
|| Boolean.FALSE.equals(world.getGameRuleValue(GameRule.DO_MOB_SPAWNING))
|| players.isEmpty()
|| manager.getEnergy() < 100)
return;
var p = PrecisionStopwatch.start();
var entities = new KList<>(this.entities);
var conditionCache = new KMap<UUID, KMap<String, Boolean>>();
var data = engine.getData();
var invalid = data.getSpawnerLoader()
.streamAllPossible()
.filter(Predicate.not(spawner -> spawner.canSpawn(engine)
&& spawner.getConditions().check(conditionCache, entities)))
.map(IrisSpawner::getLoadKey)
.collect(Collectors.toSet());
var centers = players.stream()
.filter(Objects::nonNull)
.filter(Player::isOnline)
.map(Player::getLocation)
.map(BlockPosition::fromLocation)
.collect(KList.collector())
.shuffle();
if (centers.isEmpty())
return;
double delta = 0;
int actuallySpawned = 0;
KMap<Position2, Pair<Entity[], ChunkSnapshot>> cache = new KMap<>();
while (centers.isNotEmpty()) {
var center = centers.pop();
var pos = center.randomPoint(MAX_RADIUS, SAFE_RADIUS);
if (pos.getY() < world.getMinHeight() || pos.getY() >= world.getMaxHeight())
continue;
var chunkPos = new Position2(center.getX() >> 4, center.getZ() >> 4);
var pair = cache.computeIfAbsent(chunkPos, cPos -> {
try {
return PaperLib.getChunkAtAsync(world, cPos.getX(), cPos.getZ(), false)
.thenApply(c -> c != null ? new Pair<>(c.getEntities(), c.getChunkSnapshot(false, false, false)) : null)
.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
});
if (pair == null)
continue;
var spawners = spawnersAt(pair.getB(), pos, invalid);
spawners.removeIf(i -> invalid.contains(i.getLoadKey()));
spawners.removeIf(i -> !i.canSpawn(engine, chunkPos.getX(), chunkPos.getZ()));
if (spawners.isEmpty())
continue;
boolean failed = true;
IrisPosition irisPos = new IrisPosition(pos.getX(), pos.getY(), pos.getZ());
for (var spawner : spawners) {
var spawns = spawner.getSpawns().copy();
spawns.removeIf(spawn -> !spawn.check(engine, irisPos, pair.getB()));
var entity = IRare.pick(spawns, RNG.r.nextDouble());
if (entity == null)
continue;
entity.setReferenceSpawner(spawner);
entity.setReferenceMarker(spawner.getReferenceMarker());
int spawned = entity.spawn(engine, irisPos, RNG.r);
if (spawned == 0)
continue;
delta += spawned * ((entity.getEnergyMultiplier() * spawner.getEnergyMultiplier() * 1));
actuallySpawned += spawned;
spawner.spawn(engine, chunkPos.getX(), chunkPos.getZ());
if (!spawner.canSpawn(engine))
invalid.add(spawner.getLoadKey());
failed = false;
break;
}
if (failed && p.getMilliseconds() < 1000)
centers.add(center);
}
manager.setEnergy(manager.getEnergy() - delta);
if (actuallySpawned > 0) {
Iris.info("Async Mob Spawning " + world.getName() + " used " + delta + " energy and took " + Form.duration((long) p.getMilliseconds()));
}
}
private KSet<IrisSpawner> spawnersAt(ChunkSnapshot chunk, BlockPosition pos, Set<String> invalid) {
KSet<IrisSpawner> spawners = markerAt(chunk, pos, invalid);
var loader = engine.getData().getSpawnerLoader();
int y = pos.getY() - engine.getWorld().minHeight();
Stream.concat(engine.getRegion(pos.getX(), pos.getZ())
.getEntitySpawners()
.stream(),
engine.getBiomeOrMantle(pos.getX(), y, pos.getZ())
.getEntitySpawners()
.stream())
.filter(Predicate.not(invalid::contains))
.map(loader::load)
.forEach(spawners::add);
return spawners;
}
private KSet<IrisSpawner> markerAt(ChunkSnapshot chunk, BlockPosition pos, Set<String> invalid) {
if (!IrisSettings.get().getWorld().isMarkerEntitySpawningSystem())
return new KSet<>();
int y = pos.getY() - engine.getWorld().minHeight();
Mantle mantle = engine.getMantle().getMantle();
MatterMarker matter = mantle.get(pos.getX(), y, pos.getZ(), MatterMarker.class);
if (matter == null || CAVE_TAGS.contains(matter.getTag()))
return new KSet<>();
IrisData data = engine.getData();
IrisMarker mark = data.getMarkerLoader().load(matter.getTag());
if (mark == null)
return new KSet<>();
if (mark.isEmptyAbove()) {
int x = pos.getX() & 15, z = pos.getZ() & 15;
boolean remove = chunk.getBlockData(x, pos.getY() + 1, z).getMaterial().isSolid() || chunk.getBlockData(x, pos.getY() + 2, z).getMaterial().isSolid();
if (remove) {
mantle.remove(pos.getX(), y, pos.getZ(), MatterMarker.class);
return new KSet<>();
}
}
KSet<IrisSpawner> spawners = new KSet<>();
for (String key : mark.getSpawners()) {
if (invalid.contains(key))
continue;
IrisSpawner spawner = data.getSpawnerLoader().load(key);
if (spawner == null) {
Iris.error("Cannot load spawner: " + key + " for marker " + matter.getTag());
continue;
}
spawner.setReferenceMarker(mark);
spawners.add(spawner);
}
return spawners;
}
@EventHandler(priority = EventPriority.LOWEST)
public void on(PlayerJoinEvent event) {
var player = event.getPlayer();
if (player.getWorld() != engine.getWorld().realWorld())
return;
players.add(player);
}
@EventHandler(priority = EventPriority.LOWEST)
public void on(PlayerQuitEvent event) {
players.remove(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void on(PlayerChangedWorldEvent event) {
var player = event.getPlayer();
if (player.getWorld() == engine.getWorld().realWorld())
players.add(player);
else
players.remove(player);
}
@SneakyThrows
private static void cancel(Thread thread) {
if (thread == null || !thread.isAlive()) return;
thread.interrupt();
}
private static boolean noSpawning() {
var world = IrisSettings.get().getWorld();
return !world.isMarkerEntitySpawningSystem() && !world.isAnbientEntitySpawningSystem();
}
}

View File

@@ -21,7 +21,6 @@ package com.volmit.iris.util.mantle;
import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
@@ -425,7 +424,7 @@ public class Mantle {
ioTrim.set(true);
unloadLock.lock();
try {
if (lastUse != null && IrisEngineSVC.instance != null) {
if (lastUse != null) {
if (!lastUse.isEmpty()) {
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0));
for (long i : new ArrayList<>(lastUse.keySet())) {
@@ -435,7 +434,6 @@ public class Mantle {
if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) {
toUnload.add(i);
Iris.debug("Tectonic Region added to unload");
IrisEngineSVC.instance.trimActiveAlive.reset();
}
});
}
@@ -454,53 +452,49 @@ public class Mantle {
AtomicInteger i = new AtomicInteger();
unloadLock.lock();
BurstExecutor burst = null;
if (IrisEngineSVC.instance != null) {
try {
KList<Long> copy = toUnload.copy();
if (!disableClear) toUnload.clear();
burst = MultiBurst.burst.burst(copy.size());
burst.setMulticore(copy.size() > tectonicLimit);
for (int j = 0; j < copy.size(); j++) {
Long id = copy.get(j);
if (id == null) {
Iris.error("Null id in unloadTectonicPlate at index " + j);
continue;
}
burst.queue(() ->
hyperLock.withLong(id, () -> {
TectonicPlate m = loadedRegions.get(id);
if (m != null) {
if (m.inUse()) {
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
if (disableClear) toUnload.remove(id);
lastUse.put(id, M.ms());
return;
}
try {
m.write(fileForRegion(dataFolder, id));
loadedRegions.remove(id);
lastUse.remove(id);
if (disableClear) toUnload.remove(id);
i.incrementAndGet();
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
IrisEngineSVC.instance.unloadActiveAlive.reset();
} catch (IOException e) {
Iris.reportError(e);
}
}
}));
try {
KList<Long> copy = toUnload.copy();
if (!disableClear) toUnload.clear();
burst = MultiBurst.burst.burst(copy.size());
burst.setMulticore(copy.size() > tectonicLimit);
for (int j = 0; j < copy.size(); j++) {
Long id = copy.get(j);
if (id == null) {
Iris.error("Null id in unloadTectonicPlate at index " + j);
continue;
}
burst.complete();
} catch (Throwable e) {
e.printStackTrace();
if (burst != null)
burst.complete();
} finally {
unloadLock.unlock();
ioTectonicUnload.set(true);
burst.queue(() ->
hyperLock.withLong(id, () -> {
TectonicPlate m = loadedRegions.get(id);
if (m != null) {
if (m.inUse()) {
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
if (disableClear) toUnload.remove(id);
lastUse.put(id, M.ms());
return;
}
try {
m.write(fileForRegion(dataFolder, id));
loadedRegions.remove(id);
lastUse.remove(id);
if (disableClear) toUnload.remove(id);
i.incrementAndGet();
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
} catch (IOException e) {
Iris.reportError(e);
}
}
}));
}
return i.get();
burst.complete();
} catch (Throwable e) {
e.printStackTrace();
if (burst != null)
burst.complete();
} finally {
unloadLock.unlock();
ioTectonicUnload.set(true);
}
return i.get();
}

View File

@@ -19,11 +19,14 @@
package com.volmit.iris.util.math;
import lombok.Data;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import java.util.Objects;
import static com.volmit.iris.util.math.RNG.r;
@Data
public class BlockPosition {
//Magic numbers
@@ -43,6 +46,10 @@ public class BlockPosition {
this.z = z;
}
public static BlockPosition fromLocation(Location loc) {
return new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
}
public static long toLong(int x, int y, int z) {
long var3 = 0L;
var3 |= (x & m4) << m3;
@@ -108,4 +115,18 @@ public class BlockPosition {
setY(Math.max(i.getY(), getY()));
setZ(Math.max(i.getZ(), getZ()));
}
public BlockPosition randomPoint(int radius, int innerRadius) {
int max = radius * radius;
int min = innerRadius * innerRadius;
while (true) {
int x = r.nextInt(-radius, radius + 1);
int y = r.nextInt(-radius, radius + 1);
int z = r.nextInt(-radius, radius + 1);
double dist = x * x + y * y + z * z;
if (dist < min || dist > max) continue;
return add(x, y, z);
}
}
}

View File

@@ -0,0 +1,20 @@
package com.volmit.iris.util.parallel;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
public class Sync<T> {
private final AtomicReference<CompletableFuture<T>> tick = new AtomicReference<>(new CompletableFuture<>());
public void cancel(boolean mayInterruptIfRunning) {
tick.getAndSet(new CompletableFuture<>()).cancel(mayInterruptIfRunning);
}
public CompletableFuture<T> next() {
return tick.get();
}
public void advance(T value) {
tick.getAndSet(new CompletableFuture<>()).complete(value);
}
}

View File

@@ -209,7 +209,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
return WeightedRandomList.create();
}
@Override