diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..3205926 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,6 +12,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +25,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7ce3e3b..9bc5ec9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -25,43 +25,43 @@ jobs: # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Setup Java JDK - uses: actions/setup-java@v3.3.0 - with: - distribution: temurin - java-version: 17 - cache: maven + - name: Checkout repository + uses: actions/checkout@v3 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality + - name: Setup Java JDK + uses: actions/setup-java@v3.3.0 + with: + distribution: temurin + java-version: 17 + cache: gradle - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/README.md b/README.md index 5474407..fad72be 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,54 @@ # Scaffolding + Scaffolding is a library for Minestom that allows you to load and place schematics. -> This library is still under heavy development and has too many bugs to count. For your own safety, this should not be used in a production environment. +> This library is still under heavy development and has too many bugs to count. For your own safety, this should not be +> used in a production environment. > This library's API is likely to change a lot as this project works towards a stable 1.0.0 release. ## Usage + ```java -// Load a schematic from File. -public void method1() { - Schematic schematic = Scaffolding.fromFile(new File("schematics/my_schematic.schematic")); -} -public void method2() { - Schematic schematic = new SpongeSchematic(); - schematic.read(new FileInputStream(new File("schematics/my_schematic.schematic"))); +//Load Schematics +public class LoadASchematic { + public void method1(){ + Schematic schematic = Scaffolding.fromFile(new File("schematics/my_schematic.schematic")); + } + + + public void method2(){ + Schematic schematic = new Schematic(); + schematic.read(new FileInputStream(new File("schematics/my_schematic.schematic"))); + } } + ``` + ```java -// Place a schematic at a location. -Instance instance = player.getInstance(); -Pos position = player.getPosition(); -schematic.build(instance, position).thenRun(() -> player.sendMessage("Schematic placed!")); -``` -```java -// Write a schematic (Soon:tm:) -Region region = new Region(new Pos(0, 0, 0), new Pos(10, 10, 10)); -Schematic schematic = new SpongeSchematic(); -schematic.write(new FileOutputStream("schematics/my_schematic.schematic"), region); + +public class LoadAndPasteSchematicOrSaveIt { + + // Place a schematic at a location. + public void method1() { + Instance instance = player.getInstance(); + Pos position = player.getPosition(); + schematic.build(instance, position).thenRun(() -> player.sendMessage("Schematic placed!")); + } + + + //Select a region and save it to a schematic (Soon TM) + public void method2() { + Region region = new Region(new Pos(0, 0, 0), new Pos(10, 10, 10)); + Schematic schematic = new SpongeSchematic(); + schematic.write(new FileOutputStream("schematics/my_schematic.schematic"), region); + } +} ``` ## Dependency ### Gradle (Kotlin) + Add Scaffolding as a dependency in your `build.gradle.kts` file. ```kt @@ -44,6 +62,7 @@ dependencies { ``` ### Gradle (Groovy) + Add Scaffolding as a dependency in your `build.gradle` file. ```groovy @@ -57,6 +76,7 @@ dependencies { ``` ### Maven + Add Scaffolding as a dependency in your `pom.xml` file. ```xml diff --git a/build.gradle.kts b/build.gradle.kts index f75bff5..d27c43b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,9 +23,32 @@ group = "dev.hypera" version = "0.2.0-SNAPSHOT" +//Minestom +val minestomVersion by extra("-SNAPSHOT") + +//Gson +val gsonVersion by extra("2.9.0") + +//Fast Utililities +val fastUtilVersion by extra("2.0.1") + +//Jupiter Testing +val jupiterAPIVersion by extra("5.8.2") +val jupiterEngineVersion by extra("5.8.2") + +plugins { + `java-library` +} + allprojects { repositories { mavenCentral() maven("https://jitpack.io/") } +} + +tasks { + compileJava { + options.encoding = "UTF-8" + } } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6719cdf..462263c 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -32,13 +32,19 @@ java { } dependencies { - compileOnlyApi("com.github.Minestom:Minestom:7be96b7679") - compileOnly("space.vectrix.flare:flare-fastutil:2.0.1") - compileOnly("com.google.code.gson:gson:2.9.0") + compileOnlyApi("com.github.Minestom:Minestom:${rootProject.extra["minestomVersion"]}") + compileOnly("space.vectrix.flare:flare-fastutil:${rootProject.extra["fastUtilVersion"]}") + compileOnly("com.google.code.gson:gson:${rootProject.extra["gsonVersion"]}") - testImplementation("com.github.Minestom:Minestom:7be96b7679") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") + testImplementation("com.github.Minestom:Minestom:${rootProject.extra["minestomVersion"]}") + testImplementation("org.junit.jupiter:junit-jupiter-api:${rootProject.extra["jupiterAPIVersion"]}") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${rootProject.extra["jupiterEngineVersion"]}") +} + +tasks { + compileJava { + options.encoding = "UTF-8" + } } publishing { diff --git a/core/src/main/java/dev/hypera/scaffolding/Scaffolding.java b/core/src/main/java/dev/hypera/scaffolding/Scaffolding.java index 6953ffa..e06eedc 100644 --- a/core/src/main/java/dev/hypera/scaffolding/Scaffolding.java +++ b/core/src/main/java/dev/hypera/scaffolding/Scaffolding.java @@ -26,41 +26,47 @@ import dev.hypera.scaffolding.schematic.Schematic; import dev.hypera.scaffolding.schematic.readers.MCEditSchematicReader; import dev.hypera.scaffolding.schematic.readers.SpongeSchematicReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.jetbrains.annotations.NotNull; import org.jglrxavpok.hephaistos.nbt.CompressedProcesser; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTException; import org.jglrxavpok.hephaistos.nbt.NBTReader; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + /** * A static utility class primarily used to parse schematics. */ @SuppressWarnings("unused") public final class Scaffolding { + private static final ObjectArrayList MC_SCHEMATIC_READERS; + + + static { + MC_SCHEMATIC_READERS = new ObjectArrayList<>(); - private static final @NotNull NBTSchematicReader MC_EDIT_SCHEMATIC_READER = new MCEditSchematicReader(); - private static final @NotNull NBTSchematicReader SPONGE_SCHEMATIC_READER = new SpongeSchematicReader(); + MC_SCHEMATIC_READERS.add(new MCEditSchematicReader()); + MC_SCHEMATIC_READERS.add(new SpongeSchematicReader()); + } - private Scaffolding() {} + private Scaffolding() { + } /** * Automatically detects the type of schematic and parses the file. * * @param path Schematic path - * * @return parsed schematic * @throws IOException if the file is invalid * @throws NBTException if the schematic is invalid * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic + * */ public static @NotNull CompletableFuture fromPath(@NotNull Path path) throws IOException, NBTException, IllegalArgumentException { return fromPath(path, new Schematic()); @@ -69,10 +75,10 @@ private Scaffolding() {} /** * @param path the {@link Path} to read from * @param schematic the {@link Schematic} to load the data into - * * @return a {@link CompletableFuture} that will be completed when the schematic is loaded * @throws IOException if the file is invalid * @throws NBTException if the NBT tag is invalid + * */ public static @NotNull CompletableFuture fromPath(@NotNull Path path, @NotNull Schematic schematic) throws IOException, NBTException { if (!Files.exists(path)) throw new FileNotFoundException("Invalid Schematic: File does not exist"); @@ -84,11 +90,11 @@ private Scaffolding() {} * Automatically detects the type of schematic and parses the file. * * @param file Schematic file - * * @return parsed schematic * @throws IOException if the file is invalid * @throws NBTException if the schematic is invalid * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic + * */ public static @NotNull CompletableFuture fromFile(@NotNull File file) throws IOException, NBTException, IllegalArgumentException { return fromFile(file, new Schematic()); @@ -97,10 +103,10 @@ private Scaffolding() {} /** * @param file the {@link File} to read from * @param schematic the {@link Schematic} to load the data into - * * @return a {@link CompletableFuture} that will be completed when the schematic is loaded * @throws IOException if the file is invalid * @throws NBTException if the NBT tag is invalid + * */ public static @NotNull CompletableFuture fromFile(@NotNull File file, @NotNull Schematic schematic) throws IOException, NBTException { if (!file.exists()) throw new FileNotFoundException("Invalid Schematic: File does not exist"); @@ -112,11 +118,11 @@ private Scaffolding() {} * Automatically detects the type of schematic and parses the input stream * * @param inputStream Schematic input - * * @return a {@link CompletableFuture} that will contain the schematic once loaded * @throws IOException if the input stream is invalid * @throws NBTException if the schematic is invalid * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic + * */ public static @NotNull CompletableFuture fromStream(@NotNull InputStream inputStream) throws IOException, NBTException, IllegalArgumentException { return fromStream(inputStream, new Schematic()); @@ -125,10 +131,10 @@ private Scaffolding() {} /** * @param inputStream the {@link InputStream} to read from * @param schematic the {@link Schematic} to load the data into - * * @return a {@link CompletableFuture} that will be completed when the schematic is loaded * @throws IOException if the input stream is invalid * @throws NBTException if the NBT tag is invalid + * */ public static @NotNull CompletableFuture fromStream(@NotNull InputStream inputStream, @NotNull Schematic schematic) throws IOException, NBTException { return fromNBT((NBTCompound) new NBTReader(inputStream, CompressedProcesser.GZIP).read(), schematic); @@ -139,9 +145,9 @@ private Scaffolding() {} * Automatically detects the schematic format from the provided {@link NBTCompound} and parses it. * * @param nbtTag The {@link NBTCompound} to read from - * * @return A {@link CompletableFuture} that will complete with the schematic once it's loaded * @throws NBTException If the NBT tag is invalid + * */ public static @NotNull CompletableFuture fromNBT(@NotNull NBTCompound nbtTag) throws NBTException, IllegalArgumentException { return fromNBT(nbtTag, new Schematic()); @@ -150,29 +156,29 @@ private Scaffolding() {} /** * @param nbtTag The NBT tag to parse * @param schematic The {@link Schematic} to load the data into - * * @return a {@link CompletableFuture} that will be completed when the schematic is loaded * @throws NBTException if the NBT tag is invalid + * */ public static @NotNull CompletableFuture fromNBT(@NotNull NBTCompound nbtTag, @NotNull Schematic schematic) throws NBTException { - if (MC_EDIT_SCHEMATIC_READER.isReadable(nbtTag)) { - return MC_EDIT_SCHEMATIC_READER.read(nbtTag, schematic); - } else if (SPONGE_SCHEMATIC_READER.isReadable(nbtTag)) { - return SPONGE_SCHEMATIC_READER.read(nbtTag, schematic); - } else { - throw new IllegalArgumentException("Unknown schematic type"); - } - } + Optional reader = MC_SCHEMATIC_READERS + .stream() + .filter(nbtSchematicReader -> nbtSchematicReader.isReadable(nbtTag)) + .findFirst(); + + if (reader.isPresent()) return reader.get().read(nbtTag, schematic); + throw new NBTException("No valid schematic-reader found."); + } /** * @param path The {@link Path} to read from - * * @return The parsed {@link Schematic} * @throws IOException if the file is invalid * @throws NBTException if the schematic is invalid * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic + * */ public static @NotNull Schematic fromPathSync(@NotNull Path path) throws IOException, NBTException, IllegalArgumentException { return fromPath(path).join(); @@ -181,10 +187,10 @@ private Scaffolding() {} /** * @param path the {@link Path} to read from * @param schematic the {@link Schematic} to load the data into - * * @return the parsed {@link Schematic} * @throws IOException if the file is invalid * @throws NBTException if the NBT tag is invalid + * */ public static @NotNull Schematic fromPathSync(@NotNull Path path, @NotNull Schematic schematic) throws IOException, NBTException { return fromPath(path, schematic).join(); @@ -193,11 +199,11 @@ private Scaffolding() {} /** * @param file The {@link File} to read from - * * @return The parsed {@link Schematic} * @throws IOException if the file is invalid * @throws NBTException if the schematic is invalid * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic + * */ public static @NotNull Schematic fromFileSync(@NotNull File file) throws IOException, NBTException, IllegalArgumentException { return fromFile(file).join(); @@ -206,10 +212,10 @@ private Scaffolding() {} /** * @param file the {@link File} to read from * @param schematic the {@link Schematic} to load the data into - * * @return the parsed {@link Schematic} * @throws IOException if the file is invalid * @throws NBTException if the NBT tag is invalid + * */ public static @NotNull Schematic fromFileSync(@NotNull File file, @NotNull Schematic schematic) throws IOException, NBTException { return fromFile(file, schematic).join(); @@ -220,10 +226,10 @@ private Scaffolding() {} * Automatically detects the schematic format from the provided {@link NBTCompound} and parses it synchronously. * * @param nbtTag the {@link NBTCompound} to read from - * * @return the parsed {@link Schematic} * @throws NBTException if the NBT tag is invalid * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic + * */ public static @NotNull Schematic fromNBTSync(@NotNull NBTCompound nbtTag) throws NBTException, IllegalArgumentException { return fromNBT(nbtTag).join(); @@ -232,9 +238,9 @@ private Scaffolding() {} /** * @param nbtTag The NBT tag to parse * @param schematic The {@link Schematic} to load the data into - * * @return the parsed {@link Schematic} * @throws NBTException if the NBT tag is invalid + * */ public static @NotNull Schematic fromNBTSync(@NotNull NBTCompound nbtTag, @NotNull Schematic schematic) throws NBTException { return fromNBT(nbtTag, schematic).join(); @@ -243,11 +249,11 @@ private Scaffolding() {} /** * @param inputStream The {@link InputStream} to read from - * * @return The parsed {@link Schematic} * @throws IOException if the input stream is invalid * @throws NBTException if the schematic is invalid * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic + * */ public static @NotNull Schematic fromStreamSync(@NotNull InputStream inputStream) throws IOException, NBTException, IllegalArgumentException { return fromStream(inputStream).join(); @@ -256,13 +262,13 @@ private Scaffolding() {} /** * @param inputStream the {@link InputStream} to read from * @param schematic the {@link Schematic} to load the data into - * * @return the parsed {@link Schematic} * @throws IOException if the input stream is invalid * @throws NBTException if the NBT tag is invalid + * */ public static @NotNull Schematic fromStreamSync(@NotNull InputStream inputStream, @NotNull Schematic schematic) throws IOException, NBTException { return fromStream(inputStream, schematic).join(); } -} +} \ No newline at end of file diff --git a/core/src/main/java/dev/hypera/scaffolding/instance/SchematicChunkLoader.java b/core/src/main/java/dev/hypera/scaffolding/instance/SchematicChunkLoader.java index 9ca78aa..026ab99 100644 --- a/core/src/main/java/dev/hypera/scaffolding/instance/SchematicChunkLoader.java +++ b/core/src/main/java/dev/hypera/scaffolding/instance/SchematicChunkLoader.java @@ -1,34 +1,7 @@ -/* - * Scaffolding - Schematic library for Minestom - * Copyright (c) 2022-latest The Scaffolding Library Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the “Software”), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ package dev.hypera.scaffolding.instance; import dev.hypera.scaffolding.schematic.Schematic; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; import net.minestom.server.coordinate.Vec; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.DynamicChunk; @@ -42,7 +15,14 @@ import org.jetbrains.annotations.Nullable; import space.vectrix.flare.fastutil.Long2ObjectSyncMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + // TODO: Entities? +//Todo: Implement a working save handler @SuppressWarnings("UnstableApiUsage") public class SchematicChunkLoader implements IChunkLoader { @@ -114,7 +94,8 @@ public static class Builder { private int yOffset; private int zOffset; - private Builder() {} + private Builder() { + } /** * Adds a schematic to this chunk loader. @@ -124,8 +105,8 @@ private Builder() {} * This means that the last added schematic is the only schematic that is guaranteed to have all its data. * * @param schematic The schematic to add. - * * @return This builder. + * */ // TODO: Add a way to position schematics within the instance. @Contract("_ -> this") @@ -140,8 +121,8 @@ private Builder() {} * @param x The x offset. * @param y The y offset. * @param z The z offset. - * * @return This builder. + * */ @Contract("_,_,_ -> this") public @NotNull Builder offset(int x, int y, int z) { @@ -155,8 +136,8 @@ private Builder() {} * Specifies the handler to use to save the chunks. * * @param handler The handler. - * * @return This builder. + * */ @Contract("_ -> this") public @NotNull Builder saveHandler(@NotNull Function<@NotNull Chunk, @NotNull CompletableFuture> handler) { @@ -171,4 +152,4 @@ private Builder() {} } -} +} \ No newline at end of file diff --git a/core/src/main/java/dev/hypera/scaffolding/region/Region.java b/core/src/main/java/dev/hypera/scaffolding/region/Region.java index f81c82a..2372d87 100644 --- a/core/src/main/java/dev/hypera/scaffolding/region/Region.java +++ b/core/src/main/java/dev/hypera/scaffolding/region/Region.java @@ -22,20 +22,20 @@ */ package dev.hypera.scaffolding.region; -import dev.hypera.scaffolding.schematic.LegacyLookup; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; 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 org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; + /** * Represents a rectangle 3 dimensional region of blocks within an {@link Instance}. + * */ public final class Region { @@ -49,6 +49,7 @@ public final class Region { * @param instance The instance that the region is in. * @param p1 The first point of the region. * @param p2 The second point of the region. + * */ public Region(@NotNull Instance instance, @NotNull Point p1, @NotNull Point p2) { this.instance = Objects.requireNonNull(instance); @@ -57,11 +58,11 @@ public Region(@NotNull Instance instance, @NotNull Point p1, @NotNull Point p2) } - /** * Force loads all {@link Chunk}s this region. * * @return a {@link CompletableFuture} that will complete once all chunks in the region have been loaded. The future will give the region as the result so that you can chain it. + * */ public @NotNull CompletableFuture loadChunks() { int lengthX = getUpperChunkX() - getLowerChunkX() + 1; @@ -82,6 +83,7 @@ public Region(@NotNull Instance instance, @NotNull Point p1, @NotNull Point p2) /** * @return the width of this region. + * */ public int getWidth() { return (upper.blockX() - lower.blockX()) + 1; @@ -89,6 +91,7 @@ public int getWidth() { /** * @return the height of this region. + * */ public int getHeight() { return (upper.blockY() - lower.blockY()) + 1; @@ -96,6 +99,7 @@ public int getHeight() { /** * @return the length of this region. + * */ public int getLength() { return (upper.blockZ() - lower.blockZ()) + 1; @@ -103,6 +107,7 @@ public int getLength() { /** * @return the x coordinate of the upper {@link Chunk} of this region. + * */ @Contract(pure = true) public int getUpperChunkX() { @@ -111,6 +116,7 @@ public int getUpperChunkX() { /** * @return the z coordinate of the upper {@link Chunk} of this region. + * */ @Contract(pure = true) public int getUpperChunkZ() { @@ -119,6 +125,7 @@ public int getUpperChunkZ() { /** * @return the x coordinate of the lower {@link Chunk} of this region. + * */ @Contract(pure = true) public int getLowerChunkX() { @@ -127,6 +134,7 @@ public int getLowerChunkX() { /** * @return the z coordinate of the lower {@link Chunk} of this region. + * */ @Contract(pure = true) public int getLowerChunkZ() { @@ -135,6 +143,7 @@ public int getLowerChunkZ() { /** * @return the number of {@link Chunk}s along the x coordinate of this region. + * */ @Contract(pure = true) public int getChunkSizeX() { @@ -143,6 +152,7 @@ public int getChunkSizeX() { /** * @return the number of {@link Chunk}s along the z coordinate of this region. + * */ @Contract(pure = true) public int getChunkSizeZ() { @@ -151,6 +161,7 @@ public int getChunkSizeZ() { /** * @return the instance that this region is in + * */ @Contract(pure = true) public @NotNull Instance getInstance() { @@ -159,6 +170,7 @@ public int getChunkSizeZ() { /** * @return the upper {@link Point} of this region. + * */ @Contract(pure = true) public @NotNull Point getUpper() { @@ -167,6 +179,7 @@ public int getChunkSizeZ() { /** * @return the lower {@link Point} of this region. + * */ @Contract(pure = true) public @NotNull Point getLower() { @@ -187,10 +200,10 @@ public int hashCode() { @Override public boolean equals(Object obj) { return obj == this || ( - obj instanceof Region region - && Objects.equals(this.instance, region.getInstance()) - && Objects.equals(this.lower, region.getLower()) - && Objects.equals(this.upper, region.getUpper()) + obj instanceof Region region + && Objects.equals(this.instance, region.getInstance()) + && Objects.equals(this.lower, region.getLower()) + && Objects.equals(this.upper, region.getUpper()) ); } diff --git a/core/src/main/java/dev/hypera/scaffolding/schematic/LegacyLookup.java b/core/src/main/java/dev/hypera/scaffolding/schematic/LegacyLookup.java index b37f5f8..3c6f48d 100644 --- a/core/src/main/java/dev/hypera/scaffolding/schematic/LegacyLookup.java +++ b/core/src/main/java/dev/hypera/scaffolding/schematic/LegacyLookup.java @@ -23,24 +23,21 @@ package dev.hypera.scaffolding.schematic; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import dev.hypera.scaffolding.Scaffolding; -import dev.hypera.scaffolding.region.Region; -import java.io.BufferedReader; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import net.minestom.server.instance.Chunk; -import net.minestom.server.instance.Instance; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; /** * A static utility class containing useful methods used throughout Scaffolding. @@ -48,30 +45,45 @@ @Internal public final class LegacyLookup { - private static final @NotNull String LEGACY_FILE_NAME = "legacy.json"; - private static final @NotNull Map LEGACY_LOOKUP = new HashMap<>(); + private static final @NotNull String LEGACY_FILE_NAME = "/legacy.json"; + private static final @NotNull Int2ObjectMap LEGACY_LOOKUP = new Int2ObjectOpenHashMap<>(); + private static final @NotNull Logger LOGGER = LoggerFactory.getLogger(LegacyLookup.class); + // Loading data on access + // TODO: Maybe loading data on program start for faster schematic loading static { - try (BufferedReader reader = Files.newBufferedReader(Path.of(Objects.requireNonNull(Scaffolding.class.getClassLoader().getResource(LEGACY_FILE_NAME)).toURI()))) { - JsonParser.parseReader(reader).getAsJsonArray().forEach(data -> { - JsonObject object = data.getAsJsonObject(); - LEGACY_LOOKUP.put(getLookupId(object.get("block").getAsInt(), object.get("data").getAsByte()), object.get("state").getAsShort()); - }); - } catch (IOException | URISyntaxException ex) { - ex.printStackTrace(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(LegacyLookup.class.getResourceAsStream(LEGACY_FILE_NAME), StandardCharsets.UTF_8))) { + JsonArray obj = JsonParser.parseReader(reader).getAsJsonArray(); + + + for (JsonElement jsonElement : obj) { + JsonObject object = jsonElement.getAsJsonObject(); + + LEGACY_LOOKUP.computeIfAbsent( + getLookupId(object.get("block").getAsInt(), object.get("data").getAsByte() + ), + integer -> object.get("state").getAsShort() + ); + } + LOGGER.info("Loaded legacy data."); + } catch (IOException e) { + LOGGER.error("Error while initializing Legacy data", e); } } + private LegacyLookup() { + throw new UnsupportedOperationException(); } /** * @param legacyBlockId The legacy block ID * @param legacyBlockData The legacy block data - * * @return The modern state ID for the given legacy block ID and data + * */ + public static short stateIdFromLegacy(int legacyBlockId, byte legacyBlockData) { return LEGACY_LOOKUP.get(getLookupId(legacyBlockId, legacyBlockData)); } @@ -82,8 +94,8 @@ public static short stateIdFromLegacy(int legacyBlockId, byte legacyBlockData) { * * @param legacyBlockId the legacy block ID * @param legacyBlockData the legacy block data - * * @return the lookup ID + * */ @Contract(pure = true) private static int getLookupId(int legacyBlockId, byte legacyBlockData) { diff --git a/core/src/main/java/dev/hypera/scaffolding/schematic/NBTSchematicReader.java b/core/src/main/java/dev/hypera/scaffolding/schematic/NBTSchematicReader.java index 7781432..fc4a9d8 100644 --- a/core/src/main/java/dev/hypera/scaffolding/schematic/NBTSchematicReader.java +++ b/core/src/main/java/dev/hypera/scaffolding/schematic/NBTSchematicReader.java @@ -22,12 +22,13 @@ */ package dev.hypera.scaffolding.schematic; -import java.util.concurrent.CompletableFuture; import org.jetbrains.annotations.NotNull; import org.jglrxavpok.hephaistos.collections.ImmutableByteArray; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTException; +import java.util.concurrent.CompletableFuture; + /** * A parser for schematics that uses NBT to store data. */ @@ -35,8 +36,10 @@ public abstract class NBTSchematicReader { /** * Checks if the provided NBT tag can be read by this reader. + * * @param compound The {@link NBTCompound} to check * @return whether this reader can read the provided tag. + * */ public abstract boolean isReadable(@NotNull NBTCompound compound); @@ -45,9 +48,9 @@ public abstract class NBTSchematicReader { * * @param compound The {@link NBTCompound} to read from * @param schematic The {@link Schematic} to read into - * * @return a {@link CompletableFuture} that will be completed with the {@link Schematic} * @throws NBTException If the provided NBT tag is invalid + * */ public abstract CompletableFuture read(@NotNull NBTCompound compound, @NotNull Schematic schematic) throws NBTException; @@ -55,9 +58,9 @@ public abstract class NBTSchematicReader { * @param compound The {@link NBTCompound} to read from * @param key The key to look for * @param exceptionMessage The exception message to throw if the key is not found - * * @return The value of the key * @throws NBTException If the provided NBT tag is invalid + * */ protected final int getInteger(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException { Integer value = compound.getInt(key); @@ -72,9 +75,9 @@ protected final int getInteger(@NotNull NBTCompound compound, @NotNull String ke * @param compound The {@link NBTCompound} to read from * @param key The key to look for * @param exceptionMessage The exception message to throw if the key is not found - * * @return The value of the key * @throws NBTException If the provided NBT tag is invalid + * */ protected final short getShort(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException { Short value = compound.getShort(key); @@ -89,9 +92,9 @@ protected final short getShort(@NotNull NBTCompound compound, @NotNull String ke * @param compound The {@link NBTCompound} to read from * @param key The key to look for * @param exceptionMessage The exception message to throw if the key is not found - * * @return The value of the key * @throws NBTException If the provided NBT tag is invalid + * */ protected final NBTCompound getCompound(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException { NBTCompound value = compound.getCompound(key); @@ -106,9 +109,9 @@ protected final NBTCompound getCompound(@NotNull NBTCompound compound, @NotNull * @param compound The {@link NBTCompound} to read from * @param key The key to look for * @param exceptionMessage The exception message to throw if the key is not found - * * @return The value of the key * @throws NBTException If the provided NBT tag is invalid + * */ protected final byte getByte(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException { Byte value = compound.getByte(key); @@ -123,9 +126,9 @@ protected final byte getByte(@NotNull NBTCompound compound, @NotNull String key, * @param compound The {@link NBTCompound} to read from * @param key The key to look for * @param exceptionMessage The exception message to throw if the key is not found - * * @return The value of the key * @throws NBTException If the provided NBT tag is invalid + * */ protected final byte[] getByteArray(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException { ImmutableByteArray value = compound.getByteArray(key); @@ -140,9 +143,9 @@ protected final byte[] getByteArray(@NotNull NBTCompound compound, @NotNull Stri * @param compound The {@link NBTCompound} to read from * @param key The key to look for * @param exceptionMessage The exception message to throw if the key is not found - * * @return The value of the key * @throws NBTException If the provided NBT tag is invalid + * */ protected final boolean getBoolean(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException { Boolean value = compound.getBoolean(key); @@ -157,9 +160,9 @@ protected final boolean getBoolean(@NotNull NBTCompound compound, @NotNull Strin * @param compound The {@link NBTCompound} to read from * @param key The key to look for * @param exceptionMessage The exception message to throw if the key is not found - * * @return The value of the key * @throws NBTException If the provided NBT tag is invalid + * */ protected final String getString(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException { String value = compound.getString(key); @@ -170,4 +173,4 @@ protected final String getString(@NotNull NBTCompound compound, @NotNull String return value; } -} +} \ No newline at end of file diff --git a/core/src/main/java/dev/hypera/scaffolding/schematic/Schematic.java b/core/src/main/java/dev/hypera/scaffolding/schematic/Schematic.java index 1b44302..5d889fb 100644 --- a/core/src/main/java/dev/hypera/scaffolding/schematic/Schematic.java +++ b/core/src/main/java/dev/hypera/scaffolding/schematic/Schematic.java @@ -23,10 +23,8 @@ package dev.hypera.scaffolding.schematic; import dev.hypera.scaffolding.region.Region; -import java.util.concurrent.CompletableFuture; 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.AbsoluteBlockBatch; import net.minestom.server.instance.block.Block; @@ -36,21 +34,24 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; + /** * A parsed schematic. */ -@SuppressWarnings({ "unused", "UnstableApiUsage" }) +@SuppressWarnings({"unused", "UnstableApiUsage"}) public final class Schematic implements Block.Setter { + private final AtomicBoolean locked = new AtomicBoolean(); private short[] blocks; - private int width, height, length; private int offsetX, offsetY, offsetZ; private int area; - private boolean locked; /** + * * Constructs a new schematic. The schematic will be locked and have an area of 0. */ public Schematic() { @@ -61,6 +62,7 @@ public Schematic() { * Resets this schematic to its original state. This is useful if you want to reuse a schematic multiple times. *

