diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 12c142134..41d13e6c7 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -27,6 +27,7 @@ body:
description: Which server version version you using? If your server version is not listed, it is not supported. Update to a supported version first.
multiple: false
options:
+ - '1.19.3'
- '1.19.2'
- '1.19.1'
- '1.19'
diff --git a/.github/workflows/announce-release-on-discord.yml b/.github/workflows/announce-release-on-discord.yml
new file mode 100644
index 000000000..78af47080
--- /dev/null
+++ b/.github/workflows/announce-release-on-discord.yml
@@ -0,0 +1,29 @@
+name: Announce release on discord
+on:
+ workflow_run:
+ workflows: ["Upload release assets"]
+ types:
+ - completed
+
+jobs:
+ send_announcement:
+ runs-on: ubuntu-latest
+ steps:
+ - name: send custom message with args
+ env:
+ DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
+ DISCORD_USERNAME: FastAsyncWorldEdit Release
+ DISCORD_AVATAR: https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.png
+ uses: Ilshidur/action-discord@0.3.2
+ with:
+ args: |
+ "<@&525015715300900875> <@&706463154804097105> <@&671372968462516240>"
+ ""
+ "<:fawe:730750370984493106> **FastAsyncWorldEdit ${{ github.event.release.tag_name }} has been released!**"
+ ""
+ "Click here to view changelog: https://github.com/IntellectualSites/FastAsyncWorldEdit/releases/tag/${{ github.event.release.tag_name }}"
+ ""
+ "The download is available at:"
+ "- Spigot: "
+ "- Modrinth: "
+ "- CurseForge: "
diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml
new file mode 100644
index 000000000..597a64143
--- /dev/null
+++ b/.github/workflows/build-pr.yml
@@ -0,0 +1,23 @@
+name: Build PR
+
+on: [ pull_request ]
+
+jobs:
+ build_pr:
+ if: github.repository_owner == 'IntellectualSites'
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, windows-latest, macos-latest ]
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v3
+ - name: Validate Gradle Wrapper
+ uses: gradle/wrapper-validation-action@v1
+ - name: Setup Java
+ uses: actions/setup-java@v3
+ with:
+ distribution: temurin
+ java-version: 17
+ - name: Build on ${{ matrix.os }}
+ run: ./gradlew clean build
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8c548fc61..444f5a958 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,10 +1,12 @@
-name: build
-
-on: [pull_request, push]
+name: Build
+on:
+ push:
+ branches:
+ - main
jobs:
build:
- if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
+ if: github.repository_owner == 'IntellectualSites'
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
@@ -70,3 +72,23 @@ jobs:
with:
name: FastAsyncWorldEdit-Bukkit-SNAPSHOT
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar
+ - name: Publish to Modrinth
+ if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
+ run: ./gradlew modrinth
+ env:
+ MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
+ - name : Publish to CurseForge
+ if : ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
+ uses: itsmeow/curseforge-upload@v3
+ with:
+ file_path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-${{ env.VERSION }}.jar
+ # https://minecraft.curseforge.com/api/game/versions?token=redacted
+ # gameVersionTypeID: 1
+ game_versions: "8503,9016,9190,9261,9560,9561"
+ project_id: 103525
+ game_endpoint: minecraft
+ token: ${{ secrets.CURSEFORGE_TOKEN }}
+ display_name: FastAsyncWorldEdit ${{ env.VERSION }}
+ release_type: release
+ changelog: "Click here to view changelog: https://github.com/IntellectualSites/FastAsyncWorldEdit/releases/tag/${{ github.event.release.tag_name }}"
+ changelog_type: markdown
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 000000000..d5fa715d1
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,35 @@
+name: "CodeQL"
+
+on:
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ main ]
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'java' ]
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/upload-release-assets.yml b/.github/workflows/upload-release-assets.yml
index cbfa9eafa..93587ed76 100644
--- a/.github/workflows/upload-release-assets.yml
+++ b/.github/workflows/upload-release-assets.yml
@@ -26,23 +26,3 @@ jobs:
files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar'
repo-token: ${{ secrets.GITHUB_TOKEN }}
release-tag: ${{ github.event.release.tag_name }}
- - name: Upload release to CurseForge
- uses: Kir-Antipov/mc-publish@v3.2
- with:
- curseforge-id: 103525
- curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
-
- files-primary: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar'
-
- name: FastAsyncWorldEdit ${{ github.event.release.tag_name }}
- version: ${{ github.event.release.tag_name }}
- version-type: release
-
- game-versions: |
- 1.19.1
- 1.19
- 1.18.2
- 1.18.1
- 1.18
- 1.17
- 1.16
diff --git a/.gitignore b/.gitignore
index 9640418d6..14ba6416a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,7 +24,6 @@ logs/
worldedit-bukkit/src/main/java/ignore/*
todo.txt
mvn/*
-docs/
*.sh
# i18n
worldedit-core/src/main/resources/lang/*
diff --git a/README.adoc b/README.adoc
deleted file mode 100644
index 58af413a5..000000000
--- a/README.adoc
+++ /dev/null
@@ -1,47 +0,0 @@
-image::https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.svg[200,200,align=center]
-
-'''
-
-FastAsyncWorldEdit is a fork of WorldEdit that has huge speed and memory improvements and considerably more features
-
-== A Minecraft Map Editor... that runs in-game!
-
-* With selections, schematics, copy and paste, brushes, and scripting!
-* Use it in creative, survival in single player or on your server.
-* Use it on your Minecraft server to fix grieving and mistakes.
-
-Java Edition required. FastAsyncWorldEdit is compatible with Bukkit, Spigot and Paper.
-
-=== Download FastAsyncWorldEdit
-* Snapshot builds: https://ci.athion.net/job/FastAsyncWorldEdit/
-* Spigot: https://www.spigotmc.org/resources/fast-async-worldedit.13932/
-* Older, unsupported, versions: https://intellectualsites.github.io/download/fawe.html
-
-=== Links
-
-* link:https://discord.gg/intellectualsites[Discord]
-* link:https://intellectualsites.github.io/fastasyncworldedit-documentation/[Wiki]
-* link:https://github.com/IntellectualSites/FastAsyncWorldEdit/issues[Report Issue]
-* link:https://intellectualsites.github.io/fastasyncworldedit-javadocs/[Javadocs]
-* link:https://intellectualsites.crowdin.com/fastasyncworldedit[Crowdin (Translations)]
-
-=== Edit The Code
-
-Want to add new features to FastAsyncWorldEdit or fix bugs yourself? You can get the game running, with FastAsyncWorldEdit, from the code here:
-
-For additional information about compiling FastAsyncWorldEdit, see link:COMPILING.adoc[COMPILING.adoc].
-
-=== Submitting Your Changes
-FastAsyncWorldEdit is open source (specifically licensed under GPL v3), so note that your contributions will also be open source. The best way to submit a change is to create a fork on GitHub, put your changes there, and then create a "pull request" on our FastAsyncWorldEdit repository.
-
-Please read link:https://github.com/IntellectualSites/.github/blob/main/CONTRIBUTING.md[CONTRIBUTING.md] for important guidelines to follow.
-
-=== Special thanks to:
-image::https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg[150,150]
-link:https://jb.gg/OpenSourceSupport[JetBrains], creators of the IntelliJ IDEA, supports us with their Open Source Licenses.
-
-image::https://www.yourkit.com/images/yklogo.png[200,200,align=left]
-
-Thank you to YourKit for supporting our product by providing us with their innovative and intelligent tools
-for monitoring and profiling Java and .NET applications.
-YourKit is the creator of link:https://www.yourkit.com/java/profiler/[YourKit Java Profiler], link:https://www.yourkit.com/.net/profiler/[YourKit .NET Profiler], and link:https://www.yourkit.com/youmonitor/[YourKit YouMonitor].
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..ed012e6b5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,95 @@
+# FastAsyncWorldEdit
+[![Join us on Discord](https://img.shields.io/discord/268444645527126017.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/intellectualsites)
+[![bStats Servers](https://img.shields.io/bstats/servers/1403)](https://bstats.org/plugin/bukkit/FastAsyncWorldEdit/1403)
+[![Crowdin](https://badges.crowdin.net/e/4a5819fae3fd88234a8ea13bfbb072bb/localized.svg)](https://intellectualsites.crowdin.com/fastasyncworldedit)
+
+## What is FAWE and why should I use it?
+
+FAWE is designed for efficient world editing.
+* Simple to set up and use
+* Extremely configurable
+* Uses minimal CPU/Memory
+* Safe for many players to use
+* Insanely fast, when using the slowest mode
+
+FastAsyncWorldEdit is a fork of WorldEdit that has huge speed and memory improvements and considerably more features.
+If you use other plugins which depend on WorldEdit, simply having FAWE installed will boost their performance.
+
+## Downloads
+
+Downloads are available either on SpigotMC, Modrinth or on CurseForge.
+- [SpigotMC](https://www.spigotmc.org/resources/13932/)
+- [Modrinth](https://modrinth.com/plugin/fastasyncworldedit/)
+- [CurseForge](https://dev.bukkit.org/projects/fawe)
+
+Snapshots are available on [Jenkins](https://ci.athion.net/job/FastAsyncWorldEdit/).
+
+## Features
+
+* Over 200 Commands
+* Style and translate messages and commands
+* (No setup required) Clipboard web integration (Clipboard)
+* Unlimited //undo, per world history, instant lookups/rollback and cross server clipboards
+* Advanced per player limits (entity, tiles, memory, changes, iterations, regions, inventory)
+* Visualization, targeting modes/masks and scroll actions
+* Adds lots of powerful new //brushes and //tools.
+* Adds a lot more mask functionality. (new mask syntax, patterns, expressions, source masks)
+* Adds a lot more pattern functionality. (a lot of new pattern syntax and patterns)
+* Adds edit transforms (apply transforms to a source, e.g. on //paste)
+* Adds support for new formats (e.g. Structure Blocks)
+* Instant copying of arbitrary size with `//lazycopy`
+* Auto repair partially corrupt schematic files
+* Biome mixing, in-game world painting, dynamic view distance, vanilla cui, off axis rotation, image importing, cave generation,
+ multi-clipboards, interactive messages, schematic visualization, lag prevention, persistent brushes + A LOT MORE
+
+### Performance
+
+There are several placement modes, each supporting higher throughput than the previous. All editing is processed
+asynchronously, with
+certain tasks being broken up on the main thread. The default mode is chunk placement.
+* Blocks (Bukkit-API) - Only used if chunk placement isn't supported. Still faster than any other plugin on spigot.
+* Chunks (NMS) - Places entire chunk sections
+* World (CFI) - Used to generate new worlds / regions
+
+### Protection Plugins
+
+The following plugins are supported with Bukkit:
+* [WorldGuard](https://dev.bukkit.org/projects/worldguard)
+* [PlotSquared](https://www.spigotmc.org/resources/77506/)
+
+### Logging and Rollback
+
+By default you can use `//inspect` and `//history rollback` to search and restore changes. To reduce disk usage, increase the
+compression level and buffer size. To bypass logging use `//fast`.
+
+### Developer API
+
+FAWE maintains API compatibility with WorldEdit, so you can use the normal WorldEdit API asynchronously.
+FAWE also has some asynchronously wrappers for the Bukkit API.
+The wiki has examples for various things like reading NBT, modifying world files, pasting schematics, splitting up tasks, lighting etc.
+If you need help with anything, hop on discord (link on the left bar).
+
+## Documentation
+
+* [Wiki](https://intellectualsites.github.io/fastasyncworldedit-documentation/)
+* [Javadocs](https://intellectualsites.github.io/fastasyncworldedit-javadocs/)
+
+## Contributing
+
+Want to add new features to FastAsyncWorldEdit or fix bugs yourself? You can get the game running, with FastAsyncWorldEdit, from the code here:
+
+For additional information about compiling FastAsyncWorldEdit, read the [compiling documentation](https://github.com/IntellectualSites/FastAsyncWorldEdit/blob/main/COMPILING.adoc).
+
+## Special thanks
+
+
+
+
+[JetBrains](https://jb.gg/OpenSourceSupport), creators of the IntelliJ IDEA, supports us with their Open Source Licenses.
+
+
+
+
+Thank you to YourKit for supporting our product by providing us with their innovative and intelligent tools
+for monitoring and profiling Java and .NET applications.
+YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/), and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/).
diff --git a/build.gradle.kts b/build.gradle.kts
index 069db1c3e..eac46709c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,7 +6,17 @@ import java.net.URI
plugins {
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
- id("xyz.jpenilla.run-paper") version "1.0.6"
+ id("xyz.jpenilla.run-paper") version "2.0.1"
+}
+
+if (!File("$rootDir/.git").exists()) {
+ logger.lifecycle("""
+ **************************************************************************************
+ You need to fork and clone this repository! Don't download a .zip file.
+ If you need assistance, consult the GitHub docs: https://docs.github.com/get-started/quickstart/fork-a-repo
+ **************************************************************************************
+ """.trimIndent()
+ ).also { kotlin.system.exitProcess(1) }
}
logger.lifecycle("""
@@ -23,7 +33,7 @@ logger.lifecycle("""
*******************************************
""")
-var rootVersion by extra("2.4.7")
+var rootVersion by extra("2.5.2")
var snapshot by extra("SNAPSHOT")
var revision: String by extra("")
var buildNumber by extra("")
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 7c4ea152e..ba983666c 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -31,7 +31,7 @@ dependencies {
implementation(gradleApi())
implementation("org.ajoberstar.grgit:grgit-gradle:4.1.1")
implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2")
- implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.3.8")
+ implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.4.1")
}
kotlin {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b6a7f4236..9a442bd1a 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,8 +1,8 @@
[versions]
# Minecraft expectations
-fastutil = "8.5.6"
-guava = "31.0.1-jre"
-log4j = "2.17.1"
+fastutil = "8.5.9"
+guava = "31.1-jre"
+log4j = "2.19.0"
# Plugins
dummypermscompat = "1.10"
@@ -12,7 +12,7 @@ griefprevention = "16.18"
griefdefender = "2.1.0-SNAPSHOT"
mcore = "7.0.1"
residence = "4.5._13.1"
-towny = "0.98.3.8"
+towny = "0.98.4.18"
redprotect = "1.9.6"
# Third party
@@ -21,11 +21,11 @@ sparsebitset = "1.2"
parallelgzip = "1.0.5"
adventure = "4.9.3"
truezip = "6.8.4"
-auto-value = "1.9"
+auto-value = "1.10.1"
findbugs = "3.0.2"
rhino-runtime = "1.7.14"
zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs
-antlr4 = "4.10.1"
+antlr4 = "4.11.1"
json-simple = "1.1.1"
jlibnoise = "1.0.0"
jchronic = "0.2.4a"
@@ -38,7 +38,7 @@ text = "3.0.4"
piston = "0.5.7"
# Tests
-mockito = "4.8.0"
+mockito = "5.1.1"
# Gradle plugins
pluginyml = "0.5.2"
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 249e5832f..943f0cbfa 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8fad3f5a9..2b22d057a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
+networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index a69d9cb6c..65dcd68d6 100755
--- a/gradlew
+++ b/gradlew
@@ -55,7 +55,7 @@
# 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
+# https://github.com/gradle/gradle/blob/HEAD/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/.
@@ -80,10 +80,10 @@ do
esac
done
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# 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"'
@@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
diff --git a/gradlew.bat b/gradlew.bat
index 53a6b238d..6689b85be 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
diff --git a/settings.gradle.kts b/settings.gradle.kts
index ff6ad2cb2..c2359c9b0 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit"
include("worldedit-libs")
-listOf("legacy", "1_17_1", "1_18_2", "1_19").forEach {
+listOf("legacy", "1_17_1", "1_18_2", "1_19", "1_19_3").forEach {
include("worldedit-bukkit:adapters:adapter-$it")
}
diff --git a/steamwarci.yml b/steamwarci.yml
index 4254b49c8..643222aa1 100644
--- a/steamwarci.yml
+++ b/steamwarci.yml
@@ -1,9 +1,9 @@
build:
- - "./gradlew build"
- - "./gradlew --stop"
+ - "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew build"
+ - "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew --stop"
artifacts:
- "/binarys/FastAsyncWorldEdit-1.18.jar": "worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-2.4.7-SNAPSHOT.jar"
+ "/binarys/FastAsyncWorldEdit-1.18.jar": "worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-2.5.2-SNAPSHOT.jar"
release:
- - "mvn deploy:deploy-file -DgroupId=de.steamwar -DartifactId=fastasyncworldedit -Dversion=1.18 -Dpackaging=jar -Dfile=/binarys/FastAsyncWorldEdit-1.18.jar -Durl=file:///var/www/html/maven/"
\ No newline at end of file
+ - "mvn deploy:deploy-file -DgroupId=de.steamwar -DartifactId=fastasyncworldedit -Dversion=1.18 -Dpackaging=jar -Dfile=/binarys/FastAsyncWorldEdit-1.18.jar -Durl=file:///var/www/html/maven/"
diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts
index 3000e6af6..986666fef 100644
--- a/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts
+++ b/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts
@@ -13,6 +13,6 @@ repositories {
dependencies {
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
- paperDevBundle("1.18.2-R0.1-20220703.182221-166")
+ paperDevBundle("1.18.2-R0.1-20220920.010157-167")
compileOnly("io.papermc:paperlib")
}
diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java
index 31d087ff0..25f93844a 100644
--- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java
+++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java
@@ -55,6 +55,7 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.BlockMaterial;
+import io.papermc.lib.PaperLib;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.WritableRegistry;
@@ -663,17 +664,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override
public RelighterFactory getRelighterFactory() {
- try {
- Class.forName("ca.spottedleaf.starlight.common.light.StarLightEngine");
- if (PaperweightStarlightRelighter.isUsable()) {
- return new PaperweightStarlightRelighterFactory();
- }
- } catch (ThreadDeath td) {
- throw td;
- } catch (Throwable ignored) {
-
+ if (PaperLib.isPaper()) {
+ return new PaperweightStarlightRelighterFactory();
+ } else {
+ return new NMSRelighterFactory();
}
- return new NMSRelighterFactory();
}
@Override
diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightStarlightRelighter.java
index 33dcdc63b..397931bc5 100644
--- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightStarlightRelighter.java
+++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightStarlightRelighter.java
@@ -14,16 +14,12 @@ import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.server.MCUtil;
import net.minecraft.server.level.ServerLevel;
-import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -35,7 +31,6 @@ import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter {
- public static final MethodHandle RELIGHT;
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
private static final int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
@@ -43,26 +38,6 @@ public class PaperweightStarlightRelighter implements Relighter {
private static final TicketType FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
- static {
- MethodHandle tmp = null;
- try {
- MethodHandles.Lookup lookup = MethodHandles.lookup();
- tmp = lookup.findVirtual(
- ThreadedLevelLightEngine.class,
- "relight",
- MethodType.methodType(
- int.class, // return type
- // params
- Set.class,
- Consumer.class,
- IntConsumer.class
- )
- );
- } catch (NoSuchMethodException | IllegalAccessException e) {
- LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
- }
- RELIGHT = tmp;
- }
private final ServerLevel serverLevel;
private final ReentrantLock lock = new ReentrantLock();
@@ -76,10 +51,6 @@ public class PaperweightStarlightRelighter implements Relighter {
this.delegate = new NMSRelighter(queue);
}
- public static boolean isUsable() {
- return RELIGHT != null;
- }
-
@Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
areaLock.lock();
@@ -169,14 +140,9 @@ public class PaperweightStarlightRelighter implements Relighter {
IntConsumer processCallback
) {
try {
- int unused = (int) RELIGHT.invokeExact(
- serverLevel.getChunkSource().getLightEngine(),
- coords,
- chunkCallback, // callback per chunk
- processCallback // callback for all chunks
- );
- } catch (Throwable throwable) {
- LOGGER.error("Error occurred on relighting", throwable);
+ serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
+ } catch (Exception e) {
+ LOGGER.error("Error occurred on relighting", e);
}
}
diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java
index eae01a685..51bd22bdb 100644
--- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java
+++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java
@@ -146,10 +146,10 @@ public class PaperweightRegen extends Regenerator {
private static final Logger LOGGER = LogManagerCompat.getLogger();
+ private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
+
+ static {
+ try {
+ CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
+ } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
+ }
+ }
private final PaperweightAdapter parent;
// ------------------------------------------------------------------------
@@ -490,7 +501,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
- if (map != null && map.wasAccessibleSinceLastSave()) {
+ if (map != null && wasAccessibleSinceLastSave(map)) {
boolean flag = false;
// PlayerChunk.d players = map.players;
Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
@@ -642,17 +653,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override
public RelighterFactory getRelighterFactory() {
- try {
- Class.forName("ca.spottedleaf.starlight.common.light.StarLightEngine");
- if (PaperweightStarlightRelighter.isUsable()) {
- return new PaperweightStarlightRelighterFactory();
- }
- } catch (ThreadDeath td) {
- throw td;
- } catch (Throwable ignored) {
-
+ if (PaperLib.isPaper()) {
+ return new PaperweightStarlightRelighterFactory();
+ } else {
+ return new NMSRelighterFactory();
}
- return new NMSRelighterFactory();
}
@Override
@@ -674,4 +679,16 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
return new PaperweightPostProcessor();
}
+ private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
+ if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
+ try {
+ return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
+ } catch (IllegalAccessException | InvocationTargetException ignored) {
+ // fall-through
+ }
+ }
+ // Papers new chunk system has no related replacement - therefor we assume true.
+ return true;
+ }
+
}
diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java
index 9ee467201..7a0b00f1f 100644
--- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java
+++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java
@@ -14,7 +14,6 @@ import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
import com.google.common.base.Suppliers;
-import com.google.common.collect.Iterables;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
@@ -82,7 +81,6 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks {
diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java
index 307a52d77..0423a03a7 100644
--- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java
+++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java
@@ -1,5 +1,6 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
+import com.destroystokyo.paper.util.maplist.EntityList;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
@@ -18,18 +19,18 @@ import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import io.papermc.paper.world.ChunkEntitySlices;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.IdMap;
import net.minecraft.core.Registry;
-import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.BitStorage;
+import net.minecraft.util.ExceptionCollector;
import net.minecraft.util.SimpleBitStorage;
import net.minecraft.util.ThreadingDetector;
import net.minecraft.util.ZeroBitStorage;
@@ -49,6 +50,7 @@ import net.minecraft.world.level.chunk.LinearPalette;
import net.minecraft.world.level.chunk.Palette;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.SingleValuePalette;
+import net.minecraft.world.level.entity.PersistentEntitySectionManager;
import net.minecraft.world.level.gameevent.GameEventDispatcher;
import net.minecraft.world.level.gameevent.GameEventListener;
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
@@ -106,6 +108,11 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final Field fieldRemove;
+ static final boolean POST_CHUNK_REWRITE;
+ private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
+ private static Field LEVEL_CHUNK_ENTITIES;
+ private static Field SERVER_LEVEL_ENTITY_MANAGER;
+
static {
try {
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
@@ -176,6 +183,28 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
throw new Error("data type scale not a power of two");
}
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+ boolean chunkRewrite;
+ try {
+ ServerLevel.class.getDeclaredMethod("getEntityLookup");
+ chunkRewrite = true;
+ PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities");
+ PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true);
+ } catch (NoSuchMethodException ignored) {
+ chunkRewrite = false;
+ }
+ try {
+ // Paper - Pre-Chunk-Update
+ LEVEL_CHUNK_ENTITIES = LevelChunk.class.getDeclaredField("entities");
+ LEVEL_CHUNK_ENTITIES.setAccessible(true);
+ } catch (NoSuchFieldException ignored) {
+ }
+ try {
+ // Non-Paper
+ SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField("entityManager");
+ LEVEL_CHUNK_ENTITIES.setAccessible(true);
+ } catch (NoSuchFieldException ignored) {
+ }
+ POST_CHUNK_REWRITE = chunkRewrite;
} catch (RuntimeException e) {
throw e;
} catch (Throwable rethrow) {
@@ -590,10 +619,32 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
}
static List getEntities(LevelChunk chunk) {
+ ExceptionCollector collector = new ExceptionCollector<>();
if (PaperLib.isPaper()) {
- return Arrays.asList(chunk.entities.getRawData());
+ if (POST_CHUNK_REWRITE) {
+ try {
+ //noinspection unchecked
+ return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ));
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e);
+ }
+ }
+ try {
+ EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk);
+ return List.of(entityList.getRawData());
+ } catch (IllegalAccessException e) {
+ collector.add(new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=false]", e));
+ // fall through
+ }
}
- return chunk.level.entityManager.getEntities(chunk.getPos());
+ try {
+ //noinspection unchecked
+ return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos());
+ } catch (IllegalAccessException e) {
+ collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e));
+ }
+ collector.throwIfPresent();
+ return List.of();
}
record FakeIdMapBlock(int size) implements IdMap {
diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightStarlightRelighter.java
index 8591aab4c..8e1e763b1 100644
--- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightStarlightRelighter.java
+++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightStarlightRelighter.java
@@ -12,18 +12,14 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet;
-import net.minecraft.server.MCUtil;
+import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
-import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -35,34 +31,13 @@ import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter {
- public static final MethodHandle RELIGHT;
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
private static final int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
private static final TicketType FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
- private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
+ private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
- static {
- MethodHandle tmp = null;
- try {
- MethodHandles.Lookup lookup = MethodHandles.lookup();
- tmp = lookup.findVirtual(
- ThreadedLevelLightEngine.class,
- "relight",
- MethodType.methodType(
- int.class, // return type
- // params
- Set.class,
- Consumer.class,
- IntConsumer.class
- )
- );
- } catch (NoSuchMethodException | IllegalAccessException e) {
- LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
- }
- RELIGHT = tmp;
- }
private final ServerLevel serverLevel;
private final ReentrantLock lock = new ReentrantLock();
@@ -76,10 +51,6 @@ public class PaperweightStarlightRelighter implements Relighter {
this.delegate = new NMSRelighter(queue);
}
- public static boolean isUsable() {
- return RELIGHT != null;
- }
-
@Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
areaLock.lock();
@@ -169,14 +140,9 @@ public class PaperweightStarlightRelighter implements Relighter {
IntConsumer processCallback
) {
try {
- int unused = (int) RELIGHT.invokeExact(
- serverLevel.getChunkSource().getLightEngine(),
- coords,
- chunkCallback, // callback per chunk
- processCallback // callback for all chunks
- );
- } catch (Throwable throwable) {
- LOGGER.error("Error occurred on relighting", throwable);
+ serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
+ } catch (Exception e) {
+ LOGGER.error("Error occurred on relighting", e);
}
}
diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/regen/PaperweightRegen.java
index e97f78247..ebe096996 100644
--- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/regen/PaperweightRegen.java
+++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/regen/PaperweightRegen.java
@@ -294,7 +294,7 @@ public class PaperweightRegen extends Regenerator {
try {
freshChunkProvider.close(false);
- } catch (IOException e) {
+ } catch (Exception e) {
throw new RuntimeException(e);
}
});
diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts
new file mode 100644
index 000000000..25f5295c3
--- /dev/null
+++ b/worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts
@@ -0,0 +1,18 @@
+plugins {
+ java
+}
+
+applyPaperweightAdapterConfiguration()
+
+repositories {
+ maven {
+ name = "PaperMC"
+ url = uri("https://repo.papermc.io/repository/maven-public/")
+ }
+}
+
+dependencies {
+ // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
+ paperDevBundle("1.19.3-R0.1-20221226.180038-55")
+ compileOnly("io.papermc:paperlib")
+}
diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightAdapter.java
new file mode 100644
index 000000000..ddaa2c6b4
--- /dev/null
+++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightAdapter.java
@@ -0,0 +1,1029 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R2;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Futures;
+import com.mojang.datafixers.util.Either;
+import com.mojang.serialization.Lifecycle;
+import com.sk89q.jnbt.ByteArrayTag;
+import com.sk89q.jnbt.ByteTag;
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.jnbt.DoubleTag;
+import com.sk89q.jnbt.EndTag;
+import com.sk89q.jnbt.FloatTag;
+import com.sk89q.jnbt.IntArrayTag;
+import com.sk89q.jnbt.IntTag;
+import com.sk89q.jnbt.ListTag;
+import com.sk89q.jnbt.LongArrayTag;
+import com.sk89q.jnbt.LongTag;
+import com.sk89q.jnbt.NBTConstants;
+import com.sk89q.jnbt.ShortTag;
+import com.sk89q.jnbt.StringTag;
+import com.sk89q.jnbt.Tag;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.blocks.BaseItem;
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.bukkit.BukkitAdapter;
+import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
+import com.sk89q.worldedit.bukkit.adapter.Refraction;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.extension.platform.Watchdog;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.internal.Constants;
+import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
+import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
+import com.sk89q.worldedit.math.BlockVector2;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.registry.state.BooleanProperty;
+import com.sk89q.worldedit.registry.state.DirectionalProperty;
+import com.sk89q.worldedit.registry.state.EnumProperty;
+import com.sk89q.worldedit.registry.state.IntegerProperty;
+import com.sk89q.worldedit.registry.state.Property;
+import com.sk89q.worldedit.util.Direction;
+import com.sk89q.worldedit.util.SideEffect;
+import com.sk89q.worldedit.util.concurrency.LazyReference;
+import com.sk89q.worldedit.util.formatting.text.Component;
+import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
+import com.sk89q.worldedit.util.io.file.SafeFiles;
+import com.sk89q.worldedit.util.nbt.BinaryTag;
+import com.sk89q.worldedit.util.nbt.ByteArrayBinaryTag;
+import com.sk89q.worldedit.util.nbt.ByteBinaryTag;
+import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
+import com.sk89q.worldedit.util.nbt.DoubleBinaryTag;
+import com.sk89q.worldedit.util.nbt.EndBinaryTag;
+import com.sk89q.worldedit.util.nbt.FloatBinaryTag;
+import com.sk89q.worldedit.util.nbt.IntArrayBinaryTag;
+import com.sk89q.worldedit.util.nbt.IntBinaryTag;
+import com.sk89q.worldedit.util.nbt.ListBinaryTag;
+import com.sk89q.worldedit.util.nbt.LongArrayBinaryTag;
+import com.sk89q.worldedit.util.nbt.LongBinaryTag;
+import com.sk89q.worldedit.util.nbt.ShortBinaryTag;
+import com.sk89q.worldedit.util.nbt.StringBinaryTag;
+import com.sk89q.worldedit.world.DataFixer;
+import com.sk89q.worldedit.world.RegenOptions;
+import com.sk89q.worldedit.world.biome.BiomeType;
+import com.sk89q.worldedit.world.biome.BiomeTypes;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import com.sk89q.worldedit.world.block.BlockState;
+import com.sk89q.worldedit.world.block.BlockStateHolder;
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import com.sk89q.worldedit.world.item.ItemType;
+import net.minecraft.Util;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
+import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.dedicated.DedicatedServer;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ServerChunkCache;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.progress.ChunkProgressListener;
+import net.minecraft.util.StringRepresentable;
+import net.minecraft.util.thread.BlockableEventLoop;
+import net.minecraft.world.Clearable;
+import net.minecraft.world.InteractionHand;
+import net.minecraft.world.InteractionResult;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.context.UseOnContext;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.LevelSettings;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.entity.StructureBlockEntity;
+import net.minecraft.world.level.block.state.StateDefinition;
+import net.minecraft.world.level.block.state.properties.DirectionProperty;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.dimension.LevelStem;
+import net.minecraft.world.level.levelgen.WorldOptions;
+import net.minecraft.world.level.storage.LevelStorageSource;
+import net.minecraft.world.level.storage.PrimaryLevelData;
+import net.minecraft.world.phys.BlockHitResult;
+import net.minecraft.world.phys.Vec3;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World.Environment;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_19_R2.util.CraftMagicNumbers;
+import org.bukkit.entity.Player;
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
+import org.bukkit.generator.ChunkGenerator;
+import org.spigotmc.SpigotConfig;
+import org.spigotmc.WatchdogThread;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+public final class PaperweightAdapter implements BukkitImplAdapter {
+
+ private final Logger LOGGER = Logger.getLogger(getClass().getCanonicalName());
+
+ private final Field serverWorldsField;
+ private final Method getChunkFutureMethod;
+ private final Field chunkProviderExecutorField;
+ private final Watchdog watchdog;
+
+ // ------------------------------------------------------------------------
+ // Code that may break between versions of Minecraft
+ // ------------------------------------------------------------------------
+
+ public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException {
+ // A simple test
+ CraftServer.class.cast(Bukkit.getServer());
+
+ int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion();
+ if (dataVersion != 3218) {
+ throw new UnsupportedClassVersionError("Not 1.19.3!");
+ }
+
+ serverWorldsField = CraftServer.class.getDeclaredField("worlds");
+ serverWorldsField.setAccessible(true);
+
+ getChunkFutureMethod = ServerChunkCache.class.getDeclaredMethod(
+ Refraction.pickName("getChunkFutureMainThread", "c"),
+ int.class, int.class, ChunkStatus.class, boolean.class
+ );
+ getChunkFutureMethod.setAccessible(true);
+
+ chunkProviderExecutorField = ServerChunkCache.class.getDeclaredField(
+ Refraction.pickName("mainThreadProcessor", "g")
+ );
+ chunkProviderExecutorField.setAccessible(true);
+
+ new PaperweightDataConverters(CraftMagicNumbers.INSTANCE.getDataVersion(), this).buildUnoptimized();
+
+ Watchdog watchdog;
+ try {
+ Class.forName("org.spigotmc.WatchdogThread");
+ watchdog = new SpigotWatchdog();
+ } catch (ClassNotFoundException | NoSuchFieldException e) {
+ try {
+ watchdog = new MojangWatchdog(((CraftServer) Bukkit.getServer()).getServer());
+ } catch (NoSuchFieldException ex) {
+ watchdog = null;
+ }
+ }
+ this.watchdog = watchdog;
+
+ try {
+ Class.forName("org.spigotmc.SpigotConfig");
+ SpigotConfig.config.set("world-settings.faweregentempworld.verbose", false);
+ } catch (ClassNotFoundException ignored) {
+ }
+ }
+
+ @Override
+ public DataFixer getDataFixer() {
+ return PaperweightDataConverters.INSTANCE;
+ }
+
+ /**
+ * Read the given NBT data into the given tile entity.
+ *
+ * @param tileEntity the tile entity
+ * @param tag the tag
+ */
+ static void readTagIntoTileEntity(net.minecraft.nbt.CompoundTag tag, BlockEntity tileEntity) {
+ tileEntity.load(tag);
+ tileEntity.setChanged();
+ }
+
+ /**
+ * Get the ID string of the given entity.
+ *
+ * @param entity the entity
+ * @return the entity ID
+ */
+ private static String getEntityId(Entity entity) {
+ return EntityType.getKey(entity.getType()).toString();
+ }
+
+ /**
+ * Create an entity using the given entity ID.
+ *
+ * @param id the entity ID
+ * @param world the world
+ * @return an entity or null
+ */
+ @Nullable
+ private static Entity createEntityFromId(String id, net.minecraft.world.level.Level world) {
+ return EntityType.byString(id).map(t -> t.create(world)).orElse(null);
+ }
+
+ /**
+ * Write the given NBT data into the given entity.
+ *
+ * @param entity the entity
+ * @param tag the tag
+ */
+ private static void readTagIntoEntity(net.minecraft.nbt.CompoundTag tag, Entity entity) {
+ entity.load(tag);
+ }
+
+ /**
+ * Write the entity's NBT data to the given tag.
+ *
+ * @param entity the entity
+ * @param tag the tag
+ */
+ private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag tag) {
+ entity.save(tag);
+ }
+
+ private static Block getBlockFromType(BlockType blockType) {
+
+ return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK).get(ResourceLocation.tryParse(blockType.getId()));
+ }
+
+ private static Item getItemFromType(ItemType itemType) {
+ return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(itemType.getId()));
+ }
+
+ @Override
+ public OptionalInt getInternalBlockStateId(BlockData data) {
+ net.minecraft.world.level.block.state.BlockState state = ((CraftBlockData) data).getState();
+ int combinedId = Block.getId(state);
+ return combinedId == 0 && state.getBlock() != Blocks.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId);
+ }
+
+ @Override
+ public OptionalInt getInternalBlockStateId(BlockState state) {
+ Block mcBlock = getBlockFromType(state.getBlockType());
+ net.minecraft.world.level.block.state.BlockState newState = mcBlock.defaultBlockState();
+ Map, Object> states = state.getStates();
+ newState = applyProperties(mcBlock.getStateDefinition(), newState, states);
+ final int combinedId = Block.getId(newState);
+ return combinedId == 0 && state.getBlockType() != BlockTypes.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId);
+ }
+
+ @Override
+ public BlockState getBlock(Location location) {
+ checkNotNull(location);
+
+ CraftWorld craftWorld = ((CraftWorld) location.getWorld());
+ int x = location.getBlockX();
+ int y = location.getBlockY();
+ int z = location.getBlockZ();
+
+ final ServerLevel handle = craftWorld.getHandle();
+ LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
+ final BlockPos blockPos = new BlockPos(x, y, z);
+ final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
+ int internalId = Block.getId(blockData);
+ BlockState state = BlockStateIdAccess.getBlockStateById(internalId);
+ if (state == null) {
+ org.bukkit.block.Block bukkitBlock = location.getBlock();
+ state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
+ }
+
+ return state;
+ }
+
+ @Override
+ public BaseBlock getFullBlock(Location location) {
+ BlockState state = getBlock(location);
+
+ CraftWorld craftWorld = ((CraftWorld) location.getWorld());
+ int x = location.getBlockX();
+ int y = location.getBlockY();
+ int z = location.getBlockZ();
+
+ final ServerLevel handle = craftWorld.getHandle();
+ LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
+ final BlockPos blockPos = new BlockPos(x, y, z);
+
+ // Read the NBT data
+ BlockEntity te = chunk.getBlockEntity(blockPos);
+ if (te != null) {
+ net.minecraft.nbt.CompoundTag tag = te.saveWithId();
+ return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
+ }
+
+ return state.toBaseBlock();
+ }
+
+ @Override
+ public WorldNativeAccess, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
+ return new PaperweightWorldNativeAccess(this,
+ new WeakReference<>(((CraftWorld) world).getHandle()));
+ }
+
+ private static net.minecraft.core.Direction adapt(Direction face) {
+ switch (face) {
+ case NORTH:
+ return net.minecraft.core.Direction.NORTH;
+ case SOUTH:
+ return net.minecraft.core.Direction.SOUTH;
+ case WEST:
+ return net.minecraft.core.Direction.WEST;
+ case EAST:
+ return net.minecraft.core.Direction.EAST;
+ case DOWN:
+ return net.minecraft.core.Direction.DOWN;
+ case UP:
+ default:
+ return net.minecraft.core.Direction.UP;
+ }
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private net.minecraft.world.level.block.state.BlockState applyProperties(
+ StateDefinition stateContainer,
+ net.minecraft.world.level.block.state.BlockState newState,
+ Map, Object> states
+ ) {
+ for (Map.Entry, Object> state : states.entrySet()) {
+ net.minecraft.world.level.block.state.properties.Property> property =
+ stateContainer.getProperty(state.getKey().getName());
+ Comparable> value = (Comparable) state.getValue();
+ // we may need to adapt this value, depending on the source prop
+ if (property instanceof DirectionProperty) {
+ Direction dir = (Direction) value;
+ value = adapt(dir);
+ } else if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
+ String enumName = (String) value;
+ value = ((net.minecraft.world.level.block.state.properties.EnumProperty>) property)
+ .getValue(enumName).orElseThrow(() ->
+ new IllegalStateException(
+ "Enum property " + property.getName() + " does not contain " + enumName
+ )
+ );
+ }
+
+ newState = newState.setValue(
+ (net.minecraft.world.level.block.state.properties.Property) property,
+ (Comparable) value
+ );
+ }
+ return newState;
+ }
+
+ @Override
+ public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
+ checkNotNull(entity);
+
+ CraftEntity craftEntity = ((CraftEntity) entity);
+ Entity mcEntity = craftEntity.getHandle();
+
+ // Do not allow creating of passenger entity snapshots, passengers are included in the vehicle entity
+ if (mcEntity.isPassenger()) {
+ return null;
+ }
+
+ String id = getEntityId(mcEntity);
+
+ net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
+ readEntityIntoTag(mcEntity, tag);
+ return new BaseEntity(
+ com.sk89q.worldedit.world.entity.EntityTypes.get(id),
+ LazyReference.from(() -> (CompoundBinaryTag) toNativeBinary(tag))
+ );
+ }
+
+ @Nullable
+ @Override
+ public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) {
+ checkNotNull(location);
+ checkNotNull(state);
+
+ CraftWorld craftWorld = ((CraftWorld) location.getWorld());
+ ServerLevel worldServer = craftWorld.getHandle();
+
+ Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle());
+
+ if (createdEntity != null) {
+ CompoundBinaryTag nativeTag = state.getNbt();
+ if (nativeTag != null) {
+ net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(nativeTag);
+ for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
+ tag.remove(name);
+ }
+ readTagIntoEntity(tag, createdEntity);
+ }
+
+ createdEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
+
+ worldServer.addFreshEntity(createdEntity, SpawnReason.CUSTOM);
+ return createdEntity.getBukkitEntity();
+ } else {
+ return null;
+ }
+ }
+
+ // This removes all unwanted tags from the main entity and all its passengers
+ private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) {
+ for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
+ tag.remove(name);
+ }
+
+ // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive
+ if (tag.contains("Passengers", NBTConstants.TYPE_LIST)) {
+ net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", NBTConstants.TYPE_COMPOUND);
+
+ for (int i = 0; i < nbttaglist.size(); ++i) {
+ removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i));
+ }
+ }
+ }
+
+ @Override
+ public Component getRichBlockName(BlockType blockType) {
+ return TranslatableComponent.of(getBlockFromType(blockType).getDescriptionId());
+ }
+
+ @Override
+ public Component getRichItemName(ItemType itemType) {
+ return TranslatableComponent.of(getItemFromType(itemType).getDescriptionId());
+ }
+
+ @Override
+ public Component getRichItemName(BaseItemStack itemStack) {
+ return TranslatableComponent.of(CraftItemStack.asNMSCopy(BukkitAdapter.adapt(itemStack)).getDescriptionId());
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader>() {
+ @Override
+ public Property> load(net.minecraft.world.level.block.state.properties.Property state) throws Exception {
+ if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) {
+ return new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues()));
+ } else if (state instanceof DirectionProperty) {
+ return new DirectionalProperty(state.getName(),
+ (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).collect(Collectors.toList()));
+ } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
+ return new EnumProperty(state.getName(),
+ (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).collect(Collectors.toList()));
+ } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) {
+ return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues()));
+ } else {
+ throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName());
+ }
+ }
+ });
+
+ @SuppressWarnings({ "rawtypes" })
+ @Override
+ public Map> getProperties(BlockType blockType) {
+ Map> properties = new TreeMap<>();
+ Block block = getBlockFromType(blockType);
+ StateDefinition blockStateList =
+ block.getStateDefinition();
+ for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) {
+ Property> property = PROPERTY_CACHE.getUnchecked(state);
+ properties.put(property.getName(), property);
+ }
+ return properties;
+ }
+
+ @Override
+ public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) {
+ ((CraftPlayer) player).getHandle().connection.send(ClientboundBlockEntityDataPacket.create(
+ new StructureBlockEntity(
+ new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()),
+ Blocks.STRUCTURE_BLOCK.defaultBlockState()
+ ),
+ __ -> (net.minecraft.nbt.CompoundTag) fromNativeBinary(nbtData)
+ ));
+ }
+
+ @Override
+ public void sendFakeOP(Player player) {
+ ((CraftPlayer) player).getHandle().connection.send(new ClientboundEntityEventPacket(
+ ((CraftPlayer) player).getHandle(), (byte) 28
+ ));
+ }
+
+ @Override
+ public org.bukkit.inventory.ItemStack adapt(BaseItemStack item) {
+ ItemStack stack = new ItemStack(
+ DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(item.getType().getId())),
+ item.getAmount()
+ );
+ stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData())));
+ return CraftItemStack.asCraftMirror(stack);
+ }
+
+ @Override
+ public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
+ final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
+ final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount());
+ weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag())));
+ return weStack;
+ }
+
+ private final LoadingCache fakePlayers
+ = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new));
+
+ @Override
+ public boolean simulateItemUse(org.bukkit.World world, BlockVector3 position, BaseItem item, Direction face) {
+ CraftWorld craftWorld = (CraftWorld) world;
+ ServerLevel worldServer = craftWorld.getHandle();
+ ItemStack stack = CraftItemStack.asNMSCopy(BukkitAdapter.adapt(item instanceof BaseItemStack
+ ? ((BaseItemStack) item) : new BaseItemStack(item.getType(), item.getNbtData(), 1)));
+ stack.setTag((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData()));
+
+ PaperweightFakePlayer fakePlayer;
+ try {
+ fakePlayer = fakePlayers.get(worldServer);
+ } catch (ExecutionException ignored) {
+ return false;
+ }
+ fakePlayer.setItemInHand(InteractionHand.MAIN_HAND, stack);
+ fakePlayer.absMoveTo(position.getBlockX(), position.getBlockY(), position.getBlockZ(),
+ (float) face.toVector().toYaw(), (float) face.toVector().toPitch());
+
+ final BlockPos blockPos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
+ final Vec3 blockVec = Vec3.atLowerCornerOf(blockPos);
+ final net.minecraft.core.Direction enumFacing = adapt(face);
+ BlockHitResult rayTrace = new BlockHitResult(blockVec, enumFacing, blockPos, false);
+ UseOnContext context = new UseOnContext(fakePlayer, InteractionHand.MAIN_HAND, rayTrace);
+ InteractionResult result = stack.useOn(context, InteractionHand.MAIN_HAND);
+ if (result != InteractionResult.SUCCESS) {
+ if (worldServer.getBlockState(blockPos).use(worldServer, fakePlayer, InteractionHand.MAIN_HAND, rayTrace).consumesAction()) {
+ result = InteractionResult.SUCCESS;
+ } else {
+ result = stack.getItem().use(worldServer, fakePlayer, InteractionHand.MAIN_HAND).getResult();
+ }
+ }
+
+ return result == InteractionResult.SUCCESS;
+ }
+
+ @Override
+ public boolean canPlaceAt(org.bukkit.World world, BlockVector3 position, BlockState blockState) {
+ int internalId = BlockStateIdAccess.getBlockStateId(blockState);
+ net.minecraft.world.level.block.state.BlockState blockData = Block.stateById(internalId);
+ return blockData.canSurvive(((CraftWorld) world).getHandle(), new BlockPos(position.getX(), position.getY(), position.getZ()));
+ }
+
+ @Override
+ public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) {
+ try {
+ doRegen(bukkitWorld, region, extent, options);
+ } catch (Exception e) {
+ throw new IllegalStateException("Regen failed.", e);
+ }
+
+ return true;
+ }
+
+ private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) throws Exception {
+ Environment env = bukkitWorld.getEnvironment();
+ ChunkGenerator gen = bukkitWorld.getGenerator();
+
+ Path tempDir = Files.createTempDirectory("WorldEditWorldGen");
+ LevelStorageSource levelStorage = LevelStorageSource.createDefault(tempDir);
+ ResourceKey worldDimKey = getWorldDimKey(env);
+ try (LevelStorageSource.LevelStorageAccess session = levelStorage.createAccess("faweregentempworld", worldDimKey)) {
+ ServerLevel originalWorld = ((CraftWorld) bukkitWorld).getHandle();
+ PrimaryLevelData levelProperties = (PrimaryLevelData) originalWorld.getServer()
+ .getWorldData().overworldData();
+ WorldOptions originalOpts = levelProperties.worldGenOptions();
+
+ long seed = options.getSeed().orElse(originalWorld.getSeed());
+ WorldOptions newOpts = options.getSeed().isPresent()
+ ? originalOpts.withSeed(OptionalLong.of(seed))
+ : originalOpts;
+
+ LevelSettings newWorldSettings = new LevelSettings(
+ "faweregentempworld",
+ levelProperties.settings.gameType(),
+ levelProperties.settings.hardcore(),
+ levelProperties.settings.difficulty(),
+ levelProperties.settings.allowCommands(),
+ levelProperties.settings.gameRules(),
+ levelProperties.settings.getDataConfiguration()
+ );
+
+ PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
+ levelProperties.isFlatWorld()
+ ? PrimaryLevelData.SpecialWorldProperty.FLAT
+ : levelProperties.isDebugWorld()
+ ? PrimaryLevelData.SpecialWorldProperty.DEBUG
+ : PrimaryLevelData.SpecialWorldProperty.NONE;
+
+ PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
+
+ ServerLevel freshWorld = new ServerLevel(
+ originalWorld.getServer(),
+ originalWorld.getServer().executor,
+ session, newWorldData,
+ originalWorld.dimension(),
+ new LevelStem(
+ originalWorld.dimensionTypeRegistration(),
+ originalWorld.getChunkSource().getGenerator()
+ ),
+ new NoOpWorldLoadListener(),
+ originalWorld.isDebug(),
+ seed,
+ ImmutableList.of(),
+ false,
+ env,
+ gen,
+ bukkitWorld.getBiomeProvider()
+ );
+ try {
+ regenForWorld(region, extent, freshWorld, options);
+ } finally {
+ freshWorld.getChunkSource().close(false);
+ }
+ } finally {
+ try {
+ @SuppressWarnings("unchecked")
+ Map map = (Map) serverWorldsField.get(Bukkit.getServer());
+ map.remove("faweregentempworld");
+ } catch (IllegalAccessException ignored) {
+ }
+ SafeFiles.tryHardToDeleteDir(tempDir);
+ }
+ }
+
+ private BiomeType adapt(ServerLevel serverWorld, Biome origBiome) {
+ ResourceLocation key = serverWorld.registryAccess().registryOrThrow(Registries.BIOME).getKey(origBiome);
+ if (key == null) {
+ return null;
+ }
+ return BiomeTypes.get(key.toString());
+ }
+
+ @SuppressWarnings("unchecked")
+ private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld, RegenOptions options) throws WorldEditException {
+ List> chunkLoadings = submitChunkLoadTasks(region, serverWorld);
+ BlockableEventLoop executor;
+ try {
+ executor = (BlockableEventLoop) chunkProviderExecutorField.get(serverWorld.getChunkSource());
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException("Couldn't get executor for chunk loading.", e);
+ }
+ executor.managedBlock(() -> {
+ // bail out early if a future fails
+ if (chunkLoadings.stream().anyMatch(ftr ->
+ ftr.isDone() && Futures.getUnchecked(ftr) == null
+ )) {
+ return false;
+ }
+ return chunkLoadings.stream().allMatch(CompletableFuture::isDone);
+ });
+ Map chunks = new HashMap<>();
+ for (CompletableFuture future : chunkLoadings) {
+ @Nullable
+ ChunkAccess chunk = future.getNow(null);
+ checkState(chunk != null, "Failed to generate a chunk, regen failed.");
+ chunks.put(chunk.getPos(), chunk);
+ }
+
+ for (BlockVector3 vec : region) {
+ BlockPos pos = new BlockPos(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ());
+ ChunkAccess chunk = chunks.get(new ChunkPos(pos));
+ final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(pos);
+ int internalId = Block.getId(blockData);
+ BlockStateHolder> state = BlockStateIdAccess.getBlockStateById(internalId);
+ Objects.requireNonNull(state);
+ BlockEntity blockEntity = chunk.getBlockEntity(pos);
+ if (blockEntity != null) {
+ net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
+ state = state.toBaseBlock(((CompoundBinaryTag) toNativeBinary(tag)));
+ }
+ extent.setBlock(vec, state.toBaseBlock());
+ if (options.shouldRegenBiomes()) {
+ Biome origBiome = chunk.getNoiseBiome(vec.getX(), vec.getY(), vec.getZ()).value();
+ BiomeType adaptedBiome = adapt(serverWorld, origBiome);
+ if (adaptedBiome != null) {
+ extent.setBiome(vec, adaptedBiome);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private List> submitChunkLoadTasks(Region region, ServerLevel serverWorld) {
+ ServerChunkCache chunkManager = serverWorld.getChunkSource();
+ List> chunkLoadings = new ArrayList<>();
+ // Pre-gen all the chunks
+ for (BlockVector2 chunk : region.getChunks()) {
+ try {
+ //noinspection unchecked
+ chunkLoadings.add(
+ ((CompletableFuture>)
+ getChunkFutureMethod.invoke(chunkManager, chunk.getX(), chunk.getZ(), ChunkStatus.FEATURES, true))
+ .thenApply(either -> either.left().orElse(null))
+ );
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalStateException("Couldn't load chunk for regen.", e);
+ }
+ }
+ return chunkLoadings;
+ }
+
+ private ResourceKey getWorldDimKey(Environment env) {
+ switch (env) {
+ case NETHER:
+ return LevelStem.NETHER;
+ case THE_END:
+ return LevelStem.END;
+ case NORMAL:
+ default:
+ return LevelStem.OVERWORLD;
+ }
+ }
+
+ private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
+ SideEffect.NEIGHBORS,
+ SideEffect.LIGHTING,
+ SideEffect.VALIDATION,
+ SideEffect.ENTITY_AI,
+ SideEffect.EVENTS,
+ SideEffect.UPDATE
+ );
+
+ @Override
+ public Set getSupportedSideEffects() {
+ return SUPPORTED_SIDE_EFFECTS;
+ }
+
+ @Override
+ public boolean clearContainerBlockContents(org.bukkit.World world, BlockVector3 pt) {
+ ServerLevel originalWorld = ((CraftWorld) world).getHandle();
+
+ BlockEntity entity = originalWorld.getBlockEntity(new BlockPos(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()));
+ if (entity instanceof Clearable) {
+ ((Clearable) entity).clearContent();
+ return true;
+ }
+ return false;
+ }
+
+ // ------------------------------------------------------------------------
+ // Code that is less likely to break
+ // ------------------------------------------------------------------------
+
+ /**
+ * Converts from a non-native NMS NBT structure to a native WorldEdit NBT
+ * structure.
+ *
+ * @param foreign non-native NMS NBT structure
+ * @return native WorldEdit NBT structure
+ */
+ @Override
+ public BinaryTag toNativeBinary(net.minecraft.nbt.Tag foreign) {
+ if (foreign == null) {
+ return null;
+ }
+ if (foreign instanceof net.minecraft.nbt.CompoundTag) {
+ Map values = new HashMap<>();
+ Set foreignKeys = ((net.minecraft.nbt.CompoundTag) foreign).getAllKeys();
+
+ for (String str : foreignKeys) {
+ net.minecraft.nbt.Tag base = ((net.minecraft.nbt.CompoundTag) foreign).get(str);
+ values.put(str, toNativeBinary(base));
+ }
+ return CompoundBinaryTag.from(values);
+ } else if (foreign instanceof net.minecraft.nbt.ByteTag) {
+ return ByteBinaryTag.of(((net.minecraft.nbt.ByteTag) foreign).getAsByte());
+ } else if (foreign instanceof net.minecraft.nbt.ByteArrayTag) {
+ return ByteArrayBinaryTag.of(((net.minecraft.nbt.ByteArrayTag) foreign).getAsByteArray());
+ } else if (foreign instanceof net.minecraft.nbt.DoubleTag) {
+ return DoubleBinaryTag.of(((net.minecraft.nbt.DoubleTag) foreign).getAsDouble());
+ } else if (foreign instanceof net.minecraft.nbt.FloatTag) {
+ return FloatBinaryTag.of(((net.minecraft.nbt.FloatTag) foreign).getAsFloat());
+ } else if (foreign instanceof net.minecraft.nbt.IntTag) {
+ return IntBinaryTag.of(((net.minecraft.nbt.IntTag) foreign).getAsInt());
+ } else if (foreign instanceof net.minecraft.nbt.IntArrayTag) {
+ return IntArrayBinaryTag.of(((net.minecraft.nbt.IntArrayTag) foreign).getAsIntArray());
+ } else if (foreign instanceof net.minecraft.nbt.LongArrayTag) {
+ return LongArrayBinaryTag.of(((net.minecraft.nbt.LongArrayTag) foreign).getAsLongArray());
+ } else if (foreign instanceof net.minecraft.nbt.ListTag) {
+ try {
+ return toNativeList((net.minecraft.nbt.ListTag) foreign);
+ } catch (Throwable e) {
+ LOGGER.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e);
+ return ListBinaryTag.empty();
+ }
+ } else if (foreign instanceof net.minecraft.nbt.LongTag) {
+ return LongBinaryTag.of(((net.minecraft.nbt.LongTag) foreign).getAsLong());
+ } else if (foreign instanceof net.minecraft.nbt.ShortTag) {
+ return ShortBinaryTag.of(((net.minecraft.nbt.ShortTag) foreign).getAsShort());
+ } else if (foreign instanceof net.minecraft.nbt.StringTag) {
+ return StringBinaryTag.of(foreign.getAsString());
+ } else if (foreign instanceof net.minecraft.nbt.EndTag) {
+ return EndBinaryTag.get();
+ } else {
+ throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName());
+ }
+ }
+
+ /**
+ * Convert a foreign NBT list tag into a native WorldEdit one.
+ *
+ * @param foreign the foreign tag
+ * @return the converted tag
+ * @throws SecurityException on error
+ * @throws IllegalArgumentException on error
+ */
+ private ListBinaryTag toNativeList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException {
+ ListBinaryTag.Builder values = ListBinaryTag.builder();
+
+ for (net.minecraft.nbt.Tag tag : foreign) {
+ values.add(toNativeBinary(tag));
+ }
+
+ return values.build();
+ }
+
+ /**
+ * Converts a WorldEdit-native NBT structure to a NMS structure.
+ *
+ * @param foreign structure to convert
+ * @return non-native structure
+ */
+ @Override
+ public net.minecraft.nbt.Tag fromNativeBinary(BinaryTag foreign) {
+ if (foreign == null) {
+ return null;
+ }
+ if (foreign instanceof CompoundBinaryTag) {
+ net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
+ for (String key : ((CompoundBinaryTag) foreign).keySet()) {
+ tag.put(key, fromNativeBinary(((CompoundBinaryTag) foreign).get(key)));
+ }
+ return tag;
+ } else if (foreign instanceof ByteBinaryTag) {
+ return net.minecraft.nbt.ByteTag.valueOf(((ByteBinaryTag) foreign).value());
+ } else if (foreign instanceof ByteArrayBinaryTag) {
+ return new net.minecraft.nbt.ByteArrayTag(((ByteArrayBinaryTag) foreign).value());
+ } else if (foreign instanceof DoubleBinaryTag) {
+ return net.minecraft.nbt.DoubleTag.valueOf(((DoubleBinaryTag) foreign).value());
+ } else if (foreign instanceof FloatBinaryTag) {
+ return net.minecraft.nbt.FloatTag.valueOf(((FloatBinaryTag) foreign).value());
+ } else if (foreign instanceof IntBinaryTag) {
+ return net.minecraft.nbt.IntTag.valueOf(((IntBinaryTag) foreign).value());
+ } else if (foreign instanceof IntArrayBinaryTag) {
+ return new net.minecraft.nbt.IntArrayTag(((IntArrayBinaryTag) foreign).value());
+ } else if (foreign instanceof LongArrayBinaryTag) {
+ return new net.minecraft.nbt.LongArrayTag(((LongArrayBinaryTag) foreign).value());
+ } else if (foreign instanceof ListBinaryTag) {
+ net.minecraft.nbt.ListTag tag = new net.minecraft.nbt.ListTag();
+ ListBinaryTag foreignList = (ListBinaryTag) foreign;
+ for (BinaryTag t : foreignList) {
+ tag.add(fromNativeBinary(t));
+ }
+ return tag;
+ } else if (foreign instanceof LongBinaryTag) {
+ return net.minecraft.nbt.LongTag.valueOf(((LongBinaryTag) foreign).value());
+ } else if (foreign instanceof ShortBinaryTag) {
+ return net.minecraft.nbt.ShortTag.valueOf(((ShortBinaryTag) foreign).value());
+ } else if (foreign instanceof StringBinaryTag) {
+ return net.minecraft.nbt.StringTag.valueOf(((StringBinaryTag) foreign).value());
+ } else if (foreign instanceof EndBinaryTag) {
+ return net.minecraft.nbt.EndTag.INSTANCE;
+ } else {
+ throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName());
+ }
+ }
+
+ @Override
+ public boolean supportsWatchdog() {
+ return watchdog != null;
+ }
+
+ @Override
+ public void tickWatchdog() {
+ watchdog.tick();
+ }
+
+ private class SpigotWatchdog implements Watchdog {
+ private final Field instanceField;
+ private final Field lastTickField;
+
+ SpigotWatchdog() throws NoSuchFieldException {
+ Field instanceField = WatchdogThread.class.getDeclaredField("instance");
+ instanceField.setAccessible(true);
+ this.instanceField = instanceField;
+
+ Field lastTickField = WatchdogThread.class.getDeclaredField("lastTick");
+ lastTickField.setAccessible(true);
+ this.lastTickField = lastTickField;
+ }
+
+ @Override
+ public void tick() {
+ try {
+ WatchdogThread instance = (WatchdogThread) this.instanceField.get(null);
+ if ((long) lastTickField.get(instance) != 0) {
+ WatchdogThread.tick();
+ }
+ } catch (IllegalAccessException e) {
+ LOGGER.log(Level.WARNING, "Failed to tick watchdog", e);
+ }
+ }
+ }
+
+ private static class MojangWatchdog implements Watchdog {
+ private final DedicatedServer server;
+ private final Field tickField;
+
+ MojangWatchdog(DedicatedServer server) throws NoSuchFieldException {
+ this.server = server;
+ Field tickField = MinecraftServer.class.getDeclaredField(
+ Refraction.pickName("nextTickTime", "ag")
+ );
+ if (tickField.getType() != long.class) {
+ throw new IllegalStateException("nextTickTime is not a long field, mapping is likely incorrect");
+ }
+ tickField.setAccessible(true);
+ this.tickField = tickField;
+ }
+
+ @Override
+ public void tick() {
+ try {
+ tickField.set(server, Util.getMillis());
+ } catch (IllegalAccessException ignored) {
+ }
+ }
+ }
+
+ private static class NoOpWorldLoadListener implements ChunkProgressListener {
+ @Override
+ public void updateSpawnPos(ChunkPos spawnPos) {
+ }
+
+ @Override
+ public void onStatusChange(ChunkPos pos, @org.jetbrains.annotations.Nullable ChunkStatus status) {
+ }
+
+ @Override
+ public void start() {
+ }
+
+ @Override
+ public void stop() {
+ }
+
+ @Override
+ public void setChunkRadius(int radius) {
+ }
+ }
+}
diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightDataConverters.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightDataConverters.java
new file mode 100644
index 000000000..7e735efdb
--- /dev/null
+++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightDataConverters.java
@@ -0,0 +1,2800 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R2;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.mojang.datafixers.DSL.TypeReference;
+import com.mojang.datafixers.DataFixer;
+import com.mojang.datafixers.DataFixerBuilder;
+import com.mojang.datafixers.schemas.Schema;
+import com.mojang.serialization.Dynamic;
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
+import net.minecraft.core.Direction;
+import net.minecraft.nbt.NbtOps;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.MutableComponent;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.GsonHelper;
+import net.minecraft.util.StringUtil;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
+import net.minecraft.world.item.DyeColor;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+/**
+ * Handles converting all Pre 1.13.2 data using the Legacy DataFix System (ported to 1.13.2)
+ *
+ * We register a DFU Fixer per Legacy Data Version and apply the fixes using legacy strategy
+ * which is safer, faster and cleaner code.
+ *
+ * The pre DFU code did not fail when the Source version was unknown.
+ *
+ * This class also provides util methods for converting compounds to wrap the update call to
+ * receive the source version in the compound
+ */
+@SuppressWarnings({ "rawtypes", "unchecked" })
+class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer {
+
+ //FAWE start - BinaryTag
+ @SuppressWarnings("unchecked")
+ @Override
+ public T fixUp(FixType type, T original, int srcVer) {
+ if (type == FixTypes.CHUNK) {
+ return (T) fixChunk((CompoundBinaryTag) original, srcVer);
+ } else if (type == FixTypes.BLOCK_ENTITY) {
+ return (T) fixBlockEntity((CompoundBinaryTag) original, srcVer);
+ } else if (type == FixTypes.ENTITY) {
+ return (T) fixEntity((CompoundBinaryTag) original, srcVer);
+ } else if (type == FixTypes.BLOCK_STATE) {
+ return (T) fixBlockState((String) original, srcVer);
+ } else if (type == FixTypes.ITEM_TYPE) {
+ return (T) fixItemType((String) original, srcVer);
+ } else if (type == FixTypes.BIOME) {
+ return (T) fixBiome((String) original, srcVer);
+ }
+ return original;
+ }
+
+ private CompoundBinaryTag fixChunk(CompoundBinaryTag originalChunk, int srcVer) {
+ net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(originalChunk);
+ net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.CHUNK, tag, srcVer);
+ return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
+ }
+
+ private CompoundBinaryTag fixBlockEntity(CompoundBinaryTag origTileEnt, int srcVer) {
+ net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origTileEnt);
+ net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer);
+ return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
+ }
+
+ private CompoundBinaryTag fixEntity(CompoundBinaryTag origEnt, int srcVer) {
+ net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origEnt);
+ net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.ENTITY, tag, srcVer);
+ return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
+ }
+ //FAWE end
+
+ private String fixBlockState(String blockState, int srcVer) {
+ net.minecraft.nbt.CompoundTag stateNBT = stateToNBT(blockState);
+ Dynamic dynamic = new Dynamic<>(OPS_NBT, stateNBT);
+ net.minecraft.nbt.CompoundTag fixed = (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update(References.BLOCK_STATE, dynamic, srcVer, DATA_VERSION).getValue();
+ return nbtToState(fixed);
+ }
+
+ private String nbtToState(net.minecraft.nbt.CompoundTag tagCompound) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(tagCompound.getString("Name"));
+ if (tagCompound.contains("Properties", 10)) {
+ sb.append('[');
+ net.minecraft.nbt.CompoundTag props = tagCompound.getCompound("Properties");
+ sb.append(props.getAllKeys().stream().map(k -> k + "=" + props.getString(k).replace("\"", "")).collect(Collectors.joining(",")));
+ sb.append(']');
+ }
+ return sb.toString();
+ }
+
+ private static net.minecraft.nbt.CompoundTag stateToNBT(String blockState) {
+ int propIdx = blockState.indexOf('[');
+ net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
+ if (propIdx < 0) {
+ tag.putString("Name", blockState);
+ } else {
+ tag.putString("Name", blockState.substring(0, propIdx));
+ net.minecraft.nbt.CompoundTag propTag = new net.minecraft.nbt.CompoundTag();
+ String props = blockState.substring(propIdx + 1, blockState.length() - 1);
+ String[] propArr = props.split(",");
+ for (String pair : propArr) {
+ final String[] split = pair.split("=");
+ propTag.putString(split[0], split[1]);
+ }
+ tag.put("Properties", propTag);
+ }
+ return tag;
+ }
+
+ private String fixBiome(String key, int srcVer) {
+ return fixName(key, srcVer, References.BIOME);
+ }
+
+ private String fixItemType(String key, int srcVer) {
+ return fixName(key, srcVer, References.ITEM_NAME);
+ }
+
+ private static String fixName(String key, int srcVer, TypeReference type) {
+ return INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, net.minecraft.nbt.StringTag.valueOf(key)), srcVer, DATA_VERSION)
+ .getValue().getAsString();
+ }
+
+ private final PaperweightAdapter adapter;
+
+ private static final NbtOps OPS_NBT = NbtOps.INSTANCE;
+ private static final int LEGACY_VERSION = 1343;
+ private static int DATA_VERSION;
+ static PaperweightDataConverters INSTANCE;
+
+ private final Map> converters = new EnumMap<>(LegacyType.class);
+ private final Map> inspectors = new EnumMap<>(LegacyType.class);
+
+ // Set on build
+ private DataFixer fixer;
+ private static final Map DFU_TO_LEGACY = new HashMap<>();
+
+ public enum LegacyType {
+ LEVEL(References.LEVEL),
+ PLAYER(References.PLAYER),
+ CHUNK(References.CHUNK),
+ BLOCK_ENTITY(References.BLOCK_ENTITY),
+ ENTITY(References.ENTITY),
+ ITEM_INSTANCE(References.ITEM_STACK),
+ OPTIONS(References.OPTIONS),
+ STRUCTURE(References.STRUCTURE);
+
+ private final TypeReference type;
+
+ LegacyType(TypeReference type) {
+ this.type = type;
+ DFU_TO_LEGACY.put(type.typeName(), this);
+ }
+
+ public TypeReference getDFUType() {
+ return type;
+ }
+ }
+
+ PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) {
+ super(dataVersion);
+ DATA_VERSION = dataVersion;
+ INSTANCE = this;
+ this.adapter = adapter;
+ registerConverters();
+ registerInspectors();
+ }
+
+
+ // Called after fixers are built and ready for FIXING
+ @Override
+ public DataFixer buildUnoptimized() {
+ return this.fixer = new WrappedDataFixer(DataFixers.getDataFixer());
+ }
+
+ @Override
+ public DataFixer buildOptimized(Executor executor) {
+ return buildUnoptimized();
+ }
+
+ @SuppressWarnings("unchecked")
+ private class WrappedDataFixer implements DataFixer {
+ private final DataFixer realFixer;
+
+ WrappedDataFixer(DataFixer realFixer) {
+ this.realFixer = realFixer;
+ }
+
+ @Override
+ public Dynamic update(TypeReference type, Dynamic dynamic, int sourceVer, int targetVer) {
+ LegacyType legacyType = DFU_TO_LEGACY.get(type.typeName());
+ if (sourceVer < LEGACY_VERSION && legacyType != null) {
+ net.minecraft.nbt.CompoundTag cmp = (net.minecraft.nbt.CompoundTag) dynamic.getValue();
+ int desiredVersion = Math.min(targetVer, LEGACY_VERSION);
+
+ cmp = convert(legacyType, cmp, sourceVer, desiredVersion);
+ sourceVer = desiredVersion;
+ dynamic = new Dynamic(OPS_NBT, cmp);
+ }
+ return realFixer.update(type, dynamic, sourceVer, targetVer);
+ }
+
+ private net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int desiredVersion) {
+ List converters = PaperweightDataConverters.this.converters.get(type);
+ if (converters != null && !converters.isEmpty()) {
+ for (DataConverter converter : converters) {
+ int dataVersion = converter.getDataVersion();
+ if (dataVersion > sourceVer && dataVersion <= desiredVersion) {
+ cmp = converter.convert(cmp);
+ }
+ }
+ }
+
+ List inspectors = PaperweightDataConverters.this.inspectors.get(type);
+ if (inspectors != null && !inspectors.isEmpty()) {
+ for (DataInspector inspector : inspectors) {
+ cmp = inspector.inspect(cmp, sourceVer, desiredVersion);
+ }
+ }
+
+ return cmp;
+ }
+
+ @Override
+ public Schema getSchema(int i) {
+ return realFixer.getSchema(i);
+ }
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp) {
+ return convert(type.getDFUType(), cmp);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) {
+ return convert(type.getDFUType(), cmp, sourceVer);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ return convert(type.getDFUType(), cmp, sourceVer, targetVer);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp) {
+ int i = cmp.contains("DataVersion", 99) ? cmp.getInt("DataVersion") : -1;
+ return convert(type, cmp, i);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) {
+ return convert(type, cmp, sourceVer, DATA_VERSION);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (sourceVer >= targetVer) {
+ return cmp;
+ }
+ return (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, cmp), sourceVer, targetVer).getValue();
+ }
+
+
+ public interface DataInspector {
+ net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer);
+ }
+
+ public interface DataConverter {
+
+ int getDataVersion();
+
+ net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp);
+ }
+
+
+ private void registerInspector(LegacyType type, DataInspector inspector) {
+ this.inspectors.computeIfAbsent(type, k -> new ArrayList<>()).add(inspector);
+ }
+
+ private void registerConverter(LegacyType type, DataConverter converter) {
+ int version = converter.getDataVersion();
+
+ List list = this.converters.computeIfAbsent(type, k -> new ArrayList<>());
+ if (!list.isEmpty() && list.get(list.size() - 1).getDataVersion() > version) {
+ for (int j = 0; j < list.size(); ++j) {
+ if (list.get(j).getDataVersion() > version) {
+ list.add(j, converter);
+ break;
+ }
+ }
+ } else {
+ list.add(converter);
+ }
+ }
+
+ private void registerInspectors() {
+ registerEntityItemList("EntityHorseDonkey", "SaddleItem", "Items");
+ registerEntityItemList("EntityHorseMule", "Items");
+ registerEntityItemList("EntityMinecartChest", "Items");
+ registerEntityItemList("EntityMinecartHopper", "Items");
+ registerEntityItemList("EntityVillager", "Inventory");
+ registerEntityItemListEquipment("EntityArmorStand");
+ registerEntityItemListEquipment("EntityBat");
+ registerEntityItemListEquipment("EntityBlaze");
+ registerEntityItemListEquipment("EntityCaveSpider");
+ registerEntityItemListEquipment("EntityChicken");
+ registerEntityItemListEquipment("EntityCow");
+ registerEntityItemListEquipment("EntityCreeper");
+ registerEntityItemListEquipment("EntityEnderDragon");
+ registerEntityItemListEquipment("EntityEnderman");
+ registerEntityItemListEquipment("EntityEndermite");
+ registerEntityItemListEquipment("EntityEvoker");
+ registerEntityItemListEquipment("EntityGhast");
+ registerEntityItemListEquipment("EntityGiantZombie");
+ registerEntityItemListEquipment("EntityGuardian");
+ registerEntityItemListEquipment("EntityGuardianElder");
+ registerEntityItemListEquipment("EntityHorse");
+ registerEntityItemListEquipment("EntityHorseDonkey");
+ registerEntityItemListEquipment("EntityHorseMule");
+ registerEntityItemListEquipment("EntityHorseSkeleton");
+ registerEntityItemListEquipment("EntityHorseZombie");
+ registerEntityItemListEquipment("EntityIronGolem");
+ registerEntityItemListEquipment("EntityMagmaCube");
+ registerEntityItemListEquipment("EntityMushroomCow");
+ registerEntityItemListEquipment("EntityOcelot");
+ registerEntityItemListEquipment("EntityPig");
+ registerEntityItemListEquipment("EntityPigZombie");
+ registerEntityItemListEquipment("EntityRabbit");
+ registerEntityItemListEquipment("EntitySheep");
+ registerEntityItemListEquipment("EntityShulker");
+ registerEntityItemListEquipment("EntitySilverfish");
+ registerEntityItemListEquipment("EntitySkeleton");
+ registerEntityItemListEquipment("EntitySkeletonStray");
+ registerEntityItemListEquipment("EntitySkeletonWither");
+ registerEntityItemListEquipment("EntitySlime");
+ registerEntityItemListEquipment("EntitySnowman");
+ registerEntityItemListEquipment("EntitySpider");
+ registerEntityItemListEquipment("EntitySquid");
+ registerEntityItemListEquipment("EntityVex");
+ registerEntityItemListEquipment("EntityVillager");
+ registerEntityItemListEquipment("EntityVindicator");
+ registerEntityItemListEquipment("EntityWitch");
+ registerEntityItemListEquipment("EntityWither");
+ registerEntityItemListEquipment("EntityWolf");
+ registerEntityItemListEquipment("EntityZombie");
+ registerEntityItemListEquipment("EntityZombieHusk");
+ registerEntityItemListEquipment("EntityZombieVillager");
+ registerEntityItemSingle("EntityFireworks", "FireworksItem");
+ registerEntityItemSingle("EntityHorse", "ArmorItem");
+ registerEntityItemSingle("EntityHorse", "SaddleItem");
+ registerEntityItemSingle("EntityHorseMule", "SaddleItem");
+ registerEntityItemSingle("EntityHorseSkeleton", "SaddleItem");
+ registerEntityItemSingle("EntityHorseZombie", "SaddleItem");
+ registerEntityItemSingle("EntityItem", "Item");
+ registerEntityItemSingle("EntityItemFrame", "Item");
+ registerEntityItemSingle("EntityPotion", "Potion");
+
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItem("TileEntityRecordPlayer", "RecordItem"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityBrewingStand", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityChest", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDispenser", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDropper", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityFurnace", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityHopper", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityShulkerBox", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorMobSpawnerMobs());
+ registerInspector(LegacyType.CHUNK, new DataInspectorChunks());
+ registerInspector(LegacyType.ENTITY, new DataInspectorCommandBlock());
+ registerInspector(LegacyType.ENTITY, new DataInspectorEntityPassengers());
+ registerInspector(LegacyType.ENTITY, new DataInspectorMobSpawnerMinecart());
+ registerInspector(LegacyType.ENTITY, new DataInspectorVillagers());
+ registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorBlockEntity());
+ registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorEntity());
+ registerInspector(LegacyType.LEVEL, new DataInspectorLevelPlayer());
+ registerInspector(LegacyType.PLAYER, new DataInspectorPlayer());
+ registerInspector(LegacyType.PLAYER, new DataInspectorPlayerVehicle());
+ registerInspector(LegacyType.STRUCTURE, new DataInspectorStructure());
+ }
+
+ private void registerConverters() {
+ registerConverter(LegacyType.ENTITY, new DataConverterEquipment());
+ registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterSignText());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterMaterialId());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionId());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterSpawnEgg());
+ registerConverter(LegacyType.ENTITY, new DataConverterMinecart());
+ registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterMobSpawner());
+ registerConverter(LegacyType.ENTITY, new DataConverterUUID());
+ registerConverter(LegacyType.ENTITY, new DataConverterHealth());
+ registerConverter(LegacyType.ENTITY, new DataConverterSaddle());
+ registerConverter(LegacyType.ENTITY, new DataConverterHanging());
+ registerConverter(LegacyType.ENTITY, new DataConverterDropChances());
+ registerConverter(LegacyType.ENTITY, new DataConverterRiding());
+ registerConverter(LegacyType.ENTITY, new DataConverterArmorStand());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBook());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterCookedFish());
+ registerConverter(LegacyType.ENTITY, new DataConverterZombie());
+ registerConverter(LegacyType.OPTIONS, new DataConverterVBO());
+ registerConverter(LegacyType.ENTITY, new DataConverterGuardian());
+ registerConverter(LegacyType.ENTITY, new DataConverterSkeleton());
+ registerConverter(LegacyType.ENTITY, new DataConverterZombieType());
+ registerConverter(LegacyType.ENTITY, new DataConverterHorse());
+ registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterTileEntity());
+ registerConverter(LegacyType.ENTITY, new DataConverterEntity());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBanner());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionWater());
+ registerConverter(LegacyType.ENTITY, new DataConverterShulker());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterShulkerBoxItem());
+ registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterShulkerBoxBlock());
+ registerConverter(LegacyType.OPTIONS, new DataConverterLang());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterTotem());
+ registerConverter(LegacyType.CHUNK, new DataConverterBedBlock());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBedItem());
+ }
+
+ private void registerEntityItemList(String type, String... keys) {
+ registerInspector(LegacyType.ENTITY, new DataInspectorItemList(type, keys));
+ }
+
+ private void registerEntityItemSingle(String type, String key) {
+ registerInspector(LegacyType.ENTITY, new DataInspectorItem(type, key));
+ }
+
+ private void registerEntityItemListEquipment(String type) {
+ registerEntityItemList(type, "ArmorItems", "HandItems");
+ }
+
+ private static final Map OLD_ID_TO_KEY_MAP = new HashMap<>();
+
+ static {
+ final Map map = OLD_ID_TO_KEY_MAP;
+ map.put("EntityItem", new ResourceLocation("item"));
+ map.put("EntityExperienceOrb", new ResourceLocation("xp_orb"));
+ map.put("EntityAreaEffectCloud", new ResourceLocation("area_effect_cloud"));
+ map.put("EntityGuardianElder", new ResourceLocation("elder_guardian"));
+ map.put("EntitySkeletonWither", new ResourceLocation("wither_skeleton"));
+ map.put("EntitySkeletonStray", new ResourceLocation("stray"));
+ map.put("EntityEgg", new ResourceLocation("egg"));
+ map.put("EntityLeash", new ResourceLocation("leash_knot"));
+ map.put("EntityPainting", new ResourceLocation("painting"));
+ map.put("EntityTippedArrow", new ResourceLocation("arrow"));
+ map.put("EntitySnowball", new ResourceLocation("snowball"));
+ map.put("EntityLargeFireball", new ResourceLocation("fireball"));
+ map.put("EntitySmallFireball", new ResourceLocation("small_fireball"));
+ map.put("EntityEnderPearl", new ResourceLocation("ender_pearl"));
+ map.put("EntityEnderSignal", new ResourceLocation("eye_of_ender_signal"));
+ map.put("EntityPotion", new ResourceLocation("potion"));
+ map.put("EntityThrownExpBottle", new ResourceLocation("xp_bottle"));
+ map.put("EntityItemFrame", new ResourceLocation("item_frame"));
+ map.put("EntityWitherSkull", new ResourceLocation("wither_skull"));
+ map.put("EntityTNTPrimed", new ResourceLocation("tnt"));
+ map.put("EntityFallingBlock", new ResourceLocation("falling_block"));
+ map.put("EntityFireworks", new ResourceLocation("fireworks_rocket"));
+ map.put("EntityZombieHusk", new ResourceLocation("husk"));
+ map.put("EntitySpectralArrow", new ResourceLocation("spectral_arrow"));
+ map.put("EntityShulkerBullet", new ResourceLocation("shulker_bullet"));
+ map.put("EntityDragonFireball", new ResourceLocation("dragon_fireball"));
+ map.put("EntityZombieVillager", new ResourceLocation("zombie_villager"));
+ map.put("EntityHorseSkeleton", new ResourceLocation("skeleton_horse"));
+ map.put("EntityHorseZombie", new ResourceLocation("zombie_horse"));
+ map.put("EntityArmorStand", new ResourceLocation("armor_stand"));
+ map.put("EntityHorseDonkey", new ResourceLocation("donkey"));
+ map.put("EntityHorseMule", new ResourceLocation("mule"));
+ map.put("EntityEvokerFangs", new ResourceLocation("evocation_fangs"));
+ map.put("EntityEvoker", new ResourceLocation("evocation_illager"));
+ map.put("EntityVex", new ResourceLocation("vex"));
+ map.put("EntityVindicator", new ResourceLocation("vindication_illager"));
+ map.put("EntityIllagerIllusioner", new ResourceLocation("illusion_illager"));
+ map.put("EntityMinecartCommandBlock", new ResourceLocation("commandblock_minecart"));
+ map.put("EntityBoat", new ResourceLocation("boat"));
+ map.put("EntityMinecartRideable", new ResourceLocation("minecart"));
+ map.put("EntityMinecartChest", new ResourceLocation("chest_minecart"));
+ map.put("EntityMinecartFurnace", new ResourceLocation("furnace_minecart"));
+ map.put("EntityMinecartTNT", new ResourceLocation("tnt_minecart"));
+ map.put("EntityMinecartHopper", new ResourceLocation("hopper_minecart"));
+ map.put("EntityMinecartMobSpawner", new ResourceLocation("spawner_minecart"));
+ map.put("EntityCreeper", new ResourceLocation("creeper"));
+ map.put("EntitySkeleton", new ResourceLocation("skeleton"));
+ map.put("EntitySpider", new ResourceLocation("spider"));
+ map.put("EntityGiantZombie", new ResourceLocation("giant"));
+ map.put("EntityZombie", new ResourceLocation("zombie"));
+ map.put("EntitySlime", new ResourceLocation("slime"));
+ map.put("EntityGhast", new ResourceLocation("ghast"));
+ map.put("EntityPigZombie", new ResourceLocation("zombie_pigman"));
+ map.put("EntityEnderman", new ResourceLocation("enderman"));
+ map.put("EntityCaveSpider", new ResourceLocation("cave_spider"));
+ map.put("EntitySilverfish", new ResourceLocation("silverfish"));
+ map.put("EntityBlaze", new ResourceLocation("blaze"));
+ map.put("EntityMagmaCube", new ResourceLocation("magma_cube"));
+ map.put("EntityEnderDragon", new ResourceLocation("ender_dragon"));
+ map.put("EntityWither", new ResourceLocation("wither"));
+ map.put("EntityBat", new ResourceLocation("bat"));
+ map.put("EntityWitch", new ResourceLocation("witch"));
+ map.put("EntityEndermite", new ResourceLocation("endermite"));
+ map.put("EntityGuardian", new ResourceLocation("guardian"));
+ map.put("EntityShulker", new ResourceLocation("shulker"));
+ map.put("EntityPig", new ResourceLocation("pig"));
+ map.put("EntitySheep", new ResourceLocation("sheep"));
+ map.put("EntityCow", new ResourceLocation("cow"));
+ map.put("EntityChicken", new ResourceLocation("chicken"));
+ map.put("EntitySquid", new ResourceLocation("squid"));
+ map.put("EntityWolf", new ResourceLocation("wolf"));
+ map.put("EntityMushroomCow", new ResourceLocation("mooshroom"));
+ map.put("EntitySnowman", new ResourceLocation("snowman"));
+ map.put("EntityOcelot", new ResourceLocation("ocelot"));
+ map.put("EntityIronGolem", new ResourceLocation("villager_golem"));
+ map.put("EntityHorse", new ResourceLocation("horse"));
+ map.put("EntityRabbit", new ResourceLocation("rabbit"));
+ map.put("EntityPolarBear", new ResourceLocation("polar_bear"));
+ map.put("EntityLlama", new ResourceLocation("llama"));
+ map.put("EntityLlamaSpit", new ResourceLocation("llama_spit"));
+ map.put("EntityParrot", new ResourceLocation("parrot"));
+ map.put("EntityVillager", new ResourceLocation("villager"));
+ map.put("EntityEnderCrystal", new ResourceLocation("ender_crystal"));
+ map.put("TileEntityFurnace", new ResourceLocation("furnace"));
+ map.put("TileEntityChest", new ResourceLocation("chest"));
+ map.put("TileEntityEnderChest", new ResourceLocation("ender_chest"));
+ map.put("TileEntityRecordPlayer", new ResourceLocation("jukebox"));
+ map.put("TileEntityDispenser", new ResourceLocation("dispenser"));
+ map.put("TileEntityDropper", new ResourceLocation("dropper"));
+ map.put("TileEntitySign", new ResourceLocation("sign"));
+ map.put("TileEntityMobSpawner", new ResourceLocation("mob_spawner"));
+ map.put("TileEntityNote", new ResourceLocation("noteblock"));
+ map.put("TileEntityPiston", new ResourceLocation("piston"));
+ map.put("TileEntityBrewingStand", new ResourceLocation("brewing_stand"));
+ map.put("TileEntityEnchantTable", new ResourceLocation("enchanting_table"));
+ map.put("TileEntityEnderPortal", new ResourceLocation("end_portal"));
+ map.put("TileEntityBeacon", new ResourceLocation("beacon"));
+ map.put("TileEntitySkull", new ResourceLocation("skull"));
+ map.put("TileEntityLightDetector", new ResourceLocation("daylight_detector"));
+ map.put("TileEntityHopper", new ResourceLocation("hopper"));
+ map.put("TileEntityComparator", new ResourceLocation("comparator"));
+ map.put("TileEntityFlowerPot", new ResourceLocation("flower_pot"));
+ map.put("TileEntityBanner", new ResourceLocation("banner"));
+ map.put("TileEntityStructure", new ResourceLocation("structure_block"));
+ map.put("TileEntityEndGateway", new ResourceLocation("end_gateway"));
+ map.put("TileEntityCommand", new ResourceLocation("command_block"));
+ map.put("TileEntityShulkerBox", new ResourceLocation("shulker_box"));
+ map.put("TileEntityBed", new ResourceLocation("bed"));
+ }
+
+ private static ResourceLocation getKey(String type) {
+ final ResourceLocation key = OLD_ID_TO_KEY_MAP.get(type);
+ if (key == null) {
+ throw new IllegalArgumentException("Unknown mapping for " + type);
+ }
+ return key;
+ }
+
+ private static void convertCompound(LegacyType type, net.minecraft.nbt.CompoundTag cmp, String key, int sourceVer, int targetVer) {
+ cmp.put(key, convert(type, cmp.getCompound(key), sourceVer, targetVer));
+ }
+
+ private static void convertItem(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) {
+ if (nbttagcompound.contains(key, 10)) {
+ convertCompound(LegacyType.ITEM_INSTANCE, nbttagcompound, key, sourceVer, targetVer);
+ }
+ }
+
+ private static void convertItems(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) {
+ if (nbttagcompound.contains(key, 9)) {
+ net.minecraft.nbt.ListTag nbttaglist = nbttagcompound.getList(key, 10);
+
+ for (int j = 0; j < nbttaglist.size(); ++j) {
+ nbttaglist.set(j, convert(LegacyType.ITEM_INSTANCE, nbttaglist.getCompound(j), sourceVer, targetVer));
+ }
+ }
+
+ }
+
+ private static class DataConverterEquipment implements DataConverter {
+
+ DataConverterEquipment() {
+ }
+
+ public int getDataVersion() {
+ return 100;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ net.minecraft.nbt.ListTag nbttaglist = cmp.getList("Equipment", 10);
+ net.minecraft.nbt.ListTag nbttaglist1;
+
+ if (!nbttaglist.isEmpty() && !cmp.contains("HandItems", 10)) {
+ nbttaglist1 = new net.minecraft.nbt.ListTag();
+ nbttaglist1.add(nbttaglist.get(0));
+ nbttaglist1.add(new net.minecraft.nbt.CompoundTag());
+ cmp.put("HandItems", nbttaglist1);
+ }
+
+ if (nbttaglist.size() > 1 && !cmp.contains("ArmorItem", 10)) {
+ nbttaglist1 = new net.minecraft.nbt.ListTag();
+ nbttaglist1.add(nbttaglist.get(1));
+ nbttaglist1.add(nbttaglist.get(2));
+ nbttaglist1.add(nbttaglist.get(3));
+ nbttaglist1.add(nbttaglist.get(4));
+ cmp.put("ArmorItems", nbttaglist1);
+ }
+
+ cmp.remove("Equipment");
+ if (cmp.contains("DropChances", 9)) {
+ nbttaglist1 = cmp.getList("DropChances", 5);
+ net.minecraft.nbt.ListTag nbttaglist2;
+
+ if (!cmp.contains("HandDropChances", 10)) {
+ nbttaglist2 = new net.minecraft.nbt.ListTag();
+ nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(0)));
+ nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(0.0F));
+ cmp.put("HandDropChances", nbttaglist2);
+ }
+
+ if (!cmp.contains("ArmorDropChances", 10)) {
+ nbttaglist2 = new net.minecraft.nbt.ListTag();
+ nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(1)));
+ nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(2)));
+ nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(3)));
+ nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(4)));
+ cmp.put("ArmorDropChances", nbttaglist2);
+ }
+
+ cmp.remove("DropChances");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorBlockEntity implements DataInspector {
+
+ private static final Map b = Maps.newHashMap();
+ private static final Map c = Maps.newHashMap();
+
+ DataInspectorBlockEntity() {
+ }
+
+ @Nullable
+ private static String convertEntityId(int i, String s) {
+ String key = new ResourceLocation(s).toString();
+ if (i < 515 && DataInspectorBlockEntity.b.containsKey(key)) {
+ return DataInspectorBlockEntity.b.get(key);
+ } else {
+ return DataInspectorBlockEntity.c.get(key);
+ }
+ }
+
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (!cmp.contains("tag", 10)) {
+ return cmp;
+ } else {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.contains("BlockEntityTag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag");
+ String s = cmp.getString("id");
+ String s1 = convertEntityId(sourceVer, s);
+ boolean flag;
+
+ if (s1 == null) {
+ // CraftBukkit - Remove unnecessary warning (occurs when deserializing a Shulker Box item)
+ // DataInspectorBlockEntity.a.warn("Unable to resolve BlockEntity for ItemInstance: {}", s);
+ flag = false;
+ } else {
+ flag = !nbttagcompound2.contains("id");
+ nbttagcompound2.putString("id", s1);
+ }
+
+ convert(LegacyType.BLOCK_ENTITY, nbttagcompound2, sourceVer, targetVer);
+ if (flag) {
+ nbttagcompound2.remove("id");
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ static {
+ Map map = DataInspectorBlockEntity.b;
+
+ map.put("minecraft:furnace", "Furnace");
+ map.put("minecraft:lit_furnace", "Furnace");
+ map.put("minecraft:chest", "Chest");
+ map.put("minecraft:trapped_chest", "Chest");
+ map.put("minecraft:ender_chest", "EnderChest");
+ map.put("minecraft:jukebox", "RecordPlayer");
+ map.put("minecraft:dispenser", "Trap");
+ map.put("minecraft:dropper", "Dropper");
+ map.put("minecraft:sign", "Sign");
+ map.put("minecraft:mob_spawner", "MobSpawner");
+ map.put("minecraft:noteblock", "Music");
+ map.put("minecraft:brewing_stand", "Cauldron");
+ map.put("minecraft:enhanting_table", "EnchantTable");
+ map.put("minecraft:command_block", "CommandBlock");
+ map.put("minecraft:beacon", "Beacon");
+ map.put("minecraft:skull", "Skull");
+ map.put("minecraft:daylight_detector", "DLDetector");
+ map.put("minecraft:hopper", "Hopper");
+ map.put("minecraft:banner", "Banner");
+ map.put("minecraft:flower_pot", "FlowerPot");
+ map.put("minecraft:repeating_command_block", "CommandBlock");
+ map.put("minecraft:chain_command_block", "CommandBlock");
+ map.put("minecraft:standing_sign", "Sign");
+ map.put("minecraft:wall_sign", "Sign");
+ map.put("minecraft:piston_head", "Piston");
+ map.put("minecraft:daylight_detector_inverted", "DLDetector");
+ map.put("minecraft:unpowered_comparator", "Comparator");
+ map.put("minecraft:powered_comparator", "Comparator");
+ map.put("minecraft:wall_banner", "Banner");
+ map.put("minecraft:standing_banner", "Banner");
+ map.put("minecraft:structure_block", "Structure");
+ map.put("minecraft:end_portal", "Airportal");
+ map.put("minecraft:end_gateway", "EndGateway");
+ map.put("minecraft:shield", "Shield");
+ map = DataInspectorBlockEntity.c;
+ map.put("minecraft:furnace", "minecraft:furnace");
+ map.put("minecraft:lit_furnace", "minecraft:furnace");
+ map.put("minecraft:chest", "minecraft:chest");
+ map.put("minecraft:trapped_chest", "minecraft:chest");
+ map.put("minecraft:ender_chest", "minecraft:enderchest");
+ map.put("minecraft:jukebox", "minecraft:jukebox");
+ map.put("minecraft:dispenser", "minecraft:dispenser");
+ map.put("minecraft:dropper", "minecraft:dropper");
+ map.put("minecraft:sign", "minecraft:sign");
+ map.put("minecraft:mob_spawner", "minecraft:mob_spawner");
+ map.put("minecraft:noteblock", "minecraft:noteblock");
+ map.put("minecraft:brewing_stand", "minecraft:brewing_stand");
+ map.put("minecraft:enhanting_table", "minecraft:enchanting_table");
+ map.put("minecraft:command_block", "minecraft:command_block");
+ map.put("minecraft:beacon", "minecraft:beacon");
+ map.put("minecraft:skull", "minecraft:skull");
+ map.put("minecraft:daylight_detector", "minecraft:daylight_detector");
+ map.put("minecraft:hopper", "minecraft:hopper");
+ map.put("minecraft:banner", "minecraft:banner");
+ map.put("minecraft:flower_pot", "minecraft:flower_pot");
+ map.put("minecraft:repeating_command_block", "minecraft:command_block");
+ map.put("minecraft:chain_command_block", "minecraft:command_block");
+ map.put("minecraft:shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:white_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:orange_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:magenta_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:light_blue_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:yellow_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:lime_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:pink_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:gray_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:silver_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:cyan_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:purple_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:blue_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:brown_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:green_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:red_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:black_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:bed", "minecraft:bed");
+ map.put("minecraft:standing_sign", "minecraft:sign");
+ map.put("minecraft:wall_sign", "minecraft:sign");
+ map.put("minecraft:piston_head", "minecraft:piston");
+ map.put("minecraft:daylight_detector_inverted", "minecraft:daylight_detector");
+ map.put("minecraft:unpowered_comparator", "minecraft:comparator");
+ map.put("minecraft:powered_comparator", "minecraft:comparator");
+ map.put("minecraft:wall_banner", "minecraft:banner");
+ map.put("minecraft:standing_banner", "minecraft:banner");
+ map.put("minecraft:structure_block", "minecraft:structure_block");
+ map.put("minecraft:end_portal", "minecraft:end_portal");
+ map.put("minecraft:end_gateway", "minecraft:end_gateway");
+ map.put("minecraft:shield", "minecraft:shield");
+ }
+ }
+
+ private static class DataInspectorEntity implements DataInspector {
+
+ DataInspectorEntity() {
+ }
+
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.contains("EntityTag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag");
+ String s = cmp.getString("id");
+ String s1;
+
+ if ("minecraft:armor_stand".equals(s)) {
+ s1 = sourceVer < 515 ? "ArmorStand" : "minecraft:armor_stand";
+ } else {
+ if (!"minecraft:spawn_egg".equals(s)) {
+ return cmp;
+ }
+
+ s1 = nbttagcompound2.getString("id");
+ }
+
+ boolean flag;
+
+ flag = !nbttagcompound2.contains("id", 8);
+ nbttagcompound2.putString("id", s1);
+
+ convert(LegacyType.ENTITY, nbttagcompound2, sourceVer, targetVer);
+ if (flag) {
+ nbttagcompound2.remove("id");
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+
+ private abstract static class DataInspectorTagged implements DataInspector {
+
+ private final ResourceLocation key;
+
+ DataInspectorTagged(String type) {
+ this.key = getKey(type);
+ }
+
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (this.key.equals(new ResourceLocation(cmp.getString("id")))) {
+ cmp = this.inspectChecked(cmp, sourceVer, targetVer);
+ }
+
+ return cmp;
+ }
+
+ abstract net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer);
+ }
+
+ private static class DataInspectorItemList extends DataInspectorTagged {
+
+ private final String[] keys;
+
+ DataInspectorItemList(String oclass, String... astring) {
+ super(oclass);
+ this.keys = astring;
+ }
+
+ net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) {
+ for (String s : this.keys) {
+ PaperweightDataConverters.convertItems(nbttagcompound, s, sourceVer, targetVer);
+ }
+
+ return nbttagcompound;
+ }
+ }
+
+ private static class DataInspectorItem extends DataInspectorTagged {
+
+ private final String[] keys;
+
+ DataInspectorItem(String oclass, String... astring) {
+ super(oclass);
+ this.keys = astring;
+ }
+
+ net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) {
+ for (String key : this.keys) {
+ PaperweightDataConverters.convertItem(nbttagcompound, key, sourceVer, targetVer);
+ }
+
+ return nbttagcompound;
+ }
+ }
+
+ private static class DataConverterMaterialId implements DataConverter {
+
+ private static final String[] materials = new String[2268];
+
+ DataConverterMaterialId() {
+ }
+
+ public int getDataVersion() {
+ return 102;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (cmp.contains("id", 99)) {
+ short short0 = cmp.getShort("id");
+
+ if (short0 > 0 && short0 < materials.length && materials[short0] != null) {
+ cmp.putString("id", materials[short0]);
+ }
+ }
+
+ return cmp;
+ }
+
+ static {
+ materials[1] = "minecraft:stone";
+ materials[2] = "minecraft:grass";
+ materials[3] = "minecraft:dirt";
+ materials[4] = "minecraft:cobblestone";
+ materials[5] = "minecraft:planks";
+ materials[6] = "minecraft:sapling";
+ materials[7] = "minecraft:bedrock";
+ materials[8] = "minecraft:flowing_water";
+ materials[9] = "minecraft:water";
+ materials[10] = "minecraft:flowing_lava";
+ materials[11] = "minecraft:lava";
+ materials[12] = "minecraft:sand";
+ materials[13] = "minecraft:gravel";
+ materials[14] = "minecraft:gold_ore";
+ materials[15] = "minecraft:iron_ore";
+ materials[16] = "minecraft:coal_ore";
+ materials[17] = "minecraft:log";
+ materials[18] = "minecraft:leaves";
+ materials[19] = "minecraft:sponge";
+ materials[20] = "minecraft:glass";
+ materials[21] = "minecraft:lapis_ore";
+ materials[22] = "minecraft:lapis_block";
+ materials[23] = "minecraft:dispenser";
+ materials[24] = "minecraft:sandstone";
+ materials[25] = "minecraft:noteblock";
+ materials[27] = "minecraft:golden_rail";
+ materials[28] = "minecraft:detector_rail";
+ materials[29] = "minecraft:sticky_piston";
+ materials[30] = "minecraft:web";
+ materials[31] = "minecraft:tallgrass";
+ materials[32] = "minecraft:deadbush";
+ materials[33] = "minecraft:piston";
+ materials[35] = "minecraft:wool";
+ materials[37] = "minecraft:yellow_flower";
+ materials[38] = "minecraft:red_flower";
+ materials[39] = "minecraft:brown_mushroom";
+ materials[40] = "minecraft:red_mushroom";
+ materials[41] = "minecraft:gold_block";
+ materials[42] = "minecraft:iron_block";
+ materials[43] = "minecraft:double_stone_slab";
+ materials[44] = "minecraft:stone_slab";
+ materials[45] = "minecraft:brick_block";
+ materials[46] = "minecraft:tnt";
+ materials[47] = "minecraft:bookshelf";
+ materials[48] = "minecraft:mossy_cobblestone";
+ materials[49] = "minecraft:obsidian";
+ materials[50] = "minecraft:torch";
+ materials[51] = "minecraft:fire";
+ materials[52] = "minecraft:mob_spawner";
+ materials[53] = "minecraft:oak_stairs";
+ materials[54] = "minecraft:chest";
+ materials[56] = "minecraft:diamond_ore";
+ materials[57] = "minecraft:diamond_block";
+ materials[58] = "minecraft:crafting_table";
+ materials[60] = "minecraft:farmland";
+ materials[61] = "minecraft:furnace";
+ materials[62] = "minecraft:lit_furnace";
+ materials[65] = "minecraft:ladder";
+ materials[66] = "minecraft:rail";
+ materials[67] = "minecraft:stone_stairs";
+ materials[69] = "minecraft:lever";
+ materials[70] = "minecraft:stone_pressure_plate";
+ materials[72] = "minecraft:wooden_pressure_plate";
+ materials[73] = "minecraft:redstone_ore";
+ materials[76] = "minecraft:redstone_torch";
+ materials[77] = "minecraft:stone_button";
+ materials[78] = "minecraft:snow_layer";
+ materials[79] = "minecraft:ice";
+ materials[80] = "minecraft:snow";
+ materials[81] = "minecraft:cactus";
+ materials[82] = "minecraft:clay";
+ materials[84] = "minecraft:jukebox";
+ materials[85] = "minecraft:fence";
+ materials[86] = "minecraft:pumpkin";
+ materials[87] = "minecraft:netherrack";
+ materials[88] = "minecraft:soul_sand";
+ materials[89] = "minecraft:glowstone";
+ materials[90] = "minecraft:portal";
+ materials[91] = "minecraft:lit_pumpkin";
+ materials[95] = "minecraft:stained_glass";
+ materials[96] = "minecraft:trapdoor";
+ materials[97] = "minecraft:monster_egg";
+ materials[98] = "minecraft:stonebrick";
+ materials[99] = "minecraft:brown_mushroom_block";
+ materials[100] = "minecraft:red_mushroom_block";
+ materials[101] = "minecraft:iron_bars";
+ materials[102] = "minecraft:glass_pane";
+ materials[103] = "minecraft:melon_block";
+ materials[106] = "minecraft:vine";
+ materials[107] = "minecraft:fence_gate";
+ materials[108] = "minecraft:brick_stairs";
+ materials[109] = "minecraft:stone_brick_stairs";
+ materials[110] = "minecraft:mycelium";
+ materials[111] = "minecraft:waterlily";
+ materials[112] = "minecraft:nether_brick";
+ materials[113] = "minecraft:nether_brick_fence";
+ materials[114] = "minecraft:nether_brick_stairs";
+ materials[116] = "minecraft:enchanting_table";
+ materials[119] = "minecraft:end_portal";
+ materials[120] = "minecraft:end_portal_frame";
+ materials[121] = "minecraft:end_stone";
+ materials[122] = "minecraft:dragon_egg";
+ materials[123] = "minecraft:redstone_lamp";
+ materials[125] = "minecraft:double_wooden_slab";
+ materials[126] = "minecraft:wooden_slab";
+ materials[127] = "minecraft:cocoa";
+ materials[128] = "minecraft:sandstone_stairs";
+ materials[129] = "minecraft:emerald_ore";
+ materials[130] = "minecraft:ender_chest";
+ materials[131] = "minecraft:tripwire_hook";
+ materials[133] = "minecraft:emerald_block";
+ materials[134] = "minecraft:spruce_stairs";
+ materials[135] = "minecraft:birch_stairs";
+ materials[136] = "minecraft:jungle_stairs";
+ materials[137] = "minecraft:command_block";
+ materials[138] = "minecraft:beacon";
+ materials[139] = "minecraft:cobblestone_wall";
+ materials[141] = "minecraft:carrots";
+ materials[142] = "minecraft:potatoes";
+ materials[143] = "minecraft:wooden_button";
+ materials[145] = "minecraft:anvil";
+ materials[146] = "minecraft:trapped_chest";
+ materials[147] = "minecraft:light_weighted_pressure_plate";
+ materials[148] = "minecraft:heavy_weighted_pressure_plate";
+ materials[151] = "minecraft:daylight_detector";
+ materials[152] = "minecraft:redstone_block";
+ materials[153] = "minecraft:quartz_ore";
+ materials[154] = "minecraft:hopper";
+ materials[155] = "minecraft:quartz_block";
+ materials[156] = "minecraft:quartz_stairs";
+ materials[157] = "minecraft:activator_rail";
+ materials[158] = "minecraft:dropper";
+ materials[159] = "minecraft:stained_hardened_clay";
+ materials[160] = "minecraft:stained_glass_pane";
+ materials[161] = "minecraft:leaves2";
+ materials[162] = "minecraft:log2";
+ materials[163] = "minecraft:acacia_stairs";
+ materials[164] = "minecraft:dark_oak_stairs";
+ materials[170] = "minecraft:hay_block";
+ materials[171] = "minecraft:carpet";
+ materials[172] = "minecraft:hardened_clay";
+ materials[173] = "minecraft:coal_block";
+ materials[174] = "minecraft:packed_ice";
+ materials[175] = "minecraft:double_plant";
+ materials[256] = "minecraft:iron_shovel";
+ materials[257] = "minecraft:iron_pickaxe";
+ materials[258] = "minecraft:iron_axe";
+ materials[259] = "minecraft:flint_and_steel";
+ materials[260] = "minecraft:apple";
+ materials[261] = "minecraft:bow";
+ materials[262] = "minecraft:arrow";
+ materials[263] = "minecraft:coal";
+ materials[264] = "minecraft:diamond";
+ materials[265] = "minecraft:iron_ingot";
+ materials[266] = "minecraft:gold_ingot";
+ materials[267] = "minecraft:iron_sword";
+ materials[268] = "minecraft:wooden_sword";
+ materials[269] = "minecraft:wooden_shovel";
+ materials[270] = "minecraft:wooden_pickaxe";
+ materials[271] = "minecraft:wooden_axe";
+ materials[272] = "minecraft:stone_sword";
+ materials[273] = "minecraft:stone_shovel";
+ materials[274] = "minecraft:stone_pickaxe";
+ materials[275] = "minecraft:stone_axe";
+ materials[276] = "minecraft:diamond_sword";
+ materials[277] = "minecraft:diamond_shovel";
+ materials[278] = "minecraft:diamond_pickaxe";
+ materials[279] = "minecraft:diamond_axe";
+ materials[280] = "minecraft:stick";
+ materials[281] = "minecraft:bowl";
+ materials[282] = "minecraft:mushroom_stew";
+ materials[283] = "minecraft:golden_sword";
+ materials[284] = "minecraft:golden_shovel";
+ materials[285] = "minecraft:golden_pickaxe";
+ materials[286] = "minecraft:golden_axe";
+ materials[287] = "minecraft:string";
+ materials[288] = "minecraft:feather";
+ materials[289] = "minecraft:gunpowder";
+ materials[290] = "minecraft:wooden_hoe";
+ materials[291] = "minecraft:stone_hoe";
+ materials[292] = "minecraft:iron_hoe";
+ materials[293] = "minecraft:diamond_hoe";
+ materials[294] = "minecraft:golden_hoe";
+ materials[295] = "minecraft:wheat_seeds";
+ materials[296] = "minecraft:wheat";
+ materials[297] = "minecraft:bread";
+ materials[298] = "minecraft:leather_helmet";
+ materials[299] = "minecraft:leather_chestplate";
+ materials[300] = "minecraft:leather_leggings";
+ materials[301] = "minecraft:leather_boots";
+ materials[302] = "minecraft:chainmail_helmet";
+ materials[303] = "minecraft:chainmail_chestplate";
+ materials[304] = "minecraft:chainmail_leggings";
+ materials[305] = "minecraft:chainmail_boots";
+ materials[306] = "minecraft:iron_helmet";
+ materials[307] = "minecraft:iron_chestplate";
+ materials[308] = "minecraft:iron_leggings";
+ materials[309] = "minecraft:iron_boots";
+ materials[310] = "minecraft:diamond_helmet";
+ materials[311] = "minecraft:diamond_chestplate";
+ materials[312] = "minecraft:diamond_leggings";
+ materials[313] = "minecraft:diamond_boots";
+ materials[314] = "minecraft:golden_helmet";
+ materials[315] = "minecraft:golden_chestplate";
+ materials[316] = "minecraft:golden_leggings";
+ materials[317] = "minecraft:golden_boots";
+ materials[318] = "minecraft:flint";
+ materials[319] = "minecraft:porkchop";
+ materials[320] = "minecraft:cooked_porkchop";
+ materials[321] = "minecraft:painting";
+ materials[322] = "minecraft:golden_apple";
+ materials[323] = "minecraft:sign";
+ materials[324] = "minecraft:wooden_door";
+ materials[325] = "minecraft:bucket";
+ materials[326] = "minecraft:water_bucket";
+ materials[327] = "minecraft:lava_bucket";
+ materials[328] = "minecraft:minecart";
+ materials[329] = "minecraft:saddle";
+ materials[330] = "minecraft:iron_door";
+ materials[331] = "minecraft:redstone";
+ materials[332] = "minecraft:snowball";
+ materials[333] = "minecraft:boat";
+ materials[334] = "minecraft:leather";
+ materials[335] = "minecraft:milk_bucket";
+ materials[336] = "minecraft:brick";
+ materials[337] = "minecraft:clay_ball";
+ materials[338] = "minecraft:reeds";
+ materials[339] = "minecraft:paper";
+ materials[340] = "minecraft:book";
+ materials[341] = "minecraft:slime_ball";
+ materials[342] = "minecraft:chest_minecart";
+ materials[343] = "minecraft:furnace_minecart";
+ materials[344] = "minecraft:egg";
+ materials[345] = "minecraft:compass";
+ materials[346] = "minecraft:fishing_rod";
+ materials[347] = "minecraft:clock";
+ materials[348] = "minecraft:glowstone_dust";
+ materials[349] = "minecraft:fish";
+ materials[350] = "minecraft:cooked_fish"; // Paper - cooked_fished -> cooked_fish
+ materials[351] = "minecraft:dye";
+ materials[352] = "minecraft:bone";
+ materials[353] = "minecraft:sugar";
+ materials[354] = "minecraft:cake";
+ materials[355] = "minecraft:bed";
+ materials[356] = "minecraft:repeater";
+ materials[357] = "minecraft:cookie";
+ materials[358] = "minecraft:filled_map";
+ materials[359] = "minecraft:shears";
+ materials[360] = "minecraft:melon";
+ materials[361] = "minecraft:pumpkin_seeds";
+ materials[362] = "minecraft:melon_seeds";
+ materials[363] = "minecraft:beef";
+ materials[364] = "minecraft:cooked_beef";
+ materials[365] = "minecraft:chicken";
+ materials[366] = "minecraft:cooked_chicken";
+ materials[367] = "minecraft:rotten_flesh";
+ materials[368] = "minecraft:ender_pearl";
+ materials[369] = "minecraft:blaze_rod";
+ materials[370] = "minecraft:ghast_tear";
+ materials[371] = "minecraft:gold_nugget";
+ materials[372] = "minecraft:nether_wart";
+ materials[373] = "minecraft:potion";
+ materials[374] = "minecraft:glass_bottle";
+ materials[375] = "minecraft:spider_eye";
+ materials[376] = "minecraft:fermented_spider_eye";
+ materials[377] = "minecraft:blaze_powder";
+ materials[378] = "minecraft:magma_cream";
+ materials[379] = "minecraft:brewing_stand";
+ materials[380] = "minecraft:cauldron";
+ materials[381] = "minecraft:ender_eye";
+ materials[382] = "minecraft:speckled_melon";
+ materials[383] = "minecraft:spawn_egg";
+ materials[384] = "minecraft:experience_bottle";
+ materials[385] = "minecraft:fire_charge";
+ materials[386] = "minecraft:writable_book";
+ materials[387] = "minecraft:written_book";
+ materials[388] = "minecraft:emerald";
+ materials[389] = "minecraft:item_frame";
+ materials[390] = "minecraft:flower_pot";
+ materials[391] = "minecraft:carrot";
+ materials[392] = "minecraft:potato";
+ materials[393] = "minecraft:baked_potato";
+ materials[394] = "minecraft:poisonous_potato";
+ materials[395] = "minecraft:map";
+ materials[396] = "minecraft:golden_carrot";
+ materials[397] = "minecraft:skull";
+ materials[398] = "minecraft:carrot_on_a_stick";
+ materials[399] = "minecraft:nether_star";
+ materials[400] = "minecraft:pumpkin_pie";
+ materials[401] = "minecraft:fireworks";
+ materials[402] = "minecraft:firework_charge";
+ materials[403] = "minecraft:enchanted_book";
+ materials[404] = "minecraft:comparator";
+ materials[405] = "minecraft:netherbrick";
+ materials[406] = "minecraft:quartz";
+ materials[407] = "minecraft:tnt_minecart";
+ materials[408] = "minecraft:hopper_minecart";
+ materials[417] = "minecraft:iron_horse_armor";
+ materials[418] = "minecraft:golden_horse_armor";
+ materials[419] = "minecraft:diamond_horse_armor";
+ materials[420] = "minecraft:lead";
+ materials[421] = "minecraft:name_tag";
+ materials[422] = "minecraft:command_block_minecart";
+ materials[2256] = "minecraft:record_13";
+ materials[2257] = "minecraft:record_cat";
+ materials[2258] = "minecraft:record_blocks";
+ materials[2259] = "minecraft:record_chirp";
+ materials[2260] = "minecraft:record_far";
+ materials[2261] = "minecraft:record_mall";
+ materials[2262] = "minecraft:record_mellohi";
+ materials[2263] = "minecraft:record_stal";
+ materials[2264] = "minecraft:record_strad";
+ materials[2265] = "minecraft:record_ward";
+ materials[2266] = "minecraft:record_11";
+ materials[2267] = "minecraft:record_wait";
+ // Paper start
+ materials[409] = "minecraft:prismarine_shard";
+ materials[410] = "minecraft:prismarine_crystals";
+ materials[411] = "minecraft:rabbit";
+ materials[412] = "minecraft:cooked_rabbit";
+ materials[413] = "minecraft:rabbit_stew";
+ materials[414] = "minecraft:rabbit_foot";
+ materials[415] = "minecraft:rabbit_hide";
+ materials[416] = "minecraft:armor_stand";
+ materials[423] = "minecraft:mutton";
+ materials[424] = "minecraft:cooked_mutton";
+ materials[425] = "minecraft:banner";
+ materials[426] = "minecraft:end_crystal";
+ materials[427] = "minecraft:spruce_door";
+ materials[428] = "minecraft:birch_door";
+ materials[429] = "minecraft:jungle_door";
+ materials[430] = "minecraft:acacia_door";
+ materials[431] = "minecraft:dark_oak_door";
+ materials[432] = "minecraft:chorus_fruit";
+ materials[433] = "minecraft:chorus_fruit_popped";
+ materials[434] = "minecraft:beetroot";
+ materials[435] = "minecraft:beetroot_seeds";
+ materials[436] = "minecraft:beetroot_soup";
+ materials[437] = "minecraft:dragon_breath";
+ materials[438] = "minecraft:splash_potion";
+ materials[439] = "minecraft:spectral_arrow";
+ materials[440] = "minecraft:tipped_arrow";
+ materials[441] = "minecraft:lingering_potion";
+ materials[442] = "minecraft:shield";
+ materials[443] = "minecraft:elytra";
+ materials[444] = "minecraft:spruce_boat";
+ materials[445] = "minecraft:birch_boat";
+ materials[446] = "minecraft:jungle_boat";
+ materials[447] = "minecraft:acacia_boat";
+ materials[448] = "minecraft:dark_oak_boat";
+ materials[449] = "minecraft:totem_of_undying";
+ materials[450] = "minecraft:shulker_shell";
+ materials[452] = "minecraft:iron_nugget";
+ materials[453] = "minecraft:knowledge_book";
+ // Paper end
+ }
+ }
+
+ private static class DataConverterArmorStand implements DataConverter {
+
+ DataConverterArmorStand() {
+ }
+
+ public int getDataVersion() {
+ return 147;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("ArmorStand".equals(cmp.getString("id")) && cmp.getBoolean("Silent") && !cmp.getBoolean("Marker")) {
+ cmp.remove("Silent");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterBanner implements DataConverter {
+
+ DataConverterBanner() {
+ }
+
+ public int getDataVersion() {
+ return 804;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:banner".equals(cmp.getString("id")) && cmp.contains("tag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.contains("BlockEntityTag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag");
+
+ if (nbttagcompound2.contains("Base", 99)) {
+ cmp.putShort("Damage", (short) (nbttagcompound2.getShort("Base") & 15));
+ if (nbttagcompound1.contains("display", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound1.getCompound("display");
+
+ if (nbttagcompound3.contains("Lore", 9)) {
+ net.minecraft.nbt.ListTag nbttaglist = nbttagcompound3.getList("Lore", 8);
+
+ if (nbttaglist.size() == 1 && "(+NBT)".equals(nbttaglist.getString(0))) {
+ return cmp;
+ }
+ }
+ }
+
+ nbttagcompound2.remove("Base");
+ if (nbttagcompound2.isEmpty()) {
+ nbttagcompound1.remove("BlockEntityTag");
+ }
+
+ if (nbttagcompound1.isEmpty()) {
+ cmp.remove("tag");
+ }
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterPotionId implements DataConverter {
+
+ private static final String[] potions = new String[128];
+
+ DataConverterPotionId() {
+ }
+
+ public int getDataVersion() {
+ return 102;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:potion".equals(cmp.getString("id"))) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+ short short0 = cmp.getShort("Damage");
+
+ if (!nbttagcompound1.contains("Potion", 8)) {
+ String s = DataConverterPotionId.potions[short0 & 127];
+
+ nbttagcompound1.putString("Potion", s == null ? "minecraft:water" : s);
+ cmp.put("tag", nbttagcompound1);
+ if ((short0 & 16384) == 16384) {
+ cmp.putString("id", "minecraft:splash_potion");
+ }
+ }
+
+ if (short0 != 0) {
+ cmp.putShort("Damage", (short) 0);
+ }
+ }
+
+ return cmp;
+ }
+
+ static {
+ DataConverterPotionId.potions[0] = "minecraft:water";
+ DataConverterPotionId.potions[1] = "minecraft:regeneration";
+ DataConverterPotionId.potions[2] = "minecraft:swiftness";
+ DataConverterPotionId.potions[3] = "minecraft:fire_resistance";
+ DataConverterPotionId.potions[4] = "minecraft:poison";
+ DataConverterPotionId.potions[5] = "minecraft:healing";
+ DataConverterPotionId.potions[6] = "minecraft:night_vision";
+ DataConverterPotionId.potions[7] = null;
+ DataConverterPotionId.potions[8] = "minecraft:weakness";
+ DataConverterPotionId.potions[9] = "minecraft:strength";
+ DataConverterPotionId.potions[10] = "minecraft:slowness";
+ DataConverterPotionId.potions[11] = "minecraft:leaping";
+ DataConverterPotionId.potions[12] = "minecraft:harming";
+ DataConverterPotionId.potions[13] = "minecraft:water_breathing";
+ DataConverterPotionId.potions[14] = "minecraft:invisibility";
+ DataConverterPotionId.potions[15] = null;
+ DataConverterPotionId.potions[16] = "minecraft:awkward";
+ DataConverterPotionId.potions[17] = "minecraft:regeneration";
+ DataConverterPotionId.potions[18] = "minecraft:swiftness";
+ DataConverterPotionId.potions[19] = "minecraft:fire_resistance";
+ DataConverterPotionId.potions[20] = "minecraft:poison";
+ DataConverterPotionId.potions[21] = "minecraft:healing";
+ DataConverterPotionId.potions[22] = "minecraft:night_vision";
+ DataConverterPotionId.potions[23] = null;
+ DataConverterPotionId.potions[24] = "minecraft:weakness";
+ DataConverterPotionId.potions[25] = "minecraft:strength";
+ DataConverterPotionId.potions[26] = "minecraft:slowness";
+ DataConverterPotionId.potions[27] = "minecraft:leaping";
+ DataConverterPotionId.potions[28] = "minecraft:harming";
+ DataConverterPotionId.potions[29] = "minecraft:water_breathing";
+ DataConverterPotionId.potions[30] = "minecraft:invisibility";
+ DataConverterPotionId.potions[31] = null;
+ DataConverterPotionId.potions[32] = "minecraft:thick";
+ DataConverterPotionId.potions[33] = "minecraft:strong_regeneration";
+ DataConverterPotionId.potions[34] = "minecraft:strong_swiftness";
+ DataConverterPotionId.potions[35] = "minecraft:fire_resistance";
+ DataConverterPotionId.potions[36] = "minecraft:strong_poison";
+ DataConverterPotionId.potions[37] = "minecraft:strong_healing";
+ DataConverterPotionId.potions[38] = "minecraft:night_vision";
+ DataConverterPotionId.potions[39] = null;
+ DataConverterPotionId.potions[40] = "minecraft:weakness";
+ DataConverterPotionId.potions[41] = "minecraft:strong_strength";
+ DataConverterPotionId.potions[42] = "minecraft:slowness";
+ DataConverterPotionId.potions[43] = "minecraft:strong_leaping";
+ DataConverterPotionId.potions[44] = "minecraft:strong_harming";
+ DataConverterPotionId.potions[45] = "minecraft:water_breathing";
+ DataConverterPotionId.potions[46] = "minecraft:invisibility";
+ DataConverterPotionId.potions[47] = null;
+ DataConverterPotionId.potions[48] = null;
+ DataConverterPotionId.potions[49] = "minecraft:strong_regeneration";
+ DataConverterPotionId.potions[50] = "minecraft:strong_swiftness";
+ DataConverterPotionId.potions[51] = "minecraft:fire_resistance";
+ DataConverterPotionId.potions[52] = "minecraft:strong_poison";
+ DataConverterPotionId.potions[53] = "minecraft:strong_healing";
+ DataConverterPotionId.potions[54] = "minecraft:night_vision";
+ DataConverterPotionId.potions[55] = null;
+ DataConverterPotionId.potions[56] = "minecraft:weakness";
+ DataConverterPotionId.potions[57] = "minecraft:strong_strength";
+ DataConverterPotionId.potions[58] = "minecraft:slowness";
+ DataConverterPotionId.potions[59] = "minecraft:strong_leaping";
+ DataConverterPotionId.potions[60] = "minecraft:strong_harming";
+ DataConverterPotionId.potions[61] = "minecraft:water_breathing";
+ DataConverterPotionId.potions[62] = "minecraft:invisibility";
+ DataConverterPotionId.potions[63] = null;
+ DataConverterPotionId.potions[64] = "minecraft:mundane";
+ DataConverterPotionId.potions[65] = "minecraft:long_regeneration";
+ DataConverterPotionId.potions[66] = "minecraft:long_swiftness";
+ DataConverterPotionId.potions[67] = "minecraft:long_fire_resistance";
+ DataConverterPotionId.potions[68] = "minecraft:long_poison";
+ DataConverterPotionId.potions[69] = "minecraft:healing";
+ DataConverterPotionId.potions[70] = "minecraft:long_night_vision";
+ DataConverterPotionId.potions[71] = null;
+ DataConverterPotionId.potions[72] = "minecraft:long_weakness";
+ DataConverterPotionId.potions[73] = "minecraft:long_strength";
+ DataConverterPotionId.potions[74] = "minecraft:long_slowness";
+ DataConverterPotionId.potions[75] = "minecraft:long_leaping";
+ DataConverterPotionId.potions[76] = "minecraft:harming";
+ DataConverterPotionId.potions[77] = "minecraft:long_water_breathing";
+ DataConverterPotionId.potions[78] = "minecraft:long_invisibility";
+ DataConverterPotionId.potions[79] = null;
+ DataConverterPotionId.potions[80] = "minecraft:awkward";
+ DataConverterPotionId.potions[81] = "minecraft:long_regeneration";
+ DataConverterPotionId.potions[82] = "minecraft:long_swiftness";
+ DataConverterPotionId.potions[83] = "minecraft:long_fire_resistance";
+ DataConverterPotionId.potions[84] = "minecraft:long_poison";
+ DataConverterPotionId.potions[85] = "minecraft:healing";
+ DataConverterPotionId.potions[86] = "minecraft:long_night_vision";
+ DataConverterPotionId.potions[87] = null;
+ DataConverterPotionId.potions[88] = "minecraft:long_weakness";
+ DataConverterPotionId.potions[89] = "minecraft:long_strength";
+ DataConverterPotionId.potions[90] = "minecraft:long_slowness";
+ DataConverterPotionId.potions[91] = "minecraft:long_leaping";
+ DataConverterPotionId.potions[92] = "minecraft:harming";
+ DataConverterPotionId.potions[93] = "minecraft:long_water_breathing";
+ DataConverterPotionId.potions[94] = "minecraft:long_invisibility";
+ DataConverterPotionId.potions[95] = null;
+ DataConverterPotionId.potions[96] = "minecraft:thick";
+ DataConverterPotionId.potions[97] = "minecraft:regeneration";
+ DataConverterPotionId.potions[98] = "minecraft:swiftness";
+ DataConverterPotionId.potions[99] = "minecraft:long_fire_resistance";
+ DataConverterPotionId.potions[100] = "minecraft:poison";
+ DataConverterPotionId.potions[101] = "minecraft:strong_healing";
+ DataConverterPotionId.potions[102] = "minecraft:long_night_vision";
+ DataConverterPotionId.potions[103] = null;
+ DataConverterPotionId.potions[104] = "minecraft:long_weakness";
+ DataConverterPotionId.potions[105] = "minecraft:strength";
+ DataConverterPotionId.potions[106] = "minecraft:long_slowness";
+ DataConverterPotionId.potions[107] = "minecraft:leaping";
+ DataConverterPotionId.potions[108] = "minecraft:strong_harming";
+ DataConverterPotionId.potions[109] = "minecraft:long_water_breathing";
+ DataConverterPotionId.potions[110] = "minecraft:long_invisibility";
+ DataConverterPotionId.potions[111] = null;
+ DataConverterPotionId.potions[112] = null;
+ DataConverterPotionId.potions[113] = "minecraft:regeneration";
+ DataConverterPotionId.potions[114] = "minecraft:swiftness";
+ DataConverterPotionId.potions[115] = "minecraft:long_fire_resistance";
+ DataConverterPotionId.potions[116] = "minecraft:poison";
+ DataConverterPotionId.potions[117] = "minecraft:strong_healing";
+ DataConverterPotionId.potions[118] = "minecraft:long_night_vision";
+ DataConverterPotionId.potions[119] = null;
+ DataConverterPotionId.potions[120] = "minecraft:long_weakness";
+ DataConverterPotionId.potions[121] = "minecraft:strength";
+ DataConverterPotionId.potions[122] = "minecraft:long_slowness";
+ DataConverterPotionId.potions[123] = "minecraft:leaping";
+ DataConverterPotionId.potions[124] = "minecraft:strong_harming";
+ DataConverterPotionId.potions[125] = "minecraft:long_water_breathing";
+ DataConverterPotionId.potions[126] = "minecraft:long_invisibility";
+ DataConverterPotionId.potions[127] = null;
+ }
+ }
+
+ private static class DataConverterSpawnEgg implements DataConverter {
+
+ private static final String[] eggs = new String[256];
+
+ DataConverterSpawnEgg() {
+ }
+
+ public int getDataVersion() {
+ return 105;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:spawn_egg".equals(cmp.getString("id"))) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag");
+ short short0 = cmp.getShort("Damage");
+
+ if (!nbttagcompound2.contains("id", 8)) {
+ String s = DataConverterSpawnEgg.eggs[short0 & 255];
+
+ if (s != null) {
+ nbttagcompound2.putString("id", s);
+ nbttagcompound1.put("EntityTag", nbttagcompound2);
+ cmp.put("tag", nbttagcompound1);
+ }
+ }
+
+ if (short0 != 0) {
+ cmp.putShort("Damage", (short) 0);
+ }
+ }
+
+ return cmp;
+ }
+
+ static {
+
+ DataConverterSpawnEgg.eggs[1] = "Item";
+ DataConverterSpawnEgg.eggs[2] = "XPOrb";
+ DataConverterSpawnEgg.eggs[7] = "ThrownEgg";
+ DataConverterSpawnEgg.eggs[8] = "LeashKnot";
+ DataConverterSpawnEgg.eggs[9] = "Painting";
+ DataConverterSpawnEgg.eggs[10] = "Arrow";
+ DataConverterSpawnEgg.eggs[11] = "Snowball";
+ DataConverterSpawnEgg.eggs[12] = "Fireball";
+ DataConverterSpawnEgg.eggs[13] = "SmallFireball";
+ DataConverterSpawnEgg.eggs[14] = "ThrownEnderpearl";
+ DataConverterSpawnEgg.eggs[15] = "EyeOfEnderSignal";
+ DataConverterSpawnEgg.eggs[16] = "ThrownPotion";
+ DataConverterSpawnEgg.eggs[17] = "ThrownExpBottle";
+ DataConverterSpawnEgg.eggs[18] = "ItemFrame";
+ DataConverterSpawnEgg.eggs[19] = "WitherSkull";
+ DataConverterSpawnEgg.eggs[20] = "PrimedTnt";
+ DataConverterSpawnEgg.eggs[21] = "FallingSand";
+ DataConverterSpawnEgg.eggs[22] = "FireworksRocketEntity";
+ DataConverterSpawnEgg.eggs[23] = "TippedArrow";
+ DataConverterSpawnEgg.eggs[24] = "SpectralArrow";
+ DataConverterSpawnEgg.eggs[25] = "ShulkerBullet";
+ DataConverterSpawnEgg.eggs[26] = "DragonFireball";
+ DataConverterSpawnEgg.eggs[30] = "ArmorStand";
+ DataConverterSpawnEgg.eggs[41] = "Boat";
+ DataConverterSpawnEgg.eggs[42] = "MinecartRideable";
+ DataConverterSpawnEgg.eggs[43] = "MinecartChest";
+ DataConverterSpawnEgg.eggs[44] = "MinecartFurnace";
+ DataConverterSpawnEgg.eggs[45] = "MinecartTNT";
+ DataConverterSpawnEgg.eggs[46] = "MinecartHopper";
+ DataConverterSpawnEgg.eggs[47] = "MinecartSpawner";
+ DataConverterSpawnEgg.eggs[40] = "MinecartCommandBlock";
+ DataConverterSpawnEgg.eggs[48] = "Mob";
+ DataConverterSpawnEgg.eggs[49] = "Monster";
+ DataConverterSpawnEgg.eggs[50] = "Creeper";
+ DataConverterSpawnEgg.eggs[51] = "Skeleton";
+ DataConverterSpawnEgg.eggs[52] = "Spider";
+ DataConverterSpawnEgg.eggs[53] = "Giant";
+ DataConverterSpawnEgg.eggs[54] = "Zombie";
+ DataConverterSpawnEgg.eggs[55] = "Slime";
+ DataConverterSpawnEgg.eggs[56] = "Ghast";
+ DataConverterSpawnEgg.eggs[57] = "PigZombie";
+ DataConverterSpawnEgg.eggs[58] = "Enderman";
+ DataConverterSpawnEgg.eggs[59] = "CaveSpider";
+ DataConverterSpawnEgg.eggs[60] = "Silverfish";
+ DataConverterSpawnEgg.eggs[61] = "Blaze";
+ DataConverterSpawnEgg.eggs[62] = "LavaSlime";
+ DataConverterSpawnEgg.eggs[63] = "EnderDragon";
+ DataConverterSpawnEgg.eggs[64] = "WitherBoss";
+ DataConverterSpawnEgg.eggs[65] = "Bat";
+ DataConverterSpawnEgg.eggs[66] = "Witch";
+ DataConverterSpawnEgg.eggs[67] = "Endermite";
+ DataConverterSpawnEgg.eggs[68] = "Guardian";
+ DataConverterSpawnEgg.eggs[69] = "Shulker";
+ DataConverterSpawnEgg.eggs[90] = "Pig";
+ DataConverterSpawnEgg.eggs[91] = "Sheep";
+ DataConverterSpawnEgg.eggs[92] = "Cow";
+ DataConverterSpawnEgg.eggs[93] = "Chicken";
+ DataConverterSpawnEgg.eggs[94] = "Squid";
+ DataConverterSpawnEgg.eggs[95] = "Wolf";
+ DataConverterSpawnEgg.eggs[96] = "MushroomCow";
+ DataConverterSpawnEgg.eggs[97] = "SnowMan";
+ DataConverterSpawnEgg.eggs[98] = "Ozelot";
+ DataConverterSpawnEgg.eggs[99] = "VillagerGolem";
+ DataConverterSpawnEgg.eggs[100] = "EntityHorse";
+ DataConverterSpawnEgg.eggs[101] = "Rabbit";
+ DataConverterSpawnEgg.eggs[120] = "Villager";
+ DataConverterSpawnEgg.eggs[200] = "EnderCrystal";
+ }
+ }
+
+ private static class DataConverterMinecart implements DataConverter {
+
+ private static final List a = Lists.newArrayList("MinecartRideable", "MinecartChest", "MinecartFurnace", "MinecartTNT", "MinecartSpawner", "MinecartHopper", "MinecartCommandBlock");
+
+ DataConverterMinecart() {
+ }
+
+ public int getDataVersion() {
+ return 106;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Minecart".equals(cmp.getString("id"))) {
+ String s = "MinecartRideable";
+ int i = cmp.getInt("Type");
+
+ if (i > 0 && i < DataConverterMinecart.a.size()) {
+ s = DataConverterMinecart.a.get(i);
+ }
+
+ cmp.putString("id", s);
+ cmp.remove("Type");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterMobSpawner implements DataConverter {
+
+ DataConverterMobSpawner() {
+ }
+
+ public int getDataVersion() {
+ return 107;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (!"MobSpawner".equals(cmp.getString("id"))) {
+ return cmp;
+ } else {
+ if (cmp.contains("EntityId", 8)) {
+ String s = cmp.getString("EntityId");
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("SpawnData");
+
+ nbttagcompound1.putString("id", s.isEmpty() ? "Pig" : s);
+ cmp.put("SpawnData", nbttagcompound1);
+ cmp.remove("EntityId");
+ }
+
+ if (cmp.contains("SpawnPotentials", 9)) {
+ net.minecraft.nbt.ListTag nbttaglist = cmp.getList("SpawnPotentials", 10);
+
+ for (int i = 0; i < nbttaglist.size(); ++i) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompound(i);
+
+ if (nbttagcompound2.contains("Type", 8)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound2.getCompound("Properties");
+
+ nbttagcompound3.putString("id", nbttagcompound2.getString("Type"));
+ nbttagcompound2.put("Entity", nbttagcompound3);
+ nbttagcompound2.remove("Type");
+ nbttagcompound2.remove("Properties");
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+ }
+
+ private static class DataConverterUUID implements DataConverter {
+
+ DataConverterUUID() {
+ }
+
+ public int getDataVersion() {
+ return 108;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (cmp.contains("UUID", 8)) {
+ cmp.putUUID("UUID", UUID.fromString(cmp.getString("UUID")));
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterHealth implements DataConverter {
+
+ private static final Set a = Sets.newHashSet("ArmorStand", "Bat", "Blaze", "CaveSpider", "Chicken", "Cow", "Creeper", "EnderDragon", "Enderman", "Endermite", "EntityHorse", "Ghast", "Giant", "Guardian", "LavaSlime", "MushroomCow", "Ozelot", "Pig", "PigZombie", "Rabbit", "Sheep", "Shulker", "Silverfish", "Skeleton", "Slime", "SnowMan", "Spider", "Squid", "Villager", "VillagerGolem", "Witch", "WitherBoss", "Wolf", "Zombie");
+
+ DataConverterHealth() {
+ }
+
+ public int getDataVersion() {
+ return 109;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (DataConverterHealth.a.contains(cmp.getString("id"))) {
+ float f;
+
+ if (cmp.contains("HealF", 99)) {
+ f = cmp.getFloat("HealF");
+ cmp.remove("HealF");
+ } else {
+ if (!cmp.contains("Health", 99)) {
+ return cmp;
+ }
+
+ f = cmp.getFloat("Health");
+ }
+
+ cmp.putFloat("Health", f);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterSaddle implements DataConverter {
+
+ DataConverterSaddle() {
+ }
+
+ public int getDataVersion() {
+ return 110;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("EntityHorse".equals(cmp.getString("id")) && !cmp.contains("SaddleItem", 10) && cmp.getBoolean("Saddle")) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = new net.minecraft.nbt.CompoundTag();
+
+ nbttagcompound1.putString("id", "minecraft:saddle");
+ nbttagcompound1.putByte("Count", (byte) 1);
+ nbttagcompound1.putShort("Damage", (short) 0);
+ cmp.put("SaddleItem", nbttagcompound1);
+ cmp.remove("Saddle");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterHanging implements DataConverter {
+
+ DataConverterHanging() {
+ }
+
+ public int getDataVersion() {
+ return 111;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = cmp.getString("id");
+ boolean flag = "Painting".equals(s);
+ boolean flag1 = "ItemFrame".equals(s);
+
+ if ((flag || flag1) && !cmp.contains("Facing", 99)) {
+ Direction enumdirection;
+
+ if (cmp.contains("Direction", 99)) {
+ enumdirection = Direction.from2DDataValue(cmp.getByte("Direction"));
+ cmp.putInt("TileX", cmp.getInt("TileX") + enumdirection.getStepX());
+ cmp.putInt("TileY", cmp.getInt("TileY") + enumdirection.getStepY());
+ cmp.putInt("TileZ", cmp.getInt("TileZ") + enumdirection.getStepZ());
+ cmp.remove("Direction");
+ if (flag1 && cmp.contains("ItemRotation", 99)) {
+ cmp.putByte("ItemRotation", (byte) (cmp.getByte("ItemRotation") * 2));
+ }
+ } else {
+ enumdirection = Direction.from2DDataValue(cmp.getByte("Dir"));
+ cmp.remove("Dir");
+ }
+
+ cmp.putByte("Facing", (byte) enumdirection.get2DDataValue());
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterDropChances implements DataConverter {
+
+ DataConverterDropChances() {
+ }
+
+ public int getDataVersion() {
+ return 113;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ net.minecraft.nbt.ListTag nbttaglist;
+
+ if (cmp.contains("HandDropChances", 9)) {
+ nbttaglist = cmp.getList("HandDropChances", 5);
+ if (nbttaglist.size() == 2 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F) {
+ cmp.remove("HandDropChances");
+ }
+ }
+
+ if (cmp.contains("ArmorDropChances", 9)) {
+ nbttaglist = cmp.getList("ArmorDropChances", 5);
+ if (nbttaglist.size() == 4 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F && nbttaglist.getFloat(2) == 0.0F && nbttaglist.getFloat(3) == 0.0F) {
+ cmp.remove("ArmorDropChances");
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterRiding implements DataConverter {
+
+ DataConverterRiding() {
+ }
+
+ public int getDataVersion() {
+ return 135;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ while (cmp.contains("Riding", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = this.b(cmp);
+
+ this.convert(cmp, nbttagcompound1);
+ cmp = nbttagcompound1;
+ }
+
+ return cmp;
+ }
+
+ protected void convert(net.minecraft.nbt.CompoundTag nbttagcompound, net.minecraft.nbt.CompoundTag nbttagcompound1) {
+ net.minecraft.nbt.ListTag nbttaglist = new net.minecraft.nbt.ListTag();
+
+ nbttaglist.add(nbttagcompound);
+ nbttagcompound1.put("Passengers", nbttaglist);
+ }
+
+ protected net.minecraft.nbt.CompoundTag b(net.minecraft.nbt.CompoundTag nbttagcompound) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Riding");
+
+ nbttagcompound.remove("Riding");
+ return nbttagcompound1;
+ }
+ }
+
+ private static class DataConverterBook implements DataConverter {
+
+ DataConverterBook() {
+ }
+
+ public int getDataVersion() {
+ return 165;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:written_book".equals(cmp.getString("id"))) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.contains("pages", 9)) {
+ net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("pages", 8);
+
+ for (int i = 0; i < nbttaglist.size(); ++i) {
+ String s = nbttaglist.getString(i);
+ Component object = null;
+
+ if (!"null".equals(s) && !StringUtil.isNullOrEmpty(s)) {
+ if ((s.charAt(0) != 34 || s.charAt(s.length() - 1) != 34) && (s.charAt(0) != 123 || s.charAt(s.length() - 1) != 125)) {
+ object = Component.literal(s);
+ } else {
+ try {
+ object = GsonHelper.fromJson(DataConverterSignText.a, s, Component.class, true);
+ if (object == null) {
+ object = Component.literal("");
+ }
+ } catch (JsonParseException jsonparseexception) {
+ ;
+ }
+
+ if (object == null) {
+ try {
+ object = Component.Serializer.fromJson(s);
+ } catch (JsonParseException jsonparseexception1) {
+ ;
+ }
+ }
+
+ if (object == null) {
+ try {
+ object = Component.Serializer.fromJsonLenient(s);
+ } catch (JsonParseException jsonparseexception2) {
+ ;
+ }
+ }
+
+ if (object == null) {
+ object = Component.literal(s);
+ }
+ }
+ } else {
+ object = Component.literal("");
+ }
+
+ nbttaglist.set(i, net.minecraft.nbt.StringTag.valueOf(Component.Serializer.toJson(object)));
+ }
+
+ nbttagcompound1.put("pages", nbttaglist);
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterCookedFish implements DataConverter {
+
+ private static final ResourceLocation a = new ResourceLocation("cooked_fished");
+
+ DataConverterCookedFish() {
+ }
+
+ public int getDataVersion() {
+ return 502;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (cmp.contains("id", 8) && DataConverterCookedFish.a.equals(new ResourceLocation(cmp.getString("id")))) {
+ cmp.putString("id", "minecraft:cooked_fish");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterZombie implements DataConverter {
+
+ private static final Random a = new Random();
+
+ DataConverterZombie() {
+ }
+
+ public int getDataVersion() {
+ return 502;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Zombie".equals(cmp.getString("id")) && cmp.getBoolean("IsVillager")) {
+ if (!cmp.contains("ZombieType", 99)) {
+ int i = -1;
+
+ if (cmp.contains("VillagerProfession", 99)) {
+ try {
+ i = this.convert(cmp.getInt("VillagerProfession"));
+ } catch (RuntimeException runtimeexception) {
+ ;
+ }
+ }
+
+ if (i == -1) {
+ i = this.convert(DataConverterZombie.a.nextInt(6));
+ }
+
+ cmp.putInt("ZombieType", i);
+ }
+
+ cmp.remove("IsVillager");
+ }
+
+ return cmp;
+ }
+
+ private int convert(int i) {
+ return i >= 0 && i < 6 ? i : -1;
+ }
+ }
+
+ private static class DataConverterVBO implements DataConverter {
+
+ DataConverterVBO() {
+ }
+
+ public int getDataVersion() {
+ return 505;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ cmp.putString("useVbo", "true");
+ return cmp;
+ }
+ }
+
+ private static class DataConverterGuardian implements DataConverter {
+
+ DataConverterGuardian() {
+ }
+
+ public int getDataVersion() {
+ return 700;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Guardian".equals(cmp.getString("id"))) {
+ if (cmp.getBoolean("Elder")) {
+ cmp.putString("id", "ElderGuardian");
+ }
+
+ cmp.remove("Elder");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterSkeleton implements DataConverter {
+
+ DataConverterSkeleton() {
+ }
+
+ public int getDataVersion() {
+ return 701;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = cmp.getString("id");
+
+ if ("Skeleton".equals(s)) {
+ int i = cmp.getInt("SkeletonType");
+
+ if (i == 1) {
+ cmp.putString("id", "WitherSkeleton");
+ } else if (i == 2) {
+ cmp.putString("id", "Stray");
+ }
+
+ cmp.remove("SkeletonType");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterZombieType implements DataConverter {
+
+ DataConverterZombieType() {
+ }
+
+ public int getDataVersion() {
+ return 702;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Zombie".equals(cmp.getString("id"))) {
+ int i = cmp.getInt("ZombieType");
+
+ switch (i) {
+ case 0:
+ default:
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ cmp.putString("id", "ZombieVillager");
+ cmp.putInt("Profession", i - 1);
+ break;
+
+ case 6:
+ cmp.putString("id", "Husk");
+ }
+
+ cmp.remove("ZombieType");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterHorse implements DataConverter {
+
+ DataConverterHorse() {
+ }
+
+ public int getDataVersion() {
+ return 703;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("EntityHorse".equals(cmp.getString("id"))) {
+ int i = cmp.getInt("Type");
+
+ switch (i) {
+ case 0:
+ default:
+ cmp.putString("id", "Horse");
+ break;
+
+ case 1:
+ cmp.putString("id", "Donkey");
+ break;
+
+ case 2:
+ cmp.putString("id", "Mule");
+ break;
+
+ case 3:
+ cmp.putString("id", "ZombieHorse");
+ break;
+
+ case 4:
+ cmp.putString("id", "SkeletonHorse");
+ }
+
+ cmp.remove("Type");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterTileEntity implements DataConverter {
+
+ private static final Map a = Maps.newHashMap();
+
+ DataConverterTileEntity() {
+ }
+
+ public int getDataVersion() {
+ return 704;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = DataConverterTileEntity.a.get(cmp.getString("id"));
+
+ if (s != null) {
+ cmp.putString("id", s);
+ }
+
+ return cmp;
+ }
+
+ static {
+ DataConverterTileEntity.a.put("Airportal", "minecraft:end_portal");
+ DataConverterTileEntity.a.put("Banner", "minecraft:banner");
+ DataConverterTileEntity.a.put("Beacon", "minecraft:beacon");
+ DataConverterTileEntity.a.put("Cauldron", "minecraft:brewing_stand");
+ DataConverterTileEntity.a.put("Chest", "minecraft:chest");
+ DataConverterTileEntity.a.put("Comparator", "minecraft:comparator");
+ DataConverterTileEntity.a.put("Control", "minecraft:command_block");
+ DataConverterTileEntity.a.put("DLDetector", "minecraft:daylight_detector");
+ DataConverterTileEntity.a.put("Dropper", "minecraft:dropper");
+ DataConverterTileEntity.a.put("EnchantTable", "minecraft:enchanting_table");
+ DataConverterTileEntity.a.put("EndGateway", "minecraft:end_gateway");
+ DataConverterTileEntity.a.put("EnderChest", "minecraft:ender_chest");
+ DataConverterTileEntity.a.put("FlowerPot", "minecraft:flower_pot");
+ DataConverterTileEntity.a.put("Furnace", "minecraft:furnace");
+ DataConverterTileEntity.a.put("Hopper", "minecraft:hopper");
+ DataConverterTileEntity.a.put("MobSpawner", "minecraft:mob_spawner");
+ DataConverterTileEntity.a.put("Music", "minecraft:noteblock");
+ DataConverterTileEntity.a.put("Piston", "minecraft:piston");
+ DataConverterTileEntity.a.put("RecordPlayer", "minecraft:jukebox");
+ DataConverterTileEntity.a.put("Sign", "minecraft:sign");
+ DataConverterTileEntity.a.put("Skull", "minecraft:skull");
+ DataConverterTileEntity.a.put("Structure", "minecraft:structure_block");
+ DataConverterTileEntity.a.put("Trap", "minecraft:dispenser");
+ }
+ }
+
+ private static class DataConverterEntity implements DataConverter {
+
+ private static final Map a = Maps.newHashMap();
+
+ DataConverterEntity() {
+ }
+
+ public int getDataVersion() {
+ return 704;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = DataConverterEntity.a.get(cmp.getString("id"));
+
+ if (s != null) {
+ cmp.putString("id", s);
+ }
+
+ return cmp;
+ }
+
+ static {
+ DataConverterEntity.a.put("AreaEffectCloud", "minecraft:area_effect_cloud");
+ DataConverterEntity.a.put("ArmorStand", "minecraft:armor_stand");
+ DataConverterEntity.a.put("Arrow", "minecraft:arrow");
+ DataConverterEntity.a.put("Bat", "minecraft:bat");
+ DataConverterEntity.a.put("Blaze", "minecraft:blaze");
+ DataConverterEntity.a.put("Boat", "minecraft:boat");
+ DataConverterEntity.a.put("CaveSpider", "minecraft:cave_spider");
+ DataConverterEntity.a.put("Chicken", "minecraft:chicken");
+ DataConverterEntity.a.put("Cow", "minecraft:cow");
+ DataConverterEntity.a.put("Creeper", "minecraft:creeper");
+ DataConverterEntity.a.put("Donkey", "minecraft:donkey");
+ DataConverterEntity.a.put("DragonFireball", "minecraft:dragon_fireball");
+ DataConverterEntity.a.put("ElderGuardian", "minecraft:elder_guardian");
+ DataConverterEntity.a.put("EnderCrystal", "minecraft:ender_crystal");
+ DataConverterEntity.a.put("EnderDragon", "minecraft:ender_dragon");
+ DataConverterEntity.a.put("Enderman", "minecraft:enderman");
+ DataConverterEntity.a.put("Endermite", "minecraft:endermite");
+ DataConverterEntity.a.put("EyeOfEnderSignal", "minecraft:eye_of_ender_signal");
+ DataConverterEntity.a.put("FallingSand", "minecraft:falling_block");
+ DataConverterEntity.a.put("Fireball", "minecraft:fireball");
+ DataConverterEntity.a.put("FireworksRocketEntity", "minecraft:fireworks_rocket");
+ DataConverterEntity.a.put("Ghast", "minecraft:ghast");
+ DataConverterEntity.a.put("Giant", "minecraft:giant");
+ DataConverterEntity.a.put("Guardian", "minecraft:guardian");
+ DataConverterEntity.a.put("Horse", "minecraft:horse");
+ DataConverterEntity.a.put("Husk", "minecraft:husk");
+ DataConverterEntity.a.put("Item", "minecraft:item");
+ DataConverterEntity.a.put("ItemFrame", "minecraft:item_frame");
+ DataConverterEntity.a.put("LavaSlime", "minecraft:magma_cube");
+ DataConverterEntity.a.put("LeashKnot", "minecraft:leash_knot");
+ DataConverterEntity.a.put("MinecartChest", "minecraft:chest_minecart");
+ DataConverterEntity.a.put("MinecartCommandBlock", "minecraft:commandblock_minecart");
+ DataConverterEntity.a.put("MinecartFurnace", "minecraft:furnace_minecart");
+ DataConverterEntity.a.put("MinecartHopper", "minecraft:hopper_minecart");
+ DataConverterEntity.a.put("MinecartRideable", "minecraft:minecart");
+ DataConverterEntity.a.put("MinecartSpawner", "minecraft:spawner_minecart");
+ DataConverterEntity.a.put("MinecartTNT", "minecraft:tnt_minecart");
+ DataConverterEntity.a.put("Mule", "minecraft:mule");
+ DataConverterEntity.a.put("MushroomCow", "minecraft:mooshroom");
+ DataConverterEntity.a.put("Ozelot", "minecraft:ocelot");
+ DataConverterEntity.a.put("Painting", "minecraft:painting");
+ DataConverterEntity.a.put("Pig", "minecraft:pig");
+ DataConverterEntity.a.put("PigZombie", "minecraft:zombie_pigman");
+ DataConverterEntity.a.put("PolarBear", "minecraft:polar_bear");
+ DataConverterEntity.a.put("PrimedTnt", "minecraft:tnt");
+ DataConverterEntity.a.put("Rabbit", "minecraft:rabbit");
+ DataConverterEntity.a.put("Sheep", "minecraft:sheep");
+ DataConverterEntity.a.put("Shulker", "minecraft:shulker");
+ DataConverterEntity.a.put("ShulkerBullet", "minecraft:shulker_bullet");
+ DataConverterEntity.a.put("Silverfish", "minecraft:silverfish");
+ DataConverterEntity.a.put("Skeleton", "minecraft:skeleton");
+ DataConverterEntity.a.put("SkeletonHorse", "minecraft:skeleton_horse");
+ DataConverterEntity.a.put("Slime", "minecraft:slime");
+ DataConverterEntity.a.put("SmallFireball", "minecraft:small_fireball");
+ DataConverterEntity.a.put("SnowMan", "minecraft:snowman");
+ DataConverterEntity.a.put("Snowball", "minecraft:snowball");
+ DataConverterEntity.a.put("SpectralArrow", "minecraft:spectral_arrow");
+ DataConverterEntity.a.put("Spider", "minecraft:spider");
+ DataConverterEntity.a.put("Squid", "minecraft:squid");
+ DataConverterEntity.a.put("Stray", "minecraft:stray");
+ DataConverterEntity.a.put("ThrownEgg", "minecraft:egg");
+ DataConverterEntity.a.put("ThrownEnderpearl", "minecraft:ender_pearl");
+ DataConverterEntity.a.put("ThrownExpBottle", "minecraft:xp_bottle");
+ DataConverterEntity.a.put("ThrownPotion", "minecraft:potion");
+ DataConverterEntity.a.put("Villager", "minecraft:villager");
+ DataConverterEntity.a.put("VillagerGolem", "minecraft:villager_golem");
+ DataConverterEntity.a.put("Witch", "minecraft:witch");
+ DataConverterEntity.a.put("WitherBoss", "minecraft:wither");
+ DataConverterEntity.a.put("WitherSkeleton", "minecraft:wither_skeleton");
+ DataConverterEntity.a.put("WitherSkull", "minecraft:wither_skull");
+ DataConverterEntity.a.put("Wolf", "minecraft:wolf");
+ DataConverterEntity.a.put("XPOrb", "minecraft:xp_orb");
+ DataConverterEntity.a.put("Zombie", "minecraft:zombie");
+ DataConverterEntity.a.put("ZombieHorse", "minecraft:zombie_horse");
+ DataConverterEntity.a.put("ZombieVillager", "minecraft:zombie_villager");
+ }
+ }
+
+ private static class DataConverterPotionWater implements DataConverter {
+
+ DataConverterPotionWater() {
+ }
+
+ public int getDataVersion() {
+ return 806;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = cmp.getString("id");
+
+ if ("minecraft:potion".equals(s) || "minecraft:splash_potion".equals(s) || "minecraft:lingering_potion".equals(s) || "minecraft:tipped_arrow".equals(s)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (!nbttagcompound1.contains("Potion", 8)) {
+ nbttagcompound1.putString("Potion", "minecraft:water");
+ }
+
+ if (!cmp.contains("tag", 10)) {
+ cmp.put("tag", nbttagcompound1);
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterShulker implements DataConverter {
+
+ DataConverterShulker() {
+ }
+
+ public int getDataVersion() {
+ return 808;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:shulker".equals(cmp.getString("id")) && !cmp.contains("Color", 99)) {
+ cmp.putByte("Color", (byte) 10);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterShulkerBoxItem implements DataConverter {
+
+ public static final String[] a = new String[] { "minecraft:white_shulker_box", "minecraft:orange_shulker_box", "minecraft:magenta_shulker_box", "minecraft:light_blue_shulker_box", "minecraft:yellow_shulker_box", "minecraft:lime_shulker_box", "minecraft:pink_shulker_box", "minecraft:gray_shulker_box", "minecraft:silver_shulker_box", "minecraft:cyan_shulker_box", "minecraft:purple_shulker_box", "minecraft:blue_shulker_box", "minecraft:brown_shulker_box", "minecraft:green_shulker_box", "minecraft:red_shulker_box", "minecraft:black_shulker_box" };
+
+ DataConverterShulkerBoxItem() {
+ }
+
+ public int getDataVersion() {
+ return 813;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:shulker_box".equals(cmp.getString("id")) && cmp.contains("tag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.contains("BlockEntityTag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag");
+
+ if (nbttagcompound2.getList("Items", 10).isEmpty()) {
+ nbttagcompound2.remove("Items");
+ }
+
+ int i = nbttagcompound2.getInt("Color");
+
+ nbttagcompound2.remove("Color");
+ if (nbttagcompound2.isEmpty()) {
+ nbttagcompound1.remove("BlockEntityTag");
+ }
+
+ if (nbttagcompound1.isEmpty()) {
+ cmp.remove("tag");
+ }
+
+ cmp.putString("id", DataConverterShulkerBoxItem.a[i % 16]);
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterShulkerBoxBlock implements DataConverter {
+
+ DataConverterShulkerBoxBlock() {
+ }
+
+ public int getDataVersion() {
+ return 813;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:shulker".equals(cmp.getString("id"))) {
+ cmp.remove("Color");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterLang implements DataConverter {
+
+ DataConverterLang() {
+ }
+
+ public int getDataVersion() {
+ return 816;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (cmp.contains("lang", 8)) {
+ cmp.putString("lang", cmp.getString("lang").toLowerCase(Locale.ROOT));
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterTotem implements DataConverter {
+
+ DataConverterTotem() {
+ }
+
+ public int getDataVersion() {
+ return 820;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:totem".equals(cmp.getString("id"))) {
+ cmp.putString("id", "minecraft:totem_of_undying");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterBedBlock implements DataConverter {
+
+ private static final Logger a = LogManager.getLogger(PaperweightDataConverters.class);
+
+ DataConverterBedBlock() {
+ }
+
+ public int getDataVersion() {
+ return 1125;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ try {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level");
+ int i = nbttagcompound1.getInt("xPos");
+ int j = nbttagcompound1.getInt("zPos");
+ net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("TileEntities", 10);
+ net.minecraft.nbt.ListTag nbttaglist1 = nbttagcompound1.getList("Sections", 10);
+
+ for (int k = 0; k < nbttaglist1.size(); ++k) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist1.getCompound(k);
+ byte b0 = nbttagcompound2.getByte("Y");
+ byte[] abyte = nbttagcompound2.getByteArray("Blocks");
+
+ for (int l = 0; l < abyte.length; ++l) {
+ if (416 == (abyte[l] & 255) << 4) {
+ int i1 = l & 15;
+ int j1 = l >> 8 & 15;
+ int k1 = l >> 4 & 15;
+ net.minecraft.nbt.CompoundTag nbttagcompound3 = new net.minecraft.nbt.CompoundTag();
+
+ nbttagcompound3.putString("id", "bed");
+ nbttagcompound3.putInt("x", i1 + (i << 4));
+ nbttagcompound3.putInt("y", j1 + (b0 << 4));
+ nbttagcompound3.putInt("z", k1 + (j << 4));
+ nbttaglist.add(nbttagcompound3);
+ }
+ }
+ }
+ } catch (Exception exception) {
+ DataConverterBedBlock.a.warn("Unable to datafix Bed blocks, level format may be missing tags.");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterBedItem implements DataConverter {
+
+ DataConverterBedItem() {
+ }
+
+ public int getDataVersion() {
+ return 1125;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:bed".equals(cmp.getString("id")) && cmp.getShort("Damage") == 0) {
+ cmp.putShort("Damage", (short) DyeColor.RED.getId());
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterSignText implements DataConverter {
+
+ public static final Gson a = new GsonBuilder().registerTypeAdapter(Component.class, new JsonDeserializer() {
+ MutableComponent a(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException {
+ if (jsonelement.isJsonPrimitive()) {
+ return Component.literal(jsonelement.getAsString());
+ } else if (jsonelement.isJsonArray()) {
+ JsonArray jsonarray = jsonelement.getAsJsonArray();
+ MutableComponent ichatbasecomponent = null;
+ Iterator iterator = jsonarray.iterator();
+
+ while (iterator.hasNext()) {
+ JsonElement jsonelement1 = (JsonElement) iterator.next();
+ MutableComponent ichatbasecomponent1 = this.a(jsonelement1, jsonelement1.getClass(), jsondeserializationcontext);
+
+ if (ichatbasecomponent == null) {
+ ichatbasecomponent = ichatbasecomponent1;
+ } else {
+ ichatbasecomponent.append(ichatbasecomponent1);
+ }
+ }
+
+ return ichatbasecomponent;
+ } else {
+ throw new JsonParseException("Don't know how to turn " + jsonelement + " into a Component");
+ }
+ }
+
+ public Object deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException {
+ return this.a(jsonelement, type, jsondeserializationcontext);
+ }
+ }).create();
+
+ DataConverterSignText() {
+ }
+
+ public int getDataVersion() {
+ return 101;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Sign".equals(cmp.getString("id"))) {
+ this.convert(cmp, "Text1");
+ this.convert(cmp, "Text2");
+ this.convert(cmp, "Text3");
+ this.convert(cmp, "Text4");
+ }
+
+ return cmp;
+ }
+
+ private void convert(net.minecraft.nbt.CompoundTag nbttagcompound, String s) {
+ String s1 = nbttagcompound.getString(s);
+ Component object = null;
+
+ if (!"null".equals(s1) && !StringUtil.isNullOrEmpty(s1)) {
+ if ((s1.charAt(0) != 34 || s1.charAt(s1.length() - 1) != 34) && (s1.charAt(0) != 123 || s1.charAt(s1.length() - 1) != 125)) {
+ object = Component.literal(s1);
+ } else {
+ try {
+ object = GsonHelper.fromJson(DataConverterSignText.a, s1, Component.class, true);
+ if (object == null) {
+ object = Component.literal("");
+ }
+ } catch (JsonParseException jsonparseexception) {
+ ;
+ }
+
+ if (object == null) {
+ try {
+ object = Component.Serializer.fromJson(s1);
+ } catch (JsonParseException jsonparseexception1) {
+ ;
+ }
+ }
+
+ if (object == null) {
+ try {
+ object = Component.Serializer.fromJsonLenient(s1);
+ } catch (JsonParseException jsonparseexception2) {
+ ;
+ }
+ }
+
+ if (object == null) {
+ object = Component.literal(s1);
+ }
+ }
+ } else {
+ object = Component.literal("");
+ }
+
+ nbttagcompound.putString(s, Component.Serializer.toJson(object));
+ }
+ }
+
+ private static class DataInspectorPlayerVehicle implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (cmp.contains("RootVehicle", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("RootVehicle");
+
+ if (nbttagcompound1.contains("Entity", 10)) {
+ convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer);
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorLevelPlayer implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (cmp.contains("Player", 10)) {
+ convertCompound(LegacyType.PLAYER, cmp, "Player", sourceVer, targetVer);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorStructure implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ net.minecraft.nbt.ListTag nbttaglist;
+ int j;
+ net.minecraft.nbt.CompoundTag nbttagcompound1;
+
+ if (cmp.contains("entities", 9)) {
+ nbttaglist = cmp.getList("entities", 10);
+
+ for (j = 0; j < nbttaglist.size(); ++j) {
+ nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j);
+ if (nbttagcompound1.contains("nbt", 10)) {
+ convertCompound(LegacyType.ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer);
+ }
+ }
+ }
+
+ if (cmp.contains("blocks", 9)) {
+ nbttaglist = cmp.getList("blocks", 10);
+
+ for (j = 0; j < nbttaglist.size(); ++j) {
+ nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j);
+ if (nbttagcompound1.contains("nbt", 10)) {
+ convertCompound(LegacyType.BLOCK_ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer);
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorChunks implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (cmp.contains("Level", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level");
+ net.minecraft.nbt.ListTag nbttaglist;
+ int j;
+
+ if (nbttagcompound1.contains("Entities", 9)) {
+ nbttaglist = nbttagcompound1.getList("Entities", 10);
+
+ for (j = 0; j < nbttaglist.size(); ++j) {
+ nbttaglist.set(j, convert(LegacyType.ENTITY, (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), sourceVer, targetVer));
+ }
+ }
+
+ if (nbttagcompound1.contains("TileEntities", 9)) {
+ nbttaglist = nbttagcompound1.getList("TileEntities", 10);
+
+ for (j = 0; j < nbttaglist.size(); ++j) {
+ nbttaglist.set(j, convert(LegacyType.BLOCK_ENTITY, (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), sourceVer, targetVer));
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorEntityPassengers implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (cmp.contains("Passengers", 9)) {
+ net.minecraft.nbt.ListTag nbttaglist = cmp.getList("Passengers", 10);
+
+ for (int j = 0; j < nbttaglist.size(); ++j) {
+ nbttaglist.set(j, convert(LegacyType.ENTITY, nbttaglist.getCompound(j), sourceVer, targetVer));
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorPlayer implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ convertItems(cmp, "Inventory", sourceVer, targetVer);
+ convertItems(cmp, "EnderItems", sourceVer, targetVer);
+ if (cmp.contains("ShoulderEntityLeft", 10)) {
+ convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityLeft", sourceVer, targetVer);
+ }
+
+ if (cmp.contains("ShoulderEntityRight", 10)) {
+ convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityRight", sourceVer, targetVer);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorVillagers implements DataInspector {
+ ResourceLocation entityVillager = getKey("EntityVillager");
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (entityVillager.equals(new ResourceLocation(cmp.getString("id"))) && cmp.contains("Offers", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Offers");
+
+ if (nbttagcompound1.contains("Recipes", 9)) {
+ net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("Recipes", 10);
+
+ for (int j = 0; j < nbttaglist.size(); ++j) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompound(j);
+
+ convertItem(nbttagcompound2, "buy", sourceVer, targetVer);
+ convertItem(nbttagcompound2, "buyB", sourceVer, targetVer);
+ convertItem(nbttagcompound2, "sell", sourceVer, targetVer);
+ nbttaglist.set(j, nbttagcompound2);
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorMobSpawnerMinecart implements DataInspector {
+ ResourceLocation entityMinecartMobSpawner = getKey("EntityMinecartMobSpawner");
+ ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner");
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ String s = cmp.getString("id");
+ if (entityMinecartMobSpawner.equals(new ResourceLocation(s))) {
+ cmp.putString("id", tileEntityMobSpawner.toString());
+ convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer);
+ cmp.putString("id", s);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorMobSpawnerMobs implements DataInspector {
+ ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner");
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (tileEntityMobSpawner.equals(new ResourceLocation(cmp.getString("id")))) {
+ if (cmp.contains("SpawnPotentials", 9)) {
+ net.minecraft.nbt.ListTag nbttaglist = cmp.getList("SpawnPotentials", 10);
+
+ for (int j = 0; j < nbttaglist.size(); ++j) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttaglist.getCompound(j);
+
+ convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer);
+ }
+ }
+
+ convertCompound(LegacyType.ENTITY, cmp, "SpawnData", sourceVer, targetVer);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorCommandBlock implements DataInspector {
+ ResourceLocation tileEntityCommand = getKey("TileEntityCommand");
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (tileEntityCommand.equals(new ResourceLocation(cmp.getString("id")))) {
+ cmp.putString("id", "Control");
+ convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer);
+ cmp.putString("id", "MinecartCommandBlock");
+ }
+
+ return cmp;
+ }
+ }
+}
diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightFakePlayer.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightFakePlayer.java
new file mode 100644
index 000000000..874b5323f
--- /dev/null
+++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightFakePlayer.java
@@ -0,0 +1,93 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R2;
+
+import com.mojang.authlib.GameProfile;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.stats.Stat;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.block.entity.SignBlockEntity;
+import net.minecraft.world.phys.Vec3;
+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
+
+import java.util.OptionalInt;
+import java.util.UUID;
+
+class PaperweightFakePlayer extends ServerPlayer {
+ private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]");
+ private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
+
+ PaperweightFakePlayer(ServerLevel world) {
+ super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
+ }
+
+ @Override
+ public Vec3 position() {
+ return ORIGIN;
+ }
+
+ @Override
+ public void tick() {
+ }
+
+ @Override
+ public void die(DamageSource damagesource) {
+ }
+
+ @Override
+ public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
+ return this;
+ }
+
+ @Override
+ public OptionalInt openMenu(MenuProvider factory) {
+ return OptionalInt.empty();
+ }
+
+ @Override
+ public void updateOptions(ServerboundClientInformationPacket packet) {
+ }
+
+ @Override
+ public void displayClientMessage(Component message, boolean actionBar) {
+ }
+
+ @Override
+ public void awardStat(Stat> stat, int amount) {
+ }
+
+ @Override
+ public void awardStat(Stat> stat) {
+ }
+
+ @Override
+ public boolean isInvulnerableTo(DamageSource damageSource) {
+ return true;
+ }
+
+ @Override
+ public void openTextEdit(SignBlockEntity sign) {
+ }
+}
diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightWorldNativeAccess.java
new file mode 100644
index 000000000..89ba0092b
--- /dev/null
+++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R2/PaperweightWorldNativeAccess.java
@@ -0,0 +1,183 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R2;
+
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.worldedit.bukkit.BukkitAdapter;
+import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
+import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
+import com.sk89q.worldedit.util.SideEffect;
+import com.sk89q.worldedit.util.SideEffectSet;
+import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
+import com.sk89q.worldedit.world.block.BlockState;
+import net.minecraft.core.BlockPos;
+import net.minecraft.nbt.Tag;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.chunk.LevelChunk;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
+import org.bukkit.event.block.BlockPhysicsEvent;
+
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+public class PaperweightWorldNativeAccess implements WorldNativeAccess {
+ private static final int UPDATE = 1;
+ private static final int NOTIFY = 2;
+
+ private final PaperweightAdapter adapter;
+ private final WeakReference world;
+ private SideEffectSet sideEffectSet;
+
+ public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference world) {
+ this.adapter = adapter;
+ this.world = world;
+ }
+
+ private ServerLevel getWorld() {
+ return Objects.requireNonNull(world.get(), "The reference to the world was lost");
+ }
+
+ @Override
+ public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
+ this.sideEffectSet = sideEffectSet;
+ }
+
+ @Override
+ public LevelChunk getChunk(int x, int z) {
+ return getWorld().getChunk(x, z);
+ }
+
+ @Override
+ public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) {
+ int stateId = BlockStateIdAccess.getBlockStateId(state);
+ return BlockStateIdAccess.isValidInternalId(stateId)
+ ? Block.stateById(stateId)
+ : ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
+ }
+
+ @Override
+ public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) {
+ return chunk.getBlockState(position);
+ }
+
+ @Nullable
+ @Override
+ public net.minecraft.world.level.block.state.BlockState setBlockState(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState state) {
+ return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE));
+ }
+
+ @Override
+ public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(net.minecraft.world.level.block.state.BlockState block, BlockPos position) {
+ return Block.updateFromNeighbourShapes(block, getWorld(), position);
+ }
+
+ @Override
+ public BlockPos getPosition(int x, int y, int z) {
+ return new BlockPos(x, y, z);
+ }
+
+ @Override
+ public void updateLightingForBlock(BlockPos position) {
+ getWorld().getChunkSource().getLightEngine().checkBlock(position);
+ }
+
+ @Override
+ public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) {
+ return false;
+ }
+
+ @Override
+ public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
+ if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
+ getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY);
+ }
+ }
+
+ @Override
+ public boolean isChunkTicking(LevelChunk chunk) {
+ return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
+ }
+
+ @Override
+ public void markBlockChanged(LevelChunk chunk, BlockPos position) {
+ if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
+ getWorld().getChunkSource().blockChanged(position);
+ }
+ }
+
+ @Override
+ public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
+ ServerLevel world = getWorld();
+ if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
+ world.updateNeighborsAt(pos, oldState.getBlock());
+ } else {
+ // When we don't want events, manually run the physics without them.
+ Block block = oldState.getBlock();
+ fireNeighborChanged(pos, world, block, pos.west());
+ fireNeighborChanged(pos, world, block, pos.east());
+ fireNeighborChanged(pos, world, block, pos.below());
+ fireNeighborChanged(pos, world, block, pos.above());
+ fireNeighborChanged(pos, world, block, pos.north());
+ fireNeighborChanged(pos, world, block, pos.south());
+ }
+ if (newState.hasAnalogOutputSignal()) {
+ world.updateNeighbourForOutputSignal(pos, newState.getBlock());
+ }
+ }
+
+ // Not sure why neighborChanged is deprecated
+ @SuppressWarnings("deprecation")
+ private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
+ world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
+ }
+
+ @Override
+ public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) {
+ ServerLevel world = getWorld();
+ // a == updateNeighbors
+ // b == updateDiagonalNeighbors
+ oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
+ if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
+ CraftWorld craftWorld = world.getWorld();
+ BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState));
+ world.getCraftServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return;
+ }
+ }
+ newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
+ newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
+ }
+
+ @Override
+ public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
+ getWorld().onBlockStateChange(pos, oldState, newState);
+ }
+
+ @Override
+ public void flush() {
+
+ }
+}
diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightBlockMaterial.java
new file mode 100644
index 000000000..69528e201
--- /dev/null
+++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightBlockMaterial.java
@@ -0,0 +1,189 @@
+package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
+
+import com.google.common.base.Suppliers;
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.util.ReflectionUtil;
+import com.sk89q.worldedit.bukkit.adapter.Refraction;
+import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.nbt.PaperweightLazyCompoundTag;
+import com.sk89q.worldedit.world.registry.BlockMaterial;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.EmptyBlockGetter;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.EntityBlock;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.state.BlockBehaviour;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.material.Material;
+import net.minecraft.world.level.material.PushReaction;
+import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
+
+public class PaperweightBlockMaterial implements BlockMaterial {
+
+ private final Block block;
+ private final BlockState blockState;
+ private final Material material;
+ private final boolean isTranslucent;
+ private final CraftBlockData craftBlockData;
+ private final org.bukkit.Material craftMaterial;
+ private final int opacity;
+ private final CompoundTag tile;
+
+ public PaperweightBlockMaterial(Block block) {
+ this(block, block.defaultBlockState());
+ }
+
+ public PaperweightBlockMaterial(Block block, BlockState blockState) {
+ this.block = block;
+ this.blockState = blockState;
+ this.material = blockState.getMaterial();
+ this.craftBlockData = CraftBlockData.fromData(blockState);
+ this.craftMaterial = craftBlockData.getMaterial();
+ BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block,
+ Refraction.pickName("properties", "aP"));
+ this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
+ Refraction.pickName("canOcclude", "n")
+ );
+ opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
+ BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
+ BlockPos.ZERO,
+ blockState
+ );
+ tile = tileEntity == null
+ ? null
+ : new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
+ }
+
+ public Block getBlock() {
+ return block;
+ }
+
+ public BlockState getState() {
+ return blockState;
+ }
+
+ public CraftBlockData getCraftBlockData() {
+ return craftBlockData;
+ }
+
+ public Material getMaterial() {
+ return material;
+ }
+
+ @Override
+ public boolean isAir() {
+ return blockState.isAir();
+ }
+
+ @Override
+ public boolean isFullCube() {
+ return craftMaterial.isOccluding();
+ }
+
+ @Override
+ public boolean isOpaque() {
+ return material.isSolidBlocking();
+ }
+
+ @Override
+ public boolean isPowerSource() {
+ return blockState.isSignalSource();
+ }
+
+ @Override
+ public boolean isLiquid() {
+ return material.isLiquid();
+ }
+
+ @Override
+ public boolean isSolid() {
+ return material.isSolid();
+ }
+
+ @Override
+ public float getHardness() {
+ return craftBlockData.getState().destroySpeed;
+ }
+
+ @Override
+ public float getResistance() {
+ return block.getExplosionResistance();
+ }
+
+ @Override
+ public float getSlipperiness() {
+ return block.getFriction();
+ }
+
+ @Override
+ public int getLightValue() {
+ return blockState.getLightEmission();
+ }
+
+ @Override
+ public int getLightOpacity() {
+ return opacity;
+ }
+
+ @Override
+ public boolean isFragileWhenPushed() {
+ return material.getPushReaction() == PushReaction.DESTROY;
+ }
+
+ @Override
+ public boolean isUnpushable() {
+ return material.getPushReaction() == PushReaction.BLOCK;
+ }
+
+ @Override
+ public boolean isTicksRandomly() {
+ return block.isRandomlyTicking(blockState);
+ }
+
+ @Override
+ public boolean isMovementBlocker() {
+ return material.isSolid();
+ }
+
+ @Override
+ public boolean isBurnable() {
+ return material.isFlammable();
+ }
+
+ @Override
+ public boolean isToolRequired() {
+ // Removed in 1.16.1, this is not present in higher versions
+ return false;
+ }
+
+ @Override
+ public boolean isReplacedDuringPlacement() {
+ return material.isReplaceable();
+ }
+
+ @Override
+ public boolean isTranslucent() {
+ return isTranslucent;
+ }
+
+ @Override
+ public boolean hasContainer() {
+ return block instanceof EntityBlock;
+ }
+
+ @Override
+ public boolean isTile() {
+ return block instanceof EntityBlock;
+ }
+
+ @Override
+ public CompoundTag getDefaultTile() {
+ return tile;
+ }
+
+ @Override
+ public int getMapColor() {
+ // rgb field
+ return material.getColor().col;
+ }
+
+}
diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java
new file mode 100644
index 000000000..af0bcfa32
--- /dev/null
+++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java
@@ -0,0 +1,699 @@
+package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
+
+import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
+import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
+import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
+import com.fastasyncworldedit.core.FaweCache;
+import com.fastasyncworldedit.core.entity.LazyBaseEntity;
+import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
+import com.fastasyncworldedit.core.queue.IBatchProcessor;
+import com.fastasyncworldedit.core.queue.IChunkGet;
+import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
+import com.fastasyncworldedit.core.util.NbtUtils;
+import com.fastasyncworldedit.core.util.TaskManager;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.sk89q.jnbt.Tag;
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.blocks.TileEntityBlock;
+import com.sk89q.worldedit.bukkit.BukkitAdapter;
+import com.sk89q.worldedit.bukkit.BukkitWorld;
+import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
+import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R2.PaperweightAdapter;
+import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.nbt.PaperweightLazyCompoundTag;
+import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.regen.PaperweightRegen;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
+import com.sk89q.worldedit.internal.util.LogManagerCompat;
+import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.registry.state.BooleanProperty;
+import com.sk89q.worldedit.registry.state.DirectionalProperty;
+import com.sk89q.worldedit.registry.state.EnumProperty;
+import com.sk89q.worldedit.registry.state.IntegerProperty;
+import com.sk89q.worldedit.registry.state.Property;
+import com.sk89q.worldedit.util.Direction;
+import com.sk89q.worldedit.util.SideEffect;
+import com.sk89q.worldedit.util.SideEffectSet;
+import com.sk89q.worldedit.util.TreeGenerator;
+import com.sk89q.worldedit.util.formatting.text.Component;
+import com.sk89q.worldedit.util.nbt.BinaryTag;
+import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
+import com.sk89q.worldedit.util.nbt.StringBinaryTag;
+import com.sk89q.worldedit.world.RegenOptions;
+import com.sk89q.worldedit.world.biome.BiomeType;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import com.sk89q.worldedit.world.block.BlockState;
+import com.sk89q.worldedit.world.block.BlockStateHolder;
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import com.sk89q.worldedit.world.block.BlockTypesCache;
+import com.sk89q.worldedit.world.entity.EntityType;
+import com.sk89q.worldedit.world.item.ItemType;
+import com.sk89q.worldedit.world.registry.BlockMaterial;
+import io.papermc.lib.PaperLib;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Registry;
+import net.minecraft.core.WritableRegistry;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.nbt.IntTag;
+import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.dedicated.DedicatedServer;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.util.StringRepresentable;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.state.properties.BlockStateProperties;
+import net.minecraft.world.level.block.state.properties.DirectionProperty;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+import org.apache.logging.log4j.Logger;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.TreeType;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.craftbukkit.v1_19_R2.CraftChunk;
+import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlockState;
+import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_19_R2.util.CraftNamespacedKey;
+import org.bukkit.entity.Player;
+
+import javax.annotation.Nullable;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.OptionalInt;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static net.minecraft.core.registries.Registries.BIOME;
+
+public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
+ IDelegateBukkitImplAdapter {
+
+ private static final Logger LOGGER = LogManagerCompat.getLogger();
+ private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
+
+ static {
+ try {
+ CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
+ } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
+ }
+ }
+
+ private final PaperweightAdapter parent;
+ // ------------------------------------------------------------------------
+ // Code that may break between versions of Minecraft
+ // ------------------------------------------------------------------------
+ private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
+ private char[] ibdToStateOrdinal = null;
+ private int[] ordinalToIbdID = null;
+ private boolean initialised = false;
+ private Map>> allBlockProperties = null;
+
+ public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
+ this.parent = new PaperweightAdapter();
+ }
+
+ @Nullable
+ private static String getEntityId(Entity entity) {
+ ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
+ return resourceLocation == null ? null : resourceLocation.toString();
+ }
+
+ private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) {
+ entity.save(compoundTag);
+ }
+
+ @Override
+ public BukkitImplAdapter getParent() {
+ return parent;
+ }
+
+ private synchronized boolean init() {
+ if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) {
+ return false;
+ }
+ ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size
+ ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size
+ for (int i = 0; i < ibdToStateOrdinal.length; i++) {
+ BlockState blockState = BlockTypesCache.states[i];
+ PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial();
+ int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState());
+ char ordinal = blockState.getOrdinalChar();
+ ibdToStateOrdinal[id] = ordinal;
+ ordinalToIbdID[ordinal] = id;
+ }
+ Map>> properties = new HashMap<>();
+ try {
+ for (Field field : BlockStateProperties.class.getDeclaredFields()) {
+ Object obj = field.get(null);
+ if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property> state)) {
+ continue;
+ }
+ Property> property;
+ if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) {
+ property = new BooleanProperty(
+ state.getName(),
+ (List) ImmutableList.copyOf(state.getPossibleValues())
+ );
+ } else if (state instanceof DirectionProperty) {
+ property = new DirectionalProperty(
+ state.getName(),
+ state
+ .getPossibleValues()
+ .stream()
+ .map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase()))
+ .collect(Collectors.toList())
+ );
+ } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
+ property = new EnumProperty(
+ state.getName(),
+ state
+ .getPossibleValues()
+ .stream()
+ .map(e -> ((StringRepresentable) e).getSerializedName())
+ .collect(Collectors.toList())
+ );
+ } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) {
+ property = new IntegerProperty(
+ state.getName(),
+ (List) ImmutableList.copyOf(state.getPossibleValues())
+ );
+ } else {
+ throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state
+ .getClass()
+ .getSimpleName());
+ }
+ properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> {
+ if (v == null) {
+ v = new ArrayList<>(Collections.singletonList(property));
+ } else {
+ v.add(property);
+ }
+ return v;
+ });
+ }
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } finally {
+ allBlockProperties = ImmutableMap.copyOf(properties);
+ }
+ initialised = true;
+ return true;
+ }
+
+ @Override
+ public BlockMaterial getMaterial(BlockType blockType) {
+ Block block = getBlock(blockType);
+ return new PaperweightBlockMaterial(block);
+ }
+
+ @Override
+ public synchronized BlockMaterial getMaterial(BlockState state) {
+ net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
+ return new PaperweightBlockMaterial(blockState.getBlock(), blockState);
+ }
+
+ public Block getBlock(BlockType blockType) {
+ return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK)
+ .get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
+ }
+
+ @Deprecated
+ @Override
+ public BlockState getBlock(Location location) {
+ Preconditions.checkNotNull(location);
+
+ CraftWorld craftWorld = ((CraftWorld) location.getWorld());
+ int x = location.getBlockX();
+ int y = location.getBlockY();
+ int z = location.getBlockZ();
+ final ServerLevel handle = craftWorld.getHandle();
+ LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
+ final BlockPos blockPos = new BlockPos(x, y, z);
+ final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
+ BlockState state = adapt(blockData);
+ if (state == null) {
+ org.bukkit.block.Block bukkitBlock = location.getBlock();
+ state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
+ }
+ return state;
+ }
+
+ @Override
+ public BaseBlock getFullBlock(final Location location) {
+ Preconditions.checkNotNull(location);
+
+ CraftWorld craftWorld = ((CraftWorld) location.getWorld());
+ int x = location.getBlockX();
+ int y = location.getBlockY();
+ int z = location.getBlockZ();
+
+ final ServerLevel handle = craftWorld.getHandle();
+ LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
+ final BlockPos blockPos = new BlockPos(x, y, z);
+ final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
+ BlockState state = adapt(blockData);
+ if (state == null) {
+ org.bukkit.block.Block bukkitBlock = location.getBlock();
+ state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
+ }
+ if (state.getBlockType().getMaterial().hasContainer()) {
+
+ // Read the NBT data
+ BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
+ if (blockEntity != null) {
+ net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
+ return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
+ }
+ }
+
+ return state.toBaseBlock();
+ }
+
+ @Override
+ public Set getSupportedSideEffects() {
+ return SideEffectSet.defaults().getSideEffectsToApply();
+ }
+
+ public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) {
+ CraftChunk craftChunk = (CraftChunk) chunk;
+ LevelChunk levelChunk = craftChunk.getHandle();
+ Level level = levelChunk.getLevel();
+
+ BlockPos blockPos = new BlockPos(x, y, z);
+ net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState();
+ LevelChunkSection[] levelChunkSections = levelChunk.getSections();
+ int y4 = y >> 4;
+ LevelChunkSection section = levelChunkSections[y4];
+
+ net.minecraft.world.level.block.state.BlockState existing;
+ if (section == null) {
+ existing = ((PaperweightBlockMaterial) BlockTypes.AIR.getDefaultState().getMaterial()).getState();
+ } else {
+ existing = section.getBlockState(x & 15, y & 15, z & 15);
+ }
+
+ levelChunk.removeBlockEntity(blockPos); // Force delete the old tile entity
+
+ CompoundBinaryTag compoundTag = state instanceof BaseBlock ? state.getNbt() : null;
+ if (compoundTag != null || existing instanceof TileEntityBlock) {
+ level.setBlock(blockPos, blockState, 0);
+ // remove tile
+ if (compoundTag != null) {
+ // We will assume that the tile entity was created for us,
+ // though we do not do this on the Forge version
+ BlockEntity blockEntity = level.getBlockEntity(blockPos);
+ if (blockEntity != null) {
+ net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(compoundTag);
+ tag.put("x", IntTag.valueOf(x));
+ tag.put("y", IntTag.valueOf(y));
+ tag.put("z", IntTag.valueOf(z));
+ blockEntity.load(tag); // readTagIntoTileEntity - load data
+ }
+ }
+ } else {
+ if (existing == blockState) {
+ return true;
+ }
+ levelChunk.setBlockState(blockPos, blockState, false);
+ }
+ if (update) {
+ level.getMinecraftWorld().sendBlockUpdated(blockPos, existing, blockState, 0);
+ }
+ return true;
+ }
+
+ @Override
+ public WorldNativeAccess, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
+ return new PaperweightFaweWorldNativeAccess(
+ this,
+ new WeakReference<>(((CraftWorld) world).getHandle())
+ );
+ }
+
+ @Override
+ public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
+ Preconditions.checkNotNull(entity);
+
+ CraftEntity craftEntity = ((CraftEntity) entity);
+ Entity mcEntity = craftEntity.getHandle();
+
+ String id = getEntityId(mcEntity);
+
+ if (id != null) {
+ EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
+ Supplier saveTag = () -> {
+ final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag();
+ readEntityIntoTag(mcEntity, minecraftTag);
+ //add Id for AbstractChangeSet to work
+ final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag);
+ final Map tags = NbtUtils.getCompoundBinaryTagValues(tag);
+ tags.put("Id", StringBinaryTag.of(id));
+ return CompoundBinaryTag.from(tags);
+ };
+ return new LazyBaseEntity(type, saveTag);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Component getRichBlockName(BlockType blockType) {
+ return parent.getRichBlockName(blockType);
+ }
+
+ @Override
+ public Component getRichItemName(ItemType itemType) {
+ return parent.getRichItemName(itemType);
+ }
+
+ @Override
+ public Component getRichItemName(BaseItemStack itemStack) {
+ return parent.getRichItemName(itemStack);
+ }
+
+ @Override
+ public OptionalInt getInternalBlockStateId(BlockState state) {
+ PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
+ net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState();
+ return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState));
+ }
+
+ @Override
+ public BlockState adapt(BlockData blockData) {
+ CraftBlockData cbd = ((CraftBlockData) blockData);
+ net.minecraft.world.level.block.state.BlockState ibd = cbd.getState();
+ return adapt(ibd);
+ }
+
+ public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
+ return BlockTypesCache.states[adaptToChar(blockState)];
+ }
+
+ public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) {
+ int id = Block.BLOCK_STATE_REGISTRY.getId(blockState);
+ if (initialised) {
+ return ibdToStateOrdinal[id];
+ }
+ synchronized (this) {
+ if (initialised) {
+ return ibdToStateOrdinal[id];
+ }
+ try {
+ init();
+ return ibdToStateOrdinal[id];
+ } catch (ArrayIndexOutOfBoundsException e1) {
+ LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!",
+ blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1
+ );
+ return BlockTypesCache.ReservedIDs.AIR;
+ }
+ }
+ }
+
+ public char ibdIDToOrdinal(int id) {
+ if (initialised) {
+ return ibdToStateOrdinal[id];
+ }
+ synchronized (this) {
+ if (initialised) {
+ return ibdToStateOrdinal[id];
+ }
+ init();
+ return ibdToStateOrdinal[id];
+ }
+ }
+
+ @Override
+ public char[] getIbdToStateOrdinal() {
+ if (initialised) {
+ return ibdToStateOrdinal;
+ }
+ synchronized (this) {
+ if (initialised) {
+ return ibdToStateOrdinal;
+ }
+ init();
+ return ibdToStateOrdinal;
+ }
+ }
+
+ public int ordinalToIbdID(char ordinal) {
+ if (initialised) {
+ return ordinalToIbdID[ordinal];
+ }
+ synchronized (this) {
+ if (initialised) {
+ return ordinalToIbdID[ordinal];
+ }
+ init();
+ return ordinalToIbdID[ordinal];
+ }
+ }
+
+ @Override
+ public int[] getOrdinalToIbdID() {
+ if (initialised) {
+ return ordinalToIbdID;
+ }
+ synchronized (this) {
+ if (initialised) {
+ return ordinalToIbdID;
+ }
+ init();
+ return ordinalToIbdID;
+ }
+ }
+
+ @Override
+ public > BlockData adapt(B state) {
+ PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
+ return material.getCraftBlockData();
+ }
+
+ @Override
+ public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
+ ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
+ ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
+ if (map != null && wasAccessibleSinceLastSave(map)) {
+ boolean flag = false;
+ // PlayerChunk.d players = map.players;
+ Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
+ */ Stream.empty();
+
+ ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
+ stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
+ .forEach(entityPlayer -> {
+ synchronized (chunkPacket) {
+ ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket();
+ if (nmsPacket == null) {
+ nmsPacket = mapUtil.create(this, chunkPacket);
+ chunkPacket.setNativePacket(nmsPacket);
+ }
+ try {
+ FaweCache.INSTANCE.CHUNK_FLAG.get().set(true);
+ entityPlayer.connection.send(nmsPacket);
+ } finally {
+ FaweCache.INSTANCE.CHUNK_FLAG.get().set(false);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public Map> getProperties(BlockType blockType) {
+ return getParent().getProperties(blockType);
+ }
+
+ @Override
+ public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) {
+ int internalId = BlockStateIdAccess.getBlockStateId(blockState);
+ net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
+ return blockState1.hasPostProcess(
+ ((CraftWorld) world).getHandle(),
+ new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
+ );
+ }
+
+ @Override
+ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
+ ItemStack stack = new ItemStack(
+ DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM)
+ .get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
+ baseItemStack.getAmount()
+ );
+ stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
+ return CraftItemStack.asCraftMirror(stack);
+ }
+
+ @Override
+ public boolean generateTree(
+ TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
+ org.bukkit.World bukkitWorld
+ ) {
+ TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
+ if (bukkitType == TreeType.CHORUS_PLANT) {
+ blockVector3 = blockVector3.add(
+ 0,
+ 1,
+ 0
+ ); // bukkit skips the feature gen which does this offset normally, so we have to add it back
+ }
+ ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
+ final BlockVector3 finalBlockVector = blockVector3;
+ // Sync to main thread to ensure no clashes occur
+ Map placed = TaskManager.taskManager().sync(() -> {
+ serverLevel.captureTreeGeneration = true;
+ serverLevel.captureBlockStates = true;
+ try {
+ if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
+ return null;
+ }
+ return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
+ } finally {
+ serverLevel.captureBlockStates = false;
+ serverLevel.captureTreeGeneration = false;
+ serverLevel.capturedBlockStates.clear();
+ }
+ });
+ if (placed == null || placed.isEmpty()) {
+ return false;
+ }
+ for (CraftBlockState craftBlockState : placed.values()) {
+ if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
+ continue;
+ }
+ editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
+ BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
+ );
+ }
+ return true;
+ }
+
+ @Override
+ public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
+ final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
+ final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount());
+ weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag())));
+ return weStack;
+ }
+
+ @Override
+ public Tag toNative(net.minecraft.nbt.Tag foreign) {
+ return parent.toNative(foreign);
+ }
+
+ @Override
+ public net.minecraft.nbt.Tag fromNative(Tag foreign) {
+ if (foreign instanceof PaperweightLazyCompoundTag) {
+ return ((PaperweightLazyCompoundTag) foreign).get();
+ }
+ return parent.fromNative(foreign);
+ }
+
+ @Override
+ public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
+ return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
+ }
+
+ @Override
+ public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) {
+ return new PaperweightGetBlocks(world, chunkX, chunkZ);
+ }
+
+ @Override
+ public int getInternalBiomeId(BiomeType biomeType) {
+ final Registry registry = MinecraftServer
+ .getServer()
+ .registryAccess()
+ .registryOrThrow(BIOME);
+ ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
+ Biome biome = registry.get(resourceLocation);
+ return registry.getId(biome);
+ }
+
+ @Override
+ public Iterable getRegisteredBiomes() {
+ WritableRegistry biomeRegistry = (WritableRegistry) ((CraftServer) Bukkit.getServer())
+ .getServer()
+ .registryAccess()
+ .registryOrThrow(BIOME);
+ return biomeRegistry.stream()
+ .map(biomeRegistry::getKey).filter(Objects::nonNull)
+ .map(CraftNamespacedKey::fromMinecraft)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public RelighterFactory getRelighterFactory() {
+ if (PaperLib.isPaper()) {
+ return new PaperweightStarlightRelighterFactory();
+ } else {
+ return new NMSRelighterFactory();
+ }
+ }
+
+ @Override
+ public Map>> getAllProperties() {
+ if (initialised) {
+ return allBlockProperties;
+ }
+ synchronized (this) {
+ if (initialised) {
+ return allBlockProperties;
+ }
+ init();
+ return allBlockProperties;
+ }
+ }
+
+ @Override
+ public IBatchProcessor getTickingPostProcessor() {
+ return new PaperweightPostProcessor();
+ }
+
+ private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
+ if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
+ try {
+ return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
+ } catch (IllegalAccessException | InvocationTargetException ignored) {
+ // fall-through
+ }
+ }
+ // Papers new chunk system has no related replacement - therefor we assume true.
+ return true;
+ }
+
+}
diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweWorldNativeAccess.java
new file mode 100644
index 000000000..e5c26aba5
--- /dev/null
+++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweWorldNativeAccess.java
@@ -0,0 +1,286 @@
+package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
+
+import com.fastasyncworldedit.core.Fawe;
+import com.fastasyncworldedit.core.math.IntPair;
+import com.fastasyncworldedit.core.util.TaskManager;
+import com.fastasyncworldedit.core.util.task.RunnableVal;
+import com.sk89q.worldedit.bukkit.BukkitAdapter;
+import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
+import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
+import com.sk89q.worldedit.util.SideEffect;
+import com.sk89q.worldedit.util.SideEffectSet;
+import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
+import com.sk89q.worldedit.world.block.BlockState;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ServerChunkCache;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.chunk.LevelChunk;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
+import org.bukkit.event.block.BlockPhysicsEvent;
+
+import javax.annotation.Nullable;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess {
+
+ private static final int UPDATE = 1;
+ private static final int NOTIFY = 2;
+ private static final Direction[] NEIGHBOUR_ORDER = {
+ Direction.EAST,
+ Direction.WEST,
+ Direction.DOWN,
+ Direction.UP,
+ Direction.NORTH,
+ Direction.SOUTH
+ };
+ private final PaperweightFaweAdapter paperweightFaweAdapter;
+ private final WeakReference level;
+ private final AtomicInteger lastTick;
+ private final Set cachedChanges = new HashSet<>();
+ private final Set cachedChunksToSend = new HashSet<>();
+ private SideEffectSet sideEffectSet;
+
+ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference level) {
+ this.paperweightFaweAdapter = paperweightFaweAdapter;
+ this.level = level;
+ // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
+ // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
+ this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
+ }
+
+ private Level getLevel() {
+ return Objects.requireNonNull(level.get(), "The reference to the world was lost");
+ }
+
+ @Override
+ public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
+ this.sideEffectSet = sideEffectSet;
+ }
+
+ @Override
+ public LevelChunk getChunk(int x, int z) {
+ return getLevel().getChunk(x, z);
+ }
+
+ @Override
+ public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) {
+ int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar());
+ return BlockStateIdAccess.isValidInternalId(stateId)
+ ? Block.stateById(stateId)
+ : ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState();
+ }
+
+ @Override
+ public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) {
+ return levelChunk.getBlockState(blockPos);
+ }
+
+ @Nullable
+ @Override
+ public synchronized net.minecraft.world.level.block.state.BlockState setBlockState(
+ LevelChunk levelChunk, BlockPos blockPos,
+ net.minecraft.world.level.block.state.BlockState blockState
+ ) {
+ int currentTick = MinecraftServer.currentTick;
+ if (Fawe.isMainThread()) {
+ return levelChunk.setBlockState(blockPos, blockState,
+ this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
+ );
+ }
+ // Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
+ cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
+ cachedChunksToSend.add(new IntPair(levelChunk.bukkitChunk.getX(), levelChunk.bukkitChunk.getZ()));
+ boolean nextTick = lastTick.get() > currentTick;
+ if (nextTick || cachedChanges.size() >= 1024) {
+ if (nextTick) {
+ lastTick.set(currentTick);
+ }
+ flushAsync(nextTick);
+ }
+ return blockState;
+ }
+
+ @Override
+ public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
+ net.minecraft.world.level.block.state.BlockState blockState,
+ BlockPos blockPos
+ ) {
+ return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos);
+ }
+
+ @Override
+ public BlockPos getPosition(int x, int y, int z) {
+ return new BlockPos(x, y, z);
+ }
+
+ @Override
+ public void updateLightingForBlock(BlockPos blockPos) {
+ getLevel().getChunkSource().getLightEngine().checkBlock(blockPos);
+ }
+
+ @Override
+ public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) {
+ // We will assume that the tile entity was created for us,
+ // though we do not do this on the other versions
+ BlockEntity blockEntity = getLevel().getBlockEntity(blockPos);
+ if (blockEntity == null) {
+ return false;
+ }
+ net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeBinary(tag);
+ blockEntity.load((CompoundTag) nativeTag);
+ return true;
+ }
+
+ @Override
+ public void notifyBlockUpdate(
+ LevelChunk levelChunk, BlockPos blockPos,
+ net.minecraft.world.level.block.state.BlockState oldState,
+ net.minecraft.world.level.block.state.BlockState newState
+ ) {
+ if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
+ getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY);
+ }
+ }
+
+ @Override
+ public boolean isChunkTicking(LevelChunk levelChunk) {
+ return levelChunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
+ }
+
+ @Override
+ public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) {
+ if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
+ ((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos);
+ }
+ }
+
+ @Override
+ public void notifyNeighbors(
+ BlockPos blockPos,
+ net.minecraft.world.level.block.state.BlockState oldState,
+ net.minecraft.world.level.block.state.BlockState newState
+ ) {
+ Level level = getLevel();
+ if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
+ level.blockUpdated(blockPos, oldState.getBlock());
+ } else {
+ // When we don't want events, manually run the physics without them.
+ // Un-nest neighbour updating
+ for (Direction direction : NEIGHBOUR_ORDER) {
+ BlockPos shifted = blockPos.relative(direction);
+ level.getBlockState(shifted).neighborChanged(level, shifted, oldState.getBlock(), blockPos, false);
+ }
+ }
+ if (newState.hasAnalogOutputSignal()) {
+ level.updateNeighbourForOutputSignal(blockPos, newState.getBlock());
+ }
+ }
+
+ @Override
+ public void updateNeighbors(
+ BlockPos blockPos,
+ net.minecraft.world.level.block.state.BlockState oldState,
+ net.minecraft.world.level.block.state.BlockState newState,
+ int recursionLimit
+ ) {
+ Level level = getLevel();
+ // a == updateNeighbors
+ // b == updateDiagonalNeighbors
+ oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
+ if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
+ CraftWorld craftWorld = level.getWorld();
+ if (craftWorld != null) {
+ BlockPhysicsEvent event = new BlockPhysicsEvent(
+ craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
+ CraftBlockData.fromData(newState)
+ );
+ level.getCraftServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return;
+ }
+ }
+ }
+ newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit);
+ newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
+ }
+
+ @Override
+ public void onBlockStateChange(
+ BlockPos blockPos,
+ net.minecraft.world.level.block.state.BlockState oldState,
+ net.minecraft.world.level.block.state.BlockState newState
+ ) {
+ getLevel().onBlockStateChange(blockPos, oldState, newState);
+ }
+
+ private synchronized void flushAsync(final boolean sendChunks) {
+ final Set changes = Set.copyOf(cachedChanges);
+ cachedChanges.clear();
+ final Set toSend;
+ if (sendChunks) {
+ toSend = Set.copyOf(cachedChunksToSend);
+ cachedChunksToSend.clear();
+ } else {
+ toSend = Collections.emptySet();
+ }
+ RunnableVal