updater fixes

This commit is contained in:
Julian Krings 2024-11-08 21:01:11 +01:00
parent 11ae48bea1
commit a1495a10e5
4 changed files with 136 additions and 48 deletions

View File

@ -43,6 +43,10 @@ public class CommandUpdater implements DecreeExecutor {
sender().sendMessage(C.GOLD + "This is not an Iris world");
return;
}
if (chunkUpdater != null) {
chunkUpdater.stop();
}
chunkUpdater = new ChunkUpdater(world);
if (sender().isPlayer()) {
sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks()));
@ -53,14 +57,7 @@ public class CommandUpdater implements DecreeExecutor {
}
@Decree(description = "Pause the updater")
public void pause(
@Param(description = "World to pause the Updater at")
World world
) {
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.GOLD + "This is not an Iris world");
return;
}
public void pause( ) {
if (chunkUpdater == null) {
sender().sendMessage(C.GOLD + "You cant pause something that doesnt exist?");
return;
@ -68,40 +65,32 @@ public class CommandUpdater implements DecreeExecutor {
boolean status = chunkUpdater.pause();
if (sender().isPlayer()) {
if (status) {
sender().sendMessage(C.IRIS + "Paused task for: " + C.GRAY + world.getName());
sender().sendMessage(C.IRIS + "Paused task for: " + C.GRAY + chunkUpdater.getName());
} else {
sender().sendMessage(C.IRIS + "Unpause task for: " + C.GRAY + world.getName());
sender().sendMessage(C.IRIS + "Unpause task for: " + C.GRAY + chunkUpdater.getName());
}
} else {
if (status) {
Iris.info(C.IRIS + "Paused task for: " + C.GRAY + world.getName());
Iris.info(C.IRIS + "Paused task for: " + C.GRAY + chunkUpdater.getName());
} else {
Iris.info(C.IRIS + "Unpause task for: " + C.GRAY + world.getName());
Iris.info(C.IRIS + "Unpause task for: " + C.GRAY + chunkUpdater.getName());
}
}
}
@Decree(description = "Stops the updater")
public void stop(
@Param(description = "World to stop the Updater at")
World world
) {
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.GOLD + "This is not an Iris world");
return;
}
public void stop() {
if (chunkUpdater == null) {
sender().sendMessage(C.GOLD + "You cant stop something that doesnt exist?");
return;
}
if (sender().isPlayer()) {
sender().sendMessage("Stopping Updater for: " + C.GRAY + world.getName());
sender().sendMessage("Stopping Updater for: " + C.GRAY + chunkUpdater.getName());
} else {
Iris.info("Stopping Updater for: " + C.GRAY + world.getName());
Iris.info("Stopping Updater for: " + C.GRAY + chunkUpdater.getName());
}
chunkUpdater.stop();
}
}

View File