* The schematic will be locked after this method is called. + * */ public void reset() { width = height = length = 0; @@ -74,8 +76,8 @@ public void reset() { * Copies blocks from the given region into this schematic. * * @param region the {@link Region} to copy from - * * @return a {@link CompletableFuture} that will complete once all blocks have been copied + * */ public @NotNull CompletableFuture copy(@NotNull Region region) { reset(); @@ -104,7 +106,7 @@ public void reset() { } } - locked = false; + locked.set(false); return this; }); } @@ -115,6 +117,7 @@ public void reset() { * @param width new width * @param height new height * @param length new length + * */ public void setSize(int width, int height, int length) { this.width = width; @@ -131,8 +134,8 @@ public void setSize(int width, int height, int length) { * @param x block x coordinate * @param y block y coordinate * @param z block z coordinate - * * @return the index of the block at the given coordinates + * */ public int getBlockIndex(int x, int y, int z) { return y * width * length + z * width + x; @@ -143,8 +146,8 @@ public int getBlockIndex(int x, int y, int z) { * * @param instance the {@link Instance} to build this schematic in * @param position the {@link Point} to build this schematic at (note: the schematics offset will be applied to this position to get the lower corner) - * * @return a {@link CompletableFuture} that will complete once the schematic has been built + * */ public @NotNull CompletableFuture build(@NotNull Instance instance, @NotNull Point position) { return build(instance, position, false, false, false); @@ -158,11 +161,11 @@ public int getBlockIndex(int x, int y, int z) { * @param flipX whether to flip the schematic along the X axis * @param flipY whether to flip the schematic along the Y axis * @param flipZ whether to flip the schematic along the Z axis - * * @return a {@link CompletableFuture} that will complete once the schematic has been built + * */ public @NotNull CompletableFuture build(@NotNull Instance instance, @NotNull Point position, boolean flipX, boolean flipY, boolean flipZ) { - if (locked) { + if (locked.get()) { throw new IllegalStateException("Cannot build a locked schematic."); } @@ -184,8 +187,8 @@ public int getBlockIndex(int x, int y, int z) { /** * @param instance the {@link Instance} to check * @param position the {@link Point} to check - * * @return the {@link Region} that this schematic would take up if placed at the given position + * */ public @NotNull Region getContainingRegion(@NotNull Instance instance, @NotNull Point position) { return new Region(instance, position.add(offsetX, offsetY, offsetZ), position.add(offsetX + width, offsetY + height, offsetZ + length)); @@ -202,8 +205,8 @@ private boolean isPlaceable(@NotNull Region region) { * Applies the schematic to the given block setter. * * @param setter the block setter - * * @deprecated See {@link Schematic#apply(Point, boolean, boolean, boolean, Setter)} + * */ @Deprecated public void apply(@NotNull Block.Setter setter) { @@ -218,9 +221,10 @@ public void apply(@NotNull Block.Setter setter) { * @param flipY whether to flip the schematic along the Y axis * @param flipZ whether to flip the schematic along the Z axis * @param setter the {@link Block.Setter} to apply this schematic to + * */ public void apply(@NotNull Point position, boolean flipX, boolean flipY, boolean flipZ, @NotNull Block.Setter setter) { - if (locked) { + if (locked.get()) { throw new IllegalStateException("Cannot apply a locked schematic."); } @@ -247,8 +251,8 @@ public void apply(@NotNull Point position, boolean flipX, boolean flipY, boolean * @param x block x coordinate * @param y block y coordinate * @param z block z coordinate - * * @return the block at the given coordinates + * */ @Nullable public Block getBlock(int x, int y, int z) { @@ -259,7 +263,6 @@ public Block getBlock(int x, int y, int z) { * @param x block x coordinate * @param y block y coordinate * @param z block z coordinate - * * @return the state ID at the given coordinates */ public short getStateId(int x, int y, int z) { @@ -272,9 +275,10 @@ public short getStateId(int x, int y, int z) { * @param flipX whether to flip the schematic along the X axis * @param flipY whether to flip the schematic along the Y axis * @param flipZ whether to flip the schematic along the Z axis + * */ public void fork(@NotNull GenerationUnit unit, @NotNull Point position, boolean flipX, boolean flipY, boolean flipZ) { - if (locked) { + if (locked.get()) { throw new IllegalStateException("Cannot fork a locked schematic."); } @@ -286,6 +290,7 @@ public void fork(@NotNull GenerationUnit unit, @NotNull Point position, boolean /** * @param position the {@link Point} of the block to set * @param block the {@link Block} to set + * */ public void setBlock(@NotNull Point position, @NotNull Block block) { setBlock(position.blockX(), position.blockY(), position.blockZ(), block); @@ -300,6 +305,7 @@ public void setBlock(int x, int y, int z, @NotNull Block block) { * @param y the Y coordinate * @param z the Z coordinate * @param stateId the state ID + * */ public void setBlock(int x, int y, int z, short stateId) { blocks[getBlockIndex(x, y, z)] = stateId; @@ -308,6 +314,7 @@ public void setBlock(int x, int y, int z, short stateId) { /** * @param position the {@link Point} to place the block at * @param stateId the state id of the block to place. + * */ public void setBlock(@NotNull Point position, short stateId) { setBlock(position.blockX(), position.blockY(), position.blockZ(), stateId); @@ -315,6 +322,7 @@ public void setBlock(@NotNull Point position, short stateId) { /** * @return the width of this schematic + * */ public int getWidth() { return width; @@ -322,6 +330,7 @@ public int getWidth() { /** * @return the height of this schematic + * */ public int getHeight() { return height; @@ -329,6 +338,7 @@ public int getHeight() { /** * @return the length of the schematic + * */ public int getLength() { return length; @@ -338,6 +348,7 @@ public int getLength() { * Gets the offset in the x-axis used when {@link #build(Instance, Point)} or {@link #apply(Point, boolean, boolean, boolean, Block.Setter)} are called. * * @return the x offset + * */ public int getOffsetX() { return offsetX; @@ -347,6 +358,7 @@ public int getOffsetX() { * Gets the offset in the y-axis used when {@link #build(Instance, Point)} or {@link #apply(Point, boolean, boolean, boolean, Block.Setter)} are called. * * @return the y offset + * */ public int getOffsetY() { return offsetY; @@ -356,6 +368,7 @@ public int getOffsetY() { * Gets the offset in the z-axis used when {@link #build(Instance, Point)} or {@link #apply(Point, boolean, boolean, boolean, Block.Setter)} are called. * * @return the z offset + * */ public int getOffsetZ() { return offsetZ; @@ -365,6 +378,7 @@ public int getOffsetZ() { * Gets the area of this schematic. ({@code width} * {@code height} * {@code length}) * * @return the area of this schematic + * */ public int getArea() { return area; @@ -372,22 +386,25 @@ public int getArea() { /** * @return true if this schematic is locked, false otherwise + * */ public boolean isLocked() { - return locked; + return locked.getAcquire(); } /** * Sets the locked state of this schematic. Locked schematics can't be built, applied or forked, or saved. * * @param locked whether to lock this schematic + * */ public void setLocked(boolean locked) { - this.locked = locked; + this.locked.setRelease(locked); } /** * @param offset the {@link Point} to offset this schematic by + * */ public void setOffset(@NotNull Point offset) { setOffset(offset.blockX(), offset.blockY(), offset.blockZ()); @@ -397,6 +414,7 @@ public void setOffset(@NotNull Point offset) { * @param x new x offset * @param y new y offset * @param z new z offset + * */ public void setOffset(int x, int y, int z) { offsetX = x; @@ -407,8 +425,8 @@ public void setOffset(int x, int y, int z) { /** * @param instance the {@link Instance} to check * @param position the {@link Point} to check - * * @return {@code true} if the given position is within the bounds of the given instance, {@code false} otherwise. If either the instance or the position is null, false is returned. + * */ public boolean isPlaceable(@Nullable Instance instance, @Nullable Point position) { if (instance == null || position == null) { diff --git a/core/src/main/java/dev/hypera/scaffolding/schematic/readers/MCEditSchematicReader.java b/core/src/main/java/dev/hypera/scaffolding/schematic/readers/MCEditSchematicReader.java index 1bf1cc0..7f7b0d9 100644 --- a/core/src/main/java/dev/hypera/scaffolding/schematic/readers/MCEditSchematicReader.java +++ b/core/src/main/java/dev/hypera/scaffolding/schematic/readers/MCEditSchematicReader.java @@ -22,16 +22,17 @@ */ package dev.hypera.scaffolding.schematic.readers; -import dev.hypera.scaffolding.schematic.NBTSchematicReader; import dev.hypera.scaffolding.schematic.LegacyLookup; +import dev.hypera.scaffolding.schematic.NBTSchematicReader; import dev.hypera.scaffolding.schematic.Schematic; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import org.jetbrains.annotations.NotNull; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTException; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + /** * A parser for MCEdit schematics. (.schematic files) * @@ -127,5 +128,4 @@ private void readBlocksData(@NotNull Schematic schematic, @NotNull NBTCompound n } } } - } diff --git a/core/src/main/java/dev/hypera/scaffolding/schematic/readers/SpongeSchematicReader.java b/core/src/main/java/dev/hypera/scaffolding/schematic/readers/SpongeSchematicReader.java index ec67f37..7a47a0b 100644 --- a/core/src/main/java/dev/hypera/scaffolding/schematic/readers/SpongeSchematicReader.java +++ b/core/src/main/java/dev/hypera/scaffolding/schematic/readers/SpongeSchematicReader.java @@ -24,14 +24,6 @@ import dev.hypera.scaffolding.schematic.NBTSchematicReader; import dev.hypera.scaffolding.schematic.Schematic; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import net.minestom.server.instance.block.Block; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,6 +31,10 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTException; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + /** * A parser for Sponge schematics. (.schem files) * @@ -50,7 +46,8 @@ public class SpongeSchematicReader extends NBTSchematicReader { @Override public boolean isReadable(@NotNull NBTCompound compound) { // TODO: Improve this - return compound.contains("Palette"); + int version = compound.contains("Version") ? compound.getAsInt("Version") : 0; + return compound.contains("Palette") || version < 2; } @Override @@ -103,7 +100,7 @@ private void readBlockPalette(@NotNull Schematic schematic, @NotNull NBTCompound } Map palette = unsortedPalette.entrySet().stream().sorted(Map.Entry.comparingByValue()) - .collect(LinkedHashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), LinkedHashMap::putAll); + .collect(LinkedHashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), LinkedHashMap::putAll); ImmutableByteArray blocksData = nbtTag.getByteArray("BlockData"); if (blocksData == null || blocksData.getSize() == 0) { @@ -179,4 +176,4 @@ private short getStateId(@NotNull String input) { return Block.fromNamespaceId(input.split("\\[")[0]); } -} +} \ No newline at end of file diff --git a/docs/content/.vitepress/config.js b/docs/content/.vitepress/config.js index f860c0c..66ac952 100644 --- a/docs/content/.vitepress/config.js +++ b/docs/content/.vitepress/config.js @@ -24,42 +24,42 @@ import {defineConfig} from 'vitepress' export default defineConfig({ - title: 'Scaffolding', - description: 'Schematic library for Minestom', + title: 'Scaffolding', + description: 'Schematic library for Minestom', - themeConfig: { - sidebar: { - '/': getSidebar() + themeConfig: { + sidebar: { + '/': getSidebar() + } } - } }) function getSidebar() { - return [ - { - text: 'Introduction', - link: '/' - }, - { - text: 'Setup', - children: [ - { text: 'Getting Started', link: '/setup/getting-started' }, - ] - }, - { - text: 'Using Schematics', - children: [ - { text: 'Load', link: '/usage/read' }, - { text: 'Copy', link: '/usage/copy' }, - { text: 'Build', link: '/usage/build ' }, - { text: 'Write', link: '/usage/write ' }, - ] - }, - { - text: 'Utilities', - children: [ - { text: 'Region', link: '/utilities/region' }, - ] - }, - ] + return [ + { + text: 'Introduction', + link: '/' + }, + { + text: 'Setup', + children: [ + {text: 'Getting Started', link: '/setup/getting-started'}, + ] + }, + { + text: 'Using Schematics', + children: [ + {text: 'Load', link: '/usage/read'}, + {text: 'Copy', link: '/usage/copy'}, + {text: 'Build', link: '/usage/build '}, + {text: 'Write', link: '/usage/write '}, + ] + }, + { + text: 'Utilities', + children: [ + {text: 'Region', link: '/utilities/region'}, + ] + }, + ] } diff --git a/docs/content/index.md b/docs/content/index.md index d1759c3..f8a32db 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -1,2 +1,3 @@ # Introduction + Scaffolding is a library for interacting with schematics for the [Minestom](https://minestom.net/) framework. \ No newline at end of file diff --git a/docs/content/setup/getting-started.md b/docs/content/setup/getting-started.md index 1eab389..25dd3bd 100644 --- a/docs/content/setup/getting-started.md +++ b/docs/content/setup/getting-started.md @@ -3,16 +3,18 @@ This guide describes how to setup Scaffolding as a dependency for your project. ::: info -Scaffolding is intended to be used as a library for the Minestom framework. You can learn how to setup Minestom [here](https://wiki.minestom.net/setup/dependencies/). +Scaffolding is intended to be used as a library for the Minestom framework. You can learn how to setup +Minestom [here](https://wiki.minestom.net/setup/dependencies/). ::: - ## Repository As with Minestom, Scaffolding uses JitPack to distribute releases. ### Gradle (Kotlin) + Add JitPack as a repository to your ```build.gradle.kts``` file. + ```kotlin{2} repositories { maven(url = "https://jitpack.io") @@ -20,7 +22,9 @@ repositories { ``` ### Gradle (Groovy) + Add JitPack as a repository to your ```build.gradle``` file. + ```groovy{2} repositories { maven { url 'https://jitpack.io' } @@ -28,7 +32,9 @@ repositories { ``` ### Maven + Add JitPack as a repository to your ```pom.xml``` file. + ```xml{2-5} @@ -43,7 +49,9 @@ Add JitPack as a repository to your ```pom.xml``` file. Keep an eye out for new releases on [Jitpack](https://jitpack.io/#CrystalGamesMc/scaffolding). ### Gradle (Kotlin) + Add Scaffolding as a dependency in your ```build.gradle.kts``` file. + ```kts{2} dependencies { implementation("com.github.CrystalGamesMc:scaffolding:Tag") @@ -51,7 +59,9 @@ dependencies { ``` ### Gradle (Groovy) + Add Scaffolding as a dependency in your ```build.gradle``` file. + ```groovy{2} dependencies { implementation 'com.github.CrystalGamesMc:scaffolding:Tag' @@ -59,7 +69,9 @@ dependencies { ``` ### Maven + Add Scaffolding as a dependency in your ```pom.xml``` file. + ```xml{2-6} diff --git a/docs/content/usage/read.md b/docs/content/usage/read.md index 77876db..f44327d 100644 --- a/docs/content/usage/read.md +++ b/docs/content/usage/read.md @@ -1,7 +1,9 @@ # Loading Schematics + Scaffolding offers many ways to load schematics. ## Automatically parsing type + ```java File file = new File("schematics/your_schematic.schematic"); Schematic schematic = Scaffolding.fromFileSync(file); diff --git a/editor/README.md b/editor/README.md index 0e2af61..e4d2d91 100644 --- a/editor/README.md +++ b/editor/README.md @@ -1,2 +1,3 @@ # Scaffolding Editor + Scaffolding Editor is by no means production-ready. Currently, this is just a test environment for Scaffolding's core. \ No newline at end of file diff --git a/editor/build.gradle.kts b/editor/build.gradle.kts index e34efa4..7b763bb 100644 --- a/editor/build.gradle.kts +++ b/editor/build.gradle.kts @@ -31,9 +31,14 @@ java { targetCompatibility = JavaVersion.VERSION_17 } +tasks { + compileJava { + options.encoding = "UTF-8" + } +} dependencies { implementation(project(":core")) - implementation("com.github.Minestom:Minestom:7be96b7679") + implementation("com.github.Minestom:Minestom:${rootProject.extra["minestomVersion"]}") } \ No newline at end of file diff --git a/editor/src/main/java/dev/hypera/scaffolding/editor/Clipboard.java b/editor/src/main/java/dev/hypera/scaffolding/editor/Clipboard.java index 6ae4b32..edb692a 100644 --- a/editor/src/main/java/dev/hypera/scaffolding/editor/Clipboard.java +++ b/editor/src/main/java/dev/hypera/scaffolding/editor/Clipboard.java @@ -38,24 +38,28 @@ import net.minestom.server.particle.ParticleCreator; import net.minestom.server.timer.Task; import net.minestom.server.utils.time.TimeUnit; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.logging.Logger; + public class Clipboard { public static final Component FIRST_POINT_COMPONENT = Component.text("First point", NamedTextColor.GOLD); public static final Component SECOND_POINT_COMPONENT = Component.text("Second point", NamedTextColor.AQUA); private final Player player; + private final Task drawParticlesTask; private final Schematic schematic = new Schematic(); - private Point firstPoint, secondPoint; + private Point firstPoint, secondPoint, firstPointSet, secondPointSet; private Hologram firstPointHologram, secondPointHologram; public Clipboard(Player player) { this.player = player; - drawParticlesTask = MinecraftServer.getSchedulerManager().buildTask(this::drawSelection).repeat(50, TimeUnit.MILLISECOND).schedule(); + this.drawParticlesTask = MinecraftServer.getSchedulerManager().buildTask(this::drawSelection).repeat(150, TimeUnit.MILLISECOND).schedule(); } public boolean hasValidSelection() { @@ -72,7 +76,9 @@ public boolean hasValidSelection() { public void drawSelection() { Region region = createRegionFromSelection(); - if (region == null) return; + if (region == null) { + return; + } Point lower = region.getLower(); Point upper = region.getUpper().add(1); @@ -87,20 +93,23 @@ public void drawSelection() { Vec p7 = new Vec(upper.x(), upper.y(), upper.z()); Vec p8 = new Vec(lower.x(), upper.y(), upper.z()); - drawLine(player, Particle.CRIT, p1, p2); - drawLine(player, Particle.CRIT, p2, p3); - drawLine(player, Particle.CRIT, p3, p4); - drawLine(player, Particle.CRIT, p4, p1); + Particle p = Particle.REVERSE_PORTAL; + - drawLine(player, Particle.CRIT, p5, p6); - drawLine(player, Particle.CRIT, p6, p7); - drawLine(player, Particle.CRIT, p7, p8); - drawLine(player, Particle.CRIT, p8, p5); + drawLine(player, p, p1, p2); + drawLine(player, p, p2, p3); + drawLine(player, p, p3, p4); + drawLine(player, p, p4, p1); - drawLine(player, Particle.CRIT, p1, p5); - drawLine(player, Particle.CRIT, p2, p6); - drawLine(player, Particle.CRIT, p3, p7); - drawLine(player, Particle.CRIT, p4, p8); + drawLine(player, p, p5, p6); + drawLine(player, p, p6, p7); + drawLine(player, p, p7, p8); + drawLine(player, p, p8, p5); + + drawLine(player, p, p1, p5); + drawLine(player, p, p2, p6); + drawLine(player, p, p3, p7); + drawLine(player, p, p4, p8); } private void drawLine(Player player, Particle particle, Point p1, Point p2) { @@ -108,8 +117,9 @@ private void drawLine(Player player, Particle particle, Point p1, Point p2) { final Vec v2 = Vec.fromPoint(p2); Vec direction = v2.sub(v1).normalize(); + double repeat = v1.distance(v2) / 4; - for (Vec position = v1; position.sub(v2).dot(direction) < 0; position = position.add(direction.mul(0.2d))) { + for (Vec position = v1; position.sub(v2).dot(direction) < 0; position = position.add(direction.mul(repeat))) { ParticlePacket packet = ParticleCreator.createParticlePacket(particle, true, position.x(), position.y(), position.z(), 0, 0, 0, 0, 1, null); player.sendPacket(packet); } @@ -120,12 +130,98 @@ public Point getFirstPoint() { } public void setFirstPoint(Point firstPoint) { - this.firstPoint = firstPoint; + boolean isOverride = this.firstPointSet != null && this.firstPointSet.samePoint(firstPoint); + this.firstPointSet = firstPoint; + + //Setting + if(this.firstPoint == null) { + //set + this.firstPoint = firstPoint; + + //Creating hologram + if (firstPointHologram != null) firstPointHologram.remove(); + firstPointHologram = createHologram(firstPoint, FIRST_POINT_COMPONENT); + + //Starting animation + this.drawParticlesTask.unpark(); + + + player.sendMessage("Setzen"); + return; + } + if(this.firstPoint.samePoint(firstPoint)) { + //Entfernen + if (firstPointHologram != null) firstPointHologram.remove(); + this.firstPoint = null; + + player.sendMessage("entfernen"); + return; + } + //Replace + if(this.firstPoint != null) { + player.sendMessage("Double click the same location for override"); + + //Double clicked location + if(isOverride) { + player.sendMessage("Overriden!"); + this.firstPoint = firstPoint; + + //Create hologram + if (firstPointHologram != null) firstPointHologram.remove(); + firstPointHologram = createHologram(firstPoint, FIRST_POINT_COMPONENT); + + //Start animation + this.drawParticlesTask.unpark(); + } + } + } + + public void setSecondPoint(Point secondPoint) { + boolean isOverride = this.secondPointSet != null && this.secondPointSet.samePoint(secondPoint); + this.secondPointSet = secondPoint; + + //Setting + if(this.secondPoint == null) { + //set + this.secondPoint = secondPoint; - if (firstPointHologram != null) firstPointHologram.remove(); - firstPointHologram = createHologram(firstPoint, FIRST_POINT_COMPONENT); + //Creating hologram + if (secondPointHologram != null) secondPointHologram.remove(); + secondPointHologram = createHologram(secondPoint, SECOND_POINT_COMPONENT); + + //Starting animation + if(this.drawParticlesTask != null) { + this.drawParticlesTask.unpark(); + } + + player.sendMessage("Setzen"); + return; + } + if(this.secondPoint.samePoint(secondPoint)) { + //Entfernen + if (secondPointHologram != null) secondPointHologram.remove(); + this.secondPoint = null; - player.sendMessage(Component.text("Set ", NamedTextColor.GRAY).append(FIRST_POINT_COMPONENT)); + player.sendMessage("entfernen"); + return; + } + //Replace + if(this.secondPoint != null) { + player.sendMessage("Double click the same location for override"); + + //Double clicked location + if(isOverride) { + player.sendMessage("Overriden!"); + this.secondPoint = secondPoint; + + //Create hologram + if (secondPointHologram != null) secondPointHologram.remove(); + secondPointHologram = createHologram(secondPoint, SECOND_POINT_COMPONENT); + + //Start animation + this.drawParticlesTask.unpark(); + } + } } private Hologram createHologram(Point position, Component text) { @@ -139,20 +235,11 @@ public Point getSecondPoint() { return secondPoint; } - public void setSecondPoint(Point secondPoint) { - this.secondPoint = secondPoint; - - if (secondPointHologram != null) secondPointHologram.remove(); - secondPointHologram = createHologram(secondPoint, SECOND_POINT_COMPONENT); - - player.sendMessage(Component.text("Set ", NamedTextColor.GRAY).append(SECOND_POINT_COMPONENT)); - } - public Schematic getSchematic() { return schematic; } public void cleanup() { - drawParticlesTask.cancel(); + this.drawParticlesTask.cancel(); } } diff --git a/editor/src/main/java/dev/hypera/scaffolding/editor/ScaffoldingEditor.java b/editor/src/main/java/dev/hypera/scaffolding/editor/ScaffoldingEditor.java index fc524d0..0efc1da 100644 --- a/editor/src/main/java/dev/hypera/scaffolding/editor/ScaffoldingEditor.java +++ b/editor/src/main/java/dev/hypera/scaffolding/editor/ScaffoldingEditor.java @@ -25,12 +25,8 @@ import dev.hypera.scaffolding.editor.commands.CopyCommand; import dev.hypera.scaffolding.editor.commands.LoadCommand; import dev.hypera.scaffolding.editor.commands.PasteCommand; +import dev.hypera.scaffolding.editor.commands.SaveCommand; import dev.hypera.scaffolding.editor.features.SelectionFeature; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; @@ -50,6 +46,12 @@ import net.minestom.server.utils.NamespaceID; import net.minestom.server.world.DimensionType; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; + public class ScaffoldingEditor { public static final Path SCHEMATICS_PATH = Paths.get("schematics"); @@ -71,12 +73,13 @@ public static void main(String[] args) throws IOException { commandManager.register(new LoadCommand()); commandManager.register(new CopyCommand()); commandManager.register(new PasteCommand()); + commandManager.register(new SaveCommand()); GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler(); globalEventHandler.addListener(PlayerLoginEvent.class, event -> { Player player = event.getPlayer(); - clipboards.put(player, new Clipboard(player)); + clipboards.computeIfAbsent(player,Clipboard::new); player.setRespawnPoint(new Pos(0, 6, 0)); event.setSpawningInstance(instance); }); @@ -86,7 +89,7 @@ public static void main(String[] args) throws IOException { ItemStack wand = ItemStack.builder(Material.WOODEN_AXE) .amount(1) - .displayName(Component.text("Selection Tool", NamedTextColor.WHITE)) + .displayName(Component.text("Selection Tool", NamedTextColor.GOLD)) .lore( Component.text("Use this to edit the world.", NamedTextColor.GRAY), Component.empty(), Clipboard.FIRST_POINT_COMPONENT.append(Component.text(" - left click", NamedTextColor.GRAY)), diff --git a/editor/src/main/java/dev/hypera/scaffolding/editor/commands/LoadCommand.java b/editor/src/main/java/dev/hypera/scaffolding/editor/commands/LoadCommand.java index bcf7ed0..511625f 100644 --- a/editor/src/main/java/dev/hypera/scaffolding/editor/commands/LoadCommand.java +++ b/editor/src/main/java/dev/hypera/scaffolding/editor/commands/LoadCommand.java @@ -25,10 +25,7 @@ import dev.hypera.scaffolding.Scaffolding; import dev.hypera.scaffolding.editor.Clipboard; import dev.hypera.scaffolding.editor.ScaffoldingEditor; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.stream.Stream; +import dev.hypera.scaffolding.schematic.Schematic; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.command.builder.Command; @@ -38,6 +35,17 @@ import net.minestom.server.entity.Player; import org.jglrxavpok.hephaistos.nbt.NBTException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.DecimalFormat; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Stream; + public class LoadCommand extends Command { public LoadCommand() { @@ -55,18 +63,29 @@ public LoadCommand() { } }); + addSyntax((sender, context) -> { if (sender instanceof Player player) { try { String schematicName = context.get(nameArgument); - Clipboard clipboard = ScaffoldingEditor.getClipboard(player); + CompletableFuture ms = CompletableFuture.completedFuture(System.currentTimeMillis()); - Scaffolding.fromFile(ScaffoldingEditor.SCHEMATICS_PATH.resolve(schematicName).toFile(), clipboard.getSchematic()).thenRun(() -> { - player.sendMessage(Component.text("Loaded schematic " + schematicName, NamedTextColor.GRAY)); + CompletableFuture cmp = Scaffolding.fromFile(ScaffoldingEditor.SCHEMATICS_PATH.resolve(schematicName).toFile(), clipboard.getSchematic()); + cmp.whenCompleteAsync((schematic, throwable) -> { + if(throwable != null) { + player.sendMessage(Component.text("Error while loading schematic: " + throwable.getLocalizedMessage())); + throwable.printStackTrace(); + return; + } + String formatted = DecimalFormat.getInstance().format((System.currentTimeMillis() - ms.join()) / 1000.0); + player.sendMessage(Component + .text("Loaded schematic " + schematicName + " with size of blocks: " + clipboard.getSchematic().getArea() + " in seconds: " + formatted, NamedTextColor.GRAY) + ); }); } catch (IOException | NBTException e) { - player.sendMessage("Failed to load schematic"); + player.sendMessage("Failed to load schematic" + e.getLocalizedMessage()); + e.printStackTrace(); } } }, nameArgument); diff --git a/editor/src/main/java/dev/hypera/scaffolding/editor/commands/PasteCommand.java b/editor/src/main/java/dev/hypera/scaffolding/editor/commands/PasteCommand.java index 9469949..868b31f 100644 --- a/editor/src/main/java/dev/hypera/scaffolding/editor/commands/PasteCommand.java +++ b/editor/src/main/java/dev/hypera/scaffolding/editor/commands/PasteCommand.java @@ -25,11 +25,16 @@ import dev.hypera.scaffolding.editor.Clipboard; import dev.hypera.scaffolding.editor.ScaffoldingEditor; import dev.hypera.scaffolding.schematic.Schematic; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.command.builder.Command; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.Player; import net.minestom.server.instance.Instance; +import java.text.DecimalFormat; +import java.util.concurrent.CompletableFuture; + public class PasteCommand extends Command { public PasteCommand() { @@ -37,12 +42,14 @@ public PasteCommand() { setDefaultExecutor((sender, context) -> { if ((sender instanceof Player player)) { + CompletableFuture ms = CompletableFuture.completedFuture(System.currentTimeMillis()); Clipboard clipboard = ScaffoldingEditor.getClipboard(player); Schematic schematic = clipboard.getSchematic(); Instance instance = player.getInstance(); Pos placementPosition = player.getPosition(); + if (instance == null) { player.sendMessage("You are not in an instance. This should probably not happen..."); return; @@ -58,7 +65,18 @@ public PasteCommand() { return; } - schematic.build(instance, placementPosition).thenRunAsync(() -> player.sendMessage("Schematic pasted")); + schematic.build(instance, placementPosition).whenCompleteAsync((region, throwable) -> { + if(throwable != null) { + player.sendMessage(Component.text("Error while pasting schematic: " + throwable.getLocalizedMessage())); + throwable.printStackTrace(); + return; + } + + String formatted = DecimalFormat.getInstance().format((System.currentTimeMillis() - ms.join()) / 1000.0); + player.sendMessage(Component + .text("Pasted schematic with blocks: " + clipboard.getSchematic().getArea() + " in seconds: " + formatted, NamedTextColor.GRAY) + ); + }); } }); } diff --git a/editor/src/main/java/dev/hypera/scaffolding/editor/commands/SaveCommand.java b/editor/src/main/java/dev/hypera/scaffolding/editor/commands/SaveCommand.java new file mode 100644 index 0000000..c10bfda --- /dev/null +++ b/editor/src/main/java/dev/hypera/scaffolding/editor/commands/SaveCommand.java @@ -0,0 +1,77 @@ +package dev.hypera.scaffolding.editor.commands; + +import com.google.gson.Gson; +import com.google.gson.stream.JsonWriter; +import dev.hypera.scaffolding.Scaffolding; +import dev.hypera.scaffolding.editor.Clipboard; +import dev.hypera.scaffolding.editor.ScaffoldingEditor; +import dev.hypera.scaffolding.schematic.Schematic; +import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; +import net.minestom.server.command.builder.Command; +import net.minestom.server.command.builder.arguments.ArgumentType; +import net.minestom.server.command.builder.arguments.ArgumentWord; +import net.minestom.server.command.builder.suggestion.SuggestionEntry; +import net.minestom.server.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +public class SaveCommand extends Command { + + public SaveCommand() { + super("save"); + + ArgumentWord nameArgument = ArgumentType.Word("nameArgument"); + nameArgument.setSuggestionCallback((sender, context, suggestion) -> { + try (Stream paths = Files.walk(ScaffoldingEditor.SCHEMATICS_PATH)) { + paths.filter(Files::isRegularFile).forEach(path -> { + String file = path.getFileName().toString(); + suggestion.addEntry(new SuggestionEntry(file, Component.text("Test"))); + }); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + this.addSyntax((sender, context) -> { + if(sender instanceof Player player) { + String schematicName = context.get(nameArgument); + Clipboard clipboard = ScaffoldingEditor.getClipboard(player); + + if(!clipboard.hasValidSelection()) { + player.sendMessage("No selection to save."); + return; + } + + if(clipboard.getSchematic() == null) { + player.sendMessage("Not schematic"); + return; + } + + File file = new File(ScaffoldingEditor.SCHEMATICS_PATH + "/" + schematicName); + if(file.exists()) { + player.sendMessage("You cannot override schematics."); + return; + } + + try { + file.createNewFile(); + player.sendMessage(clipboard.getSchematic().toString()); + FileWriter writer = new FileWriter(file); + writer.write(new Gson().toJson(clipboard.getSchematic())); + writer.flush(); + writer.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + player.sendMessage("Saved to file" + file); + } + + }, nameArgument); + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..2d720b9 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,8 @@ +forceSign=true + +# Gradle +org.gradle.jvmargs=-Xmx4G +org.gradle.parallel=true +org.gradle.daemon=true +org.gradle.configureondemand=true +org.gradle.caching=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f5589ad..2010eb1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -20,9 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # - distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +org.gradle.jvmargs=-Dfile.encoding=UTF-8