diff --git a/modules/schem/src/main/java/net/hollowcube/util/schem/Schematic.java b/modules/schem/src/main/java/net/hollowcube/util/schem/Schematic.java index 88f63c2..9db6ade 100644 --- a/modules/schem/src/main/java/net/hollowcube/util/schem/Schematic.java +++ b/modules/schem/src/main/java/net/hollowcube/util/schem/Schematic.java @@ -2,6 +2,8 @@ import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; +import net.minestom.server.instance.Chunk; +import net.minestom.server.instance.Instance; import net.minestom.server.instance.batch.BatchOption; import net.minestom.server.instance.batch.RelativeBlockBatch; import net.minestom.server.instance.block.Block; @@ -10,8 +12,10 @@ import org.jetbrains.annotations.Nullable; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Function; @@ -50,6 +54,36 @@ public Point offset(Rotation rotation) { return rotatePos(offset, rotation); } + public @NotNull CompletableFuture applyToInstance(@NotNull Instance instance, @NotNull Point startPoint, boolean loadChunks, @NotNull Rotation rotation, @Nullable Function blockModifier) { + RelativeBlockBatch batch = build(rotation, blockModifier); + if (loadChunks) { + Point min = startPoint.add(offset()); + Point max = min.add(size()); + int chunkXStart = min.chunkX(); + int chunkXSize = max.chunkX() - min.chunkX() + 1; + int chunkZStart = min.chunkZ(); + int chunkZSize = max.chunkZ() - min.chunkZ() + 1; + ArrayList> chunksToLoad = new ArrayList<>(); + for (int i = chunkXStart; i < chunkXStart + chunkXSize; i++) { + for (int j = chunkZStart; j < chunkZStart + chunkZSize; j++) { + chunksToLoad.add(instance.loadOptionalChunk(i, j)); + } + } + return CompletableFuture.allOf(chunksToLoad.toArray(new CompletableFuture[0])).thenRun(() -> batch.apply(instance, startPoint, null)).thenRun(() -> { + for (int i = chunkXStart; i < chunkXStart + chunkXSize; i++) { + for (int j = chunkZStart; j < chunkZStart + chunkZSize; j++) { + Chunk chunk = instance.getChunk(i, j); + if(chunk != null && chunk.isLoaded() && chunk.getViewers().isEmpty()) { + instance.unloadChunk(chunk); + } + } + } + }); + } else { + return CompletableFuture.runAsync(() -> batch.apply(instance, startPoint, null)); + } + } + /** * Convert the schematic into a {@link RelativeBlockBatch} which can be applied to an instance. * The schematic can be rotated around its {@link #offset()} before placement. diff --git a/modules/schem/src/test/java/SchemPasterTest.java b/modules/schem/src/test/java/SchemPasterTest.java new file mode 100644 index 0000000..184b3c5 --- /dev/null +++ b/modules/schem/src/test/java/SchemPasterTest.java @@ -0,0 +1,39 @@ +import net.hollowcube.util.schem.Rotation; +import net.hollowcube.util.schem.Schematic; +import net.hollowcube.util.schem.SchematicReader; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.event.instance.InstanceChunkLoadEvent; +import net.minestom.server.event.instance.InstanceChunkUnloadEvent; +import net.minestom.server.instance.Instance; +import net.minestom.server.test.Env; +import net.minestom.server.test.EnvTest; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@EnvTest +public class SchemPasterTest { + + @Test + public void testSchemPaste(Env env) { + AtomicInteger chunksLoaded = new AtomicInteger(); + AtomicInteger chunksUnloaded = new AtomicInteger(); + Instance instance = env.createFlatInstance(); + instance.eventNode().addListener(InstanceChunkLoadEvent.class, event -> chunksLoaded.getAndIncrement()); + instance.eventNode().addListener(InstanceChunkUnloadEvent.class, event -> chunksUnloaded.getAndIncrement()); + try { + Schematic big = SchematicReader.read(Path.of("src/test/resources/big.schem")); + CompletableFuture pasteProcess = big.applyToInstance(instance, new Pos(15, 40, 15), true, Rotation.NONE, null); + pasteProcess.join(); + } catch (IOException e) { + throw new RuntimeException(e); + } + assertEquals(2, chunksLoaded.get()); + assertEquals(chunksLoaded.get(), chunksUnloaded.get()); + } +}