├── .github └── workflows │ ├── build.yml │ └── pages.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── bukkit ├── build.gradle.kts └── src │ └── main │ ├── java │ └── org │ │ └── popcraft │ │ └── chunky │ │ ├── ChunkyBukkit.java │ │ ├── integration │ │ └── WorldBorderIntegration.java │ │ └── platform │ │ ├── BukkitBorder.java │ │ ├── BukkitConfig.java │ │ ├── BukkitPlayer.java │ │ ├── BukkitSender.java │ │ ├── BukkitServer.java │ │ └── BukkitWorld.java │ └── resources │ ├── config.yml │ └── plugin.yml ├── common ├── build.gradle.kts └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── popcraft │ │ │ └── chunky │ │ │ ├── Chunky.java │ │ │ ├── ChunkyProvider.java │ │ │ ├── GenerationTask.java │ │ │ ├── Selection.java │ │ │ ├── api │ │ │ ├── ChunkyAPI.java │ │ │ ├── ChunkyAPIImpl.java │ │ │ └── event │ │ │ │ └── task │ │ │ │ ├── GenerationCompleteEvent.java │ │ │ │ └── GenerationProgressEvent.java │ │ │ ├── command │ │ │ ├── CancelCommand.java │ │ │ ├── CenterCommand.java │ │ │ ├── ChunkyCommand.java │ │ │ ├── CommandArguments.java │ │ │ ├── CommandLiteral.java │ │ │ ├── ConfirmCommand.java │ │ │ ├── ContinueCommand.java │ │ │ ├── CornersCommand.java │ │ │ ├── HelpCommand.java │ │ │ ├── PatternCommand.java │ │ │ ├── PauseCommand.java │ │ │ ├── ProgressCommand.java │ │ │ ├── QuietCommand.java │ │ │ ├── RadiusCommand.java │ │ │ ├── ReloadCommand.java │ │ │ ├── SelectionCommand.java │ │ │ ├── ShapeCommand.java │ │ │ ├── SilentCommand.java │ │ │ ├── SpawnCommand.java │ │ │ ├── StartCommand.java │ │ │ ├── TrimCommand.java │ │ │ ├── WorldBorderCommand.java │ │ │ └── WorldCommand.java │ │ │ ├── event │ │ │ ├── Cancellable.java │ │ │ ├── Event.java │ │ │ ├── EventBus.java │ │ │ ├── command │ │ │ │ └── ReloadCommandEvent.java │ │ │ └── task │ │ │ │ ├── GenerationTaskFinishEvent.java │ │ │ │ └── GenerationTaskUpdateEvent.java │ │ │ ├── integration │ │ │ ├── BorderIntegration.java │ │ │ └── Integration.java │ │ │ ├── iterator │ │ │ ├── ChunkIterator.java │ │ │ ├── ChunkIteratorFactory.java │ │ │ ├── ConcentricChunkIterator.java │ │ │ ├── CsvChunkIterator.java │ │ │ ├── Loop2ChunkIterator.java │ │ │ ├── PatternType.java │ │ │ ├── RegionChunkIterator.java │ │ │ ├── SpiralChunkIterator.java │ │ │ └── WorldChunkIterator.java │ │ │ ├── platform │ │ │ ├── Border.java │ │ │ ├── Config.java │ │ │ ├── Player.java │ │ │ ├── Sender.java │ │ │ ├── Server.java │ │ │ ├── World.java │ │ │ ├── impl │ │ │ │ └── GsonConfig.java │ │ │ └── util │ │ │ │ ├── Location.java │ │ │ │ ├── Vector2.java │ │ │ │ └── Vector3.java │ │ │ ├── shape │ │ │ ├── AbstractEllipse.java │ │ │ ├── AbstractPolygon.java │ │ │ ├── AbstractShape.java │ │ │ ├── Circle.java │ │ │ ├── Diamond.java │ │ │ ├── Ellipse.java │ │ │ ├── Hexagon.java │ │ │ ├── Pentagon.java │ │ │ ├── Rectangle.java │ │ │ ├── Shape.java │ │ │ ├── ShapeFactory.java │ │ │ ├── ShapeType.java │ │ │ ├── ShapeUtil.java │ │ │ ├── Square.java │ │ │ ├── Star.java │ │ │ └── Triangle.java │ │ │ └── util │ │ │ ├── ChunkCoordinate.java │ │ │ ├── ChunkMath.java │ │ │ ├── Disk.java │ │ │ ├── Formatting.java │ │ │ ├── Hilbert.java │ │ │ ├── Input.java │ │ │ ├── Pair.java │ │ │ ├── Parameter.java │ │ │ ├── PendingAction.java │ │ │ ├── RegionCache.java │ │ │ ├── TaskLoader.java │ │ │ ├── TaskScheduler.java │ │ │ ├── TranslationKey.java │ │ │ ├── Translator.java │ │ │ └── Version.java │ └── resources │ │ ├── lang │ │ ├── bg.json │ │ ├── bs.json │ │ ├── cs.json │ │ ├── de.json │ │ ├── en.json │ │ ├── es.json │ │ ├── fi.json │ │ ├── fr.json │ │ ├── he.json │ │ ├── hi.json │ │ ├── hr.json │ │ ├── it.json │ │ ├── ja.json │ │ ├── ko.json │ │ ├── nl.json │ │ ├── no.json │ │ ├── pl.json │ │ ├── pt.json │ │ ├── pt_BR.json │ │ ├── ru.json │ │ ├── sr_CS.json │ │ ├── tr.json │ │ ├── uk.json │ │ ├── vi.json │ │ ├── zh_CN.json │ │ ├── zh_HK.json │ │ └── zh_TW.json │ │ └── version.properties │ └── test │ └── java │ └── org │ └── popcraft │ └── chunky │ ├── iterator │ ├── BoundaryTest.java │ ├── ConstructorTest.java │ └── TotalTest.java │ ├── shape │ └── ShapeTest.java │ └── util │ ├── RegionCacheTest.java │ └── TranslatorTest.java ├── crowdin.yml ├── fabric ├── build.gradle.kts └── src │ └── main │ ├── java │ └── org │ │ └── popcraft │ │ └── chunky │ │ ├── ChunkyFabric.java │ │ ├── command │ │ └── suggestion │ │ │ ├── PatternSuggestionProvider.java │ │ │ ├── ShapeSuggestionProvider.java │ │ │ ├── SuggestionProviders.java │ │ │ └── TrimModeSuggestionProvider.java │ │ ├── listeners │ │ └── bossbar │ │ │ ├── BossBarTaskFinishListener.java │ │ │ └── BossBarTaskUpdateListener.java │ │ ├── mixin │ │ ├── ChunkMapMixin.java │ │ ├── MinecraftServerMixin.java │ │ └── ServerChunkCacheMixin.java │ │ └── platform │ │ ├── FabricBorder.java │ │ ├── FabricPlayer.java │ │ ├── FabricSender.java │ │ ├── FabricServer.java │ │ └── FabricWorld.java │ └── resources │ ├── assets │ └── chunky │ │ └── icon.png │ ├── chunky.mixins.json │ └── fabric.mod.json ├── folia ├── build.gradle.kts └── src │ └── main │ └── java │ └── org │ └── popcraft │ └── chunky │ └── platform │ └── Folia.java ├── forge ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ ├── java │ └── org │ │ └── popcraft │ │ └── chunky │ │ ├── ChunkyForge.java │ │ ├── command │ │ └── suggestion │ │ │ ├── PatternSuggestionProvider.java │ │ │ ├── ShapeSuggestionProvider.java │ │ │ ├── SuggestionProviders.java │ │ │ └── TrimModeSuggestionProvider.java │ │ ├── listeners │ │ └── bossbar │ │ │ ├── BossBarTaskFinishListener.java │ │ │ └── BossBarTaskUpdateListener.java │ │ └── platform │ │ ├── ForgeBorder.java │ │ ├── ForgePlayer.java │ │ ├── ForgeSender.java │ │ ├── ForgeServer.java │ │ └── ForgeWorld.java │ └── resources │ ├── META-INF │ ├── accesstransformer.cfg │ └── mods.toml │ ├── icon.png │ └── pack.mcmeta ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── nbt ├── build.gradle.kts └── src │ └── main │ └── java │ └── org │ └── popcraft │ └── chunky │ └── nbt │ ├── ByteArrayTag.java │ ├── ByteTag.java │ ├── CompoundTag.java │ ├── DoubleTag.java │ ├── EndTag.java │ ├── FloatTag.java │ ├── IntArrayTag.java │ ├── IntTag.java │ ├── ListTag.java │ ├── LongArrayTag.java │ ├── LongTag.java │ ├── NBT.java │ ├── ShortTag.java │ ├── StringTag.java │ ├── Tag.java │ ├── TagType.java │ └── util │ ├── Chunk.java │ ├── ChunkFilter.java │ ├── ChunkPos.java │ └── RegionFile.java ├── neoforge ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ ├── java │ └── org │ │ └── popcraft │ │ └── chunky │ │ ├── ChunkyNeoForge.java │ │ ├── command │ │ └── suggestion │ │ │ ├── PatternSuggestionProvider.java │ │ │ ├── ShapeSuggestionProvider.java │ │ │ ├── SuggestionProviders.java │ │ │ └── TrimModeSuggestionProvider.java │ │ ├── listeners │ │ └── bossbar │ │ │ ├── BossBarTaskFinishListener.java │ │ │ └── BossBarTaskUpdateListener.java │ │ └── platform │ │ ├── NeoForgeBorder.java │ │ ├── NeoForgePlayer.java │ │ ├── NeoForgeSender.java │ │ ├── NeoForgeServer.java │ │ └── NeoForgeWorld.java │ └── resources │ ├── META-INF │ ├── accesstransformer.cfg │ └── neoforge.mods.toml │ ├── icon.png │ └── pack.mcmeta ├── paper ├── build.gradle.kts └── src │ └── main │ └── java │ └── org │ └── popcraft │ └── chunky │ └── platform │ └── Paper.java ├── settings.gradle.kts └── sponge ├── build.gradle.kts └── src └── main ├── java └── org │ └── popcraft │ └── chunky │ ├── ChunkySponge.java │ ├── command │ └── suggestion │ │ ├── PatternSuggestionProvider.java │ │ ├── ShapeSuggestionProvider.java │ │ └── SuggestionProviders.java │ └── platform │ ├── SpongeBorder.java │ ├── SpongeConfig.java │ ├── SpongePlayer.java │ ├── SpongeSender.java │ ├── SpongeServer.java │ └── SpongeWorld.java └── resources └── main.conf /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Chunky 2 | on: [ push, pull_request ] 3 | jobs: 4 | build: 5 | # Only run on PRs if the source branch is on someone else's repo 6 | if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }} 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout repository 10 | uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | - name: Gradle wrapper validation 14 | uses: gradle/wrapper-validation-action@v2 15 | - name: Set up JDK 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: 'temurin' 19 | java-version: '21' 20 | cache: 'gradle' 21 | - name: Build 22 | run: ./gradlew build 23 | - name: Upload Bukkit 24 | uses: actions/upload-artifact@v4 25 | with: 26 | name: Bukkit 27 | path: "bukkit/build/libs/Chunky-*.jar" 28 | - name: Upload Fabric 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: Fabric 32 | path: "fabric/build/libs/Chunky-*.jar" 33 | - name: Upload Forge 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: Forge 37 | path: "forge/build/libs/Chunky-*.jar" 38 | - name: Upload NeoForge 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: NeoForge 42 | path: "neoforge/build/libs/Chunky-*.jar" 43 | - name: Upload Sponge 44 | uses: actions/upload-artifact@v4 45 | with: 46 | name: Sponge 47 | path: "sponge/build/libs/Chunky-*.jar" 48 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Javadoc to Pages 2 | 3 | on: 4 | push: 5 | branches: ["javadoc"] 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | # Allow one concurrent deployment 17 | concurrency: 18 | group: "pages" 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | # Single deploy job since we're just deploying 23 | deploy: 24 | environment: 25 | name: github-pages 26 | url: ${{ steps.deployment.outputs.page_url }} 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v3 31 | - name: Setup Pages 32 | uses: actions/configure-pages@v2 33 | - name: Upload artifact 34 | uses: actions/upload-pages-artifact@v1 35 | with: 36 | path: './docs/' 37 | - name: Deploy to GitHub Pages 38 | id: deployment 39 | uses: actions/deploy-pages@v1 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chunky 2 | 3 | ![chunkylogo](https://user-images.githubusercontent.com/17698576/171119146-122497e4-7527-438b-8f8e-c1c04c2841b9.jpg) 4 | 5 | Minecraft chunk pre-generator, supporting a wide variety of server implementations including Spigot, Paper, Fabric, Forge, NeoForge, and Sponge. 6 | 7 | Pre-generate chunks, quickly, efficiently, and safely! 8 | 9 | ## Getting Started 10 | 11 | - [Installing](https://github.com/pop4959/Chunky/wiki/Installing) 12 | - [Pre-generating Chunks](https://github.com/pop4959/Chunky/wiki/Pregeneration) 13 | - [Command Reference](https://github.com/pop4959/Chunky/wiki/Commands) 14 | 15 | ## Support 16 | 17 | For questions about Chunky, first try the [Wiki](https://github.com/pop4959/Chunky/wiki) to see if your question is already answered there. 18 | 19 | If you can't find what you're looking for, visit us in the #chunky channel on our [Discord server](https://discord.gg/ZwVJukcNQG). 20 | 21 | ## Special Thanks 22 | 23 | [![yklogo](https://www.yourkit.com/images/yklogo.png)](https://www.yourkit.com/) 24 | 25 | YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/), 26 | and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/). We thank them for granting us open source project licenses so that we can continue to deliver the best performance possible. 27 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.io.ByteArrayOutputStream 2 | 3 | plugins { 4 | id("java-library") 5 | id("maven-publish") 6 | id("io.github.goooler.shadow") version "8.1.7" 7 | } 8 | 9 | subprojects { 10 | plugins.apply("java-library") 11 | plugins.apply("maven-publish") 12 | plugins.apply("io.github.goooler.shadow") 13 | 14 | group = "${project.property("group")}" 15 | version = "${project.property("version")}.${commitsSinceLastTag()}" 16 | 17 | repositories { 18 | mavenCentral() 19 | maven("https://oss.sonatype.org/content/repositories/snapshots/") 20 | } 21 | 22 | java { 23 | toolchain { 24 | languageVersion.set(JavaLanguageVersion.of(21)) 25 | } 26 | withSourcesJar() 27 | } 28 | 29 | tasks { 30 | withType { 31 | options.encoding = "UTF-8" 32 | options.release = 21 33 | options.compilerArgs.add("-Xlint:none") 34 | } 35 | jar { 36 | archiveClassifier.set("noshade") 37 | } 38 | shadowJar { 39 | archiveClassifier.set("") 40 | archiveFileName.set("${project.property("artifactName")}-${project.version}.jar") 41 | } 42 | build { 43 | dependsOn(shadowJar) 44 | } 45 | } 46 | 47 | publishing { 48 | repositories { 49 | if (project.hasProperty("mavenUsername") && project.hasProperty("mavenPassword")) { 50 | maven { 51 | credentials { 52 | username = "${project.property("mavenUsername")}" 53 | password = "${project.property("mavenPassword")}" 54 | } 55 | url = uri("https://repo.codemc.io/repository/maven-releases/") 56 | } 57 | } 58 | } 59 | publications { 60 | create("maven") { 61 | groupId = "${project.group}" 62 | artifactId = project.name 63 | version = "${project.version}" 64 | from(components["java"]) 65 | } 66 | } 67 | } 68 | } 69 | 70 | fun commitsSinceLastTag(): String { 71 | val tagDescription = ByteArrayOutputStream() 72 | exec { 73 | commandLine("git", "describe", "--tags") 74 | standardOutput = tagDescription 75 | } 76 | if (tagDescription.toString().indexOf('-') < 0) { 77 | return "0" 78 | } 79 | return tagDescription.toString().split('-')[1] 80 | } 81 | -------------------------------------------------------------------------------- /bukkit/build.gradle.kts: -------------------------------------------------------------------------------- 1 | repositories { 2 | maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") 3 | maven("https://repo.papermc.io/repository/maven-public/") 4 | maven("https://jitpack.io") 5 | } 6 | 7 | dependencies { 8 | compileOnly(group = "org.spigotmc", name = "spigot-api", version = "1.21.2-R0.1-SNAPSHOT") 9 | compileOnly(group = "com.github.Puremin0rez", name = "WorldBorder", version = "1.19") { 10 | isTransitive = false 11 | } 12 | implementation(group = "org.bstats", name = "bstats-bukkit", version = "3.0.2") 13 | implementation(project(":chunky-common")) 14 | implementation(project(":chunky-paper")) 15 | implementation(project(":chunky-folia")) 16 | } 17 | 18 | tasks { 19 | processResources { 20 | filesMatching("plugin.yml") { 21 | expand( 22 | "name" to project.property("artifactName"), 23 | "version" to project.version, 24 | "group" to project.group, 25 | "author" to project.property("author"), 26 | "description" to project.property("description"), 27 | ) 28 | } 29 | } 30 | shadowJar { 31 | minimize { 32 | exclude(project(":chunky-common")) 33 | exclude(project(":chunky-paper")) 34 | exclude(project(":chunky-folia")) 35 | } 36 | relocate("org.bstats", "${project.group}.${rootProject.name}.lib.bstats") 37 | manifest { 38 | attributes("paperweight-mappings-namespace" to "mojang") 39 | } 40 | archiveFileName.set("${project.property("artifactName")}-Bukkit-${project.version}.jar") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /bukkit/src/main/java/org/popcraft/chunky/integration/WorldBorderIntegration.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.integration; 2 | 3 | import com.wimbli.WorldBorder.BorderData; 4 | import com.wimbli.WorldBorder.Config; 5 | import org.popcraft.chunky.platform.Border; 6 | import org.popcraft.chunky.platform.util.Vector2; 7 | import org.popcraft.chunky.shape.ShapeType; 8 | 9 | public class WorldBorderIntegration implements BorderIntegration { 10 | @Override 11 | public boolean hasBorder(final String world) { 12 | return Config.Border(world) != null; 13 | } 14 | 15 | @Override 16 | public Border getBorder(final String world) { 17 | return new Border() { 18 | @Override 19 | public Vector2 getCenter() { 20 | final BorderData borderData = Config.Border(world); 21 | return Vector2.of(borderData.getX(), borderData.getZ()); 22 | } 23 | 24 | @Override 25 | public double getRadiusX() { 26 | return Config.Border(world).getRadiusX(); 27 | } 28 | 29 | @Override 30 | public double getRadiusZ() { 31 | return Config.Border(world).getRadiusZ(); 32 | } 33 | 34 | @Override 35 | public String getShape() { 36 | final BorderData borderData = Config.Border(world); 37 | final double radiusX = getRadiusX(); 38 | final double radiusZ = getRadiusZ(); 39 | final boolean round = borderData.getShape() == null ? Config.ShapeRound() : borderData.getShape(); 40 | final String shape; 41 | if (radiusX == radiusZ) { 42 | shape = round ? ShapeType.CIRCLE : ShapeType.SQUARE; 43 | } else { 44 | shape = round ? ShapeType.ELLIPSE : ShapeType.RECTANGLE; 45 | } 46 | return shape; 47 | } 48 | }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /bukkit/src/main/java/org/popcraft/chunky/platform/BukkitBorder.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.bukkit.WorldBorder; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | import org.popcraft.chunky.shape.ShapeType; 6 | 7 | public class BukkitBorder implements Border { 8 | final WorldBorder worldBorder; 9 | 10 | public BukkitBorder(final WorldBorder worldBorder) { 11 | this.worldBorder = worldBorder; 12 | } 13 | 14 | @Override 15 | public Vector2 getCenter() { 16 | return Vector2.of(worldBorder.getCenter().getX(), worldBorder.getCenter().getZ()); 17 | } 18 | 19 | @Override 20 | public double getRadiusX() { 21 | return worldBorder.getSize() / 2d; 22 | } 23 | 24 | @Override 25 | public double getRadiusZ() { 26 | return worldBorder.getSize() / 2d; 27 | } 28 | 29 | @Override 30 | public String getShape() { 31 | return ShapeType.SQUARE; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bukkit/src/main/java/org/popcraft/chunky/platform/BukkitConfig.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.bukkit.configuration.file.FileConfigurationOptions; 4 | import org.popcraft.chunky.ChunkyBukkit; 5 | import org.popcraft.chunky.util.Input; 6 | import org.popcraft.chunky.util.Translator; 7 | 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.nio.file.Path; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | public class BukkitConfig implements Config { 14 | private static final List HEADER = Arrays.asList("Chunky Configuration", "https://github.com/pop4959/Chunky/wiki/Configuration"); 15 | private final ChunkyBukkit plugin; 16 | 17 | public BukkitConfig(final ChunkyBukkit plugin) { 18 | this.plugin = plugin; 19 | final FileConfigurationOptions options = plugin.getConfig().options(); 20 | options.copyDefaults(true); 21 | try { 22 | FileConfigurationOptions.class.getMethod("header", String.class).invoke(options, String.join("\n", HEADER)); 23 | } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 24 | options.setHeader(HEADER); 25 | } 26 | plugin.saveConfig(); 27 | Translator.setLanguage(getLanguage()); 28 | } 29 | 30 | @Override 31 | public Path getDirectory() { 32 | return plugin.getDataFolder().toPath(); 33 | } 34 | 35 | @Override 36 | public int getVersion() { 37 | return plugin.getConfig().getInt("version", 0); 38 | } 39 | 40 | @Override 41 | public String getLanguage() { 42 | return Input.checkLanguage(plugin.getConfig().getString("language", "en")); 43 | } 44 | 45 | @Override 46 | public boolean getContinueOnRestart() { 47 | return plugin.getConfig().getBoolean("continue-on-restart", false); 48 | } 49 | 50 | @Override 51 | public boolean isForceLoadExistingChunks() { 52 | return plugin.getConfig().getBoolean("force-load-existing-chunks", false); 53 | } 54 | 55 | @Override 56 | public boolean isSilent() { 57 | return plugin.getConfig().getBoolean("silent", false); 58 | } 59 | 60 | @Override 61 | public void setSilent(final boolean silent) { 62 | plugin.getConfig().set("silent", silent); 63 | } 64 | 65 | @Override 66 | public int getUpdateInterval() { 67 | return plugin.getConfig().getInt("update-interval", 1); 68 | } 69 | 70 | @Override 71 | public void setUpdateInterval(final int updateInterval) { 72 | plugin.getConfig().set("update-interval", updateInterval); 73 | } 74 | 75 | @Override 76 | public void reload() { 77 | plugin.reloadConfig(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /bukkit/src/main/java/org/popcraft/chunky/platform/BukkitSender.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.md_5.bungee.api.ChatColor; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.command.CommandSender; 6 | import org.bukkit.entity.Player; 7 | import org.popcraft.chunky.platform.util.Location; 8 | 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | import static org.popcraft.chunky.util.Translator.translateKey; 13 | 14 | public class BukkitSender implements Sender { 15 | private static final Pattern RGB_PATTERN = Pattern.compile("&#[0-9a-fA-F]{6}"); 16 | private static final boolean RGB_COLORS_SUPPORTED; 17 | 18 | static { 19 | boolean rgbSupported; 20 | try { 21 | Class.forName("net.md_5.bungee.api.ChatColor"); 22 | ChatColor.class.getMethod("of", String.class); 23 | rgbSupported = true; 24 | } catch (ClassNotFoundException | NoSuchMethodException e) { 25 | rgbSupported = false; 26 | } 27 | RGB_COLORS_SUPPORTED = rgbSupported; 28 | } 29 | 30 | private final CommandSender sender; 31 | 32 | public BukkitSender(final CommandSender sender) { 33 | this.sender = sender; 34 | } 35 | 36 | @Override 37 | public boolean isPlayer() { 38 | return sender instanceof Player; 39 | } 40 | 41 | @Override 42 | public String getName() { 43 | return sender.getName(); 44 | } 45 | 46 | @Override 47 | public World getWorld() { 48 | return new BukkitWorld(Bukkit.getWorlds().get(0)); 49 | } 50 | 51 | @Override 52 | public Location getLocation() { 53 | return new Location(getWorld(), 0, 0, 0, 0, 0); 54 | } 55 | 56 | @Override 57 | public boolean hasPermission(final String permission) { 58 | return sender.hasPermission(permission); 59 | } 60 | 61 | @Override 62 | public void sendMessage(final String key, final boolean prefixed, final Object... args) { 63 | sender.sendMessage(formatColored(translateKey(key, prefixed, args))); 64 | } 65 | 66 | protected String formatColored(final String message) { 67 | String coloredMessage = message; 68 | if (RGB_COLORS_SUPPORTED) { 69 | Matcher rgbMatcher = RGB_PATTERN.matcher(message); 70 | while (rgbMatcher.find()) { 71 | final ChatColor rgbColor = ChatColor.of(rgbMatcher.group().substring(1)); 72 | final String messageStart = coloredMessage.substring(0, rgbMatcher.start()); 73 | final String messageEnd = coloredMessage.substring(rgbMatcher.end()); 74 | coloredMessage = messageStart + rgbColor + messageEnd; 75 | rgbMatcher = RGB_PATTERN.matcher(coloredMessage); 76 | } 77 | } 78 | return org.bukkit.ChatColor.translateAlternateColorCodes('&', coloredMessage); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /bukkit/src/main/java/org/popcraft/chunky/platform/BukkitServer.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.popcraft.chunky.ChunkyBukkit; 4 | import org.popcraft.chunky.integration.Integration; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collection; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Optional; 12 | 13 | public class BukkitServer implements Server { 14 | private final ChunkyBukkit plugin; 15 | private final Map integrations; 16 | 17 | public BukkitServer(final ChunkyBukkit plugin) { 18 | this.plugin = plugin; 19 | this.integrations = new HashMap<>(); 20 | } 21 | 22 | @Override 23 | public Map getIntegrations() { 24 | return integrations; 25 | } 26 | 27 | @Override 28 | public Optional getWorld(final String name) { 29 | final org.bukkit.World world = plugin.getServer().getWorld(name); 30 | return Optional.ofNullable(world == null ? null : new BukkitWorld(world)); 31 | } 32 | 33 | @Override 34 | public List getWorlds() { 35 | final List worlds = new ArrayList<>(); 36 | plugin.getServer().getWorlds().forEach(world -> worlds.add(new BukkitWorld(world))); 37 | return worlds; 38 | } 39 | 40 | @Override 41 | public int getMaxWorldSize() { 42 | return plugin.getServer().getMaxWorldSize(); 43 | } 44 | 45 | @Override 46 | public Sender getConsole() { 47 | return new BukkitSender(plugin.getServer().getConsoleSender()); 48 | } 49 | 50 | @Override 51 | public Collection getPlayers() { 52 | final Collection players = new ArrayList<>(); 53 | plugin.getServer().getOnlinePlayers().forEach(player -> players.add(new BukkitPlayer(player))); 54 | return players; 55 | } 56 | 57 | @Override 58 | public Optional getPlayer(final String name) { 59 | return Optional.ofNullable(plugin.getServer().getPlayer(name)).map(BukkitPlayer::new); 60 | } 61 | 62 | @Override 63 | public Config getConfig() { 64 | return plugin.getChunky().getConfig(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bukkit/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | language: en 3 | continue-on-restart: false 4 | force-load-existing-chunks: false 5 | silent: false 6 | update-interval: 1 7 | -------------------------------------------------------------------------------- /common/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compileOnly(group = "com.google.code.gson", name = "gson", version = "2.8.9") 3 | testImplementation(group = "junit", name = "junit", version = "4.13.2") 4 | testImplementation(group = "com.google.code.gson", name = "gson", version = "2.10.1") 5 | implementation(project(":chunky-nbt")) 6 | } 7 | 8 | tasks { 9 | processResources { 10 | filesMatching("version.properties") { 11 | expand( 12 | "version" to project.version 13 | ) 14 | } 15 | } 16 | javadoc { 17 | sourceSets { 18 | main { 19 | allJava 20 | } 21 | } 22 | setDestinationDir(rootProject.projectDir.resolve("docs/chunky/javadoc")) 23 | include("org/popcraft/chunky/api/**") 24 | exclude("org/popcraft/chunky/api/ChunkyAPIImpl.java") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/ChunkyProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky; 2 | 3 | public final class ChunkyProvider { 4 | private static Chunky instance; 5 | 6 | private ChunkyProvider() { 7 | throw new UnsupportedOperationException("This class cannot be instantiated."); 8 | } 9 | 10 | public static Chunky get() { 11 | if (instance == null) { 12 | throw new IllegalStateException("Chunky is not loaded."); 13 | } 14 | return instance; 15 | } 16 | 17 | static void register(final Chunky instance) { 18 | ChunkyProvider.instance = instance; 19 | } 20 | 21 | static void unregister() { 22 | ChunkyProvider.instance = null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/api/ChunkyAPI.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.api; 2 | 3 | import org.popcraft.chunky.api.event.task.GenerationCompleteEvent; 4 | import org.popcraft.chunky.api.event.task.GenerationProgressEvent; 5 | 6 | import java.util.function.Consumer; 7 | 8 | /** 9 | * The Chunky API 10 | */ 11 | @SuppressWarnings("unused") 12 | public interface ChunkyAPI { 13 | /** 14 | * Gets the current API version. 15 | * 16 | * @return The api version 17 | */ 18 | int version(); 19 | 20 | /** 21 | * Gets whether a generation task is currently running for a world. 22 | * 23 | * @param world The world identifier 24 | * @return If a task is running in that world 25 | */ 26 | boolean isRunning(final String world); 27 | 28 | /** 29 | * Starts a generation task with a given selection in a world. 30 | * 31 | * @param world The world identifier 32 | * @param shape The selection shape 33 | * @param centerX The center x coordinate 34 | * @param centerZ The center z coordinate 35 | * @param radiusX The primary radius (x-axis) 36 | * @param radiusZ The secondary radius (z-axis) (only used for certain shapes) 37 | * @param pattern The generation pattern 38 | * @return If the task was created and started successfully 39 | */ 40 | boolean startTask(final String world, final String shape, final double centerX, final double centerZ, final double radiusX, final double radiusZ, final String pattern); 41 | 42 | /** 43 | * Pauses a generation task in a world. 44 | * 45 | * @param world The world identifier 46 | * @return If the task was paused 47 | */ 48 | boolean pauseTask(final String world); 49 | 50 | /** 51 | * Continues a generation task in a world. 52 | * 53 | * @param world The world identifier 54 | * @return If the task was continued 55 | */ 56 | boolean continueTask(final String world); 57 | 58 | /** 59 | * Cancels a generation task in a world. 60 | * 61 | * @param world The world identifier 62 | * @return If the task was cancelled 63 | */ 64 | boolean cancelTask(final String world); 65 | 66 | /** 67 | * Register a listener for generation progress events. 68 | * 69 | * @param listener The listener 70 | */ 71 | void onGenerationProgress(final Consumer listener); 72 | 73 | /** 74 | * Register a listener for generation complete events. 75 | * 76 | * @param listener The listener 77 | */ 78 | void onGenerationComplete(final Consumer listener); 79 | } 80 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/api/event/task/GenerationCompleteEvent.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.api.event.task; 2 | 3 | import org.popcraft.chunky.event.Event; 4 | 5 | /** 6 | * Event which is fired when a generation task completes. 7 | * 8 | * @param world The world identifier associated with the completed task 9 | */ 10 | public record GenerationCompleteEvent(String world) implements Event { 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/api/event/task/GenerationProgressEvent.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.api.event.task; 2 | 3 | import org.popcraft.chunky.event.Event; 4 | 5 | /** 6 | * Event which is fired when a generation task calculates progress. 7 | * 8 | * @param world The world identifier 9 | * @param chunks The number of chunks generated 10 | * @param complete If the generation task completed 11 | * @param progress The percent progress 12 | * @param hours The number of hours elapsed for this task 13 | * @param minutes The number of minutes elapsed for this task 14 | * @param seconds The number of seconds elapsed for this task 15 | * @param rate The current average generation rate 16 | * @param x The current chunk's x coordinate 17 | * @param z The current chunk's z coordinate 18 | */ 19 | public record GenerationProgressEvent(String world, long chunks, boolean complete, float progress, long hours, 20 | long minutes, long seconds, double rate, long x, long z) implements Event { 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/CenterCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.platform.Sender; 5 | import org.popcraft.chunky.platform.util.Location; 6 | import org.popcraft.chunky.util.Formatting; 7 | import org.popcraft.chunky.util.Input; 8 | import org.popcraft.chunky.util.TranslationKey; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | public class CenterCommand implements ChunkyCommand { 14 | private final Chunky chunky; 15 | 16 | public CenterCommand(final Chunky chunky) { 17 | this.chunky = chunky; 18 | } 19 | 20 | @Override 21 | public void execute(final Sender sender, final CommandArguments arguments) { 22 | final Optional newX = arguments.next().flatMap(Input::tryDoubleSuffixed); 23 | final Optional newZ = arguments.next().flatMap(Input::tryDoubleSuffixed); 24 | final double centerX; 25 | final double centerZ; 26 | if (newX.isEmpty() && newZ.isEmpty()) { 27 | final Location coordinate = sender.getLocation(); 28 | centerX = coordinate.getX(); 29 | centerZ = coordinate.getZ(); 30 | } else if (newX.isPresent() && newZ.isPresent()) { 31 | centerX = newX.get(); 32 | centerZ = newZ.get(); 33 | } else { 34 | sender.sendMessage(TranslationKey.HELP_CENTER); 35 | return; 36 | } 37 | if (Input.isPastWorldLimit(centerX) || Input.isPastWorldLimit(centerZ)) { 38 | sender.sendMessage(TranslationKey.HELP_CENTER); 39 | return; 40 | } 41 | chunky.getSelection().center(centerX, centerZ); 42 | sender.sendMessagePrefixed(TranslationKey.FORMAT_CENTER, Formatting.number(centerX), Formatting.number(centerZ)); 43 | } 44 | 45 | @Override 46 | public List suggestions(final CommandArguments arguments) { 47 | return List.of(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/ChunkyCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.platform.Sender; 4 | 5 | import java.util.List; 6 | 7 | public interface ChunkyCommand { 8 | void execute(Sender sender, CommandArguments arguments); 9 | 10 | List suggestions(final CommandArguments arguments); 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/CommandArguments.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Optional; 7 | import java.util.Queue; 8 | 9 | public final class CommandArguments { 10 | private final int size; 11 | private final Queue args = new LinkedList<>(); 12 | 13 | private CommandArguments(final List arguments) { 14 | this.size = arguments.size(); 15 | this.args.addAll(arguments); 16 | } 17 | 18 | public static CommandArguments of(final List arguments) { 19 | return new CommandArguments(arguments); 20 | } 21 | 22 | public static CommandArguments of(final String... arguments) { 23 | return new CommandArguments(List.of(arguments)); 24 | } 25 | 26 | public static CommandArguments empty() { 27 | return new CommandArguments(List.of()); 28 | } 29 | 30 | public int size() { 31 | return size; 32 | } 33 | 34 | public Optional next() { 35 | return Optional.ofNullable(args.poll()); 36 | } 37 | 38 | public List remaining() { 39 | final List arguments = new ArrayList<>(args); 40 | args.clear(); 41 | return arguments; 42 | } 43 | 44 | public String joined() { 45 | return String.join(" ", remaining()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/CommandLiteral.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | public final class CommandLiteral { 4 | public static final String BORDER = "border"; 5 | public static final String CANCEL = "cancel"; 6 | public static final String CENTER = "center"; 7 | public static final String CENTER_X = "centerX"; 8 | public static final String CENTER_Z = "centerZ"; 9 | public static final String CHUNKY = "chunky"; 10 | public static final String CONFIRM = "confirm"; 11 | public static final String CONTINUE = "continue"; 12 | public static final String CORNERS = "corners"; 13 | public static final String HELP = "help"; 14 | public static final String INHABITED = "inhabited"; 15 | public static final String INTERVAL = "interval"; 16 | public static final String PAGE = "page"; 17 | public static final String PATTERN = "pattern"; 18 | public static final String PAUSE = "pause"; 19 | public static final String PROGRESS = "progress"; 20 | public static final String QUIET = "quiet"; 21 | public static final String RADIUS = "radius"; 22 | public static final String RADIUS_X = "radiusX"; 23 | public static final String RADIUS_Z = "radiusZ"; 24 | public static final String RELOAD = "reload"; 25 | public static final String SELECTION = "selection"; 26 | public static final String SHAPE = "shape"; 27 | public static final String SILENT = "silent"; 28 | public static final String SPAWN = "spawn"; 29 | public static final String START = "start"; 30 | public static final String TRIM = "trim"; 31 | public static final String TRIM_MODE = "trimMode"; 32 | public static final String WORLDBORDER = "worldborder"; 33 | public static final String WORLD = "world"; 34 | public static final String X = "x"; 35 | public static final String X1 = "x1"; 36 | public static final String X2 = "x2"; 37 | public static final String Z = "z"; 38 | public static final String Z1 = "z1"; 39 | public static final String Z2 = "z2"; 40 | public static final String ADD = "add"; 41 | public static final String BYPASS = "bypass"; 42 | public static final String LIST = "list"; 43 | public static final String LOAD = "load"; 44 | public static final String REMOVE = "remove"; 45 | public static final String WRAP = "wrap"; 46 | public static final String PLAYER = "player"; 47 | public static final String VALUE = "value"; 48 | public static final String TYPE = "type"; 49 | 50 | private CommandLiteral() { 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/ConfirmCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.platform.Sender; 5 | import org.popcraft.chunky.util.TranslationKey; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public class ConfirmCommand implements ChunkyCommand { 11 | private final Chunky chunky; 12 | 13 | public ConfirmCommand(final Chunky chunky) { 14 | this.chunky = chunky; 15 | } 16 | 17 | @Override 18 | public void execute(final Sender sender, final CommandArguments arguments) { 19 | final Optional pendingAction = chunky.getPendingAction(sender); 20 | if (pendingAction.isEmpty()) { 21 | sender.sendMessagePrefixed(TranslationKey.FORMAT_CONFIRM); 22 | return; 23 | } 24 | pendingAction.get().run(); 25 | } 26 | 27 | @Override 28 | public List suggestions(final CommandArguments arguments) { 29 | return List.of(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/ContinueCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.GenerationTask; 5 | import org.popcraft.chunky.platform.Sender; 6 | import org.popcraft.chunky.platform.World; 7 | import org.popcraft.chunky.util.Input; 8 | import org.popcraft.chunky.util.TranslationKey; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Optional; 14 | 15 | public class ContinueCommand implements ChunkyCommand { 16 | private final Chunky chunky; 17 | 18 | public ContinueCommand(final Chunky chunky) { 19 | this.chunky = chunky; 20 | } 21 | 22 | @Override 23 | public void execute(final Sender sender, final CommandArguments arguments) { 24 | final List loadTasks; 25 | if (arguments.size() > 0) { 26 | final Optional world = Input.tryWorld(chunky, arguments.joined()); 27 | if (world.isEmpty()) { 28 | sender.sendMessage(TranslationKey.HELP_CONTINUE); 29 | return; 30 | } 31 | loadTasks = chunky.getTaskLoader().loadTask(world.get()).map(List::of).orElse(List.of()); 32 | } else { 33 | loadTasks = chunky.getTaskLoader().loadTasks(); 34 | } 35 | if (loadTasks.stream().allMatch(GenerationTask::isCancelled)) { 36 | sender.sendMessagePrefixed(TranslationKey.FORMAT_CONTINUE_NO_TASKS); 37 | return; 38 | } 39 | final Map generationTasks = chunky.getGenerationTasks(); 40 | loadTasks.stream().filter(task -> !task.isCancelled()).forEach(generationTask -> { 41 | final World world = generationTask.getSelection().world(); 42 | if (!generationTasks.containsKey(world.getName())) { 43 | generationTasks.put(world.getName(), generationTask); 44 | chunky.getScheduler().runTask(generationTask); 45 | sender.sendMessagePrefixed(TranslationKey.FORMAT_CONTINUE, world.getName()); 46 | } else { 47 | sender.sendMessagePrefixed(TranslationKey.FORMAT_STARTED_ALREADY, world.getName()); 48 | } 49 | }); 50 | } 51 | 52 | @Override 53 | public List suggestions(final CommandArguments arguments) { 54 | if (arguments.size() == 1) { 55 | final List suggestions = new ArrayList<>(); 56 | chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName())); 57 | return suggestions; 58 | } 59 | return List.of(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/CornersCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.platform.Sender; 5 | import org.popcraft.chunky.shape.ShapeType; 6 | import org.popcraft.chunky.util.Formatting; 7 | import org.popcraft.chunky.util.Input; 8 | import org.popcraft.chunky.util.TranslationKey; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | public class CornersCommand implements ChunkyCommand { 14 | private final Chunky chunky; 15 | 16 | public CornersCommand(final Chunky chunky) { 17 | this.chunky = chunky; 18 | } 19 | 20 | @Override 21 | public void execute(final Sender sender, final CommandArguments arguments) { 22 | final Optional x1 = arguments.next().flatMap(Input::tryDoubleSuffixed); 23 | final Optional z1 = arguments.next().flatMap(Input::tryDoubleSuffixed); 24 | final Optional x2 = arguments.next().flatMap(Input::tryDoubleSuffixed); 25 | final Optional z2 = arguments.next().flatMap(Input::tryDoubleSuffixed); 26 | if (x1.isEmpty() || z1.isEmpty() || x2.isEmpty() || z2.isEmpty()) { 27 | sender.sendMessage(TranslationKey.HELP_CORNERS); 28 | return; 29 | } 30 | if (Input.isPastWorldLimit(x1.get()) || Input.isPastWorldLimit(z1.get()) || Input.isPastWorldLimit(x2.get()) || Input.isPastWorldLimit(z2.get())) { 31 | sender.sendMessage(TranslationKey.HELP_CORNERS); 32 | return; 33 | } 34 | final double centerX = (x1.get() + x2.get()) / 2d; 35 | final double centerZ = (z1.get() + z2.get()) / 2d; 36 | final double radiusX = Math.abs(x1.get() - x2.get()) / 2d; 37 | final double radiusZ = Math.abs(z1.get() - z2.get()) / 2d; 38 | chunky.getSelection().center(centerX, centerZ).radiusX(radiusX).radiusZ(radiusZ); 39 | sender.sendMessagePrefixed(TranslationKey.FORMAT_CENTER, Formatting.number(centerX), Formatting.number(centerZ)); 40 | final String shape; 41 | if (radiusX == radiusZ) { 42 | sender.sendMessagePrefixed(TranslationKey.FORMAT_RADIUS, Formatting.number(radiusX)); 43 | shape = ShapeType.SQUARE; 44 | } else { 45 | sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(radiusX), Formatting.number(radiusZ)); 46 | shape = ShapeType.RECTANGLE; 47 | } 48 | chunky.getSelection().shape(shape); 49 | sender.sendMessagePrefixed(TranslationKey.FORMAT_SHAPE, shape); 50 | } 51 | 52 | @Override 53 | public List suggestions(final CommandArguments arguments) { 54 | return List.of(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/PatternCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.iterator.PatternType; 5 | import org.popcraft.chunky.platform.Sender; 6 | import org.popcraft.chunky.util.Input; 7 | import org.popcraft.chunky.util.Parameter; 8 | import org.popcraft.chunky.util.TranslationKey; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | import static org.popcraft.chunky.util.Translator.translate; 14 | 15 | public class PatternCommand implements ChunkyCommand { 16 | private final Chunky chunky; 17 | 18 | public PatternCommand(final Chunky chunky) { 19 | this.chunky = chunky; 20 | } 21 | 22 | @Override 23 | public void execute(final Sender sender, final CommandArguments arguments) { 24 | final Optional optionalType = arguments.next().flatMap(Input::tryPattern); 25 | if (optionalType.isEmpty()) { 26 | sender.sendMessage(TranslationKey.HELP_PATTERN); 27 | return; 28 | } 29 | final String type = optionalType.get(); 30 | final Optional value = arguments.next(); 31 | if (PatternType.CSV.equals(type) && value.isEmpty()) { 32 | sender.sendMessage(TranslationKey.HELP_PATTERN); 33 | return; 34 | } 35 | final Parameter pattern = Parameter.of(type, value.orElse(null)); 36 | chunky.getSelection().pattern(pattern); 37 | sender.sendMessagePrefixed(TranslationKey.FORMAT_PATTERN, translate("pattern_" + pattern.getType())); 38 | } 39 | 40 | @Override 41 | public List suggestions(final CommandArguments arguments) { 42 | if (arguments.size() == 1) { 43 | return PatternType.ALL; 44 | } 45 | return List.of(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/PauseCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.GenerationTask; 5 | import org.popcraft.chunky.platform.Sender; 6 | import org.popcraft.chunky.platform.World; 7 | import org.popcraft.chunky.util.Input; 8 | import org.popcraft.chunky.util.TranslationKey; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Optional; 14 | 15 | public class PauseCommand implements ChunkyCommand { 16 | private final Chunky chunky; 17 | 18 | public PauseCommand(final Chunky chunky) { 19 | this.chunky = chunky; 20 | } 21 | 22 | @Override 23 | public void execute(final Sender sender, final CommandArguments arguments) { 24 | final Map generationTasks = chunky.getGenerationTasks(); 25 | if (generationTasks.isEmpty()) { 26 | sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE_NO_TASKS); 27 | return; 28 | } 29 | if (arguments.size() > 0) { 30 | final Optional world = Input.tryWorld(chunky, arguments.joined()); 31 | if (world.isEmpty() || !generationTasks.containsKey(world.get().getName())) { 32 | sender.sendMessage(TranslationKey.HELP_PAUSE); 33 | } else { 34 | generationTasks.get(world.get().getName()).stop(false); 35 | sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE, world.get().getName()); 36 | } 37 | return; 38 | } 39 | for (GenerationTask generationTask : chunky.getGenerationTasks().values()) { 40 | generationTask.stop(false); 41 | sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE, generationTask.getSelection().world().getName()); 42 | } 43 | } 44 | 45 | @Override 46 | public List suggestions(final CommandArguments arguments) { 47 | if (arguments.size() == 1) { 48 | final List suggestions = new ArrayList<>(); 49 | chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName())); 50 | return suggestions; 51 | } 52 | return List.of(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/ProgressCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.GenerationTask; 5 | import org.popcraft.chunky.platform.Sender; 6 | import org.popcraft.chunky.platform.World; 7 | import org.popcraft.chunky.util.TranslationKey; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class ProgressCommand implements ChunkyCommand { 13 | private final Chunky chunky; 14 | 15 | public ProgressCommand(final Chunky chunky) { 16 | this.chunky = chunky; 17 | } 18 | 19 | @Override 20 | public void execute(final Sender sender, final CommandArguments arguments) { 21 | final Map generationTasks = chunky.getGenerationTasks(); 22 | if (generationTasks.isEmpty()) { 23 | sender.sendMessagePrefixed(TranslationKey.FORMAT_PROGRESS_NO_TASKS); 24 | return; 25 | } 26 | for (World world : chunky.getServer().getWorlds()) { 27 | if (generationTasks.containsKey(world.getName())) { 28 | generationTasks.get(world.getName()).getProgress().sendUpdate(sender); 29 | } 30 | } 31 | } 32 | 33 | @Override 34 | public List suggestions(final CommandArguments arguments) { 35 | return List.of(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/QuietCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.platform.Sender; 5 | import org.popcraft.chunky.util.Input; 6 | import org.popcraft.chunky.util.TranslationKey; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | public class QuietCommand implements ChunkyCommand { 12 | private final Chunky chunky; 13 | 14 | public QuietCommand(final Chunky chunky) { 15 | this.chunky = chunky; 16 | } 17 | 18 | @Override 19 | public void execute(final Sender sender, final CommandArguments arguments) { 20 | final Optional newQuiet = arguments.next().flatMap(Input::tryInteger); 21 | if (newQuiet.isEmpty()) { 22 | sender.sendMessage(TranslationKey.HELP_QUIET); 23 | return; 24 | } 25 | final int quietInterval = Math.max(0, newQuiet.get()); 26 | chunky.getConfig().setUpdateInterval(quietInterval); 27 | sender.sendMessagePrefixed(TranslationKey.FORMAT_QUIET, quietInterval); 28 | } 29 | 30 | @Override 31 | public List suggestions(final CommandArguments arguments) { 32 | return List.of(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/RadiusCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.Selection; 5 | import org.popcraft.chunky.platform.Sender; 6 | import org.popcraft.chunky.util.Formatting; 7 | import org.popcraft.chunky.util.Input; 8 | import org.popcraft.chunky.util.TranslationKey; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | public class RadiusCommand implements ChunkyCommand { 14 | private final Chunky chunky; 15 | 16 | public RadiusCommand(final Chunky chunky) { 17 | this.chunky = chunky; 18 | } 19 | 20 | @Override 21 | public void execute(final Sender sender, final CommandArguments arguments) { 22 | final Optional newX = arguments.next(); 23 | final Optional signX = newX.flatMap(Input::trySign); 24 | final Optional newRadiusX = newX.map(x -> signX.isPresent() ? x.substring(1) : x).flatMap(Input::tryDoubleSuffixed); 25 | if (newRadiusX.isEmpty() || newRadiusX.get() < 0 || Input.isPastWorldLimit(newRadiusX.get())) { 26 | sender.sendMessage(TranslationKey.HELP_RADIUS); 27 | return; 28 | } 29 | final Selection current = chunky.getSelection().build(); 30 | final double radiusX = signX.map(sign -> current.radiusX() + sign * newRadiusX.get()).orElseGet(newRadiusX::get); 31 | if (radiusX < 0 || Input.isPastWorldLimit(radiusX)) { 32 | sender.sendMessage(TranslationKey.HELP_RADIUS); 33 | return; 34 | } 35 | final Optional newZ = arguments.next(); 36 | if (newZ.isPresent()) { 37 | final Optional signZ = newZ.flatMap(Input::trySign); 38 | final Optional newRadiusZ = newZ.map(z -> signZ.isPresent() ? z.substring(1) : z).flatMap(Input::tryDoubleSuffixed); 39 | if (newRadiusZ.isEmpty() || newRadiusZ.get() < 0 || Input.isPastWorldLimit(newRadiusZ.get())) { 40 | sender.sendMessage(TranslationKey.HELP_RADIUS); 41 | return; 42 | } 43 | final double radiusZ = signZ.map(sign -> current.radiusZ() + sign * newRadiusZ.get()).orElseGet(newRadiusZ::get); 44 | if (radiusZ < 0 || Input.isPastWorldLimit(radiusZ)) { 45 | sender.sendMessage(TranslationKey.HELP_RADIUS); 46 | return; 47 | } 48 | chunky.getSelection().radiusX(radiusX).radiusZ(radiusZ); 49 | sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(radiusX), Formatting.number(radiusZ)); 50 | } else { 51 | chunky.getSelection().radius(radiusX); 52 | sender.sendMessagePrefixed(TranslationKey.FORMAT_RADIUS, Formatting.number(radiusX)); 53 | } 54 | } 55 | 56 | @Override 57 | public List suggestions(final CommandArguments arguments) { 58 | return List.of(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/ReloadCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.event.command.ReloadCommandEvent; 5 | import org.popcraft.chunky.platform.Config; 6 | import org.popcraft.chunky.platform.Sender; 7 | import org.popcraft.chunky.util.TranslationKey; 8 | 9 | import java.util.List; 10 | 11 | public class ReloadCommand implements ChunkyCommand { 12 | private final Chunky chunky; 13 | 14 | public ReloadCommand(final Chunky chunky) { 15 | this.chunky = chunky; 16 | } 17 | 18 | @Override 19 | public void execute(final Sender sender, final CommandArguments arguments) { 20 | final String type = arguments.next().orElse(null); 21 | if ("tasks".equals(type)) { 22 | if (!chunky.getGenerationTasks().isEmpty()) { 23 | sender.sendMessagePrefixed(TranslationKey.FORMAT_RELOAD_TASKS_RUNNING); 24 | return; 25 | } 26 | chunky.getTaskLoader().reload(); 27 | } else { 28 | final Config config = chunky.getServer().getConfig(); 29 | config.reload(); 30 | chunky.setLanguage(config.getLanguage()); 31 | chunky.getEventBus().call(new ReloadCommandEvent()); 32 | } 33 | sender.sendMessagePrefixed(TranslationKey.FORMAT_RELOAD); 34 | } 35 | 36 | @Override 37 | public List suggestions(final CommandArguments arguments) { 38 | if (arguments.size() == 1) { 39 | return List.of("config", "tasks"); 40 | } 41 | return List.of(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/SelectionCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.Selection; 5 | import org.popcraft.chunky.platform.Sender; 6 | import org.popcraft.chunky.util.Formatting; 7 | import org.popcraft.chunky.util.TranslationKey; 8 | 9 | import java.util.List; 10 | 11 | import static org.popcraft.chunky.util.Translator.translate; 12 | 13 | public class SelectionCommand implements ChunkyCommand { 14 | private final Chunky chunky; 15 | 16 | public SelectionCommand(final Chunky chunky) { 17 | this.chunky = chunky; 18 | } 19 | 20 | @Override 21 | public void execute(final Sender sender, final CommandArguments arguments) { 22 | final Selection current = chunky.getSelection().build(); 23 | sender.sendMessagePrefixed(TranslationKey.FORMAT_SELECTION); 24 | sender.sendMessage(TranslationKey.FORMAT_SELECTION_WORLD, current.world().getName()); 25 | sender.sendMessage(TranslationKey.FORMAT_SELECTION_SHAPE, translate("shape_" + current.shape())); 26 | sender.sendMessage(TranslationKey.FORMAT_SELECTION_CENTER, Formatting.number(current.centerX()), Formatting.number(current.centerZ())); 27 | final double radiusX = current.radiusX(); 28 | final double radiusZ = current.radiusZ(); 29 | if (radiusX == radiusZ) { 30 | sender.sendMessage(TranslationKey.FORMAT_SELECTION_RADIUS, Formatting.number(radiusX)); 31 | } else { 32 | sender.sendMessage(TranslationKey.FORMAT_SELECTION_RADII, Formatting.number(radiusX), Formatting.number(radiusZ)); 33 | } 34 | } 35 | 36 | @Override 37 | public List suggestions(final CommandArguments arguments) { 38 | return List.of(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/ShapeCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.platform.Sender; 5 | import org.popcraft.chunky.shape.ShapeType; 6 | import org.popcraft.chunky.util.Input; 7 | import org.popcraft.chunky.util.TranslationKey; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | import static org.popcraft.chunky.util.Translator.translate; 13 | 14 | public class ShapeCommand implements ChunkyCommand { 15 | private final Chunky chunky; 16 | 17 | public ShapeCommand(final Chunky chunky) { 18 | this.chunky = chunky; 19 | } 20 | 21 | @Override 22 | public void execute(final Sender sender, final CommandArguments arguments) { 23 | final Optional inputShape = arguments.next().flatMap(Input::tryShape); 24 | if (inputShape.isEmpty()) { 25 | sender.sendMessage(TranslationKey.HELP_SHAPE); 26 | return; 27 | } 28 | final String shape = inputShape.get(); 29 | chunky.getSelection().shape(shape); 30 | sender.sendMessagePrefixed(TranslationKey.FORMAT_SHAPE, translate("shape_" + shape)); 31 | } 32 | 33 | @Override 34 | public List suggestions(final CommandArguments arguments) { 35 | if (arguments.size() == 1) { 36 | return ShapeType.all(); 37 | } 38 | return List.of(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/SilentCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.platform.Sender; 5 | import org.popcraft.chunky.util.TranslationKey; 6 | 7 | import java.util.List; 8 | 9 | import static org.popcraft.chunky.util.Translator.translate; 10 | 11 | public class SilentCommand implements ChunkyCommand { 12 | private final Chunky chunky; 13 | 14 | public SilentCommand(final Chunky chunky) { 15 | this.chunky = chunky; 16 | } 17 | 18 | @Override 19 | public void execute(final Sender sender, final CommandArguments arguments) { 20 | chunky.getConfig().setSilent(!chunky.getConfig().isSilent()); 21 | final String status = translate(chunky.getConfig().isSilent() ? TranslationKey.ENABLED : TranslationKey.DISABLED); 22 | sender.sendMessagePrefixed(TranslationKey.FORMAT_SILENT, status); 23 | } 24 | 25 | @Override 26 | public List suggestions(final CommandArguments arguments) { 27 | return List.of(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/SpawnCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.Selection; 5 | import org.popcraft.chunky.platform.Sender; 6 | import org.popcraft.chunky.util.Formatting; 7 | import org.popcraft.chunky.util.TranslationKey; 8 | 9 | import java.util.List; 10 | 11 | public class SpawnCommand implements ChunkyCommand { 12 | private final Chunky chunky; 13 | 14 | public SpawnCommand(final Chunky chunky) { 15 | this.chunky = chunky; 16 | } 17 | 18 | @Override 19 | public void execute(final Sender sender, final CommandArguments arguments) { 20 | chunky.getSelection().spawn(); 21 | final Selection current = chunky.getSelection().build(); 22 | sender.sendMessagePrefixed(TranslationKey.FORMAT_CENTER, Formatting.number(current.centerX()), Formatting.number(current.centerZ())); 23 | } 24 | 25 | @Override 26 | public List suggestions(final CommandArguments arguments) { 27 | return List.of(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/command/WorldCommand.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command; 2 | 3 | import org.popcraft.chunky.Chunky; 4 | import org.popcraft.chunky.platform.Player; 5 | import org.popcraft.chunky.platform.Sender; 6 | import org.popcraft.chunky.platform.World; 7 | import org.popcraft.chunky.util.Input; 8 | import org.popcraft.chunky.util.TranslationKey; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class WorldCommand implements ChunkyCommand { 14 | private final Chunky chunky; 15 | 16 | public WorldCommand(final Chunky chunky) { 17 | this.chunky = chunky; 18 | } 19 | 20 | @Override 21 | public void execute(final Sender sender, final CommandArguments arguments) { 22 | final World world; 23 | if (arguments.size() == 0 && sender instanceof final Player player) { 24 | world = player.getWorld(); 25 | } else { 26 | world = Input.tryWorld(chunky, arguments.joined()).orElse(null); 27 | } 28 | if (world == null) { 29 | sender.sendMessage(TranslationKey.HELP_WORLD); 30 | return; 31 | } 32 | chunky.getSelection().world(world); 33 | sender.sendMessagePrefixed(TranslationKey.FORMAT_WORLD, world.getName()); 34 | } 35 | 36 | @Override 37 | public List suggestions(final CommandArguments arguments) { 38 | if (arguments.size() == 1) { 39 | final List suggestions = new ArrayList<>(); 40 | chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName())); 41 | return suggestions; 42 | } 43 | return List.of(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/event/Cancellable.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.event; 2 | 3 | public abstract class Cancellable implements Event { 4 | private boolean cancelled; 5 | 6 | public boolean isCancelled() { 7 | return cancelled; 8 | } 9 | 10 | public void setCancelled(final boolean cancelled) { 11 | this.cancelled = cancelled; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/event/Event.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.event; 2 | 3 | public interface Event { 4 | } 5 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/event/EventBus.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.event; 2 | 3 | import java.lang.invoke.MethodHandle; 4 | import java.lang.invoke.MethodHandles; 5 | import java.lang.invoke.MethodType; 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | import java.util.Map; 9 | import java.util.Set; 10 | import java.util.function.Consumer; 11 | 12 | public final class EventBus { 13 | private static final MethodHandle accept; 14 | 15 | static { 16 | MethodHandle acceptMethodHandle = null; 17 | try { 18 | acceptMethodHandle = MethodHandles.publicLookup().findVirtual(Consumer.class, "accept", MethodType.methodType(void.class, Object.class)); 19 | } catch (NoSuchMethodException | IllegalAccessException e) { 20 | e.printStackTrace(); 21 | } 22 | accept = acceptMethodHandle; 23 | } 24 | 25 | private final Map, Set>> subscribers = new HashMap<>(); 26 | 27 | public void subscribe(final Class eventClass, final Consumer subscriber) { 28 | subscribers.computeIfAbsent(eventClass, x -> new HashSet<>()); 29 | subscribers.get(eventClass).add(subscriber); 30 | } 31 | 32 | public void unsubscribe(final Class eventClass, final Consumer subscriber) { 33 | subscribers.computeIfAbsent(eventClass, x -> new HashSet<>()); 34 | subscribers.get(eventClass).remove(subscriber); 35 | } 36 | 37 | public void unsubscribeAll() { 38 | subscribers.clear(); 39 | } 40 | 41 | public void unsubscribeAll(final Class eventClass) { 42 | subscribers.remove(eventClass); 43 | } 44 | 45 | public void call(final Object event) { 46 | final Class eventClass = event.getClass(); 47 | if (accept == null || !subscribers.containsKey(eventClass)) { 48 | return; 49 | } 50 | subscribers.get(eventClass).forEach(subscriber -> { 51 | try { 52 | accept.invoke(subscriber, event); 53 | } catch (Throwable e) { 54 | e.printStackTrace(); 55 | } 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/event/command/ReloadCommandEvent.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.event.command; 2 | 3 | import org.popcraft.chunky.event.Event; 4 | 5 | public record ReloadCommandEvent() implements Event { 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/event/task/GenerationTaskFinishEvent.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.event.task; 2 | 3 | import org.popcraft.chunky.GenerationTask; 4 | import org.popcraft.chunky.event.Event; 5 | 6 | public record GenerationTaskFinishEvent(GenerationTask generationTask) implements Event { 7 | } 8 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/event/task/GenerationTaskUpdateEvent.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.event.task; 2 | 3 | import org.popcraft.chunky.GenerationTask; 4 | import org.popcraft.chunky.event.Event; 5 | 6 | public record GenerationTaskUpdateEvent(GenerationTask generationTask) implements Event { 7 | } 8 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/integration/BorderIntegration.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.integration; 2 | 3 | import org.popcraft.chunky.platform.Border; 4 | 5 | public interface BorderIntegration extends Integration { 6 | boolean hasBorder(String world); 7 | 8 | Border getBorder(String world); 9 | } 10 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/integration/Integration.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.integration; 2 | 3 | public interface Integration { 4 | } 5 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/iterator/ChunkIterator.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.iterator; 2 | 3 | import org.popcraft.chunky.util.ChunkCoordinate; 4 | 5 | import java.util.Iterator; 6 | 7 | public interface ChunkIterator extends Iterator { 8 | long total(); 9 | 10 | String name(); 11 | 12 | default boolean process() { 13 | return true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/iterator/ChunkIteratorFactory.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.iterator; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.shape.ShapeType; 5 | 6 | public final class ChunkIteratorFactory { 7 | private ChunkIteratorFactory() { 8 | } 9 | 10 | public static ChunkIterator getChunkIterator(final Selection selection, final long count) { 11 | if (selection.pattern().getType().equals(PatternType.WORLD)) { 12 | return new WorldChunkIterator(selection); 13 | } 14 | final String shape = selection.shape(); 15 | if (ShapeType.RECTANGLE.equals(shape) || ShapeType.ELLIPSE.equals(shape) || ShapeType.OVAL.equals(shape)) { 16 | return new Loop2ChunkIterator(selection, count); 17 | } 18 | return switch (selection.pattern().getType()) { 19 | case PatternType.LOOP -> new Loop2ChunkIterator(selection, count); 20 | case PatternType.SPIRAL -> new SpiralChunkIterator(selection, count); 21 | case PatternType.CSV -> new CsvChunkIterator(selection, count); 22 | case PatternType.CONCENTRIC -> new ConcentricChunkIterator(selection, count); 23 | default -> new RegionChunkIterator(selection, count); 24 | }; 25 | } 26 | 27 | public static ChunkIterator getChunkIterator(final Selection selection) { 28 | return getChunkIterator(selection, 0); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/iterator/CsvChunkIterator.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.iterator; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.util.ChunkCoordinate; 5 | import org.popcraft.chunky.util.Input; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.LinkedList; 11 | import java.util.NoSuchElementException; 12 | import java.util.Optional; 13 | import java.util.Queue; 14 | import java.util.concurrent.atomic.AtomicLong; 15 | import java.util.stream.Stream; 16 | 17 | public class CsvChunkIterator implements ChunkIterator { 18 | private final Queue chunks; 19 | private final long total; 20 | private final String name; 21 | 22 | public CsvChunkIterator(final Selection selection, final long count) { 23 | this(selection); 24 | for (int i = 0; i < count && hasNext(); ++i) { 25 | chunks.poll(); 26 | } 27 | } 28 | 29 | public CsvChunkIterator(final Selection selection) { 30 | final Path filePath = selection.pattern().getValue() 31 | .map(value -> selection.chunky().getConfig().getDirectory().resolve(String.format("%s.csv", value))) 32 | .orElse(null); 33 | this.chunks = new LinkedList<>(); 34 | final AtomicLong valid = new AtomicLong(); 35 | if (filePath != null) { 36 | try (final Stream lines = Files.lines(filePath)) { 37 | lines.forEach(line -> { 38 | final String[] split = line.split(","); 39 | if (split.length > 1) { 40 | final Optional x = Input.tryInteger(split[0]); 41 | final Optional z = Input.tryInteger(split[1]); 42 | if (x.isPresent() && z.isPresent()) { 43 | chunks.add(new ChunkCoordinate(x.get(), z.get())); 44 | valid.incrementAndGet(); 45 | } 46 | } 47 | }); 48 | } catch (final IOException e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | this.total = valid.get(); 53 | this.name = selection.pattern().toString(); 54 | } 55 | 56 | @Override 57 | public boolean hasNext() { 58 | return !chunks.isEmpty(); 59 | } 60 | 61 | @Override 62 | public ChunkCoordinate next() { 63 | if (chunks.isEmpty()) { 64 | throw new NoSuchElementException(); 65 | } 66 | return chunks.poll(); 67 | } 68 | 69 | @Override 70 | public long total() { 71 | return total; 72 | } 73 | 74 | @Override 75 | public String name() { 76 | return name; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/iterator/Loop2ChunkIterator.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.iterator; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.util.ChunkCoordinate; 5 | 6 | import java.util.NoSuchElementException; 7 | 8 | public class Loop2ChunkIterator implements ChunkIterator { 9 | private final int x1, x2, z1, z2; 10 | private final long diameterChunksZ; 11 | private final long total; 12 | private int x, z; 13 | private boolean hasNext = true; 14 | 15 | public Loop2ChunkIterator(final Selection selection, final long count) { 16 | this(selection); 17 | if (count <= 0) { 18 | return; 19 | } 20 | this.x = x1 + (int) (count / diameterChunksZ); 21 | this.z = z1 + (int) (count % diameterChunksZ); 22 | if (x > x2) { 23 | hasNext = false; 24 | } 25 | } 26 | 27 | public Loop2ChunkIterator(final Selection selection) { 28 | final int radiusChunksX = selection.radiusChunksX(); 29 | final int radiusChunksZ = selection.radiusChunksZ(); 30 | final int centerChunkX = selection.centerChunkX(); 31 | final int centerChunkZ = selection.centerChunkZ(); 32 | this.x1 = centerChunkX - radiusChunksX; 33 | this.x2 = centerChunkX + radiusChunksX; 34 | this.z1 = centerChunkZ - radiusChunksZ; 35 | this.z2 = centerChunkZ + radiusChunksZ; 36 | this.x = x1; 37 | this.z = z1; 38 | final int diameterChunksX = selection.diameterChunksX(); 39 | this.diameterChunksZ = selection.diameterChunksZ(); 40 | this.total = diameterChunksX * diameterChunksZ; 41 | } 42 | 43 | @Override 44 | public boolean hasNext() { 45 | return hasNext; 46 | } 47 | 48 | @Override 49 | public ChunkCoordinate next() { 50 | if (!hasNext) { 51 | throw new NoSuchElementException(); 52 | } 53 | final ChunkCoordinate chunkCoord = new ChunkCoordinate(x, z); 54 | if (++z > z2) { 55 | z = z1; 56 | if (++x > x2) { 57 | hasNext = false; 58 | } 59 | } 60 | return chunkCoord; 61 | } 62 | 63 | @Override 64 | public long total() { 65 | return total; 66 | } 67 | 68 | @Override 69 | public String name() { 70 | return PatternType.LOOP; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/iterator/PatternType.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.iterator; 2 | 3 | import java.util.List; 4 | 5 | public final class PatternType { 6 | public static final String CONCENTRIC = "concentric"; 7 | public static final String LOOP = "loop"; 8 | public static final String SPIRAL = "spiral"; 9 | public static final String CSV = "csv"; 10 | public static final String REGION = "region"; 11 | public static final String WORLD = "world"; 12 | 13 | public static final List ALL = List.of(CONCENTRIC, LOOP, SPIRAL, CSV, REGION, WORLD); 14 | 15 | private PatternType() { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/Border.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.popcraft.chunky.platform.util.Vector2; 4 | 5 | public interface Border { 6 | Vector2 getCenter(); 7 | 8 | double getRadiusX(); 9 | 10 | double getRadiusZ(); 11 | 12 | String getShape(); 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/Config.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import java.nio.file.Path; 4 | 5 | public interface Config { 6 | Path getDirectory(); 7 | 8 | int getVersion(); 9 | 10 | String getLanguage(); 11 | 12 | boolean getContinueOnRestart(); 13 | 14 | boolean isForceLoadExistingChunks(); 15 | 16 | boolean isSilent(); 17 | 18 | void setSilent(boolean silent); 19 | 20 | int getUpdateInterval(); 21 | 22 | void setUpdateInterval(int updateInterval); 23 | 24 | void reload(); 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/Player.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.popcraft.chunky.platform.util.Location; 4 | 5 | import java.util.UUID; 6 | 7 | public interface Player extends Sender { 8 | UUID getUUID(); 9 | 10 | void teleport(Location location); 11 | 12 | void sendActionBar(String key); 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/Sender.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.popcraft.chunky.platform.util.Location; 4 | 5 | public interface Sender { 6 | boolean isPlayer(); 7 | 8 | String getName(); 9 | 10 | World getWorld(); 11 | 12 | Location getLocation(); 13 | 14 | boolean hasPermission(String permission); 15 | 16 | void sendMessage(String key, boolean prefixed, Object... args); 17 | 18 | default void sendMessage(String key, Object... args) { 19 | sendMessage(key, false, args); 20 | } 21 | 22 | default void sendMessagePrefixed(String key, Object... args) { 23 | sendMessage(key, true, args); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/Server.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.popcraft.chunky.integration.Integration; 4 | 5 | import java.util.Collection; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Optional; 9 | 10 | public interface Server { 11 | Map getIntegrations(); 12 | 13 | Optional getWorld(String name); 14 | 15 | List getWorlds(); 16 | 17 | int getMaxWorldSize(); 18 | 19 | Sender getConsole(); 20 | 21 | Collection getPlayers(); 22 | 23 | Optional getPlayer(String name); 24 | 25 | Config getConfig(); 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/World.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.popcraft.chunky.platform.util.Location; 4 | 5 | import java.nio.file.Path; 6 | import java.util.Optional; 7 | import java.util.UUID; 8 | import java.util.concurrent.CompletableFuture; 9 | 10 | public interface World { 11 | String getName(); 12 | 13 | String getKey(); 14 | 15 | CompletableFuture isChunkGenerated(int x, int z); 16 | 17 | CompletableFuture getChunkAtAsync(int x, int z); 18 | 19 | UUID getUUID(); 20 | 21 | int getSeaLevel(); 22 | 23 | Location getSpawn(); 24 | 25 | Border getWorldBorder(); 26 | 27 | int getElevation(int x, int z); 28 | 29 | int getMaxElevation(); 30 | 31 | void playEffect(Player player, String effect); 32 | 33 | void playSound(Player player, String sound); 34 | 35 | Optional getDirectory(String name); 36 | 37 | default Optional getEntitiesDirectory() { 38 | return getDirectory("entities"); 39 | } 40 | 41 | default Optional getPOIDirectory() { 42 | return getDirectory("poi"); 43 | } 44 | 45 | default Optional getRegionDirectory() { 46 | return getDirectory("region"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/util/Location.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform.util; 2 | 3 | import org.popcraft.chunky.platform.World; 4 | 5 | public class Location { 6 | private World world; 7 | private double x; 8 | private double y; 9 | private double z; 10 | private float yaw; 11 | private float pitch; 12 | 13 | public Location(final World world, final double x, final double y, final double z, final float yaw, final float pitch) { 14 | this.world = world; 15 | this.x = x; 16 | this.y = y; 17 | this.z = z; 18 | this.yaw = yaw; 19 | this.pitch = pitch; 20 | } 21 | 22 | public Location(final World world, final double x, final double y, final double z) { 23 | this.world = world; 24 | this.x = x; 25 | this.y = y; 26 | this.z = z; 27 | } 28 | 29 | public Vector3 toVector() { 30 | return new Vector3(x, y, z); 31 | } 32 | 33 | public Location add(final Vector3 vector) { 34 | x += vector.getX(); 35 | y += vector.getY(); 36 | z += vector.getZ(); 37 | return this; 38 | } 39 | 40 | public Location setDirection(final Vector3 direction) { 41 | final double dirX = direction.getX(); 42 | final double dirY = direction.getY(); 43 | final double dirZ = direction.getZ(); 44 | if (dirX == 0 && dirZ == 0) { 45 | if (dirY == 0) { 46 | pitch = 0; 47 | } else { 48 | pitch = dirY < 0 ? 90 : -90; 49 | } 50 | return this; 51 | } 52 | yaw = (float) Math.toDegrees(Math.atan2(-dirX, dirZ)); 53 | pitch = (float) Math.toDegrees(-Math.atan(dirY / Math.sqrt(dirX * dirX + dirZ * dirZ))); 54 | return this; 55 | } 56 | 57 | public World getWorld() { 58 | return world; 59 | } 60 | 61 | public void setWorld(final World world) { 62 | this.world = world; 63 | } 64 | 65 | public double getX() { 66 | return x; 67 | } 68 | 69 | public void setX(final double x) { 70 | this.x = x; 71 | } 72 | 73 | public double getY() { 74 | return y; 75 | } 76 | 77 | public void setY(final double y) { 78 | this.y = y; 79 | } 80 | 81 | public double getZ() { 82 | return z; 83 | } 84 | 85 | public void setZ(final double z) { 86 | this.z = z; 87 | } 88 | 89 | public float getYaw() { 90 | return yaw; 91 | } 92 | 93 | public void setYaw(final float yaw) { 94 | this.yaw = yaw; 95 | } 96 | 97 | public float getPitch() { 98 | return pitch; 99 | } 100 | 101 | public void setPitch(final float pitch) { 102 | this.pitch = pitch; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/util/Vector2.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform.util; 2 | 3 | public class Vector2 { 4 | private double x; 5 | private double z; 6 | 7 | public Vector2(final double x, final double z) { 8 | this.x = x; 9 | this.z = z; 10 | } 11 | 12 | public static Vector2 of(final double x, final double z) { 13 | return new Vector2(x, z); 14 | } 15 | 16 | public Vector2 add(final Vector2 other) { 17 | x += other.x; 18 | z += other.z; 19 | return this; 20 | } 21 | 22 | public Vector2 multiply(final double value) { 23 | x *= value; 24 | z *= value; 25 | return this; 26 | } 27 | 28 | public Vector2 normalize() { 29 | final double length = length(); 30 | x /= length; 31 | z /= length; 32 | return this; 33 | } 34 | 35 | public double distance(final Vector2 other) { 36 | return Math.sqrt(distanceSquared(other)); 37 | } 38 | 39 | public double distanceSquared(final Vector2 other) { 40 | final double dx = this.x - other.x; 41 | final double dz = this.z - other.z; 42 | return dx * dx + dz * dz; 43 | } 44 | 45 | public double length() { 46 | return Math.sqrt(lengthSquared()); 47 | } 48 | 49 | public double lengthSquared() { 50 | return x * x + z * z; 51 | } 52 | 53 | public double getX() { 54 | return x; 55 | } 56 | 57 | public void setX(final double x) { 58 | this.x = x; 59 | } 60 | 61 | public double getZ() { 62 | return z; 63 | } 64 | 65 | public void setZ(final double z) { 66 | this.z = z; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/platform/util/Vector3.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform.util; 2 | 3 | public class Vector3 { 4 | private double x; 5 | private double y; 6 | private double z; 7 | 8 | public Vector3(final double x, final double y, final double z) { 9 | this.x = x; 10 | this.y = y; 11 | this.z = z; 12 | } 13 | 14 | public static Vector3 of(final double x, final double y, final double z) { 15 | return new Vector3(x, y, z); 16 | } 17 | 18 | public Vector3 add(final Vector3 other) { 19 | x += other.x; 20 | y += other.y; 21 | z += other.z; 22 | return this; 23 | } 24 | 25 | public Vector3 multiply(final double value) { 26 | x *= value; 27 | y *= value; 28 | z *= value; 29 | return this; 30 | } 31 | 32 | public Vector3 normalize() { 33 | final double length = length(); 34 | x /= length; 35 | y /= length; 36 | z /= length; 37 | return this; 38 | } 39 | 40 | public double distance(final Vector3 other) { 41 | return Math.sqrt(distanceSquared(other)); 42 | } 43 | 44 | public double distanceSquared(final Vector3 other) { 45 | final double dx = this.x - other.x; 46 | final double dy = this.y - other.y; 47 | final double dz = this.z - other.z; 48 | return dx * dx + dy * dy + dz * dz; 49 | } 50 | 51 | public double length() { 52 | return Math.sqrt(lengthSquared()); 53 | } 54 | 55 | public double lengthSquared() { 56 | return x * x + y * y + z * z; 57 | } 58 | 59 | public double getX() { 60 | return x; 61 | } 62 | 63 | public void setX(final double x) { 64 | this.x = x; 65 | } 66 | 67 | public double getY() { 68 | return y; 69 | } 70 | 71 | public void setY(final double y) { 72 | this.y = y; 73 | } 74 | 75 | public double getZ() { 76 | return z; 77 | } 78 | 79 | public void setZ(final double z) { 80 | this.z = z; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/AbstractEllipse.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | public abstract class AbstractEllipse extends AbstractShape { 7 | protected AbstractEllipse(final Selection selection, final boolean chunkAligned) { 8 | super(selection, chunkAligned); 9 | } 10 | 11 | public abstract Vector2 radii(); 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/AbstractPolygon.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | import java.util.List; 7 | 8 | public abstract class AbstractPolygon extends AbstractShape { 9 | protected AbstractPolygon(final Selection selection, final boolean chunkAligned) { 10 | super(selection, chunkAligned); 11 | } 12 | 13 | public abstract List points(); 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/AbstractShape.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | public abstract class AbstractShape implements Shape { 7 | protected final double centerX, centerZ; 8 | protected final double diameterX, diameterZ; 9 | protected final double radiusX, radiusZ; 10 | 11 | protected AbstractShape(final Selection selection, final boolean chunkAligned) { 12 | if (chunkAligned) { 13 | this.centerX = (double) (selection.centerChunkX() << 4) + 8; 14 | this.centerZ = (double) (selection.centerChunkZ() << 4) + 8; 15 | this.diameterX = selection.diameterChunksX() << 4; 16 | this.diameterZ = selection.diameterChunksZ() << 4; 17 | this.radiusX = diameterX / 2; 18 | this.radiusZ = diameterZ / 2; 19 | } else { 20 | this.centerX = selection.centerX(); 21 | this.centerZ = selection.centerZ(); 22 | this.radiusX = selection.radiusX(); 23 | this.radiusZ = selection.radiusZ(); 24 | this.diameterX = 2 * radiusX; 25 | this.diameterZ = 2 * radiusZ; 26 | } 27 | } 28 | 29 | public Vector2 center() { 30 | return Vector2.of(centerX, centerZ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Circle.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | public class Circle extends AbstractEllipse { 7 | public Circle(final Selection selection, final boolean chunkAligned) { 8 | super(selection, chunkAligned); 9 | } 10 | 11 | @Override 12 | public boolean isBounding(final double x, final double z) { 13 | return Math.hypot(centerX - x, centerZ - z) <= radiusX; 14 | } 15 | 16 | @Override 17 | public String name() { 18 | return ShapeType.CIRCLE; 19 | } 20 | 21 | @Override 22 | public Vector2 radii() { 23 | return Vector2.of(radiusX, radiusX); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Diamond.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import static org.popcraft.chunky.shape.ShapeUtil.insideLine; 10 | 11 | public class Diamond extends AbstractPolygon { 12 | final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z; 13 | 14 | public Diamond(final Selection selection, final boolean chunkAligned) { 15 | super(selection, chunkAligned); 16 | this.p1x = centerX; 17 | this.p1z = centerZ + radiusX; 18 | this.p2x = centerX - radiusX; 19 | this.p2z = centerZ; 20 | this.p3x = centerX; 21 | this.p3z = centerZ - radiusX; 22 | this.p4x = centerX + radiusX; 23 | this.p4z = centerZ; 24 | } 25 | 26 | @Override 27 | public List points() { 28 | return Arrays.asList( 29 | Vector2.of(p1x, p1z), 30 | Vector2.of(p2x, p2z), 31 | Vector2.of(p3x, p3z), 32 | Vector2.of(p4x, p4z) 33 | ); 34 | } 35 | 36 | @Override 37 | public boolean isBounding(final double x, final double z) { 38 | if (!insideLine(p1x, p1z, p2x, p2z, x, z)) { 39 | return false; 40 | } 41 | if (!insideLine(p2x, p2z, p3x, p3z, x, z)) { 42 | return false; 43 | } 44 | if (!insideLine(p3x, p3z, p4x, p4z, x, z)) { 45 | return false; 46 | } 47 | return insideLine(p4x, p4z, p1x, p1z, x, z); 48 | } 49 | 50 | @Override 51 | public String name() { 52 | return ShapeType.DIAMOND; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Ellipse.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | public class Ellipse extends AbstractEllipse { 7 | public Ellipse(final Selection selection, final boolean chunkAligned) { 8 | super(selection, chunkAligned); 9 | } 10 | 11 | @Override 12 | public boolean isBounding(final double x, final double z) { 13 | return (Math.pow(x - centerX, 2) / Math.pow(radiusX, 2)) + (Math.pow(z - centerZ, 2) / Math.pow(radiusZ, 2)) <= 1; 14 | } 15 | 16 | @Override 17 | public String name() { 18 | return ShapeType.ELLIPSE; 19 | } 20 | 21 | @Override 22 | public Vector2 radii() { 23 | return Vector2.of(radiusX, radiusZ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Hexagon.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import static org.popcraft.chunky.shape.ShapeUtil.insideLine; 10 | 11 | public class Hexagon extends AbstractPolygon { 12 | private final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z, p5x, p5z, p6x, p6z; 13 | 14 | public Hexagon(final Selection selection, final boolean chunkAligned) { 15 | super(selection, chunkAligned); 16 | this.p1x = centerX + radiusX * Math.cos(Math.toRadians(60)); 17 | this.p1z = centerZ + radiusX * Math.sin(Math.toRadians(60)); 18 | this.p2x = centerX + radiusX * Math.cos(Math.toRadians(120)); 19 | this.p2z = centerZ + radiusX * Math.sin(Math.toRadians(120)); 20 | this.p3x = centerX + radiusX * Math.cos(Math.toRadians(180)); 21 | this.p3z = centerZ + radiusX * Math.sin(Math.toRadians(180)); 22 | this.p4x = centerX + radiusX * Math.cos(Math.toRadians(240)); 23 | this.p4z = centerZ + radiusX * Math.sin(Math.toRadians(240)); 24 | this.p5x = centerX + radiusX * Math.cos(Math.toRadians(300)); 25 | this.p5z = centerZ + radiusX * Math.sin(Math.toRadians(300)); 26 | this.p6x = centerX + radiusX * Math.cos(Math.toRadians(360)); 27 | this.p6z = centerZ + radiusX * Math.sin(Math.toRadians(360)); 28 | } 29 | 30 | @Override 31 | public List points() { 32 | return Arrays.asList( 33 | Vector2.of(p1x, p1z), 34 | Vector2.of(p2x, p2z), 35 | Vector2.of(p3x, p3z), 36 | Vector2.of(p4x, p4z), 37 | Vector2.of(p5x, p5z), 38 | Vector2.of(p6x, p6z) 39 | ); 40 | } 41 | 42 | @Override 43 | public boolean isBounding(final double x, final double z) { 44 | final boolean inside12 = insideLine(p1x, p1z, p2x, p2z, x, z); 45 | final boolean inside23 = insideLine(p2x, p2z, p3x, p3z, x, z); 46 | final boolean inside34 = insideLine(p3x, p3z, p4x, p4z, x, z); 47 | final boolean inside45 = insideLine(p4x, p4z, p5x, p5z, x, z); 48 | final boolean inside56 = insideLine(p5x, p5z, p6x, p6z, x, z); 49 | final boolean inside61 = insideLine(p6x, p6z, p1x, p1z, x, z); 50 | return inside12 && inside23 && inside34 && inside45 && inside56 && inside61; 51 | } 52 | 53 | @Override 54 | public String name() { 55 | return ShapeType.HEXAGON; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Pentagon.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import static org.popcraft.chunky.shape.ShapeUtil.insideLine; 10 | 11 | public class Pentagon extends AbstractPolygon { 12 | private final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z, p5x, p5z; 13 | 14 | public Pentagon(final Selection selection, final boolean chunkAligned) { 15 | super(selection, chunkAligned); 16 | this.p1x = centerX + radiusX * Math.cos(Math.toRadians(54)); 17 | this.p1z = centerZ + radiusX * Math.sin(Math.toRadians(54)); 18 | this.p2x = centerX + radiusX * Math.cos(Math.toRadians(126)); 19 | this.p2z = centerZ + radiusX * Math.sin(Math.toRadians(126)); 20 | this.p3x = centerX + radiusX * Math.cos(Math.toRadians(198)); 21 | this.p3z = centerZ + radiusX * Math.sin(Math.toRadians(198)); 22 | this.p4x = centerX + radiusX * Math.cos(Math.toRadians(270)); 23 | this.p4z = centerZ + radiusX * Math.sin(Math.toRadians(270)); 24 | this.p5x = centerX + radiusX * Math.cos(Math.toRadians(342)); 25 | this.p5z = centerZ + radiusX * Math.sin(Math.toRadians(342)); 26 | } 27 | 28 | @Override 29 | public List points() { 30 | return Arrays.asList( 31 | Vector2.of(p1x, p1z), 32 | Vector2.of(p2x, p2z), 33 | Vector2.of(p3x, p3z), 34 | Vector2.of(p4x, p4z), 35 | Vector2.of(p5x, p5z) 36 | ); 37 | } 38 | 39 | @Override 40 | public boolean isBounding(final double x, final double z) { 41 | final boolean inside12 = insideLine(p1x, p1z, p2x, p2z, x, z); 42 | final boolean inside23 = insideLine(p2x, p2z, p3x, p3z, x, z); 43 | final boolean inside34 = insideLine(p3x, p3z, p4x, p4z, x, z); 44 | final boolean inside45 = insideLine(p4x, p4z, p5x, p5z, x, z); 45 | final boolean inside51 = insideLine(p5x, p5z, p1x, p1z, x, z); 46 | return inside12 && inside23 && inside34 && inside45 && inside51; 47 | } 48 | 49 | @Override 50 | public String name() { 51 | return ShapeType.PENTAGON; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Rectangle.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class Rectangle extends AbstractPolygon { 10 | final double b1x, b1z, b2x, b2z; 11 | final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z; 12 | 13 | protected Rectangle(final Selection selection, final boolean chunkAligned) { 14 | super(selection, chunkAligned); 15 | this.b1x = centerX - radiusX; 16 | this.b1z = centerZ - radiusZ; 17 | this.b2x = centerX + radiusX; 18 | this.b2z = centerZ + radiusZ; 19 | this.p1x = centerX + radiusX; 20 | this.p1z = centerZ - radiusZ; 21 | this.p2x = centerX - radiusX; 22 | this.p2z = centerZ - radiusZ; 23 | this.p3x = centerX - radiusX; 24 | this.p3z = centerZ + radiusZ; 25 | this.p4x = centerX + radiusX; 26 | this.p4z = centerZ + radiusZ; 27 | } 28 | 29 | @Override 30 | public List points() { 31 | return Arrays.asList( 32 | Vector2.of(p1x, p1z), 33 | Vector2.of(p2x, p2z), 34 | Vector2.of(p3x, p3z), 35 | Vector2.of(p4x, p4z) 36 | ); 37 | } 38 | 39 | @Override 40 | public boolean isBounding(final double x, final double z) { 41 | return x >= b1x && x <= b2x && z >= b1z && z <= b2z; 42 | } 43 | 44 | @Override 45 | public String name() { 46 | return ShapeType.RECTANGLE; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Shape.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | @FunctionalInterface 4 | public interface Shape { 5 | boolean isBounding(double x, double z); 6 | 7 | default String name() { 8 | return "shape"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/ShapeFactory.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.util.Translator; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.function.BiFunction; 10 | 11 | public final class ShapeFactory { 12 | private static final Map> custom = new HashMap<>(); 13 | 14 | private ShapeFactory() { 15 | } 16 | 17 | public static Shape getShape(final Selection selection) { 18 | return getShape(selection, true); 19 | } 20 | 21 | public static Shape getShape(final Selection selection, final boolean chunkAligned) { 22 | return switch (selection.shape()) { 23 | case ShapeType.CIRCLE -> new Circle(selection, chunkAligned); 24 | case ShapeType.DIAMOND -> new Diamond(selection, chunkAligned); 25 | case ShapeType.ELLIPSE, ShapeType.OVAL -> new Ellipse(selection, chunkAligned); 26 | case ShapeType.HEXAGON -> new Hexagon(selection, chunkAligned); 27 | case ShapeType.PENTAGON -> new Pentagon(selection, chunkAligned); 28 | case ShapeType.RECTANGLE -> new Rectangle(selection, chunkAligned); 29 | case ShapeType.STAR -> new Star(selection, chunkAligned); 30 | case ShapeType.TRIANGLE -> new Triangle(selection, chunkAligned); 31 | default -> custom.getOrDefault(selection.shape(), Square::new).apply(selection, chunkAligned); 32 | }; 33 | } 34 | 35 | public static void registerCustom(final String name, final BiFunction shapeFunction) { 36 | custom.put(name, shapeFunction); 37 | Translator.addCustomTranslation("shape_%s".formatted(name), name); 38 | } 39 | 40 | public static Set getCustomTypes() { 41 | return custom.keySet(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/ShapeType.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | public final class ShapeType { 8 | public static final String CIRCLE = "circle"; 9 | public static final String DIAMOND = "diamond"; 10 | public static final String ELLIPSE = "ellipse"; 11 | public static final String HEXAGON = "hexagon"; 12 | public static final String OVAL = "oval"; 13 | public static final String PENTAGON = "pentagon"; 14 | public static final String RECTANGLE = "rectangle"; 15 | public static final String SQUARE = "square"; 16 | public static final String STAR = "star"; 17 | public static final String TRIANGLE = "triangle"; 18 | 19 | private static final List DEFAULTS = List.of(CIRCLE, DIAMOND, ELLIPSE, HEXAGON, PENTAGON, RECTANGLE, SQUARE, STAR, TRIANGLE); 20 | 21 | private ShapeType() { 22 | } 23 | 24 | public static List all() { 25 | final Set customTypes = ShapeFactory.getCustomTypes(); 26 | if (customTypes.isEmpty()) { 27 | return DEFAULTS; 28 | } 29 | final List allTypes = new ArrayList<>(DEFAULTS); 30 | allTypes.addAll(customTypes); 31 | return allTypes; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Square.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class Square extends AbstractPolygon { 10 | final double b1x, b1z, b2x, b2z; 11 | final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z; 12 | 13 | protected Square(final Selection selection, final boolean chunkAligned) { 14 | super(selection, chunkAligned); 15 | this.b1x = centerX - radiusX; 16 | this.b1z = centerZ - radiusX; 17 | this.b2x = centerX + radiusX; 18 | this.b2z = centerZ + radiusX; 19 | this.p1x = centerX + radiusX; 20 | this.p1z = centerZ - radiusX; 21 | this.p2x = centerX - radiusX; 22 | this.p2z = centerZ - radiusX; 23 | this.p3x = centerX - radiusX; 24 | this.p3z = centerZ + radiusX; 25 | this.p4x = centerX + radiusX; 26 | this.p4z = centerZ + radiusX; 27 | } 28 | 29 | @Override 30 | public List points() { 31 | return Arrays.asList( 32 | Vector2.of(p1x, p1z), 33 | Vector2.of(p2x, p2z), 34 | Vector2.of(p3x, p3z), 35 | Vector2.of(p4x, p4z) 36 | ); 37 | } 38 | 39 | @Override 40 | public boolean isBounding(final double x, final double z) { 41 | return x >= b1x && x <= b2x && z >= b1z && z <= b2z; 42 | } 43 | 44 | @Override 45 | public String name() { 46 | return ShapeType.SQUARE; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/shape/Triangle.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import static org.popcraft.chunky.shape.ShapeUtil.insideLine; 10 | 11 | public class Triangle extends AbstractPolygon { 12 | private final double p1x, p1z, p2x, p2z, p3x, p3z; 13 | 14 | public Triangle(final Selection selection, final boolean chunkAligned) { 15 | super(selection, chunkAligned); 16 | this.p1x = centerX + radiusX; 17 | this.p1z = centerZ + radiusX; 18 | this.p2x = centerX - radiusX; 19 | this.p2z = centerZ + radiusX; 20 | this.p3x = centerX; 21 | this.p3z = centerZ - radiusX; 22 | } 23 | 24 | @Override 25 | public List points() { 26 | return Arrays.asList( 27 | Vector2.of(p1x, p1z), 28 | Vector2.of(p2x, p2z), 29 | Vector2.of(p3x, p3z) 30 | ); 31 | } 32 | 33 | @Override 34 | public boolean isBounding(final double x, final double z) { 35 | if (!insideLine(p1x, p1z, p2x, p2z, x, z)) { 36 | return false; 37 | } 38 | if (!insideLine(p2x, p2z, p3x, p3z, x, z)) { 39 | return false; 40 | } 41 | return insideLine(p3x, p3z, p1x, p1z, x, z); 42 | } 43 | 44 | @Override 45 | public String name() { 46 | return ShapeType.TRIANGLE; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/ChunkCoordinate.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import java.util.Objects; 4 | import java.util.Optional; 5 | 6 | public record ChunkCoordinate(int x, int z) implements Comparable { 7 | public static Optional fromRegionFile(final String regionFileName) { 8 | if (!regionFileName.startsWith("r.")) { 9 | return Optional.empty(); 10 | } 11 | final int extension = regionFileName.indexOf(".mca"); 12 | if (extension < 2) { 13 | return Optional.empty(); 14 | } 15 | final String regionCoordinates = regionFileName.substring(2, extension); 16 | final int separator = regionCoordinates.indexOf('.'); 17 | final Optional regionX = Input.tryInteger(regionCoordinates.substring(0, separator)); 18 | final Optional regionZ = Input.tryInteger(regionCoordinates.substring(separator + 1)); 19 | if (regionX.isPresent() && regionZ.isPresent()) { 20 | return Optional.of(new ChunkCoordinate(regionX.get(), regionZ.get())); 21 | } 22 | return Optional.empty(); 23 | } 24 | 25 | @Override 26 | public int compareTo(final ChunkCoordinate o) { 27 | return this.x == o.x ? Integer.compare(this.z, o.z) : Integer.compare(this.x, o.x); 28 | } 29 | 30 | @Override 31 | public boolean equals(final Object o) { 32 | if (this == o) { 33 | return true; 34 | } 35 | if (o == null || getClass() != o.getClass()) { 36 | return false; 37 | } 38 | final ChunkCoordinate that = (ChunkCoordinate) o; 39 | return x == that.x && z == that.z; 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return Objects.hash(x, z); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return String.format("%d, %d", x, z); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/ChunkMath.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | public final class ChunkMath { 4 | private ChunkMath() { 5 | } 6 | 7 | public static long pack(final int x, final int z) { 8 | final long lx = x & 0xFFFFFFFFL; 9 | final long lz = z & 0xFFFFFFFFL; 10 | return lx << 32 | lz; 11 | } 12 | 13 | public static int regionIndex(final int x, final int z) { 14 | return (x & 0x1F) * 32 + (z & 0x1F); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/Disk.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.platform.World; 5 | 6 | import java.nio.file.Files; 7 | import java.nio.file.InvalidPathException; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | import java.util.Optional; 11 | 12 | public final class Disk { 13 | private static final long ESTIMATED_SPACE_PER_CHUNK = 7031L; 14 | private static final double PERCENT_OVERESTIMATE = 1.05d; 15 | 16 | private Disk() { 17 | } 18 | 19 | public static long estimatedSpace(final Selection selection) { 20 | final long diameterChunksX = selection.diameterChunksX(); 21 | final long diameterChunksZ = selection.diameterChunksZ(); 22 | return (long) (PERCENT_OVERESTIMATE * (diameterChunksX * diameterChunksZ * ESTIMATED_SPACE_PER_CHUNK)); 23 | } 24 | 25 | public static long remainingSpace(final World world) { 26 | final Optional regionDirectory = world.getRegionDirectory(); 27 | try { 28 | if (regionDirectory.isPresent()) { 29 | return regionDirectory.get().toFile().getUsableSpace(); 30 | } 31 | final Path currentWorkingDirectory = Paths.get(""); 32 | if (Files.exists(currentWorkingDirectory)) { 33 | return currentWorkingDirectory.toFile().getUsableSpace(); 34 | } 35 | } catch (UnsupportedOperationException | InvalidPathException | SecurityException ignored) { 36 | return Long.MAX_VALUE; 37 | } 38 | return 0L; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/Formatting.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import org.popcraft.chunky.Selection; 4 | import org.popcraft.chunky.shape.ShapeType; 5 | 6 | import java.text.DecimalFormat; 7 | 8 | public final class Formatting { 9 | private static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("#.##"); 10 | private static final char[] BINARY_PREFIXES = new char[]{'K', 'M', 'G', 'T', 'P'}; 11 | 12 | private Formatting() { 13 | } 14 | 15 | public static String bytes(final long bytes) { 16 | final long value = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); 17 | if (value < 1024) { 18 | return String.format("%d B", bytes); 19 | } 20 | int i = BINARY_PREFIXES.length - 1; 21 | long prefixValue = 1L << (BINARY_PREFIXES.length * 10); 22 | for (; i > 0; --i) { 23 | if (value >= prefixValue) { 24 | break; 25 | } 26 | prefixValue >>= 10; 27 | } 28 | return String.format("%.1f %cB", bytes / (double) prefixValue, BINARY_PREFIXES[i]); 29 | } 30 | 31 | public static String radius(final Selection selection) { 32 | if (ShapeType.RECTANGLE.equals(selection.shape()) || ShapeType.ELLIPSE.equals(selection.shape())) { 33 | return String.format("%s, %s", number(selection.radiusX()), number(selection.radiusZ())); 34 | } else { 35 | return String.format("%s", number(selection.radiusX())); 36 | } 37 | } 38 | 39 | public static synchronized String number(final double number) { 40 | return NUMBER_FORMAT.format(number); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/Pair.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | public record Pair(L left, R right) { 4 | public static Pair of(final L left, final R right) { 5 | return new Pair<>(left, right); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/Parameter.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import java.util.Optional; 4 | 5 | public class Parameter { 6 | private final String type; 7 | private final String value; 8 | 9 | public Parameter(final String type, final String value) { 10 | this.type = type; 11 | this.value = value; 12 | } 13 | 14 | public Parameter(final String expression) { 15 | final String[] parts = expression.split("="); 16 | this.type = parts[0]; 17 | this.value = parts.length > 1 ? parts[1] : null; 18 | } 19 | 20 | public static Parameter of(final String expression) { 21 | return new Parameter(expression); 22 | } 23 | 24 | public static Parameter of(final String type, final String value) { 25 | return new Parameter(type, value); 26 | } 27 | 28 | public String getType() { 29 | return type; 30 | } 31 | 32 | public Optional getValue() { 33 | return Optional.ofNullable(value); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | final StringBuilder builder = new StringBuilder(type); 39 | if (value != null) { 40 | builder.append("=").append(value); 41 | } 42 | return builder.toString(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/PendingAction.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | public class PendingAction { 6 | private final Runnable action; 7 | private final long expiry; 8 | 9 | public PendingAction(final Runnable action) { 10 | this.action = action; 11 | this.expiry = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1); 12 | } 13 | 14 | public Runnable getAction() { 15 | return action; 16 | } 17 | 18 | public boolean hasExpired() { 19 | return System.currentTimeMillis() > expiry; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/RegionCache.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import java.util.BitSet; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | public class RegionCache { 8 | private final Map cache = new ConcurrentHashMap<>(); 9 | 10 | public WorldState getWorld(final String world) { 11 | return cache.computeIfAbsent(world, x -> new WorldState()); 12 | } 13 | 14 | public void clear(final String world) { 15 | cache.remove(world); 16 | } 17 | 18 | @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") 19 | public static final class WorldState { 20 | private final Map regions = new ConcurrentHashMap<>(); 21 | 22 | public void setGenerated(final int x, final int z) { 23 | final int regionX = x >> 5; 24 | final int regionZ = z >> 5; 25 | final long regionKey = ChunkMath.pack(regionX, regionZ); 26 | final BitSet region = regions.computeIfAbsent(regionKey, v -> new BitSet()); 27 | final int chunkIndex = ChunkMath.regionIndex(x, z); 28 | synchronized (region) { 29 | region.set(chunkIndex); 30 | } 31 | } 32 | 33 | public boolean isGenerated(final int x, final int z) { 34 | final int regionX = x >> 5; 35 | final int regionZ = z >> 5; 36 | final long regionKey = ChunkMath.pack(regionX, regionZ); 37 | if (!regions.containsKey(regionKey)) { 38 | return false; 39 | } 40 | final BitSet region = regions.get(regionKey); 41 | final int chunkIndex = ChunkMath.regionIndex(x, z); 42 | synchronized (region) { 43 | return region.get(chunkIndex); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/TaskScheduler.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import java.util.Set; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Future; 7 | import java.util.concurrent.SynchronousQueue; 8 | import java.util.concurrent.ThreadPoolExecutor; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | public class TaskScheduler { 12 | private final ExecutorService executor; 13 | private final Set> futures = ConcurrentHashMap.newKeySet(); 14 | 15 | public TaskScheduler() { 16 | final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, Integer.MAX_VALUE, 5, TimeUnit.MINUTES, new SynchronousQueue<>()); 17 | threadPoolExecutor.setThreadFactory(runnable -> { 18 | final Thread thread = new Thread(runnable); 19 | thread.setDaemon(true); 20 | return thread; 21 | }); 22 | threadPoolExecutor.prestartAllCoreThreads(); 23 | threadPoolExecutor.allowCoreThreadTimeOut(true); 24 | this.executor = threadPoolExecutor; 25 | } 26 | 27 | public void runTask(final Runnable runnable) { 28 | futures.add(executor.submit(runnable)); 29 | futures.removeIf(Future::isDone); 30 | } 31 | 32 | public void cancelTasks() { 33 | for (Future future : futures) { 34 | future.cancel(true); 35 | } 36 | futures.clear(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/Translator.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.reflect.TypeToken; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public final class Translator { 14 | private static final Map fallbackTranslations; 15 | private static Map translations = Map.of(); 16 | 17 | static { 18 | fallbackTranslations = load("en"); 19 | } 20 | 21 | private Translator() { 22 | } 23 | 24 | public static void setLanguage(final String language) { 25 | translations = load(language); 26 | } 27 | 28 | public static boolean isValidLanguage(final String language) { 29 | return Translator.class.getClassLoader().getResource("lang/" + language + ".json") != null; 30 | } 31 | 32 | public static String translateKey(final String key, final boolean prefixed, final Object... args) { 33 | final StringBuilder translation = new StringBuilder(); 34 | final String message = translations.getOrDefault(key, fallbackTranslations.getOrDefault(key, key)); 35 | if (prefixed) { 36 | translation.append("[Chunky] "); 37 | } 38 | translation.append(String.format(message, args)); 39 | return translation.toString(); 40 | } 41 | 42 | public static String translate(final String key, final Object... args) { 43 | return translateKey(key, false, args); 44 | } 45 | 46 | public static void addCustomTranslation(final String key, final String message) { 47 | fallbackTranslations.put(key, message); 48 | } 49 | 50 | private static Map load(final String language) { 51 | final InputStream input = Translator.class.getClassLoader().getResourceAsStream("lang/" + language + ".json"); 52 | if (input != null) { 53 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { 54 | final StringBuilder lang = new StringBuilder(); 55 | String s; 56 | while ((s = reader.readLine()) != null) { 57 | lang.append(s); 58 | } 59 | return new Gson().fromJson(lang.toString(), new TypeToken>() { 60 | }.getType()); 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | return Map.of(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /common/src/main/java/org/popcraft/chunky/util/Version.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import java.util.Objects; 4 | 5 | public class Version implements Comparable { 6 | public static final Version INVALID = new Version(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); 7 | public static final Version MINECRAFT_1_13_2 = new Version(1, 13, 2); 8 | private int major = 0, minor = 0, patch = 0; 9 | 10 | public Version(final int major, final int minor, final int patch) { 11 | this.major = major; 12 | this.minor = minor; 13 | this.patch = patch; 14 | } 15 | 16 | public Version(final String version) { 17 | if (version == null || version.isEmpty()) { 18 | this.major = Integer.MIN_VALUE; 19 | return; 20 | } 21 | final String[] semVer = version.split("\\."); 22 | if (semVer.length > 0) { 23 | this.major = Input.tryInteger(semVer[0]).orElse(Integer.MIN_VALUE); 24 | } 25 | if (semVer.length > 1) { 26 | this.minor = Input.tryInteger(semVer[1]).orElse(Integer.MIN_VALUE); 27 | } 28 | if (semVer.length > 2) { 29 | this.patch = Input.tryInteger(semVer[2]).orElse(Integer.MIN_VALUE); 30 | } 31 | } 32 | 33 | public Version(final String version, final boolean minecraft) { 34 | this(minecraft && version.indexOf('-') > -1 ? version.substring(0, version.indexOf('-')) : version); 35 | } 36 | 37 | public boolean isEqualTo(final Version o) { 38 | return compareTo(o) == 0; 39 | } 40 | 41 | public boolean isHigherThan(final Version o) { 42 | return compareTo(o) > 0; 43 | } 44 | 45 | public boolean isHigherThanOrEqualTo(final Version o) { 46 | return compareTo(o) >= 0; 47 | } 48 | 49 | public boolean isLowerThan(final Version o) { 50 | return compareTo(o) < 0; 51 | } 52 | 53 | public boolean isLowerThanOrEqualTo(final Version o) { 54 | return compareTo(o) <= 0; 55 | } 56 | 57 | public boolean isValid() { 58 | return major != Integer.MIN_VALUE && minor != Integer.MIN_VALUE && patch != Integer.MIN_VALUE; 59 | } 60 | 61 | @Override 62 | public int compareTo(final Version o) { 63 | if (this.major != o.major) { 64 | return this.major - o.major; 65 | } 66 | if (this.minor != o.minor) { 67 | return this.minor - o.minor; 68 | } 69 | return this.patch - o.patch; 70 | } 71 | 72 | @Override 73 | public boolean equals(final Object o) { 74 | if (this == o) { 75 | return true; 76 | } 77 | if (o == null || getClass() != o.getClass()) { 78 | return false; 79 | } 80 | final Version version = (Version) o; 81 | return major == version.major && minor == version.minor && patch == version.patch; 82 | } 83 | 84 | @Override 85 | public int hashCode() { 86 | return Objects.hash(major, minor, patch); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /common/src/main/resources/version.properties: -------------------------------------------------------------------------------- 1 | version=${version} 2 | -------------------------------------------------------------------------------- /common/src/test/java/org/popcraft/chunky/iterator/TotalTest.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.iterator; 2 | 3 | import org.junit.Test; 4 | import org.popcraft.chunky.Selection; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * This test checks to make sure the total number of chunks generated matches across iterators. 10 | */ 11 | public class TotalTest { 12 | private static final Selection.Builder SELECTION = Selection.builder(null, null).center(-25, 25).radius(50); 13 | 14 | /** 15 | * Checks that the totals still match when the radius is changed. 16 | */ 17 | @Test 18 | public void radius() { 19 | final Selection original = SELECTION.build(); 20 | for (int i = 0; i < original.radiusX(); ++i) { 21 | final Selection s = SELECTION.radiusX(i).radiusZ(i).build(); 22 | final ChunkIterator concentricIterator = new ConcentricChunkIterator(s); 23 | final ChunkIterator loop2Iterator = new Loop2ChunkIterator(s); 24 | final ChunkIterator spiralIterator = new SpiralChunkIterator(s); 25 | final ChunkIterator regionIterator = new RegionChunkIterator(s); 26 | assertEquals(concentricIterator.total(), loop2Iterator.total()); 27 | assertEquals(loop2Iterator.total(), spiralIterator.total()); 28 | assertEquals(spiralIterator.total(), regionIterator.total()); 29 | } 30 | } 31 | 32 | /** 33 | * Checks that the totals still match when the center is moved. 34 | */ 35 | @Test 36 | public void center() { 37 | final Selection original = SELECTION.build(); 38 | for (int i = 0; i > original.centerX(); --i) { 39 | for (int j = 0; j < original.centerZ(); ++j) { 40 | final Selection s = SELECTION.center(i, j).build(); 41 | final ChunkIterator concentricIterator = new ConcentricChunkIterator(s); 42 | final ChunkIterator loop2Iterator = new Loop2ChunkIterator(s); 43 | final ChunkIterator spiralIterator = new SpiralChunkIterator(s); 44 | final ChunkIterator regionIterator = new RegionChunkIterator(s); 45 | assertEquals(concentricIterator.total(), loop2Iterator.total()); 46 | assertEquals(loop2Iterator.total(), spiralIterator.total()); 47 | assertEquals(spiralIterator.total(), regionIterator.total()); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /common/src/test/java/org/popcraft/chunky/shape/ShapeTest.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.shape; 2 | 3 | import org.junit.Test; 4 | import org.popcraft.chunky.Selection; 5 | import org.popcraft.chunky.iterator.ChunkIterator; 6 | import org.popcraft.chunky.iterator.ChunkIteratorFactory; 7 | import org.popcraft.chunky.util.ChunkCoordinate; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | /** 12 | * This test checks each shape to ensure that the number of chunks they generate is correct. 13 | */ 14 | public class ShapeTest { 15 | private static final Selection.Builder SELECTION = Selection.builder(null, null).center(-500, 500).radiusX(1000).radiusZ(500); 16 | 17 | @Test 18 | public void square() { 19 | testShape("square", 16129); 20 | } 21 | 22 | @Test 23 | public void circle() { 24 | testShape("circle", 12645); 25 | } 26 | 27 | @Test 28 | public void triangle() { 29 | testShape("triangle", 8065); 30 | } 31 | 32 | @Test 33 | public void diamond() { 34 | testShape("diamond", 8065); 35 | } 36 | 37 | @Test 38 | public void pentagon() { 39 | testShape("pentagon", 9593); 40 | } 41 | 42 | @Test 43 | public void star() { 44 | testShape("star", 4518); 45 | } 46 | 47 | @Test 48 | public void rectangle() { 49 | testShape("rectangle", 8255); 50 | } 51 | 52 | @Test 53 | public void ellipse() { 54 | testShape("ellipse", 6503); 55 | } 56 | 57 | private void testShape(final String type, final int expected) { 58 | final Selection s = SELECTION.shape(type).build(); 59 | final ChunkIterator chunkIterator = ChunkIteratorFactory.getChunkIterator(s); 60 | final Shape shape = ShapeFactory.getShape(s); 61 | int generated = 0; 62 | while (chunkIterator.hasNext()) { 63 | final ChunkCoordinate chunkCoordinate = chunkIterator.next(); 64 | final int xChunkCenter = (chunkCoordinate.x() << 4) + 8; 65 | final int zChunkCenter = (chunkCoordinate.z() << 4) + 8; 66 | if (shape.isBounding(xChunkCenter, zChunkCenter)) { 67 | ++generated; 68 | } 69 | } 70 | assertEquals(expected, generated); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/src/test/java/org/popcraft/chunky/util/RegionCacheTest.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.util; 2 | 3 | import org.junit.Test; 4 | import org.popcraft.chunky.Selection; 5 | import org.popcraft.chunky.iterator.ChunkIterator; 6 | import org.popcraft.chunky.iterator.ConcentricChunkIterator; 7 | 8 | import static org.junit.Assert.assertTrue; 9 | 10 | public class RegionCacheTest { 11 | @Test 12 | public void testChunkCache() { 13 | final RegionCache regionCache = new RegionCache(); 14 | final Selection selection = Selection.builder(null, null).center(0, 0).radius(16).build(); 15 | final ChunkIterator iterator = new ConcentricChunkIterator(selection); 16 | final RegionCache.WorldState worldState = regionCache.getWorld("world"); 17 | iterator.forEachRemaining(chunk -> worldState.setGenerated(chunk.x(), chunk.z())); 18 | final ChunkIterator iterator2 = new ConcentricChunkIterator(selection); 19 | iterator2.forEachRemaining(chunk -> assertTrue(worldState.isGenerated(chunk.x(), chunk.z()))); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /common/src/main/resources/lang/en.json 3 | translation: /common/src/main/resources/lang/%locale_with_underscore%.json 4 | languages_mapping: 5 | locale_with_underscore: 6 | bg: bg 7 | bs: bs 8 | cs: cs 9 | de: de 10 | es-ES: es 11 | fi: fi 12 | fr: fr 13 | he: he 14 | hi: hi 15 | hr: hr 16 | it: it 17 | ja: ja 18 | ko: ko 19 | nl: nl 20 | no: no 21 | pl: pl 22 | pt-BR: pt_BR 23 | pt-PT: pt 24 | ru: ru 25 | sr-CS: sr_CS 26 | tr: tr 27 | uk: uk 28 | vi: vi 29 | zh-CN: zh_CN 30 | zh-TW: zh_TW 31 | zh-HK: zh_HK 32 | -------------------------------------------------------------------------------- /fabric/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("dev.architectury.loom") version "1.10-SNAPSHOT" 3 | } 4 | 5 | val shade: Configuration by configurations.creating 6 | 7 | dependencies { 8 | minecraft(group = "com.mojang", name = "minecraft", version = "1.21.5") 9 | mappings(loom.officialMojangMappings()) 10 | modImplementation(group = "net.fabricmc", name = "fabric-loader", version = "0.16.10") 11 | modImplementation(group = "net.fabricmc.fabric-api", name = "fabric-api", version = "0.119.5+1.21.5") 12 | modCompileOnly(group = "me.lucko", name = "fabric-permissions-api", version = "0.3.3") 13 | implementation(project(":chunky-common")) 14 | shade(project(":chunky-common")) 15 | } 16 | 17 | tasks { 18 | processResources { 19 | filesMatching("fabric.mod.json") { 20 | expand( 21 | "id" to rootProject.name, 22 | "version" to project.version, 23 | "name" to project.property("artifactName"), 24 | "description" to project.property("description"), 25 | "author" to project.property("author"), 26 | "github" to project.property("github") 27 | ) 28 | } 29 | } 30 | shadowJar { 31 | configurations = listOf(shade) 32 | archiveClassifier.set("dev") 33 | archiveFileName.set(null as String?) 34 | } 35 | remapJar { 36 | inputFile.set(shadowJar.get().archiveFile) 37 | archiveFileName.set("${project.property("artifactName")}-Fabric-${project.version}.jar") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/command/suggestion/PatternSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | import org.popcraft.chunky.iterator.PatternType; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class PatternSuggestionProvider implements SuggestionProvider { 14 | @Override 15 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 16 | try { 17 | final String input = context.getArgument(CommandLiteral.PATTERN, String.class); 18 | PatternType.ALL.forEach(pattern -> { 19 | if (pattern.contains(input.toLowerCase())) { 20 | builder.suggest(pattern); 21 | } 22 | }); 23 | } catch (IllegalArgumentException e) { 24 | PatternType.ALL.forEach(builder::suggest); 25 | } 26 | return builder.buildFuture(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/command/suggestion/ShapeSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | import org.popcraft.chunky.shape.ShapeType; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class ShapeSuggestionProvider implements SuggestionProvider { 14 | @Override 15 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 16 | try { 17 | final String input = context.getArgument(CommandLiteral.SHAPE, String.class); 18 | ShapeType.all().forEach(shape -> { 19 | if (shape.contains(input.toLowerCase())) { 20 | builder.suggest(shape); 21 | } 22 | }); 23 | } catch (IllegalArgumentException e) { 24 | ShapeType.all().forEach(builder::suggest); 25 | } 26 | return builder.buildFuture(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/command/suggestion/SuggestionProviders.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.suggestion.SuggestionProvider; 4 | import net.minecraft.commands.CommandSourceStack; 5 | 6 | public final class SuggestionProviders { 7 | public static final SuggestionProvider PATTERNS; 8 | public static final SuggestionProvider SHAPES; 9 | public static final SuggestionProvider TRIM_MODES; 10 | 11 | static { 12 | PATTERNS = new PatternSuggestionProvider(); 13 | SHAPES = new ShapeSuggestionProvider(); 14 | TRIM_MODES = new TrimModeSuggestionProvider(); 15 | } 16 | 17 | private SuggestionProviders() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/command/suggestion/TrimModeSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class TrimModeSuggestionProvider implements SuggestionProvider { 14 | private static final List TRIM_MODES = List.of("inside", "outside"); 15 | 16 | @Override 17 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 18 | try { 19 | final String input = context.getArgument(CommandLiteral.TRIM_MODE, String.class); 20 | TRIM_MODES.forEach(shape -> { 21 | if (shape.contains(input.toLowerCase())) { 22 | builder.suggest(shape); 23 | } 24 | }); 25 | } catch (IllegalArgumentException e) { 26 | TRIM_MODES.forEach(builder::suggest); 27 | } 28 | return builder.buildFuture(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/listeners/bossbar/BossBarTaskFinishListener.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.listeners.bossbar; 2 | 3 | import net.minecraft.resources.ResourceLocation; 4 | import net.minecraft.server.level.ServerBossEvent; 5 | import org.popcraft.chunky.GenerationTask; 6 | import org.popcraft.chunky.event.task.GenerationTaskFinishEvent; 7 | import org.popcraft.chunky.platform.World; 8 | 9 | import java.util.Map; 10 | import java.util.function.Consumer; 11 | 12 | public class BossBarTaskFinishListener implements Consumer { 13 | private final Map bossBars; 14 | 15 | public BossBarTaskFinishListener(final Map bossBars) { 16 | this.bossBars = bossBars; 17 | } 18 | 19 | @Override 20 | public void accept(final GenerationTaskFinishEvent event) { 21 | final GenerationTask task = event.generationTask(); 22 | final World world = task.getSelection().world(); 23 | final ResourceLocation worldIdentifier = ResourceLocation.tryParse(world.getKey()); 24 | if (worldIdentifier == null) { 25 | return; 26 | } 27 | final ServerBossEvent bossBar = bossBars.get(worldIdentifier); 28 | if (bossBar != null) { 29 | bossBar.removeAllPlayers(); 30 | bossBars.remove(worldIdentifier); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/mixin/ChunkMapMixin.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.mixin; 2 | 3 | import net.minecraft.nbt.CompoundTag; 4 | import net.minecraft.server.level.ChunkHolder; 5 | import net.minecraft.server.level.ChunkMap; 6 | import net.minecraft.world.level.ChunkPos; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.gen.Invoker; 9 | 10 | import java.util.Optional; 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | @SuppressWarnings("UnnecessaryInterfaceModifier") 14 | @Mixin(ChunkMap.class) 15 | public interface ChunkMapMixin { 16 | @Invoker("getVisibleChunkIfPresent") 17 | public ChunkHolder invokeGetVisibleChunkIfPresent(long pos); 18 | 19 | @Invoker("readChunk") 20 | public CompletableFuture> invokeReadChunk(ChunkPos pos); 21 | } 22 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/mixin/MinecraftServerMixin.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.mixin; 2 | 3 | import net.minecraft.server.MinecraftServer; 4 | import org.popcraft.chunky.ChunkyProvider; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(MinecraftServer.class) 12 | public class MinecraftServerMixin { 13 | @Shadow 14 | private int emptyTicks; 15 | 16 | @Inject(method = "tickServer", at = @At("HEAD")) 17 | private void preventPausing(CallbackInfo ci) { 18 | if (!ChunkyProvider.get().getGenerationTasks().isEmpty()) { 19 | this.emptyTicks = 0; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/mixin/ServerChunkCacheMixin.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.mixin; 2 | 3 | import net.minecraft.server.level.ChunkResult; 4 | import net.minecraft.server.level.ServerChunkCache; 5 | import net.minecraft.world.level.chunk.ChunkAccess; 6 | import net.minecraft.world.level.chunk.status.ChunkStatus; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.gen.Invoker; 9 | 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | @Mixin(ServerChunkCache.class) 13 | public interface ServerChunkCacheMixin { 14 | @SuppressWarnings("UnnecessaryModifier") 15 | @Invoker("getChunkFutureMainThread") 16 | public CompletableFuture> invokeGetChunkFutureMainThread(final int chunkX, 17 | final int chunkZ, 18 | final ChunkStatus toStatus, 19 | final boolean create); 20 | } 21 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/platform/FabricBorder.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.minecraft.world.level.border.WorldBorder; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | import org.popcraft.chunky.shape.ShapeType; 6 | 7 | public class FabricBorder implements Border { 8 | private final WorldBorder worldBorder; 9 | 10 | public FabricBorder(final WorldBorder worldBorder) { 11 | this.worldBorder = worldBorder; 12 | } 13 | 14 | @Override 15 | public Vector2 getCenter() { 16 | return Vector2.of(worldBorder.getCenterX(), worldBorder.getCenterZ()); 17 | } 18 | 19 | @Override 20 | public double getRadiusX() { 21 | return worldBorder.getSize() / 2d; 22 | } 23 | 24 | @Override 25 | public double getRadiusZ() { 26 | return worldBorder.getSize() / 2d; 27 | } 28 | 29 | @Override 30 | public String getShape() { 31 | return ShapeType.SQUARE; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/platform/FabricPlayer.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.minecraft.network.chat.Component; 4 | import net.minecraft.server.level.ServerPlayer; 5 | import net.minecraft.world.entity.Relative; 6 | import org.popcraft.chunky.platform.util.Location; 7 | 8 | import java.util.EnumSet; 9 | import java.util.UUID; 10 | 11 | import static org.popcraft.chunky.util.Translator.translateKey; 12 | 13 | public class FabricPlayer extends FabricSender implements Player { 14 | private final ServerPlayer player; 15 | 16 | public FabricPlayer(final ServerPlayer player) { 17 | super(player.createCommandSourceStack()); 18 | this.player = player; 19 | } 20 | 21 | @Override 22 | public boolean isPlayer() { 23 | return true; 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return player.getName().toString(); 29 | } 30 | 31 | @Override 32 | public World getWorld() { 33 | return new FabricWorld(player.serverLevel()); 34 | } 35 | 36 | @Override 37 | public Location getLocation() { 38 | return new Location(getWorld(), player.getX(), player.getY(), player.getZ(), player.getXRot(), player.getYRot()); 39 | } 40 | 41 | @Override 42 | public void sendMessage(final String key, final boolean prefixed, final Object... args) { 43 | player.sendSystemMessage(formatColored(translateKey(key, prefixed, args))); 44 | } 45 | 46 | @Override 47 | public UUID getUUID() { 48 | return player.getUUID(); 49 | } 50 | 51 | @Override 52 | public void teleport(final Location location) { 53 | player.teleportTo(((FabricWorld) location.getWorld()).getWorld(), location.getX(), location.getY(), location.getZ(), EnumSet.noneOf(Relative.class), location.getYaw(), location.getPitch(), true); 54 | } 55 | 56 | @Override 57 | public void sendActionBar(final String key) { 58 | player.displayClientMessage(formatColored(translateKey(key, false)), true); 59 | } 60 | 61 | private Component formatColored(final String message) { 62 | return Component.nullToEmpty(message.replaceAll("&(?=[0-9a-fk-orA-FK-OR])", "§")); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /fabric/src/main/java/org/popcraft/chunky/platform/FabricSender.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import me.lucko.fabric.api.permissions.v0.Permissions; 4 | import net.minecraft.commands.CommandSourceStack; 5 | import net.minecraft.network.chat.Component; 6 | import net.minecraft.server.level.ServerPlayer; 7 | import net.minecraft.world.phys.Vec2; 8 | import net.minecraft.world.phys.Vec3; 9 | import org.popcraft.chunky.platform.util.Location; 10 | 11 | import static org.popcraft.chunky.util.Translator.translateKey; 12 | 13 | public class FabricSender implements Sender { 14 | private static final boolean HAS_PERMISSIONS; 15 | 16 | static { 17 | boolean hasPermissions; 18 | try { 19 | Class.forName("me.lucko.fabric.api.permissions.v0.Permissions"); 20 | hasPermissions = true; 21 | } catch (ClassNotFoundException e) { 22 | hasPermissions = false; 23 | } 24 | HAS_PERMISSIONS = hasPermissions; 25 | } 26 | 27 | private final CommandSourceStack source; 28 | 29 | public FabricSender(final CommandSourceStack source) { 30 | this.source = source; 31 | } 32 | 33 | @Override 34 | public boolean isPlayer() { 35 | return source.getEntity() instanceof ServerPlayer; 36 | } 37 | 38 | @Override 39 | public String getName() { 40 | return source.getTextName(); 41 | } 42 | 43 | @Override 44 | public World getWorld() { 45 | return new FabricWorld(source.getLevel()); 46 | } 47 | 48 | @Override 49 | public Location getLocation() { 50 | final Vec3 pos = source.getPosition(); 51 | final Vec2 rot = source.getRotation(); 52 | return new Location(getWorld(), pos.x(), pos.y(), pos.z(), rot.x, rot.y); 53 | } 54 | 55 | @Override 56 | public boolean hasPermission(final String permission) { 57 | return hasPermission(permission, false); 58 | } 59 | 60 | public boolean hasPermission(final String permission, final boolean defaultOp) { 61 | if (HAS_PERMISSIONS) { 62 | if (defaultOp) { 63 | return Permissions.check(source, permission, 2); 64 | } else { 65 | return Permissions.check(source, permission, false); 66 | } 67 | } else { 68 | return source.hasPermission(2); 69 | } 70 | } 71 | 72 | @Override 73 | public void sendMessage(final String key, final boolean prefixed, final Object... args) { 74 | source.sendSuccess(() -> Component.nullToEmpty(translateKey(key, prefixed, args).replaceAll("&[0-9a-fk-orA-FK-OR]", "")), false); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /fabric/src/main/resources/assets/chunky/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pop4959/Chunky/ed0094c13ff1b5ae3748d86c4c29ba89122d4583/fabric/src/main/resources/assets/chunky/icon.png -------------------------------------------------------------------------------- /fabric/src/main/resources/chunky.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "org.popcraft.chunky.mixin", 5 | "compatibilityLevel": "JAVA_17", 6 | "mixins": [ 7 | "ChunkMapMixin", 8 | "MinecraftServerMixin", 9 | "ServerChunkCacheMixin" 10 | ], 11 | "client": [ 12 | ], 13 | "injectors": { 14 | "defaultRequire": 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "${id}", 4 | "version": "${version}", 5 | "name": "${name}", 6 | "description": "${description}", 7 | "authors": [ 8 | "${author}" 9 | ], 10 | "contact": { 11 | "website": "https://www.curseforge.com/minecraft/mc-mods/chunky-pregenerator", 12 | "homepage": "https://www.curseforge.com/minecraft/mc-mods/chunky-pregenerator", 13 | "repo": "${github}", 14 | "sources": "${github}", 15 | "issues": "${github}/issues" 16 | }, 17 | "license": "GPLv3", 18 | "icon": "assets/chunky/icon.png", 19 | "environment": "*", 20 | "entrypoints": { 21 | "main": [ 22 | "org.popcraft.chunky.ChunkyFabric" 23 | ] 24 | }, 25 | "mixins": [ 26 | "chunky.mixins.json" 27 | ], 28 | "depends": { 29 | "fabricloader": ">=0.16.10", 30 | "fabric": "*", 31 | "minecraft": ">=1.21.5", 32 | "java": ">=21" 33 | }, 34 | "custom": { 35 | "modmenu": { 36 | "links": { 37 | "modmenu.discord": "https://discord.gg/ZwVJukcNQG" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /folia/build.gradle.kts: -------------------------------------------------------------------------------- 1 | repositories { 2 | maven("https://repo.papermc.io/repository/maven-public/") 3 | } 4 | 5 | dependencies { 6 | compileOnly(group = "dev.folia", name = "folia-api", version = "1.20.2-R0.1-SNAPSHOT") 7 | } 8 | -------------------------------------------------------------------------------- /folia/src/main/java/org/popcraft/chunky/platform/Folia.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import io.papermc.paper.threadedregions.RegionizedServerInitEvent; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.Location; 6 | import org.bukkit.entity.Entity; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.Listener; 9 | import org.bukkit.plugin.Plugin; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public final class Folia { 13 | private static final boolean CONFIG_EXISTS = classExists("io.papermc.paper.threadedregions.RegionizedServer") || classExists("io.papermc.paper.threadedregions.RegionizedServerInitEvent"); 14 | 15 | private Folia() { 16 | } 17 | 18 | public static boolean isFolia() { 19 | return CONFIG_EXISTS; 20 | } 21 | 22 | public static void schedule(final Plugin plugin, final Location location, final Runnable runnable) { 23 | Bukkit.getServer().getRegionScheduler().execute(plugin, location, runnable); 24 | } 25 | 26 | public static void schedule(final Plugin plugin, final Entity entity, final Runnable runnable, final long delay) { 27 | entity.getScheduler().execute(plugin, runnable, () -> {}, delay); 28 | } 29 | 30 | public static void scheduleFixed(final Plugin plugin, final Location location, final Runnable runnable, final long delay, final long period) { 31 | Bukkit.getServer().getRegionScheduler().runAtFixedRate(plugin, location, ignored -> runnable.run(), delay, period); 32 | } 33 | 34 | public static void scheduleFixedGlobal(final Plugin plugin, final Runnable runnable, final long delay, final long period) { 35 | Bukkit.getServer().getGlobalRegionScheduler().runAtFixedRate(plugin, ignored -> runnable.run(), delay, period); 36 | } 37 | 38 | public static void cancelTasks(final Plugin plugin) { 39 | Bukkit.getServer().getGlobalRegionScheduler().cancelTasks(plugin); 40 | } 41 | 42 | public static boolean isTickThread(final @NotNull Location location) { 43 | return Bukkit.getServer().isOwnedByCurrentRegion(location); 44 | } 45 | 46 | public static void onServerInit(final Plugin plugin, final Runnable runnable) { 47 | Bukkit.getPluginManager().registerEvents(new Listener() { 48 | @EventHandler 49 | public void onRegionisedServerInit(final RegionizedServerInitEvent event) { 50 | runnable.run(); 51 | } 52 | }, plugin); 53 | } 54 | 55 | private static boolean classExists(final String clazz) { 56 | try { 57 | Class.forName(clazz); 58 | return true; 59 | } catch (ClassNotFoundException e) { 60 | return false; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /forge/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("dev.architectury.loom") version "1.10-SNAPSHOT" 3 | } 4 | 5 | val shade: Configuration by configurations.creating 6 | 7 | dependencies { 8 | minecraft(group = "com.mojang", name = "minecraft", version = "1.21.5") 9 | mappings(loom.officialMojangMappings()) 10 | forge(group = "net.minecraftforge", name = "forge", version = "1.21.5-55.0.0") 11 | implementation(project(":chunky-common")) 12 | shade(project(":chunky-common")) 13 | } 14 | 15 | tasks { 16 | processResources { 17 | filesMatching("META-INF/mods.toml") { 18 | expand( 19 | "github" to project.property("github"), 20 | "id" to rootProject.name, 21 | "version" to project.version, 22 | "name" to project.property("artifactName"), 23 | "author" to project.property("author"), 24 | "description" to project.property("description") 25 | ) 26 | } 27 | } 28 | jar { 29 | manifest { 30 | attributes( 31 | mapOf( 32 | "Implementation-Title" to rootProject.name, 33 | "Implementation-Version" to project.version, 34 | "Implementation-Vendor" to project.property("author") 35 | ) 36 | ) 37 | } 38 | } 39 | shadowJar { 40 | configurations = listOf(shade) 41 | archiveClassifier.set(null as String?) 42 | archiveFileName.set("${project.property("artifactName")}-Forge-${project.version}.jar") 43 | } 44 | remapJar { 45 | enabled = false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /forge/gradle.properties: -------------------------------------------------------------------------------- 1 | loom.platform=forge 2 | -------------------------------------------------------------------------------- /forge/src/main/java/org/popcraft/chunky/command/suggestion/PatternSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | import org.popcraft.chunky.iterator.PatternType; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class PatternSuggestionProvider implements SuggestionProvider { 14 | @Override 15 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 16 | try { 17 | final String input = context.getArgument(CommandLiteral.PATTERN, String.class); 18 | PatternType.ALL.forEach(pattern -> { 19 | if (pattern.contains(input.toLowerCase())) { 20 | builder.suggest(pattern); 21 | } 22 | }); 23 | } catch (IllegalArgumentException e) { 24 | PatternType.ALL.forEach(builder::suggest); 25 | } 26 | return builder.buildFuture(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /forge/src/main/java/org/popcraft/chunky/command/suggestion/ShapeSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | import org.popcraft.chunky.shape.ShapeType; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class ShapeSuggestionProvider implements SuggestionProvider { 14 | @Override 15 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 16 | try { 17 | final String input = context.getArgument(CommandLiteral.SHAPE, String.class); 18 | ShapeType.all().forEach(shape -> { 19 | if (shape.contains(input.toLowerCase())) { 20 | builder.suggest(shape); 21 | } 22 | }); 23 | } catch (IllegalArgumentException e) { 24 | ShapeType.all().forEach(builder::suggest); 25 | } 26 | return builder.buildFuture(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /forge/src/main/java/org/popcraft/chunky/command/suggestion/SuggestionProviders.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.suggestion.SuggestionProvider; 4 | import net.minecraft.commands.CommandSourceStack; 5 | 6 | public final class SuggestionProviders { 7 | public static final SuggestionProvider PATTERNS; 8 | public static final SuggestionProvider SHAPES; 9 | public static final SuggestionProvider TRIM_MODES; 10 | 11 | static { 12 | PATTERNS = new PatternSuggestionProvider(); 13 | SHAPES = new ShapeSuggestionProvider(); 14 | TRIM_MODES = new TrimModeSuggestionProvider(); 15 | } 16 | 17 | private SuggestionProviders() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /forge/src/main/java/org/popcraft/chunky/command/suggestion/TrimModeSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class TrimModeSuggestionProvider implements SuggestionProvider { 14 | private static final List TRIM_MODES = List.of("inside", "outside"); 15 | 16 | @Override 17 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 18 | try { 19 | final String input = context.getArgument(CommandLiteral.TRIM_MODE, String.class); 20 | TRIM_MODES.forEach(shape -> { 21 | if (shape.contains(input.toLowerCase())) { 22 | builder.suggest(shape); 23 | } 24 | }); 25 | } catch (IllegalArgumentException e) { 26 | TRIM_MODES.forEach(builder::suggest); 27 | } 28 | return builder.buildFuture(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /forge/src/main/java/org/popcraft/chunky/listeners/bossbar/BossBarTaskFinishListener.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.listeners.bossbar; 2 | 3 | import net.minecraft.resources.ResourceLocation; 4 | import net.minecraft.server.level.ServerBossEvent; 5 | import org.popcraft.chunky.GenerationTask; 6 | import org.popcraft.chunky.event.task.GenerationTaskFinishEvent; 7 | import org.popcraft.chunky.platform.World; 8 | 9 | import java.util.Map; 10 | import java.util.function.Consumer; 11 | 12 | public class BossBarTaskFinishListener implements Consumer { 13 | private final Map bossBars; 14 | 15 | public BossBarTaskFinishListener(final Map bossBars) { 16 | this.bossBars = bossBars; 17 | } 18 | 19 | @Override 20 | public void accept(final GenerationTaskFinishEvent event) { 21 | final GenerationTask task = event.generationTask(); 22 | final World world = task.getSelection().world(); 23 | final ResourceLocation worldIdentifier = ResourceLocation.tryParse(world.getKey()); 24 | if (worldIdentifier == null) { 25 | return; 26 | } 27 | final ServerBossEvent bossBar = bossBars.get(worldIdentifier); 28 | if (bossBar != null) { 29 | bossBar.removeAllPlayers(); 30 | bossBars.remove(worldIdentifier); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /forge/src/main/java/org/popcraft/chunky/platform/ForgeBorder.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.minecraft.world.level.border.WorldBorder; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | import org.popcraft.chunky.shape.ShapeType; 6 | 7 | public class ForgeBorder implements Border { 8 | private final WorldBorder worldBorder; 9 | 10 | public ForgeBorder(final WorldBorder worldBorder) { 11 | this.worldBorder = worldBorder; 12 | } 13 | 14 | @Override 15 | public Vector2 getCenter() { 16 | return Vector2.of(worldBorder.getCenterX(), worldBorder.getCenterZ()); 17 | } 18 | 19 | @Override 20 | public double getRadiusX() { 21 | return worldBorder.getSize() / 2d; 22 | } 23 | 24 | @Override 25 | public double getRadiusZ() { 26 | return worldBorder.getSize() / 2d; 27 | } 28 | 29 | @Override 30 | public String getShape() { 31 | return ShapeType.SQUARE; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /forge/src/main/java/org/popcraft/chunky/platform/ForgePlayer.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.minecraft.network.chat.Component; 4 | import net.minecraft.server.level.ServerPlayer; 5 | import net.minecraft.world.entity.Relative; 6 | import org.popcraft.chunky.platform.util.Location; 7 | 8 | import java.util.EnumSet; 9 | import java.util.UUID; 10 | 11 | import static org.popcraft.chunky.util.Translator.translateKey; 12 | 13 | public class ForgePlayer extends ForgeSender implements Player { 14 | private final ServerPlayer player; 15 | 16 | public ForgePlayer(final ServerPlayer player) { 17 | super(player.createCommandSourceStack()); 18 | this.player = player; 19 | } 20 | 21 | @Override 22 | public boolean isPlayer() { 23 | return true; 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return player.getName().toString(); 29 | } 30 | 31 | @Override 32 | public World getWorld() { 33 | return new ForgeWorld(player.serverLevel()); 34 | } 35 | 36 | @Override 37 | public Location getLocation() { 38 | return new Location(getWorld(), player.getX(), player.getY(), player.getZ(), player.getXRot(), player.getYRot()); 39 | } 40 | 41 | @Override 42 | public void sendMessage(final String key, final boolean prefixed, final Object... args) { 43 | player.sendSystemMessage(formatColored(translateKey(key, prefixed, args))); 44 | } 45 | 46 | @Override 47 | public UUID getUUID() { 48 | return player.getUUID(); 49 | } 50 | 51 | @Override 52 | public void teleport(final Location location) { 53 | player.teleportTo(((ForgeWorld) location.getWorld()).getWorld(), location.getX(), location.getY(), location.getZ(), EnumSet.noneOf(Relative.class), location.getYaw(), location.getPitch(), true); 54 | } 55 | 56 | @Override 57 | public void sendActionBar(final String key) { 58 | player.displayClientMessage(formatColored(translateKey(key, false)), true); 59 | } 60 | 61 | private Component formatColored(final String message) { 62 | return Component.nullToEmpty(message.replaceAll("&(?=[0-9a-fk-orA-FK-OR])", "§")); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /forge/src/main/java/org/popcraft/chunky/platform/ForgeSender.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.minecraft.commands.CommandSourceStack; 4 | import net.minecraft.network.chat.Component; 5 | import net.minecraft.server.level.ServerPlayer; 6 | import net.minecraft.world.phys.Vec2; 7 | import net.minecraft.world.phys.Vec3; 8 | import org.popcraft.chunky.platform.util.Location; 9 | 10 | import static org.popcraft.chunky.util.Translator.translateKey; 11 | 12 | public class ForgeSender implements Sender { 13 | private final CommandSourceStack source; 14 | 15 | public ForgeSender(final CommandSourceStack source) { 16 | this.source = source; 17 | } 18 | 19 | @Override 20 | public boolean isPlayer() { 21 | return source.getEntity() instanceof ServerPlayer; 22 | } 23 | 24 | @Override 25 | public String getName() { 26 | return source.getTextName(); 27 | } 28 | 29 | @Override 30 | public World getWorld() { 31 | return new ForgeWorld(source.getLevel()); 32 | } 33 | 34 | @Override 35 | public Location getLocation() { 36 | final Vec3 pos = source.getPosition(); 37 | final Vec2 rot = source.getRotation(); 38 | return new Location(getWorld(), pos.x(), pos.y(), pos.z(), rot.x, rot.y); 39 | } 40 | 41 | @Override 42 | public boolean hasPermission(final String permission) { 43 | return source.hasPermission(2); 44 | } 45 | 46 | @Override 47 | public void sendMessage(final String key, final boolean prefixed, final Object... args) { 48 | source.sendSuccess(() -> Component.nullToEmpty(translateKey(key, prefixed, args).replaceAll("&[0-9a-fk-orA-FK-OR]", "")), false); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /forge/src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | # MinecraftServer 2 | public net.minecraft.server.MinecraftServer emptyTicks 3 | # ChunkMap 4 | public net.minecraft.server.level.ChunkMap readChunk(Lnet/minecraft/world/level/ChunkPos;)Ljava/util/concurrent/CompletableFuture; 5 | public net.minecraft.server.level.ChunkMap getVisibleChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder; 6 | public net.minecraft.server.level.ChunkMap pendingUnloads 7 | # ServerChunkCache 8 | public net.minecraft.server.level.ServerChunkCache runDistanceManagerUpdates()Z 9 | -------------------------------------------------------------------------------- /forge/src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="[38,)" 3 | license="GNU GPLv3" 4 | issueTrackerURL="${github}/issues" 5 | [[mods]] 6 | modId="${id}" 7 | version="${version}" 8 | displayName="${name}" 9 | displayURL="${github}" 10 | logoFile="icon.png" 11 | authors="${author}" 12 | description="${description}" 13 | [[dependencies.chunky]] 14 | modId="forge" 15 | mandatory=true 16 | versionRange="[55,)" 17 | ordering="NONE" 18 | side="BOTH" 19 | [[dependencies.chunky]] 20 | modId="minecraft" 21 | mandatory=true 22 | versionRange="[1.21.5,1.22)" 23 | ordering="NONE" 24 | side="BOTH" 25 | -------------------------------------------------------------------------------- /forge/src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pop4959/Chunky/ed0094c13ff1b5ae3748d86c4c29ba89122d4583/forge/src/main/resources/icon.png -------------------------------------------------------------------------------- /forge/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "chunky resources", 4 | "pack_format": 8 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2G 2 | group=org.popcraft 3 | artifactName=Chunky 4 | version=1.4 5 | description=Pre-generates chunks, quickly, efficiently, and safely 6 | github=https://github.com/pop4959/Chunky 7 | author=pop4959 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pop4959/Chunky/ed0094c13ff1b5ae3748d86c4c29ba89122d4583/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /nbt/build.gradle.kts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pop4959/Chunky/ed0094c13ff1b5ae3748d86c4c29ba89122d4583/nbt/build.gradle.kts -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/ByteArrayTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | import java.util.Arrays; 7 | 8 | public class ByteArrayTag extends Tag { 9 | private byte[] value; 10 | 11 | protected ByteArrayTag(final String name) { 12 | super(name); 13 | } 14 | 15 | public ByteArrayTag(final String name, final byte[] value) { 16 | super(name); 17 | this.value = value; 18 | } 19 | 20 | @Override 21 | public void read(final DataInput input) throws IOException { 22 | final int size = input.readInt(); 23 | this.value = new byte[size]; 24 | input.readFully(value); 25 | } 26 | 27 | @Override 28 | public void skip(final DataInput input) throws IOException { 29 | final int size = input.readInt(); 30 | input.skipBytes(size); 31 | } 32 | 33 | @Override 34 | public void write(final DataOutput output) throws IOException { 35 | output.writeInt(value.length); 36 | output.write(value); 37 | } 38 | 39 | @Override 40 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 41 | skip(input); 42 | return null; 43 | } 44 | 45 | @Override 46 | public byte type() { 47 | return TagType.BYTE_ARRAY; 48 | } 49 | 50 | @Override 51 | public String typeName() { 52 | return "TAG_Byte_Array"; 53 | } 54 | 55 | @Override 56 | public String print(final int level) { 57 | return "%s%s('%s'): %s".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, Arrays.toString(value)); 58 | } 59 | 60 | public byte[] value() { 61 | return value; 62 | } 63 | 64 | public void value(final byte[] value) { 65 | this.value = value; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/ByteTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public class ByteTag extends Tag { 8 | private byte value; 9 | 10 | protected ByteTag(final String name) { 11 | super(name); 12 | } 13 | 14 | public ByteTag(final String name, final byte value) { 15 | super(name); 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public void read(final DataInput input) throws IOException { 21 | this.value = input.readByte(); 22 | } 23 | 24 | @Override 25 | public void skip(final DataInput input) throws IOException { 26 | input.skipBytes(1); 27 | } 28 | 29 | @Override 30 | public void write(final DataOutput output) throws IOException { 31 | output.writeByte(value); 32 | } 33 | 34 | @Override 35 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 36 | skip(input); 37 | return null; 38 | } 39 | 40 | @Override 41 | public byte type() { 42 | return TagType.BYTE; 43 | } 44 | 45 | @Override 46 | public String typeName() { 47 | return "TAG_Byte"; 48 | } 49 | 50 | @Override 51 | public String print(final int level) { 52 | return "%s%s('%s'): %d".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, value); 53 | } 54 | 55 | public byte value() { 56 | return value; 57 | } 58 | 59 | public void value(final byte value) { 60 | this.value = value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/DoubleTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public class DoubleTag extends Tag { 8 | private double value; 9 | 10 | protected DoubleTag(final String name) { 11 | super(name); 12 | } 13 | 14 | public DoubleTag(final String name, final double value) { 15 | super(name); 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public void read(final DataInput input) throws IOException { 21 | this.value = input.readDouble(); 22 | } 23 | 24 | @Override 25 | public void skip(final DataInput input) throws IOException { 26 | input.skipBytes(8); 27 | } 28 | 29 | @Override 30 | public void write(final DataOutput output) throws IOException { 31 | output.writeDouble(value); 32 | } 33 | 34 | @Override 35 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 36 | skip(input); 37 | return null; 38 | } 39 | 40 | @Override 41 | public byte type() { 42 | return TagType.DOUBLE; 43 | } 44 | 45 | @Override 46 | public String typeName() { 47 | return "TAG_Double"; 48 | } 49 | 50 | @Override 51 | public String print(final int level) { 52 | return "%s%s('%s'): %f".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, value); 53 | } 54 | 55 | public double value() { 56 | return value; 57 | } 58 | 59 | public void value(final double value) { 60 | this.value = value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/EndTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public class EndTag extends Tag { 8 | public EndTag() { 9 | super(""); 10 | } 11 | 12 | @Override 13 | public void read(final DataInput input) throws IOException { 14 | // No data 15 | } 16 | 17 | @Override 18 | public void skip(final DataInput input) throws IOException { 19 | // No data 20 | } 21 | 22 | @Override 23 | public void write(final DataOutput output) throws IOException { 24 | // No data 25 | } 26 | 27 | @Override 28 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 29 | skip(input); 30 | return null; 31 | } 32 | 33 | @Override 34 | public byte type() { 35 | return TagType.END; 36 | } 37 | 38 | @Override 39 | public String typeName() { 40 | return "TAG_End"; 41 | } 42 | 43 | @Override 44 | public String print(final int level) { 45 | return ""; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/FloatTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public class FloatTag extends Tag { 8 | private float value; 9 | 10 | protected FloatTag(final String name) { 11 | super(name); 12 | } 13 | 14 | public FloatTag(final String name, final float value) { 15 | super(name); 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public void read(final DataInput input) throws IOException { 21 | this.value = input.readFloat(); 22 | } 23 | 24 | @Override 25 | public void skip(final DataInput input) throws IOException { 26 | input.skipBytes(4); 27 | } 28 | 29 | @Override 30 | public void write(final DataOutput output) throws IOException { 31 | output.writeFloat(value); 32 | } 33 | 34 | @Override 35 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 36 | skip(input); 37 | return null; 38 | } 39 | 40 | @Override 41 | public byte type() { 42 | return TagType.FLOAT; 43 | } 44 | 45 | @Override 46 | public String typeName() { 47 | return "TAG_Float"; 48 | } 49 | 50 | @Override 51 | public String print(final int level) { 52 | return "%s%s('%s'): %f".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, value); 53 | } 54 | 55 | public float value() { 56 | return value; 57 | } 58 | 59 | public void value(final float value) { 60 | this.value = value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/IntArrayTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | import java.util.Arrays; 7 | 8 | public class IntArrayTag extends Tag { 9 | private int[] value; 10 | 11 | protected IntArrayTag(final String name) { 12 | super(name); 13 | } 14 | 15 | public IntArrayTag(final String name, final int[] value) { 16 | super(name); 17 | this.value = value; 18 | } 19 | 20 | @Override 21 | public void read(final DataInput input) throws IOException { 22 | final int size = input.readInt(); 23 | this.value = new int[size]; 24 | for (int i = 0; i < size; ++i) { 25 | value[i] = input.readInt(); 26 | } 27 | } 28 | 29 | @Override 30 | public void skip(final DataInput input) throws IOException { 31 | final int size = input.readInt(); 32 | input.skipBytes(4 * size); 33 | } 34 | 35 | @Override 36 | public void write(final DataOutput output) throws IOException { 37 | final int size = value.length; 38 | output.writeInt(size); 39 | for (int i : value) { 40 | output.writeInt(i); 41 | } 42 | } 43 | 44 | @Override 45 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 46 | skip(input); 47 | return null; 48 | } 49 | 50 | @Override 51 | public byte type() { 52 | return TagType.INT_ARRAY; 53 | } 54 | 55 | @Override 56 | public String typeName() { 57 | return "TAG_Int_Array"; 58 | } 59 | 60 | @Override 61 | public String print(final int level) { 62 | return "%s%s('%s'): %s".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, Arrays.toString(value)); 63 | } 64 | 65 | public int[] value() { 66 | return value; 67 | } 68 | 69 | public void value(final int[] value) { 70 | this.value = value; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/IntTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public class IntTag extends Tag { 8 | private int value; 9 | 10 | protected IntTag(final String name) { 11 | super(name); 12 | } 13 | 14 | public IntTag(final String name, final int value) { 15 | super(name); 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public void read(final DataInput input) throws IOException { 21 | this.value = input.readInt(); 22 | } 23 | 24 | @Override 25 | public void skip(final DataInput input) throws IOException { 26 | input.skipBytes(4); 27 | } 28 | 29 | @Override 30 | public void write(final DataOutput output) throws IOException { 31 | output.writeInt(value); 32 | } 33 | 34 | @Override 35 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 36 | skip(input); 37 | return null; 38 | } 39 | 40 | @Override 41 | public byte type() { 42 | return TagType.INT; 43 | } 44 | 45 | @Override 46 | public String typeName() { 47 | return "TAG_Int"; 48 | } 49 | 50 | @Override 51 | public String print(final int level) { 52 | return "%s%s('%s'): %d".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, value); 53 | } 54 | 55 | public int value() { 56 | return value; 57 | } 58 | 59 | public void value(final int value) { 60 | this.value = value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/ListTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class ListTag extends Tag { 10 | private byte type; 11 | private List value = new ArrayList<>(); 12 | 13 | protected ListTag(final String name) { 14 | super(name); 15 | } 16 | 17 | public ListTag(final String name, final byte type, final List value) { 18 | super(name); 19 | this.type = type; 20 | this.value = value; 21 | } 22 | 23 | @Override 24 | public void read(final DataInput input) throws IOException { 25 | this.type = input.readByte(); 26 | final int size = input.readInt(); 27 | this.value = new ArrayList<>(); 28 | for (int i = 0; i < size; ++i) { 29 | final Tag tag = Tag.create(type, ""); 30 | tag.read(input); 31 | value.add(tag); 32 | } 33 | } 34 | 35 | @Override 36 | public void skip(final DataInput input) throws IOException { 37 | this.type = input.readByte(); 38 | final int size = input.readInt(); 39 | final Tag tag = Tag.create(type, ""); 40 | for (int i = 0; i < size; ++i) { 41 | tag.skip(input); 42 | } 43 | } 44 | 45 | @Override 46 | public void write(final DataOutput output) throws IOException { 47 | output.writeByte(type); 48 | final int size = value.size(); 49 | output.writeInt(size); 50 | for (final Tag tag : value) { 51 | tag.write(output); 52 | } 53 | } 54 | 55 | @Override 56 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 57 | skip(input); 58 | return null; 59 | } 60 | 61 | @Override 62 | public byte type() { 63 | return TagType.LIST; 64 | } 65 | 66 | @Override 67 | public String typeName() { 68 | return "TAG_List"; 69 | } 70 | 71 | @Override 72 | public String print(final int level) { 73 | final int size = value.size(); 74 | final String entry = size == 1 ? "entry" : "entries"; 75 | final String indent = " ".repeat(level * Tag.INDENT); 76 | final StringBuilder listBuilder = new StringBuilder("%s%s('%s'): %d %s".formatted(indent, typeName(), name, size, entry)); 77 | listBuilder.append('\n').append(indent).append("{\n"); 78 | for (final Tag tag : value) { 79 | listBuilder.append(tag.print(level + 1)).append('\n'); 80 | } 81 | listBuilder.append(indent).append('}'); 82 | return listBuilder.toString(); 83 | } 84 | 85 | public List value() { 86 | return value; 87 | } 88 | 89 | public void value(final List value) { 90 | if (!value.isEmpty()) { 91 | this.type = value.get(0).type(); 92 | } 93 | this.value = value; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/LongArrayTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | import java.util.Arrays; 7 | 8 | public class LongArrayTag extends Tag { 9 | private long[] value; 10 | 11 | protected LongArrayTag(final String name) { 12 | super(name); 13 | } 14 | 15 | public LongArrayTag(final String name, final long[] value) { 16 | super(name); 17 | this.value = value; 18 | } 19 | 20 | @Override 21 | public void read(final DataInput input) throws IOException { 22 | final int size = input.readInt(); 23 | this.value = new long[size]; 24 | for (int i = 0; i < size; ++i) { 25 | value[i] = input.readLong(); 26 | } 27 | } 28 | 29 | @Override 30 | public void skip(final DataInput input) throws IOException { 31 | final int size = input.readInt(); 32 | input.skipBytes(8 * size); 33 | } 34 | 35 | @Override 36 | public void write(final DataOutput output) throws IOException { 37 | final int size = value.length; 38 | output.writeLong(size); 39 | for (long i : value) { 40 | output.writeLong(i); 41 | } 42 | } 43 | 44 | @Override 45 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 46 | skip(input); 47 | return null; 48 | } 49 | 50 | @Override 51 | public byte type() { 52 | return TagType.LONG_ARRAY; 53 | } 54 | 55 | @Override 56 | public String typeName() { 57 | return "TAG_Long_Array"; 58 | } 59 | 60 | @Override 61 | public String print(final int level) { 62 | return "%s%s('%s'): %s".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, Arrays.toString(value)); 63 | } 64 | 65 | public long[] value() { 66 | return value; 67 | } 68 | 69 | public void value(final long[] value) { 70 | this.value = value; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/LongTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public class LongTag extends Tag { 8 | private long value; 9 | 10 | protected LongTag(final String name) { 11 | super(name); 12 | } 13 | 14 | public LongTag(final String name, final long value) { 15 | super(name); 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public void read(final DataInput input) throws IOException { 21 | this.value = input.readLong(); 22 | } 23 | 24 | @Override 25 | public void skip(final DataInput input) throws IOException { 26 | input.skipBytes(8); 27 | } 28 | 29 | @Override 30 | public void write(final DataOutput output) throws IOException { 31 | output.writeLong(value); 32 | } 33 | 34 | @Override 35 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 36 | skip(input); 37 | return null; 38 | } 39 | 40 | @Override 41 | public byte type() { 42 | return TagType.LONG; 43 | } 44 | 45 | @Override 46 | public String typeName() { 47 | return "TAG_Long"; 48 | } 49 | 50 | @Override 51 | public String print(final int level) { 52 | return "%s%s('%s'): %d".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, value); 53 | } 54 | 55 | public long value() { 56 | return value; 57 | } 58 | 59 | public void value(final long value) { 60 | this.value = value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/NBT.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataInputStream; 5 | import java.io.DataOutput; 6 | import java.io.DataOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.util.zip.GZIPInputStream; 11 | import java.util.zip.GZIPOutputStream; 12 | 13 | public final class NBT { 14 | private NBT() { 15 | } 16 | 17 | public static Tag load(final InputStream inputStream) throws IOException { 18 | final DataInput dataInput = new DataInputStream(inputStream); 19 | return Tag.load(dataInput); 20 | } 21 | 22 | public static void save(final OutputStream outputStream, final Tag tag) throws IOException { 23 | final DataOutput dataOutput = new DataOutputStream(outputStream); 24 | Tag.save(dataOutput, tag); 25 | } 26 | 27 | public static Tag loadCompressed(final InputStream inputStream) throws IOException { 28 | try (final GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) { 29 | final DataInput dataInput = new DataInputStream(gzipInputStream); 30 | return Tag.load(dataInput); 31 | } 32 | } 33 | 34 | public static void saveCompressed(final OutputStream outputStream, final Tag tag) throws IOException { 35 | try (final GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream)) { 36 | final DataOutput dataOutput = new DataOutputStream(gzipOutputStream); 37 | Tag.save(dataOutput, tag); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/ShortTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public class ShortTag extends Tag { 8 | private short value; 9 | 10 | protected ShortTag(final String name) { 11 | super(name); 12 | } 13 | 14 | public ShortTag(final String name, final short value) { 15 | super(name); 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public void read(final DataInput input) throws IOException { 21 | this.value = input.readShort(); 22 | } 23 | 24 | @Override 25 | public void skip(final DataInput input) throws IOException { 26 | input.skipBytes(2); 27 | } 28 | 29 | @Override 30 | public void write(final DataOutput output) throws IOException { 31 | output.writeShort(value); 32 | } 33 | 34 | @Override 35 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 36 | skip(input); 37 | return null; 38 | } 39 | 40 | @Override 41 | public byte type() { 42 | return TagType.SHORT; 43 | } 44 | 45 | @Override 46 | public String typeName() { 47 | return "TAG_Short"; 48 | } 49 | 50 | @Override 51 | public String print(final int level) { 52 | return "%s%s('%s'): %d".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, value); 53 | } 54 | 55 | public short value() { 56 | return value; 57 | } 58 | 59 | public void value(final short value) { 60 | this.value = value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/StringTag.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | public class StringTag extends Tag { 8 | private String value; 9 | 10 | protected StringTag(final String name) { 11 | super(name); 12 | } 13 | 14 | public StringTag(final String name, final String value) { 15 | super(name); 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public void read(final DataInput input) throws IOException { 21 | this.value = input.readUTF(); 22 | } 23 | 24 | @Override 25 | public void skip(final DataInput input) throws IOException { 26 | final int size = input.readUnsignedShort(); 27 | input.skipBytes(size); 28 | } 29 | 30 | @Override 31 | public void write(final DataOutput output) throws IOException { 32 | output.writeUTF(value); 33 | } 34 | 35 | @Override 36 | public Tag search(final DataInput input, final byte type, final String name) throws IOException { 37 | skip(input); 38 | return null; 39 | } 40 | 41 | @Override 42 | public byte type() { 43 | return TagType.STRING; 44 | } 45 | 46 | @Override 47 | public String typeName() { 48 | return "TAG_String"; 49 | } 50 | 51 | @Override 52 | public String print(final int level) { 53 | return "%s%s('%s'): '%s'".formatted(" ".repeat(level * Tag.INDENT), typeName(), name, value); 54 | } 55 | 56 | public String value() { 57 | return value; 58 | } 59 | 60 | public void value(final String value) { 61 | this.value = value; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/TagType.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt; 2 | 3 | public final class TagType { 4 | public static final byte END = 0; 5 | public static final byte BYTE = 1; 6 | public static final byte SHORT = 2; 7 | public static final byte INT = 3; 8 | public static final byte LONG = 4; 9 | public static final byte FLOAT = 5; 10 | public static final byte DOUBLE = 6; 11 | public static final byte BYTE_ARRAY = 7; 12 | public static final byte STRING = 8; 13 | public static final byte LIST = 9; 14 | public static final byte COMPOUND = 10; 15 | public static final byte INT_ARRAY = 11; 16 | public static final byte LONG_ARRAY = 12; 17 | 18 | private TagType() { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/util/Chunk.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt.util; 2 | 3 | import org.popcraft.chunky.nbt.Tag; 4 | 5 | public final class Chunk { 6 | private int x; 7 | private int z; 8 | private Tag data; 9 | private long lastModified; 10 | 11 | public Chunk(final int x, final int z, final Tag data, final long lastModified) { 12 | this.x = x; 13 | this.z = z; 14 | this.data = data; 15 | this.lastModified = lastModified; 16 | } 17 | 18 | public Tag getData() { 19 | return data; 20 | } 21 | 22 | public int getX() { 23 | return x; 24 | } 25 | 26 | public void setX(int x) { 27 | this.x = x; 28 | } 29 | 30 | public int getZ() { 31 | return z; 32 | } 33 | 34 | public void setZ(int z) { 35 | this.z = z; 36 | } 37 | 38 | public void setData(Tag data) { 39 | this.data = data; 40 | } 41 | 42 | public long getLastModified() { 43 | return lastModified; 44 | } 45 | 46 | public void setLastModified(final long lastModified) { 47 | this.lastModified = lastModified; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/util/ChunkFilter.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt.util; 2 | 3 | public final class ChunkFilter { 4 | private final byte type; 5 | private final String name; 6 | 7 | private ChunkFilter(final byte type, final String name) { 8 | this.type = type; 9 | this.name = name; 10 | } 11 | 12 | public static ChunkFilter of(final byte type, final String name) { 13 | return new ChunkFilter(type, name); 14 | } 15 | 16 | public byte getType() { 17 | return type; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /nbt/src/main/java/org/popcraft/chunky/nbt/util/ChunkPos.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.nbt.util; 2 | 3 | public record ChunkPos(int x, int z) { 4 | public static ChunkPos of(final int x, final int z) { 5 | return new ChunkPos(x, z); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /neoforge/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("dev.architectury.loom") version "1.10-SNAPSHOT" 3 | } 4 | 5 | val shade: Configuration by configurations.creating 6 | 7 | repositories { 8 | maven("https://maven.neoforged.net/releases/") 9 | } 10 | 11 | dependencies { 12 | minecraft(group = "com.mojang", name = "minecraft", version = "1.21.5") 13 | mappings(loom.officialMojangMappings()) 14 | neoForge(group = "net.neoforged", name = "neoforge", version = "21.5.1-beta") 15 | implementation(project(":chunky-common")) 16 | shade(project(":chunky-common")) 17 | } 18 | 19 | tasks { 20 | processResources { 21 | filesMatching("META-INF/neoforge.mods.toml") { 22 | expand( 23 | "github" to project.property("github"), 24 | "id" to rootProject.name, 25 | "version" to project.version, 26 | "name" to project.property("artifactName"), 27 | "author" to project.property("author"), 28 | "description" to project.property("description") 29 | ) 30 | } 31 | } 32 | jar { 33 | manifest { 34 | attributes( 35 | mapOf( 36 | "Implementation-Title" to rootProject.name, 37 | "Implementation-Version" to project.version, 38 | "Implementation-Vendor" to project.property("author") 39 | ) 40 | ) 41 | } 42 | } 43 | shadowJar { 44 | configurations = listOf(shade) 45 | archiveClassifier.set("dev") 46 | archiveFileName.set(null as String?) 47 | } 48 | remapJar { 49 | inputFile.set(shadowJar.get().archiveFile) 50 | archiveFileName.set("${project.property("artifactName")}-NeoForge-${project.version}.jar") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /neoforge/gradle.properties: -------------------------------------------------------------------------------- 1 | loom.platform=neoforge 2 | -------------------------------------------------------------------------------- /neoforge/src/main/java/org/popcraft/chunky/command/suggestion/PatternSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | import org.popcraft.chunky.iterator.PatternType; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class PatternSuggestionProvider implements SuggestionProvider { 14 | @Override 15 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 16 | try { 17 | final String input = context.getArgument(CommandLiteral.PATTERN, String.class); 18 | PatternType.ALL.forEach(pattern -> { 19 | if (pattern.contains(input.toLowerCase())) { 20 | builder.suggest(pattern); 21 | } 22 | }); 23 | } catch (IllegalArgumentException e) { 24 | PatternType.ALL.forEach(builder::suggest); 25 | } 26 | return builder.buildFuture(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /neoforge/src/main/java/org/popcraft/chunky/command/suggestion/ShapeSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | import org.popcraft.chunky.shape.ShapeType; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class ShapeSuggestionProvider implements SuggestionProvider { 14 | @Override 15 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 16 | try { 17 | final String input = context.getArgument(CommandLiteral.SHAPE, String.class); 18 | ShapeType.all().forEach(shape -> { 19 | if (shape.contains(input.toLowerCase())) { 20 | builder.suggest(shape); 21 | } 22 | }); 23 | } catch (IllegalArgumentException e) { 24 | ShapeType.all().forEach(builder::suggest); 25 | } 26 | return builder.buildFuture(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /neoforge/src/main/java/org/popcraft/chunky/command/suggestion/SuggestionProviders.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.suggestion.SuggestionProvider; 4 | import net.minecraft.commands.CommandSourceStack; 5 | 6 | public final class SuggestionProviders { 7 | public static final SuggestionProvider PATTERNS; 8 | public static final SuggestionProvider SHAPES; 9 | public static final SuggestionProvider TRIM_MODES; 10 | 11 | static { 12 | PATTERNS = new PatternSuggestionProvider(); 13 | SHAPES = new ShapeSuggestionProvider(); 14 | TRIM_MODES = new TrimModeSuggestionProvider(); 15 | } 16 | 17 | private SuggestionProviders() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /neoforge/src/main/java/org/popcraft/chunky/command/suggestion/TrimModeSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.suggestion.SuggestionProvider; 5 | import com.mojang.brigadier.suggestion.Suggestions; 6 | import com.mojang.brigadier.suggestion.SuggestionsBuilder; 7 | import net.minecraft.commands.CommandSourceStack; 8 | import org.popcraft.chunky.command.CommandLiteral; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class TrimModeSuggestionProvider implements SuggestionProvider { 14 | private static final List TRIM_MODES = List.of("inside", "outside"); 15 | 16 | @Override 17 | public CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) { 18 | try { 19 | final String input = context.getArgument(CommandLiteral.TRIM_MODE, String.class); 20 | TRIM_MODES.forEach(shape -> { 21 | if (shape.contains(input.toLowerCase())) { 22 | builder.suggest(shape); 23 | } 24 | }); 25 | } catch (IllegalArgumentException e) { 26 | TRIM_MODES.forEach(builder::suggest); 27 | } 28 | return builder.buildFuture(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /neoforge/src/main/java/org/popcraft/chunky/listeners/bossbar/BossBarTaskFinishListener.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.listeners.bossbar; 2 | 3 | import net.minecraft.resources.ResourceLocation; 4 | import net.minecraft.server.level.ServerBossEvent; 5 | import org.popcraft.chunky.GenerationTask; 6 | import org.popcraft.chunky.event.task.GenerationTaskFinishEvent; 7 | import org.popcraft.chunky.platform.World; 8 | 9 | import java.util.Map; 10 | import java.util.function.Consumer; 11 | 12 | public class BossBarTaskFinishListener implements Consumer { 13 | private final Map bossBars; 14 | 15 | public BossBarTaskFinishListener(final Map bossBars) { 16 | this.bossBars = bossBars; 17 | } 18 | 19 | @Override 20 | public void accept(final GenerationTaskFinishEvent event) { 21 | final GenerationTask task = event.generationTask(); 22 | final World world = task.getSelection().world(); 23 | final ResourceLocation worldIdentifier = ResourceLocation.tryParse(world.getKey()); 24 | if (worldIdentifier == null) { 25 | return; 26 | } 27 | final ServerBossEvent bossBar = bossBars.get(worldIdentifier); 28 | if (bossBar != null) { 29 | bossBar.removeAllPlayers(); 30 | bossBars.remove(worldIdentifier); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /neoforge/src/main/java/org/popcraft/chunky/platform/NeoForgeBorder.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.minecraft.world.level.border.WorldBorder; 4 | import org.popcraft.chunky.platform.util.Vector2; 5 | import org.popcraft.chunky.shape.ShapeType; 6 | 7 | public class NeoForgeBorder implements Border { 8 | private final WorldBorder worldBorder; 9 | 10 | public NeoForgeBorder(final WorldBorder worldBorder) { 11 | this.worldBorder = worldBorder; 12 | } 13 | 14 | @Override 15 | public Vector2 getCenter() { 16 | return Vector2.of(worldBorder.getCenterX(), worldBorder.getCenterZ()); 17 | } 18 | 19 | @Override 20 | public double getRadiusX() { 21 | return worldBorder.getSize() / 2d; 22 | } 23 | 24 | @Override 25 | public double getRadiusZ() { 26 | return worldBorder.getSize() / 2d; 27 | } 28 | 29 | @Override 30 | public String getShape() { 31 | return ShapeType.SQUARE; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /neoforge/src/main/java/org/popcraft/chunky/platform/NeoForgePlayer.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.minecraft.network.chat.Component; 4 | import net.minecraft.server.level.ServerPlayer; 5 | import net.minecraft.world.entity.Relative; 6 | import org.popcraft.chunky.platform.util.Location; 7 | 8 | import java.util.EnumSet; 9 | import java.util.UUID; 10 | 11 | import static org.popcraft.chunky.util.Translator.translateKey; 12 | 13 | public class NeoForgePlayer extends NeoForgeSender implements Player { 14 | private final ServerPlayer player; 15 | 16 | public NeoForgePlayer(final ServerPlayer player) { 17 | super(player.createCommandSourceStack()); 18 | this.player = player; 19 | } 20 | 21 | @Override 22 | public boolean isPlayer() { 23 | return true; 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return player.getName().toString(); 29 | } 30 | 31 | @Override 32 | public World getWorld() { 33 | return new NeoForgeWorld(player.serverLevel()); 34 | } 35 | 36 | @Override 37 | public Location getLocation() { 38 | return new Location(getWorld(), player.getX(), player.getY(), player.getZ(), player.getXRot(), player.getYRot()); 39 | } 40 | 41 | @Override 42 | public void sendMessage(final String key, final boolean prefixed, final Object... args) { 43 | player.sendSystemMessage(formatColored(translateKey(key, prefixed, args))); 44 | } 45 | 46 | @Override 47 | public UUID getUUID() { 48 | return player.getUUID(); 49 | } 50 | 51 | @Override 52 | public void teleport(final Location location) { 53 | player.teleportTo(((NeoForgeWorld) location.getWorld()).getWorld(), location.getX(), location.getY(), location.getZ(), EnumSet.noneOf(Relative.class), location.getYaw(), location.getPitch(), true); 54 | } 55 | 56 | @Override 57 | public void sendActionBar(final String key) { 58 | player.displayClientMessage(formatColored(translateKey(key, false)), true); 59 | } 60 | 61 | private Component formatColored(final String message) { 62 | return Component.nullToEmpty(message.replaceAll("&(?=[0-9a-fk-orA-FK-OR])", "§")); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /neoforge/src/main/java/org/popcraft/chunky/platform/NeoForgeSender.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.minecraft.commands.CommandSourceStack; 4 | import net.minecraft.network.chat.Component; 5 | import net.minecraft.server.level.ServerPlayer; 6 | import net.minecraft.world.phys.Vec2; 7 | import net.minecraft.world.phys.Vec3; 8 | import org.popcraft.chunky.platform.util.Location; 9 | 10 | import static org.popcraft.chunky.util.Translator.translateKey; 11 | 12 | public class NeoForgeSender implements Sender { 13 | private final CommandSourceStack source; 14 | 15 | public NeoForgeSender(final CommandSourceStack source) { 16 | this.source = source; 17 | } 18 | 19 | @Override 20 | public boolean isPlayer() { 21 | return source.getEntity() instanceof ServerPlayer; 22 | } 23 | 24 | @Override 25 | public String getName() { 26 | return source.getTextName(); 27 | } 28 | 29 | @Override 30 | public World getWorld() { 31 | return new NeoForgeWorld(source.getLevel()); 32 | } 33 | 34 | @Override 35 | public Location getLocation() { 36 | final Vec3 pos = source.getPosition(); 37 | final Vec2 rot = source.getRotation(); 38 | return new Location(getWorld(), pos.x(), pos.y(), pos.z(), rot.x, rot.y); 39 | } 40 | 41 | @Override 42 | public boolean hasPermission(final String permission) { 43 | return source.hasPermission(2); 44 | } 45 | 46 | @Override 47 | public void sendMessage(final String key, final boolean prefixed, final Object... args) { 48 | source.sendSuccess(() -> Component.nullToEmpty(translateKey(key, prefixed, args).replaceAll("&[0-9a-fk-orA-FK-OR]", "")), false); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | # MinecraftServer 2 | public net.minecraft.server.MinecraftServer emptyTicks 3 | # ChunkMap 4 | public net.minecraft.server.level.ChunkMap readChunk(Lnet/minecraft/world/level/ChunkPos;)Ljava/util/concurrent/CompletableFuture; 5 | public net.minecraft.server.level.ChunkMap getVisibleChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder; 6 | # ServerChunkCache 7 | public net.minecraft.server.level.ServerChunkCache getChunkFutureMainThread(IILnet/minecraft/world/level/chunk/status/ChunkStatus;Z)Ljava/util/concurrent/CompletableFuture; 8 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="[3,)" 3 | license="GNU GPLv3" 4 | issueTrackerURL="${github}/issues" 5 | [[mods]] 6 | modId="${id}" 7 | version="${version}" 8 | displayName="${name}" 9 | displayURL="${github}" 10 | logoFile="icon.png" 11 | authors="${author}" 12 | description="${description}" 13 | [[dependencies.chunky]] 14 | modId="neoforge" 15 | type="required" 16 | versionRange="[21.5.0-beta,)" 17 | ordering="NONE" 18 | side="BOTH" 19 | [[dependencies.chunky]] 20 | modId="minecraft" 21 | type="required" 22 | versionRange="[1.21.5,1.22)" 23 | ordering="NONE" 24 | side="BOTH" 25 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pop4959/Chunky/ed0094c13ff1b5ae3748d86c4c29ba89122d4583/neoforge/src/main/resources/icon.png -------------------------------------------------------------------------------- /neoforge/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "chunky resources", 4 | "pack_format": 8 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /paper/build.gradle.kts: -------------------------------------------------------------------------------- 1 | repositories { 2 | maven("https://repo.papermc.io/repository/maven-public/") 3 | } 4 | 5 | dependencies { 6 | compileOnly(group = "io.papermc.paper", name = "paper-api", version = "1.20.4-R0.1-SNAPSHOT") 7 | } 8 | -------------------------------------------------------------------------------- /paper/src/main/java/org/popcraft/chunky/platform/Paper.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import io.papermc.paper.entity.TeleportFlag; 4 | import org.bukkit.Chunk; 5 | import org.bukkit.Location; 6 | import org.bukkit.World; 7 | import org.bukkit.entity.Entity; 8 | import org.bukkit.event.player.PlayerTeleportEvent; 9 | 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | public final class Paper { 13 | private static final boolean CONFIG_EXISTS = classExists("com.destroystokyo.paper.PaperConfig") || classExists("io.papermc.paper.configuration.Configuration"); 14 | 15 | private Paper() { 16 | } 17 | 18 | public static boolean isPaper() { 19 | return CONFIG_EXISTS; 20 | } 21 | 22 | public static CompletableFuture getChunkAtAsync(final World world, final int x, final int z) { 23 | return world.getChunkAtAsync(x, z, true); 24 | } 25 | 26 | public static CompletableFuture teleportAsync(final Entity entity, final Location location) { 27 | return entity.teleportAsync(location); 28 | } 29 | 30 | public static CompletableFuture teleportAsyncWithPassengers(final Entity entity, final Location location) { 31 | return entity.teleportAsync(location, PlayerTeleportEvent.TeleportCause.PLUGIN, TeleportFlag.EntityState.RETAIN_PASSENGERS, TeleportFlag.EntityState.RETAIN_VEHICLE); 32 | } 33 | 34 | private static boolean classExists(final String clazz) { 35 | try { 36 | Class.forName(clazz); 37 | return true; 38 | } catch (ClassNotFoundException e) { 39 | return false; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven("https://maven.fabricmc.net/") 5 | maven("https://maven.minecraftforge.net") 6 | maven("https://maven.architectury.dev/") 7 | } 8 | } 9 | 10 | rootProject.name = "chunky" 11 | 12 | sequenceOf( 13 | "nbt", 14 | "common", 15 | "paper", 16 | "folia", 17 | "bukkit", 18 | "fabric", 19 | "forge", 20 | "neoforge", 21 | "sponge" 22 | ).forEach { 23 | include("${rootProject.name}-$it") 24 | project(":${rootProject.name}-$it").projectDir = file(it) 25 | } 26 | -------------------------------------------------------------------------------- /sponge/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.spongepowered.gradle.plugin.config.PluginLoaders 2 | import org.spongepowered.plugin.metadata.model.PluginDependency 3 | 4 | plugins { 5 | id("org.spongepowered.gradle.plugin") version "2.2.0" 6 | } 7 | 8 | dependencies { 9 | implementation(project(":chunky-common")) 10 | } 11 | 12 | sponge { 13 | apiVersion("8.1.0") 14 | loader { 15 | name(PluginLoaders.JAVA_PLAIN) 16 | version("1.0") 17 | } 18 | license("GPL-3.0") 19 | plugin(rootProject.name) { 20 | displayName("${project.property("artifactName")}") 21 | version("${project.version}") 22 | entrypoint("${project.group}.${rootProject.name}.ChunkySponge") 23 | description("${project.property("description")}") 24 | links { 25 | homepage("${project.property("github")}") 26 | source("${project.property("github")}") 27 | issues("${project.property("github")}/issues") 28 | } 29 | contributor("${project.property("author")}") { 30 | description("Lead Developer") 31 | } 32 | dependency("spongeapi") { 33 | loadOrder(PluginDependency.LoadOrder.AFTER) 34 | optional(false) 35 | } 36 | } 37 | } 38 | 39 | tasks { 40 | shadowJar { 41 | archiveFileName.set("${project.property("artifactName")}-Sponge-${project.version}.jar") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sponge/src/main/java/org/popcraft/chunky/command/suggestion/PatternSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import org.popcraft.chunky.iterator.PatternType; 4 | import org.spongepowered.api.command.CommandCompletion; 5 | import org.spongepowered.api.command.parameter.CommandContext; 6 | import org.spongepowered.api.command.parameter.managed.ValueCompleter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class PatternSuggestionProvider implements ValueCompleter { 12 | @Override 13 | public List complete(final CommandContext context, final String currentInput) { 14 | final List completions = new ArrayList<>(); 15 | PatternType.ALL.forEach(pattern -> { 16 | if (pattern.contains(currentInput.toLowerCase())) { 17 | completions.add(CommandCompletion.of(pattern)); 18 | } 19 | }); 20 | return completions; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sponge/src/main/java/org/popcraft/chunky/command/suggestion/ShapeSuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | import org.popcraft.chunky.shape.ShapeType; 4 | import org.spongepowered.api.command.CommandCompletion; 5 | import org.spongepowered.api.command.parameter.CommandContext; 6 | import org.spongepowered.api.command.parameter.managed.ValueCompleter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class ShapeSuggestionProvider implements ValueCompleter { 12 | @Override 13 | public List complete(final CommandContext context, final String currentInput) { 14 | final List completions = new ArrayList<>(); 15 | ShapeType.all().forEach(pattern -> { 16 | if (pattern.contains(currentInput.toLowerCase())) { 17 | completions.add(CommandCompletion.of(pattern)); 18 | } 19 | }); 20 | return completions; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sponge/src/main/java/org/popcraft/chunky/command/suggestion/SuggestionProviders.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.command.suggestion; 2 | 3 | public final class SuggestionProviders { 4 | public static final PatternSuggestionProvider PATTERNS; 5 | public static final ShapeSuggestionProvider SHAPES; 6 | 7 | static { 8 | PATTERNS = new PatternSuggestionProvider(); 9 | SHAPES = new ShapeSuggestionProvider(); 10 | } 11 | 12 | private SuggestionProviders() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sponge/src/main/java/org/popcraft/chunky/platform/SpongeBorder.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.popcraft.chunky.platform.util.Vector2; 4 | import org.popcraft.chunky.shape.ShapeType; 5 | import org.spongepowered.api.world.server.ServerWorld; 6 | import org.spongepowered.math.vector.Vector2d; 7 | 8 | public class SpongeBorder implements Border { 9 | private final ServerWorld serverWorld; 10 | 11 | public SpongeBorder(final ServerWorld serverWorld) { 12 | this.serverWorld = serverWorld; 13 | } 14 | 15 | @Override 16 | public Vector2 getCenter() { 17 | final Vector2d center = serverWorld.border().center(); 18 | return Vector2.of(center.x(), center.y()); 19 | } 20 | 21 | @Override 22 | public double getRadiusX() { 23 | return serverWorld.border().diameter() / 2d; 24 | } 25 | 26 | @Override 27 | public double getRadiusZ() { 28 | return serverWorld.border().diameter() / 2d; 29 | } 30 | 31 | @Override 32 | public String getShape() { 33 | return ShapeType.SQUARE; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sponge/src/main/java/org/popcraft/chunky/platform/SpongePlayer.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 4 | import org.popcraft.chunky.platform.util.Location; 5 | import org.spongepowered.api.entity.living.player.server.ServerPlayer; 6 | import org.spongepowered.api.world.server.ServerLocation; 7 | import org.spongepowered.math.vector.Vector3d; 8 | 9 | import java.util.UUID; 10 | 11 | import static org.popcraft.chunky.util.Translator.translateKey; 12 | 13 | public class SpongePlayer extends SpongeSender implements Player { 14 | private final ServerPlayer player; 15 | 16 | public SpongePlayer(final ServerPlayer player) { 17 | super(player); 18 | this.player = player; 19 | } 20 | 21 | @Override 22 | public boolean isPlayer() { 23 | return true; 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return player.name(); 29 | } 30 | 31 | @Override 32 | public World getWorld() { 33 | return new SpongeWorld(player.serverLocation().world()); 34 | } 35 | 36 | @Override 37 | public Location getLocation() { 38 | final ServerLocation loc = player.serverLocation(); 39 | final Vector3d rot = player.rotation(); 40 | return new Location(getWorld(), loc.x(), loc.y(), loc.z(), rot.floorX(), rot.floorY()); 41 | } 42 | 43 | @Override 44 | public UUID getUUID() { 45 | return player.uniqueId(); 46 | } 47 | 48 | @Override 49 | public void teleport(final Location location) { 50 | player.setLocation(ServerLocation.of(((SpongeWorld) location.getWorld()).getWorld(), location.getX(), location.getY(), location.getZ())); 51 | player.setRotation(Vector3d.from(location.getYaw(), location.getPitch(), 0)); 52 | } 53 | 54 | @Override 55 | public void sendActionBar(final String key) { 56 | player.sendActionBar(LegacyComponentSerializer.legacyAmpersand().deserialize(translateKey(key, false))); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sponge/src/main/java/org/popcraft/chunky/platform/SpongeSender.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import net.kyori.adventure.audience.Audience; 4 | import net.kyori.adventure.identity.Identity; 5 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 6 | import org.popcraft.chunky.platform.util.Location; 7 | import org.spongepowered.api.Sponge; 8 | import org.spongepowered.api.entity.living.player.Player; 9 | import org.spongepowered.api.entity.living.player.User; 10 | import org.spongepowered.api.service.permission.Subject; 11 | import org.spongepowered.api.world.DefaultWorldKeys; 12 | import org.spongepowered.api.world.server.WorldManager; 13 | 14 | import static org.popcraft.chunky.util.Translator.translateKey; 15 | 16 | public class SpongeSender implements Sender { 17 | private final Object source; 18 | 19 | public SpongeSender(final Object source) { 20 | this.source = source; 21 | } 22 | 23 | @Override 24 | public boolean isPlayer() { 25 | return source instanceof Player; 26 | } 27 | 28 | @Override 29 | public String getName() { 30 | return source instanceof final User user ? user.name() : "Console"; 31 | } 32 | 33 | @Override 34 | public World getWorld() { 35 | final WorldManager worldManager = Sponge.game().server().worldManager(); 36 | return new SpongeWorld(worldManager.world(DefaultWorldKeys.DEFAULT).orElseThrow(IllegalStateException::new)); 37 | } 38 | 39 | @Override 40 | public Location getLocation() { 41 | return new Location(getWorld(), 0, 0, 0, 0, 0); 42 | } 43 | 44 | @Override 45 | public boolean hasPermission(final String permission) { 46 | return source instanceof final Subject subject && subject.hasPermission(permission); 47 | } 48 | 49 | @Override 50 | public void sendMessage(final String key, final boolean prefixed, final Object... args) { 51 | if (source instanceof final Audience audience) { 52 | audience.sendMessage(Identity.nil(), LegacyComponentSerializer.legacyAmpersand().deserialize(translateKey(key, prefixed, args))); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sponge/src/main/java/org/popcraft/chunky/platform/SpongeServer.java: -------------------------------------------------------------------------------- 1 | package org.popcraft.chunky.platform; 2 | 3 | import org.popcraft.chunky.ChunkySponge; 4 | import org.popcraft.chunky.integration.Integration; 5 | import org.spongepowered.api.ResourceKey; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Optional; 13 | 14 | public class SpongeServer implements Server { 15 | private static final int MAX_WORLD_SIZE = 29999984; 16 | private final ChunkySponge plugin; 17 | private final Map integrations; 18 | 19 | public SpongeServer(final ChunkySponge plugin) { 20 | this.plugin = plugin; 21 | this.integrations = new HashMap<>(); 22 | } 23 | 24 | @Override 25 | public Map getIntegrations() { 26 | return integrations; 27 | } 28 | 29 | @Override 30 | public Optional getWorld(final String name) { 31 | return plugin.getGame().server().worldManager().world(ResourceKey.resolve(name)).map(SpongeWorld::new); 32 | } 33 | 34 | @Override 35 | public List getWorlds() { 36 | final List worlds = new ArrayList<>(); 37 | plugin.getGame().server().worldManager().worlds().forEach(world -> worlds.add(new SpongeWorld(world))); 38 | return worlds; 39 | } 40 | 41 | @Override 42 | public int getMaxWorldSize() { 43 | return MAX_WORLD_SIZE; 44 | } 45 | 46 | @Override 47 | public Sender getConsole() { 48 | return new SpongeSender(plugin.getGame().systemSubject()); 49 | } 50 | 51 | @Override 52 | public Collection getPlayers() { 53 | final Collection players = new ArrayList<>(); 54 | plugin.getGame().server().onlinePlayers().forEach(player -> players.add(new SpongePlayer(player))); 55 | return players; 56 | } 57 | 58 | @Override 59 | public Optional getPlayer(final String name) { 60 | return plugin.getGame().server().player(name).map(SpongePlayer::new); 61 | } 62 | 63 | @Override 64 | public Config getConfig() { 65 | return plugin.getChunky().getConfig(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /sponge/src/main/resources/main.conf: -------------------------------------------------------------------------------- 1 | # Chunky Configuration 2 | # https://github.com/pop4959/Chunky/wiki/Configuration 3 | config { 4 | continue-on-restart=false 5 | force-load-existing-chunks=false 6 | language=en 7 | silent=false 8 | update-interval=1 9 | version=2 10 | } 11 | --------------------------------------------------------------------------------