@ -2,7 +2,9 @@ package com.volmit.iris.core.pregenerator;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.Form;
@ -28,8 +30,7 @@ import java.util.concurrent.atomic.AtomicLong;
public class ChunkUpdater {
private final AtomicBoolean paused = new AtomicBoolean();
private final AtomicBoolean cancelled = new AtomicBoolean();
private final KMap<Chunk, Long> lastUse = new KMap<>();
private final KMap<Chunk, AtomicInteger> counters = new KMap<>();
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>();
private final RollingSequence chunksPerSecond = new RollingSequence(5);
private final AtomicInteger totalMaxChunks = new AtomicInteger();
private final AtomicInteger chunksProcessed = new AtomicInteger();
@ -58,6 +59,10 @@ public class ChunkUpdater {
this.latch = new CountDownLatch(totalMaxChunks.get());
}
public String getName() {
return world.getName();
}
public int getChunks() {
return totalMaxChunks.get();
}
@ -83,7 +88,6 @@ public class ChunkUpdater {
cancelled.set(true);
}
private void update() {
Iris.info("Updating..");
try {
@ -192,9 +196,8 @@ public class ChunkUpdater {
for (int xx = -1; xx <= 1; xx++) {
for (int zz = -1; zz <= 1; zz++) {
var chunk = world.getChunkAt(x + xx, z + zz, false);
var counter = counters.get(chunk);
if (counter != null) counter.decrementAndGet();
var counter = lastUse.get(Cache.key(x + xx, z + zz));
if (counter != null) counter.getB().decrementAndGet();
}
}
} finally {
@ -243,9 +246,9 @@ public class ChunkUpdater {
if (!c.isGenerated())
generated.set(false);
counters.computeIfAbsent(c, k -> new AtomicInteger(-1))
.updateAndGet(i -> i == -1 ? 1 : ++i);
lastUse.put(c, M.ms());
var pair = lastUse.computeIfAbsent(Cache.key(c), k -> new Pair<>(0L, new AtomicInteger(-1)));
pair.setA(M.ms());
pair.getB().updateAndGet(i -> i == -1 ? 1 : ++i);
} finally {
latch.countDown();
}
@ -262,15 +265,22 @@ public class ChunkUpdater {
}
private synchronized void unloadChunks() {
for (Chunk i : new ArrayList<>(lastUse.keySet())) {
Long lastUseTime = lastUse.get(i);
var counter = counters.get(i);
if (lastUseTime != null && M.ms() - lastUseTime >= 5000 && (counter == null || counter.get() == 0)) {
for (var key : new ArrayList<>(lastUse.keySet())) {
if (key == null) continue;
var pair = lastUse.get(key);
if (pair == null) continue;
var lastUseTime = pair.getA();
var counter = pair.getB();
if (lastUseTime == null || counter == null)
continue;
if (M.ms() - lastUseTime >= 5000 && counter.get() == 0) {
int x = Cache.keyX(key);
int z = Cache.keyZ(key);
J.s(() -> {
i.removePluginChunkTicket(Iris.instance);
i.unload();
lastUse.remove(i);
counters.remove(i);
world.removePluginChunkTicket(x, z, Iris.instance);
world.unloadChunk(x, z);
lastUse.remove(key);
});
}
}

View File

@ -82,6 +82,7 @@ import java.awt.*;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@ -289,23 +290,25 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return;
}
if (mantle.hasFlag(c.getX(), c.getZ(), MantleFlag.ETCHED)) return;
mantle.flag(c.getX(), c.getZ(), MantleFlag.ETCHED, true);
var chunk = mantle.getChunk(c);
if (chunk.isFlagged(MantleFlag.ETCHED)) return;
chunk.flag(MantleFlag.ETCHED, true);
mantle.raiseFlag(c.getX(), c.getZ(), MantleFlag.TILE, () -> J.sfut(() -> {
Semaphore semaphore = new Semaphore(3);
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
int betterY = y + getWorld().minHeight();
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
});
}).join());
mantle.raiseFlag(c.getX(), c.getZ(), MantleFlag.CUSTOM, () -> J.sfut(() -> {
})));
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
});
}).join());
})));
mantle.raiseFlag(c.getX(), c.getZ(), MantleFlag.UPDATE, () -> J.sfut(() -> {
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
PrecisionStopwatch p = PrecisionStopwatch.start();
KMap<Long, Integer> updates = new KMap<>();
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
@ -352,7 +355,23 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
});
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
getMetrics().getUpdates().put(p.getMilliseconds());
}, RNG.r.i(0, 20)).join());
}, RNG.r.i(0, 20))));
try {
semaphore.acquire(3);
} catch (InterruptedException ignored) {}
}
private static Runnable run(Semaphore semaphore, Runnable runnable) {
return () -> {
if (!semaphore.tryAcquire())
return;
try {
runnable.run();
} finally {
semaphore.release();
}
};
}
@BlockCoordinates

View File

@ -0,0 +1,70 @@
package com.volmit.iris.util.profile;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.math.M;
import lombok.Getter;
import lombok.Setter;
import java.util.concurrent.Semaphore;
@Getter
public class LoadBalancer extends MsptTimings {
private final Semaphore semaphore;
private final int maxPermits;
private final double range;
@Setter
private int minMspt, maxMspt;
private int permits, lastMspt;
private long lastTime = M.ms();
public LoadBalancer(Semaphore semaphore, int maxPermits, IrisSettings.MsRange range) {
this(semaphore, maxPermits, range.getMin(), range.getMax());
}
public LoadBalancer(Semaphore semaphore, int maxPermits, int minMspt, int maxMspt) {
this.semaphore = semaphore;
this.maxPermits = maxPermits;
this.minMspt = minMspt;
this.maxMspt = maxMspt;
this.range = maxMspt - minMspt;
setName("LoadBalancer");
start();
}
@Override
protected void update(int raw) {
lastTime = M.ms();
int mspt = raw;
if (mspt < lastMspt) {
int min = (int) Math.max(lastMspt * IrisSettings.get().getUpdater().getChunkLoadSensitivity(), 1);
mspt = Math.max(mspt, min);
}
lastMspt = mspt;
mspt = Math.max(mspt - minMspt, 0);
double percent = mspt / range;
int target = (int) (maxPermits * percent);
target = Math.min(target, maxPermits - 20);
int diff = target - permits;
permits = target;
if (diff == 0) return;
Iris.debug("Adjusting load to %s (%s) permits (%s mspt, %.2f)".formatted(target, diff, raw, percent));
if (diff > 0) semaphore.acquireUninterruptibly(diff);
else semaphore.release(Math.abs(diff));
}
public void close() {
interrupt();
semaphore.release(permits);
}
public void setRange(IrisSettings.MsRange range) {
minMspt = range.getMin();
maxMspt = range.getMax();
}
}