diff --git a/.github/README.md b/.github/README.md
new file mode 100644
index 0000000..7260fae
--- /dev/null
+++ b/.github/README.md
@@ -0,0 +1,84 @@
+# Scaffolding
+
+Scaffolding is a library for Minestom that allows you to load and place schematics.
+> This library is very early in development and has too many bugs to count. For your own safety, you should not use it in a production environment.
+
+> Also expect the API to change quite a bit in the future as this project works towards a 1.0.0 release.
+
+## Getting Started
+
+### Repository
+
+As with Minestom, Scaffolding uses JitPack to distribute releases.
+
+#### Gradle (Kotlin)
+Add JitPack as a repository to your ```build.gradle.kts``` file.
+```kotlin
+repositories {
+ maven(url = "https://jitpack.io")
+}
+```
+
+#### Gradle (Groovy)
+Add JitPack as a repository to your ```build.gradle``` file.
+```groovy
+repositories {
+ maven { url 'https://jitpack.io' }
+}
+```
+
+#### Maven
+Add JitPack as a repository to your ```pom.xml``` file.
+```xml
+
+
+ jitpack
+ https://jitpack.io
+
+
+```
+
+### Dependency
+
+Keep an eye out for new releases on [Jitpack](https://jitpack.io/#CrystalGamesMc/scaffolding).
+
+#### Gradle (Kotlin)
+Add Scaffolding as a dependency in your ```build.gradle.kts``` file.
+```kts
+dependencies {
+ implementation("com.github.CrystalGamesMc:scaffolding:Tag")
+}
+```
+
+#### Gradle (Groovy)
+Add Scaffolding as a dependency in your ```build.gradle``` file.
+```groovy
+dependencies {
+ implementation 'com.github.CrystalGamesMc:scaffolding:Tag'
+}
+```
+
+#### Maven
+Add Scaffolding as a dependency in your ```pom.xml``` file.
+```xml
+
+
+ com.github.CrystalGamesMc
+ scaffolding
+ Tag
+
+
+```
+
+## Usage
+Read
+```java
+File file = new File("schematics/your_schematic.schematic");
+Schematic schematic = Scaffolding.fromFileSync(file);
+```
+
+Build
+```java
+Schematic schematic = ...;
+schematic.build(instance, new Pos(0, 64, 0)).join();
+```
diff --git a/.gitignore b/.gitignore
index 862db5a..d86f258 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
-# Created by https://www.toptal.com/developers/gitignore/api/maven,intellij+all,java
-# Edit at https://www.toptal.com/developers/gitignore?templates=maven,intellij+all,java
+# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,gradle,java
+# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,gradle,java
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
@@ -66,6 +66,9 @@ atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
+# SonarLint plugin
+.idea/sonarlint/
+
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
@@ -79,20 +82,13 @@ fabric.properties
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
-# Ignores the whole .idea folder and all .iml files
-# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
-
-.idea/
-
-# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
+# Ignore everything but code style settings and run configurations
+# that are supposed to be shared within teams.
-*.iml
-modules.xml
-.idea/misc.xml
-*.ipr
+.idea/*
-# Sonarlint plugin
-.idea/sonarlint
+!.idea/codeStyles
+!.idea/runConfigurations
### Java ###
# Compiled class file
@@ -118,32 +114,36 @@ modules.xml
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+replay_pid*
+
+### Gradle ###
+.gradle
+**/build/
+!src/**/build/
-### Maven ###
-target/
-pom.xml.tag
-pom.xml.releaseBackup
-pom.xml.versionsBackup
-pom.xml.next
-release.properties
-dependency-reduced-pom.xml
-buildNumber.properties
-.mvn/timing.properties
-# https://github.com/takari/maven-wrapper#usage-without-binary-jar
-.mvn/wrapper/maven-wrapper.jar
-
-### Maven Patch ###
-# Eclipse m2e generated files
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Avoid ignore Gradle wrappper properties
+!gradle-wrapper.properties
+
+# Cache of project
+.gradletasknamecache
+
+# Eclipse Gradle plugin generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
+# End of https://www.toptal.com/developers/gitignore/api/intellij+all,gradle,java
+
### Minestom ###
extensions/
### Scaffolding ###
*.schematic
-*.schem
-
-# End of https://www.toptal.com/developers/gitignore/api/maven,intellij+all,java
\ No newline at end of file
+*.schem
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index 0fdcc9c..0000000
--- a/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Scaffolding
-Scaffolding is a library for Minestom that allows you to load and place schematics.
-> This library is very early in development and has too many bugs to count. For your own safety, you should not use it in a production environment.
-
-## Usage
-```java
-// Load a schematic from File.
-public void method1() {
- Schematic schematic = Scaffolding.fromFile(new File("schematics/my_schematic.schematic"));
-}
-
-public void method2() {
- Schematic schematic = new SpongeSchematic();
- schematic.read(new FileInputStream(new File("schematics/my_schematic.schematic")));
-}
-```
-```java
-// Place a schematic at a location.
-Instance instance = player.getInstance();
-Pos position = player.getPosition();
-schematic.build(instance, position).thenRun(() -> player.sendMessage("Schematic placed!"));
-```
-```java
-// Write a schematic (SOONtm)
-Region region = new Region(new Pos(0, 0, 0), new Pos(10, 10, 10));
-Schematic schematic = new SpongeSchematic();
-schematic.write(new FileOutputStream("schematics/my_schematic.schematic"), region);
-```
-
-## Build Tools
-
-/!\ Repository is currently down, cloning the repository will be required until fixed.
-
-### Repository
-```xml
-
- crystalgames
- https://repo.crystalgames.net/snapshots/
-
-```
-### Dependency
-```xml
-
- net.crystalgames
- Scaffolding
- 0.1.1-SNAPSHOT
-
-```
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..8700cc0
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,10 @@
+
+allprojects {
+ repositories {
+ mavenCentral()
+ maven { url = uri("https://jitpack.io") }
+ }
+
+ group = "net.crystalgames"
+ version = "0.2.0"
+}
\ No newline at end of file
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..6b4fdc1
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,162 @@
+
+# Created by https://www.toptal.com/developers/gitignore/api/yarn,node
+# Edit at https://www.toptal.com/developers/gitignore?templates=yarn,node
+
+### Node ###
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional stylelint cache
+.stylelintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+.temp
+
+# Docusaurus cache and generated files
+.docusaurus
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+### Node Patch ###
+# Serverless Webpack directories
+.webpack/
+
+# Optional stylelint cache
+
+# SvelteKit build / generate output
+.svelte-kit
+
+### yarn ###
+# https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
+
+.yarn/*
+!.yarn/releases
+!.yarn/patches
+!.yarn/plugins
+!.yarn/sdks
+!.yarn/versions
+
+# if you are NOT using Zero-installs, then:
+# comment the following lines
+!.yarn/cache
+
+# and uncomment the following lines
+# .pnp.*
+
+# End of https://www.toptal.com/developers/gitignore/api/yarn,node
diff --git a/docs/content/.vitepress/config.js b/docs/content/.vitepress/config.js
new file mode 100644
index 0000000..dd80113
--- /dev/null
+++ b/docs/content/.vitepress/config.js
@@ -0,0 +1,42 @@
+import { defineConfig } from 'vitepress'
+
+export default defineConfig({
+ title: 'Scaffolding',
+ description: 'Schematic library for Minestom',
+
+ themeConfig: {
+ sidebar: {
+ '/': getSidebar()
+ }
+ }
+})
+
+function getSidebar() {
+ return [
+ {
+ text: 'Introduction',
+ link: '/'
+ },
+ {
+ text: 'Setup',
+ children: [
+ { text: 'Getting Started', link: '/setup/getting-started' },
+ ]
+ },
+ {
+ text: 'Using Schematics',
+ children: [
+ { text: 'Load', link: '/usage/read' },
+ { text: 'Copy', link: '/usage/copy' },
+ { text: 'Build', link: '/usage/build ' },
+ { text: 'Write', link: '/usage/write ' },
+ ]
+ },
+ {
+ text: 'Utilities',
+ children: [
+ { text: 'Region', link: '/utilities/region' },
+ ]
+ },
+ ]
+}
diff --git a/docs/content/index.md b/docs/content/index.md
new file mode 100644
index 0000000..d1759c3
--- /dev/null
+++ b/docs/content/index.md
@@ -0,0 +1,2 @@
+# Introduction
+Scaffolding is a library for interacting with schematics for the [Minestom](https://minestom.net/) framework.
\ No newline at end of file
diff --git a/docs/content/setup/getting-started.md b/docs/content/setup/getting-started.md
new file mode 100644
index 0000000..1eab389
--- /dev/null
+++ b/docs/content/setup/getting-started.md
@@ -0,0 +1,71 @@
+# Getting Started
+
+This guide describes how to setup Scaffolding as a dependency for your project.
+
+::: info
+Scaffolding is intended to be used as a library for the Minestom framework. You can learn how to setup Minestom [here](https://wiki.minestom.net/setup/dependencies/).
+:::
+
+
+## Repository
+
+As with Minestom, Scaffolding uses JitPack to distribute releases.
+
+### Gradle (Kotlin)
+Add JitPack as a repository to your ```build.gradle.kts``` file.
+```kotlin{2}
+repositories {
+ maven(url = "https://jitpack.io")
+}
+```
+
+### Gradle (Groovy)
+Add JitPack as a repository to your ```build.gradle``` file.
+```groovy{2}
+repositories {
+ maven { url 'https://jitpack.io' }
+}
+```
+
+### Maven
+Add JitPack as a repository to your ```pom.xml``` file.
+```xml{2-5}
+
+
+ jitpack
+ https://jitpack.io
+
+
+```
+
+## Dependency
+
+Keep an eye out for new releases on [Jitpack](https://jitpack.io/#CrystalGamesMc/scaffolding).
+
+### Gradle (Kotlin)
+Add Scaffolding as a dependency in your ```build.gradle.kts``` file.
+```kts{2}
+dependencies {
+ implementation("com.github.CrystalGamesMc:scaffolding:Tag")
+}
+```
+
+### Gradle (Groovy)
+Add Scaffolding as a dependency in your ```build.gradle``` file.
+```groovy{2}
+dependencies {
+ implementation 'com.github.CrystalGamesMc:scaffolding:Tag'
+}
+```
+
+### Maven
+Add Scaffolding as a dependency in your ```pom.xml``` file.
+```xml{2-6}
+
+
+ com.github.CrystalGamesMc
+ scaffolding
+ Tag
+
+
+```
\ No newline at end of file
diff --git a/docs/content/usage/build.md b/docs/content/usage/build.md
new file mode 100644
index 0000000..01c7c5e
--- /dev/null
+++ b/docs/content/usage/build.md
@@ -0,0 +1,3 @@
+# Building Schematics
+
+Documentation coming soon!
\ No newline at end of file
diff --git a/docs/content/usage/copy.md b/docs/content/usage/copy.md
new file mode 100644
index 0000000..12c60ab
--- /dev/null
+++ b/docs/content/usage/copy.md
@@ -0,0 +1,3 @@
+# Copying Schematics
+
+Documentation coming soon!
\ No newline at end of file
diff --git a/docs/content/usage/read.md b/docs/content/usage/read.md
new file mode 100644
index 0000000..77876db
--- /dev/null
+++ b/docs/content/usage/read.md
@@ -0,0 +1,8 @@
+# Loading Schematics
+Scaffolding offers many ways to load schematics.
+
+## Automatically parsing type
+```java
+File file = new File("schematics/your_schematic.schematic");
+Schematic schematic = Scaffolding.fromFileSync(file);
+```
diff --git a/docs/content/usage/write.md b/docs/content/usage/write.md
new file mode 100644
index 0000000..f0fc2c6
--- /dev/null
+++ b/docs/content/usage/write.md
@@ -0,0 +1,3 @@
+# Serializing Schematics
+
+This functionality is coming soon!
\ No newline at end of file
diff --git a/docs/content/utilities/region.md b/docs/content/utilities/region.md
new file mode 100644
index 0000000..da336c0
--- /dev/null
+++ b/docs/content/utilities/region.md
@@ -0,0 +1,3 @@
+# Region
+
+Documentation coming soon!
\ No newline at end of file
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 0000000..bd22882
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "scaffolding-docs",
+ "version": "1.0.0",
+ "description": "Schematic library for Minestom",
+ "repository": "https://github.com/CrystalGamesMC/Scaffolding",
+ "license": "MIT",
+ "private": true,
+ "devDependencies": {
+ "vitepress": "^0.22.4"
+ },
+ "scripts": {
+ "docs:dev": "vitepress dev content",
+ "docs:build": "vitepress build content",
+ "docs:serve": "vitepress serve content"
+ }
+}
diff --git a/docs/yarn.lock b/docs/yarn.lock
new file mode 100644
index 0000000..03bb431
--- /dev/null
+++ b/docs/yarn.lock
@@ -0,0 +1,541 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@algolia/autocomplete-core@1.5.2":
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.5.2.tgz#ec0178e07b44fd74a057728ac157291b26cecf37"
+ integrity sha512-DY0bhyczFSS1b/CqJlTE/nQRtnTAHl6IemIkBy0nEWnhDzRDdtdx4p5Uuk3vwAFxwEEgi1WqKwgSSMx6DpNL4A==
+ dependencies:
+ "@algolia/autocomplete-shared" "1.5.2"
+
+"@algolia/autocomplete-preset-algolia@1.5.2":
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.5.2.tgz#36c5638cc6dba6ea46a86e5a0314637ca40a77ca"
+ integrity sha512-3MRYnYQFJyovANzSX2CToS6/5cfVjbLLqFsZTKcvF3abhQzxbqwwaMBlJtt620uBUOeMzhdfasKhCc40+RHiZw==
+ dependencies:
+ "@algolia/autocomplete-shared" "1.5.2"
+
+"@algolia/autocomplete-shared@1.5.2":
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.5.2.tgz#e157f9ad624ab8fd940ff28bd2094cdf199cdd79"
+ integrity sha512-ylQAYv5H0YKMfHgVWX0j0NmL8XBcAeeeVQUmppnnMtzDbDnca6CzhKj3Q8eF9cHCgcdTDdb5K+3aKyGWA0obug==
+
+"@algolia/cache-browser-local-storage@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.13.0.tgz#f8aa4fe31104b19d616ea392f9ed5c2ea847d964"
+ integrity sha512-nj1vHRZauTqP/bluwkRIgEADEimqojJgoTRCel5f6q8WCa9Y8QeI4bpDQP28FoeKnDRYa3J5CauDlN466jqRhg==
+ dependencies:
+ "@algolia/cache-common" "4.13.0"
+
+"@algolia/cache-common@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.13.0.tgz#27b83fd3939d08d72261b36a07eeafc4cb4d2113"
+ integrity sha512-f9mdZjskCui/dA/fA/5a+6hZ7xnHaaZI5tM/Rw9X8rRB39SUlF/+o3P47onZ33n/AwkpSbi5QOyhs16wHd55kA==
+
+"@algolia/cache-in-memory@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.13.0.tgz#10801a74550cbabb64b59ff08c56bce9c278ff2d"
+ integrity sha512-hHdc+ahPiMM92CQMljmObE75laYzNFYLrNOu0Q3/eyvubZZRtY2SUsEEgyUEyzXruNdzrkcDxFYa7YpWBJYHAg==
+ dependencies:
+ "@algolia/cache-common" "4.13.0"
+
+"@algolia/client-account@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.13.0.tgz#f8646dd40d1e9e3353e10abbd5d6c293ea92a8e2"
+ integrity sha512-FzFqFt9b0g/LKszBDoEsW+dVBuUe1K3scp2Yf7q6pgHWM1WqyqUlARwVpLxqyc+LoyJkTxQftOKjyFUqddnPKA==
+ dependencies:
+ "@algolia/client-common" "4.13.0"
+ "@algolia/client-search" "4.13.0"
+ "@algolia/transporter" "4.13.0"
+
+"@algolia/client-analytics@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.13.0.tgz#a00bd02df45d71becb9dd4c5c993d805f2e1786d"
+ integrity sha512-klmnoq2FIiiMHImkzOm+cGxqRLLu9CMHqFhbgSy9wtXZrqb8BBUIUE2VyBe7azzv1wKcxZV2RUyNOMpFqmnRZA==
+ dependencies:
+ "@algolia/client-common" "4.13.0"
+ "@algolia/client-search" "4.13.0"
+ "@algolia/requester-common" "4.13.0"
+ "@algolia/transporter" "4.13.0"
+
+"@algolia/client-common@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.13.0.tgz#8bc373d164dbdcce38b4586912bbe162492bcb86"
+ integrity sha512-GoXfTp0kVcbgfSXOjfrxx+slSipMqGO9WnNWgeMmru5Ra09MDjrcdunsiiuzF0wua6INbIpBQFTC2Mi5lUNqGA==
+ dependencies:
+ "@algolia/requester-common" "4.13.0"
+ "@algolia/transporter" "4.13.0"
+
+"@algolia/client-personalization@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.13.0.tgz#10fb7af356422551f11a67222b39c52306f1512c"
+ integrity sha512-KneLz2WaehJmNfdr5yt2HQETpLaCYagRdWwIwkTqRVFCv4DxRQ2ChPVW9jeTj4YfAAhfzE6F8hn7wkQ/Jfj6ZA==
+ dependencies:
+ "@algolia/client-common" "4.13.0"
+ "@algolia/requester-common" "4.13.0"
+ "@algolia/transporter" "4.13.0"
+
+"@algolia/client-search@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.13.0.tgz#2d8ff8e755c4a37ec89968f3f9b358eed005c7f0"
+ integrity sha512-blgCKYbZh1NgJWzeGf+caKE32mo3j54NprOf0LZVCubQb3Kx37tk1Hc8SDs9bCAE8hUvf3cazMPIg7wscSxspA==
+ dependencies:
+ "@algolia/client-common" "4.13.0"
+ "@algolia/requester-common" "4.13.0"
+ "@algolia/transporter" "4.13.0"
+
+"@algolia/logger-common@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.13.0.tgz#be2606e71aae618a1ff1ea9a1b5f5a74284b35a8"
+ integrity sha512-8yqXk7rMtmQJ9wZiHOt/6d4/JDEg5VCk83gJ39I+X/pwUPzIsbKy9QiK4uJ3aJELKyoIiDT1hpYVt+5ia+94IA==
+
+"@algolia/logger-console@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.13.0.tgz#f28028a760e3d9191e28a10b12925e48f6c9afde"
+ integrity sha512-YepRg7w2/87L0vSXRfMND6VJ5d6699sFJBRWzZPOlek2p5fLxxK7O0VncYuc/IbVHEgeApvgXx0WgCEa38GVuQ==
+ dependencies:
+ "@algolia/logger-common" "4.13.0"
+
+"@algolia/requester-browser-xhr@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.13.0.tgz#e2483f4e8d7f09e27cd0daf6c77711d15c5a919f"
+ integrity sha512-Dj+bnoWR5MotrnjblzGKZ2kCdQi2cK/VzPURPnE616NU/il7Ypy6U6DLGZ/ZYz+tnwPa0yypNf21uqt84fOgrg==
+ dependencies:
+ "@algolia/requester-common" "4.13.0"
+
+"@algolia/requester-common@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.13.0.tgz#47fb3464cfb26b55ba43676d13f295d812830596"
+ integrity sha512-BRTDj53ecK+gn7ugukDWOOcBRul59C4NblCHqj4Zm5msd5UnHFjd/sGX+RLOEoFMhetILAnmg6wMrRrQVac9vw==
+
+"@algolia/requester-node-http@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.13.0.tgz#7d981bbd31492f51dd11820a665f9d8906793c37"
+ integrity sha512-9b+3O4QFU4azLhGMrZAr/uZPydvzOR4aEZfSL8ZrpLZ7fbbqTO0S/5EVko+QIgglRAtVwxvf8UJ1wzTD2jvKxQ==
+ dependencies:
+ "@algolia/requester-common" "4.13.0"
+
+"@algolia/transporter@4.13.0":
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.13.0.tgz#f6379e5329efa2127da68c914d1141f5f21dbd07"
+ integrity sha512-8tSQYE+ykQENAdeZdofvtkOr5uJ9VcQSWgRhQ9h01AehtBIPAczk/b2CLrMsw5yQZziLs5cZ3pJ3478yI+urhA==
+ dependencies:
+ "@algolia/cache-common" "4.13.0"
+ "@algolia/logger-common" "4.13.0"
+ "@algolia/requester-common" "4.13.0"
+
+"@babel/parser@^7.16.4":
+ version "7.17.10"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78"
+ integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==
+
+"@docsearch/css@3.0.0", "@docsearch/css@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.0.0.tgz#fe57b474802ffd706d3246eab25d52fac8aa3698"
+ integrity sha512-1kkV7tkAsiuEd0shunYRByKJe3xQDG2q7wYg24SOw1nV9/2lwEd4WrUYRJC/ukGTl2/kHeFxsaUvtiOy0y6fFA==
+
+"@docsearch/js@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@docsearch/js/-/js-3.0.0.tgz#394a99f68895503d57faf523ecec0b25b02f638c"
+ integrity sha512-j3tUJWlgW3slYqzGB8fm7y05kh2qqrIK1dZOXHeMUm/5gdKE85fiz/ltfCPMDFb/MXF+bLZChJXSMzqY0Ck30Q==
+ dependencies:
+ "@docsearch/react" "3.0.0"
+ preact "^10.0.0"
+
+"@docsearch/react@3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.0.0.tgz#d02ebdc67573412185a6a4df13bc254c7c0da491"
+ integrity sha512-yhMacqS6TVQYoBh/o603zszIb5Bl8MIXuOc6Vy617I74pirisDzzcNh0NEaYQt50fVVR3khUbeEhUEWEWipESg==
+ dependencies:
+ "@algolia/autocomplete-core" "1.5.2"
+ "@algolia/autocomplete-preset-algolia" "1.5.2"
+ "@docsearch/css" "3.0.0"
+ algoliasearch "^4.0.0"
+
+"@vitejs/plugin-vue@^2.3.2":
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-2.3.3.tgz#fbf80cc039b82ac21a1acb0f0478de8f61fbf600"
+ integrity sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw==
+
+"@vue/compiler-core@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.33.tgz#e915d59cce85898f5c5cfebe4c09e539278c3d59"
+ integrity sha512-AAmr52ji3Zhk7IKIuigX2osWWsb2nQE5xsdFYjdnmtQ4gymmqXbjLvkSE174+fF3A3kstYrTgGkqgOEbsdLDpw==
+ dependencies:
+ "@babel/parser" "^7.16.4"
+ "@vue/shared" "3.2.33"
+ estree-walker "^2.0.2"
+ source-map "^0.6.1"
+
+"@vue/compiler-dom@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.33.tgz#6db84296f949f18e5d3e7fd5e80f943dbed7d5ec"
+ integrity sha512-GhiG1C8X98Xz9QUX/RlA6/kgPBWJkjq0Rq6//5XTAGSYrTMBgcLpP9+CnlUg1TFxnnCVughAG+KZl28XJqw8uQ==
+ dependencies:
+ "@vue/compiler-core" "3.2.33"
+ "@vue/shared" "3.2.33"
+
+"@vue/compiler-sfc@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.33.tgz#7ce01dc947a8b76c099811dc6ca58494d4dc773d"
+ integrity sha512-H8D0WqagCr295pQjUYyO8P3IejM3vEzeCO1apzByAEaAR/WimhMYczHfZVvlCE/9yBaEu/eu9RdiWr0kF8b71Q==
+ dependencies:
+ "@babel/parser" "^7.16.4"
+ "@vue/compiler-core" "3.2.33"
+ "@vue/compiler-dom" "3.2.33"
+ "@vue/compiler-ssr" "3.2.33"
+ "@vue/reactivity-transform" "3.2.33"
+ "@vue/shared" "3.2.33"
+ estree-walker "^2.0.2"
+ magic-string "^0.25.7"
+ postcss "^8.1.10"
+ source-map "^0.6.1"
+
+"@vue/compiler-ssr@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.33.tgz#3e820267e4eea48fde9519f006dedca3f5e42e71"
+ integrity sha512-XQh1Xdk3VquDpXsnoCd7JnMoWec9CfAzQDQsaMcSU79OrrO2PNR0ErlIjm/mGq3GmBfkQjzZACV+7GhfRB8xMQ==
+ dependencies:
+ "@vue/compiler-dom" "3.2.33"
+ "@vue/shared" "3.2.33"
+
+"@vue/reactivity-transform@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.33.tgz#286063f44ca56150ae9b52f8346a26e5913fa699"
+ integrity sha512-4UL5KOIvSQb254aqenW4q34qMXbfZcmEsV/yVidLUgvwYQQ/D21bGX3DlgPUGI3c4C+iOnNmDCkIxkILoX/Pyw==
+ dependencies:
+ "@babel/parser" "^7.16.4"
+ "@vue/compiler-core" "3.2.33"
+ "@vue/shared" "3.2.33"
+ estree-walker "^2.0.2"
+ magic-string "^0.25.7"
+
+"@vue/reactivity@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.33.tgz#c84eedb5225138dbfc2472864c151d3efbb4b673"
+ integrity sha512-62Sq0mp9/0bLmDuxuLD5CIaMG2susFAGARLuZ/5jkU1FCf9EDbwUuF+BO8Ub3Rbodx0ziIecM/NsmyjardBxfQ==
+ dependencies:
+ "@vue/shared" "3.2.33"
+
+"@vue/runtime-core@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.33.tgz#2df8907c85c37c3419fbd1bdf1a2df097fa40df2"
+ integrity sha512-N2D2vfaXsBPhzCV3JsXQa2NECjxP3eXgZlFqKh4tgakp3iX6LCGv76DLlc+IfFZq+TW10Y8QUfeihXOupJ1dGw==
+ dependencies:
+ "@vue/reactivity" "3.2.33"
+ "@vue/shared" "3.2.33"
+
+"@vue/runtime-dom@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.33.tgz#123b8969247029ea0d9c1983676d4706a962d848"
+ integrity sha512-LSrJ6W7CZTSUygX5s8aFkraDWlO6K4geOwA3quFF2O+hC3QuAMZt/0Xb7JKE3C4JD4pFwCSO7oCrZmZ0BIJUnw==
+ dependencies:
+ "@vue/runtime-core" "3.2.33"
+ "@vue/shared" "3.2.33"
+ csstype "^2.6.8"
+
+"@vue/server-renderer@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.33.tgz#4b45d6d2ae10ea4e3d2cf8e676804cf60f331979"
+ integrity sha512-4jpJHRD4ORv8PlbYi+/MfP8ec1okz6rybe36MdpkDrGIdEItHEUyaHSKvz+ptNEyQpALmmVfRteHkU9F8vxOew==
+ dependencies:
+ "@vue/compiler-ssr" "3.2.33"
+ "@vue/shared" "3.2.33"
+
+"@vue/shared@3.2.33":
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.33.tgz#69a8c99ceb37c1b031d5cc4aec2ff1dc77e1161e"
+ integrity sha512-UBc1Pg1T3yZ97vsA2ueER0F6GbJebLHYlEi4ou1H5YL4KWvMOOWwpYo9/QpWq93wxKG6Wo13IY74Hcn/f7c7Bg==
+
+algoliasearch@^4.0.0:
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.13.0.tgz#e36611fda82b1fc548c156ae7929a7f486e4b663"
+ integrity sha512-oHv4faI1Vl2s+YC0YquwkK/TsaJs79g2JFg5FDm2rKN12VItPTAeQ7hyJMHarOPPYuCnNC5kixbtcqvb21wchw==
+ dependencies:
+ "@algolia/cache-browser-local-storage" "4.13.0"
+ "@algolia/cache-common" "4.13.0"
+ "@algolia/cache-in-memory" "4.13.0"
+ "@algolia/client-account" "4.13.0"
+ "@algolia/client-analytics" "4.13.0"
+ "@algolia/client-common" "4.13.0"
+ "@algolia/client-personalization" "4.13.0"
+ "@algolia/client-search" "4.13.0"
+ "@algolia/logger-common" "4.13.0"
+ "@algolia/logger-console" "4.13.0"
+ "@algolia/requester-browser-xhr" "4.13.0"
+ "@algolia/requester-common" "4.13.0"
+ "@algolia/requester-node-http" "4.13.0"
+ "@algolia/transporter" "4.13.0"
+
+csstype@^2.6.8:
+ version "2.6.20"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda"
+ integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==
+
+esbuild-android-64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.39.tgz#09f12a372eed9743fd77ff6d889ac14f7b340c21"
+ integrity sha512-EJOu04p9WgZk0UoKTqLId9VnIsotmI/Z98EXrKURGb3LPNunkeffqQIkjS2cAvidh+OK5uVrXaIP229zK6GvhQ==
+
+esbuild-android-arm64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.39.tgz#f608d00ea03fe26f3b1ab92a30f99220390f3071"
+ integrity sha512-+twajJqO7n3MrCz9e+2lVOnFplRsaGRwsq1KL/uOy7xK7QdRSprRQcObGDeDZUZsacD5gUkk6OiHiYp6RzU3CA==
+
+esbuild-darwin-64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.39.tgz#31528daa75b4c9317721ede344195163fae3e041"
+ integrity sha512-ImT6eUw3kcGcHoUxEcdBpi6LfTRWaV6+qf32iYYAfwOeV+XaQ/Xp5XQIBiijLeo+LpGci9M0FVec09nUw41a5g==
+
+esbuild-darwin-arm64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.39.tgz#247f770d86d90a215fa194f24f90e30a0bd97245"
+ integrity sha512-/fcQ5UhE05OiT+bW5v7/up1bDsnvaRZPJxXwzXsMRrr7rZqPa85vayrD723oWMT64dhrgWeA3FIneF8yER0XTw==
+
+esbuild-freebsd-64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.39.tgz#479414d294905055eb396ebe455ed42213284ee0"
+ integrity sha512-oMNH8lJI4wtgN5oxuFP7BQ22vgB/e3Tl5Woehcd6i2r6F3TszpCnNl8wo2d/KvyQ4zvLvCWAlRciumhQg88+kQ==
+
+esbuild-freebsd-arm64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.39.tgz#cedeb10357c88533615921ae767a67dc870a474c"
+ integrity sha512-1GHK7kwk57ukY2yI4ILWKJXaxfr+8HcM/r/JKCGCPziIVlL+Wi7RbJ2OzMcTKZ1HpvEqCTBT/J6cO4ZEwW4Ypg==
+
+esbuild-linux-32@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.39.tgz#d9f008c4322d771f3958f59c1eee5a05cdf92485"
+ integrity sha512-g97Sbb6g4zfRLIxHgW2pc393DjnkTRMeq3N1rmjDUABxpx8SjocK4jLen+/mq55G46eE2TA0MkJ4R3SpKMu7dg==
+
+esbuild-linux-64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.39.tgz#ba58d7f66858913aeb1ab5c6bde1bbd824731795"
+ integrity sha512-4tcgFDYWdI+UbNMGlua9u1Zhu0N5R6u9tl5WOM8aVnNX143JZoBZLpCuUr5lCKhnD0SCO+5gUyMfupGrHtfggQ==
+
+esbuild-linux-arm64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.39.tgz#708785a30072702b5b1c16b65cf9c25c51202529"
+ integrity sha512-23pc8MlD2D6Px1mV8GMglZlKgwgNKAO8gsgsLLcXWSs9lQsCYkIlMo/2Ycfo5JrDIbLdwgP8D2vpfH2KcBqrDQ==
+
+esbuild-linux-arm@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.39.tgz#4e8b5deaa7ab60d0d28fab131244ef82b40684f4"
+ integrity sha512-t0Hn1kWVx5UpCzAJkKRfHeYOLyFnXwYynIkK54/h3tbMweGI7dj400D1k0Vvtj2u1P+JTRT9tx3AjtLEMmfVBQ==
+
+esbuild-linux-mips64le@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.39.tgz#6f3bf3023f711084e5a1e8190487d2020f39f0f7"
+ integrity sha512-epwlYgVdbmkuRr5n4es3B+yDI0I2e/nxhKejT9H0OLxFAlMkeQZxSpxATpDc9m8NqRci6Kwyb/SfmD1koG2Zuw==
+
+esbuild-linux-ppc64le@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.39.tgz#900e718a4ea3f6aedde8424828eeefdd4b48d4b9"
+ integrity sha512-W/5ezaq+rQiQBThIjLMNjsuhPHg+ApVAdTz2LvcuesZFMsJoQAW2hutoyg47XxpWi7aEjJGrkS26qCJKhRn3QQ==
+
+esbuild-linux-riscv64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.39.tgz#dcbff622fa37047a75d2ff7a1d8d2949d80277e4"
+ integrity sha512-IS48xeokcCTKeQIOke2O0t9t14HPvwnZcy+5baG13Z1wxs9ZrC5ig5ypEQQh4QMKxURD5TpCLHw2W42CLuVZaA==
+
+esbuild-linux-s390x@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.39.tgz#3f725a7945b419406c99d93744b28552561dcdfd"
+ integrity sha512-zEfunpqR8sMomqXhNTFEKDs+ik7HC01m3M60MsEjZOqaywHu5e5682fMsqOlZbesEAAaO9aAtRBsU7CHnSZWyA==
+
+esbuild-netbsd-64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.39.tgz#e10e40b6a765798b90d4eb85901cc85c8b7ff85e"
+ integrity sha512-Uo2suJBSIlrZCe4E0k75VDIFJWfZy+bOV6ih3T4MVMRJh1lHJ2UyGoaX4bOxomYN3t+IakHPyEoln1+qJ1qYaA==
+
+esbuild-openbsd-64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.39.tgz#935ec143f75ce10bd9cdb1c87fee00287eb0edbc"
+ integrity sha512-secQU+EpgUPpYjJe3OecoeGKVvRMLeKUxSMGHnK+aK5uQM3n1FPXNJzyz1LHFOo0WOyw+uoCxBYdM4O10oaCAA==
+
+esbuild-sunos-64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.39.tgz#0e7aa82b022a2e6d55b0646738b2582c2d72c3c0"
+ integrity sha512-qHq0t5gePEDm2nqZLb+35p/qkaXVS7oIe32R0ECh2HOdiXXkj/1uQI9IRogGqKkK+QjDG+DhwiUw7QoHur/Rwg==
+
+esbuild-windows-32@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.39.tgz#3f1538241f31b538545f4b5841b248cac260fa35"
+ integrity sha512-XPjwp2OgtEX0JnOlTgT6E5txbRp6Uw54Isorm3CwOtloJazeIWXuiwK0ONJBVb/CGbiCpS7iP2UahGgd2p1x+Q==
+
+esbuild-windows-64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.39.tgz#b100c59f96d3c2da2e796e42fee4900d755d3e03"
+ integrity sha512-E2wm+5FwCcLpKsBHRw28bSYQw0Ikxb7zIMxw3OPAkiaQhLVr3dnVO8DofmbWhhf6b97bWzg37iSZ45ZDpLw7Ow==
+
+esbuild-windows-arm64@0.14.39:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.39.tgz#00268517e665b33c89778d61f144e4256b39f631"
+ integrity sha512-sBZQz5D+Gd0EQ09tZRnz/PpVdLwvp/ufMtJ1iDFYddDaPpZXKqPyaxfYBLs3ueiaksQ26GGa7sci0OqFzNs7KA==
+
+esbuild@^0.14.27:
+ version "0.14.39"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.39.tgz#c926b2259fe6f6d3a94f528fb42e103c5a6d909a"
+ integrity sha512-2kKujuzvRWYtwvNjYDY444LQIA3TyJhJIX3Yo4+qkFlDDtGlSicWgeHVJqMUP/2sSfH10PGwfsj+O2ro1m10xQ==
+ optionalDependencies:
+ esbuild-android-64 "0.14.39"
+ esbuild-android-arm64 "0.14.39"
+ esbuild-darwin-64 "0.14.39"
+ esbuild-darwin-arm64 "0.14.39"
+ esbuild-freebsd-64 "0.14.39"
+ esbuild-freebsd-arm64 "0.14.39"
+ esbuild-linux-32 "0.14.39"
+ esbuild-linux-64 "0.14.39"
+ esbuild-linux-arm "0.14.39"
+ esbuild-linux-arm64 "0.14.39"
+ esbuild-linux-mips64le "0.14.39"
+ esbuild-linux-ppc64le "0.14.39"
+ esbuild-linux-riscv64 "0.14.39"
+ esbuild-linux-s390x "0.14.39"
+ esbuild-netbsd-64 "0.14.39"
+ esbuild-openbsd-64 "0.14.39"
+ esbuild-sunos-64 "0.14.39"
+ esbuild-windows-32 "0.14.39"
+ esbuild-windows-64 "0.14.39"
+ esbuild-windows-arm64 "0.14.39"
+
+estree-walker@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+ integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
+fsevents@~2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+is-core-module@^2.8.1:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
+ integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
+ dependencies:
+ has "^1.0.3"
+
+magic-string@^0.25.7:
+ version "0.25.9"
+ resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
+ integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
+ dependencies:
+ sourcemap-codec "^1.4.8"
+
+nanoid@^3.3.3:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
+ integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
+
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+postcss@^8.1.10, postcss@^8.4.13:
+ version "8.4.13"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575"
+ integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
+ dependencies:
+ nanoid "^3.3.3"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
+preact@^10.0.0:
+ version "10.7.2"
+ resolved "https://registry.yarnpkg.com/preact/-/preact-10.7.2.tgz#5c632ba194b87345dcaee6598b3b6529b58e6a12"
+ integrity sha512-GLjn0I3r6ka+NvxJUppsVFqb4V0qDTEHT/QxHlidPuClGaxF/4AI2Qti4a0cv3XMh5n1+D3hLScW10LRIm5msQ==
+
+prismjs@^1.25.0:
+ version "1.28.0"
+ resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.28.0.tgz#0d8f561fa0f7cf6ebca901747828b149147044b6"
+ integrity sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==
+
+resolve@^1.22.0:
+ version "1.22.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
+ integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
+ dependencies:
+ is-core-module "^2.8.1"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+rollup@^2.59.0:
+ version "2.73.0"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.73.0.tgz#128fef4b333fd92d02d6929afbb6ee38d7feb32d"
+ integrity sha512-h/UngC3S4Zt28mB3g0+2YCMegT5yoftnQplwzPqGZcKvlld5e+kT/QRmJiL+qxGyZKOYpgirWGdLyEO1b0dpLQ==
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+source-map@^0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+sourcemap-codec@^1.4.8:
+ version "1.4.8"
+ resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+ integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+vite@^2.9.7:
+ version "2.9.9"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.9.tgz#8b558987db5e60fedec2f4b003b73164cb081c5e"
+ integrity sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==
+ dependencies:
+ esbuild "^0.14.27"
+ postcss "^8.4.13"
+ resolve "^1.22.0"
+ rollup "^2.59.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+vitepress@^0.22.4:
+ version "0.22.4"
+ resolved "https://registry.yarnpkg.com/vitepress/-/vitepress-0.22.4.tgz#d4d778fb06decfc2c31c105f6a7a136843cdfb08"
+ integrity sha512-oZUnLO/SpYdThaBKefDeOiVlr0Rie4Ppx3FzMnMyLtJnI5GlBMNjqYqMy/4+umm/iC+ZDJfI+IlDKxv5fZnYzA==
+ dependencies:
+ "@docsearch/css" "^3.0.0"
+ "@docsearch/js" "^3.0.0"
+ "@vitejs/plugin-vue" "^2.3.2"
+ prismjs "^1.25.0"
+ vite "^2.9.7"
+ vue "^3.2.33"
+
+vue@^3.2.33:
+ version "3.2.33"
+ resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.33.tgz#7867eb16a3293a28c4d190a837bc447878bd64c2"
+ integrity sha512-si1ExAlDUrLSIg/V7D/GgA4twJwfsfgG+t9w10z38HhL/HA07132pUQ2KuwAo8qbCyMJ9e6OqrmWrOCr+jW7ZQ==
+ dependencies:
+ "@vue/compiler-dom" "3.2.33"
+ "@vue/compiler-sfc" "3.2.33"
+ "@vue/runtime-dom" "3.2.33"
+ "@vue/server-renderer" "3.2.33"
+ "@vue/shared" "3.2.33"
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..41d9927
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..41dfb87
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..1b6c787
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/jitpack.yml b/jitpack.yml
new file mode 100644
index 0000000..efde7bf
--- /dev/null
+++ b/jitpack.yml
@@ -0,0 +1,2 @@
+jdk:
+ - openjdk17
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 8c9a79e..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
- 4.0.0
-
- net.crystalgames
- Scaffolding
- 0.1.3-SNAPSHOT
-
-
- 17
- 17
-
-
-
-
- jitpack
- https://jitpack.io
-
-
-
-
-
- com.github.Minestom
- Minestom
- 64de8f87c0
- provided
-
-
- org.jboss.shrinkwrap.resolver
- shrinkwrap-resolver-depchain
-
-
-
-
-
-
-
- crystalgames
- https://repo.crystalgames.net/releases/
-
-
- crystalgames
- https://repo.crystalgames.net/snapshots/
-
-
-
-
diff --git a/scaffolding-core/build.gradle.kts b/scaffolding-core/build.gradle.kts
new file mode 100644
index 0000000..2495fba
--- /dev/null
+++ b/scaffolding-core/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ java
+ `maven-publish`
+}
+
+description = "Schematic library for Minestom"
+java.sourceCompatibility = JavaVersion.VERSION_17
+
+var minestomVersion = "f774cc3b0f"
+
+dependencies {
+ compileOnly("com.github.Minestom:Minestom:${minestomVersion}")
+ testImplementation("com.github.Minestom:Minestom:${minestomVersion}")
+
+ testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
+}
+
+publishing {
+ publications.create("maven") {
+ from(components["java"])
+ }
+}
\ No newline at end of file
diff --git a/scaffolding-core/src/main/java/net/crystalgames/scaffolding/Scaffolding.java b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/Scaffolding.java
new file mode 100644
index 0000000..95b7a54
--- /dev/null
+++ b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/Scaffolding.java
@@ -0,0 +1,170 @@
+package net.crystalgames.scaffolding;
+
+import net.crystalgames.scaffolding.schematic.NBTSchematicReader;
+import net.crystalgames.scaffolding.schematic.Schematic;
+import net.crystalgames.scaffolding.schematic.readers.MCEditSchematicReader;
+import net.crystalgames.scaffolding.schematic.readers.SpongeSchematicReader;
+import org.jetbrains.annotations.NotNull;
+import org.jglrxavpok.hephaistos.nbt.CompressedProcesser;
+import org.jglrxavpok.hephaistos.nbt.NBTCompound;
+import org.jglrxavpok.hephaistos.nbt.NBTException;
+import org.jglrxavpok.hephaistos.nbt.NBTReader;
+
+import java.io.*;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A static utility class primarily used to parse schematics.
+ */
+@SuppressWarnings("unused")
+public final class Scaffolding {
+
+ private static final NBTSchematicReader MC_EDIT_SCHEMATIC_READER = new MCEditSchematicReader();
+ private static final NBTSchematicReader SPONGE_SCHEMATIC_READER = new SpongeSchematicReader();
+
+ private Scaffolding() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param nbtTag The NBT tag to parse
+ * @param schematic The {@link Schematic} to load the data into
+ * @return a {@link CompletableFuture} that will be completed when the schematic is loaded
+ * @throws NBTException if the NBT tag is invalid
+ */
+ public static @NotNull CompletableFuture fromNbt(@NotNull NBTCompound nbtTag, @NotNull Schematic schematic) throws NBTException {
+ if (nbtTag.contains("Blocks")) return MC_EDIT_SCHEMATIC_READER.read(nbtTag, schematic);
+ else if (nbtTag.contains("Palette")) return SPONGE_SCHEMATIC_READER.read(nbtTag, schematic);
+ else throw new IllegalArgumentException("Unknown schematic type.");
+ }
+
+ /**
+ * @param inputStream the {@link InputStream} to read from
+ * @param schematic the {@link Schematic} to load the data into
+ * @return a {@link CompletableFuture} that will be completed when the schematic is loaded
+ * @throws IOException if the input stream is invalid
+ * @throws NBTException if the NBT tag is invalid
+ */
+ public static @NotNull CompletableFuture fromStream(@NotNull InputStream inputStream, @NotNull Schematic schematic) throws IOException, NBTException {
+ final NBTReader reader = new NBTReader(inputStream, CompressedProcesser.GZIP);
+ final NBTCompound nbtTag = (NBTCompound) reader.read();
+
+ return fromNbt(nbtTag, schematic);
+ }
+
+ /**
+ * @param file the {@link File} to read from
+ * @param schematic the {@link Schematic} to load the data into
+ * @return a {@link CompletableFuture} that will be completed when the schematic is loaded
+ * @throws IOException if the file is invalid
+ * @throws NBTException if the NBT tag is invalid
+ */
+ public static @NotNull CompletableFuture fromFile(@NotNull File file, @NotNull Schematic schematic) throws IOException, NBTException {
+ if (!file.exists()) throw new FileNotFoundException("Invalid Schematic: File does not exist");
+ return fromStream(new FileInputStream(file), schematic);
+ }
+
+ /**
+ * Automatically detects the schematic format from the provided {@link NBTCompound} and parses it.
+ *
+ * @param nbtTag The {@link NBTCompound} to read from
+ * @return A {@link CompletableFuture} that will complete with the schematic once it's loaded
+ * @throws NBTException If the NBT tag is invalid
+ */
+ public static @NotNull CompletableFuture fromNbt(@NotNull final NBTCompound nbtTag) throws NBTException, IllegalArgumentException {
+ return fromNbt(nbtTag, new Schematic());
+ }
+
+ /**
+ * Automatically detects the type of schematic and parses the input stream
+ *
+ * @param inputStream Schematic input
+ * @return a {@link CompletableFuture} that will contain the schematic once loaded
+ * @throws IOException if the input stream is invalid
+ * @throws NBTException if the schematic is invalid
+ * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic
+ */
+ public static @NotNull CompletableFuture fromStream(@NotNull final InputStream inputStream) throws IOException, NBTException, IllegalArgumentException {
+ return fromStream(inputStream, new Schematic());
+ }
+
+
+ /**
+ * Automatically detects the type of schematic and parses the file.
+ *
+ * @param file Schematic file
+ * @return parsed schematic
+ * @throws IOException if the file is invalid
+ * @throws NBTException if the schematic is invalid
+ * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic
+ */
+ public static @NotNull CompletableFuture fromFile(@NotNull final File file) throws IOException, NBTException, IllegalArgumentException {
+ return fromFile(file, new Schematic());
+ }
+
+ /**
+ * @param nbtTag The NBT tag to parse
+ * @param schematic The {@link Schematic} to load the data into
+ * @return the parsed {@link Schematic}
+ * @throws NBTException if the NBT tag is invalid
+ */
+ public static @NotNull Schematic fromNbtSync(@NotNull NBTCompound nbtTag, @NotNull Schematic schematic) throws NBTException {
+ return fromNbt(nbtTag, schematic).join();
+ }
+
+ /**
+ * @param inputStream the {@link InputStream} to read from
+ * @param schematic the {@link Schematic} to load the data into
+ * @return the parsed {@link Schematic}
+ * @throws IOException if the input stream is invalid
+ * @throws NBTException if the NBT tag is invalid
+ */
+ public static @NotNull Schematic fromStreamSync(@NotNull InputStream inputStream, @NotNull Schematic schematic) throws IOException, NBTException {
+ return fromStream(inputStream, schematic).join();
+ }
+
+ /**
+ * @param file the {@link File} to read from
+ * @param schematic the {@link Schematic} to load the data into
+ * @return the parsed {@link Schematic}
+ * @throws IOException if the file is invalid
+ * @throws NBTException if the NBT tag is invalid
+ */
+ public static @NotNull Schematic fromFileSync(@NotNull File file, @NotNull Schematic schematic) throws IOException, NBTException {
+ return fromFile(file, schematic).join();
+ }
+
+ /**
+ * Automatically detects the schematic format from the provided {@link NBTCompound} and parses it synchronously.
+ *
+ * @param nbtTag the {@link NBTCompound} to read from
+ * @return the parsed {@link Schematic}
+ * @throws NBTException if the NBT tag is invalid
+ * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic
+ */
+ public static @NotNull Schematic fromNbtSync(@NotNull final NBTCompound nbtTag) throws NBTException, IllegalArgumentException {
+ return fromNbt(nbtTag).join();
+ }
+
+ /**
+ * @param inputStream The {@link InputStream} to read from
+ * @return The parsed {@link Schematic}
+ * @throws IOException if the input stream is invalid
+ * @throws NBTException if the schematic is invalid
+ * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic
+ */
+ public static @NotNull Schematic fromStreamSync(@NotNull final InputStream inputStream) throws IOException, NBTException, IllegalArgumentException {
+ return fromStream(inputStream).join();
+ }
+
+ /**
+ * @param file The {@link File} to read from
+ * @return The parsed {@link Schematic}
+ * @throws IOException if the file is invalid
+ * @throws NBTException if the schematic is invalid
+ * @throws IllegalArgumentException if the schematic is neither an MCEdit nor a Sponge schematic
+ */
+ public static @NotNull Schematic fromFileSync(@NotNull final File file) throws IOException, NBTException, IllegalArgumentException {
+ return fromFile(file).join();
+ }
+}
diff --git a/src/main/java/net/crystalgames/scaffolding/instance/SchematicChunkLoader.java b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/instance/SchematicChunkLoader.java
similarity index 99%
rename from src/main/java/net/crystalgames/scaffolding/instance/SchematicChunkLoader.java
rename to scaffolding-core/src/main/java/net/crystalgames/scaffolding/instance/SchematicChunkLoader.java
index bc33c01..54b041b 100644
--- a/src/main/java/net/crystalgames/scaffolding/instance/SchematicChunkLoader.java
+++ b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/instance/SchematicChunkLoader.java
@@ -55,6 +55,7 @@ private SchematicChunkLoader(
/**
* Creates a builder for a {@link SchematicChunkLoader}.
+ *
* @return The builder.
*/
public static @NotNull Builder builder() {
@@ -100,6 +101,7 @@ private Builder() {
* Note that schematics are loaded in the order they are added.
*
* This means that the last added schematic is the only schematic that is guaranteed to have all its data.
+ *
* @param schematic The schematic to add.
* @return This builder.
*/
@@ -111,6 +113,7 @@ private Builder() {
/**
* Specifies the offset that applies to all schematics added to this chunk loader.
+ *
* @param x The x offset.
* @param y The y offset.
* @param z The z offset.
@@ -125,6 +128,7 @@ private Builder() {
/**
* Specifies the handler to use to save the chunks.
+ *
* @param handler The handler.
* @return This builder.
*/
diff --git a/scaffolding-core/src/main/java/net/crystalgames/scaffolding/region/Region.java b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/region/Region.java
new file mode 100644
index 0000000..b2ecb71
--- /dev/null
+++ b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/region/Region.java
@@ -0,0 +1,169 @@
+package net.crystalgames.scaffolding.region;
+
+import net.crystalgames.scaffolding.schematic.ScaffoldingUtils;
+import net.minestom.server.coordinate.Point;
+import net.minestom.server.coordinate.Vec;
+import net.minestom.server.instance.Chunk;
+import net.minestom.server.instance.Instance;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.BiFunction;
+
+/**
+ * Represents a rectangle 3 dimensional region of blocks within an {@link Instance}.
+ */
+public final class Region {
+
+ private final Instance instance;
+ private final Point lower;
+ private final Point upper;
+
+ /**
+ * Constructs a new region. The region is defined by the two provided positions. As long as the two positions are opposite of each other in the region, {@code lower} and {@code upper} will be calculated automatically.
+ *
+ * @param instance The instance that the region is in.
+ * @param p1 The first point of the region.
+ * @param p2 The second point of the region.
+ */
+ public Region(@NotNull final Instance instance, @NotNull final Point p1, @NotNull final Point p2) {
+ this.instance = Objects.requireNonNull(instance);
+ this.lower = calcPoint(p1, p2, Math::min);
+ this.upper = calcPoint(p1, p2, Math::max);
+ }
+
+ /**
+ * Force loads all {@link Chunk}s in this region.
+ *
+ * @return a {@link CompletableFuture} that will complete once all chunks in the region have been loaded. The future will give the region as the result so that you can chain it.
+ */
+ public CompletableFuture loadChunksWithinRegion() {
+ return ScaffoldingUtils.loadChunks(this);
+ }
+
+ /**
+ * @return the width of this region.
+ */
+ public int getWidth() {
+ return (upper.blockX() - lower.blockX()) + 1;
+ }
+
+ /**
+ * @return the height of this region.
+ */
+ public int getHeight() {
+ return (upper.blockY() - lower.blockY()) + 1;
+ }
+
+ /**
+ * @return the length of this region.
+ */
+ public int getLength() {
+ return (upper.blockZ() - lower.blockZ()) + 1;
+ }
+
+ /**
+ * @return the x coordinate of the upper {@link Chunk} of this region.
+ */
+ @Contract(pure = true)
+ public int getUpperChunkX() {
+ return upper.blockX() >> 4;
+ }
+
+ /**
+ * @return the z coordinate of the upper {@link Chunk} of this region.
+ */
+ @Contract(pure = true)
+ public int getUpperChunkZ() {
+ return upper.blockZ() >> 4;
+ }
+
+ /**
+ * @return the x coordinate of the lower {@link Chunk} of this region.
+ */
+ @Contract(pure = true)
+ public int getLowerChunkX() {
+ return lower.blockX() >> 4;
+ }
+
+ /**
+ * @return the z coordinate of the lower {@link Chunk} of this region.
+ */
+ @Contract(pure = true)
+ public int getLowerChunkZ() {
+ return lower.blockZ() >> 4;
+ }
+
+ /**
+ * @return the number of {@link Chunk}s along the x coordinate of this region.
+ */
+ @Contract(pure = true)
+ public int getChunkSizeX() {
+ return getUpperChunkX() - getLowerChunkX() + 1;
+ }
+
+ /**
+ * @return the number of {@link Chunk}s along the z coordinate of this region.
+ */
+ @Contract(pure = true)
+ public int getChunkSizeZ() {
+ return getUpperChunkZ() - getLowerChunkZ() + 1;
+ }
+
+ /**
+ * @return the instance that this region is in
+ */
+ @Contract(pure = true)
+ public @NotNull Instance getInstance() {
+ return instance;
+ }
+
+ /**
+ * @return the upper {@link Point} of this region.
+ */
+ @Contract(pure = true)
+ public @NotNull Point getUpper() {
+ return upper;
+ }
+
+ /**
+ * @return the lower {@link Point} of this region.
+ */
+ @Contract(pure = true)
+ public @NotNull Point getLower() {
+ return lower;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(instance, lower, upper);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) return true;
+ if (obj == null || obj.getClass() != this.getClass()) return false;
+ var that = (Region) obj;
+ return Objects.equals(this.instance, that.instance) &&
+ Objects.equals(this.lower, that.lower) &&
+ Objects.equals(this.upper, that.upper);
+ }
+
+ @Override
+ public String toString() {
+ return "Region[" +
+ "instance=" + instance + ", " +
+ "lower=" + lower + ", " +
+ "upper=" + upper + ']';
+ }
+
+ private @NotNull Point calcPoint(@NotNull Point p1, @NotNull Point p2, BiFunction operation) {
+ final int x = operation.apply(p1.blockX(), p2.blockX());
+ final int y = operation.apply(p1.blockY(), p2.blockY());
+ final int z = operation.apply(p1.blockZ(), p2.blockZ());
+
+ return new Vec(x, y, z);
+ }
+}
diff --git a/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/NBTSchematicReader.java b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/NBTSchematicReader.java
new file mode 100644
index 0000000..3785ebf
--- /dev/null
+++ b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/NBTSchematicReader.java
@@ -0,0 +1,122 @@
+package net.crystalgames.scaffolding.schematic;
+
+import org.jetbrains.annotations.NotNull;
+import org.jglrxavpok.hephaistos.collections.ImmutableByteArray;
+import org.jglrxavpok.hephaistos.nbt.NBTCompound;
+import org.jglrxavpok.hephaistos.nbt.NBTException;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A parser for schematics that uses NBT to store data.
+ */
+public abstract class NBTSchematicReader {
+
+ /**
+ * Parses data from the provided NBT tag and stores it in the provided schematic.
+ *
+ * @param compound The {@link NBTCompound} to read from
+ * @param schematic The {@link Schematic} to read into
+ * @return a {@link CompletableFuture} that will be completed with the {@link Schematic}
+ * @throws NBTException If the provided NBT tag is invalid
+ */
+ public abstract CompletableFuture read(@NotNull final NBTCompound compound, @NotNull final Schematic schematic) throws NBTException;
+
+ /**
+ * @param compound The {@link NBTCompound} to read from
+ * @param key The key to look for
+ * @param exceptionMessage The exception message to throw if the key is not found
+ * @return The value of the key
+ * @throws NBTException If the provided NBT tag is invalid
+ */
+ protected int getInteger(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException {
+ Integer value = compound.getInt(key);
+ if (value == null) throw new NBTException(exceptionMessage);
+
+ return value;
+ }
+
+ /**
+ * @param compound The {@link NBTCompound} to read from
+ * @param key The key to look for
+ * @param exceptionMessage The exception message to throw if the key is not found
+ * @return The value of the key
+ * @throws NBTException If the provided NBT tag is invalid
+ */
+ protected short getShort(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException {
+ Short value = compound.getShort(key);
+ if (value == null) throw new NBTException(exceptionMessage);
+
+ return value;
+ }
+
+ /**
+ * @param compound The {@link NBTCompound} to read from
+ * @param key The key to look for
+ * @param exceptionMessage The exception message to throw if the key is not found
+ * @return The value of the key
+ * @throws NBTException If the provided NBT tag is invalid
+ */
+ protected NBTCompound getCompound(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException {
+ NBTCompound value = compound.getCompound(key);
+ if (value == null) throw new NBTException(exceptionMessage);
+
+ return value;
+ }
+
+ /**
+ * @param compound The {@link NBTCompound} to read from
+ * @param key The key to look for
+ * @param exceptionMessage The exception message to throw if the key is not found
+ * @return The value of the key
+ * @throws NBTException If the provided NBT tag is invalid
+ */
+ protected byte getByte(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException {
+ Byte value = compound.getByte(key);
+ if (value == null) throw new NBTException(exceptionMessage);
+
+ return value;
+ }
+
+ /**
+ * @param compound The {@link NBTCompound} to read from
+ * @param key The key to look for
+ * @param exceptionMessage The exception message to throw if the key is not found
+ * @return The value of the key
+ * @throws NBTException If the provided NBT tag is invalid
+ */
+ protected byte[] getByteArray(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException {
+ ImmutableByteArray value = compound.getByteArray(key);
+ if (value == null) throw new NBTException(exceptionMessage);
+
+ return value.copyArray();
+ }
+
+ /**
+ * @param compound The {@link NBTCompound} to read from
+ * @param key The key to look for
+ * @param exceptionMessage The exception message to throw if the key is not found
+ * @return The value of the key
+ * @throws NBTException If the provided NBT tag is invalid
+ */
+ protected boolean getBoolean(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException {
+ Boolean value = compound.getBoolean(key);
+ if (value == null) throw new NBTException(exceptionMessage);
+
+ return value;
+ }
+
+ /**
+ * @param compound The {@link NBTCompound} to read from
+ * @param key The key to look for
+ * @param exceptionMessage The exception message to throw if the key is not found
+ * @return The value of the key
+ * @throws NBTException If the provided NBT tag is invalid
+ */
+ protected String getString(@NotNull NBTCompound compound, @NotNull String key, String exceptionMessage) throws NBTException {
+ String value = compound.getString(key);
+ if (value == null) throw new NBTException(exceptionMessage);
+
+ return value;
+ }
+}
diff --git a/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/ScaffoldingUtils.java b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/ScaffoldingUtils.java
new file mode 100644
index 0000000..dcfcac6
--- /dev/null
+++ b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/ScaffoldingUtils.java
@@ -0,0 +1,97 @@
+package net.crystalgames.scaffolding.schematic;
+
+import net.crystalgames.scaffolding.region.Region;
+import net.crystalgames.scaffolding.schematic.readers.MCEditSchematicReader;
+import net.minestom.server.instance.Chunk;
+import net.minestom.server.instance.Instance;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A static utility class containing useful methods used throughout Scaffolding.
+ */
+@ApiStatus.Internal
+public final class ScaffoldingUtils {
+
+ // TODO: Replace with a collection that doesn't require autoboxing
+ private static final HashMap LEGACY_LOOKUP = new HashMap<>();
+
+ // This is awful, but it'll work for now. TODO: rewrite
+ static {
+ try {
+ // Load state IDS from lookup table
+ InputStream is = MCEditSchematicReader.class.getClassLoader().getResourceAsStream("LegacyLookupTable.txt");
+ BufferedInputStream bis = new BufferedInputStream(Objects.requireNonNull(is));
+ String raw = new String(bis.readAllBytes());
+ for (String line : raw.split("\n")) {
+ String[] split = line.split("=");
+ String[] key = split[0].split(":");
+
+ int blockId = Integer.parseInt(key[0]);
+ byte blockData = Byte.parseByte(key[1]);
+
+ LEGACY_LOOKUP.put(getLookupId(blockId, blockData), Short.parseShort(split[1]));
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private ScaffoldingUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param legacyBlockId The legacy block ID
+ * @param legacyBlockData The legacy block data
+ * @return The modern state ID for the given legacy block ID and data
+ */
+ public static short stateIdFromLegacy(final int legacyBlockId, final byte legacyBlockData) {
+ final int lookupId = getLookupId(legacyBlockId, legacyBlockData);
+ return LEGACY_LOOKUP.get(lookupId);
+ }
+
+ /**
+ * Used to get the lookup ID for the given block ID and data from the legacy lookup table.
+ *
+ * @param legacyBlockId the legacy block ID
+ * @param legacyBlockData the legacy block data
+ * @return the lookup ID
+ */
+ @Contract(pure = true)
+ private static int getLookupId(final int legacyBlockId, final byte legacyBlockData) {
+ return legacyBlockId << 8 | legacyBlockData;
+ }
+
+ /**
+ * Force loads all {@link Chunk}s in the given {@link Region}.
+ *
+ * @param region the {@link Region} in which to load chunks
+ * @return a {@link CompletableFuture} that will complete once all chunks in the region have been loaded. The future will give the region as the result so that you can chain it.
+ */
+ public static @NotNull CompletableFuture loadChunks(@NotNull final Region region) {
+ final Instance instance = region.getInstance();
+
+ final int lengthX = region.getUpperChunkX() - region.getLowerChunkX() + 1;
+ final int lengthZ = region.getUpperChunkZ() - region.getLowerChunkZ() + 1;
+
+ final CompletableFuture>[] futures = new CompletableFuture[lengthX * lengthZ];
+ int index = 0;
+
+ for (int x = region.getLowerChunkX(); x <= region.getUpperChunkX(); ++x) {
+ for (int z = region.getLowerChunkZ(); z <= region.getUpperChunkZ(); ++z) {
+ futures[index++] = instance.loadChunk(x, z);
+ }
+ }
+
+ return CompletableFuture.allOf(futures).thenApply(v -> region);
+ }
+}
diff --git a/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/Schematic.java b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/Schematic.java
new file mode 100644
index 0000000..7a8ffa1
--- /dev/null
+++ b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/Schematic.java
@@ -0,0 +1,401 @@
+package net.crystalgames.scaffolding.schematic;
+
+import net.crystalgames.scaffolding.region.Region;
+import net.minestom.server.coordinate.Point;
+import net.minestom.server.coordinate.Vec;
+import net.minestom.server.instance.Instance;
+import net.minestom.server.instance.batch.AbsoluteBlockBatch;
+import net.minestom.server.instance.block.Block;
+import net.minestom.server.instance.generator.GenerationUnit;
+import net.minestom.server.instance.generator.UnitModifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A parsed schematic.
+ */
+public class Schematic implements Block.Setter {
+
+ private short[] blocks;
+
+ private int width, height, length;
+ private int offsetX, offsetY, offsetZ;
+ private int area;
+
+ private boolean locked;
+
+ /**
+ * Constructs a new schematic. The schematic will be locked and have an area of 0.
+ */
+ public Schematic() {
+ reset();
+ }
+
+ /**
+ * Resets this schematic to its original state. This is useful if you want to reuse a schematic multiple times.
+ *
+ * The schematic will be locked after this method is called.
+ */
+ public void reset() {
+ width = height = length = 0;
+ offsetX = offsetY = offsetZ = 0;
+
+ blocks = null; // Does this actually have to be nulled out? This looks a bit too much like a deconstructor.
+ setLocked(true);
+ }
+
+ /**
+ * Copies blocks from the given region into this schematic.
+ *
+ * @param region the {@link Region} to copy from
+ * @return a {@link CompletableFuture} that will complete once all blocks have been copied
+ */
+ public @NotNull CompletableFuture copy(@NotNull final Region region) {
+ reset();
+
+ return ScaffoldingUtils.loadChunks(region).thenApply((v) -> {
+ final Instance instance = region.getInstance();
+ final Point lower = region.getLower();
+ final int width = region.getWidth();
+ final int height = region.getHeight();
+ final int length = region.getLength();
+
+ setSize(width, height, length);
+
+ for (int x = 0; x < width; ++x) {
+ for (int y = 0; y < height; ++y) {
+ for (int z = 0; z < length; ++z) {
+ final int blockX = lower.blockX() + x;
+ final int blockY = lower.blockY() + y;
+ final int blockZ = lower.blockZ() + z;
+
+ final Block block = region.getInstance().getBlock(blockX, blockY, blockZ, Block.Getter.Condition.TYPE);
+
+ if (block != null) blocks[getBlockIndex(x, y, z)] = block.stateId();
+ }
+ }
+ }
+
+ locked = false;
+ return this;
+ });
+ }
+
+ /**
+ * Sets the size of this schematic. {@code area} will be updated accordingly.
+ *
+ * @param width new width
+ * @param height new height
+ * @param length new length
+ */
+ public void setSize(int width, int height, int length) {
+ this.width = width;
+ this.height = height;
+ this.length = length;
+
+ area = width * height * length;
+ blocks = new short[area];
+ }
+
+ /**
+ * Gets the index of a block in this schematic if blocks were stored in a 1-dimensional array.
+ *
+ * @param x block x coordinate
+ * @param y block y coordinate
+ * @param z block z coordinate
+ * @return the index of the block at the given coordinates
+ */
+ public int getBlockIndex(int x, int y, int z) {
+ return y * width * length + z * width + x;
+ }
+
+ /**
+ * Builds this schematic in the given {@link Instance} at the given {@link Point}.
+ *
+ * @param instance the {@link Instance} to build this schematic in
+ * @param position the {@link Point} to build this schematic at (note: the schematics offset will be applied to this position to get the lower corner)
+ * @return a {@link CompletableFuture} that will complete once the schematic has been built
+ */
+ public @NotNull CompletableFuture build(@NotNull final Instance instance, @NotNull final Point position) {
+ return build(instance, position, false, false, false);
+ }
+
+ /**
+ * Builds this schematic in the given {@link Instance} at the given {@link Point}. The schematic can be flipped along the X, Y, or Z axis using the {@code flipX}, {@code flipY}, and {@code flipZ} parameters.
+ *
+ * @param instance the {@link Instance} to build this schematic in
+ * @param position the {@link Point} to build this schematic at (note: the schematics offset will be applied to this position to get the lower corner)
+ * @param flipX whether to flip the schematic along the X axis
+ * @param flipY whether to flip the schematic along the Y axis
+ * @param flipZ whether to flip the schematic along the Z axis
+ * @return a {@link CompletableFuture} that will complete once the schematic has been built
+ */
+ public @NotNull CompletableFuture build(@NotNull final Instance instance, @NotNull final Point position, final boolean flipX, final boolean flipY, final boolean flipZ) {
+ if (locked) throw new IllegalStateException("Cannot build a locked schematic.");
+
+ final Region region = getContainingRegion(instance, position);
+ if (!isPlaceable(region))
+ throw new IllegalStateException("Cannot build schematic at this position since blocks would go outside of world boundaries. " + position);
+
+ return ScaffoldingUtils.loadChunks(region).thenApplyAsync((ignored) -> {
+ final AbsoluteBlockBatch blockBatch = new AbsoluteBlockBatch();
+
+ apply(region.getLower(), flipX, flipY, flipZ, blockBatch);
+ System.out.println("Building schematic.");
+
+ final CompletableFuture future = new CompletableFuture<>();
+ blockBatch.apply(instance, () -> future.complete(null));
+ return future.join();
+ });
+ }
+
+ /**
+ * @param instance the {@link Instance} to check
+ * @param position the {@link Point} to check
+ * @return the {@link Region} that this schematic would take up if placed at the given position
+ */
+ public @NotNull Region getContainingRegion(@NotNull final Instance instance, @NotNull final Point position) {
+ return new Region(instance, position.add(offsetX, offsetY, offsetZ), position.add(offsetX + width, offsetY + height, offsetZ + length));
+ }
+
+ @ApiStatus.Internal
+ private boolean isPlaceable(@NotNull final Region region) {
+ final Instance instance = region.getInstance();
+
+ final boolean isAboveWorldBounds = region.getUpper().blockY() >= instance.getDimensionType().getMaxY();
+ final boolean isBelowWorldBounds = region.getLower().blockY() < instance.getDimensionType().getMinY();
+
+ return !(isAboveWorldBounds || isBelowWorldBounds);
+ }
+
+ /**
+ * Applies this schematic to the given {@link Block.Setter} at the given {@link Point}. The schematic can be flipped along the X, Y, or Z axis using the {@code flipX}, {@code flipY}, and {@code flipZ} parameters.
+ *
+ * @param position the {@link Point} to apply this schematic at within the given {@link Block.Setter}. Acts like an offset.
+ * @param flipX whether to flip the schematic along the X axis
+ * @param flipY whether to flip the schematic along the Y axis
+ * @param flipZ whether to flip the schematic along the Z axis
+ * @param setter the {@link Block.Setter} to apply this schematic to
+ */
+ public void apply(@NotNull final Point position, final boolean flipX, final boolean flipY, final boolean flipZ, @NotNull final Block.Setter setter) {
+ if (locked) throw new IllegalStateException("Cannot apply a locked schematic.");
+
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ for (int z = 0; z < length; z++) {
+ // Will the JVM optimize out the ternary operator? I hope so.
+ final int relativeBlockX = flipX ? width - x - 1 : x;
+ final int relativeBlockY = flipY ? height - y - 1 : y;
+ final int relativeBlockZ = flipZ ? length - z - 1 : z;
+
+ final int absoluteX = position.blockX() + x;
+ final int absoluteY = position.blockY() + y;
+ final int absoluteZ = position.blockZ() + z;
+
+ final Block block = getBlock(relativeBlockX, relativeBlockY, relativeBlockZ);
+
+ if (block != null) setter.setBlock(absoluteX, absoluteY, absoluteZ, block);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param x block x coordinate
+ * @param y block y coordinate
+ * @param z block z coordinate
+ * @return the block at the given coordinates
+ */
+ @Nullable
+ public Block getBlock(final int x, final int y, final int z) {
+ short stateId = getStateId(x, y, z);
+ return Block.fromStateId(stateId);
+ }
+
+ /**
+ * @param x block x coordinate
+ * @param y block y coordinate
+ * @param z block z coordinate
+ * @return the state ID at the given coordinates
+ */
+ public short getStateId(int x, int y, int z) {
+ return blocks[getBlockIndex(x, y, z)];
+ }
+
+ /**
+ * @param unit the {@link GenerationUnit} to fork
+ * @param position the {@link Point} to place the schematic at. Offsets will be applied to this position to get the lower corner.
+ * @param flipX whether to flip the schematic along the X axis
+ * @param flipY whether to flip the schematic along the Y axis
+ * @param flipZ whether to flip the schematic along the Z axis
+ */
+ public void fork(@NotNull GenerationUnit unit, @NotNull Point position, boolean flipX, boolean flipY, boolean flipZ) {
+ if (locked) throw new IllegalStateException("Cannot fork a locked schematic.");
+
+ final Point start = position.sub(offsetX, offsetY, offsetZ);
+ final Point end = start.add(width, height, length);
+
+ UnitModifier forkModifier = unit.fork(start, end).modifier();
+
+ apply(position, flipX, flipY, flipZ, forkModifier);
+ }
+
+ /**
+ * @param position the {@link Point} of the block to set
+ * @param block the {@link Block} to set
+ */
+ public void setBlock(@NotNull Point position, @NotNull Block block) {
+ setBlock(position.blockX(), position.blockY(), position.blockZ(), block);
+ }
+
+ public void setBlock(int x, int y, int z, @NotNull Block block) {
+ setBlock(x, y, z, block.stateId());
+ }
+
+ /**
+ * @param x the X coordinate
+ * @param y the Y coordinate
+ * @param z the Z coordinate
+ * @param stateId the state ID
+ */
+ public void setBlock(int x, int y, int z, short stateId) {
+ blocks[getBlockIndex(x, y, z)] = stateId;
+ }
+
+ /**
+ * @param position the {@link Point} to place the block at
+ * @param stateId the state id of the block to place.
+ */
+ public void setBlock(@NotNull Point position, short stateId) {
+ setBlock(position.blockX(), position.blockY(), position.blockZ(), stateId);
+ }
+
+ /**
+ * @return the width of this schematic
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * @return the height of this schematic
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * @return the length of the schematic
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * Gets the offset in the x-axis used when {@link #build(Instance, Point)} or {@link #apply(Point, boolean, boolean, boolean, Block.Setter)} are called.
+ *
+ * @return the x offset
+ */
+ public int getOffsetX() {
+ return offsetX;
+ }
+
+ /**
+ * Gets the offset in the y-axis used when {@link #build(Instance, Point)} or {@link #apply(Point, boolean, boolean, boolean, Block.Setter)} are called.
+ *
+ * @return the y offset
+ */
+ public int getOffsetY() {
+ return offsetY;
+ }
+
+ /**
+ * Gets the offset in the z-axis used when {@link #build(Instance, Point)} or {@link #apply(Point, boolean, boolean, boolean, Block.Setter)} are called.
+ *
+ * @return the z offset
+ */
+ public int getOffsetZ() {
+ return offsetZ;
+ }
+
+ /**
+ * Gets the area of this schematic. ({@code width} * {@code height} * {@code length})
+ *
+ * @return the area of this schematic
+ */
+ public int getArea() {
+ return area;
+ }
+
+ /**
+ * @return true if this schematic is locked, false otherwise
+ */
+ public boolean isLocked() {
+ return locked;
+ }
+
+ /**
+ * Sets the locked state of this schematic. Locked schematics can't be built, applied or forked, or saved.
+ *
+ * @param locked whether to lock this schematic
+ */
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ }
+
+ /**
+ * @param offset the {@link Point} to offset this schematic by
+ */
+ public void setOffset(@NotNull final Point offset) {
+ setOffset(offset.blockX(), offset.blockY(), offset.blockZ());
+ }
+
+ /**
+ * @param x new x offset
+ * @param y new y offset
+ * @param z new z offset
+ */
+ public void setOffset(int x, int y, int z) {
+ offsetX = x;
+ offsetY = y;
+ offsetZ = z;
+ }
+
+ /**
+ * Applies the schematic to the given block setter.
+ *
+ * @param setter the block setter
+ */
+ @Deprecated
+ public void apply(@NotNull Block.Setter setter) {
+ apply(Vec.ZERO, false, false, false, setter);
+ }
+
+ /**
+ * @param instance the {@link Instance} to check
+ * @param position the {@link Point} to check
+ * @return {@code true} if the given position is within the bounds of the given instance, {@code false} otherwise. If either the instance or the position is null, false is returned.
+ */
+ public boolean isPlaceable(@Nullable final Instance instance, @Nullable final Point position) {
+ if (instance == null || position == null) return false;
+
+ return isPlaceable(getContainingRegion(instance, position));
+ }
+
+ @Override
+ public String toString() {
+ return "Schematic{" +
+ ", width=" + width +
+ ", height=" + height +
+ ", length=" + length +
+ ", offsetX=" + offsetX +
+ ", offsetY=" + offsetY +
+ ", offsetZ=" + offsetZ +
+ ", area=" + area +
+ ", locked=" + locked +
+ '}';
+ }
+}
diff --git a/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/readers/MCEditSchematicReader.java b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/readers/MCEditSchematicReader.java
new file mode 100644
index 0000000..c67b0ea
--- /dev/null
+++ b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/readers/MCEditSchematicReader.java
@@ -0,0 +1,99 @@
+package net.crystalgames.scaffolding.schematic.readers;
+
+import net.crystalgames.scaffolding.schematic.NBTSchematicReader;
+import net.crystalgames.scaffolding.schematic.ScaffoldingUtils;
+import net.crystalgames.scaffolding.schematic.Schematic;
+import org.jetbrains.annotations.NotNull;
+import org.jglrxavpok.hephaistos.nbt.NBTCompound;
+import org.jglrxavpok.hephaistos.nbt.NBTException;
+
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+/**
+ * A parser for MCEdit schematics. (.schematic files)
+ *
+ *
MCEdit format specification
+ *
Reference parser
+ */
+public class MCEditSchematicReader extends NBTSchematicReader {
+
+ @Override
+ public CompletableFuture read(@NotNull final NBTCompound nbtTag, @NotNull final Schematic schematic) {
+ schematic.reset();
+
+ return CompletableFuture.supplyAsync(() -> {
+ try {
+ if (!nbtTag.containsKey("Blocks")) throw new NBTException("Invalid Schematic: No Blocks");
+
+ readSizes(schematic, nbtTag);
+ readOffsets(schematic, nbtTag);
+ readBlocksData(schematic, nbtTag);
+
+ schematic.setLocked(false);
+
+ return schematic;
+ } catch (Exception e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ private void readSizes(@NotNull Schematic schematic, @NotNull NBTCompound nbtTag) throws NBTException {
+ short width = getShort(nbtTag, "Width", "Invalid Schematic: No Width");
+ short height = getShort(nbtTag, "Height", "Invalid Schematic: No Height");
+ short length = getShort(nbtTag, "Length", "Invalid Schematic: No Length");
+
+ schematic.setSize(width, height, length);
+ }
+
+ private void readOffsets(@NotNull Schematic schematic, @NotNull NBTCompound nbtTag) throws NBTException {
+ int weOffsetX = getInteger(nbtTag, "WEOffsetX", "Invalid Schematic: No WEOffsetX");
+ int weOffsetY = getInteger(nbtTag, "WEOffsetY", "Invalid Schematic: No WEOffsetY");
+ int weOffsetZ = getInteger(nbtTag, "WEOffsetZ", "Invalid Schematic: No WEOffsetZ");
+
+ schematic.setOffset(weOffsetX, weOffsetY, weOffsetZ);
+ }
+
+ private void readBlocksData(@NotNull Schematic schematic, @NotNull NBTCompound nbtTag) throws NBTException {
+ String materials = getString(nbtTag, "Materials", "Invalid Schematic: No Materials");
+ if (!materials.equals("Alpha")) throw new NBTException("Invalid Schematic: Invalid Materials");
+
+ byte[] blocks = getByteArray(nbtTag, "Blocks", "Invalid Schematic: No Blocks");
+ byte[] blockData = getByteArray(nbtTag, "Data", "Invalid Schematic: No Block Data");
+
+ // Each "add block" contains the upper 4 bits for 2 blocks packed in one byte
+ // addBlocks.length / 2 = number of blocks
+ byte[] addBlocks = nbtTag.containsKey("AddBlocks") ? Objects.requireNonNull(nbtTag.getByteArray("AddBlocks")).copyArray() : new byte[0];
+
+ short[] outdatedBlockIds = new short[schematic.getArea()];
+
+ for (int index = 0; index < blocks.length; index++) {
+ final int halfIndex = index >> 1; // same as 'index / 2'
+ short addAmount = 0;
+
+ if (halfIndex < addBlocks.length) {
+ final short rawAdd = (short) (addBlocks[halfIndex] & 0b11111111);
+ // If index is even, we want to shift 8 bits (a full byte) to the left, otherwise 4 since a single byte holds 2 blocks.
+ final int leftShiftAmount = (index % 2 == 0) ? 8 : 4;
+ addAmount = (short) (rawAdd << leftShiftAmount);
+ }
+
+ outdatedBlockIds[index] = (short) (addAmount + (blocks[index] & 0b11111111));
+ }
+
+ for (int x = 0; x < schematic.getWidth(); ++x) {
+ for (int y = 0; y < schematic.getHeight(); ++y) {
+ for (int z = 0; z < schematic.getLength(); ++z) {
+ int index = schematic.getBlockIndex(x, y, z);
+ // Let's just ignore unknown blocks for now
+ // TODO: log when unknown blocks are encountered?
+ short stateId = ScaffoldingUtils.stateIdFromLegacy(outdatedBlockIds[index], blockData[index]);
+
+ schematic.setBlock(x, y, z, stateId);
+ }
+ }
+ }
+ }
+}
diff --git a/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/readers/SpongeSchematicReader.java b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/readers/SpongeSchematicReader.java
new file mode 100644
index 0000000..62b0add
--- /dev/null
+++ b/scaffolding-core/src/main/java/net/crystalgames/scaffolding/schematic/readers/SpongeSchematicReader.java
@@ -0,0 +1,136 @@
+package net.crystalgames.scaffolding.schematic.readers;
+
+import net.crystalgames.scaffolding.schematic.NBTSchematicReader;
+import net.crystalgames.scaffolding.schematic.Schematic;
+import net.minestom.server.instance.block.Block;
+import org.jetbrains.annotations.NotNull;
+import org.jglrxavpok.hephaistos.collections.ImmutableByteArray;
+import org.jglrxavpok.hephaistos.nbt.NBTCompound;
+import org.jglrxavpok.hephaistos.nbt.NBTException;
+
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+/**
+ * A parser for Sponge schematics. (.schem files)
+ *
+ *
Sponge format specification
+ *
Reference parser
+ */
+public class SpongeSchematicReader extends NBTSchematicReader {
+
+ @Override
+ public CompletableFuture read(@NotNull final NBTCompound nbtTag, @NotNull final Schematic schematic) {
+ schematic.reset();
+
+ return CompletableFuture.supplyAsync(() -> {
+ try {
+ readSizes(schematic, nbtTag);
+ readOffsets(schematic, nbtTag);
+ readBlockPalette(schematic, nbtTag);
+
+ schematic.setLocked(false);
+ return schematic;
+ } catch (Exception e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ private void readSizes(@NotNull Schematic schematic, @NotNull NBTCompound nbtTag) throws NBTException {
+ short width = getShort(nbtTag, "Width", "Invalid Schematic: No Width");
+ short height = getShort(nbtTag, "Height", "Invalid Schematic: No Height");
+ short length = getShort(nbtTag, "Length", "Invalid Schematic: No Length");
+ schematic.setSize(width, height, length);
+ }
+
+ private void readOffsets(@NotNull Schematic schematic, @NotNull NBTCompound nbtTag) throws NBTException {
+ NBTCompound metadata = getCompound(nbtTag, "Metadata", "Invalid Schematic: No Metadata");
+ int weOffsetX = getInteger(metadata, "WEOffsetX", "Invalid Schematic: No WEOffsetX In Metadata");
+ int weOffsetY = getInteger(metadata, "WEOffsetY", "Invalid Schematic: No WEOffsetY In Metadata");
+ int weOffsetZ = getInteger(metadata, "WEOffsetZ", "Invalid Schematic: No WEOffsetZ In Metadata");
+ schematic.setOffset(weOffsetX, weOffsetY, weOffsetZ);
+ }
+
+ private void readBlockPalette(@NotNull Schematic schematic, @NotNull NBTCompound nbtTag) throws NBTException {
+ int maxPalette = getInteger(nbtTag, "PaletteMax", "Invalid Schematic: No PaletteMax");
+ NBTCompound nbtPalette = getCompound(nbtTag, "Palette", "Invalid Schematic: No Palette");
+
+ Set keys = nbtPalette.getKeys();
+ if (keys.size() != maxPalette)
+ throw new NBTException("Invalid Schematic: PaletteMax does not match Palette size");
+
+ final Map unsortedPalette = new HashMap<>();
+ for (String key : keys)
+ unsortedPalette.put(key, getInteger(nbtPalette, key, "Invalid Schematic: Palette contains invalid value"));
+
+
+ final Map palette = unsortedPalette.entrySet().stream()
+ .sorted(Map.Entry.comparingByValue())
+ .collect(LinkedHashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), LinkedHashMap::putAll);
+
+ ImmutableByteArray blocksData = nbtTag.getByteArray("BlockData");
+ if (blocksData == null || blocksData.getSize() == 0) throw new NBTException("Invalid Schematic: No BlockData");
+ byte[] blocksDataArr = blocksData.copyArray();
+
+ int index = 0;
+ int i = 0;
+ int value;
+ int varIntLength;
+ List paletteKeys = new ArrayList<>(palette.keySet());
+
+ while (i < blocksDataArr.length) {
+ value = 0;
+ varIntLength = 0;
+
+ while (true) {
+ value |= (blocksDataArr[i] & 127) << (varIntLength++ * 7);
+ if (varIntLength > 5) throw new NBTException("Invalid Schematic: BlockData has invalid length");
+ if ((blocksDataArr[i] & 128) != 128) {
+ i++;
+ break;
+ }
+ i++;
+ }
+
+ int x = (index % (schematic.getWidth() * schematic.getLength())) % schematic.getWidth();
+ int y = index / (schematic.getWidth() * schematic.getLength());
+ int z = (index % (schematic.getWidth() * schematic.getLength())) / schematic.getWidth();
+
+ String block = paletteKeys.get(value);
+ short stateId = getStateId(block);
+
+ schematic.setBlock(x, y, z, stateId);
+
+ index++;
+ }
+ }
+
+ private short getStateId(@NotNull String input) {
+ Block block = getBlock(input);
+ if (block == null) return 0;
+ String states = input.replaceAll(block.name(), "");
+
+ if (states.startsWith("[")) {
+ String[] stateArray = states.substring(1, states.length() - 1).split(",");
+ Map properties = new HashMap<>(block.properties());
+ for (String state : stateArray) {
+ String[] split = state.split("=");
+ properties.replace(split[0], split[1]);
+ }
+ try {
+ return block.withProperties(properties).stateId();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return block.stateId();
+ }
+ } else return block.stateId();
+ }
+
+ private Block getBlock(@NotNull String input) {
+ String namespaceId = input.split("\\[")[0];
+
+ return Block.fromNamespaceId(namespaceId);
+ }
+}
diff --git a/scaffolding-core/src/main/resources/LegacyLookupTable.txt b/scaffolding-core/src/main/resources/LegacyLookupTable.txt
new file mode 100644
index 0000000..debe92c
--- /dev/null
+++ b/scaffolding-core/src/main/resources/LegacyLookupTable.txt
@@ -0,0 +1,1682 @@
+0:0=0
+1:0=1
+1:1=2
+1:2=3
+1:3=4
+1:4=5
+1:5=6
+1:6=7
+2:0=9
+3:0=10
+3:1=11
+3:2=13
+4:0=14
+5:0=15
+5:1=16
+5:2=17
+5:3=18
+5:4=19
+5:5=20
+6:0=21
+6:1=23
+6:2=25
+6:3=27
+6:4=29
+6:5=31
+6:8=22
+6:9=24
+6:10=26
+6:11=28
+6:12=30
+6:13=32
+7:0=33
+8:0=34
+8:1=35
+8:2=36
+8:3=37
+8:4=38
+8:5=39
+8:6=40
+8:7=41
+8:8=42
+8:9=43
+8:10=44
+8:11=45
+8:12=46
+8:13=47
+8:14=48
+8:15=49
+9:0=34
+9:1=35
+9:2=36
+9:3=37
+9:4=38
+9:5=39
+9:6=40
+9:7=41
+9:8=42
+9:9=43
+9:10=44
+9:11=45
+9:12=46
+9:13=47
+9:14=48
+9:15=49
+10:0=50
+10:1=51
+10:2=52
+10:3=53
+10:4=54
+10:5=55
+10:6=56
+10:7=57
+10:8=58
+10:9=59
+10:10=60
+10:11=61
+10:12=62
+10:13=63
+10:14=64
+10:15=65
+11:0=50
+11:1=51
+11:2=52
+11:3=53
+11:4=54
+11:5=55
+11:6=56
+11:7=57
+11:8=58
+11:9=59
+11:10=60
+11:11=61
+11:12=62
+11:13=63
+11:14=64
+11:15=65
+12:0=66
+12:1=67
+13:0=68
+14:0=69
+15:0=71
+16:0=73
+17:0=77
+17:1=80
+17:2=83
+17:3=86
+17:4=76
+17:5=79
+17:6=82
+17:7=85
+17:8=78
+17:9=81
+17:10=84
+17:11=87
+17:12=113
+17:13=116
+17:14=119
+17:15=122
+18:0=149
+18:1=163
+18:2=177
+18:3=191
+18:4=148
+18:5=162
+18:6=176
+18:7=190
+18:8=149
+18:9=163
+18:10=177
+18:11=191
+18:12=148
+18:13=162
+18:14=176
+18:15=190
+19:0=260
+19:1=261
+20:0=262
+21:0=263
+22:0=265
+23:0=277
+23:1=275
+23:2=267
+23:3=271
+23:4=273
+23:5=269
+23:8=276
+23:9=274
+23:10=266
+23:11=270
+23:12=272
+23:13=268
+24:0=278
+24:1=279
+24:2=280
+25:0=282
+26:0=1312
+26:1=1316
+26:2=1308
+26:3=1320
+26:4=1310
+26:5=1314
+26:6=1306
+26:7=1318
+26:8=1311
+26:9=1315
+26:10=1307
+26:11=1319
+26:12=1309
+26:13=1313
+26:14=1305
+26:15=1317
+27:0=1350
+27:1=1352
+27:2=1354
+27:3=1356
+27:4=1358
+27:5=1360
+27:8=1338
+27:9=1340
+27:10=1342
+27:11=1344
+27:12=1346
+27:13=1348
+28:0=1374
+28:1=1376
+28:2=1378
+28:3=1380
+28:4=1382
+28:5=1384
+28:8=1362
+28:9=1364
+28:10=1366
+28:11=1368
+28:12=1370
+28:13=1372
+29:0=1396
+29:1=1395
+29:2=1391
+29:3=1393
+29:4=1394
+29:5=1392
+29:8=1390
+29:9=1389
+29:10=1385
+29:11=1387
+29:12=1388
+29:13=1386
+30:0=1397
+31:0=1400
+31:1=1398
+31:2=1399
+32:0=1400
+33:0=1415
+33:1=1414
+33:2=1410
+33:3=1412
+33:4=1413
+33:5=1411
+33:8=1409
+33:9=1408
+33:10=1404
+33:11=1406
+33:12=1407
+33:13=1405
+34:0=1438
+34:1=1434
+34:2=1418
+34:3=1426
+34:4=1430
+34:5=1422
+34:8=1439
+34:9=1435
+34:10=1419
+34:11=1427
+34:12=1431
+34:13=1423
+35:0=1440
+35:1=1441
+35:2=1442
+35:3=1443
+35:4=1444
+35:5=1445
+35:6=1446
+35:7=1447
+35:8=1448
+35:9=1449
+35:10=1450
+35:11=1451
+35:12=1452
+35:13=1453
+35:14=1454
+35:15=1455
+36:0=1466
+36:1=1464
+36:2=1456
+36:3=1460
+36:4=1462
+36:5=1458
+36:8=1467
+36:9=1465
+36:10=1457
+36:11=1461
+36:12=1463
+36:13=1459
+37:0=1468
+38:0=1469
+38:1=1470
+38:2=1471
+38:3=1472
+38:4=1473
+38:5=1474
+38:6=1475
+38:7=1476
+38:8=1477
+39:0=1481
+40:0=1482
+41:0=1483
+42:0=1484
+43:0=8591
+43:1=8603
+43:2=8615
+43:3=8621
+43:4=8627
+43:5=8633
+43:6=8639
+43:7=8645
+43:8=8664
+43:9=8665
+43:10=8615
+43:11=8621
+43:12=8627
+43:13=8633
+43:14=8639
+43:15=8666
+44:0=8589
+44:1=8601
+44:2=8613
+44:3=8619
+44:4=8625
+44:5=8631
+44:6=8637
+44:7=8643
+44:8=8587
+44:9=8599
+44:10=8611
+44:11=8617
+44:12=8623
+44:13=8629
+44:14=8635
+44:15=8641
+45:0=1485
+46:0=1487
+46:1=1486
+47:0=1488
+48:0=1489
+49:0=1490
+50:1=1495
+50:2=1494
+50:3=1493
+50:4=1492
+50:5=1491
+51:0=1527
+51:1=1559
+51:2=1591
+51:3=1623
+51:4=1655
+51:5=1687
+51:6=1719
+51:7=1751
+51:8=1783
+51:9=1815
+51:10=1847
+51:11=1879
+51:12=1911
+51:13=1943
+51:14=1975
+51:15=2007
+52:0=2009
+53:0=2081
+53:1=2061
+53:2=2041
+53:3=2021
+53:4=2071
+53:5=2051
+53:6=2031
+53:7=2011
+54:2=2091
+54:3=2097
+54:4=2103
+54:5=2109
+55:0=3274
+55:1=3283
+55:2=3292
+55:3=3301
+55:4=3310
+55:5=3319
+55:6=3328
+55:7=3337
+55:8=3346
+55:9=3355
+55:10=3364
+55:11=3373
+55:12=3382
+55:13=3391
+55:14=3400
+55:15=3409
+56:0=3410
+57:0=3412
+58:0=3413
+59:0=3414
+59:1=3415
+59:2=3416
+59:3=3417
+59:4=3418
+59:5=3419
+59:6=3420
+59:7=3421
+60:0=3422
+60:1=3423
+60:2=3424
+60:3=3425
+60:4=3426
+60:5=3427
+60:6=3428
+60:7=3429
+61:2=3431
+61:3=3433
+61:4=3435
+61:5=3437
+62:2=3430
+62:3=3432
+62:4=3434
+62:5=3436
+63:0=0
+63:1=0
+63:2=0
+63:3=0
+63:4=0
+63:5=0
+63:6=0
+63:7=0
+63:8=0
+63:9=0
+63:10=0
+63:11=0
+63:12=0
+63:13=0
+63:14=0
+63:15=0
+64:0=3693
+64:1=3661
+64:2=3677
+64:3=3645
+64:4=3691
+64:5=3659
+64:6=3675
+64:7=3643
+64:8=3681
+64:9=3685
+64:10=3680
+64:11=3684
+65:2=3695
+65:3=3697
+65:4=3699
+65:5=3701
+66:0=3703
+66:1=3705
+66:2=3707
+66:3=3709
+66:4=3711
+66:5=3713
+66:6=3715
+66:7=3717
+66:8=3719
+66:9=3721
+67:0=3793
+67:1=3773
+67:2=3753
+67:3=3733
+67:4=3783
+67:5=3763
+67:6=3743
+67:7=3723
+68:2=0
+68:3=0
+68:4=0
+68:5=0
+69:0=3867
+69:1=3865
+69:2=3863
+69:3=3861
+69:4=3859
+69:5=3857
+69:6=3851
+69:7=3873
+69:8=3866
+69:9=3864
+69:10=3862
+69:11=3860
+69:12=3858
+69:13=3856
+69:14=3850
+69:15=3872
+70:0=3875
+70:1=3874
+71:0=3939
+71:1=3907
+71:2=3923
+71:3=3891
+71:4=3937
+71:5=3905
+71:6=3921
+71:7=3889
+71:8=3927
+71:9=3931
+71:10=3926
+71:11=3930
+72:0=3941
+72:1=3940
+73:0=3953
+74:0=3952
+75:1=3965
+75:2=3963
+75:3=3961
+75:4=3959
+75:5=3957
+76:1=3964
+76:2=3962
+76:3=3960
+76:4=3958
+76:5=3956
+77:0=3989
+77:1=3981
+77:2=3979
+77:3=3977
+77:4=3975
+77:5=3973
+77:8=3984
+77:9=3980
+77:10=3978
+77:11=3976
+77:12=3974
+77:13=3968
+78:0=3990
+78:1=3991
+78:2=3992
+78:3=3993
+78:4=3994
+78:5=3995
+78:6=3996
+78:7=3997
+79:0=3998
+80:0=3999
+81:0=4000
+81:1=4001
+81:2=4002
+81:3=4003
+81:4=4004
+81:5=4005
+81:6=4006
+81:7=4007
+81:8=4008
+81:9=4009
+81:10=4010
+81:11=4011
+81:12=4012
+81:13=4013
+81:14=4014
+81:15=4015
+82:0=4016
+83:0=4017
+83:1=4018
+83:2=4019
+83:3=4020
+83:4=4021
+83:5=4022
+83:6=4023
+83:7=4024
+83:8=4025
+83:9=4026
+83:10=4027
+83:11=4028
+83:12=4029
+83:13=4030
+83:14=4031
+83:15=4032
+84:0=4034
+84:1=4033
+85:0=4066
+86:0=4086
+86:1=4087
+86:2=4085
+86:3=4088
+87:0=4068
+88:0=4069
+89:0=4082
+90:1=4083
+90:2=4084
+91:0=4090
+91:1=4091
+91:2=4089
+91:3=4092
+92:0=4093
+92:1=4094
+92:2=4095
+92:3=4096
+92:4=4097
+92:5=4098
+92:6=4099
+93:0=4107
+93:1=4111
+93:2=4103
+93:3=4115
+93:4=4123
+93:5=4127
+93:6=4119
+93:7=4131
+93:8=4139
+93:9=4143
+93:10=4135
+93:11=4147
+93:12=4155
+93:13=4159
+93:14=4151
+93:15=4163
+94:0=4106
+94:1=4110
+94:2=4102
+94:3=4114
+94:4=4122
+94:5=4126
+94:6=4118
+94:7=4130
+94:8=4138
+94:9=4142
+94:10=4134
+94:11=4146
+94:12=4154
+94:13=4158
+94:14=4150
+94:15=4162
+95:0=4164
+95:1=4165
+95:2=4166
+95:3=4167
+95:4=4168
+95:5=4169
+95:6=4170
+95:7=4171
+95:8=4172
+95:9=4173
+95:10=4174
+95:11=4175
+95:12=4176
+95:13=4177
+95:14=4178
+95:15=4179
+96:0=4195
+96:1=4211
+96:2=4227
+96:3=4243
+96:4=4189
+96:5=4205
+96:6=4221
+96:7=4237
+96:8=4187
+96:9=4203
+96:10=4219
+96:11=4235
+96:12=4181
+96:13=4197
+96:14=4213
+96:15=4229
+97:0=4568
+97:1=4569
+97:2=4570
+97:3=4571
+97:4=4572
+97:5=4573
+98:0=4564
+98:1=4565
+98:2=4566
+98:3=4567
+99:0=4637
+99:1=4626
+99:2=4627
+99:3=4611
+99:4=4634
+99:5=4635
+99:6=4619
+99:7=4630
+99:8=4631
+99:9=4615
+99:10=4736
+99:14=4574
+99:15=4702
+100:0=4701
+100:1=4690
+100:2=4691
+100:3=4675
+100:4=4698
+100:5=4699
+100:6=4683
+100:7=4694
+100:8=4695
+100:9=4679
+100:10=4736
+100:14=4638
+100:15=4702
+101:0=4797
+102:0=4835
+103:0=4836
+104:0=4845
+104:1=4846
+104:2=4847
+104:3=4848
+104:4=4849
+104:5=4850
+104:6=4851
+104:7=4852
+105:0=4853
+105:1=4854
+105:2=4855
+105:3=4856
+105:4=4857
+105:5=4858
+105:6=4859
+105:7=4860
+106:0=4892
+106:1=4888
+106:2=4891
+106:3=4887
+106:4=4884
+106:5=4880
+106:6=4883
+106:7=4879
+106:8=4876
+106:9=4872
+106:10=4875
+106:11=4871
+106:12=4868
+106:13=4864
+106:14=4867
+106:15=4863
+107:0=5036
+107:1=5044
+107:2=5028
+107:3=5052
+107:4=5034
+107:5=5042
+107:6=5026
+107:7=5050
+107:8=5035
+107:9=5043
+107:10=5027
+107:11=5051
+107:12=5033
+107:13=5041
+107:14=5025
+107:15=5049
+108:0=5124
+108:1=5104
+108:2=5084
+108:3=5064
+108:4=5114
+108:5=5094
+108:6=5074
+108:7=5054
+109:0=5204
+109:1=5184
+109:2=5164
+109:3=5144
+109:4=5194
+109:5=5174
+109:6=5154
+109:7=5134
+110:0=5214
+111:0=5215
+112:0=5216
+113:0=5248
+114:0=5320
+114:1=5300
+114:2=5280
+114:3=5260
+114:4=5310
+114:5=5290
+114:6=5270
+114:7=5250
+115:0=5329
+115:1=5330
+115:2=5331
+115:3=5332
+116:0=5333
+117:0=5341
+117:1=5337
+117:2=5339
+117:3=5335
+117:4=5340
+117:5=5336
+117:6=5338
+117:7=5334
+118:0=5342
+118:1=5342
+118:2=5342
+118:3=5342
+119:0=5350
+120:0=5356
+120:1=5357
+120:2=5355
+120:3=5358
+120:4=5352
+120:5=5353
+120:6=5351
+120:7=5354
+121:0=5359
+122:0=5360
+123:0=5362
+124:0=5361
+125:0=8555
+125:1=8561
+125:2=8567
+125:3=8573
+125:4=8579
+125:5=8585
+126:0=8553
+126:1=8559
+126:2=8565
+126:3=8571
+126:4=8577
+126:5=8583
+126:8=8551
+126:9=8557
+126:10=8563
+126:11=8569
+126:12=8575
+126:13=8581
+127:0=5364
+127:1=5365
+127:2=5363
+127:3=5366
+127:4=5368
+127:5=5369
+127:6=5367
+127:7=5370
+127:8=5372
+127:9=5373
+127:10=5371
+127:11=5374
+128:0=5446
+128:1=5426
+128:2=5406
+128:3=5386
+128:4=5436
+128:5=5416
+128:6=5396
+128:7=5376
+129:0=5455
+130:2=5458
+130:3=5460
+130:4=5462
+130:5=5464
+131:0=5476
+131:1=5478
+131:2=5474
+131:3=5480
+131:4=5468
+131:5=5470
+131:6=5466
+131:7=5472
+131:8=5475
+131:9=5477
+131:10=5473
+131:11=5479
+131:12=5467
+131:13=5469
+131:14=5465
+131:15=5471
+132:0=5608
+132:1=5604
+132:4=5544
+132:5=5540
+132:8=5576
+132:9=5572
+132:12=5512
+132:13=5508
+133:0=5609
+134:0=5681
+134:1=5661
+134:2=5641
+134:3=5621
+134:4=5671
+134:5=5651
+134:6=5631
+134:7=5611
+135:0=5761
+135:1=5741
+135:2=5721
+135:3=5701
+135:4=5751
+135:5=5731
+135:6=5711
+135:7=5691
+136:0=5841
+136:1=5821
+136:2=5801
+136:3=5781
+136:4=5831
+136:5=5811
+136:6=5791
+136:7=5771
+137:0=5861
+137:1=5860
+137:2=5856
+137:3=5858
+137:4=5859
+137:5=5857
+137:8=5855
+137:9=5854
+137:10=5850
+137:11=5852
+137:12=5853
+137:13=5851
+138:0=5862
+139:0=5866
+139:1=6196
+140:0=6511
+140:1=6520
+140:2=6519
+140:3=6512
+140:4=6513
+140:5=6514
+140:6=6515
+140:7=6532
+140:8=6533
+140:9=6535
+140:10=6534
+140:11=6518
+140:12=6516
+140:13=6517
+140:14=6521
+140:15=6522
+141:0=6536
+141:1=6537
+141:2=6538
+141:3=6539
+141:4=6540
+141:5=6541
+141:6=6542
+141:7=6543
+142:0=6544
+142:1=6545
+142:2=6546
+142:3=6547
+142:4=6548
+142:5=6549
+142:6=6550
+142:7=6551
+143:0=6575
+143:1=6567
+143:2=6565
+143:3=6563
+143:4=6561
+143:5=6559
+143:8=6570
+143:9=6566
+143:10=6564
+143:11=6562
+143:12=6560
+143:13=6554
+144:0=6696
+144:1=6700
+144:2=6712
+144:3=6713
+144:4=6714
+144:5=6715
+144:8=6704
+144:9=6708
+144:10=6712
+144:11=6713
+144:12=6714
+144:13=6715
+145:0=6817
+145:1=6818
+145:2=6816
+145:3=6819
+145:4=6821
+145:5=6822
+145:6=6820
+145:7=6823
+145:8=6825
+145:9=6826
+145:10=6824
+145:11=6827
+146:2=6829
+146:3=6835
+146:4=6841
+146:5=6847
+147:0=6852
+147:1=6853
+147:2=6854
+147:3=6855
+147:4=6856
+147:5=6857
+147:6=6858
+147:7=6859
+147:8=6860
+147:9=6861
+147:10=6862
+147:11=6863
+147:12=6864
+147:13=6865
+147:14=6866
+147:15=6867
+148:0=6868
+148:1=6869
+148:2=6870
+148:3=6871
+148:4=6872
+148:5=6873
+148:6=6874
+148:7=6875
+148:8=6876
+148:9=6877
+148:10=6878
+148:11=6879
+148:12=6880
+148:13=6881
+148:14=6882
+148:15=6883
+149:0=6889
+149:1=6893
+149:2=6885
+149:3=6897
+149:4=6891
+149:5=6895
+149:6=6887
+149:7=6899
+149:8=6889
+149:9=6893
+149:10=6885
+149:11=6897
+149:12=6891
+149:13=6895
+149:14=6887
+149:15=6899
+150:0=6888
+150:1=6892
+150:2=6884
+150:3=6896
+150:4=6890
+150:5=6894
+150:6=6886
+150:7=6898
+150:8=6888
+150:9=6892
+150:10=6884
+150:11=6896
+150:12=6890
+150:13=6894
+150:14=6886
+150:15=6898
+151:0=6916
+151:1=6917
+151:2=6918
+151:3=6919
+151:4=6920
+151:5=6921
+151:6=6922
+151:7=6923
+151:8=6924
+151:9=6925
+151:10=6926
+151:11=6927
+151:12=6928
+151:13=6929
+151:14=6930
+151:15=6931
+152:0=6932
+153:0=6933
+154:0=6934
+154:2=6935
+154:3=6936
+154:4=6937
+154:5=6938
+154:8=6939
+154:10=6940
+154:11=6941
+154:12=6942
+154:13=6943
+155:0=6944
+155:1=6945
+155:2=6947
+155:3=6946
+155:4=6948
+155:6=6946
+155:10=6948
+156:0=7020
+156:1=7000
+156:2=6980
+156:3=6960
+156:4=7010
+156:5=6990
+156:6=6970
+156:7=6950
+157:0=7042
+157:1=7044
+157:2=7046
+157:3=7048
+157:4=7050
+157:5=7052
+157:8=7030
+157:9=7032
+157:10=7034
+157:11=7036
+157:12=7038
+157:13=7040
+158:0=7064
+158:1=7062
+158:2=7054
+158:3=7058
+158:4=7060
+158:5=7056
+158:8=7063
+158:9=7061
+158:10=7053
+158:11=7057
+158:12=7059
+158:13=7055
+159:0=7065
+159:1=7066
+159:2=7067
+159:3=7068
+159:4=7069
+159:5=7070
+159:6=7071
+159:7=7072
+159:8=7073
+159:9=7074
+159:10=7075
+159:11=7076
+159:12=7077
+159:13=7078
+159:14=7079
+159:15=7080
+160:0=7112
+160:1=7144
+160:2=7176
+160:3=7208
+160:4=7240
+160:5=7272
+160:6=7304
+160:7=7336
+160:8=7368
+160:9=7400
+160:10=7432
+160:11=7464
+160:12=7496
+160:13=7528
+160:14=7560
+160:15=7592
+161:0=205
+161:1=219
+161:4=204
+161:5=218
+161:8=205
+161:9=219
+161:12=204
+161:13=218
+162:0=89
+162:1=92
+162:4=88
+162:5=91
+162:8=90
+162:9=93
+162:12=125
+162:13=128
+163:0=7664
+163:1=7644
+163:2=7624
+163:3=7604
+163:4=7654
+163:5=7634
+163:6=7614
+163:7=7594
+164:0=7744
+164:1=7724
+164:2=7704
+164:3=7684
+164:4=7734
+164:5=7714
+164:6=7694
+164:7=7674
+165:0=7753
+166:0=7754
+167:0=7802
+167:1=7818
+167:2=7834
+167:3=7850
+167:4=7798
+167:5=7814
+167:6=7830
+167:7=7846
+167:8=7794
+167:9=7810
+167:10=7826
+167:11=7842
+167:12=7790
+167:13=7806
+167:14=7822
+167:15=7838
+168:0=7851
+168:1=7852
+168:2=7853
+169:0=8112
+170:0=8114
+170:4=8113
+170:8=8115
+171:0=8116
+171:1=8117
+171:2=8118
+171:3=8119
+171:4=8120
+171:5=8121
+171:6=8122
+171:7=8123
+171:8=8124
+171:9=8125
+171:10=8126
+171:11=8127
+171:12=8128
+171:13=8129
+171:14=8130
+171:15=8131
+172:0=8132
+173:0=8133
+174:0=8134
+175:0=8136
+175:1=8138
+175:2=8144
+175:3=8146
+175:4=8140
+175:5=8142
+175:8=8135
+175:9=8137
+175:10=8143
+175:11=8145
+175:12=8139
+175:13=8141
+176:0=8147
+176:1=8148
+176:2=8149
+176:3=8150
+176:4=8151
+176:5=8152
+176:6=8153
+176:7=8154
+176:8=8155
+176:9=8156
+176:10=8157
+176:11=8158
+176:12=8159
+176:13=8160
+176:14=8161
+176:15=8162
+177:2=8403
+177:3=8404
+177:4=8405
+177:5=8406
+178:0=6900
+178:1=6901
+178:2=6902
+178:3=6903
+178:4=6904
+178:5=6905
+178:6=6906
+178:7=6907
+178:8=6908
+178:9=6909
+178:10=6910
+178:11=6911
+178:12=6912
+178:13=6913
+178:14=6914
+178:15=6915
+179:0=8467
+179:1=8468
+179:2=8469
+180:0=8541
+180:1=8521
+180:2=8501
+180:3=8481
+180:4=8531
+180:5=8511
+180:6=8491
+180:7=8471
+181:0=8651
+181:8=8667
+182:0=8649
+182:8=8647
+183:0=8683
+183:1=8691
+183:2=8675
+183:3=8699
+183:4=8681
+183:5=8689
+183:6=8673
+183:7=8697
+183:8=8682
+183:9=8690
+183:10=8674
+183:11=8698
+183:12=8680
+183:13=8688
+183:14=8672
+183:15=8696
+184:0=8715
+184:1=8723
+184:2=8707
+184:3=8731
+184:4=8713
+184:5=8721
+184:6=8705
+184:7=8729
+184:8=8714
+184:9=8722
+184:10=8706
+184:11=8730
+184:12=8712
+184:13=8720
+184:14=8704
+184:15=8728
+185:0=8747
+185:1=8755
+185:2=8739
+185:3=8763
+185:4=8745
+185:5=8753
+185:6=8737
+185:7=8761
+185:8=8746
+185:9=8754
+185:10=8738
+185:11=8762
+185:12=8744
+185:13=8752
+185:14=8736
+185:15=8760
+186:0=8811
+186:1=8819
+186:2=8803
+186:3=8827
+186:4=8809
+186:5=8817
+186:6=8801
+186:7=8825
+186:8=8810
+186:9=8818
+186:10=8802
+186:11=8826
+186:12=8808
+186:13=8816
+186:14=8800
+186:15=8824
+187:0=8779
+187:1=8787
+187:2=8771
+187:3=8795
+187:4=8777
+187:5=8785
+187:6=8769
+187:7=8793
+187:8=8778
+187:9=8786
+187:10=8770
+187:11=8794
+187:12=8776
+187:13=8784
+187:14=8768
+187:15=8792
+188:0=8859
+189:0=8891
+190:0=8923
+191:0=8987
+192:0=8955
+193:0=9051
+193:1=9019
+193:2=9035
+193:3=9003
+193:4=9049
+193:5=9017
+193:6=9033
+193:7=9001
+193:8=9039
+193:9=9043
+193:10=9038
+193:11=9042
+194:0=9115
+194:1=9083
+194:2=9099
+194:3=9067
+194:4=9113
+194:5=9081
+194:6=9097
+194:7=9065
+194:8=9103
+194:9=9107
+194:10=9102
+194:11=9106
+195:0=9179
+195:1=9147
+195:2=9163
+195:3=9131
+195:4=9177
+195:5=9145
+195:6=9161
+195:7=9129
+195:8=9167
+195:9=9171
+195:10=9166
+195:11=9170
+196:0=9243
+196:1=9211
+196:2=9227
+196:3=9195
+196:4=9241
+196:5=9209
+196:6=9225
+196:7=9193
+196:8=9231
+196:9=9235
+196:10=9230
+196:11=9234
+197:0=9307
+197:1=9275
+197:2=9291
+197:3=9259
+197:4=9305
+197:5=9273
+197:6=9289
+197:7=9257
+197:8=9295
+197:9=9299
+197:10=9294
+197:11=9298
+198:0=9313
+198:1=9312
+198:2=9308
+198:3=9310
+198:4=9311
+198:5=9309
+199:0=9377
+200:0=9378
+200:1=9379
+200:2=9380
+200:3=9381
+200:4=9382
+200:5=9383
+201:0=9384
+202:0=9386
+202:4=9385
+202:8=9387
+203:0=9459
+203:1=9439
+203:2=9419
+203:3=9399
+203:4=9449
+203:5=9429
+203:6=9409
+203:7=9389
+204:0=8663
+205:0=8661
+205:8=8659
+206:0=9468
+207:0=9469
+207:1=9470
+207:2=9471
+207:3=9472
+208:0=0
+209:0=9474
+210:0=9486
+210:1=9485
+210:2=9481
+210:3=9483
+210:4=9484
+210:5=9482
+210:8=9480
+210:9=9479
+210:10=9475
+210:11=9477
+210:12=9478
+210:13=9476
+211:0=9498
+211:1=9497
+211:2=9493
+211:3=9495
+211:4=9496
+211:5=9494
+211:8=9492
+211:9=9491
+211:10=9487
+211:11=9489
+211:12=9490
+211:13=9488
+212:0=9499
+212:1=9500
+212:2=9501
+212:3=9502
+213:0=9503
+214:0=9504
+215:0=9505
+216:0=9507
+216:4=9506
+216:8=9508
+217:0=9509
+218:0=9521
+218:1=9519
+218:2=9511
+218:3=9515
+218:4=9517
+218:5=9513
+218:8=9520
+218:9=9518
+218:10=9510
+218:11=9514
+218:12=9516
+218:13=9512
+219:0=9533
+219:1=9532
+219:2=9528
+219:3=9530
+219:4=9531
+219:5=9529
+220:0=9539
+220:1=9538
+220:2=9534
+220:3=9536
+220:4=9537
+220:5=9535
+221:0=9545
+221:1=9544
+221:2=9540
+221:3=9542
+221:4=9543
+221:5=9541
+222:0=9551
+222:1=9550
+222:2=9546
+222:3=9548
+222:4=9549
+222:5=9547
+223:0=9557
+223:1=9556
+223:2=9552
+223:3=9554
+223:4=9555
+223:5=9553
+224:0=9563
+224:1=9562
+224:2=9558
+224:3=9560
+224:4=9561
+224:5=9559
+225:0=9569
+225:1=9568
+225:2=9564
+225:3=9566
+225:4=9567
+225:5=9565
+226:0=9575
+226:1=9574
+226:2=9570
+226:3=9572
+226:4=9573
+226:5=9571
+227:0=9581
+227:1=9580
+227:2=9576
+227:3=9578
+227:4=9579
+227:5=9577
+228:0=9587
+228:1=9586
+228:2=9582
+228:3=9584
+228:4=9585
+228:5=9583
+229:0=9593
+229:1=9592
+229:2=9588
+229:3=9590
+229:4=9591
+229:5=9589
+230:0=9599
+230:1=9598
+230:2=9594
+230:3=9596
+230:4=9597
+230:5=9595
+231:0=9605
+231:1=9604
+231:2=9600
+231:3=9602
+231:4=9603
+231:5=9601
+232:0=9611
+232:1=9610
+232:2=9606
+232:3=9608
+232:4=9609
+232:5=9607
+233:0=9617
+233:1=9616
+233:2=9612
+233:3=9614
+233:4=9615
+233:5=9613
+234:0=9623
+234:1=9622
+234:2=9618
+234:3=9620
+234:4=9621
+234:5=9619
+235:0=9625
+235:1=9626
+235:2=9624
+235:3=9627
+236:0=9629
+236:1=9630
+236:2=9628
+236:3=9631
+237:0=9633
+237:1=9634
+237:2=9632
+237:3=9635
+238:0=9637
+238:1=9638
+238:2=9636
+238:3=9639
+239:0=9641
+239:1=9642
+239:2=9640
+239:3=9643
+240:0=9645
+240:1=9646
+240:2=9644
+240:3=9647
+241:0=9649
+241:1=9650
+241:2=9648
+241:3=9651
+242:0=9653
+242:1=9654
+242:2=9652
+242:3=9655
+243:0=9657
+243:1=9658
+243:2=9656
+243:3=9659
+244:0=9661
+244:1=9662
+244:2=9660
+244:3=9663
+245:0=9665
+245:1=9666
+245:2=9664
+245:3=9667
+246:0=9669
+246:1=9670
+246:2=9668
+246:3=9671
+247:0=9673
+247:1=9674
+247:2=9672
+247:3=9675
+248:0=9677
+248:1=9678
+248:2=9676
+248:3=9679
+249:0=9681
+249:1=9682
+249:2=9680
+249:3=9683
+250:0=9685
+250:1=9686
+250:2=9684
+250:3=9687
+251:0=9688
+251:1=9689
+251:2=9690
+251:3=9691
+251:4=9692
+251:5=9693
+251:6=9694
+251:7=9695
+251:8=9696
+251:9=9697
+251:10=9698
+251:11=9699
+251:12=9700
+251:13=9701
+251:14=9702
+251:15=9703
+252:0=9704
+252:1=9705
+252:2=9706
+252:3=9707
+252:4=9708
+252:5=9709
+252:6=9710
+252:7=9711
+252:8=9712
+252:9=9713
+252:10=9714
+252:11=9715
+252:12=9716
+252:13=9717
+252:14=9718
+252:15=9719
+255:0=15989
+255:1=15990
+255:2=15991
+255:3=15992
\ No newline at end of file
diff --git a/scaffolding-editor/README.md b/scaffolding-editor/README.md
new file mode 100644
index 0000000..fa9f71f
--- /dev/null
+++ b/scaffolding-editor/README.md
@@ -0,0 +1,2 @@
+# Scaffolding Editor
+Scaffolding Editor is by no means production-ready. Currently, this is just a test environment for scaffolding-core.
\ No newline at end of file
diff --git a/scaffolding-editor/build.gradle.kts b/scaffolding-editor/build.gradle.kts
new file mode 100644
index 0000000..0cea74a
--- /dev/null
+++ b/scaffolding-editor/build.gradle.kts
@@ -0,0 +1,14 @@
+plugins {
+ id("java")
+}
+
+description = "Scaffolding Editor"
+java.sourceCompatibility = JavaVersion.VERSION_17
+
+var minestomVersion = "f774cc3b0f"
+
+dependencies {
+ implementation(project(":scaffolding-core"))
+
+ implementation("com.github.Minestom:Minestom:${minestomVersion}")
+}
\ No newline at end of file
diff --git a/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/Clipboard.java b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/Clipboard.java
new file mode 100644
index 0000000..c6f0520
--- /dev/null
+++ b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/Clipboard.java
@@ -0,0 +1,136 @@
+package net.crystalgames.scaffolding.editor;
+
+import net.crystalgames.scaffolding.region.Region;
+import net.crystalgames.scaffolding.schematic.Schematic;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.minestom.server.MinecraftServer;
+import net.minestom.server.coordinate.Point;
+import net.minestom.server.coordinate.Pos;
+import net.minestom.server.coordinate.Vec;
+import net.minestom.server.entity.Player;
+import net.minestom.server.entity.hologram.Hologram;
+import net.minestom.server.instance.Instance;
+import net.minestom.server.network.packet.server.play.ParticlePacket;
+import net.minestom.server.particle.Particle;
+import net.minestom.server.particle.ParticleCreator;
+import net.minestom.server.timer.Task;
+import net.minestom.server.utils.time.TimeUnit;
+import org.jetbrains.annotations.Nullable;
+
+public class Clipboard {
+
+ public static final Component FIRST_POINT_COMPONENT = Component.text("First point", NamedTextColor.GOLD);
+ public static final Component SECOND_POINT_COMPONENT = Component.text("Second point", NamedTextColor.AQUA);
+
+ private final Player player;
+ private final Task drawParticlesTask;
+
+ private final Schematic schematic = new Schematic();
+
+ private Point firstPoint, secondPoint;
+ private Hologram firstPointHologram, secondPointHologram;
+
+ public Clipboard(Player player) {
+ this.player = player;
+ drawParticlesTask = MinecraftServer.getSchedulerManager().buildTask(this::drawSelection).repeat(50, TimeUnit.MILLISECOND).schedule();
+ }
+
+ public boolean hasValidSelection() {
+ return firstPoint != null && secondPoint != null && player.getInstance() != null;
+ }
+
+ public @Nullable Region createRegionFromSelection() {
+ Instance playerInstance = player.getInstance();
+ if (playerInstance == null || !hasValidSelection()) return null;
+
+ return new Region(playerInstance, firstPoint, secondPoint);
+ }
+
+ public void drawSelection() {
+ Region region = createRegionFromSelection();
+
+ if (region == null) return;
+
+ Point lower = region.getLower();
+ Point upper = region.getUpper().add(1);
+
+ Vec p1 = new Vec(lower.x(), lower.y(), lower.z());
+ Vec p2 = new Vec(upper.x(), lower.y(), lower.z());
+ Vec p3 = new Vec(upper.x(), lower.y(), upper.z());
+ Vec p4 = new Vec(lower.x(), lower.y(), upper.z());
+
+ Vec p5 = new Vec(lower.x(), upper.y(), lower.z());
+ Vec p6 = new Vec(upper.x(), upper.y(), lower.z());
+ Vec p7 = new Vec(upper.x(), upper.y(), upper.z());
+ Vec p8 = new Vec(lower.x(), upper.y(), upper.z());
+
+ drawLine(player, Particle.CRIT, p1, p2);
+ drawLine(player, Particle.CRIT, p2, p3);
+ drawLine(player, Particle.CRIT, p3, p4);
+ drawLine(player, Particle.CRIT, p4, p1);
+
+ drawLine(player, Particle.CRIT, p5, p6);
+ drawLine(player, Particle.CRIT, p6, p7);
+ drawLine(player, Particle.CRIT, p7, p8);
+ drawLine(player, Particle.CRIT, p8, p5);
+
+ drawLine(player, Particle.CRIT, p1, p5);
+ drawLine(player, Particle.CRIT, p2, p6);
+ drawLine(player, Particle.CRIT, p3, p7);
+ drawLine(player, Particle.CRIT, p4, p8);
+ }
+
+ private void drawLine(Player player, Particle particle, Point p1, Point p2) {
+ final Vec v1 = Vec.fromPoint(p1);
+ final Vec v2 = Vec.fromPoint(p2);
+
+ Vec direction = v2.sub(v1).normalize();
+
+ for (Vec position = v1; position.sub(v2).dot(direction) < 0; position = position.add(direction.mul(0.2d))) {
+ ParticlePacket packet = ParticleCreator.createParticlePacket(particle, true, position.x(), position.y(), position.z(), 0, 0, 0, 0, 1, null);
+ player.sendPacket(packet);
+ }
+ }
+
+ public Point getFirstPoint() {
+ return firstPoint;
+ }
+
+ public void setFirstPoint(Point firstPoint) {
+ this.firstPoint = firstPoint;
+
+ if (firstPointHologram != null) firstPointHologram.remove();
+ firstPointHologram = createHologram(firstPoint, FIRST_POINT_COMPONENT);
+
+ player.sendMessage(Component.text("Set ", NamedTextColor.GRAY).append(FIRST_POINT_COMPONENT));
+ }
+
+ private Hologram createHologram(Point position, Component text) {
+ Hologram hologram = new Hologram(player.getInstance(), Pos.fromPoint(position.add(0.5d, 1.5d, 0.5d)), text, false, true);
+ hologram.addViewer(player);
+
+ return hologram;
+ }
+
+ public Point getSecondPoint() {
+ return secondPoint;
+ }
+
+ public void setSecondPoint(Point secondPoint) {
+ this.secondPoint = secondPoint;
+
+ if (secondPointHologram != null) secondPointHologram.remove();
+ secondPointHologram = createHologram(secondPoint, SECOND_POINT_COMPONENT);
+
+ player.sendMessage(Component.text("Set ", NamedTextColor.GRAY).append(SECOND_POINT_COMPONENT));
+ }
+
+ public Schematic getSchematic() {
+ return schematic;
+ }
+
+ public void cleanup() {
+ drawParticlesTask.cancel();
+ }
+}
diff --git a/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/Feature.java b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/Feature.java
new file mode 100644
index 0000000..3e67990
--- /dev/null
+++ b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/Feature.java
@@ -0,0 +1,11 @@
+package net.crystalgames.scaffolding.editor;
+
+import net.minestom.server.event.EventNode;
+import net.minestom.server.event.trait.InstanceEvent;
+import org.jetbrains.annotations.NotNull;
+
+@FunctionalInterface
+public interface Feature {
+
+ void hook(@NotNull final EventNode node);
+}
diff --git a/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/ScaffoldingEditor.java b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/ScaffoldingEditor.java
new file mode 100644
index 0000000..328ad51
--- /dev/null
+++ b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/ScaffoldingEditor.java
@@ -0,0 +1,92 @@
+package net.crystalgames.scaffolding.editor;
+
+import net.crystalgames.scaffolding.editor.commands.CopyCommand;
+import net.crystalgames.scaffolding.editor.commands.LoadCommand;
+import net.crystalgames.scaffolding.editor.commands.PasteCommand;
+import net.crystalgames.scaffolding.editor.features.SelectionFeature;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.minestom.server.MinecraftServer;
+import net.minestom.server.command.CommandManager;
+import net.minestom.server.coordinate.Pos;
+import net.minestom.server.entity.GameMode;
+import net.minestom.server.entity.Player;
+import net.minestom.server.event.GlobalEventHandler;
+import net.minestom.server.event.player.PlayerDisconnectEvent;
+import net.minestom.server.event.player.PlayerLoginEvent;
+import net.minestom.server.event.player.PlayerSpawnEvent;
+import net.minestom.server.extras.lan.OpenToLAN;
+import net.minestom.server.instance.InstanceContainer;
+import net.minestom.server.instance.block.Block;
+import net.minestom.server.item.ItemStack;
+import net.minestom.server.item.Material;
+import net.minestom.server.utils.NamespaceID;
+import net.minestom.server.world.DimensionType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+
+public class ScaffoldingEditor {
+
+ public static final Path SCHEMATICS_PATH = Paths.get("schematics");
+ public static final DimensionType FULL_BRIGHT_DIMENSION = DimensionType.builder(NamespaceID.from("scaffolding_editor:full_bright"))
+ .ambientLight(2.0f)
+ .build();
+ public static final HashMap clipboards = new HashMap<>();
+ private static final Logger LOGGER = LoggerFactory.getLogger(ScaffoldingEditor.class);
+
+ public static void main(String[] args) throws IOException {
+ if (!Files.isDirectory(SCHEMATICS_PATH)) Files.createDirectory(SCHEMATICS_PATH);
+
+ MinecraftServer server = MinecraftServer.init();
+
+ MinecraftServer.getDimensionTypeManager().addDimension(FULL_BRIGHT_DIMENSION);
+ InstanceContainer instance = MinecraftServer.getInstanceManager().createInstanceContainer(FULL_BRIGHT_DIMENSION);
+ instance.setGenerator((unit -> unit.modifier().fillHeight(0, 6, Block.SMOOTH_QUARTZ)));
+
+ CommandManager commandManager = MinecraftServer.getCommandManager();
+ commandManager.register(new LoadCommand());
+ commandManager.register(new CopyCommand());
+ commandManager.register(new PasteCommand());
+
+ GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
+ globalEventHandler.addListener(PlayerLoginEvent.class, event -> {
+ Player player = event.getPlayer();
+
+ clipboards.put(player, new Clipboard(player));
+ player.setRespawnPoint(new Pos(0, 6, 0));
+ event.setSpawningInstance(instance);
+ });
+ globalEventHandler.addListener(PlayerDisconnectEvent.class, event -> clipboards.remove(event.getPlayer()).cleanup());
+ globalEventHandler.addListener(PlayerSpawnEvent.class, event -> {
+ Player player = event.getPlayer();
+
+ ItemStack wand = ItemStack.builder(Material.WOODEN_AXE)
+ .amount(1)
+ .displayName(Component.text("Selection Tool", NamedTextColor.WHITE))
+ .lore(
+ Component.text("Use this to edit the world.", NamedTextColor.GRAY), Component.empty(),
+ Clipboard.FIRST_POINT_COMPONENT.append(Component.text(" - left click", NamedTextColor.GRAY)),
+ Clipboard.SECOND_POINT_COMPONENT.append(Component.text(" - right click", NamedTextColor.GRAY))
+ )
+ .build();
+ player.getInventory().addItemStack(wand);
+
+ player.setGameMode(GameMode.CREATIVE);
+ });
+
+ new SelectionFeature().hook(instance.eventNode());
+
+ server.start("0.0.0.0", 25565);
+ OpenToLAN.open();
+ }
+
+ public static Clipboard getClipboard(Player player) {
+ return clipboards.get(player);
+ }
+}
diff --git a/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/CopyCommand.java b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/CopyCommand.java
new file mode 100644
index 0000000..7b51bb8
--- /dev/null
+++ b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/CopyCommand.java
@@ -0,0 +1,28 @@
+package net.crystalgames.scaffolding.editor.commands;
+
+import net.crystalgames.scaffolding.editor.Clipboard;
+import net.crystalgames.scaffolding.editor.ScaffoldingEditor;
+import net.crystalgames.scaffolding.region.Region;
+import net.minestom.server.command.builder.Command;
+import net.minestom.server.entity.Player;
+
+public class CopyCommand extends Command {
+
+ public CopyCommand() {
+ super("copy");
+
+ setDefaultExecutor((sender, context) -> {
+ Player player = (Player) sender;
+ Clipboard clipboard = ScaffoldingEditor.getClipboard(player);
+
+ Region region = clipboard.createRegionFromSelection();
+
+ if (region == null) {
+ player.sendMessage("No region selected");
+ return;
+ }
+
+ clipboard.getSchematic().copy(region).thenRunAsync(() -> player.sendMessage("Copied region"));
+ });
+ }
+}
diff --git a/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/LoadCommand.java b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/LoadCommand.java
new file mode 100644
index 0000000..2a8b682
--- /dev/null
+++ b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/LoadCommand.java
@@ -0,0 +1,54 @@
+package net.crystalgames.scaffolding.editor.commands;
+
+import net.crystalgames.scaffolding.Scaffolding;
+import net.crystalgames.scaffolding.editor.Clipboard;
+import net.crystalgames.scaffolding.editor.ScaffoldingEditor;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.minestom.server.command.builder.Command;
+import net.minestom.server.command.builder.arguments.ArgumentType;
+import net.minestom.server.command.builder.arguments.ArgumentWord;
+import net.minestom.server.command.builder.suggestion.SuggestionEntry;
+import net.minestom.server.entity.Player;
+import org.jglrxavpok.hephaistos.nbt.NBTException;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+public class LoadCommand extends Command {
+
+ public LoadCommand() {
+ super("load");
+
+ ArgumentWord nameArgument = ArgumentType.Word("nameArgument");
+ nameArgument.setSuggestionCallback((sender, context, suggestion) -> {
+ try (Stream paths = Files.walk(ScaffoldingEditor.SCHEMATICS_PATH)) {
+ paths.filter(Files::isRegularFile).forEach(path -> {
+ String name = path.getFileName().toString();
+ suggestion.addEntry(new SuggestionEntry(name));
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+
+ addSyntax((sender, context) -> {
+ if (sender instanceof Player player) {
+ try {
+ String schematicName = context.get(nameArgument);
+
+ Clipboard clipboard = ScaffoldingEditor.getClipboard(player);
+
+ Scaffolding.fromFile(ScaffoldingEditor.SCHEMATICS_PATH.resolve(schematicName).toFile(), clipboard.getSchematic()).thenRun(() -> {
+ player.sendMessage(Component.text("Loaded schematic " + schematicName, NamedTextColor.GRAY));
+ });
+ } catch (IOException | NBTException e) {
+ player.sendMessage("Failed to load schematic");
+ }
+ }
+ }, nameArgument);
+ }
+
+}
diff --git a/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/PasteCommand.java b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/PasteCommand.java
new file mode 100644
index 0000000..f660f83
--- /dev/null
+++ b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/commands/PasteCommand.java
@@ -0,0 +1,43 @@
+package net.crystalgames.scaffolding.editor.commands;
+
+import net.crystalgames.scaffolding.editor.Clipboard;
+import net.crystalgames.scaffolding.editor.ScaffoldingEditor;
+import net.crystalgames.scaffolding.schematic.Schematic;
+import net.minestom.server.command.builder.Command;
+import net.minestom.server.coordinate.Pos;
+import net.minestom.server.entity.Player;
+import net.minestom.server.instance.Instance;
+
+public class PasteCommand extends Command {
+
+ public PasteCommand() {
+ super("paste");
+
+ setDefaultExecutor((sender, context) -> {
+ if ((sender instanceof Player player)) {
+ Clipboard clipboard = ScaffoldingEditor.getClipboard(player);
+ Schematic schematic = clipboard.getSchematic();
+
+ Instance instance = player.getInstance();
+ Pos placementPosition = player.getPosition();
+
+ if (instance == null) {
+ player.sendMessage("You are not in an instance. This should probably not happen...");
+ return;
+ }
+
+ if (schematic == null) {
+ player.sendMessage("No schematic in clipboard");
+ return;
+ }
+
+ if (!schematic.isPlaceable(instance, placementPosition)) {
+ player.sendMessage("Schematic would not fit within the world boundaries at this position");
+ return;
+ }
+
+ schematic.build(instance, placementPosition).thenRunAsync(() -> player.sendMessage("Schematic pasted"));
+ }
+ });
+ }
+}
diff --git a/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/features/SelectionFeature.java b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/features/SelectionFeature.java
new file mode 100644
index 0000000..3265acc
--- /dev/null
+++ b/scaffolding-editor/src/main/java/net/crystalgames/scaffolding/editor/features/SelectionFeature.java
@@ -0,0 +1,38 @@
+package net.crystalgames.scaffolding.editor.features;
+
+import net.crystalgames.scaffolding.editor.Feature;
+import net.crystalgames.scaffolding.editor.ScaffoldingEditor;
+import net.minestom.server.entity.Player;
+import net.minestom.server.event.EventListener;
+import net.minestom.server.event.EventNode;
+import net.minestom.server.event.player.PlayerBlockBreakEvent;
+import net.minestom.server.event.player.PlayerBlockInteractEvent;
+import net.minestom.server.event.trait.InstanceEvent;
+import net.minestom.server.event.trait.PlayerEvent;
+import net.minestom.server.item.Material;
+import org.jetbrains.annotations.NotNull;
+
+public class SelectionFeature implements Feature {
+
+ public void hook(@NotNull final EventNode node) {
+ node.addListener(EventListener.builder(PlayerBlockBreakEvent.class).handler((this::handleFirstSelection)).filter((this::isValidSelection)).build());
+ node.addListener(EventListener.builder(PlayerBlockInteractEvent.class).handler((this::handleSecondSelection)).filter((this::isValidSelection)).build());
+ }
+
+ private void handleFirstSelection(PlayerBlockBreakEvent event) {
+ event.setCancelled(true);
+ ScaffoldingEditor.getClipboard(event.getPlayer()).setFirstPoint(event.getBlockPosition());
+ }
+
+ private void handleSecondSelection(PlayerBlockInteractEvent event) {
+ if (event.getHand() != Player.Hand.MAIN) return;
+
+ ScaffoldingEditor.getClipboard(event.getPlayer()).setSecondPoint(event.getBlockPosition());
+ }
+
+ private boolean isValidSelection(PlayerEvent event) {
+ Player player = event.getPlayer();
+
+ return player.getItemInMainHand().material() == Material.WOODEN_AXE;
+ }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..33f6cb4
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,9 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * This project uses @Incubating APIs which are subject to change.
+ */
+
+rootProject.name = "Scaffolding"
+include(":scaffolding-core")
+include(":scaffolding-editor")
\ No newline at end of file
diff --git a/src/main/java/net/crystalgames/scaffolding/Scaffolding.java b/src/main/java/net/crystalgames/scaffolding/Scaffolding.java
deleted file mode 100644
index 01a5a41..0000000
--- a/src/main/java/net/crystalgames/scaffolding/Scaffolding.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package net.crystalgames.scaffolding;
-
-import kotlin.Pair;
-import net.crystalgames.scaffolding.schematic.Schematic;
-import net.crystalgames.scaffolding.schematic.impl.MCEditSchematic;
-import net.crystalgames.scaffolding.schematic.impl.SpongeSchematic;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.jglrxavpok.hephaistos.nbt.*;
-
-import java.io.*;
-
-public class Scaffolding {
-
- /**
- * Automatically detects the type of schematic and parses the input stream
- * @param inputStream Schematic input
- * @return parsed schematic
- * @throws IOException if the input stream is invalid
- * @throws NBTException if the schematic is invalid
- */
- public static @Nullable Schematic fromStream(@NotNull InputStream inputStream) throws IOException, NBTException {
- NBTReader reader = new NBTReader(inputStream, CompressedProcesser.GZIP);
- Pair pair = reader.readNamed();
- NBTCompound nbtTag = (NBTCompound) pair.getSecond();
-
- Schematic schematic = null;
- if (nbtTag.contains("Blocks")) schematic = new MCEditSchematic();
- else if (nbtTag.contains("Palette")) schematic = new SpongeSchematic();
-
- if (schematic != null) schematic.read(nbtTag);
- return schematic;
- }
-
- /**
- * Automatically detects the type of schematic and parses the file
- * @param file Schematic file
- * @return parsed schematic
- * @throws IOException if the file is invalid
- * @throws NBTException if the schematic is invalid
- */
- public static @Nullable Schematic fromFile(@NotNull File file) throws IOException, NBTException {
- if (!file.exists()) throw new FileNotFoundException("Invalid Schematic: File does not exist");
- return fromStream(new FileInputStream(file));
- }
-
-}
diff --git a/src/main/java/net/crystalgames/scaffolding/region/Region.java b/src/main/java/net/crystalgames/scaffolding/region/Region.java
deleted file mode 100644
index b04d5dd..0000000
--- a/src/main/java/net/crystalgames/scaffolding/region/Region.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package net.crystalgames.scaffolding.region;
-
-import net.minestom.server.coordinate.Pos;
-import net.minestom.server.instance.Instance;
-
-public record Region(Instance instance, Pos lower, Pos upper) {
-
- public int sizeX() {
- return (upper.blockX() - lower.blockX()) + 1;
- }
-
- public int sizeY() {
- return (upper.blockY() - lower.blockY()) + 1;
- }
-
- public int sizeZ() {
- return (upper.blockZ() - lower.blockZ()) + 1;
- }
-
- public record Block(Pos position, short stateId) {}
-
-}
diff --git a/src/main/java/net/crystalgames/scaffolding/schematic/Schematic.java b/src/main/java/net/crystalgames/scaffolding/schematic/Schematic.java
deleted file mode 100644
index 326a91b..0000000
--- a/src/main/java/net/crystalgames/scaffolding/schematic/Schematic.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package net.crystalgames.scaffolding.schematic;
-
-import net.crystalgames.scaffolding.region.Region;
-import net.minestom.server.coordinate.Pos;
-import net.minestom.server.instance.Instance;
-import net.minestom.server.instance.block.Block;
-import org.jetbrains.annotations.NotNull;
-import org.jglrxavpok.hephaistos.nbt.CompressedProcesser;
-import org.jglrxavpok.hephaistos.nbt.NBTCompound;
-import org.jglrxavpok.hephaistos.nbt.NBTException;
-import org.jglrxavpok.hephaistos.nbt.NBTReader;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.concurrent.CompletableFuture;
-
-public interface Schematic {
-
- default void read(InputStream inputStream) throws IOException, NBTException {
- NBTReader reader = new NBTReader(inputStream, CompressedProcesser.GZIP);
- read((NBTCompound) reader.readNamed().getSecond());
- reader.close();
- inputStream.close();
- }
- void read(NBTCompound nbtTag) throws NBTException;
-
- void write(OutputStream outputStream, Region region) throws IOException;
- CompletableFuture build(Instance instance, Pos position);
-
- short getWidth();
- short getHeight();
- short getLength();
-
- int getOffsetX();
- int getOffsetY();
- int getOffsetZ();
-
- /**
- * Applies the schematic to the given block setter.
- * @param setter the block setter
- */
- void apply(@NotNull Block.Setter setter);
-}
diff --git a/src/main/java/net/crystalgames/scaffolding/schematic/impl/MCEditSchematic.java b/src/main/java/net/crystalgames/scaffolding/schematic/impl/MCEditSchematic.java
deleted file mode 100644
index 0c0593f..0000000
--- a/src/main/java/net/crystalgames/scaffolding/schematic/impl/MCEditSchematic.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package net.crystalgames.scaffolding.schematic.impl;
-
-import net.crystalgames.scaffolding.region.Region;
-import net.crystalgames.scaffolding.schematic.Schematic;
-import net.minestom.server.coordinate.Pos;
-import net.minestom.server.instance.Instance;
-import net.minestom.server.instance.batch.AbsoluteBlockBatch;
-import net.minestom.server.instance.block.Block;
-import org.jetbrains.annotations.NotNull;
-import org.jglrxavpok.hephaistos.collections.ImmutableByteArray;
-import org.jglrxavpok.hephaistos.nbt.NBTCompound;
-import org.jglrxavpok.hephaistos.nbt.NBTException;
-
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-
-// https://github.com/EngineHub/WorldEdit/blob/version/5.x/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java
-public class MCEditSchematic implements Schematic {
-
- private final List regionBlocks = new ArrayList<>();
-
- private short width;
- private short height;
- private short length;
- private short[] blocks;
-
- private boolean read = false;
-
- private int offsetX;
- private int offsetY;
- private int offsetZ;
-
- @Override
- public void read(NBTCompound nbtTag) throws NBTException {
- if (!nbtTag.containsKey("Blocks")) throw new NBTException("Invalid Schematic: No Blocks");
-
- readSizes(nbtTag);
- readBlocksData(nbtTag);
- readOffsets(nbtTag);
- readBlocks();
-
- read = true;
- }
-
- private void readOffsets(@NotNull NBTCompound nbtTag) throws NBTException {
- Integer weOffsetX = nbtTag.getInt("WEOffsetX");
- if (weOffsetX == null) throw new NBTException("Invalid Schematic: No WEOffsetX");
- this.offsetX = weOffsetX;
-
- Integer weOffsetY = nbtTag.getInt("WEOffsetY");
- if (weOffsetY == null) throw new NBTException("Invalid Schematic: No WEOffsetY");
- this.offsetY = weOffsetY;
-
- Integer weOffsetZ = nbtTag.getInt("WEOffsetZ");
- if (weOffsetZ == null) throw new NBTException("Invalid Schematic: No WEOffsetZ");
- this.offsetZ = weOffsetZ;
- }
-
- private void readSizes(@NotNull NBTCompound nbtTag) throws NBTException {
- Short width = nbtTag.getShort("Width");
- if (width == null) throw new NBTException("Invalid Schematic: No Width");
- this.width = width;
-
- Short height = nbtTag.getShort("Height");
- if (height == null) throw new NBTException("Invalid Schematic: No Height");
- this.height = height;
-
- Short length = nbtTag.getShort("Length");
- if (length == null) throw new NBTException("Invalid Schematic: No Length");
- this.length = length;
- }
-
-
- private void readBlocksData(@NotNull NBTCompound nbtTag) throws NBTException {
- String materials = nbtTag.getString("Materials");
- if (materials == null || !materials.equals("Alpha")) throw new NBTException("Invalid Schematic: Invalid Materials");
-
- ImmutableByteArray blockIdPre = nbtTag.getByteArray("Blocks");
- if (blockIdPre == null) throw new NBTException("Invalid Schematic: No Blocks");
- byte[] blockId = blockIdPre.copyArray();
-
- ImmutableByteArray blocksData = nbtTag.getByteArray("Data");
- if (blocksData == null) throw new NBTException("Invalid Schematic: No Block Data");
- blocksData.copyArray();
-
- byte[] addId;
- if (nbtTag.containsKey("AddBlocks")) addId = Objects.requireNonNull(nbtTag.getByteArray("AddBlocks")).copyArray();
- else addId = new byte[0];
-
- blocks = new short[blockId.length];
- for (int index = 0; index < blockId.length; index++) {
- if ((index >> 1) >= addId.length) this.blocks[index] = (short) (blockId[index] & 0xFF);
- else {
- if ((index & 1) == 0) this.blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF));
- else this.blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF));
- }
- }
- }
-
- public void readBlocks() {
- for (int x = 0; x < width; ++x) {
- for (int y = 0; y < height; ++y) {
- for (int z = 0; z < length; ++z) {
- int index = y * width * length + z * width + x;
- short stateId = this.blocks[index];
- regionBlocks.add(new Region.Block(new Pos(x+offsetX, y+offsetY, z+offsetZ), stateId));
- }
- }
- }
- }
-
- @Override
- public void write(OutputStream outputStream, Region region) {
- // TODO: Complete
- }
-
- @Override
- public CompletableFuture build(Instance instance, Pos position) {
- if (!read) throw new IllegalStateException("Schematic not read");
- CompletableFuture future = new CompletableFuture<>();
- CompletableFuture.runAsync(() -> {
- AbsoluteBlockBatch blockBatch = new AbsoluteBlockBatch();
-
- List> futures = new ArrayList<>();
- for (Region.Block regionBlock : regionBlocks) {
- Pos absoluteBlockPosition = regionBlock.position().add(position);
- short stateId = regionBlock.stateId();
-
- Block block = Block.fromStateId(stateId);
- if (block != null) futures.add(instance.loadOptionalChunk(absoluteBlockPosition).thenRun(() -> blockBatch.setBlock(absoluteBlockPosition, block)));
- }
-
- CompletableFuture.allOf(futures.toArray(new CompletableFuture[]{})).join();
- blockBatch.apply(instance, () -> future.complete(new Region(instance, position, position.add(width, height, length))));
- });
- return future;
- }
-
- @Override
- public short getWidth() {
- return width;
- }
-
- @Override
- public short getHeight() {
- return height;
- }
-
- @Override
- public short getLength() {
- return length;
- }
-
- @Override
- public int getOffsetX() {
- return offsetX;
- }
-
- @Override
- public int getOffsetY() {
- return offsetY;
- }
-
- @Override
- public int getOffsetZ() {
- return offsetZ;
- }
-
- @Override
- public void apply(Block.@NotNull Setter setter) {
- for (Region.Block block : regionBlocks) {
- Pos pos = block.position();
- Block minestomBlock = Block.fromStateId(block.stateId());
- if (minestomBlock != null) {
- setter.setBlock(pos, minestomBlock);
- } else {
- throw new IllegalStateException("Invalid block state id: " + block.stateId());
- }
- }
- }
-}
diff --git a/src/main/java/net/crystalgames/scaffolding/schematic/impl/SpongeSchematic.java b/src/main/java/net/crystalgames/scaffolding/schematic/impl/SpongeSchematic.java
deleted file mode 100644
index de864a4..0000000
--- a/src/main/java/net/crystalgames/scaffolding/schematic/impl/SpongeSchematic.java
+++ /dev/null
@@ -1,232 +0,0 @@
-package net.crystalgames.scaffolding.schematic.impl;
-
-import net.crystalgames.scaffolding.region.Region;
-import net.crystalgames.scaffolding.schematic.Schematic;
-import net.minestom.server.coordinate.Pos;
-import net.minestom.server.instance.Instance;
-import net.minestom.server.instance.batch.AbsoluteBlockBatch;
-import net.minestom.server.instance.block.Block;
-import org.jetbrains.annotations.NotNull;
-import org.jglrxavpok.hephaistos.collections.ImmutableByteArray;
-import org.jglrxavpok.hephaistos.nbt.NBTCompound;
-import org.jglrxavpok.hephaistos.nbt.NBTException;
-
-import java.io.OutputStream;
-import java.util.*;
-import java.util.concurrent.CompletableFuture;
-
-// https://github.com/EngineHub/WorldEdit/blob/303f5a76b2df70d63480f2126c9ef4b228eb3c59/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java#L261-L297
-public class SpongeSchematic implements Schematic {
-
- private final List regionBlocks = new ArrayList<>();
-
- private short width;
- private short height;
- private short length;
- private Map palette = new HashMap<>();
- private byte[] blocksData;
-
- private boolean read = false;
-
- private int offsetX;
- private int offsetY;
- private int offsetZ;
-
- @Override
- public void read(NBTCompound nbtTag) throws NBTException {
- readSizes(nbtTag);
- readBlockPalette(nbtTag);
- readOffsets(nbtTag);
- readBlocks();
- read = true;
- }
-
- private void readOffsets(@NotNull NBTCompound nbtTag) throws NBTException {
- NBTCompound metaData = nbtTag.getCompound("Metadata");
- if (metaData == null) throw new NBTException("Invalid Schematic: No Metadata");
-
- Integer weOffsetX = metaData.getInt("WEOffsetX");
- if (weOffsetX == null) throw new NBTException("Invalid Schematic: No WEOffsetX In Metadata");
- this.offsetX = weOffsetX;
-
- Integer weOffsetY = metaData.getInt("WEOffsetY");
- if (weOffsetY == null) throw new NBTException("Invalid Schematic: No WEOffsetY In Metadata");
- this.offsetY = weOffsetY;
-
- Integer weOffsetZ = metaData.getInt("WEOffsetZ");
- if (weOffsetZ == null) throw new NBTException("Invalid Schematic: No WEOffsetZ In Metadata");
- this.offsetZ = weOffsetZ;
- }
-
- private void readSizes(@NotNull NBTCompound nbtTag) throws NBTException {
- Short width = nbtTag.getShort("Width");
- if (width == null) throw new NBTException("Invalid Schematic: No Width");
- this.width = width;
-
- Short height = nbtTag.getShort("Height");
- if (height == null) throw new NBTException("Invalid Schematic: No Height");
- this.height = height;
-
- Short length = nbtTag.getShort("Length");
- if (length == null) throw new NBTException("Invalid Schematic: No Length");
- this.length = length;
- }
-
- private void readBlockPalette(@NotNull NBTCompound nbtTag) throws NBTException {
- Integer maxPalette = nbtTag.getInt("PaletteMax");
- if (maxPalette == null) throw new NBTException("Invalid Schematic: No PaletteMax");
-
- NBTCompound nbtPalette = (NBTCompound) nbtTag.get("Palette");
- if (nbtPalette == null) throw new NBTException("Invalid Schematic: No Palette");
-
- Set keys = nbtPalette.getKeys();
- if (keys.size() != maxPalette) throw new NBTException("Invalid Schematic: PaletteMax does not match Palette size");
-
- for (String key : keys) {
- Integer value = nbtPalette.getInt(key);
- if (value == null) throw new NBTException("Invalid Schematic: Palette contains invalid value");
-
- palette.put(key, value);
- }
-
- palette = palette.entrySet().stream()
- .sorted(Map.Entry.comparingByValue())
- .collect(LinkedHashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), LinkedHashMap::putAll);
-
- ImmutableByteArray blocksData = nbtTag.getByteArray("BlockData");
- if (blocksData == null || blocksData.getSize() == 0) throw new NBTException("Invalid Schematic: No BlockData");
- this.blocksData = blocksData.copyArray();
- }
-
- private void readBlocks() throws NBTException {
- int index = 0;
- int i = 0;
- int value;
- int varIntLength;
- List paletteKeys = new ArrayList<>(palette.keySet());
-
- while (i < this.blocksData.length) {
- value = 0;
- varIntLength = 0;
-
- while (true) {
- value |= (this.blocksData[i] & 127) << (varIntLength++ * 7);
- if (varIntLength > 5) throw new NBTException("Invalid Schematic: BlockData has invalid length");
- if ((this.blocksData[i] & 128) != 128) {
- i++;
- break;
- }
- i++;
- }
-
- int x = (index % (width * length)) % width;
- int y = index / (width * length);
- int z = (index % (width * length)) / width;
-
- String block = paletteKeys.get(value);
- short stateId = getStateId(block);
-
- this.regionBlocks.add(new Region.Block(new Pos(x + offsetX, y + offsetY, z + offsetZ), stateId));
-
- index++;
- }
- }
-
- @Override
- public void write(OutputStream outputStream, Region region) {
- // TODO: Complete
- }
-
- @Override
- public CompletableFuture build(Instance instance, Pos position) {
- if (!read) throw new IllegalStateException("Schematic not read");
- CompletableFuture future = new CompletableFuture<>();
- CompletableFuture.runAsync(() -> {
- AbsoluteBlockBatch blockBatch = new AbsoluteBlockBatch();
-
- List> futures = new ArrayList<>();
- for (Region.Block regionBlock : regionBlocks) {
- Pos absoluteBlockPosition = regionBlock.position().add(position);
- short stateId = regionBlock.stateId();
-
- Block block = Block.fromStateId(stateId);
- if (block != null) futures.add(instance.loadOptionalChunk(absoluteBlockPosition).thenRun(() -> blockBatch.setBlock(absoluteBlockPosition, block)));
- }
-
- CompletableFuture.allOf(futures.toArray(new CompletableFuture[]{})).join();
- blockBatch.apply(instance, () -> future.complete(new Region(instance, position, position.add(width, height, length))));
- });
- return future;
- }
-
- private Block getBlock(@NotNull String input) {
- String namespaceId = input.split("\\[")[0];
-
- return Block.fromNamespaceId(namespaceId);
- }
-
- private short getStateId(@NotNull String input) {
- Block block = getBlock(input);
- if (block == null) return 0;
- String states = input.replaceAll(block.name(), "");
-
- if (states.startsWith("[")) {
- String[] stateArray = states.substring(1, states.length() - 1).split(",");
- Map properties = new HashMap<>(block.properties());
- for (String state : stateArray) {
- String[] split = state.split("=");
- properties.replace(split[0], split[1]);
- }
- try {
- return block.withProperties(properties).stateId();
- } catch (Exception e) {
- e.printStackTrace();
- return block.stateId();
- }
- } else return block.stateId();
- }
-
- @Override
- public short getWidth() {
- return width;
- }
-
- @Override
- public short getHeight() {
- return height;
- }
-
- @Override
- public short getLength() {
- return length;
- }
-
- @Override
- public int getOffsetX() {
- return offsetX;
- }
-
- @Override
- public int getOffsetY() {
- return offsetY;
- }
-
- @Override
- public int getOffsetZ() {
- return offsetZ;
- }
-
- @Override
- public void apply(Block.@NotNull Setter setter) {
- for (Region.Block block : regionBlocks) {
- Pos pos = block.position();
- Block minestomBlock = Block.fromStateId(block.stateId());
- if (minestomBlock != null) {
- setter.setBlock(pos, minestomBlock);
- } else {
- throw new IllegalStateException("Invalid block state id: " + block.stateId());
- }
- }
- }
-
-}
diff --git a/src/test/java/dev/sllcoding/scaffolding/test/Server.java b/src/test/java/dev/sllcoding/scaffolding/test/Server.java
deleted file mode 100644
index 92acc89..0000000
--- a/src/test/java/dev/sllcoding/scaffolding/test/Server.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package dev.sllcoding.scaffolding.test;
-
-import dev.sllcoding.scaffolding.test.commands.TestCommand;
-import dev.sllcoding.scaffolding.test.generator.Generator;
-import net.crystalgames.scaffolding.Scaffolding;
-import net.crystalgames.scaffolding.instance.SchematicChunkLoader;
-import net.crystalgames.scaffolding.schematic.Schematic;
-import net.minestom.server.MinecraftServer;
-import net.minestom.server.coordinate.Pos;
-import net.minestom.server.entity.GameMode;
-import net.minestom.server.event.player.PlayerLoginEvent;
-import net.minestom.server.event.player.PlayerSpawnEvent;
-import net.minestom.server.instance.InstanceContainer;
-import net.minestom.server.utils.NamespaceID;
-import net.minestom.server.world.DimensionType;
-import org.jglrxavpok.hephaistos.nbt.NBTException;
-
-import java.io.File;
-import java.io.IOException;
-
-public class Server {
-
- private static final DimensionType FULLBRIGHT_DIMENSTION = DimensionType.builder(NamespaceID.from("scaffolding:fullbright"))
- .ambientLight(2.0f)
- .build();
-
- public static void main(String[] args) {
- MinecraftServer server = MinecraftServer.init();
-
- MinecraftServer.getDimensionTypeManager().addDimension(FULLBRIGHT_DIMENSTION);
- InstanceContainer instance = MinecraftServer.getInstanceManager().createInstanceContainer(FULLBRIGHT_DIMENSTION);
- instance.setChunkGenerator(new Generator());
- // Load schematic for schematic chunk loader
- try {
- Schematic schematic = Scaffolding.fromFile(new File("schematic.schematic"));
- SchematicChunkLoader chunkLoader = SchematicChunkLoader.builder()
- .addSchematic(schematic)
- .build();
- instance.setChunkLoader(chunkLoader);
- } catch (IOException | NBTException e) {
- e.printStackTrace();
- }
-
-
- MinecraftServer.getCommandManager().register(new TestCommand());
-
- MinecraftServer.getGlobalEventHandler().addListener(PlayerLoginEvent.class, event -> {
- event.setSpawningInstance(instance);
- event.getPlayer().setRespawnPoint(new Pos(0, 42, 0));
- }).addListener(PlayerSpawnEvent.class, event -> {
- event.getPlayer().setGameMode(GameMode.CREATIVE);
- });
-
- server.start("0.0.0.0", 25565);
- }
-
-}
diff --git a/src/test/java/dev/sllcoding/scaffolding/test/commands/TestCommand.java b/src/test/java/dev/sllcoding/scaffolding/test/commands/TestCommand.java
deleted file mode 100644
index a528393..0000000
--- a/src/test/java/dev/sllcoding/scaffolding/test/commands/TestCommand.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package dev.sllcoding.scaffolding.test.commands;
-
-import net.crystalgames.scaffolding.Scaffolding;
-import net.crystalgames.scaffolding.schematic.Schematic;
-import net.minestom.server.command.builder.Command;
-import net.minestom.server.coordinate.Pos;
-import net.minestom.server.entity.Player;
-import net.minestom.server.instance.Instance;
-
-import java.io.File;
-
-public class TestCommand extends Command {
-
- public TestCommand() {
- super("test");
-
- setDefaultExecutor((sender, context) -> {
- try {
- Schematic schematic = Scaffolding.fromFile(new File("schematic.schematic"));
-
- Player player = (Player) sender;
- Instance instance = player.getInstance();
- Pos position = player.getPosition();
-
- schematic.build(instance, position).thenRun(() -> player.sendMessage("Done!"));
- } catch (Exception e) {
- e.printStackTrace();
- }
- });
- }
-
-}
diff --git a/src/test/java/dev/sllcoding/scaffolding/test/generator/Generator.java b/src/test/java/dev/sllcoding/scaffolding/test/generator/Generator.java
deleted file mode 100644
index 06ab355..0000000
--- a/src/test/java/dev/sllcoding/scaffolding/test/generator/Generator.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package dev.sllcoding.scaffolding.test.generator;
-
-import net.minestom.server.instance.ChunkGenerator;
-import net.minestom.server.instance.ChunkPopulator;
-import net.minestom.server.instance.batch.ChunkBatch;
-import net.minestom.server.instance.block.Block;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-
-public class Generator implements ChunkGenerator {
-
- @Override
- public void generateChunkData(@NotNull ChunkBatch chunkBatch, int chunkX, int chunkZ) {
- for (int x = 0; x < 16; x++)
- for (int z = 0; z < 16; z++)
- for (int y = 0; y < 40; y++)
- chunkBatch.setBlock(x, y, z, Block.STONE);
- }
-
- @Override
- public @Nullable List getPopulators() {
- return null;
- }
-
-}