mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-06-18 06:41:08 +00:00
implement Headless for 1.20.4
This commit is contained in:
@@ -150,10 +150,12 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
@Decree(description = "Test")
|
@Decree(description = "Test")
|
||||||
public void packBenchmark(
|
public void packBenchmark(
|
||||||
@Param(description = "The pack to bench", aliases = {"pack"})
|
@Param(description = "The pack to bench", aliases = {"pack"})
|
||||||
IrisDimension dimension
|
IrisDimension dimension,
|
||||||
|
@Param(description = "Headless", defaultValue = "false")
|
||||||
|
boolean headless
|
||||||
) {
|
) {
|
||||||
Iris.info("test");
|
Iris.info("test");
|
||||||
IrisPackBenchmarking benchmark = new IrisPackBenchmarking(dimension, 1);
|
IrisPackBenchmarking benchmark = new IrisPackBenchmarking(dimension, 1, headless);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.volmit.iris.core.nms;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
|
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||||
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
|
||||||
|
public interface IHeadless extends Closeable {
|
||||||
|
|
||||||
|
void saveAll();
|
||||||
|
|
||||||
|
@RegionCoordinates
|
||||||
|
boolean exists(int x, int z);
|
||||||
|
|
||||||
|
@RegionCoordinates
|
||||||
|
void generateRegion(MultiBurst burst, int x, int z, PregenListener listener);
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
void generateChunk(int x, int z);
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import com.volmit.iris.engine.framework.Engine;
|
|||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
import com.volmit.iris.util.nbt.mca.palette.MCABiomeContainer;
|
import com.volmit.iris.util.nbt.mca.palette.MCABiomeContainer;
|
||||||
@@ -119,4 +120,8 @@ public interface INMSBinding {
|
|||||||
boolean registerDimension(String name, IrisDimension dimension);
|
boolean registerDimension(String name, IrisDimension dimension);
|
||||||
|
|
||||||
void injectBukkit();
|
void injectBukkit();
|
||||||
|
|
||||||
|
default IHeadless createHeadless(Engine engine) {
|
||||||
|
throw new IllegalStateException("Headless mode not supported");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
package com.volmit.iris.core.pregenerator.methods;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.nms.IHeadless;
|
||||||
|
import com.volmit.iris.core.nms.INMS;
|
||||||
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
|
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
public class HeadlessPregenMethod implements PregeneratorMethod {
|
||||||
|
private final Engine engine;
|
||||||
|
private final IHeadless headless;
|
||||||
|
private final MultiBurst burst;
|
||||||
|
private final KList<Future<?>> futures;
|
||||||
|
|
||||||
|
public HeadlessPregenMethod(Engine engine) {
|
||||||
|
this.engine = engine;
|
||||||
|
this.headless = INMS.get().createHeadless(engine);
|
||||||
|
this.burst = new MultiBurst("Iris Headless", Thread.MAX_PRIORITY);
|
||||||
|
this.futures = new KList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
waitForChunksPartial(0);
|
||||||
|
burst.close();
|
||||||
|
headless.saveAll();
|
||||||
|
try {
|
||||||
|
headless.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Iris.error("Failed to close headless");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {
|
||||||
|
headless.saveAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRegions(int x, int z, PregenListener listener) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethod(int x, int z) {
|
||||||
|
return "Headless";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateRegion(int x, int z, PregenListener listener) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateChunk(int x, int z, PregenListener listener) {
|
||||||
|
futures.removeIf(Future::isDone);
|
||||||
|
waitForChunksPartial(512);
|
||||||
|
futures.add(burst.complete(() -> {
|
||||||
|
listener.onChunkGenerating(x, z);
|
||||||
|
headless.generateChunk(x, z);
|
||||||
|
listener.onChunkGenerated(x, z);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mantle getMantle() {
|
||||||
|
return engine.getMantle().getMantle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForChunksPartial(int maxWaiting) {
|
||||||
|
futures.removeWhere(Objects::isNull);
|
||||||
|
while (futures.size() > maxWaiting) {
|
||||||
|
try {
|
||||||
|
Future<?> i = futures.remove(0);
|
||||||
|
|
||||||
|
if (i == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.get();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,17 @@ package com.volmit.iris.core.tools;
|
|||||||
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.pregenerator.PregenTask;
|
import com.volmit.iris.core.pregenerator.PregenTask;
|
||||||
|
import com.volmit.iris.core.pregenerator.methods.HeadlessPregenMethod;
|
||||||
|
import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
|
||||||
|
import com.volmit.iris.core.service.StudioSVC;
|
||||||
|
import com.volmit.iris.engine.IrisEngine;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.framework.EngineTarget;
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
|
import com.volmit.iris.engine.object.IrisWorld;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.exceptions.IrisException;
|
import com.volmit.iris.util.exceptions.IrisException;
|
||||||
@@ -36,13 +44,16 @@ public class IrisPackBenchmarking {
|
|||||||
public static boolean benchmarkInProgress = false;
|
public static boolean benchmarkInProgress = false;
|
||||||
private IrisDimension IrisDimension;
|
private IrisDimension IrisDimension;
|
||||||
private int radius;
|
private int radius;
|
||||||
|
private final boolean headless;
|
||||||
private boolean finished = false;
|
private boolean finished = false;
|
||||||
|
private Engine engine;
|
||||||
PrecisionStopwatch stopwatch;
|
PrecisionStopwatch stopwatch;
|
||||||
|
|
||||||
public IrisPackBenchmarking(IrisDimension dimension, int r) {
|
public IrisPackBenchmarking(IrisDimension dimension, int r, boolean headless) {
|
||||||
instance = this;
|
instance = this;
|
||||||
this.IrisDimension = dimension;
|
this.IrisDimension = dimension;
|
||||||
this.radius = r;
|
this.radius = r;
|
||||||
|
this.headless = headless;
|
||||||
runBenchmark();
|
runBenchmark();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,12 +63,12 @@ public class IrisPackBenchmarking {
|
|||||||
service.submit(() -> {
|
service.submit(() -> {
|
||||||
Iris.info("Setting up benchmark environment ");
|
Iris.info("Setting up benchmark environment ");
|
||||||
benchmarkInProgress = true;
|
benchmarkInProgress = true;
|
||||||
File file = new File("benchmark");
|
File file = new File(Bukkit.getWorldContainer(), "benchmark");
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
deleteDirectory(file.toPath());
|
deleteDirectory(file.toPath());
|
||||||
}
|
}
|
||||||
createBenchmark();
|
engine = createBenchmark();
|
||||||
while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
|
while (!headless && !IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
|
||||||
J.sleep(1000);
|
J.sleep(1000);
|
||||||
Iris.debug("Iris PackBenchmark: Waiting...");
|
Iris.debug("Iris PackBenchmark: Waiting...");
|
||||||
}
|
}
|
||||||
@@ -75,7 +86,6 @@ public class IrisPackBenchmarking {
|
|||||||
public void finishedBenchmark(KList<Integer> cps) {
|
public void finishedBenchmark(KList<Integer> cps) {
|
||||||
try {
|
try {
|
||||||
String time = Form.duration(stopwatch.getMillis());
|
String time = Form.duration(stopwatch.getMillis());
|
||||||
Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine();
|
|
||||||
Iris.info("-----------------");
|
Iris.info("-----------------");
|
||||||
Iris.info("Results:");
|
Iris.info("Results:");
|
||||||
Iris.info("- Total time: " + time);
|
Iris.info("- Total time: " + time);
|
||||||
@@ -88,8 +98,8 @@ public class IrisPackBenchmarking {
|
|||||||
File profilers = new File("plugins" + File.separator + "Iris" + File.separator + "packbenchmarks");
|
File profilers = new File("plugins" + File.separator + "Iris" + File.separator + "packbenchmarks");
|
||||||
profilers.mkdir();
|
profilers.mkdir();
|
||||||
|
|
||||||
File results = new File("plugins " + File.separator + "Iris", IrisDimension.getName() + LocalDateTime.now(Clock.systemDefaultZone()) + ".txt");
|
File results = new File("plugins" + File.separator + "Iris", IrisDimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
|
||||||
results.createNewFile();
|
results.getParentFile().mkdirs();
|
||||||
KMap<String, Double> metrics = engine.getMetrics().pull();
|
KMap<String, Double> metrics = engine.getMetrics().pull();
|
||||||
try (FileWriter writer = new FileWriter(results)) {
|
try (FileWriter writer = new FileWriter(results)) {
|
||||||
writer.write("-----------------\n");
|
writer.write("-----------------\n");
|
||||||
@@ -123,15 +133,32 @@ public class IrisPackBenchmarking {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void createBenchmark(){
|
private Engine createBenchmark(){
|
||||||
try {
|
try {
|
||||||
IrisToolbelt.createWorld()
|
if (headless) {
|
||||||
|
IrisWorld world = IrisWorld.builder()
|
||||||
|
.name("benchmark")
|
||||||
|
.minHeight(IrisDimension.getMinHeight())
|
||||||
|
.maxHeight(IrisDimension.getMaxHeight())
|
||||||
|
.seed(1337)
|
||||||
|
.worldFolder(new File(Bukkit.getWorldContainer(), "benchmark"))
|
||||||
|
.environment(IrisDimension.getEnvironment())
|
||||||
|
.build();
|
||||||
|
Iris.service(StudioSVC.class).installIntoWorld(
|
||||||
|
Iris.getSender(),
|
||||||
|
IrisDimension.getLoadKey(),
|
||||||
|
world.worldFolder());
|
||||||
|
var data = IrisData.get(new File(world.worldFolder(), "iris/pack"));
|
||||||
|
var dim = data.getDimensionLoader().load(IrisDimension.getLoadKey());
|
||||||
|
return new IrisEngine(new EngineTarget(world, dim, data), false);
|
||||||
|
}
|
||||||
|
return IrisToolbelt.access(IrisToolbelt.createWorld()
|
||||||
.dimension(IrisDimension.getName())
|
.dimension(IrisDimension.getName())
|
||||||
.name("benchmark")
|
.name("benchmark")
|
||||||
.seed(1337)
|
.seed(1337)
|
||||||
.studio(false)
|
.studio(false)
|
||||||
.benchmark(true)
|
.benchmark(true)
|
||||||
.create();
|
.create()).getEngine();
|
||||||
} catch (IrisException e) {
|
} catch (IrisException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -146,8 +173,8 @@ public class IrisPackBenchmarking {
|
|||||||
.center(new Position2(x, z))
|
.center(new Position2(x, z))
|
||||||
.width(5)
|
.width(5)
|
||||||
.height(5)
|
.height(5)
|
||||||
.build(), Bukkit.getWorld("benchmark")
|
.build(), headless ? new HeadlessPregenMethod(engine) : new HybridPregenMethod(engine.getWorld().realWorld(),
|
||||||
);
|
IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())), engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double calculateAverage(KList<Integer> list) {
|
private double calculateAverage(KList<Integer> list) {
|
||||||
|
|||||||
@@ -286,6 +286,13 @@ public class IrisEngine implements Engine {
|
|||||||
return generated.get();
|
return generated.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addGenerated() {
|
||||||
|
if (generated.incrementAndGet() == 661) {
|
||||||
|
J.a(() -> getData().savePrefetch(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getGeneratedPerSecond() {
|
public double getGeneratedPerSecond() {
|
||||||
if (perSecondLatch.flip()) {
|
if (perSecondLatch.flip()) {
|
||||||
@@ -468,11 +475,7 @@ public class IrisEngine implements Engine {
|
|||||||
|
|
||||||
getMantle().getMantle().flag(x >> 4, z >> 4, MantleFlag.REAL, true);
|
getMantle().getMantle().flag(x >> 4, z >> 4, MantleFlag.REAL, true);
|
||||||
getMetrics().getTotal().put(p.getMilliseconds());
|
getMetrics().getTotal().put(p.getMilliseconds());
|
||||||
generated.incrementAndGet();
|
addGenerated();
|
||||||
|
|
||||||
if (generated.get() == 661) {
|
|
||||||
J.a(() -> getData().savePrefetch(this));
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
fail("Failed to generate " + x + ", " + z, e);
|
fail("Failed to generate " + x + ", " + z, e);
|
||||||
|
|||||||
@@ -577,6 +577,8 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
|
|
||||||
int getGenerated();
|
int getGenerated();
|
||||||
|
|
||||||
|
void addGenerated();
|
||||||
|
|
||||||
default <T> IrisPosition lookForStreamResult(T find, ProceduralStream<T> stream, Function2<T, T, Boolean> matcher, long timeout) {
|
default <T> IrisPosition lookForStreamResult(T find, ProceduralStream<T> stream, Function2<T, T, Boolean> matcher, long timeout) {
|
||||||
AtomicInteger checked = new AtomicInteger();
|
AtomicInteger checked = new AtomicInteger();
|
||||||
AtomicLong time = new AtomicLong(M.ms());
|
AtomicLong time = new AtomicLong(M.ms());
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_20_R3;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.nms.BiomeBaseInjector;
|
||||||
|
import com.volmit.iris.core.nms.IHeadless;
|
||||||
|
import com.volmit.iris.core.nms.v1_20_R3.mca.MCATerrainChunk;
|
||||||
|
import com.volmit.iris.core.nms.v1_20_R3.mca.RegionFileStorage;
|
||||||
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.framework.EngineStage;
|
||||||
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
|
import com.volmit.iris.engine.object.IrisBiome;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.context.ChunkContext;
|
||||||
|
import com.volmit.iris.util.context.IrisContext;
|
||||||
|
import com.volmit.iris.util.documentation.BlockCoordinates;
|
||||||
|
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||||
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
|
import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder;
|
||||||
|
import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder;
|
||||||
|
import com.volmit.iris.util.mantle.MantleFlag;
|
||||||
|
import com.volmit.iris.util.math.RNG;
|
||||||
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
|
import com.volmit.iris.util.scheduling.Looper;
|
||||||
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.LevelHeightAccessor;
|
||||||
|
import net.minecraft.world.level.biome.Biome;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class Headless implements IHeadless, LevelHeightAccessor {
|
||||||
|
private final NMSBinding binding;
|
||||||
|
private final Engine engine;
|
||||||
|
private final RegionFileStorage storage;
|
||||||
|
private final Queue<ProtoChunk> chunkQueue = new ArrayDeque<>();
|
||||||
|
private final ReentrantLock manualLock = new ReentrantLock();
|
||||||
|
private final KMap<String, Holder<Biome>> customBiomes = new KMap<>();
|
||||||
|
private final KMap<NamespacedKey, Holder<Biome>> minecraftBiomes = new KMap<>();
|
||||||
|
private boolean closed = false;
|
||||||
|
|
||||||
|
public Headless(NMSBinding binding, Engine engine) {
|
||||||
|
this.binding = binding;
|
||||||
|
this.engine = engine;
|
||||||
|
this.storage = new RegionFileStorage(new File(engine.getWorld().worldFolder(), "region").toPath(), false);
|
||||||
|
var queueLooper = new Looper() {
|
||||||
|
@Override
|
||||||
|
protected long loop() {
|
||||||
|
if (manualLock.isLocked()) {
|
||||||
|
manualLock.lock();
|
||||||
|
manualLock.unlock();
|
||||||
|
}
|
||||||
|
saveAll();
|
||||||
|
return closed ? -1 : 100;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
queueLooper.setName("Region Save Looper");
|
||||||
|
queueLooper.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists(int x, int z) {
|
||||||
|
if (closed) return false;
|
||||||
|
try {
|
||||||
|
return storage.getRegionFile(new ChunkPos(x << 5, z << 5), true) != null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAll() {
|
||||||
|
manualLock.lock();
|
||||||
|
try {
|
||||||
|
save();
|
||||||
|
} finally {
|
||||||
|
manualLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void save() {
|
||||||
|
if (closed) return;
|
||||||
|
while (!chunkQueue.isEmpty()) {
|
||||||
|
ChunkAccess chunk = chunkQueue.poll();
|
||||||
|
if (chunk == null) break;
|
||||||
|
try {
|
||||||
|
storage.write(chunk.getPos(), binding.serializeChunk(chunk, this));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error("Failed to save chunk " + chunk.getPos().x + ", " + chunk.getPos().z);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateRegion(MultiBurst burst, int x, int z, PregenListener listener) {
|
||||||
|
if (closed) return;
|
||||||
|
boolean listening = listener != null;
|
||||||
|
if (listening) listener.onRegionGenerating(x, z);
|
||||||
|
CountDownLatch latch = new CountDownLatch(1024);
|
||||||
|
iterateRegion(x, z, pos -> burst.complete(() -> {
|
||||||
|
if (listening) listener.onChunkGenerating(pos.x, pos.z);
|
||||||
|
generateChunk(pos.x, pos.z);
|
||||||
|
if (listening) listener.onChunkGenerated(pos.x, pos.z);
|
||||||
|
latch.countDown();
|
||||||
|
}));
|
||||||
|
try {
|
||||||
|
latch.await();
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
if (listening) listener.onRegionGenerated(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RegionCoordinates
|
||||||
|
private static void iterateRegion(int x, int z, Consumer<ChunkPos> chunkPos) {
|
||||||
|
int cX = x << 5;
|
||||||
|
int cZ = z << 5;
|
||||||
|
for (int xx = 0; xx < 32; xx++) {
|
||||||
|
for (int zz = 0; zz < 32; zz++) {
|
||||||
|
chunkPos.accept(new ChunkPos(cX + xx, cZ + zz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateChunk(int x, int z) {
|
||||||
|
if (closed) return;
|
||||||
|
try {
|
||||||
|
var pos = new ChunkPos(x, z);
|
||||||
|
try {
|
||||||
|
CompoundTag tag = storage.read(pos);
|
||||||
|
if (tag != null && !"empty".equals(tag.getString("Status"))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
|
|
||||||
|
ProtoChunk chunk = binding.createProtoChunk(pos, this);
|
||||||
|
var tc = new MCATerrainChunk(chunk);
|
||||||
|
|
||||||
|
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc);
|
||||||
|
BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight());
|
||||||
|
ChunkContext ctx = generate(engine, pos.x << 4, pos.z << 4, blocks, biomes);
|
||||||
|
blocks.apply();
|
||||||
|
biomes.apply();
|
||||||
|
|
||||||
|
inject(engine, tc.getBiomeBaseInjector(), chunk, ctx); //TODO improve
|
||||||
|
chunk.setStatus(ChunkStatus.FULL);
|
||||||
|
chunkQueue.add(chunk);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error("Failed to generate " + x + ", " + z);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BlockCoordinates
|
||||||
|
private ChunkContext generate(Engine engine, int x, int z, Hunk<BlockData> vblocks, Hunk<org.bukkit.block.Biome> vbiomes) throws WrongEngineBroException {
|
||||||
|
if (engine.isClosed()) {
|
||||||
|
throw new WrongEngineBroException();
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.getContext().touch();
|
||||||
|
engine.getEngineData().getStatistics().generatedChunk();
|
||||||
|
ChunkContext ctx = null;
|
||||||
|
try {
|
||||||
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
|
Hunk<BlockData> blocks = vblocks.listen((xx, y, zz, t) -> engine.catchBlockUpdates(x + xx, y + engine.getMinHeight(), z + zz, t));
|
||||||
|
|
||||||
|
var dimension = engine.getDimension();
|
||||||
|
if (dimension.isDebugChunkCrossSections() && ((x >> 4) % dimension.getDebugCrossSectionsMod() == 0 || (z >> 4) % dimension.getDebugCrossSectionsMod() == 0)) {
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
blocks.set(i, 0, j, Material.CRYING_OBSIDIAN.createBlockData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx = new ChunkContext(x, z, engine.getComplex());
|
||||||
|
IrisContext.getOr(engine).setChunkContext(ctx);
|
||||||
|
|
||||||
|
for (EngineStage i : engine.getMode().getStages()) {
|
||||||
|
i.generate(x, z, blocks, vbiomes, true, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.getMantle().getMantle().flag(x >> 4, z >> 4, MantleFlag.REAL, true);
|
||||||
|
engine.getMetrics().getTotal().put(p.getMilliseconds());
|
||||||
|
engine.addGenerated();
|
||||||
|
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
engine.fail("Failed to generate " + x + ", " + z, e);
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inject(Engine engine, BiomeBaseInjector injector, ChunkAccess chunk, ChunkContext ctx) {
|
||||||
|
var pos = chunk.getPos();
|
||||||
|
for (int y = engine.getMinHeight(); y < engine.getMaxHeight(); y++) {
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = pos.getBlockX(x);
|
||||||
|
int wZ = pos.getBlockZ(z);
|
||||||
|
try {
|
||||||
|
injector.setBiome(x, y, z, getNoiseBiome(engine, ctx, x, z, wX, y, wZ));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error("Failed to inject biome for " + wX + ", " + y + ", " + wZ);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Holder<Biome> getNoiseBiome(Engine engine, ChunkContext ctx, int rX, int rZ, int x, int y, int z) {
|
||||||
|
RNG rng = new RNG(engine.getSeedManager().getBiome());
|
||||||
|
int m = (y - engine.getMinHeight()) << 2;
|
||||||
|
IrisBiome ib = ctx == null ?
|
||||||
|
engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2) :
|
||||||
|
ctx.getBiome().get(rX, rZ);
|
||||||
|
if (ib.isCustom()) {
|
||||||
|
return customBiomes.computeIfAbsent(ib.getCustomBiome(rng, x << 2, m, z << 2).getId(),
|
||||||
|
id -> binding.getBiomeHolder(engine.getDimension().getLoadKey(), id));
|
||||||
|
} else {
|
||||||
|
return minecraftBiomes.computeIfAbsent(ib.getSkyBiome(rng, x << 2, m, z << 2).getKey(),
|
||||||
|
id -> binding.getBiomeHolder(id.getNamespace(), id.getKey()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (closed) return;
|
||||||
|
try {
|
||||||
|
storage.close();
|
||||||
|
} finally {
|
||||||
|
closed = true;
|
||||||
|
customBiomes.clear();
|
||||||
|
minecraftBiomes.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return engine.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinBuildHeight() {
|
||||||
|
return engine.getMinHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,9 +21,18 @@ import com.mojang.datafixers.util.Pair;
|
|||||||
import com.mojang.serialization.Codec;
|
import com.mojang.serialization.Codec;
|
||||||
import com.mojang.serialization.JsonOps;
|
import com.mojang.serialization.JsonOps;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.nms.IHeadless;
|
||||||
|
import com.volmit.iris.core.nms.v1_20_R3.mca.ChunkSerializer;
|
||||||
|
import com.volmit.iris.core.nms.v1_20_R3.mca.MCATerrainChunk;
|
||||||
|
import com.volmit.iris.core.nms.v1_20_R3.mca.RegionFileStorage;
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
|
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder;
|
||||||
|
import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder;
|
||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.util.io.IO;
|
||||||
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||||
import net.bytebuddy.ByteBuddy;
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
@@ -35,7 +44,11 @@ import net.minecraft.server.MinecraftServer;
|
|||||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.util.GsonHelper;
|
import net.minecraft.util.GsonHelper;
|
||||||
import net.minecraft.world.RandomSequences;
|
import net.minecraft.world.RandomSequences;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.LevelHeightAccessor;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
|
import net.minecraft.world.level.chunk.UpgradeData;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
@@ -782,6 +795,11 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IHeadless createHeadless(Engine engine) {
|
||||||
|
return new Headless(this, engine);
|
||||||
|
}
|
||||||
|
|
||||||
private static class ServerLevelAdvice {
|
private static class ServerLevelAdvice {
|
||||||
@Advice.OnMethodEnter
|
@Advice.OnMethodEnter
|
||||||
static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey<Level> key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) {
|
static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey<Level> key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) {
|
||||||
@@ -814,4 +832,16 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Holder.Reference<net.minecraft.world.level.biome.Biome> getBiomeHolder(String namespace, String id) {
|
||||||
|
return getCustomBiomeRegistry().getHolder(ResourceKey.create(Registries.BIOME, new ResourceLocation(namespace, id))).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtoChunk createProtoChunk(ChunkPos pos, LevelHeightAccessor heightAccessor) {
|
||||||
|
return new ProtoChunk(pos, UpgradeData.EMPTY, heightAccessor, getCustomBiomeRegistry(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
net.minecraft.nbt.CompoundTag serializeChunk(ChunkAccess chunkAccess, LevelHeightAccessor heightAccessor) {
|
||||||
|
return ChunkSerializer.write(chunkAccess, heightAccessor, getCustomBiomeRegistry());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_20_R3.mca;
|
||||||
|
|
||||||
|
import com.mojang.logging.LogUtils;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.DataResult;
|
||||||
|
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.LongArrayTag;
|
||||||
|
import net.minecraft.nbt.NbtOps;
|
||||||
|
import net.minecraft.nbt.NbtUtils;
|
||||||
|
import net.minecraft.nbt.ShortTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.LevelHeightAccessor;
|
||||||
|
import net.minecraft.world.level.biome.Biome;
|
||||||
|
import net.minecraft.world.level.chunk.CarvingMask;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
|
import net.minecraft.world.level.chunk.PalettedContainerRO;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
|
import net.minecraft.world.level.chunk.UpgradeData;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess.TicksToSave;
|
||||||
|
import net.minecraft.world.level.levelgen.BelowZeroRetrogen;
|
||||||
|
import net.minecraft.world.level.levelgen.Heightmap;
|
||||||
|
import net.minecraft.world.level.levelgen.GenerationStep.Carving;
|
||||||
|
import net.minecraft.world.level.levelgen.Heightmap.Types;
|
||||||
|
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import static net.minecraft.world.level.chunk.storage.ChunkSerializer.BLOCK_STATE_CODEC;
|
||||||
|
|
||||||
|
public class ChunkSerializer {
|
||||||
|
private static final Logger LOGGER = LogUtils.getLogger();
|
||||||
|
private static Method CODEC = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (Method method : net.minecraft.world.level.chunk.storage.ChunkSerializer.class.getDeclaredMethods()) {
|
||||||
|
if (method.getReturnType().equals(Codec.class) && method.getParameterCount() == 1) {
|
||||||
|
CODEC = method;
|
||||||
|
CODEC.setAccessible(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CompoundTag write(ChunkAccess chunk, LevelHeightAccessor heightAccessor, Registry<Biome> biomeRegistry) {
|
||||||
|
ChunkPos pos = chunk.getPos();
|
||||||
|
CompoundTag data = NbtUtils.addCurrentDataVersion(new CompoundTag());
|
||||||
|
data.putInt("xPos", pos.x);
|
||||||
|
data.putInt("yPos", ((LevelHeightAccessor) chunk).getMinSection());
|
||||||
|
data.putInt("zPos", pos.z);
|
||||||
|
data.putLong("LastUpdate", 0L);
|
||||||
|
data.putLong("InhabitedTime", chunk.getInhabitedTime());
|
||||||
|
data.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(chunk.getStatus()).toString());
|
||||||
|
BlendingData blendingdata = chunk.getBlendingData();
|
||||||
|
if (blendingdata != null) {
|
||||||
|
DataResult<Tag> dataResult = BlendingData.CODEC.encodeStart(NbtOps.INSTANCE, blendingdata);
|
||||||
|
dataResult.resultOrPartial(LOGGER::error).ifPresent(base -> data.put("blending_data", base));
|
||||||
|
}
|
||||||
|
|
||||||
|
BelowZeroRetrogen belowzeroretrogen = chunk.getBelowZeroRetrogen();
|
||||||
|
if (belowzeroretrogen != null) {
|
||||||
|
DataResult<Tag> dataResult = BelowZeroRetrogen.CODEC.encodeStart(NbtOps.INSTANCE, belowzeroretrogen);
|
||||||
|
dataResult.resultOrPartial(LOGGER::error).ifPresent(base -> data.put("below_zero_retrogen", base));
|
||||||
|
}
|
||||||
|
|
||||||
|
UpgradeData upgradeData = chunk.getUpgradeData();
|
||||||
|
if (!upgradeData.isEmpty()) {
|
||||||
|
data.put("UpgradeData", upgradeData.write());
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelChunkSection[] chunkSections = chunk.getSections();
|
||||||
|
ListTag sections = new ListTag();
|
||||||
|
Codec<PalettedContainerRO<Holder<Biome>>> codec;
|
||||||
|
try {
|
||||||
|
codec = (Codec<PalettedContainerRO<Holder<Biome>>>) CODEC.invoke(null, biomeRegistry);
|
||||||
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
int minLightSection = heightAccessor.getMinSection() - 1;
|
||||||
|
int maxLightSection = minLightSection + heightAccessor.getSectionsCount() + 2;
|
||||||
|
for (int y = minLightSection; y < maxLightSection; y++) {
|
||||||
|
int i = ((LevelHeightAccessor) chunk).getSectionIndexFromSectionY(y);
|
||||||
|
if (i >= 0 && i < chunkSections.length) {
|
||||||
|
CompoundTag section = new CompoundTag();
|
||||||
|
LevelChunkSection chunkSection = chunkSections[i];
|
||||||
|
DataResult<Tag> dataResult = BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, chunkSection.getStates());
|
||||||
|
section.put("block_states", dataResult.getOrThrow(false, LOGGER::error));
|
||||||
|
dataResult = codec.encodeStart(NbtOps.INSTANCE, chunkSection.getBiomes());
|
||||||
|
section.put("biomes", dataResult.getOrThrow(false, LOGGER::error));
|
||||||
|
|
||||||
|
if (!section.isEmpty()) {
|
||||||
|
section.putByte("Y", (byte)y);
|
||||||
|
sections.add(section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.put("sections", sections);
|
||||||
|
if (chunk.isLightCorrect()) {
|
||||||
|
data.putBoolean("isLightOn", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ListTag blockEntities = new ListTag();
|
||||||
|
for (BlockPos blockPos : chunk.getBlockEntitiesPos()) {
|
||||||
|
CompoundTag blockEntityNbt = chunk.getBlockEntityNbtForSaving(blockPos);
|
||||||
|
if (blockEntityNbt != null) {
|
||||||
|
blockEntities.add(blockEntityNbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.put("block_entities", blockEntities);
|
||||||
|
|
||||||
|
if (chunk instanceof ProtoChunk protoChunk) {
|
||||||
|
ListTag entities = new ListTag();
|
||||||
|
entities.addAll(protoChunk.getEntities());
|
||||||
|
data.put("entities", entities);
|
||||||
|
|
||||||
|
CompoundTag carvingMasks = new CompoundTag();
|
||||||
|
for (Carving carving : Carving.values()) {
|
||||||
|
CarvingMask carvingMask = protoChunk.getCarvingMask(carving);
|
||||||
|
if (carvingMask != null) {
|
||||||
|
carvingMasks.putLongArray(carving.toString(), carvingMask.toArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.put("CarvingMasks", carvingMasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveTicks(data, chunk.getTicksForSerialization());
|
||||||
|
data.put("PostProcessing", packOffsets(chunk.getPostProcessing()));
|
||||||
|
CompoundTag heightmaps = new CompoundTag();
|
||||||
|
|
||||||
|
for (Entry<Types, Heightmap> entry : chunk.getHeightmaps()) {
|
||||||
|
if (chunk.getStatus().heightmapsAfter().contains(entry.getKey())) {
|
||||||
|
heightmaps.put(entry.getKey().getSerializationKey(), new LongArrayTag(entry.getValue().getRawData()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.put("Heightmaps", heightmaps);
|
||||||
|
|
||||||
|
CompoundTag structures = new CompoundTag();
|
||||||
|
structures.put("starts", new CompoundTag());
|
||||||
|
structures.put("References", new CompoundTag());
|
||||||
|
data.put("structures", structures);
|
||||||
|
if (!chunk.persistentDataContainer.isEmpty()) {
|
||||||
|
data.put("ChunkBukkitValues", chunk.persistentDataContainer.toTagCompound());
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void saveTicks(CompoundTag compoundTag, TicksToSave ticksToSave) {
|
||||||
|
compoundTag.put("block_ticks", ticksToSave.blocks().save(0, block -> BuiltInRegistries.BLOCK.getKey(block).toString()));
|
||||||
|
compoundTag.put("fluid_ticks", ticksToSave.fluids().save(0, fluid -> BuiltInRegistries.FLUID.getKey(fluid).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ListTag packOffsets(ShortList[] offsets) {
|
||||||
|
ListTag tags = new ListTag();
|
||||||
|
for (ShortList shorts : offsets) {
|
||||||
|
ListTag listTag = new ListTag();
|
||||||
|
if (shorts != null) {
|
||||||
|
for (Short s : shorts) {
|
||||||
|
listTag.add(ShortTag.valueOf(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tags.add(listTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
}
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_20_R3.mca;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.nms.BiomeBaseInjector;
|
||||||
|
import com.volmit.iris.engine.data.chunk.TerrainChunk;
|
||||||
|
import com.volmit.iris.util.data.IrisBlockData;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.world.level.LevelHeightAccessor;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.Biome;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBiome;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||||
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
|
import org.bukkit.material.MaterialData;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public record MCATerrainChunk(ChunkAccess chunk) implements TerrainChunk {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiomeBaseInjector getBiomeBaseInjector() {
|
||||||
|
return (x, y, z, biomeBase) -> chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) biomeBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Biome getBiome(int x, int z) {
|
||||||
|
return Biome.THE_VOID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Biome getBiome(int x, int y, int z) {
|
||||||
|
return Biome.THE_VOID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBiome(int x, int z, Biome bio) {
|
||||||
|
setBiome(x, 0, z, bio);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBiome(int x, int y, int z, Biome bio) {
|
||||||
|
chunk.setBiome(x & 15, y, z & 15, CraftBiome.bukkitToMinecraftHolder(bio));
|
||||||
|
}
|
||||||
|
|
||||||
|
private LevelHeightAccessor height() {
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinHeight() {
|
||||||
|
return height().getMinBuildHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxHeight() {
|
||||||
|
return height().getMaxBuildHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlock(int x, int y, int z, BlockData blockData) {
|
||||||
|
if (y > getMaxHeight() || y < getMinHeight()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockData == null) {
|
||||||
|
Iris.error("NULL BD");
|
||||||
|
}
|
||||||
|
if (blockData instanceof IrisBlockData data)
|
||||||
|
blockData = data.getBase();
|
||||||
|
if (!(blockData instanceof CraftBlockData craftBlockData))
|
||||||
|
throw new IllegalArgumentException("Expected CraftBlockData, got " + blockData.getClass().getSimpleName() + " instead");
|
||||||
|
chunk.setBlockState(new BlockPos(x & 15, y, z & 15), craftBlockData.getState(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockState getBlockState(int x, int y, int z) {
|
||||||
|
if (y > getMaxHeight()) {
|
||||||
|
y = getMaxHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < getMinHeight()) {
|
||||||
|
y = getMinHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk.getBlockState(new BlockPos(x & 15, y, z & 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public org.bukkit.block.data.BlockData getBlockData(int x, int y, int z) {
|
||||||
|
return CraftBlockData.fromData(getBlockState(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkGenerator.ChunkData getRaw() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRaw(ChunkGenerator.ChunkData data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void inject(ChunkGenerator.BiomeGrid biome) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlock(int x, int y, int z, @NotNull Material material) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, @NotNull Material material) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, @NotNull MaterialData material) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, @NotNull BlockData blockData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Material getType(int x, int y, int z) {
|
||||||
|
return getBlockData(x, y, z).getMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public MaterialData getTypeAndData(int x, int y, int z) {
|
||||||
|
return getBlockData(x, y, z).createBlockState().getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte getData(int x, int y, int z) {
|
||||||
|
return getTypeAndData(x, y, z).getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_20_R3.mca;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import net.minecraft.FileUtil;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.NbtAccounter;
|
||||||
|
import net.minecraft.nbt.NbtIo;
|
||||||
|
import net.minecraft.nbt.StreamTagVisitor;
|
||||||
|
import net.minecraft.util.ExceptionCollector;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.chunk.storage.RegionFile;
|
||||||
|
|
||||||
|
public final class RegionFileStorage implements AutoCloseable {
|
||||||
|
public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap<>();
|
||||||
|
private final Path folder;
|
||||||
|
private final boolean sync;
|
||||||
|
|
||||||
|
public RegionFileStorage(Path folder, boolean sync) {
|
||||||
|
this.folder = folder;
|
||||||
|
this.sync = sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException {
|
||||||
|
long id = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ());
|
||||||
|
RegionFile regionFile = this.regionCache.getAndMoveToFirst(id);
|
||||||
|
if (regionFile != null) {
|
||||||
|
return regionFile;
|
||||||
|
} else {
|
||||||
|
if (this.regionCache.size() >= 256) {
|
||||||
|
this.regionCache.removeLast().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtil.createDirectoriesSafe(this.folder);
|
||||||
|
Path path = folder.resolve("r." + chunkPos.getRegionX() + "." + chunkPos.getRegionZ() + ".mca");
|
||||||
|
if (existingOnly && !Files.exists(path)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
regionFile = new RegionFile(path, this.folder, this.sync);
|
||||||
|
this.regionCache.putAndMoveToFirst(id, regionFile);
|
||||||
|
return regionFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public CompoundTag read(ChunkPos chunkPos) throws IOException {
|
||||||
|
RegionFile regionFile = this.getRegionFile(chunkPos, true);
|
||||||
|
if (regionFile != null) {
|
||||||
|
try (DataInputStream datainputstream = regionFile.getChunkDataInputStream(chunkPos)) {
|
||||||
|
if (datainputstream != null) {
|
||||||
|
return NbtIo.read(datainputstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scanChunk(ChunkPos chunkPos, StreamTagVisitor visitor) throws IOException {
|
||||||
|
RegionFile regionFile = this.getRegionFile(chunkPos, true);
|
||||||
|
if (regionFile != null) {
|
||||||
|
try (DataInputStream din = regionFile.getChunkDataInputStream(chunkPos)) {
|
||||||
|
if (din != null) {
|
||||||
|
NbtIo.parse(din, visitor, NbtAccounter.unlimitedHeap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException {
|
||||||
|
RegionFile regionFile = this.getRegionFile(chunkPos, false);
|
||||||
|
Preconditions.checkArgument(regionFile != null, "Failed to find region file for chunk %s", chunkPos);
|
||||||
|
if (compound == null) {
|
||||||
|
regionFile.clear(chunkPos);
|
||||||
|
} else {
|
||||||
|
try (DataOutputStream dos = regionFile.getChunkDataOutputStream(chunkPos)) {
|
||||||
|
NbtIo.write(compound, dos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
ExceptionCollector<IOException> collector = new ExceptionCollector<>();
|
||||||
|
|
||||||
|
for (RegionFile regionFile : this.regionCache.values()) {
|
||||||
|
try {
|
||||||
|
regionFile.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
collector.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collector.throwIfPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush() throws IOException {
|
||||||
|
for (RegionFile regionfile : this.regionCache.values()) {
|
||||||
|
regionfile.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user