cleanup regen

This commit is contained in:
Julian Krings 2025-03-30 13:48:43 +02:00
parent a56cd4c268
commit 21825c4d12
No known key found for this signature in database
GPG Key ID: 208C6E08C3B718D2
4 changed files with 161 additions and 106 deletions

View File

@ -46,18 +46,20 @@ import com.volmit.iris.util.interpolation.InterpolationMethod;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONArray;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.noise.CNG;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.parallel.SyncExecutor;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.O;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.scheduling.jobs.QueueJob;
import com.volmit.iris.util.scheduling.jobs.ParallelQueueJob;
import io.papermc.lib.PaperLib;
import org.bukkit.*;
import org.bukkit.event.inventory.InventoryType;
@ -76,8 +78,7 @@ import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@ -161,54 +162,41 @@ public class CommandStudio implements DecreeExecutor {
@Param(name = "radius", description = "The radius of nearby cunks", defaultValue = "5")
int radius
) {
if (IrisToolbelt.isIrisWorld(player().getWorld())) {
World world = player().getWorld();
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "You must be in an Iris World to use regen!");
}
VolmitSender sender = sender();
var loc = player().getLocation().clone();
J.a(() -> {
DecreeContext.touch(sender);
PlatformChunkGenerator plat = IrisToolbelt.access(player().getWorld());
PlatformChunkGenerator plat = IrisToolbelt.access(world);
Engine engine = plat.getEngine();
try {
Chunk cx = player().getLocation().getChunk();
KList<Runnable> js = new KList<>();
BurstExecutor b = MultiBurst.burst.burst();
b.setMulticore(false);
try (SyncExecutor executor = new SyncExecutor(20)) {
int x = loc.getBlockX() >> 4;
int z = loc.getBlockZ() >> 4;
int rad = engine.getMantle().getRadius();
var mantle = engine.getMantle().getMantle();
var chunkMap = new KMap<Position2, MantleChunk>();
for (int i = -(radius + rad); i <= radius + rad; i++) {
for (int j = -(radius + rad); j <= radius + rad; j++) {
engine.getMantle().getMantle().deleteChunk(i + cx.getX(), j + cx.getZ());
int xx = i + x, zz = j + z;
if (Math.abs(i) <= radius && Math.abs(j) <= radius) {
mantle.deleteChunk(xx, zz);
continue;
}
chunkMap.put(new Position2(xx, zz), mantle.getChunk(xx, zz));
mantle.deleteChunk(xx, zz);
}
}
for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) {
int finalJ = j;
int finalI = i;
b.queue(() -> plat.injectChunkReplacement(player().getWorld(), finalI + cx.getX(), finalJ + cx.getZ(), (f) -> {
synchronized (js) {
js.add(f);
}
}));
}
}
b.complete();
sender().sendMessage(C.GREEN + "Regenerating " + Form.f(js.size()) + " Sections");
QueueJob<Runnable> r = new QueueJob<>() {
final KList<Future<?>> futures = new KList<>();
ParallelQueueJob<Position2> job = new ParallelQueueJob<>() {
@Override
public void execute(Runnable runnable) {
futures.add(J.sfut(runnable));
if (futures.size() > 64) {
while (futures.isNotEmpty()) {
try {
futures.remove(0).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
public void execute(Position2 p) {
plat.injectChunkReplacement(world, p.getX(), p.getZ(), executor);
}
@Override
@ -216,15 +204,35 @@ public class CommandStudio implements DecreeExecutor {
return "Regenerating";
}
};
r.queue(js);
r.execute(sender());
} catch (Throwable e) {
sender().sendMessage("Unable to parse view-distance");
for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) {
job.queue(new Position2(i + x, j + z));
}
}
CountDownLatch latch = new CountDownLatch(1);
job.execute(sender(), latch::countDown);
latch.await();
int sections = mantle.getWorldHeight() >> 4;
chunkMap.forEach((pos, chunk) -> {
var c = mantle.getChunk(pos.getX(), pos.getZ());
for (MantleFlag flag : MantleFlag.values()) {
c.flag(flag, chunk.isFlagged(flag));
}
c.clear();
for (int y = 0; y < sections; y++) {
var slice = chunk.get(y);
if (slice == null) continue;
var s = c.getOrCreate(y);
slice.getSliceMap().forEach(s::putSlice);
}
});
} else {
sender().sendMessage(C.RED + "You must be in an Iris World to use regen!");
} catch (Throwable e) {
sender().sendMessage("Error while regenerating chunks");
e.printStackTrace();
}
});
}
@Decree(description = "Convert objects in the \"convert\" folder")

View File

@ -61,15 +61,12 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@EqualsAndHashCode(callSuper = true)
@Data
@ -94,8 +91,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
@Setter
private volatile StudioGenerator studioGenerator;
private boolean initialized = false;
public BukkitChunkGenerator(IrisWorld world, boolean studio, File dataLocation, String dimensionKey) {
setup = new AtomicBoolean(false);
studioGenerator = null;
@ -114,10 +109,11 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldInit(WorldInitEvent event) {
try {
if (initialized || !world.name().equals(event.getWorld().getName()))
return;
if (!world.name().equals(event.getWorld().getName())) return;
Iris.instance.unregisterListener(this);
AutoClosing.closeContext();
INMS.get().removeCustomDimensions(event.getWorld());
world.setRawWorldSeed(event.getWorld().getSeed());
Engine engine = getEngine(event.getWorld());
if (engine == null) {
@ -128,7 +124,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
try {
INMS.get().inject(event.getWorld().getSeed(), engine1, event.getWorld());
Iris.info("Injected Iris Biome Source into " + event.getWorld().getName());
initialized = true;
} catch (Throwable e) {
e.printStackTrace();
}
@ -138,7 +133,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
INMS.get().inject(event.getWorld().getSeed(), engine, event.getWorld());
Iris.info("Injected Iris Biome Source into " + event.getWorld().getName());
spawnChunks.complete(INMS.get().getSpawnChunkCount(event.getWorld()));
initialized = true;
}
} catch (Throwable e) {
e.printStackTrace();
@ -194,50 +188,57 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
}
@Override
public void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs) {
public void injectChunkReplacement(World world, int x, int z, Executor syncExecutor) {
try {
loadLock.acquire();
IrisBiomeStorage st = new IrisBiomeStorage();
TerrainChunk tc = TerrainChunk.createUnsafe(world, st);
Hunk<BlockData> blocks = Hunk.view(tc);
Hunk<Biome> biomes = Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight());
this.world.bind(world);
getEngine().generate(x << 4, z << 4, blocks, biomes, true);
Iris.debug("Regenerated " + x + " " + z);
int t = 0;
for (int i = getEngine().getHeight() >> 4; i >= 0; i--) {
if (!world.isChunkLoaded(x, z)) {
continue;
}
getEngine().generate(x << 4, z << 4, tc, false);
Chunk c = world.getChunkAt(x, z);
for (Entity ee : c.getEntities()) {
Chunk c = PaperLib.getChunkAtAsync(world, x, z)
.thenApply(d -> {
d.addPluginChunkTicket(Iris.instance);
for (Entity ee : d.getEntities()) {
if (ee instanceof Player) {
continue;
}
J.s(ee::remove);
ee.remove();
}
J.s(() -> engine.getWorldManager().onChunkLoad(c, false));
engine.getWorldManager().onChunkLoad(d, false);
return d;
}).get();
int finalI = i;
jobs.accept(() -> {
KList<CompletableFuture<?>> futures = new KList<>(1 + getEngine().getHeight() >> 4);
for (int i = getEngine().getHeight() >> 4; i >= 0; i--) {
int finalI = i << 4;
futures.add(CompletableFuture.runAsync(() -> {
for (int xx = 0; xx < 16; xx++) {
for (int yy = 0; yy < 16; yy++) {
for (int zz = 0; zz < 16; zz++) {
if (yy + (finalI << 4) >= engine.getHeight() || yy + (finalI << 4) < 0) {
if (yy + finalI >= engine.getHeight() || yy + finalI < 0) {
continue;
}
c.getBlock(xx, yy + (finalI << 4) + world.getMinHeight(), zz)
.setBlockData(tc.getBlockData(xx, yy + (finalI << 4) + world.getMinHeight(), zz), false);
int y = yy + finalI + world.getMinHeight();
c.getBlock(xx, y, zz).setBlockData(tc.getBlockData(xx, y, zz), false);
}
}
}
});
}, syncExecutor));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRunAsync(() -> {
c.removePluginChunkTicket(Iris.instance);
c.unload();
}, syncExecutor)
.get();
Iris.debug("Regenerated " + x + " " + z);
loadLock.release();
} catch (Throwable e) {
loadLock.release();

View File

@ -26,7 +26,7 @@ import com.volmit.iris.util.data.DataProvider;
import org.bukkit.World;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.concurrent.Executor;
public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
Engine getEngine();
@ -40,7 +40,7 @@ public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
return getEngine().getTarget();
}
void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs);
void injectChunkReplacement(World world, int x, int z, Executor syncExecutor);
void close();

View File

@ -0,0 +1,46 @@
package com.volmit.iris.util.parallel;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.scheduling.SR;
import org.jetbrains.annotations.NotNull;
import java.util.Queue;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
public class SyncExecutor implements Executor, AutoCloseable {
private final CountDownLatch latch = new CountDownLatch(1);
private final Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
private final AtomicBoolean closed = new AtomicBoolean(false);
public SyncExecutor(int msPerTick) {
new SR() {
@Override
public void run() {
var time = M.ms() + msPerTick;
while (time > M.ms()) {
Runnable r = queue.poll();
if (r == null) break;
r.run();
}
if (closed.get() && queue.isEmpty()) {
cancel();
latch.countDown();
}
}
};
}
@Override
public void execute(@NotNull Runnable command) {
if (closed.get()) throw new IllegalStateException("Executor is closed!");
queue.add(command);
}
@Override
public void close() throws Exception {
closed.set(true);
latch.await();
}
}