geforkt von Mirrors/FastAsyncWorldEdit
Update FaWe #7
@ -342,7 +342,7 @@ ij_editorconfig_space_before_colon = false
|
||||
ij_editorconfig_space_before_comma = false
|
||||
ij_editorconfig_spaces_around_assignment_operators = true
|
||||
|
||||
[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.pom, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul}]
|
||||
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
|
||||
ij_xml_align_attributes = true
|
||||
ij_xml_align_text = false
|
||||
ij_xml_attribute_wrap = normal
|
||||
@ -360,7 +360,7 @@ ij_xml_space_around_equals_in_attribute = false
|
||||
ij_xml_space_inside_empty_tag = false
|
||||
ij_xml_text_wrap = normal
|
||||
|
||||
[{*.ats, *.ts}]
|
||||
[{*.ats,*.ts}]
|
||||
ij_continuation_indent_size = 4
|
||||
ij_typescript_align_imports = false
|
||||
ij_typescript_align_multiline_array_initializer_expression = false
|
||||
@ -528,7 +528,7 @@ ij_typescript_while_brace_force = never
|
||||
ij_typescript_while_on_new_line = false
|
||||
ij_typescript_wrap_comments = false
|
||||
|
||||
[{*.bash, *.sh, *.zsh}]
|
||||
[{*.bash,*.sh,*.zsh}]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_shell_binary_ops_start_line = false
|
||||
@ -537,7 +537,7 @@ ij_shell_minify_program = false
|
||||
ij_shell_redirect_followed_by_space = false
|
||||
ij_shell_switch_cases_indented = false
|
||||
|
||||
[{*.cjs, *.js}]
|
||||
[{*.cjs,*.js}]
|
||||
ij_continuation_indent_size = 4
|
||||
ij_javascript_align_imports = false
|
||||
ij_javascript_align_multiline_array_initializer_expression = false
|
||||
@ -702,10 +702,10 @@ ij_javascript_while_brace_force = never
|
||||
ij_javascript_while_on_new_line = false
|
||||
ij_javascript_wrap_comments = false
|
||||
|
||||
[{*.ft, *.vm, *.vsl}]
|
||||
[{*.ft,*.vm,*.vsl}]
|
||||
ij_vtl_keep_indents_on_empty_lines = false
|
||||
|
||||
[{*.gant, *.gradle, *.groovy, *.gy}]
|
||||
[{*.gant,*.gradle,*.groovy,*.gy}]
|
||||
ij_groovy_align_group_field_declarations = false
|
||||
ij_groovy_align_multiline_array_initializer_expression = false
|
||||
ij_groovy_align_multiline_assignment = false
|
||||
@ -884,7 +884,7 @@ ij_groovy_while_brace_force = never
|
||||
ij_groovy_while_on_new_line = false
|
||||
ij_groovy_wrap_long_lines = false
|
||||
|
||||
[{*.gradle.kts, *.kt, *.kts, *.main.kts}]
|
||||
[{*.gradle.kts,*.kt,*.kts,*.main.kts}]
|
||||
ij_kotlin_align_in_columns_case_branch = false
|
||||
ij_kotlin_align_multiline_binary_operation = false
|
||||
ij_kotlin_align_multiline_extends_list = false
|
||||
@ -963,7 +963,7 @@ ij_kotlin_wrap_elvis_expressions = 1
|
||||
ij_kotlin_wrap_expression_body_functions = 0
|
||||
ij_kotlin_wrap_first_method_in_call_chain = false
|
||||
|
||||
[{*.har, *.jsb2, *.jsb3, *.json, .babelrc, .eslintrc, .stylelintrc, bowerrc, jest.config, mcmod.info}]
|
||||
[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config,mcmod.info}]
|
||||
indent_size = 2
|
||||
ij_json_keep_blank_lines_in_code = 0
|
||||
ij_json_keep_indents_on_empty_lines = false
|
||||
@ -976,7 +976,7 @@ ij_json_spaces_within_braces = false
|
||||
ij_json_spaces_within_brackets = false
|
||||
ij_json_wrap_long_lines = false
|
||||
|
||||
[{*.htm, *.html, *.sht, *.shtm, *.shtml}]
|
||||
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
|
||||
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
|
||||
ij_html_align_attributes = true
|
||||
ij_html_align_text = false
|
||||
@ -1004,7 +1004,7 @@ ij_html_space_inside_empty_tag = false
|
||||
ij_html_text_wrap = normal
|
||||
ij_html_uniform_ident = false
|
||||
|
||||
[{*.yaml, *.yml}]
|
||||
[{*.yaml,*.yml}]
|
||||
indent_size = 2
|
||||
ij_yaml_keep_indents_on_empty_lines = false
|
||||
ij_yaml_keep_line_breaks = true
|
||||
|
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -27,11 +27,11 @@ 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.20.1'
|
||||
- '1.20'
|
||||
- '1.19.4'
|
||||
- '1.18.2'
|
||||
- '1.17.1'
|
||||
- '1.21.3'
|
||||
- '1.21.1'
|
||||
- '1.20.6'
|
||||
- '1.20.4'
|
||||
- '1.20.2'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
46
.github/renovate.json
vendored
46
.github/renovate.json
vendored
@ -1,11 +1,12 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base",
|
||||
":semanticCommitsDisabled"
|
||||
"$schema" : "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends" : [
|
||||
"config:recommended",
|
||||
":semanticCommitsDisabled",
|
||||
"schedule:earlyMondays"
|
||||
],
|
||||
"automerge": true,
|
||||
"ignoreDeps": [
|
||||
"automerge" : true,
|
||||
"ignoreDeps" : [
|
||||
"guava",
|
||||
"com.google.guava:guava",
|
||||
"rhino-runtime",
|
||||
@ -29,7 +30,34 @@
|
||||
"org.spongepowered:spongeapi",
|
||||
"org.yaml:snakeyaml"
|
||||
],
|
||||
"labels": ["Renovate"],
|
||||
"rebaseWhen": "conflicted",
|
||||
"schedule": ["on the first day of the month"]
|
||||
"labels" : [
|
||||
"Renovate"
|
||||
],
|
||||
"rebaseWhen" : "conflicted",
|
||||
"customManagers" : [
|
||||
{
|
||||
"customType" : "regex",
|
||||
"datasourceTemplate" : "custom.paperweight-userdev",
|
||||
"fileMatch" : "^worldedit-bukkit\\/adapters\\/adapter-\\d+_\\d+(_\\d+)?\\/build\\.gradle\\.kts$",
|
||||
"matchStrings" : [
|
||||
"url=(?<registryUrl>.*)\\s",
|
||||
"paperDevBundle\\(\"(?<currentValue>.*?)\"\\)\\s"
|
||||
],
|
||||
"matchStringsStrategy": "combination",
|
||||
"depNameTemplate" : "paperweight-userdev",
|
||||
"extractVersionTemplate" : "(?<version>\\d+\\.\\d+\\.?\\d*-R0\\.1-\\d+\\.\\d+-\\d+)"
|
||||
}
|
||||
],
|
||||
"customDatasources" : {
|
||||
"paperweight-userdev": {
|
||||
"defaultRegistryUrlTemplate": "",
|
||||
"format": "html"
|
||||
}
|
||||
},
|
||||
"packageRules" : [
|
||||
{
|
||||
"matchDatasources" : ["custom.paperweight-userdev"],
|
||||
"versioning": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)?-R0\\.1-\\d+\\d+\\.\\d+-(?<build>\\d+)$"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -24,5 +24,5 @@ jobs:
|
||||
""
|
||||
"The download is available at:"
|
||||
"- Spigot: <https://www.spigotmc.org/resources/13932/>"
|
||||
"- Modrinth: <https://modrinth.com/plugin/fastasyncworldedit/version/${{ github.event.release.tag_name }}>"
|
||||
"- Modrinth: <https://modrinth.com/plugin/fastasyncworldedit>"
|
||||
"- CurseForge: <https://www.curseforge.com/minecraft/bukkit-plugins/fawe>"
|
||||
|
10
.github/workflows/build-pr.yml
vendored
10
.github/workflows/build-pr.yml
vendored
@ -9,19 +9,19 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
uses: gradle/actions/wrapper-validation@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
- name: Build on ${{ matrix.os }}
|
||||
run: ./gradlew build -s
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: FastAsyncWorldEdit-SNAPSHOT
|
||||
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar
|
||||
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-*.jar
|
||||
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -9,15 +9,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
uses: gradle/actions/wrapper-validation@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
- name: Clean Build
|
||||
run: ./gradlew clean build --no-daemon
|
||||
- name: Determine release status
|
||||
@ -70,9 +70,9 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: FastAsyncWorldEdit-Bukkit-SNAPSHOT
|
||||
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar
|
||||
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-*.jar
|
||||
- name: Publish to Modrinth
|
||||
if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
|
||||
run: ./gradlew modrinth
|
||||
run: ./gradlew publishMods
|
||||
env:
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
|
12
.github/workflows/codeql.yml
vendored
12
.github/workflows/codeql.yml
vendored
@ -19,18 +19,18 @@ jobs:
|
||||
language: ['java']
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
23
.github/workflows/label-merge-conflicts.yaml
vendored
Normale Datei
23
.github/workflows/label-merge-conflicts.yaml
vendored
Normale Datei
@ -0,0 +1,23 @@
|
||||
name: "Label conflicting PRs"
|
||||
on:
|
||||
push:
|
||||
pull_request_target:
|
||||
types: [ synchronize ]
|
||||
pull_request:
|
||||
types: [ synchronize ]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
main:
|
||||
if: github.event.pull_request.user.login != 'dependabot[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Label conflicting PRs
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.2
|
||||
with:
|
||||
dirtyLabel: "unresolved-merge-conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: "Please take a moment and address the merge conflicts of your pull request. Thanks!"
|
||||
continueOnMissingPermissions: true
|
2
.github/workflows/release-drafter.yml
vendored
2
.github/workflows/release-drafter.yml
vendored
@ -12,6 +12,6 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
12
.github/workflows/upload-release-assets.yml
vendored
12
.github/workflows/upload-release-assets.yml
vendored
@ -7,20 +7,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
uses: gradle/actions/wrapper-validation@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
- name: Clean Build
|
||||
run: ./gradlew clean build --no-daemon
|
||||
- name: Upload Release Assets
|
||||
uses: AButler/upload-release-assets@v2.0
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
with:
|
||||
files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar'
|
||||
files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-*.jar'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release-tag: ${{ github.event.release.tag_name }}
|
||||
|
@ -3,12 +3,12 @@
|
||||
|
||||
= Compiling
|
||||
|
||||
You can compile FastAsyncWorldEdit as long as you have some version of Java greater than or equal to 17 installed. Gradle will download JDK 17 specifically if needed,
|
||||
You can compile FastAsyncWorldEdit as long as you have some version of Java greater than or equal to 21 installed. Gradle will download JDK 21 specifically if needed,
|
||||
but it needs some version of Java to bootstrap from.
|
||||
|
||||
Note that if you have JRE 8 installed, Gradle will currently attempt to use that to compile, which will not work. It is easiest to uninstall JRE 8 and replace it with JDK 17.
|
||||
Note that if you have JRE 8 installed, Gradle will currently attempt to use that to compile, which will not work. It is easiest to uninstall JRE 8 and replace it with JDK 21.
|
||||
|
||||
You can get the JDK 17 link:https://adoptium.net/[here] from Adoptium.
|
||||
You can get the JDK 21 link:https://adoptium.net/[here] from Adoptium.
|
||||
|
||||
The build process uses Gradle, which you do *not* need to download. FastAsyncWorldEdit is a multi-module project with three active modules:
|
||||
|
||||
|
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -7,7 +7,7 @@ pipeline {
|
||||
stage('Build') {
|
||||
steps {
|
||||
withEnv([
|
||||
"PATH+JAVA=${tool 'Temurin-17.0.7_7'}/bin"
|
||||
"PATH+JAVA=${tool 'Temurin-21.0.3_9'}/bin"
|
||||
]) {
|
||||
sh './gradlew clean build'
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ import java.time.format.DateTimeFormatter
|
||||
import xyz.jpenilla.runpaper.task.RunServer
|
||||
|
||||
plugins {
|
||||
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
|
||||
id("xyz.jpenilla.run-paper") version "2.1.0"
|
||||
id("io.github.gradle-nexus.publish-plugin") version "2.0.0"
|
||||
id("xyz.jpenilla.run-paper") version "2.3.1"
|
||||
}
|
||||
|
||||
if (!File("$rootDir/.git").exists()) {
|
||||
@ -34,7 +34,7 @@ logger.lifecycle("""
|
||||
*******************************************
|
||||
""")
|
||||
|
||||
var rootVersion by extra("2.7.1")
|
||||
var rootVersion by extra("2.12.3")
|
||||
var snapshot by extra("SNAPSHOT")
|
||||
var revision: String by extra("")
|
||||
var buildNumber by extra("")
|
||||
@ -83,7 +83,7 @@ allprojects {
|
||||
}
|
||||
|
||||
applyCommonConfiguration()
|
||||
val supportedVersions = listOf("1.16.5", "1.17", "1.17.1", "1.18.2", "1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4", "1.20", "1.20.1")
|
||||
val supportedVersions = listOf("1.20.4", "1.20.5", "1.20.6", "1.21", "1.21.1", "1.21.3")
|
||||
|
||||
|
||||
tasks {
|
||||
supportedVersions.forEach {
|
||||
@ -91,13 +91,13 @@ tasks {
|
||||
minecraftVersion(it)
|
||||
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
|
||||
.toTypedArray())
|
||||
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true")
|
||||
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true", "--add-modules=jdk.incubator.vector")
|
||||
group = "run paper"
|
||||
runDirectory.set(file("run-$it"))
|
||||
}
|
||||
}
|
||||
runServer {
|
||||
minecraftVersion("1.20.1")
|
||||
minecraftVersion("1.21.3")
|
||||
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
|
||||
.toTypedArray())
|
||||
|
||||
|
@ -22,13 +22,25 @@ val properties = Properties().also { props ->
|
||||
|
||||
dependencies {
|
||||
implementation(gradleApi())
|
||||
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0")
|
||||
implementation("com.github.johnrengelman:shadow:8.1.1")
|
||||
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5")
|
||||
implementation("org.ajoberstar.grgit:grgit-gradle:5.3.0")
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.5")
|
||||
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.7.5")
|
||||
constraints {
|
||||
val asmVersion = "[9.7,)"
|
||||
implementation("org.ow2.asm:asm:$asmVersion") {
|
||||
because("Need Java 21 support in shadow")
|
||||
}
|
||||
implementation("org.ow2.asm:asm-commons:$asmVersion") {
|
||||
because("Need Java 21 support in shadow")
|
||||
}
|
||||
implementation("org.vafer:jdependency:[2.10,)") {
|
||||
because("Need Java 21 support in shadow")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(17))
|
||||
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
@ -20,4 +20,8 @@ fun Project.applyPaperweightAdapterConfiguration() {
|
||||
tasks.named("assemble") {
|
||||
dependsOn("reobfJar")
|
||||
}
|
||||
|
||||
tasks.named("javadoc") {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ fun Project.applyCommonConfiguration() {
|
||||
|
||||
plugins.withId("java") {
|
||||
the<JavaPluginExtension>().toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,17 +21,18 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
|
||||
.matching { it.name == "compileJava" || it.name == "compileTestJava" }
|
||||
.configureEach {
|
||||
val disabledLint = listOf(
|
||||
"processing", "path", "fallthrough", "serial"
|
||||
"processing", "path", "fallthrough", "serial", "overloads", "this-escape",
|
||||
)
|
||||
options.release.set(17)
|
||||
options.release.set(21)
|
||||
options.compilerArgs.addAll(listOf("-Xlint:all") + disabledLint.map { "-Xlint:-$it" })
|
||||
options.isDeprecation = true
|
||||
options.encoding = "UTF-8"
|
||||
options.compilerArgs.add("-parameters")
|
||||
options.compilerArgs.add("--add-modules=jdk.incubator.vector")
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
@ -40,30 +41,30 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
|
||||
|
||||
dependencies {
|
||||
"compileOnly"("com.google.code.findbugs:jsr305:3.0.2")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.10.0")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.10.0")
|
||||
"testImplementation"("org.mockito:mockito-core:5.4.0")
|
||||
"testImplementation"("org.mockito:mockito-junit-jupiter:5.4.0")
|
||||
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.10.0")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.11.1")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.11.1")
|
||||
"testImplementation"("org.mockito:mockito-core:5.14.0")
|
||||
"testImplementation"("org.mockito:mockito-junit-jupiter:5.14.0")
|
||||
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.11.1")
|
||||
}
|
||||
|
||||
// Java 8 turns on doclint which we fail
|
||||
tasks.withType<Javadoc>().configureEach {
|
||||
(options as StandardJavadocDocletOptions).apply {
|
||||
addStringOption("Xdoclint:none", "-quiet")
|
||||
addStringOption("-add-modules", "jdk.incubator.vector")
|
||||
tags(
|
||||
"apiNote:a:API Note:",
|
||||
"implSpec:a:Implementation Requirements:",
|
||||
"implNote:a:Implementation Note:"
|
||||
)
|
||||
options.encoding = "UTF-8"
|
||||
|
||||
links(
|
||||
"https://jd.advntr.dev/api/latest/",
|
||||
"https://logging.apache.org/log4j/2.x/log4j-api/apidocs/",
|
||||
"https://logging.apache.org/log4j/2.x/javadoc/log4j-api/",
|
||||
"https://www.antlr.org/api/Java/",
|
||||
"https://docs.enginehub.org/javadoc/org.enginehub.piston/core/0.5.7/",
|
||||
"https://docs.enginehub.org/javadoc/org.enginehub.piston/default-impl/0.5.7/",
|
||||
"https://jd.papermc.io/paper/1.20/",
|
||||
"https://jd.papermc.io/paper/1.21.1/",
|
||||
"https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/"
|
||||
)
|
||||
docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}"
|
||||
|
@ -29,7 +29,7 @@ fun Project.applyLibrariesConfiguration() {
|
||||
applyCommonConfiguration()
|
||||
apply(plugin = "java-base")
|
||||
apply(plugin = "maven-publish")
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
apply(plugin = "com.gradleup.shadow")
|
||||
apply(plugin = "signing")
|
||||
|
||||
configurations {
|
||||
@ -40,8 +40,7 @@ fun Project.applyLibrariesConfiguration() {
|
||||
|
||||
val relocations = mapOf(
|
||||
"net.kyori.text" to "com.sk89q.worldedit.util.formatting.text",
|
||||
"net.kyori.minecraft" to "com.sk89q.worldedit.util.kyori",
|
||||
"net.kyori.adventure.nbt" to "com.sk89q.worldedit.util.nbt"
|
||||
"net.kyori.minecraft" to "com.sk89q.worldedit.util.kyori"
|
||||
|
||||
)
|
||||
|
||||
@ -53,9 +52,14 @@ fun Project.applyLibrariesConfiguration() {
|
||||
exclude(dependency("com.google.guava:guava"))
|
||||
exclude(dependency("com.google.code.gson:gson"))
|
||||
exclude(dependency("com.google.errorprone:error_prone_annotations"))
|
||||
exclude(dependency("com.google.guava:failureaccess"))
|
||||
exclude(dependency("org.checkerframework:checker-qual"))
|
||||
exclude(dependency("org.jetbrains:annotations"))
|
||||
exclude(dependency("org.apache.logging.log4j:log4j-api"))
|
||||
exclude(dependency("com.google.code.findbugs:jsr305"))
|
||||
exclude {
|
||||
it.moduleGroup == "org.jetbrains.kotlin"
|
||||
}
|
||||
}
|
||||
|
||||
relocations.forEach { (from, to) ->
|
||||
@ -67,11 +71,19 @@ fun Project.applyLibrariesConfiguration() {
|
||||
.filterIsInstance<ModuleDependency>()
|
||||
.map { it.copy() }
|
||||
.map { dependency ->
|
||||
dependency.artifact {
|
||||
name = dependency.name
|
||||
type = artifactType
|
||||
extension = "jar"
|
||||
classifier = artifactType
|
||||
val category = dependency.attributes.getAttribute(Category.CATEGORY_ATTRIBUTE)?.name
|
||||
if (category == Category.REGULAR_PLATFORM || category == Category.ENFORCED_PLATFORM) {
|
||||
return@map dependency
|
||||
}
|
||||
try {
|
||||
dependency.artifact {
|
||||
name = dependency.name
|
||||
type = artifactType
|
||||
extension = "jar"
|
||||
classifier = artifactType
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Failed to add artifact to dependency: $dependency", e)
|
||||
}
|
||||
dependency
|
||||
}
|
||||
@ -85,6 +97,10 @@ fun Project.applyLibrariesConfiguration() {
|
||||
from({
|
||||
altConfigFiles("sources")
|
||||
})
|
||||
|
||||
// Yeet module-info's
|
||||
exclude("module-info.java")
|
||||
|
||||
relocations.forEach { (from, to) ->
|
||||
val filePattern = Regex("(.*)${from.replace('.', '/')}((?:/|$).*)")
|
||||
val textPattern = Regex.fromLiteral(from)
|
||||
@ -122,7 +138,7 @@ fun Project.applyLibrariesConfiguration() {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR))
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
|
||||
}
|
||||
outgoing.artifact(tasks.named("jar"))
|
||||
}
|
||||
@ -137,7 +153,7 @@ fun Project.applyLibrariesConfiguration() {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR))
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
|
||||
}
|
||||
outgoing.artifact(tasks.named("jar"))
|
||||
}
|
||||
@ -241,8 +257,9 @@ fun Project.applyLibrariesConfiguration() {
|
||||
|
||||
scm {
|
||||
url.set("https://github.com/IntellectualSites/FastAsyncWorldEdit")
|
||||
connection.set("scm:https://IntellectualSites@github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
developerConnection.set("scm:git://github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
connection.set("scm:git:https://github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
developerConnection.set("scm:git:git@github.com:IntellectualSites/FastAsyncWorldEdit.git")
|
||||
tag.set("${project.version}")
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
|
@ -12,6 +12,7 @@ import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.plugins.signing.SigningExtension
|
||||
|
||||
fun Project.applyPlatformAndCoreConfiguration() {
|
||||
@ -20,7 +21,7 @@ fun Project.applyPlatformAndCoreConfiguration() {
|
||||
apply(plugin = "eclipse")
|
||||
apply(plugin = "idea")
|
||||
apply(plugin = "maven-publish")
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
apply(plugin = "com.gradleup.shadow")
|
||||
apply(plugin = "signing")
|
||||
|
||||
applyCommonJavaConfiguration(
|
||||
@ -87,23 +88,27 @@ fun Project.applyPlatformAndCoreConfiguration() {
|
||||
name.set("Alexander Brandes")
|
||||
email.set("contact(at)notmyfault.dev")
|
||||
organization.set("IntellectualSites")
|
||||
organizationUrl.set("https://github.com/IntellectualSites")
|
||||
}
|
||||
developer {
|
||||
id.set("SirYwell")
|
||||
name.set("Hannes Greule")
|
||||
organization.set("IntellectualSites")
|
||||
organizationUrl.set("https://github.com/IntellectualSites")
|
||||
}
|
||||
developer {
|
||||
id.set("dordsor21")
|
||||
name.set("dordsor21")
|
||||
organization.set("IntellectualSites")
|
||||
organizationUrl.set("https://github.com/IntellectualSites")
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
url.set("https://github.com/IntellectualSites/FastAsyncWorldEdit")
|
||||
connection.set("scm:https://IntellectualSites@github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
developerConnection.set("scm:git://github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
connection.set("scm:git:https://github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
developerConnection.set("scm:git:git@github.com:IntellectualSites/FastAsyncWorldEdit.git")
|
||||
tag.set("${project.version}")
|
||||
}
|
||||
|
||||
issueManagement{
|
||||
@ -128,7 +133,8 @@ fun Project.applyPlatformAndCoreConfiguration() {
|
||||
}
|
||||
|
||||
fun Project.applyShadowConfiguration() {
|
||||
tasks.named<ShadowJar>("shadowJar") {
|
||||
tasks.withType<ShadowJar>().configureEach {
|
||||
relocate("com.sk89q.jchronic", "com.sk89q.worldedit.jchronic")
|
||||
dependencies {
|
||||
include(project(":worldedit-libs:core"))
|
||||
include(project(":worldedit-libs:${project.name.replace("worldedit-", "")}"))
|
||||
|
@ -1,6 +1,6 @@
|
||||
[versions]
|
||||
# Minecraft expectations
|
||||
paper = "1.20.1-R0.1-SNAPSHOT"
|
||||
paper = "1.21-R0.1-SNAPSHOT"
|
||||
fastutil = "8.5.9"
|
||||
guava = "31.1-jre"
|
||||
log4j = "2.19.0"
|
||||
@ -9,48 +9,49 @@ snakeyaml = "2.0"
|
||||
|
||||
# Plugins
|
||||
dummypermscompat = "1.10"
|
||||
worldguard-bukkit = "7.0.9"
|
||||
worldguard-bukkit = "7.0.12"
|
||||
mapmanager = "1.8.0-SNAPSHOT"
|
||||
griefprevention = "16.18.1"
|
||||
griefprevention = "17.0.0"
|
||||
griefdefender = "2.1.0-SNAPSHOT"
|
||||
residence = "4.5._13.1"
|
||||
towny = "0.99.5.10"
|
||||
plotsquared = "7.0.0-rc.4"
|
||||
towny = "0.100.4.13"
|
||||
plotsquared = "7.4.0"
|
||||
|
||||
# Third party
|
||||
bstats = "3.0.2"
|
||||
sparsebitset = "1.2"
|
||||
bstats = "3.1.0"
|
||||
sparsebitset = "1.3"
|
||||
parallelgzip = "1.0.5"
|
||||
adventure = "4.14.0"
|
||||
adventure-bukkit = "4.3.0"
|
||||
checkerqual = "3.37.0"
|
||||
adventure = "4.17.0"
|
||||
adventure-bukkit = "4.3.4"
|
||||
checkerqual = "3.48.2"
|
||||
truezip = "6.8.4"
|
||||
auto-value = "1.10.2"
|
||||
auto-value = "1.11.0"
|
||||
findbugs = "3.0.2"
|
||||
rhino-runtime = "1.7.14"
|
||||
rhino-runtime = "1.7.15"
|
||||
zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs
|
||||
antlr4 = "4.13.0"
|
||||
antlr4 = "4.13.2"
|
||||
json-simple = "1.1.1"
|
||||
jlibnoise = "1.0.0"
|
||||
jchronic = "0.2.4a"
|
||||
lz4-java = "1.8.0"
|
||||
lz4-stream = "1.0.0"
|
||||
commons-cli = "1.5.0"
|
||||
commons-cli = "1.9.0"
|
||||
paperlib = "1.0.8"
|
||||
paster = "1.1.5"
|
||||
paster = "1.1.6"
|
||||
vault = "1.7.1"
|
||||
serverlib = "2.3.1"
|
||||
serverlib = "2.3.6"
|
||||
linbus = "0.2.0"
|
||||
## Internal
|
||||
text-adapter = "3.0.6"
|
||||
text = "3.0.4"
|
||||
piston = "0.5.7"
|
||||
piston = "0.5.10"
|
||||
|
||||
# Tests
|
||||
mockito = "5.4.0"
|
||||
mockito = "5.14.2"
|
||||
|
||||
# Gradle plugins
|
||||
pluginyml = "0.6.0"
|
||||
minotaur = "2.8.3"
|
||||
mod-publish-plugin = "0.8.1"
|
||||
|
||||
[libraries]
|
||||
# Minecraft expectations
|
||||
@ -78,7 +79,6 @@ bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats
|
||||
bstatsBukkit = { group = "org.bstats", name = "bstats-bukkit", version.ref = "bstats" }
|
||||
sparsebitset = { group = "com.zaxxer", name = "SparseBitSet", version.ref = "sparsebitset" }
|
||||
parallelgzip = { group = "org.anarres", name = "parallelgzip", version.ref = "parallelgzip" }
|
||||
adventureNbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" }
|
||||
truezip = { group = "de.schlichtherle", name = "truezip", version.ref = "truezip" }
|
||||
autoValueAnnotations = { group = "com.google.auto.value", name = "auto-value-annotations", version.ref = "auto-value" }
|
||||
autoValue = { group = "com.google.auto.value", name = "auto-value", version.ref = "auto-value" }
|
||||
@ -101,6 +101,11 @@ paster = { group = "com.intellectualsites.paster", name = "Paster", version.ref
|
||||
vault = { group = "com.github.MilkBowl", name = "VaultAPI", version.ref = "vault" }
|
||||
serverlib = { group = "dev.notmyfault.serverlib", name = "ServerLib", version.ref = "serverlib" }
|
||||
checkerqual = { group = "org.checkerframework", name = "checker-qual", version.ref = "checkerqual" }
|
||||
linBus-bom = { group = "org.enginehub.lin-bus", name = "lin-bus-bom", version.ref = "linbus" }
|
||||
linBus-common = { group = "org.enginehub.lin-bus", name = "lin-bus-common" }
|
||||
linBus-stream = { group = "org.enginehub.lin-bus", name = "lin-bus-stream" }
|
||||
linBus-tree = { group = "org.enginehub.lin-bus", name = "lin-bus-tree" }
|
||||
linBus-format-snbt = { group = "org.enginehub.lin-bus.format", name = "lin-bus-format-snbt" }
|
||||
|
||||
# Internal
|
||||
## Text
|
||||
@ -122,4 +127,4 @@ log4jCore = { group = "org.apache.logging.log4j", name = "log4j-core", version.r
|
||||
|
||||
[plugins]
|
||||
pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }
|
||||
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }
|
||||
mod-publish-plugin = { id = "me.modmuss50.mod-publish-plugin", version.ref = "mod-publish-plugin" }
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binäre Datei nicht angezeigt.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
22
gradlew
vendored
22
gradlew
vendored
@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@ -83,7 +85,9 @@ done
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@ -144,7 +148,7 @@ 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
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@ -201,11 +205,11 @@ fi
|
||||
# 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"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
22
gradlew.bat
vendored
22
gradlew.bat
vendored
@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit"
|
||||
|
||||
include("worldedit-libs")
|
||||
|
||||
listOf("legacy", "1_17_1", "1_18_2", "1_19", "1_19_3","1_19_4", "1_20").forEach {
|
||||
listOf("1_20_2", "1_20_4", "1_20_5", "1_21", "1_21_3").forEach {
|
||||
Lixfel
hat
Hier genauso! Hier genauso!
|
||||
include("worldedit-bukkit:adapters:adapter-$it")
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
build:
|
||||
- "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew build"
|
||||
- "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew --stop"
|
||||
- "JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ./gradlew build"
|
||||
- "JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ./gradlew --stop"
|
||||
|
||||
artifacts:
|
||||
"/binarys/FastAsyncWorldEdit-1.18.jar": "worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-2.7.1-SNAPSHOT.jar"
|
||||
"/binarys/FastAsyncWorldEdit-1.21.jar": "worldedit-bukkit/build/libs/FastAsyncWorldEdit-Paper-2.12.3-SNAPSHOT.jar"
|
||||
Lixfel
hat
Wenns nicht große Umbauarbeiten gab, müsste es weiterhin möglich sein, die FAWE-Version weiter zu nutzen (so habe ich auch schon manuell immer den 1.18 und 1.19 Support reingepatcht. Wenns nicht große Umbauarbeiten gab, müsste es weiterhin möglich sein, die FAWE-Version weiter zu nutzen (so habe ich auch schon manuell immer den 1.18 und 1.19 Support reingepatcht.
Chaoscaot
hat
Problem: der SpongeSchematicReader wurde verschoben z.B. Problem: der SpongeSchematicReader wurde verschoben z.B.
Lixfel
hat
Deshalb wäre es doch umso praktischer, den 1.18+ Support da beizubehalten, damit wir im SpigotCore da nicht noch einen Extrawrapper brauchen. Deshalb wäre es doch umso praktischer, den 1.18+ Support da beizubehalten, damit wir im SpigotCore da nicht noch einen Extrawrapper brauchen.
|
||||
|
||||
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/"
|
||||
- "mvn deploy:deploy-file -DgroupId=de.steamwar -DartifactId=fastasyncworldedit -Dversion=1.21 -Dpackaging=jar -Dfile=/binarys/FastAsyncWorldEdit-1.21.jar -Durl=file:///var/www/html/maven/"
|
||||
|
@ -1,26 +0,0 @@
|
||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||
|
||||
applyPaperweightAdapterConfiguration()
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
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 sendMessage(Component message, ChatType type, UUID sender) {
|
||||
}
|
||||
|
||||
@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) {
|
||||
}
|
||||
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe;
|
||||
|
||||
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.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PaperweightWorldNativeAccess implements
|
||||
WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
|
||||
private final PaperweightAdapter adapter;
|
||||
private final WeakReference<ServerLevel> world;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> 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.setType(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());
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
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_17_R1_2.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_17_R1.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.save(new net.minecraft.nbt.CompoundTag())));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -1,709 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
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.PaperweightAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.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 net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.WritableRegistry;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
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_17_R1.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
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;
|
||||
|
||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
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<String, List<Property<?>>> 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<net.minecraft.nbt.Tag> getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
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<String, List<Property<?>>> 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<Boolean>) 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<Integer>) 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 Registry.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.save(new net.minecraft.nbt.CompoundTag());
|
||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SideEffect> getSupportedSideEffects() {
|
||||
return SideEffectSet.defaults().getSideEffectsToApply();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
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;
|
||||
}
|
||||
if (section == null) {
|
||||
if (blockState.isAir()) {
|
||||
return true;
|
||||
}
|
||||
levelChunkSections[y4] = section = new LevelChunkSection(y4 << 4);
|
||||
}
|
||||
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<CompoundBinaryTag> 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<String, BinaryTag> 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 <B extends BlockStateHolder<B>> 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 && map.wasAccessibleSinceLastSave()) {
|
||||
boolean flag = false;
|
||||
// PlayerChunk.d players = map.players;
|
||||
Stream<ServerPlayer> 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) {
|
||||
ClientboundLevelChunkPacket nmsPacket = (ClientboundLevelChunkPacket) 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<String, ? extends Property<?>> 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(
|
||||
Registry.ITEM.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
||||
baseItemStack.getAmount()
|
||||
);
|
||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNativeBinary(baseItemStack.getNbt())));
|
||||
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<BlockPos, CraftBlockState> 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 List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
||||
// Quickly add each entity to a list copy.
|
||||
List<Entity> mcEntities = new ArrayList<>();
|
||||
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
||||
|
||||
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
||||
mcEntities.forEach((mcEnt) -> {
|
||||
org.bukkit.entity.Entity bukkitEntity = mcEnt.getBukkitEntity();
|
||||
if (bukkitEntity.isValid()) {
|
||||
list.add(bukkitEntity);
|
||||
}
|
||||
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
@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<Biome> registry = MinecraftServer
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
|
||||
Biome biome = registry.get(resourceLocation);
|
||||
return registry.getId(biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<NamespacedKey> getRegisteredBiomes() {
|
||||
WritableRegistry<Biome> biomeRegistry = ((CraftServer) Bukkit.getServer())
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(
|
||||
Registry.BIOME_REGISTRY);
|
||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||
for (ResourceLocation key : keys) {
|
||||
try {
|
||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||
}
|
||||
}
|
||||
return namespacedKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelighterFactory getRelighterFactory() {
|
||||
try {
|
||||
Class.forName("ca.spottedleaf.starlight.light.StarLightEngine");
|
||||
if (PaperweightStarlightRelighter.isUsable()) {
|
||||
return new PaperweightStarlightRelighterFactory();
|
||||
}
|
||||
} catch (ThreadDeath td) {
|
||||
throw td;
|
||||
} catch (Throwable ignored) {
|
||||
|
||||
}
|
||||
return new NMSRelighterFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Property<?>>> getAllProperties() {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
init();
|
||||
return allBlockProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getTickingPostProcessor() {
|
||||
return new PaperweightPostProcessor();
|
||||
}
|
||||
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,245 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.queue.IBlocks;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
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.BlockTypesCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private ChunkBiomeContainer chunkBiomeContainer;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
this.serverLevel = levelChunk.level;
|
||||
this.minHeight = serverLevel.getMinBuildHeight();
|
||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
this.blocks = new char[getSectionCount()][];
|
||||
}
|
||||
|
||||
protected void storeTile(BlockEntity blockEntity) {
|
||||
tiles.put(
|
||||
BlockVector3.at(
|
||||
blockEntity.getBlockPos().getX(),
|
||||
blockEntity.getBlockPos().getY(),
|
||||
blockEntity.getBlockPos().getZ()
|
||||
),
|
||||
new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.save(new net.minecraft.nbt.CompoundTag())))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
entities.add((CompoundTag) adapter.toNative(compoundTag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
for (CompoundTag tag : entities) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCreateCopy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeightmapToGet(HeightMapType type, int[] data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxY() {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return minHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSectionPosition() {
|
||||
return maxHeight >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinSectionPosition() {
|
||||
return minHeight >> 4;
|
||||
}
|
||||
|
||||
protected void storeBiomes(ChunkBiomeContainer chunkBiomeContainer) {
|
||||
// The to do one line below is pre-paperweight and needs to be revised
|
||||
// TODO revisit last parameter, BiomeStorage[] *would* be more efficient
|
||||
this.chunkBiomeContainer = new ChunkBiomeContainer(chunkBiomeContainer.biomeRegistry, serverLevel,
|
||||
chunkBiomeContainer.writeBiomes()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
Biome biome = null;
|
||||
if (y == -1) {
|
||||
for (y = serverLevel.getMinBuildHeight(); y <= serverLevel.getMaxBuildHeight(); y += 4) {
|
||||
biome = this.chunkBiomeContainer.getNoiseBiome(x >> 2, y >> 2, z >> 2);
|
||||
if (biome != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
biome = this.chunkBiomeContainer.getNoiseBiome(x >> 2, y >> 2, z >> 2);
|
||||
}
|
||||
return biome != null ? PaperweightPlatformAdapter.adapt(biome, serverLevel) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSectionLighting(int layer, boolean sky) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive, int layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlocks reset() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionCount() {
|
||||
return serverLevel.getSectionsCount();
|
||||
}
|
||||
|
||||
protected void storeSection(int layer, char[] data) {
|
||||
blocks[layer] = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
BlockState state = BlockTypesCache.states[get(x, y, z)];
|
||||
return state.toBaseBlock(this, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer] != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] loadIfPresent(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmittedLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getHeightMap(HeightMapType type) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public char get(int x, int y, int z) {
|
||||
final int layer = (y >> 4) - getMinSectionPosition();
|
||||
final int index = (y & 15) << 8 | z << 4 | x;
|
||||
return blocks[layer][index];
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
||||
|
||||
public class PaperweightMapChunkUtil extends MapChunkUtil<ClientboundLevelChunkPacket> {
|
||||
|
||||
public PaperweightMapChunkUtil() throws NoSuchFieldException {
|
||||
fieldX = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a"));
|
||||
fieldZ = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("x", "b"));
|
||||
fieldBitMask = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("z", "c"));
|
||||
fieldHeightMap = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("availableSections", "d"));
|
||||
fieldChunkData = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("biomes", "f"));
|
||||
fieldBlockEntities = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("buffer", "g"));
|
||||
fieldFull = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("blockEntitiesTags", "h"));
|
||||
fieldX.setAccessible(true);
|
||||
fieldZ.setAccessible(true);
|
||||
fieldBitMask.setAccessible(true);
|
||||
fieldHeightMap.setAccessible(true);
|
||||
fieldChunkData.setAccessible(true);
|
||||
fieldBlockEntities.setAccessible(true);
|
||||
fieldFull.setAccessible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientboundLevelChunkPacket createPacket() {
|
||||
// TODO ??? return new ClientboundLevelChunkPacket();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
@ -1,511 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
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 net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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.server.level.TicketType;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
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.chunk.ChunkBiomeContainer;
|
||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
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.gameevent.GameEventDispatcher;
|
||||
import net.minecraft.world.level.gameevent.GameEventListener;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
public static final Field fieldStorage;
|
||||
public static final Field fieldPalette;
|
||||
public static final Field fieldBits;
|
||||
|
||||
public static final Field fieldBitsPerEntry;
|
||||
|
||||
private static final Field fieldTickingFluidContent;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final Field fieldBiomes;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final Field fieldGameEventDispatcherSections;
|
||||
private static final MethodHandle methodremoveBlockEntityTicker;
|
||||
|
||||
private static final Field fieldRemove;
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l"));
|
||||
fieldBits.setAccessible(true);
|
||||
fieldStorage = PalettedContainer.class.getDeclaredField(Refraction.pickName("storage", "c"));
|
||||
fieldStorage.setAccessible(true);
|
||||
fieldPalette = PalettedContainer.class.getDeclaredField(Refraction.pickName("palette", "k"));
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldBitsPerEntry = BitStorage.class.getDeclaredField(Refraction.pickName("bits", "c"));
|
||||
fieldBitsPerEntry.setAccessible(true);
|
||||
|
||||
fieldTickingFluidContent = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "h"));
|
||||
fieldTickingFluidContent.setAccessible(true);
|
||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g"));
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f"));
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
fieldBiomes = ChunkBiomeContainer.class.getDeclaredField(Refraction.pickName("biomes", "f"));
|
||||
fieldBiomes.setAccessible(true);
|
||||
|
||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
||||
"getVisibleChunkIfPresent",
|
||||
"getVisibleChunk"
|
||||
), long.class);
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
|
||||
fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
fieldGameEventDispatcherSections = LevelChunk.class.getDeclaredField(Refraction.pickName(
|
||||
"gameEventDispatcherSections", "x"));
|
||||
fieldGameEventDispatcherSections.setAccessible(true);
|
||||
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
|
||||
Refraction.pickName(
|
||||
"removeBlockEntityTicker",
|
||||
"l"
|
||||
), BlockPos.class
|
||||
);
|
||||
removeBlockEntityTicker.setAccessible(true);
|
||||
methodremoveBlockEntityTicker = MethodHandles.lookup().unreflect(removeBlockEntityTicker);
|
||||
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable rethrow) {
|
||||
rethrow.printStackTrace();
|
||||
throw new RuntimeException(rethrow);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean setSectionAtomic(
|
||||
LevelChunkSection[] sections,
|
||||
LevelChunkSection expected,
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// There is no point in having a functional semaphore for paper servers.
|
||||
private static final ThreadLocal<DelegateSemaphore> SEMAPHORE_THREAD_LOCAL =
|
||||
ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null));
|
||||
|
||||
static DelegateSemaphore applyLock(LevelChunkSection section) {
|
||||
if (PaperLib.isPaper()) {
|
||||
return SEMAPHORE_THREAD_LOCAL.get();
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(blocks, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
||||
if (nmsChunk != null) {
|
||||
return nmsChunk;
|
||||
}
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
} else {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
// Avoid "async" methods from the main thread.
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||
try {
|
||||
CraftChunk chunk;
|
||||
try {
|
||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
String world = serverLevel.getWorld().getName();
|
||||
// We've already taken 10 seconds we can afford to wait a little here.
|
||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||
if (loaded) {
|
||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||
// Retry chunk load
|
||||
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
||||
}
|
||||
}
|
||||
return chunk.getHandle();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
// Ensure chunk is definitely loaded before applying a ticket
|
||||
net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||
.getChunkSource()
|
||||
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||
}
|
||||
|
||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||
try {
|
||||
return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ));
|
||||
} catch (Throwable thr) {
|
||||
throw new RuntimeException(thr);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
||||
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||
if (chunkHolder == null) {
|
||||
return;
|
||||
}
|
||||
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
|
||||
// UNLOADED_CHUNK
|
||||
Optional<LevelChunk> optional = ((Either) chunkHolder
|
||||
.getTickingChunkFuture()
|
||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
|
||||
if (PaperLib.isPaper()) {
|
||||
// getChunkAtIfLoadedImmediately is paper only
|
||||
optional = optional.or(() -> Optional.ofNullable(nmsWorld
|
||||
.getChunkSource()
|
||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ)));
|
||||
}
|
||||
if (optional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LevelChunk levelChunk = optional.get();
|
||||
TaskManager.taskManager().task(() -> {
|
||||
ClientboundLevelChunkPacket chunkPacket = new ClientboundLevelChunkPacket(levelChunk);
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(chunkPacket));
|
||||
if (lighting) {
|
||||
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
|
||||
boolean trustEdges = true;
|
||||
ClientboundLightUpdatePacket packet =
|
||||
new ClientboundLightUpdatePacket(coordIntPair, nmsWorld.getChunkSource().getLightEngine(), null, null,
|
||||
trustEdges
|
||||
);
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
|
||||
return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false);
|
||||
}
|
||||
|
||||
/*
|
||||
NMS conversion
|
||||
*/
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final char[] blocks,
|
||||
boolean fastMode,
|
||||
CachedBukkitAdapter adapter
|
||||
) {
|
||||
return newChunkSection(layer, null, blocks, fastMode, adapter);
|
||||
}
|
||||
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
boolean fastMode,
|
||||
CachedBukkitAdapter adapter
|
||||
) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
|
||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int num_palette;
|
||||
final short[] nonEmptyBlockCount = fastMode ? new short[1] : null;
|
||||
if (get == null) {
|
||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, nonEmptyBlockCount);
|
||||
} else {
|
||||
num_palette = createPalette(
|
||||
layer,
|
||||
blockToPalette,
|
||||
paletteToBlock,
|
||||
blocksCopy,
|
||||
get,
|
||||
set,
|
||||
adapter,
|
||||
nonEmptyBlockCount
|
||||
);
|
||||
}
|
||||
// BlockStates
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (Settings.settings().PROTOCOL_SUPPORT_FIX || num_palette != 1) {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
|
||||
} else {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
|
||||
}
|
||||
if (bitsPerEntry > 8) {
|
||||
bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1);
|
||||
}
|
||||
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry);
|
||||
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
|
||||
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) {
|
||||
blockStates[i] = 0;
|
||||
}
|
||||
} else {
|
||||
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, 4096, blockStates);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
LevelChunkSection levelChunkSection = newChunkSection(layer);
|
||||
// set palette & data bits
|
||||
final PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks =
|
||||
levelChunkSection.getStates();
|
||||
// private DataPalette<T> h;
|
||||
// protected DataBits a;
|
||||
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
|
||||
final BitStorage nmsBits = new BitStorage(bitsPerEntry, 4096, bits);
|
||||
final Palette<net.minecraft.world.level.block.state.BlockState> blockStatePalettedContainer;
|
||||
if (bitsPerEntry <= 4) {
|
||||
blockStatePalettedContainer = new LinearPalette<>(Block.BLOCK_STATE_REGISTRY, bitsPerEntry, dataPaletteBlocks,
|
||||
NbtUtils::readBlockState
|
||||
);
|
||||
} else if (bitsPerEntry < 9) {
|
||||
blockStatePalettedContainer = new HashMapPalette<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
bitsPerEntry,
|
||||
dataPaletteBlocks,
|
||||
NbtUtils::readBlockState,
|
||||
NbtUtils::writeBlockState
|
||||
);
|
||||
} else {
|
||||
blockStatePalettedContainer = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE;
|
||||
}
|
||||
|
||||
// set palette if required
|
||||
if (bitsPerEntry < 9) {
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
final int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
final BlockState state = BlockTypesCache.states[ordinal];
|
||||
final net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState();
|
||||
blockStatePalettedContainer.idFor(blockState);
|
||||
}
|
||||
}
|
||||
try {
|
||||
fieldStorage.set(dataPaletteBlocks, nmsBits);
|
||||
fieldPalette.set(dataPaletteBlocks, blockStatePalettedContainer);
|
||||
fieldBits.set(dataPaletteBlocks, bitsPerEntry);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (!fastMode) {
|
||||
levelChunkSection.recalcBlockCounts();
|
||||
} else {
|
||||
try {
|
||||
fieldNonEmptyBlockCount.set(levelChunkSection, nonEmptyBlockCount[0]);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return levelChunkSection;
|
||||
} catch (final Throwable e) {
|
||||
throw e;
|
||||
} finally {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
Arrays.fill(paletteToBlock, Integer.MAX_VALUE);
|
||||
Arrays.fill(blockStates, 0);
|
||||
Arrays.fill(blocksCopy, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static LevelChunkSection newChunkSection(int layer) {
|
||||
return new LevelChunkSection(layer);
|
||||
}
|
||||
|
||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
||||
fieldTickingFluidContent.setShort(section, (short) 0);
|
||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
||||
}
|
||||
|
||||
public static Biome[] getBiomeArray(ChunkBiomeContainer chunkBiomeContainer) {
|
||||
try {
|
||||
return (Biome[]) fieldBiomes.get(chunkBiomeContainer);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static BiomeType adapt(Biome biome, LevelAccessor levelAccessor) {
|
||||
ResourceLocation resourceLocation = levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).getKey(
|
||||
biome);
|
||||
if (resourceLocation == null) {
|
||||
return levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).getId(biome) == -1
|
||||
? BiomeTypes.OCEAN
|
||||
: null;
|
||||
}
|
||||
return BiomeTypes.get(resourceLocation.toString().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
||||
try {
|
||||
// Do the method ourselves to avoid trying to reflect generic method parameters
|
||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
||||
if (blockEntity != null) {
|
||||
if (!levelChunk.level.isClientSide) {
|
||||
Block block = beacon.getBlockState().getBlock();
|
||||
if (block instanceof EntityBlock) {
|
||||
GameEventListener gameEventListener = ((EntityBlock) block).getListener(levelChunk.level, beacon);
|
||||
if (gameEventListener != null) {
|
||||
int i = SectionPos.blockToSectionCoord(beacon.getBlockPos().getY());
|
||||
GameEventDispatcher gameEventDispatcher = levelChunk.getEventDispatcher(i);
|
||||
gameEventDispatcher.unregister(gameEventListener);
|
||||
if (gameEventDispatcher.isEmpty()) {
|
||||
try {
|
||||
((Int2ObjectMap<GameEventDispatcher>) fieldGameEventDispatcherSections.get(levelChunk))
|
||||
.remove(i);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldRemove.set(beacon, true);
|
||||
}
|
||||
}
|
||||
methodremoveBlockEntityTicker.invoke(levelChunk, beacon.getBlockPos());
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static List<Entity> getEntities(LevelChunk chunk) {
|
||||
return chunk.level.entityManager.getEntities(chunk.getPos());
|
||||
}
|
||||
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
// The PostProcessor shouldn't be added, but just in case
|
||||
if (!tickFluid) {
|
||||
return;
|
||||
}
|
||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
||||
layer:
|
||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
||||
char[] set = iChunkSet.loadIfPresent(layer);
|
||||
if (set == null) {
|
||||
// No edit means no need to process
|
||||
continue;
|
||||
}
|
||||
char[] get = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean fromGet = false; // Used for liquids
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
||||
// actually being set
|
||||
if (get == null) {
|
||||
continue layer;
|
||||
}
|
||||
fromGet = true;
|
||||
ordinal = replacedOrdinal = get[i];
|
||||
}
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
continue;
|
||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
replacedOrdinal = get[i];
|
||||
}
|
||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
||||
boolean replacedWasLiquid = false;
|
||||
BlockState replacedState = null;
|
||||
if (!ticking) {
|
||||
// If the block being replaced was not ticking, it cannot be a liquid
|
||||
if (!replacedWasTicking) {
|
||||
continue;
|
||||
}
|
||||
// If the block being replaced is not fluid, we do not need to worry
|
||||
if (!(replacedWasLiquid =
|
||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
boolean liquid = state.getMaterial().isLiquid();
|
||||
int x = i & 15;
|
||||
int y = (i >> 8) & 15;
|
||||
int z = (i >> 4) & 15;
|
||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
||||
if (liquid || replacedWasLiquid) {
|
||||
if (liquid) {
|
||||
addFluid(getBlocks.serverLevel, state, position);
|
||||
continue;
|
||||
}
|
||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
||||
// being ticked anyway. We only need it to be "hit" once.
|
||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
||||
if (set == null || get == null) {
|
||||
return false;
|
||||
}
|
||||
char ordinal;
|
||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
if (x > 0 && set[i - 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (x < 15 && set[i + 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z > 0 && set[i - 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z < 15 && set[i + 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y > 0 && set[i - 256] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y < 15 && set[i + 256] != reserved) {
|
||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isFluid(char ordinal) {
|
||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
||||
Fluid type;
|
||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
||||
} else {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
||||
}
|
||||
serverLevel.getLiquidTicks().scheduleTick(
|
||||
position,
|
||||
type,
|
||||
type.getTickDelay(serverLevel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,239 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
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.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;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
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<Unit> 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();
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
||||
this.serverLevel = serverLevel;
|
||||
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();
|
||||
try {
|
||||
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
|
||||
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
|
||||
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
|
||||
chunks.add(ChunkPos.asLong(cx, cz));
|
||||
} finally {
|
||||
areaLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLightUpdate(int x, int y, int z) {
|
||||
delegate.addLightUpdate(x, y, z);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called "recursively", iterating and removing elements
|
||||
* from the regions linked map. This way, chunks are loaded in batches to avoid
|
||||
* OOMEs.
|
||||
*/
|
||||
@Override
|
||||
public void fixLightingSafe(boolean sky) {
|
||||
this.areaLock.lock();
|
||||
try {
|
||||
if (regions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LongSet first = regions.removeFirst();
|
||||
fixLighting(first, () -> fixLightingSafe(true));
|
||||
} finally {
|
||||
this.areaLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes a set of chunks and runs an action afterwards.
|
||||
* The action is run async, the chunks are partly processed on the main thread
|
||||
* (as required by the server).
|
||||
*/
|
||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
// convert from long keys to ChunkPos
|
||||
Set<ChunkPos> coords = new HashSet<>();
|
||||
LongIterator iterator = chunks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
coords.add(new ChunkPos(iterator.nextLong()));
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (ChunkPos pos : coords) {
|
||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
pos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
))
|
||||
);
|
||||
}
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
|
||||
invokeRelight(
|
||||
coords,
|
||||
c -> {
|
||||
}, // no callback for single chunks required
|
||||
i -> {
|
||||
if (i != coords.size()) {
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLighting() {
|
||||
this.delegate.removeLighting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixBlockLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixSkyLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReentrantLock getLock() {
|
||||
return this.lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||
|
||||
@Override
|
||||
public @Nonnull
|
||||
@SuppressWarnings("rawtypes")
|
||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||
if (w == null) {
|
||||
return NullRelighter.INSTANCE;
|
||||
}
|
||||
return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.LazyCompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
|
||||
private final Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier;
|
||||
private CompoundTag compoundTag;
|
||||
|
||||
public PaperweightLazyCompoundTag(Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier) {
|
||||
super(new HashMap<>());
|
||||
this.compoundTagSupplier = compoundTagSupplier;
|
||||
}
|
||||
|
||||
public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
this(() -> compoundTag);
|
||||
}
|
||||
|
||||
public net.minecraft.nbt.CompoundTag get() {
|
||||
return compoundTagSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Tag> getValue() {
|
||||
if (compoundTag == null) {
|
||||
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
|
||||
}
|
||||
return compoundTag.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundBinaryTag asBinaryTag() {
|
||||
getValue();
|
||||
return compoundTag.asBinaryTag();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return compoundTagSupplier.get().contains(key);
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
return compoundTagSupplier.get().getByteArray(key);
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
return compoundTagSupplier.get().getByte(key);
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
return compoundTagSupplier.get().getDouble(key);
|
||||
}
|
||||
|
||||
public double asDouble(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsDouble();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
return compoundTagSupplier.get().getFloat(key);
|
||||
}
|
||||
|
||||
public int[] getIntArray(String key) {
|
||||
return compoundTagSupplier.get().getIntArray(key);
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
return compoundTagSupplier.get().getInt(key);
|
||||
}
|
||||
|
||||
public int asInt(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsInt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> getList(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
|
||||
ArrayList<Tag> list = new ArrayList<>();
|
||||
for (net.minecraft.nbt.Tag elem : nbtList) {
|
||||
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
list.add(new PaperweightLazyCompoundTag(compoundTag));
|
||||
} else {
|
||||
list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ListTag getListTag(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag) {
|
||||
return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag);
|
||||
}
|
||||
return new ListTag(StringTag.class, Collections.emptyList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
|
||||
ListTag listTag = getListTag(key);
|
||||
if (listTag.getType().equals(listType)) {
|
||||
return (List<T>) listTag.getValue();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public long[] getLongArray(String key) {
|
||||
return compoundTagSupplier.get().getLongArray(key);
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
return compoundTagSupplier.get().getLong(key);
|
||||
}
|
||||
|
||||
public long asLong(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsLong();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
return compoundTagSupplier.get().getShort(key);
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return compoundTagSupplier.get().getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return compoundTagSupplier.get().toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,697 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.data.worldgen.biome.Biomes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.LinearCongruentialGenerator;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.biome.OverworldBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.SimpleRandomSource;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
import net.minecraft.world.level.levelgen.synth.ImprovedNoise;
|
||||
import net.minecraft.world.level.newbiome.area.Area;
|
||||
import net.minecraft.world.level.newbiome.area.AreaFactory;
|
||||
import net.minecraft.world.level.newbiome.context.BigContext;
|
||||
import net.minecraft.world.level.newbiome.layer.Layer;
|
||||
import net.minecraft.world.level.newbiome.layer.Layers;
|
||||
import net.minecraft.world.level.newbiome.layer.traits.PixelTransformer;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.LongFunction;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field worldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field generateFlatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
worldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
worldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
generateFlatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "g"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "e"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "C"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureManager structureManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = generateFlatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataPackConfig()
|
||||
);
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable());
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
originalServerWorld.dimensionType(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
// placeholder. Required for new ChunkProviderServer, but we create and then set it later
|
||||
newOpts.dimensions().get(levelStemResourceKey).generator(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
private final Biome singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(
|
||||
options
|
||||
.getBiomeType()
|
||||
.getId())) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig);
|
||||
}
|
||||
|
||||
//generator
|
||||
if (originalChunkProvider.getGenerator() instanceof FlatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = (FlatLevelGeneratorSettings) generatorSettingFlatField.get(
|
||||
originalChunkProvider.getGenerator());
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator) {
|
||||
Supplier<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Supplier<NoiseGeneratorSettings>) generatorSettingBaseSupplierField
|
||||
.get(originalChunkProvider.getGenerator());
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options
|
||||
.getBiomeType()
|
||||
.getId())));
|
||||
} else {
|
||||
biomeSource = originalChunkProvider.getGenerator().getBiomeSource();
|
||||
if (biomeSource instanceof OverworldBiomeSource) {
|
||||
biomeSource = fastOverworldBiomeSource(biomeSource);
|
||||
}
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(biomeSource, seed, generatorSettingBaseSupplier);
|
||||
} else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator) {
|
||||
chunkGenerator = (ChunkGenerator) delegateField.get(originalChunkProvider.getGenerator());
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalChunkProvider.getGenerator().getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = freshChunkProvider.getLightEngine();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return PaperLib.isPaper()
|
||||
? new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, freshWorld) // paper
|
||||
: new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld); // spigot
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) worldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private BiomeSource fastOverworldBiomeSource(BiomeSource biomeSource) throws Exception {
|
||||
Field legacyBiomeInitLayerField = OverworldBiomeSource.class.getDeclaredField(
|
||||
Refraction.pickName("legacyBiomeInitLayer", "i"));
|
||||
legacyBiomeInitLayerField.setAccessible(true);
|
||||
Field largeBiomesField = OverworldBiomeSource.class.getDeclaredField(Refraction.pickName("largeBiomes", "j"));
|
||||
largeBiomesField.setAccessible(true);
|
||||
Field biomeRegistryField = OverworldBiomeSource.class.getDeclaredField(Refraction.pickName("biomes", "k"));
|
||||
biomeRegistryField.setAccessible(true);
|
||||
Field areaLazyField = Layer.class.getDeclaredField(Refraction.pickName("area", "b"));
|
||||
areaLazyField.setAccessible(true);
|
||||
Method initAreaFactoryMethod = Layers.class.getDeclaredMethod(
|
||||
Refraction.pickName("getDefaultLayer", "a"),
|
||||
boolean.class,
|
||||
int.class,
|
||||
int.class,
|
||||
LongFunction.class
|
||||
);
|
||||
initAreaFactoryMethod.setAccessible(true);
|
||||
|
||||
//init new WorldChunkManagerOverworld
|
||||
boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(biomeSource);
|
||||
boolean largebiomes = largeBiomesField.getBoolean(biomeSource);
|
||||
Registry<Biome> biomeRegistryMojang = (Registry<Biome>) biomeRegistryField.get(biomeSource);
|
||||
Registry<Biome> biomeRegistry;
|
||||
if (options.hasBiomeType()) {
|
||||
Biome biome = BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options.getBiomeType().getId()));
|
||||
biomeRegistry = new MappedRegistry<>(
|
||||
ResourceKey.createRegistryKey(new ResourceLocation("fawe_biomes")),
|
||||
Lifecycle.experimental()
|
||||
);
|
||||
((MappedRegistry) biomeRegistry).registerMapping(0, BuiltinRegistries.BIOME.getResourceKey(biome).get(), biome,
|
||||
Lifecycle.experimental()
|
||||
);
|
||||
} else {
|
||||
biomeRegistry = biomeRegistryMojang;
|
||||
}
|
||||
|
||||
//replace genLayer
|
||||
AreaFactory<FastAreaLazy> factory = (AreaFactory<FastAreaLazy>) initAreaFactoryMethod.invoke(
|
||||
null,
|
||||
legacyBiomeInitLayer,
|
||||
largebiomes ? 6 : 4,
|
||||
4,
|
||||
(LongFunction) (salt -> new FastWorldGenContextArea(seed, salt))
|
||||
);
|
||||
biomeSource = new FastOverworldBiomeSource(biomeRegistry, new FastGenLayer(factory));
|
||||
|
||||
return biomeSource;
|
||||
}
|
||||
|
||||
private static class FastOverworldBiomeSource extends BiomeSource {
|
||||
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final boolean isSingleRegistry;
|
||||
private final FastGenLayer fastGenLayer;
|
||||
|
||||
public FastOverworldBiomeSource(
|
||||
Registry<Biome> biomeRegistry,
|
||||
FastGenLayer genLayer
|
||||
) {
|
||||
super(biomeRegistry.stream().collect(Collectors.toList()));
|
||||
this.biomeRegistry = biomeRegistry;
|
||||
this.isSingleRegistry = biomeRegistry.entrySet().size() == 1;
|
||||
this.fastGenLayer = genLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Codec<? extends BiomeSource> codec() {
|
||||
return OverworldBiomeSource.CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeSource withSeed(final long seed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (this.isSingleRegistry) {
|
||||
return this.biomeRegistry.byId(0);
|
||||
}
|
||||
return this.fastGenLayer.get(this.biomeRegistry, biomeX, biomeZ);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FastWorldGenContextArea implements BigContext<FastAreaLazy> {
|
||||
|
||||
private final ConcurrentHashMap<Long, Integer> sharedAreaMap = new ConcurrentHashMap<>();
|
||||
private final ImprovedNoise improvedNoise;
|
||||
private final long magicrandom;
|
||||
private final ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(); //needed for multithreaded generation
|
||||
|
||||
public FastWorldGenContextArea(long seed, long lconst) {
|
||||
this.magicrandom = mix(seed, lconst);
|
||||
this.improvedNoise = new ImprovedNoise(new SimpleRandomSource(seed));
|
||||
}
|
||||
|
||||
private static long mix(long seed, long salt) {
|
||||
long l = LinearCongruentialGenerator.next(salt, salt);
|
||||
l = LinearCongruentialGenerator.next(l, salt);
|
||||
l = LinearCongruentialGenerator.next(l, salt);
|
||||
long m = LinearCongruentialGenerator.next(seed, l);
|
||||
m = LinearCongruentialGenerator.next(m, l);
|
||||
m = LinearCongruentialGenerator.next(m, l);
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastAreaLazy createResult(PixelTransformer pixelTransformer) {
|
||||
return new FastAreaLazy(sharedAreaMap, pixelTransformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initRandom(long x, long z) {
|
||||
long l = this.magicrandom;
|
||||
l = LinearCongruentialGenerator.next(l, x);
|
||||
l = LinearCongruentialGenerator.next(l, z);
|
||||
l = LinearCongruentialGenerator.next(l, x);
|
||||
l = LinearCongruentialGenerator.next(l, z);
|
||||
this.map.put(Thread.currentThread().getId(), l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextRandom(int y) {
|
||||
long tid = Thread.currentThread().getId();
|
||||
long e = this.map.computeIfAbsent(tid, i -> 0L);
|
||||
int mod = (int) Math.floorMod(e >> 24L, (long) y);
|
||||
this.map.put(tid, LinearCongruentialGenerator.next(e, this.magicrandom));
|
||||
return mod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImprovedNoise getBiomeNoise() {
|
||||
return this.improvedNoise;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FastGenLayer extends Layer {
|
||||
|
||||
private final FastAreaLazy fastAreaLazy;
|
||||
|
||||
public FastGenLayer(AreaFactory<FastAreaLazy> factory) {
|
||||
super(() -> null);
|
||||
this.fastAreaLazy = factory.make();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome get(Registry<Biome> registry, int x, int z) {
|
||||
ResourceKey<Biome> key = Biomes.byId(this.fastAreaLazy.get(x, z));
|
||||
if (key == null) {
|
||||
return registry.get(Biomes.byId(0));
|
||||
}
|
||||
Biome biome = registry.get(key);
|
||||
if (biome == null) {
|
||||
return registry.get(Biomes.byId(0));
|
||||
}
|
||||
return biome;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private record FastAreaLazy(ConcurrentHashMap<Long, Integer> sharedMap, PixelTransformer transformer) implements Area {
|
||||
|
||||
@Override
|
||||
public int get(int x, int z) {
|
||||
long zx = ChunkPos.asLong(x, z);
|
||||
return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
// avoid warning on paper
|
||||
public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world, ServerLevel serverLevel) {
|
||||
super(pos, upgradeData, world, serverLevel);
|
||||
}
|
||||
|
||||
// compatibility with spigot
|
||||
public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor) {
|
||||
super(pos, upgradeData, levelHeightAccessor);
|
||||
}
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
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 sendMessage(Component message, ChatType type, UUID sender) {
|
||||
}
|
||||
|
||||
@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) {
|
||||
}
|
||||
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2;
|
||||
|
||||
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.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_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<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
|
||||
private final PaperweightAdapter adapter;
|
||||
private final WeakReference<ServerLevel> world;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> 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());
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//FAWE start
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
//FAWE end
|
||||
|
||||
}
|
@ -1,700 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_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_18_R2.PaperweightAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_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.nbt.IntTag;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
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_18_R2.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
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;
|
||||
|
||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
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<String, List<Property<?>>> 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<net.minecraft.nbt.Tag> 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<String, List<Property<?>>> 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<Boolean>) 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<Integer>) 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 Registry.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<SideEffect> 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<CompoundBinaryTag> 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<String, BinaryTag> 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 <B extends BlockStateHolder<B>> 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 && map.wasAccessibleSinceLastSave()) {
|
||||
// PlayerChunk.d players = map.players;
|
||||
Stream<ServerPlayer> 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<String, ? extends Property<?>> 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(
|
||||
Registry.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<BlockPos, CraftBlockState> 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 List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
||||
// Quickly add each entity to a list copy.
|
||||
List<Entity> mcEntities = new ArrayList<>();
|
||||
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
||||
|
||||
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
||||
mcEntities.forEach((mcEnt) -> {
|
||||
org.bukkit.entity.Entity bukkitEntity = mcEnt.getBukkitEntity();
|
||||
if (bukkitEntity.isValid()) {
|
||||
list.add(bukkitEntity);
|
||||
}
|
||||
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
@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 BinaryTag toNativeBinary(final net.minecraft.nbt.Tag foreign) {
|
||||
return parent.toNativeBinary(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.nbt.Tag fromNative(Tag foreign) {
|
||||
if (foreign instanceof PaperweightLazyCompoundTag) {
|
||||
return ((PaperweightLazyCompoundTag) foreign).get();
|
||||
}
|
||||
return (net.minecraft.nbt.Tag) 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<Biome> registry = MinecraftServer
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
|
||||
Biome biome = registry.get(resourceLocation);
|
||||
return registry.getId(biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<NamespacedKey> getRegisteredBiomes() {
|
||||
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) ((CraftServer) Bukkit.getServer())
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(
|
||||
Registry.BIOME_REGISTRY);
|
||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||
for (ResourceLocation key : keys) {
|
||||
try {
|
||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||
}
|
||||
}
|
||||
return namespacedKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelighterFactory getRelighterFactory() {
|
||||
if (PaperLib.isPaper()) {
|
||||
return new PaperweightStarlightRelighterFactory();
|
||||
} else {
|
||||
return new NMSRelighterFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Property<?>>> getAllProperties() {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
init();
|
||||
return allBlockProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getTickingPostProcessor() {
|
||||
return new PaperweightPostProcessor();
|
||||
}
|
||||
|
||||
}
|
@ -1,286 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_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_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_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<LevelChunk,
|
||||
net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
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> level;
|
||||
private final AtomicInteger lastTick;
|
||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> 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<CachedChange> changes = Set.copyOf(cachedChanges);
|
||||
cachedChanges.clear();
|
||||
final Set<IntPair> toSend;
|
||||
if (sendChunks) {
|
||||
toSend = Set.copyOf(cachedChunksToSend);
|
||||
cachedChunksToSend.clear();
|
||||
} else {
|
||||
toSend = Collections.emptySet();
|
||||
}
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
if (!sendChunks) {
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
TaskManager.taskManager().sync(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
}
|
||||
|
||||
private record CachedChange(
|
||||
LevelChunk levelChunk,
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,236 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.queue.IBlocks;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
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.BlockTypesCache;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private PalettedContainer<Holder<Biome>>[] biomes = null;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
this.serverLevel = levelChunk.level;
|
||||
this.minHeight = serverLevel.getMinBuildHeight();
|
||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
this.blocks = new char[getSectionCount()][];
|
||||
}
|
||||
|
||||
protected void storeTile(BlockEntity blockEntity) {
|
||||
tiles.put(
|
||||
BlockVector3.at(
|
||||
blockEntity.getBlockPos().getX(),
|
||||
blockEntity.getBlockPos().getY(),
|
||||
blockEntity.getBlockPos().getZ()
|
||||
),
|
||||
new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
entities.add((CompoundTag) adapter.toNative(compoundTag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
for (CompoundTag tag : entities) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCreateCopy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeightmapToGet(HeightMapType type, int[] data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxY() {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return minHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSectionPosition() {
|
||||
return maxHeight >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinSectionPosition() {
|
||||
return minHeight >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()].get(x >> 2, (y & 15) >> 2, z >> 2);
|
||||
return PaperweightPlatformAdapter.adapt(biome, serverLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSectionLighting(int layer, boolean sky) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive, int layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlocks reset() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionCount() {
|
||||
return serverLevel.getSectionsCount();
|
||||
}
|
||||
|
||||
protected void storeSection(int layer, char[] data) {
|
||||
blocks[layer] = data;
|
||||
}
|
||||
|
||||
protected void storeBiomes(int layer, PalettedContainer<Holder<Biome>> biomeData) {
|
||||
if (biomes == null) {
|
||||
biomes = new PalettedContainer[getSectionCount()];
|
||||
}
|
||||
biomes[layer] = biomeData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
BlockState state = BlockTypesCache.states[get(x, y, z)];
|
||||
return state.toBaseBlock(this, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer] != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] loadIfPresent(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmittedLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getHeightMap(HeightMapType type) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public char get(int x, int y, int z) {
|
||||
final int layer = (y >> 4) - getMinSectionPosition();
|
||||
final int index = (y & 15) << 8 | z << 4 | x;
|
||||
return blocks[layer][index];
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,663 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
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 net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.core.Registry;
|
||||
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.server.level.TicketType;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.SimpleBitStorage;
|
||||
import net.minecraft.util.ThreadingDetector;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.util.ZeroBitStorage;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
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.chunk.GlobalPalette;
|
||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
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 org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
public static final Field fieldData;
|
||||
|
||||
public static final Constructor<?> dataConstructor;
|
||||
|
||||
public static final Field fieldStorage;
|
||||
public static final Field fieldPalette;
|
||||
|
||||
private static final Field fieldTickingFluidCount;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldThreadingDetector;
|
||||
private static final long fieldThreadingDetectorOffset;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final MethodHandle methodRemoveGameEventListener;
|
||||
private static final MethodHandle methodremoveTickingBlockEntity;
|
||||
|
||||
private static final Field fieldRemove;
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
||||
fieldData.setAccessible(true);
|
||||
|
||||
Class<?> dataClazz = fieldData.getType();
|
||||
dataConstructor = dataClazz.getDeclaredConstructors()[0];
|
||||
dataConstructor.setAccessible(true);
|
||||
|
||||
fieldStorage = dataClazz.getDeclaredField(Refraction.pickName("storage", "b"));
|
||||
fieldStorage.setAccessible(true);
|
||||
fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c"));
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "h"));
|
||||
fieldTickingFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g"));
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f"));
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
||||
"getVisibleChunkIfPresent",
|
||||
"b"
|
||||
), long.class);
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
|
||||
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector);
|
||||
|
||||
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldThreadingDetector = null;
|
||||
fieldThreadingDetectorOffset = -1;
|
||||
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
|
||||
Refraction.pickName("removeGameEventListener", "d"),
|
||||
BlockEntity.class
|
||||
);
|
||||
removeGameEventListener.setAccessible(true);
|
||||
methodRemoveGameEventListener = MethodHandles.lookup().unreflect(removeGameEventListener);
|
||||
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
|
||||
Refraction.pickName(
|
||||
"removeBlockEntityTicker",
|
||||
"l"
|
||||
), BlockPos.class
|
||||
);
|
||||
removeBlockEntityTicker.setAccessible(true);
|
||||
methodremoveTickingBlockEntity = MethodHandles.lookup().unreflect(removeBlockEntityTicker);
|
||||
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable rethrow) {
|
||||
rethrow.printStackTrace();
|
||||
throw new RuntimeException(rethrow);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean setSectionAtomic(
|
||||
LevelChunkSection[] sections,
|
||||
LevelChunkSection expected,
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// There is no point in having a functional semaphore for paper servers.
|
||||
private static final ThreadLocal<DelegateSemaphore> SEMAPHORE_THREAD_LOCAL =
|
||||
ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null));
|
||||
|
||||
static DelegateSemaphore applyLock(LevelChunkSection section) {
|
||||
if (PaperLib.isPaper()) {
|
||||
return SEMAPHORE_THREAD_LOCAL.get();
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject(
|
||||
blocks,
|
||||
fieldThreadingDetectorOffset
|
||||
);
|
||||
synchronized (currentThreadingDetector) {
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
||||
if (nmsChunk != null) {
|
||||
return nmsChunk;
|
||||
}
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
} else {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
// Avoid "async" methods from the main thread.
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||
try {
|
||||
CraftChunk chunk;
|
||||
try {
|
||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
String world = serverLevel.getWorld().getName();
|
||||
// We've already taken 10 seconds we can afford to wait a little here.
|
||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||
if (loaded) {
|
||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||
// Retry chunk load
|
||||
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
||||
}
|
||||
}
|
||||
return chunk.getHandle();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
// Ensure chunk is definitely loaded before applying a ticket
|
||||
net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||
.getChunkSource()
|
||||
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||
}
|
||||
|
||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||
try {
|
||||
return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ));
|
||||
} catch (Throwable thr) {
|
||||
throw new RuntimeException(thr);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
||||
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||
if (chunkHolder == null) {
|
||||
return;
|
||||
}
|
||||
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
|
||||
// UNLOADED_CHUNK
|
||||
Optional<LevelChunk> optional = ((Either) chunkHolder
|
||||
.getTickingChunkFuture()
|
||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
|
||||
if (PaperLib.isPaper()) {
|
||||
// getChunkAtIfLoadedImmediately is paper only
|
||||
optional = optional.or(() -> Optional.ofNullable(nmsWorld
|
||||
.getChunkSource()
|
||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ)));
|
||||
}
|
||||
if (optional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LevelChunk levelChunk = optional.get();
|
||||
TaskManager.taskManager().task(() -> {
|
||||
ClientboundLevelChunkWithLightPacket packet;
|
||||
if (PaperLib.isPaper()) {
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
false // last false is to not bother with x-ray
|
||||
);
|
||||
} else {
|
||||
// deprecated on paper - deprecation suppressed
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
});
|
||||
}
|
||||
|
||||
private static List<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
|
||||
return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false);
|
||||
}
|
||||
|
||||
/*
|
||||
NMS conversion
|
||||
*/
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final char[] blocks,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes);
|
||||
}
|
||||
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer, biomeRegistry, biomes);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
|
||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int num_palette;
|
||||
if (get == null) {
|
||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, null);
|
||||
} else {
|
||||
num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter, null);
|
||||
}
|
||||
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (bitsPerEntry > 0 && bitsPerEntry < 5) {
|
||||
bitsPerEntry = 4;
|
||||
} else if (bitsPerEntry > 8) {
|
||||
bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1);
|
||||
}
|
||||
|
||||
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
|
||||
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
|
||||
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) {
|
||||
blockStates[i] = 0;
|
||||
}
|
||||
} else {
|
||||
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntryNonZero, 4096, blockStates);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
|
||||
final BitStorage nmsBits;
|
||||
if (bitsPerEntry == 0) {
|
||||
nmsBits = new ZeroBitStorage(4096);
|
||||
} else {
|
||||
nmsBits = new SimpleBitStorage(bitsPerEntry, 4096, bits);
|
||||
}
|
||||
List<net.minecraft.world.level.block.state.BlockState> palette;
|
||||
if (bitsPerEntry < 9) {
|
||||
palette = new ArrayList<>();
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
final BlockState state = BlockTypesCache.states[ordinal];
|
||||
palette.add(((PaperweightBlockMaterial) state.getMaterial()).getState());
|
||||
}
|
||||
} else {
|
||||
palette = List.of();
|
||||
}
|
||||
|
||||
// Create palette with data
|
||||
@SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot
|
||||
final PalettedContainer<net.minecraft.world.level.block.state.BlockState> blockStatePalettedContainer =
|
||||
new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry),
|
||||
nmsBits,
|
||||
palette
|
||||
);
|
||||
if (biomes == null) {
|
||||
IdMap<Holder<Biome>> biomeHolderIdMap = biomeRegistry.asHolderIdMap();
|
||||
biomes = new PalettedContainer<>(
|
||||
biomeHolderIdMap,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(
|
||||
BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
return new LevelChunkSection(layer, blockStatePalettedContainer, biomes);
|
||||
} catch (final Throwable e) {
|
||||
throw e;
|
||||
} finally {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
Arrays.fill(paletteToBlock, Integer.MAX_VALUE);
|
||||
Arrays.fill(blockStates, 0);
|
||||
Arrays.fill(blocksCopy, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Only deprecated in paper
|
||||
private static LevelChunkSection newChunkSection(
|
||||
int layer,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return new LevelChunkSection(layer, biomeRegistry);
|
||||
}
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
Blocks.AIR.defaultBlockState(),
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
null
|
||||
);
|
||||
return new LevelChunkSection(layer, dataPaletteBlocks, biomes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PalettedContainer<Biome>}. Should only be used if no biome container existed beforehand.
|
||||
*/
|
||||
public static PalettedContainer<Holder<Biome>> getBiomePalettedContainer(
|
||||
BiomeType[] biomes,
|
||||
IdMap<Holder<Biome>> biomeRegistry
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return null;
|
||||
}
|
||||
BukkitImplAdapter<?> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
// Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length
|
||||
Map<BiomeType, Holder<Biome>> palette = new HashMap<>();
|
||||
for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) {
|
||||
Holder<Biome> biome;
|
||||
if (biomeType == null) {
|
||||
biome = biomeRegistry.byId(adapter.getInternalBiomeId(BiomeTypes.PLAINS));
|
||||
} else {
|
||||
biome = biomeRegistry.byId(adapter.getInternalBiomeId(biomeType));
|
||||
}
|
||||
palette.put(biomeType, biome);
|
||||
}
|
||||
int biomeCount = palette.size();
|
||||
int bitsPerEntry = MathMan.log2nlz(biomeCount - 1);
|
||||
Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration(
|
||||
new FakeIdMapBiome(biomeCount),
|
||||
bitsPerEntry
|
||||
);
|
||||
if (bitsPerEntry > 3) {
|
||||
bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1);
|
||||
}
|
||||
PalettedContainer<Holder<Biome>> biomePalettedContainer = new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
|
||||
final Palette<Holder<Biome>> biomePalette;
|
||||
if (bitsPerEntry == 0) {
|
||||
biomePalette = new SingleValuePalette<>(
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else if (bitsPerEntry == 4) {
|
||||
biomePalette = LinearPalette.create(
|
||||
4,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else if (bitsPerEntry < 9) {
|
||||
biomePalette = HashMapPalette.create(
|
||||
bitsPerEntry,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else {
|
||||
biomePalette = GlobalPalette.create(
|
||||
bitsPerEntry,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
null // unused
|
||||
);
|
||||
}
|
||||
|
||||
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
|
||||
final int arrayLength = MathMan.ceilZero(64f / blocksPerLong);
|
||||
|
||||
|
||||
BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage(
|
||||
bitsPerEntry,
|
||||
64,
|
||||
new long[arrayLength]
|
||||
);
|
||||
|
||||
try {
|
||||
Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette);
|
||||
fieldData.set(biomePalettedContainer, data);
|
||||
int index = 0;
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int z = 0; z < 4; z++) {
|
||||
for (int x = 0; x < 4; x++, index++) {
|
||||
BiomeType biomeType = biomes[index];
|
||||
if (biomeType == null) {
|
||||
continue;
|
||||
}
|
||||
Holder<Biome> biome = biomeRegistry.byId(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(biomeType));
|
||||
if (biome == null) {
|
||||
continue;
|
||||
}
|
||||
biomePalettedContainer.set(x, y, z, biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return biomePalettedContainer;
|
||||
}
|
||||
|
||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
||||
fieldTickingFluidCount.setShort(section, (short) 0);
|
||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
||||
}
|
||||
|
||||
public static BiomeType adapt(Holder<Biome> biome, LevelAccessor levelAccessor) {
|
||||
final Registry<Biome> biomeRegistry = levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
||||
if (biomeRegistry.getKey(biome.value()) == null) {
|
||||
return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN
|
||||
: null;
|
||||
}
|
||||
return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
||||
try {
|
||||
// Do the method ourselves to avoid trying to reflect generic method parameters
|
||||
// similar to removeGameEventListener
|
||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
||||
if (blockEntity != null) {
|
||||
if (!levelChunk.level.isClientSide) {
|
||||
methodRemoveGameEventListener.invoke(levelChunk, beacon);
|
||||
}
|
||||
fieldRemove.set(beacon, true);
|
||||
}
|
||||
}
|
||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static List<Entity> getEntities(LevelChunk chunk) {
|
||||
return chunk.level.entityManager.getEntities(chunk.getPos());
|
||||
}
|
||||
|
||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||
|
||||
@Override
|
||||
public int getId(final net.minecraft.world.level.block.state.BlockState entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState byId(final int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<net.minecraft.world.level.block.state.BlockState> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record FakeIdMapBiome(int size) implements IdMap<Biome> {
|
||||
|
||||
@Override
|
||||
public int getId(final Biome entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Biome byId(final int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<Biome> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,541 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field flatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
private static final Field ringPositionsField;
|
||||
private static final Field hasGeneratedPositionsField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "h"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "g"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "K"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "h"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "i"));
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureManager structureManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataPackConfig()
|
||||
);
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable());
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
originalServerWorld.dimensionTypeRegistration(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
// placeholder. Required for new ChunkProviderServer, but we create and then set it later
|
||||
newOpts.dimensions().getOrThrow(levelStemResourceKey).generator(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
private final Holder<Biome> singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.asHolderIdMap().byId(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ,
|
||||
PaperweightRegen.this.chunkGenerator.climateSampler()
|
||||
);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig);
|
||||
}
|
||||
|
||||
//generator
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(originalGenerator.structureSets, generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier =
|
||||
(Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField
|
||||
.get(originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME
|
||||
.asHolderIdMap()
|
||||
.byId(WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())));
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(originalGenerator.structureSets, noiseBasedChunkGenerator.noises,
|
||||
biomeSource, seed,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.delegate;
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(originalGenerator);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(
|
||||
originalGenerator);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(ringPositions);
|
||||
ringPositionsField.set(chunkGenerator, copy);
|
||||
hasGeneratedPositionsField.setBoolean(chunkGenerator, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chunkGenerator.conf = originalServerWorld.spigotConfig;
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = freshChunkProvider.getLightEngine();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,189 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
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_R1.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_R1.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", "aO"));
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -1,286 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
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_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.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<LevelChunk,
|
||||
net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
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> level;
|
||||
private final AtomicInteger lastTick;
|
||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> 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<CachedChange> changes = Set.copyOf(cachedChanges);
|
||||
cachedChanges.clear();
|
||||
final Set<IntPair> toSend;
|
||||
if (sendChunks) {
|
||||
toSend = Set.copyOf(cachedChunksToSend);
|
||||
cachedChunksToSend.clear();
|
||||
} else {
|
||||
toSend = Collections.emptySet();
|
||||
}
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
if (!sendChunks) {
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
TaskManager.taskManager().sync(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
}
|
||||
|
||||
private record CachedChange(
|
||||
LevelChunk levelChunk,
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
|
||||
//TODO un-very-break-this
|
||||
public class PaperweightMapChunkUtil extends MapChunkUtil<ClientboundLevelChunkWithLightPacket> {
|
||||
|
||||
public PaperweightMapChunkUtil() throws NoSuchFieldException {
|
||||
fieldX = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a"));
|
||||
fieldZ = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("x", "a"));
|
||||
fieldBitMask = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("z", "b"));
|
||||
fieldHeightMap = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("heightmaps", "b"));
|
||||
fieldChunkData = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("chunkData", "c"));
|
||||
fieldBlockEntities = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("buffer", "c"));
|
||||
fieldFull = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("blockEntitiesData", "d"));
|
||||
fieldX.setAccessible(true);
|
||||
fieldZ.setAccessible(true);
|
||||
fieldBitMask.setAccessible(true);
|
||||
fieldHeightMap.setAccessible(true);
|
||||
fieldChunkData.setAccessible(true);
|
||||
fieldBlockEntities.setAccessible(true);
|
||||
fieldFull.setAccessible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientboundLevelChunkWithLightPacket createPacket() {
|
||||
// TODO ??? return new ClientboundLevelChunkPacket();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
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.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
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.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class PaperweightStarlightRelighter implements Relighter {
|
||||
|
||||
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<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||
|
||||
|
||||
private final ServerLevel serverLevel;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
||||
this.serverLevel = serverLevel;
|
||||
this.delegate = new NMSRelighter(queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
||||
areaLock.lock();
|
||||
try {
|
||||
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
|
||||
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
|
||||
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
|
||||
chunks.add(ChunkPos.asLong(cx, cz));
|
||||
} finally {
|
||||
areaLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLightUpdate(int x, int y, int z) {
|
||||
delegate.addLightUpdate(x, y, z);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called "recursively", iterating and removing elements
|
||||
* from the regions linked map. This way, chunks are loaded in batches to avoid
|
||||
* OOMEs.
|
||||
*/
|
||||
@Override
|
||||
public void fixLightingSafe(boolean sky) {
|
||||
this.areaLock.lock();
|
||||
try {
|
||||
if (regions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LongSet first = regions.removeFirst();
|
||||
fixLighting(first, () -> fixLightingSafe(true));
|
||||
} finally {
|
||||
this.areaLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes a set of chunks and runs an action afterwards.
|
||||
* The action is run async, the chunks are partly processed on the main thread
|
||||
* (as required by the server).
|
||||
*/
|
||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
// convert from long keys to ChunkPos
|
||||
Set<ChunkPos> coords = new HashSet<>();
|
||||
LongIterator iterator = chunks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
coords.add(new ChunkPos(iterator.nextLong()));
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (ChunkPos pos : coords) {
|
||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
pos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
))
|
||||
);
|
||||
}
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
|
||||
invokeRelight(
|
||||
coords,
|
||||
c -> {
|
||||
}, // no callback for single chunks required
|
||||
i -> {
|
||||
if (i != coords.size()) {
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred on relighting", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLighting() {
|
||||
this.delegate.removeLighting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixBlockLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixSkyLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReentrantLock getLock() {
|
||||
return this.lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
}
|
@ -1,564 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field flatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
private static final Field ringPositionsField;
|
||||
private static final Field hasGeneratedPositionsField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "g"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "f"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "L"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "i"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "j"));
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataPackConfig()
|
||||
);
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable());
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
newOpts.dimensions().getOrThrow(levelStemResourceKey),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
private final Holder<Biome> singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.asHolderIdMap().byId(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(originalGenerator.structureSets, generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
|
||||
originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME
|
||||
.asHolderIdMap()
|
||||
.byId(WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())));
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(originalGenerator.structureSets,
|
||||
noiseBasedChunkGenerator.noises,
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
chunkGenerator.conf = freshWorld.spigotConfig;
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(originalGenerator);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(
|
||||
originalGenerator);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(ringPositions);
|
||||
ringPositionsField.set(chunkGenerator, copy);
|
||||
hasGeneratedPositionsField.setBoolean(chunkGenerator, true);
|
||||
}
|
||||
}
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureTemplateManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> retainData(final ChunkAccess chunk) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
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.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
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.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class PaperweightStarlightRelighter implements Relighter {
|
||||
|
||||
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<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||
|
||||
|
||||
private final ServerLevel serverLevel;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
||||
this.serverLevel = serverLevel;
|
||||
this.delegate = new NMSRelighter(queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
||||
areaLock.lock();
|
||||
try {
|
||||
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
|
||||
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
|
||||
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
|
||||
chunks.add(ChunkPos.asLong(cx, cz));
|
||||
} finally {
|
||||
areaLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLightUpdate(int x, int y, int z) {
|
||||
delegate.addLightUpdate(x, y, z);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called "recursively", iterating and removing elements
|
||||
* from the regions linked map. This way, chunks are loaded in batches to avoid
|
||||
* OOMEs.
|
||||
*/
|
||||
@Override
|
||||
public void fixLightingSafe(boolean sky) {
|
||||
this.areaLock.lock();
|
||||
try {
|
||||
if (regions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LongSet first = regions.removeFirst();
|
||||
fixLighting(first, () -> fixLightingSafe(true));
|
||||
} finally {
|
||||
this.areaLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes a set of chunks and runs an action afterwards.
|
||||
* The action is run async, the chunks are partly processed on the main thread
|
||||
* (as required by the server).
|
||||
*/
|
||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
// convert from long keys to ChunkPos
|
||||
Set<ChunkPos> coords = new HashSet<>();
|
||||
LongIterator iterator = chunks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
coords.add(new ChunkPos(iterator.nextLong()));
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (ChunkPos pos : coords) {
|
||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
pos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
))
|
||||
);
|
||||
}
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
|
||||
invokeRelight(
|
||||
coords,
|
||||
c -> {
|
||||
}, // no callback for single chunks required
|
||||
i -> {
|
||||
if (i != coords.size()) {
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred on relighting", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLighting() {
|
||||
this.delegate.removeLighting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixBlockLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixSkyLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReentrantLock getLock() {
|
||||
return this.lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
}
|
@ -1,594 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field flatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
private static final Field generatorStructureStateField;
|
||||
private static final Field ringPositionsField;
|
||||
private static final Field hasGeneratedPositionsField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "e"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "L"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "w"));
|
||||
generatorStructureStateField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
|
||||
Refraction.pickName("hasGeneratedPositions", "h")
|
||||
);
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldOptions originalOpts = originalWorldData.worldGenOptions();
|
||||
WorldOptions newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataConfiguration()
|
||||
);
|
||||
|
||||
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
|
||||
originalWorldData.isFlatWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.FLAT
|
||||
: originalWorldData.isDebugWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.DEBUG
|
||||
: PrimaryLevelData.SpecialWorldProperty.NONE;
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
|
||||
.getOrThrow(levelStemResourceKey),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
|
||||
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
|
||||
originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
|
||||
biomeSource = new FixedBiomeSource(
|
||||
DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
|
||||
origPositions);
|
||||
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
|
||||
ringPositionsField.set(newState, copy);
|
||||
hasGeneratedPositionsField.setBoolean(newState, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(BIOME), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureTemplateManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {
|
||||
}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {
|
||||
});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> retainData(final ChunkAccess chunk) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
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.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
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.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class PaperweightStarlightRelighter implements Relighter {
|
||||
|
||||
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<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||
|
||||
|
||||
private final ServerLevel serverLevel;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
||||
this.serverLevel = serverLevel;
|
||||
this.delegate = new NMSRelighter(queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
||||
areaLock.lock();
|
||||
try {
|
||||
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
|
||||
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
|
||||
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
|
||||
chunks.add(ChunkPos.asLong(cx, cz));
|
||||
} finally {
|
||||
areaLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLightUpdate(int x, int y, int z) {
|
||||
delegate.addLightUpdate(x, y, z);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called "recursively", iterating and removing elements
|
||||
* from the regions linked map. This way, chunks are loaded in batches to avoid
|
||||
* OOMEs.
|
||||
*/
|
||||
@Override
|
||||
public void fixLightingSafe(boolean sky) {
|
||||
this.areaLock.lock();
|
||||
try {
|
||||
if (regions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LongSet first = regions.removeFirst();
|
||||
fixLighting(first, () -> fixLightingSafe(true));
|
||||
} finally {
|
||||
this.areaLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes a set of chunks and runs an action afterwards.
|
||||
* The action is run async, the chunks are partly processed on the main thread
|
||||
* (as required by the server).
|
||||
*/
|
||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
// convert from long keys to ChunkPos
|
||||
Set<ChunkPos> coords = new HashSet<>();
|
||||
LongIterator iterator = chunks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
coords.add(new ChunkPos(iterator.nextLong()));
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (ChunkPos pos : coords) {
|
||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
pos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
))
|
||||
);
|
||||
}
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
|
||||
invokeRelight(
|
||||
coords,
|
||||
c -> {
|
||||
}, // no callback for single chunks required
|
||||
i -> {
|
||||
if (i != coords.size()) {
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred on relighting", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLighting() {
|
||||
this.delegate.removeLighting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixBlockLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixSkyLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReentrantLock getLock() {
|
||||
return this.lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
}
|
@ -1,599 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field flatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
private static final Field generatorStructureStateField;
|
||||
private static final Field ringPositionsField;
|
||||
private static final Field hasGeneratedPositionsField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "e"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "M"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "w"));
|
||||
generatorStructureStateField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
|
||||
Refraction.pickName("hasGeneratedPositions", "h")
|
||||
);
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldOptions originalOpts = originalWorldData.worldGenOptions();
|
||||
WorldOptions newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataConfiguration()
|
||||
);
|
||||
|
||||
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
|
||||
originalWorldData.isFlatWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.FLAT
|
||||
: originalWorldData.isDebugWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.DEBUG
|
||||
: PrimaryLevelData.SpecialWorldProperty.NONE;
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
|
||||
.getOrThrow(levelStemResourceKey),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
|
||||
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
|
||||
originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
|
||||
biomeSource = new FixedBiomeSource(
|
||||
DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
|
||||
origPositions);
|
||||
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
|
||||
ringPositionsField.set(newState, copy);
|
||||
hasGeneratedPositionsField.setBoolean(newState, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(BIOME), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> {
|
||||
final CraftWorld world = freshWorld.getWorld();
|
||||
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
|
||||
blockPopulator.populate(world, random, chunk);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureTemplateManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {
|
||||
}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {
|
||||
});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> retainData(final ChunkAccess chunk) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
applyPaperweightAdapterConfiguration()
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.1-R0.1-20230818.021330-113")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
// The PostProcessor shouldn't be added, but just in case
|
||||
if (!tickFluid) {
|
||||
return;
|
||||
}
|
||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
||||
layer:
|
||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
||||
char[] set = iChunkSet.loadIfPresent(layer);
|
||||
if (set == null) {
|
||||
// No edit means no need to process
|
||||
continue;
|
||||
}
|
||||
char[] get = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean fromGet = false; // Used for liquids
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
||||
// actually being set
|
||||
if (get == null) {
|
||||
continue layer;
|
||||
}
|
||||
fromGet = true;
|
||||
ordinal = replacedOrdinal = get[i];
|
||||
}
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
continue;
|
||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
replacedOrdinal = get[i];
|
||||
}
|
||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
||||
boolean replacedWasLiquid = false;
|
||||
BlockState replacedState = null;
|
||||
if (!ticking) {
|
||||
// If the block being replaced was not ticking, it cannot be a liquid
|
||||
if (!replacedWasTicking) {
|
||||
continue;
|
||||
}
|
||||
// If the block being replaced is not fluid, we do not need to worry
|
||||
if (!(replacedWasLiquid =
|
||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
boolean liquid = state.getMaterial().isLiquid();
|
||||
int x = i & 15;
|
||||
int y = (i >> 8) & 15;
|
||||
int z = (i >> 4) & 15;
|
||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
||||
if (liquid || replacedWasLiquid) {
|
||||
if (liquid) {
|
||||
addFluid(getBlocks.serverLevel, state, position);
|
||||
continue;
|
||||
}
|
||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
||||
// being ticked anyway. We only need it to be "hit" once.
|
||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
||||
if (set == null || get == null) {
|
||||
return false;
|
||||
}
|
||||
char ordinal;
|
||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
if (x > 0 && set[i - 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (x < 15 && set[i + 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z > 0 && set[i - 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z < 15 && set[i + 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y > 0 && set[i - 256] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y < 15 && set[i + 256] != reserved) {
|
||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isFluid(char ordinal) {
|
||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
||||
Fluid type;
|
||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
||||
} else {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
||||
}
|
||||
serverLevel.scheduleTick(
|
||||
position,
|
||||
type,
|
||||
type.getTickDelay(serverLevel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
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.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
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.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class PaperweightStarlightRelighter implements Relighter {
|
||||
|
||||
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<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||
|
||||
|
||||
private final ServerLevel serverLevel;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
||||
this.serverLevel = serverLevel;
|
||||
this.delegate = new NMSRelighter(queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
||||
areaLock.lock();
|
||||
try {
|
||||
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
|
||||
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
|
||||
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
|
||||
chunks.add(ChunkPos.asLong(cx, cz));
|
||||
} finally {
|
||||
areaLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLightUpdate(int x, int y, int z) {
|
||||
delegate.addLightUpdate(x, y, z);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called "recursively", iterating and removing elements
|
||||
* from the regions linked map. This way, chunks are loaded in batches to avoid
|
||||
* OOMEs.
|
||||
*/
|
||||
@Override
|
||||
public void fixLightingSafe(boolean sky) {
|
||||
this.areaLock.lock();
|
||||
try {
|
||||
if (regions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LongSet first = regions.removeFirst();
|
||||
fixLighting(first, () -> fixLightingSafe(true));
|
||||
} finally {
|
||||
this.areaLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes a set of chunks and runs an action afterwards.
|
||||
* The action is run async, the chunks are partly processed on the main thread
|
||||
* (as required by the server).
|
||||
*/
|
||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
// convert from long keys to ChunkPos
|
||||
Set<ChunkPos> coords = new HashSet<>();
|
||||
LongIterator iterator = chunks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
coords.add(new ChunkPos(iterator.nextLong()));
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (ChunkPos pos : coords) {
|
||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
pos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
))
|
||||
);
|
||||
}
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
|
||||
invokeRelight(
|
||||
coords,
|
||||
c -> {
|
||||
}, // no callback for single chunks required
|
||||
i -> {
|
||||
if (i != coords.size()) {
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred on relighting", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLighting() {
|
||||
this.delegate.removeLighting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixBlockLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixSkyLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReentrantLock getLock() {
|
||||
return this.lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||
|
||||
@Override
|
||||
public @Nonnull
|
||||
@SuppressWarnings("rawtypes")
|
||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||
if (w == null) {
|
||||
return NullRelighter.INSTANCE;
|
||||
}
|
||||
return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue);
|
||||
}
|
||||
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.LazyCompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
|
||||
private final Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier;
|
||||
private CompoundTag compoundTag;
|
||||
|
||||
public PaperweightLazyCompoundTag(Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier) {
|
||||
super(new HashMap<>());
|
||||
this.compoundTagSupplier = compoundTagSupplier;
|
||||
}
|
||||
|
||||
public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
this(() -> compoundTag);
|
||||
}
|
||||
|
||||
public net.minecraft.nbt.CompoundTag get() {
|
||||
return compoundTagSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Tag> getValue() {
|
||||
if (compoundTag == null) {
|
||||
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
|
||||
}
|
||||
return compoundTag.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundBinaryTag asBinaryTag() {
|
||||
getValue();
|
||||
return compoundTag.asBinaryTag();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return compoundTagSupplier.get().contains(key);
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
return compoundTagSupplier.get().getByteArray(key);
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
return compoundTagSupplier.get().getByte(key);
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
return compoundTagSupplier.get().getDouble(key);
|
||||
}
|
||||
|
||||
public double asDouble(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsDouble();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
return compoundTagSupplier.get().getFloat(key);
|
||||
}
|
||||
|
||||
public int[] getIntArray(String key) {
|
||||
return compoundTagSupplier.get().getIntArray(key);
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
return compoundTagSupplier.get().getInt(key);
|
||||
}
|
||||
|
||||
public int asInt(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsInt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> getList(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
|
||||
ArrayList<Tag> list = new ArrayList<>();
|
||||
for (net.minecraft.nbt.Tag elem : nbtList) {
|
||||
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
list.add(new PaperweightLazyCompoundTag(compoundTag));
|
||||
} else {
|
||||
list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ListTag getListTag(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag) {
|
||||
return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag);
|
||||
}
|
||||
return new ListTag(StringTag.class, Collections.emptyList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
|
||||
ListTag listTag = getListTag(key);
|
||||
if (listTag.getType().equals(listType)) {
|
||||
return (List<T>) listTag.getValue();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public long[] getLongArray(String key) {
|
||||
return compoundTagSupplier.get().getLongArray(key);
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
return compoundTagSupplier.get().getLong(key);
|
||||
}
|
||||
|
||||
public long asLong(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsLong();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
return compoundTagSupplier.get().getShort(key);
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return compoundTagSupplier.get().getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return compoundTagSupplier.get().toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,594 +0,0 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field flatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
private static final Field generatorStructureStateField;
|
||||
private static final Field ringPositionsField;
|
||||
private static final Field hasGeneratedPositionsField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
/*chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results*/
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
// chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "e"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v"));
|
||||
generatorStructureStateField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
|
||||
Refraction.pickName("hasGeneratedPositions", "h")
|
||||
);
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldOptions originalOpts = originalWorldData.worldGenOptions();
|
||||
WorldOptions newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataConfiguration()
|
||||
);
|
||||
|
||||
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
|
||||
originalWorldData.isFlatWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.FLAT
|
||||
: originalWorldData.isDebugWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.DEBUG
|
||||
: PrimaryLevelData.SpecialWorldProperty.NONE;
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
|
||||
.getOrThrow(levelStemResourceKey),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
originalServerWorld.getRandomSequences(),
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
|
||||
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
|
||||
originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
|
||||
biomeSource = new FixedBiomeSource(
|
||||
DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
|
||||
origPositions);
|
||||
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
|
||||
ringPositionsField.set(newState, copy);
|
||||
hasGeneratedPositionsField.setBoolean(newState, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(BIOME), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> {
|
||||
final CraftWorld world = freshWorld.getWorld();
|
||||
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
|
||||
blockPopulator.populate(world, random, chunk);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureTemplateManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {
|
||||
}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {
|
||||
});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -11,6 +11,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.4-R0.1-20230608.201059-104")
|
||||
// url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.2-R0.1-SNAPSHOT
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.2-R0.1-20231203.034718-121")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
@ -17,31 +17,18 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R2;
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_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.Lists;
|
||||
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;
|
||||
@ -68,22 +55,9 @@ 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.BiomeCategory;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
@ -94,6 +68,9 @@ 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.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
|
||||
@ -105,6 +82,7 @@ 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.RandomSource;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
import net.minecraft.world.Clearable;
|
||||
@ -135,21 +113,39 @@ import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
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.craftbukkit.v1_20_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.util.CraftMagicNumbers;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.enginehub.linbus.common.LinTagId;
|
||||
import org.enginehub.linbus.tree.LinByteArrayTag;
|
||||
import org.enginehub.linbus.tree.LinByteTag;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
import org.enginehub.linbus.tree.LinDoubleTag;
|
||||
import org.enginehub.linbus.tree.LinEndTag;
|
||||
import org.enginehub.linbus.tree.LinFloatTag;
|
||||
import org.enginehub.linbus.tree.LinIntArrayTag;
|
||||
import org.enginehub.linbus.tree.LinIntTag;
|
||||
import org.enginehub.linbus.tree.LinListTag;
|
||||
import org.enginehub.linbus.tree.LinLongArrayTag;
|
||||
import org.enginehub.linbus.tree.LinLongTag;
|
||||
import org.enginehub.linbus.tree.LinShortTag;
|
||||
import org.enginehub.linbus.tree.LinStringTag;
|
||||
import org.enginehub.linbus.tree.LinTag;
|
||||
import org.enginehub.linbus.tree.LinTagType;
|
||||
import org.spigotmc.SpigotConfig;
|
||||
import org.spigotmc.WatchdogThread;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -157,6 +153,7 @@ import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -171,20 +168,21 @@ 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<net.minecraft.nbt.Tag> {
|
||||
|
||||
private final Logger LOGGER = Logger.getLogger(getClass().getCanonicalName());
|
||||
private final Logger logger = Logger.getLogger(getClass().getCanonicalName());
|
||||
|
||||
private final Field serverWorldsField;
|
||||
private final Method getChunkFutureMethod;
|
||||
private final Field chunkProviderExecutorField;
|
||||
private final Watchdog watchdog;
|
||||
|
||||
private static final RandomSource random = RandomSource.create();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that may break between versions of Minecraft
|
||||
// ------------------------------------------------------------------------
|
||||
@ -194,21 +192,21 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
CraftServer.class.cast(Bukkit.getServer());
|
||||
|
||||
int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion();
|
||||
if (dataVersion != 3218) {
|
||||
throw new UnsupportedClassVersionError("Not 1.19.3!");
|
||||
if (dataVersion != 3578) {
|
||||
throw new UnsupportedClassVersionError("Not 1.20.2!");
|
||||
}
|
||||
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
getChunkFutureMethod = ServerChunkCache.class.getDeclaredMethod(
|
||||
Refraction.pickName("getChunkFutureMainThread", "c"),
|
||||
int.class, int.class, ChunkStatus.class, boolean.class
|
||||
Refraction.pickName("getChunkFutureMainThread", "c"),
|
||||
int.class, int.class, ChunkStatus.class, boolean.class
|
||||
);
|
||||
getChunkFutureMethod.setAccessible(true);
|
||||
|
||||
chunkProviderExecutorField = ServerChunkCache.class.getDeclaredField(
|
||||
Refraction.pickName("mainThreadProcessor", "g")
|
||||
Refraction.pickName("mainThreadProcessor", "g")
|
||||
);
|
||||
chunkProviderExecutorField.setAccessible(true);
|
||||
|
||||
@ -293,12 +291,11 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
private static Block getBlockFromType(BlockType blockType) {
|
||||
|
||||
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK).get(ResourceLocation.tryParse(blockType.getId()));
|
||||
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK).get(ResourceLocation.tryParse(blockType.id()));
|
||||
}
|
||||
|
||||
private static Item getItemFromType(ItemType itemType) {
|
||||
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(itemType.getId()));
|
||||
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(itemType.id()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -318,6 +315,29 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
return combinedId == 0 && state.getBlockType() != BlockTypes.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId);
|
||||
}
|
||||
|
||||
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
|
||||
int internalId = Block.getId(blockState);
|
||||
BlockState state = BlockStateIdAccess.getBlockStateById(internalId);
|
||||
if (state == null) {
|
||||
state = BukkitAdapter.adapt(CraftBlockData.createData(blockState));
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public BiomeType adapt(Biome biome) {
|
||||
var mcBiome = ((CraftServer) Bukkit.getServer()).getServer().registryAccess().registryOrThrow(Registries.BIOME).getKey(biome);
|
||||
if (mcBiome == null) {
|
||||
return null;
|
||||
}
|
||||
return BiomeType.REGISTRY.get(mcBiome.toString());
|
||||
}
|
||||
|
||||
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
|
||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||
return Block.stateById(internalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(Location location) {
|
||||
checkNotNull(location);
|
||||
@ -331,14 +351,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
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;
|
||||
return adapt(blockData);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -358,16 +371,50 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
BlockEntity te = chunk.getBlockEntity(blockPos);
|
||||
if (te != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = te.saveWithId();
|
||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
||||
return state.toBaseBlock((LinCompoundTag) toNativeLin(tag));
|
||||
}
|
||||
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
|
||||
private static final HashMap<BiomeType, Holder<Biome>> biomeTypeToNMSCache = new HashMap<>();
|
||||
private static final HashMap<Holder<Biome>, BiomeType> biomeTypeFromNMSCache = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||
public BiomeType getBiome(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);
|
||||
|
||||
return biomeTypeFromNMSCache.computeIfAbsent(chunk.getNoiseBiome(x >> 2, y >> 2, z >> 2), b -> BiomeType.REGISTRY.get(b.unwrapKey().get().location().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(Location location, BiomeType biome) {
|
||||
checkNotNull(location);
|
||||
checkNotNull(biome);
|
||||
|
||||
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);
|
||||
chunk.setBiome(x >> 2, y >> 2, z >> 2, biomeTypeToNMSCache.computeIfAbsent(biome, b -> ((CraftServer) Bukkit.getServer()).getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(ResourceKey.create(Registries.BIOME, new ResourceLocation(b.id())))));
|
||||
chunk.setUnsaved(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(World world) {
|
||||
return new PaperweightWorldNativeAccess(this,
|
||||
new WeakReference<>(((CraftWorld) world).getHandle()));
|
||||
new WeakReference<>(((CraftWorld) world).getHandle()));
|
||||
}
|
||||
|
||||
private static net.minecraft.core.Direction adapt(Direction face) {
|
||||
@ -390,13 +437,13 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private net.minecraft.world.level.block.state.BlockState applyProperties(
|
||||
StateDefinition<Block, net.minecraft.world.level.block.state.BlockState> stateContainer,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
Map<Property<?>, Object> states
|
||||
StateDefinition<Block, net.minecraft.world.level.block.state.BlockState> stateContainer,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
Map<Property<?>, Object> states
|
||||
) {
|
||||
for (Map.Entry<Property<?>, Object> state : states.entrySet()) {
|
||||
net.minecraft.world.level.block.state.properties.Property<?> property =
|
||||
stateContainer.getProperty(state.getKey().getName());
|
||||
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) {
|
||||
@ -405,16 +452,16 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
} 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
|
||||
)
|
||||
);
|
||||
.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
|
||||
(net.minecraft.world.level.block.state.properties.Property) property,
|
||||
(Comparable) value
|
||||
);
|
||||
}
|
||||
return newState;
|
||||
@ -438,7 +485,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
readEntityIntoTag(mcEntity, tag);
|
||||
return new BaseEntity(
|
||||
com.sk89q.worldedit.world.entity.EntityTypes.get(id),
|
||||
LazyReference.from(() -> (CompoundBinaryTag) toNativeBinary(tag))
|
||||
LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag))
|
||||
);
|
||||
}
|
||||
|
||||
@ -451,12 +498,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
ServerLevel worldServer = craftWorld.getHandle();
|
||||
|
||||
Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle());
|
||||
Entity createdEntity = createEntityFromId(state.getType().id(), craftWorld.getHandle());
|
||||
|
||||
if (createdEntity != null) {
|
||||
CompoundBinaryTag nativeTag = state.getNbt();
|
||||
LinCompoundTag nativeTag = state.getNbt();
|
||||
if (nativeTag != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(nativeTag);
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeLin(nativeTag);
|
||||
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||
tag.remove(name);
|
||||
}
|
||||
@ -504,24 +551,42 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private static final LoadingCache<net.minecraft.world.level.block.state.properties.Property, Property<?>> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<net.minecraft.world.level.block.state.properties.Property, Property<?>>() {
|
||||
@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<Direction>) 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<String>) 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());
|
||||
}
|
||||
}
|
||||
});
|
||||
private static final LoadingCache<net.minecraft.world.level.block.state.properties.Property, Property<?>> PROPERTY_CACHE = CacheBuilder
|
||||
.newBuilder()
|
||||
.build(new CacheLoader<>() {
|
||||
@Override
|
||||
public Property<?> load(net.minecraft.world.level.block.state.properties.Property state) {
|
||||
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(),
|
||||
new ArrayList<>((List<Direction>) state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> Direction.valueOf(((StringRepresentable) e)
|
||||
.getSerializedName()
|
||||
.toUpperCase(Locale.ROOT)))
|
||||
.toList())
|
||||
);
|
||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
|
||||
return new EnumProperty(
|
||||
state.getName(),
|
||||
new ArrayList<>((List<String>) state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> ((StringRepresentable) e).getSerializedName())
|
||||
.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
|
||||
@ -529,7 +594,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
Map<String, Property<?>> properties = new TreeMap<>();
|
||||
Block block = getBlockFromType(blockType);
|
||||
StateDefinition<Block, net.minecraft.world.level.block.state.BlockState> blockStateList =
|
||||
block.getStateDefinition();
|
||||
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);
|
||||
@ -538,28 +603,28 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) {
|
||||
public void sendFakeNBT(Player player, BlockVector3 pos, LinCompoundTag nbtData) {
|
||||
((CraftPlayer) player).getHandle().connection.send(ClientboundBlockEntityDataPacket.create(
|
||||
new StructureBlockEntity(
|
||||
new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()),
|
||||
new BlockPos(pos.x(), pos.y(), pos.z()),
|
||||
Blocks.STRUCTURE_BLOCK.defaultBlockState()
|
||||
),
|
||||
__ -> (net.minecraft.nbt.CompoundTag) fromNativeBinary(nbtData)
|
||||
__ -> (net.minecraft.nbt.CompoundTag) fromNativeLin(nbtData)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFakeOP(Player player) {
|
||||
((CraftPlayer) player).getHandle().connection.send(new ClientboundEntityEventPacket(
|
||||
((CraftPlayer) player).getHandle(), (byte) 28
|
||||
((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()
|
||||
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(item.getType().id())),
|
||||
item.getAmount()
|
||||
);
|
||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData())));
|
||||
return CraftItemStack.asCraftMirror(stack);
|
||||
@ -569,12 +634,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
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())));
|
||||
weStack.setNbt((LinCompoundTag) toNativeLin(nmsStack.getTag()));
|
||||
return weStack;
|
||||
}
|
||||
|
||||
private final LoadingCache<ServerLevel, PaperweightFakePlayer> fakePlayers
|
||||
= CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new));
|
||||
= CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new));
|
||||
|
||||
@Override
|
||||
public boolean simulateItemUse(org.bukkit.World world, BlockVector3 position, BaseItem item, Direction face) {
|
||||
@ -591,15 +656,15 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
return false;
|
||||
}
|
||||
fakePlayer.setItemInHand(InteractionHand.MAIN_HAND, stack);
|
||||
fakePlayer.absMoveTo(position.getBlockX(), position.getBlockY(), position.getBlockZ(),
|
||||
(float) face.toVector().toYaw(), (float) face.toVector().toPitch());
|
||||
fakePlayer.absMoveTo(position.x(), position.y(), position.z(),
|
||||
(float) face.toVector().toYaw(), (float) face.toVector().toPitch());
|
||||
|
||||
final BlockPos blockPos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
||||
final BlockPos blockPos = new BlockPos(position.x(), position.y(), position.z());
|
||||
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);
|
||||
InteractionResult result = stack.useOn(context);
|
||||
if (result != InteractionResult.SUCCESS) {
|
||||
if (worldServer.getBlockState(blockPos).use(worldServer, fakePlayer, InteractionHand.MAIN_HAND, rayTrace).consumesAction()) {
|
||||
result = InteractionResult.SUCCESS;
|
||||
@ -612,14 +677,14 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 position, BlockState blockState) {
|
||||
public boolean canPlaceAt(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()));
|
||||
return blockData.canSurvive(((CraftWorld) world).getHandle(), new BlockPos(position.x(), position.y(), position.z()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) {
|
||||
public boolean regenerate(World bukkitWorld, Region region, Extent extent, RegenOptions options) {
|
||||
try {
|
||||
doRegen(bukkitWorld, region, extent, options);
|
||||
} catch (Exception e) {
|
||||
@ -629,7 +694,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) throws Exception {
|
||||
private void doRegen(World bukkitWorld, Region region, Extent extent, RegenOptions options) throws Exception {
|
||||
Environment env = bukkitWorld.getEnvironment();
|
||||
ChunkGenerator gen = bukkitWorld.getGenerator();
|
||||
|
||||
@ -639,50 +704,52 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
try (LevelStorageSource.LevelStorageAccess session = levelStorage.createAccess("faweregentempworld", worldDimKey)) {
|
||||
ServerLevel originalWorld = ((CraftWorld) bukkitWorld).getHandle();
|
||||
PrimaryLevelData levelProperties = (PrimaryLevelData) originalWorld.getServer()
|
||||
.getWorldData().overworldData();
|
||||
.getWorldData().overworldData();
|
||||
WorldOptions originalOpts = levelProperties.worldGenOptions();
|
||||
|
||||
long seed = options.getSeed().orElse(originalWorld.getSeed());
|
||||
WorldOptions newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
? 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()
|
||||
"faweregentempworld",
|
||||
levelProperties.settings.gameType(),
|
||||
levelProperties.settings.hardcore(),
|
||||
levelProperties.settings.difficulty(),
|
||||
levelProperties.settings.allowCommands(),
|
||||
levelProperties.settings.gameRules(),
|
||||
levelProperties.settings.getDataConfiguration()
|
||||
);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
|
||||
levelProperties.isFlatWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.FLAT
|
||||
: levelProperties.isDebugWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.DEBUG
|
||||
: PrimaryLevelData.SpecialWorldProperty.NONE;
|
||||
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()
|
||||
originalWorld.getServer(),
|
||||
originalWorld.getServer().executor,
|
||||
session, newWorldData,
|
||||
originalWorld.dimension(),
|
||||
new LevelStem(
|
||||
originalWorld.dimensionTypeRegistration(),
|
||||
originalWorld.getChunkSource().getGenerator()
|
||||
),
|
||||
new NoOpWorldLoadListener(),
|
||||
originalWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
originalWorld.getRandomSequences(),
|
||||
env,
|
||||
gen,
|
||||
bukkitWorld.getBiomeProvider()
|
||||
);
|
||||
try {
|
||||
regenForWorld(region, extent, freshWorld, options);
|
||||
@ -692,7 +759,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
} finally {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
Map<String, World> map = (Map<String, World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
@ -720,7 +787,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
executor.managedBlock(() -> {
|
||||
// bail out early if a future fails
|
||||
if (chunkLoadings.stream().anyMatch(ftr ->
|
||||
ftr.isDone() && Futures.getUnchecked(ftr) == null
|
||||
ftr.isDone() && Futures.getUnchecked(ftr) == null
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
@ -735,7 +802,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
for (BlockVector3 vec : region) {
|
||||
BlockPos pos = new BlockPos(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ());
|
||||
BlockPos pos = new BlockPos(vec.x(), vec.y(), vec.z());
|
||||
ChunkAccess chunk = chunks.get(new ChunkPos(pos));
|
||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(pos);
|
||||
int internalId = Block.getId(blockData);
|
||||
@ -744,11 +811,11 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(pos);
|
||||
if (blockEntity != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
|
||||
state = state.toBaseBlock(((CompoundBinaryTag) toNativeBinary(tag)));
|
||||
state = state.toBaseBlock((LinCompoundTag) toNativeLin(tag));
|
||||
}
|
||||
extent.setBlock(vec, state.toBaseBlock());
|
||||
if (options.shouldRegenBiomes()) {
|
||||
Biome origBiome = chunk.getNoiseBiome(vec.getX(), vec.getY(), vec.getZ()).value();
|
||||
Biome origBiome = chunk.getNoiseBiome(vec.x(), vec.y(), vec.z()).value();
|
||||
BiomeType adaptedBiome = adapt(serverWorld, origBiome);
|
||||
if (adaptedBiome != null) {
|
||||
extent.setBiome(vec, adaptedBiome);
|
||||
@ -766,9 +833,9 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
try {
|
||||
//noinspection unchecked
|
||||
chunkLoadings.add(
|
||||
((CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>)
|
||||
getChunkFutureMethod.invoke(chunkManager, chunk.getX(), chunk.getZ(), ChunkStatus.FEATURES, true))
|
||||
.thenApply(either -> either.left().orElse(null))
|
||||
((CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>)
|
||||
getChunkFutureMethod.invoke(chunkManager, chunk.x(), chunk.z(), ChunkStatus.FEATURES, true))
|
||||
.thenApply(either -> either.left().orElse(null))
|
||||
);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IllegalStateException("Couldn't load chunk for regen.", e);
|
||||
@ -790,12 +857,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
|
||||
SideEffect.NEIGHBORS,
|
||||
SideEffect.LIGHTING,
|
||||
SideEffect.VALIDATION,
|
||||
SideEffect.ENTITY_AI,
|
||||
SideEffect.EVENTS,
|
||||
SideEffect.UPDATE
|
||||
SideEffect.NEIGHBORS,
|
||||
SideEffect.LIGHTING,
|
||||
SideEffect.VALIDATION,
|
||||
SideEffect.ENTITY_AI,
|
||||
SideEffect.EVENTS,
|
||||
SideEffect.UPDATE
|
||||
);
|
||||
|
||||
@Override
|
||||
@ -804,10 +871,10 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clearContainerBlockContents(org.bukkit.World world, BlockVector3 pt) {
|
||||
public boolean clearContainerBlockContents(World world, BlockVector3 pt) {
|
||||
ServerLevel originalWorld = ((CraftWorld) world).getHandle();
|
||||
|
||||
BlockEntity entity = originalWorld.getBlockEntity(new BlockPos(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()));
|
||||
BlockEntity entity = originalWorld.getBlockEntity(new BlockPos(pt.x(), pt.y(), pt.z()));
|
||||
if (entity instanceof Clearable) {
|
||||
((Clearable) entity).clearContent();
|
||||
return true;
|
||||
@ -815,6 +882,45 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeRegistries() {
|
||||
DedicatedServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
// Biomes
|
||||
for (ResourceLocation name : server.registryAccess().registryOrThrow(Registries.BIOME).keySet()) {
|
||||
if (BiomeType.REGISTRY.get(name.toString()) == null) {
|
||||
BiomeType.REGISTRY.register(name.toString(), new BiomeType(name.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
// BiomeCategories
|
||||
Registry<Biome> biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME);
|
||||
biomeRegistry.getTagNames().forEach(tagKey -> {
|
||||
String key = tagKey.location().toString();
|
||||
if (BiomeCategory.REGISTRY.get(key) == null) {
|
||||
BiomeCategory.REGISTRY.register(key, new BiomeCategory(
|
||||
key,
|
||||
() -> biomeRegistry.getTag(tagKey)
|
||||
.stream()
|
||||
.flatMap(HolderSet.Named::stream)
|
||||
.map(Holder::value)
|
||||
.map(this::adapt)
|
||||
.collect(Collectors.toSet()))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBiomeUpdates(World world, Iterable<BlockVector2> chunks) {
|
||||
ServerLevel originalWorld = ((CraftWorld) world).getHandle();
|
||||
|
||||
List<ChunkAccess> nativeChunks = chunks instanceof Collection<BlockVector2> chunkCollection ? Lists.newArrayListWithCapacity(chunkCollection.size()) : Lists.newArrayList();
|
||||
for (BlockVector2 chunk : chunks) {
|
||||
nativeChunks.add(originalWorld.getChunk(chunk.x(), chunk.z(), ChunkStatus.BIOMES, false));
|
||||
}
|
||||
originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that is less likely to break
|
||||
// ------------------------------------------------------------------------
|
||||
@ -827,51 +933,49 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
* @return native WorldEdit NBT structure
|
||||
*/
|
||||
@Override
|
||||
public BinaryTag toNativeBinary(net.minecraft.nbt.Tag foreign) {
|
||||
public LinTag<?> toNativeLin(net.minecraft.nbt.Tag foreign) {
|
||||
if (foreign == null) {
|
||||
return null;
|
||||
}
|
||||
if (foreign instanceof net.minecraft.nbt.CompoundTag) {
|
||||
Map<String, BinaryTag> values = new HashMap<>();
|
||||
Map<String, LinTag<?>> values = new HashMap<>();
|
||||
Set<String> 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));
|
||||
values.put(str, toNativeLin(base));
|
||||
}
|
||||
return CompoundBinaryTag.from(values);
|
||||
return LinCompoundTag.of(values);
|
||||
} else if (foreign instanceof net.minecraft.nbt.ByteTag) {
|
||||
return ByteBinaryTag.of(((net.minecraft.nbt.ByteTag) foreign).getAsByte());
|
||||
return LinByteTag.of(((net.minecraft.nbt.ByteTag) foreign).getAsByte());
|
||||
} else if (foreign instanceof net.minecraft.nbt.ByteArrayTag) {
|
||||
return ByteArrayBinaryTag.of(((net.minecraft.nbt.ByteArrayTag) foreign).getAsByteArray());
|
||||
return LinByteArrayTag.of(((net.minecraft.nbt.ByteArrayTag) foreign).getAsByteArray());
|
||||
} else if (foreign instanceof net.minecraft.nbt.DoubleTag) {
|
||||
return DoubleBinaryTag.of(((net.minecraft.nbt.DoubleTag) foreign).getAsDouble());
|
||||
return LinDoubleTag.of(((net.minecraft.nbt.DoubleTag) foreign).getAsDouble());
|
||||
} else if (foreign instanceof net.minecraft.nbt.FloatTag) {
|
||||
return FloatBinaryTag.of(((net.minecraft.nbt.FloatTag) foreign).getAsFloat());
|
||||
return LinFloatTag.of(((net.minecraft.nbt.FloatTag) foreign).getAsFloat());
|
||||
} else if (foreign instanceof net.minecraft.nbt.IntTag) {
|
||||
return IntBinaryTag.of(((net.minecraft.nbt.IntTag) foreign).getAsInt());
|
||||
return LinIntTag.of(((net.minecraft.nbt.IntTag) foreign).getAsInt());
|
||||
} else if (foreign instanceof net.minecraft.nbt.IntArrayTag) {
|
||||
return IntArrayBinaryTag.of(((net.minecraft.nbt.IntArrayTag) foreign).getAsIntArray());
|
||||
return LinIntArrayTag.of(((net.minecraft.nbt.IntArrayTag) foreign).getAsIntArray());
|
||||
} else if (foreign instanceof net.minecraft.nbt.LongArrayTag) {
|
||||
return LongArrayBinaryTag.of(((net.minecraft.nbt.LongArrayTag) foreign).getAsLongArray());
|
||||
return LinLongArrayTag.of(((net.minecraft.nbt.LongArrayTag) foreign).getAsLongArray());
|
||||
} else if (foreign instanceof net.minecraft.nbt.ListTag) {
|
||||
try {
|
||||
return toNativeList((net.minecraft.nbt.ListTag) foreign);
|
||||
return toNativeLinList((net.minecraft.nbt.ListTag) foreign);
|
||||
} catch (Throwable e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e);
|
||||
return ListBinaryTag.empty();
|
||||
logger.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e);
|
||||
}
|
||||
} else if (foreign instanceof net.minecraft.nbt.LongTag) {
|
||||
return LongBinaryTag.of(((net.minecraft.nbt.LongTag) foreign).getAsLong());
|
||||
return LinLongTag.of(((net.minecraft.nbt.LongTag) foreign).getAsLong());
|
||||
} else if (foreign instanceof net.minecraft.nbt.ShortTag) {
|
||||
return ShortBinaryTag.of(((net.minecraft.nbt.ShortTag) foreign).getAsShort());
|
||||
return LinShortTag.of(((net.minecraft.nbt.ShortTag) foreign).getAsShort());
|
||||
} else if (foreign instanceof net.minecraft.nbt.StringTag) {
|
||||
return StringBinaryTag.of(foreign.getAsString());
|
||||
return LinStringTag.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());
|
||||
return LinEndTag.instance();
|
||||
}
|
||||
throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -882,14 +986,16 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
* @throws SecurityException on error
|
||||
* @throws IllegalArgumentException on error
|
||||
*/
|
||||
private ListBinaryTag toNativeList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException {
|
||||
ListBinaryTag.Builder values = ListBinaryTag.builder();
|
||||
private LinListTag<?> toNativeLinList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException {
|
||||
LinListTag.Builder<LinTag<?>> builder = LinListTag.builder(
|
||||
LinTagType.fromId(LinTagId.fromId(foreign.getElementType()))
|
||||
);
|
||||
|
||||
for (net.minecraft.nbt.Tag tag : foreign) {
|
||||
values.add(toNativeBinary(tag));
|
||||
builder.add(toNativeLin(tag));
|
||||
}
|
||||
|
||||
return values.build();
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -899,44 +1005,43 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
* @return non-native structure
|
||||
*/
|
||||
@Override
|
||||
public net.minecraft.nbt.Tag fromNativeBinary(BinaryTag foreign) {
|
||||
public net.minecraft.nbt.Tag fromNativeLin(LinTag<?> foreign) {
|
||||
if (foreign == null) {
|
||||
return null;
|
||||
}
|
||||
if (foreign instanceof CompoundBinaryTag) {
|
||||
if (foreign instanceof LinCompoundTag compoundTag) {
|
||||
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
|
||||
for (String key : ((CompoundBinaryTag) foreign).keySet()) {
|
||||
tag.put(key, fromNativeBinary(((CompoundBinaryTag) foreign).get(key)));
|
||||
for (var entry : compoundTag.value().entrySet()) {
|
||||
tag.put(entry.getKey(), fromNativeLin(entry.getValue()));
|
||||
}
|
||||
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) {
|
||||
} else if (foreign instanceof LinByteTag byteTag) {
|
||||
return net.minecraft.nbt.ByteTag.valueOf(byteTag.valueAsByte());
|
||||
} else if (foreign instanceof LinByteArrayTag byteArrayTag) {
|
||||
return new net.minecraft.nbt.ByteArrayTag(byteArrayTag.value());
|
||||
} else if (foreign instanceof LinDoubleTag doubleTag) {
|
||||
return net.minecraft.nbt.DoubleTag.valueOf(doubleTag.valueAsDouble());
|
||||
} else if (foreign instanceof LinFloatTag floatTag) {
|
||||
return net.minecraft.nbt.FloatTag.valueOf(floatTag.valueAsFloat());
|
||||
} else if (foreign instanceof LinIntTag intTag) {
|
||||
return net.minecraft.nbt.IntTag.valueOf(intTag.valueAsInt());
|
||||
} else if (foreign instanceof LinIntArrayTag intArrayTag) {
|
||||
return new net.minecraft.nbt.IntArrayTag(intArrayTag.value());
|
||||
} else if (foreign instanceof LinLongArrayTag longArrayTag) {
|
||||
return new net.minecraft.nbt.LongArrayTag(longArrayTag.value());
|
||||
} else if (foreign instanceof LinListTag<?> listTag) {
|
||||
net.minecraft.nbt.ListTag tag = new net.minecraft.nbt.ListTag();
|
||||
ListBinaryTag foreignList = (ListBinaryTag) foreign;
|
||||
for (BinaryTag t : foreignList) {
|
||||
tag.add(fromNativeBinary(t));
|
||||
for (var t : listTag.value()) {
|
||||
tag.add(fromNativeLin(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) {
|
||||
} else if (foreign instanceof LinLongTag longTag) {
|
||||
return net.minecraft.nbt.LongTag.valueOf(longTag.valueAsLong());
|
||||
} else if (foreign instanceof LinShortTag shortTag) {
|
||||
return net.minecraft.nbt.ShortTag.valueOf(shortTag.valueAsShort());
|
||||
} else if (foreign instanceof LinStringTag stringTag) {
|
||||
return net.minecraft.nbt.StringTag.valueOf(stringTag.value());
|
||||
} else if (foreign instanceof LinEndTag) {
|
||||
return net.minecraft.nbt.EndTag.INSTANCE;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName());
|
||||
@ -975,7 +1080,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
WatchdogThread.tick();
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to tick watchdog", e);
|
||||
logger.log(Level.WARNING, "Failed to tick watchdog", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -987,7 +1092,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
MojangWatchdog(DedicatedServer server) throws NoSuchFieldException {
|
||||
this.server = server;
|
||||
Field tickField = MinecraftServer.class.getDeclaredField(
|
||||
Refraction.pickName("nextTickTime", "ag")
|
||||
Refraction.pickName("nextTickTime", "ag")
|
||||
);
|
||||
if (tickField.getType() != long.class) {
|
||||
throw new IllegalStateException("nextTickTime is not a long field, mapping is likely incorrect");
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
@ -35,7 +35,6 @@ import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.datafixers.DataFixerBuilder;
|
||||
import com.mojang.datafixers.schemas.Schema;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@ -48,7 +47,9 @@ 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 org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
@ -62,7 +63,6 @@ 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)
|
||||
@ -76,18 +76,17 @@ import javax.annotation.Nullable;
|
||||
* receive the source version in the compound
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer {
|
||||
public class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer {
|
||||
|
||||
//FAWE start - BinaryTag
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T fixUp(FixType<T> type, T original, int srcVer) {
|
||||
if (type == FixTypes.CHUNK) {
|
||||
return (T) fixChunk((CompoundBinaryTag) original, srcVer);
|
||||
return (T) fixChunk((LinCompoundTag) original, srcVer);
|
||||
} else if (type == FixTypes.BLOCK_ENTITY) {
|
||||
return (T) fixBlockEntity((CompoundBinaryTag) original, srcVer);
|
||||
return (T) fixBlockEntity((LinCompoundTag) original, srcVer);
|
||||
} else if (type == FixTypes.ENTITY) {
|
||||
return (T) fixEntity((CompoundBinaryTag) original, srcVer);
|
||||
return (T) fixEntity((LinCompoundTag) original, srcVer);
|
||||
} else if (type == FixTypes.BLOCK_STATE) {
|
||||
return (T) fixBlockState((String) original, srcVer);
|
||||
} else if (type == FixTypes.ITEM_TYPE) {
|
||||
@ -98,24 +97,23 @@ class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.wo
|
||||
return original;
|
||||
}
|
||||
|
||||
private CompoundBinaryTag fixChunk(CompoundBinaryTag originalChunk, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(originalChunk);
|
||||
private LinCompoundTag fixChunk(LinCompoundTag originalChunk, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(originalChunk);
|
||||
net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.CHUNK, tag, srcVer);
|
||||
return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
|
||||
return (LinCompoundTag) adapter.toNativeLin(fixed);
|
||||
}
|
||||
|
||||
private CompoundBinaryTag fixBlockEntity(CompoundBinaryTag origTileEnt, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origTileEnt);
|
||||
private LinCompoundTag fixBlockEntity(LinCompoundTag origTileEnt, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(origTileEnt);
|
||||
net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer);
|
||||
return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
|
||||
return (LinCompoundTag) adapter.toNativeLin(fixed);
|
||||
}
|
||||
|
||||
private CompoundBinaryTag fixEntity(CompoundBinaryTag origEnt, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origEnt);
|
||||
private LinCompoundTag fixEntity(LinCompoundTag origEnt, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(origEnt);
|
||||
net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.ENTITY, tag, srcVer);
|
||||
return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
|
||||
return (LinCompoundTag) adapter.toNativeLin(fixed);
|
||||
}
|
||||
//FAWE end
|
||||
|
||||
private String fixBlockState(String blockState, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag stateNBT = stateToNBT(blockState);
|
||||
@ -173,7 +171,7 @@ class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.wo
|
||||
private static final NbtOps OPS_NBT = NbtOps.INSTANCE;
|
||||
private static final int LEGACY_VERSION = 1343;
|
||||
private static int DATA_VERSION;
|
||||
static PaperweightDataConverters INSTANCE;
|
||||
public static PaperweightDataConverters INSTANCE;
|
||||
|
||||
private final Map<LegacyType, List<DataConverter>> converters = new EnumMap<>(LegacyType.class);
|
||||
private final Map<LegacyType, List<DataInspector>> inspectors = new EnumMap<>(LegacyType.class);
|
||||
@ -204,7 +202,7 @@ class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.wo
|
||||
}
|
||||
}
|
||||
|
||||
PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) {
|
||||
public PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) {
|
||||
super(dataVersion);
|
||||
DATA_VERSION = dataVersion;
|
||||
INSTANCE = this;
|
@ -17,17 +17,19 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
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.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
@ -38,9 +40,12 @@ 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);
|
||||
private static final ClientInformation FAKE_CLIENT_INFO = new ClientInformation(
|
||||
"en_US", 16, ChatVisiblity.FULL, true, 0, HumanoidArm.LEFT, false, false
|
||||
);
|
||||
|
||||
PaperweightFakePlayer(ServerLevel world) {
|
||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
|
||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE, FAKE_CLIENT_INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -67,7 +72,7 @@ class PaperweightFakePlayer extends ServerPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
||||
public void updateOptions(ClientInformation clientOptions) {
|
||||
}
|
||||
|
||||
@Override
|
@ -17,9 +17,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2;
|
||||
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
@ -27,17 +26,20 @@ import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
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_20_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
private static final int UPDATE = 1;
|
||||
@ -70,8 +72,8 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
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();
|
||||
? Block.stateById(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -101,8 +103,15 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(BlockPos position, CompoundBinaryTag tag) {
|
||||
return false;
|
||||
public boolean updateTileEntity(final BlockPos position, final LinCompoundTag tag) {
|
||||
// We will assume that the tile entity was created for us
|
||||
BlockEntity tileEntity = getWorld().getBlockEntity(position);
|
||||
if (tileEntity == null) {
|
||||
return false;
|
||||
}
|
||||
Tag nativeTag = adapter.fromNativeLin(tag);
|
||||
PaperweightAdapter.readTagIntoTileEntity((net.minecraft.nbt.CompoundTag) nativeTag, tileEntity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -144,6 +153,12 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
|
||||
ServerLevel world = getWorld();
|
||||
newState.onPlace(world, pos, oldState, false);
|
||||
}
|
||||
|
||||
// Not sure why neighborChanged is deprecated
|
||||
@SuppressWarnings("deprecation")
|
||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
||||
@ -153,8 +168,6 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
@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();
|
@ -1,10 +1,8 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||
import com.sk89q.util.ReflectionUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
@ -15,7 +13,9 @@ 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.PushReaction;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
|
||||
@ -25,7 +25,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
private final CraftBlockData craftBlockData;
|
||||
private final org.bukkit.Material craftMaterial;
|
||||
private final int opacity;
|
||||
private final CompoundTag tile;
|
||||
private final FaweCompoundTag tile;
|
||||
|
||||
public PaperweightBlockMaterial(Block block) {
|
||||
this(block, block.defaultBlockState());
|
||||
@ -48,7 +48,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
);
|
||||
tile = tileEntity == null
|
||||
? null
|
||||
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
||||
: PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity);
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
@ -135,9 +135,10 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
return block.isRandomlyTicking(blockState);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean isMovementBlocker() {
|
||||
return craftMaterial.isSolid();
|
||||
return blockState.blocksMotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -172,7 +173,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getDefaultTile() {
|
||||
public @Nullable FaweCompoundTag defaultTile() {
|
||||
return tile;
|
||||
}
|
||||
|
@ -1,28 +1,24 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
|
||||
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.nbt.FaweCompoundTag;
|
||||
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.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_R3.PaperweightAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.regen.PaperweightRegen;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
@ -38,11 +34,7 @@ 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;
|
||||
@ -77,19 +69,20 @@ import net.minecraft.world.level.chunk.LevelChunk;
|
||||
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.World;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.util.CraftNamespacedKey;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
import org.enginehub.linbus.tree.LinStringTag;
|
||||
import org.enginehub.linbus.tree.LinTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -105,14 +98,14 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
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<net.minecraft.nbt.Tag> {
|
||||
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
||||
@ -124,18 +117,16 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
}
|
||||
}
|
||||
|
||||
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<String, List<Property<?>>> allBlockProperties = null;
|
||||
|
||||
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
|
||||
this.parent = new PaperweightAdapter();
|
||||
super(new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2.PaperweightAdapter());
|
||||
}
|
||||
|
||||
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
|
||||
return blockEntity -> FaweCompoundTag.of(
|
||||
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId())
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -248,11 +239,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
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();
|
||||
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||
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);
|
||||
@ -268,12 +258,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
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();
|
||||
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||
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);
|
||||
@ -288,7 +277,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
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((LinCompoundTag) toNativeLin(tag));
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,10 +291,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||
return new PaperweightFaweWorldNativeAccess(
|
||||
this,
|
||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
||||
);
|
||||
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -319,14 +305,14 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
|
||||
if (id != null) {
|
||||
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
|
||||
Supplier<CompoundBinaryTag> saveTag = () -> {
|
||||
Supplier<LinCompoundTag> 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<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
|
||||
tags.put("Id", StringBinaryTag.of(id));
|
||||
return CompoundBinaryTag.from(tags);
|
||||
final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag);
|
||||
final Map<String, LinTag<?>> tags = NbtUtils.getLinCompoundTagValues(tag);
|
||||
tags.put("Id", LinStringTag.of(id));
|
||||
return LinCompoundTag.of(tags);
|
||||
};
|
||||
return new LazyBaseEntity(type, saveTag);
|
||||
} else {
|
||||
@ -450,7 +436,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
|
||||
@Override
|
||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
||||
ServerLevel nmsWorld = getServerLevel(world);
|
||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||
if (map != null && wasAccessibleSinceLastSave(map)) {
|
||||
boolean flag = false;
|
||||
@ -488,8 +474,8 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
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())
|
||||
getServerLevel(world),
|
||||
new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())
|
||||
);
|
||||
}
|
||||
|
||||
@ -497,7 +483,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
|
||||
ItemStack stack = new ItemStack(
|
||||
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM)
|
||||
.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
||||
.get(ResourceLocation.tryParse(baseItemStack.getType().id())),
|
||||
baseItemStack.getAmount()
|
||||
);
|
||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
|
||||
@ -505,54 +491,33 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
}
|
||||
|
||||
@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<BlockPos, CraftBlockState> 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;
|
||||
protected void preCaptureStates(final ServerLevel serverLevel) {
|
||||
serverLevel.captureTreeGeneration = true;
|
||||
serverLevel.captureBlockStates = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
|
||||
return new ArrayList<>(serverLevel.capturedBlockStates.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
|
||||
serverLevel.captureBlockStates = false;
|
||||
serverLevel.captureTreeGeneration = false;
|
||||
serverLevel.capturedBlockStates.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServerLevel getServerLevel(final World world) {
|
||||
return ((CraftWorld) world).getHandle();
|
||||
}
|
||||
|
||||
@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())));
|
||||
weStack.setNbt(((LinCompoundTag) toNativeLin(nmsStack.getTag())));
|
||||
return weStack;
|
||||
}
|
||||
|
||||
@ -585,7 +550,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.registryOrThrow(BIOME);
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.id());
|
||||
Biome biome = registry.get(resourceLocation);
|
||||
return registry.getId(biome);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
@ -9,22 +9,21 @@ 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.server.level.FullChunkStatus;
|
||||
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_20_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -133,14 +132,14 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) {
|
||||
public boolean updateTileEntity(BlockPos blockPos, LinCompoundTag 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);
|
||||
net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeLin(tag);
|
||||
blockEntity.load((CompoundTag) nativeTag);
|
||||
return true;
|
||||
}
|
||||
@ -218,6 +217,12 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
|
||||
Level world = getLevel();
|
||||
newState.onPlace(world, pos, oldState, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(
|
||||
BlockPos blockPos,
|
||||
@ -247,7 +252,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
PaperweightPlatformAdapter.sendChunk(chunk, getLevel().getWorld().getHandle(), chunk.x(), chunk.z());
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -263,7 +268,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
PaperweightPlatformAdapter.sendChunk(chunk, getLevel().getWorld().getHandle(), chunk.x(), chunk.z());
|
||||
}
|
||||
}
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks;
|
||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
||||
@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
||||
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.internal.Constants;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
@ -34,6 +31,7 @@ import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
@ -58,14 +56,22 @@ import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlock;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
import org.enginehub.linbus.tree.LinDoubleTag;
|
||||
import org.enginehub.linbus.tree.LinFloatTag;
|
||||
import org.enginehub.linbus.tree.LinListTag;
|
||||
import org.enginehub.linbus.tree.LinStringTag;
|
||||
import org.enginehub.linbus.tree.LinTagType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.AbstractSet;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -75,41 +81,49 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
|
||||
private static final Function<BlockEntity, CompoundTag> nmsTile2We =
|
||||
tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
||||
public static final Function<BlockEntity, FaweCompoundTag> NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()).blockEntityToCompoundTag();
|
||||
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter());
|
||||
private final ReadWriteLock sectionLock = new ReentrantReadWriteLock();
|
||||
private final ReentrantLock callLock = new ReentrantLock();
|
||||
private final ServerLevel serverLevel;
|
||||
private final int chunkX;
|
||||
private final int chunkZ;
|
||||
private final IntPair chunkPos;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
private final int minSectionPosition;
|
||||
private final int maxSectionPosition;
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final IdMap<Holder<Biome>> biomeHolderIdMap;
|
||||
private final ConcurrentHashMap<Integer, PaperweightGetBlocks_Copy> copies = new ConcurrentHashMap<>();
|
||||
private final Object sendLock = new Object();
|
||||
private LevelChunkSection[] sections;
|
||||
private LevelChunk levelChunk;
|
||||
private DataLayer[] blockLight;
|
||||
private DataLayer[] skyLight;
|
||||
private boolean createCopy = false;
|
||||
private PaperweightGetBlocks_Copy copy = null;
|
||||
private boolean forceLoadSections = true;
|
||||
private boolean lightUpdate = false;
|
||||
private int copyKey = 0;
|
||||
|
||||
public PaperweightGetBlocks(World world, int chunkX, int chunkZ) {
|
||||
this(((CraftWorld) world).getHandle(), chunkX, chunkZ);
|
||||
@ -126,8 +140,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
this.maxSectionPosition = maxHeight >> 4;
|
||||
this.skyLight = new DataLayer[getSectionCount()];
|
||||
this.blockLight = new DataLayer[getSectionCount()];
|
||||
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME);
|
||||
this.biomeHolderIdMap = biomeRegistry.asHolderIdMap();
|
||||
this.chunkPos = new IntPair(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
@ -144,13 +159,28 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
public int setCreateCopy(boolean createCopy) {
|
||||
if (!callLock.isHeldByCurrentThread()) {
|
||||
throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked.");
|
||||
}
|
||||
this.createCopy = createCopy;
|
||||
// Increment regardless of whether copy will be created or not to return null from getCopy()
|
||||
return ++this.copyKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkGet getCopy() {
|
||||
return copy;
|
||||
public IChunkGet getCopy(final int key) {
|
||||
return copies.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockCall() {
|
||||
this.callLock.lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlockCall() {
|
||||
this.callLock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -234,23 +264,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
public FaweCompoundTag tile(final int x, final int y, final int z) {
|
||||
BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + (
|
||||
chunkX << 4), y, (z & 15) + (
|
||||
chunkZ << 4)));
|
||||
if (blockEntity == null) {
|
||||
return null;
|
||||
}
|
||||
return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId));
|
||||
return NMS_TO_TILE.apply(blockEntity);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
public Map<BlockVector3, FaweCompoundTag> tiles() {
|
||||
Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities();
|
||||
if (nmsTiles.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
|
||||
return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -270,8 +301,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(
|
||||
LightLayer.BLOCK,
|
||||
sectionPos,
|
||||
dataLayer,
|
||||
true
|
||||
dataLayer
|
||||
);
|
||||
}
|
||||
skyLight[alayer] = dataLayer;
|
||||
@ -298,7 +328,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
Arrays.fill(LAYER_COUNT, (byte) 15);
|
||||
dataLayer = new DataLayer(LAYER_COUNT);
|
||||
((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos,
|
||||
dataLayer, true
|
||||
dataLayer
|
||||
);
|
||||
}
|
||||
blockLight[alayer] = dataLayer;
|
||||
@ -314,14 +344,22 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
Entity entity = serverLevel.getEntity(uuid);
|
||||
public @Nullable FaweCompoundTag entity(final UUID uuid) {
|
||||
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
|
||||
Entity entity = null;
|
||||
for (Entity e : entities) {
|
||||
if (e.getUUID().equals(uuid)) {
|
||||
entity = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (entity != null) {
|
||||
org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity();
|
||||
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData();
|
||||
return FaweCompoundTag.of(BukkitAdapter.adapt(bukkitEnt).getState().getNbt());
|
||||
}
|
||||
for (CompoundTag tag : getEntities()) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
for (FaweCompoundTag tag : entities()) {
|
||||
if (uuid.equals(NbtUtils.uuid(tag))) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
@ -329,13 +367,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> entities() {
|
||||
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
|
||||
if (entities.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
int size = entities.size();
|
||||
return new AbstractSet<>() {
|
||||
return new AbstractCollection<>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
@ -348,10 +387,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
@Override
|
||||
public boolean contains(Object get) {
|
||||
if (!(get instanceof CompoundTag getTag)) {
|
||||
if (!(get instanceof FaweCompoundTag getTag)) {
|
||||
return false;
|
||||
}
|
||||
UUID getUUID = getTag.getUUID();
|
||||
UUID getUUID = NbtUtils.uuid(getTag);
|
||||
for (Entity entity : entities) {
|
||||
UUID uuid = entity.getUUID();
|
||||
if (uuid.equals(getUUID)) {
|
||||
@ -363,12 +402,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<CompoundTag> iterator() {
|
||||
Iterable<CompoundTag> result = entities.stream().map(input -> {
|
||||
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
|
||||
public Iterator<FaweCompoundTag> iterator() {
|
||||
Iterable<FaweCompoundTag> result = entities.stream().map(input -> {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
input.save(tag);
|
||||
return (CompoundTag) adapter.toNative(tag);
|
||||
}).collect(Collectors.toList());
|
||||
return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag));
|
||||
})::iterator;
|
||||
return result.iterator();
|
||||
}
|
||||
};
|
||||
@ -385,12 +424,19 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
|
||||
if (!callLock.isHeldByCurrentThread()) {
|
||||
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
|
||||
}
|
||||
forceLoadSections = false;
|
||||
copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null;
|
||||
LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
||||
if (createCopy) {
|
||||
if (copies.containsKey(copyKey)) {
|
||||
throw new IllegalStateException("Copy key already used.");
|
||||
}
|
||||
copies.put(copyKey, copy);
|
||||
}
|
||||
try {
|
||||
ServerLevel nmsWorld = serverLevel;
|
||||
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||
|
||||
// Remove existing tiles. Create a copy so that we can remove blocks
|
||||
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||
List<BlockEntity> beacons = null;
|
||||
@ -462,6 +508,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
biomeData
|
||||
);
|
||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||
serverLevel.getWorld().getName(),
|
||||
chunkPos,
|
||||
levelChunkSections,
|
||||
null,
|
||||
newSection,
|
||||
@ -479,7 +527,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setBiomesToPalettedContainer(biomes, setSectionIndex, existingSection.getBiomes());
|
||||
PalettedContainer<Holder<Biome>> paletteBiomes = setBiomesToPalettedContainer(
|
||||
biomes,
|
||||
setSectionIndex,
|
||||
existingSection.getBiomes()
|
||||
);
|
||||
if (paletteBiomes != null) {
|
||||
PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -488,7 +543,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
bitMask |= 1 << getSectionIndex;
|
||||
|
||||
char[] setArr = set.load(layerNo);
|
||||
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
|
||||
// this chunk GET when #updateGet is called. Future dords, please listen this time.
|
||||
char[] tmp = set.load(layerNo);
|
||||
char[] setArr = new char[tmp.length];
|
||||
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
|
||||
|
||||
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
||||
// submitted to keep loaded internal chunks to queue target size.
|
||||
@ -517,13 +576,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
if (existingSection == null) {
|
||||
PalettedContainer<Holder<Biome>> biomeData = biomes == null ? new PalettedContainer<>(
|
||||
biomeHolderIdMap,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(
|
||||
BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES
|
||||
) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap);
|
||||
newSection = PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
@ -533,6 +587,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
biomeData
|
||||
);
|
||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||
serverLevel.getWorld().getName(),
|
||||
chunkPos,
|
||||
levelChunkSections,
|
||||
null,
|
||||
newSection,
|
||||
@ -589,16 +645,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
existingSection.getBiomes()
|
||||
);
|
||||
|
||||
newSection =
|
||||
PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
this::loadPrivately,
|
||||
setArr,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData
|
||||
);
|
||||
newSection = PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
this::loadPrivately,
|
||||
setArr,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
|
||||
);
|
||||
if (!PaperweightPlatformAdapter.setSectionAtomic(
|
||||
serverLevel.getWorld().getName(),
|
||||
chunkPos,
|
||||
levelChunkSections,
|
||||
existingSection,
|
||||
newSection,
|
||||
@ -672,7 +729,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
||||
for (UUID uuid : entityRemoves) {
|
||||
Entity entity = nmsWorld.getEntities().get(uuid);
|
||||
Entity entity = serverLevel.getEntities().get(uuid);
|
||||
if (entity != null) {
|
||||
removeEntity(entity);
|
||||
}
|
||||
@ -684,48 +741,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
};
|
||||
}
|
||||
|
||||
Set<CompoundTag> entities = set.getEntities();
|
||||
Collection<FaweCompoundTag> entities = set.entities();
|
||||
if (entities != null && !entities.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[2];
|
||||
}
|
||||
|
||||
syncTasks[1] = () -> {
|
||||
Iterator<CompoundTag> iterator = entities.iterator();
|
||||
Iterator<FaweCompoundTag> iterator = entities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final CompoundTag nativeTag = iterator.next();
|
||||
final Map<String, Tag> entityTagMap = nativeTag.getValue();
|
||||
final StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
final ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
final FaweCompoundTag nativeTag = iterator.next();
|
||||
final LinCompoundTag linTag = nativeTag.linTag();
|
||||
final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
|
||||
final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
|
||||
final LinListTag<LinFloatTag> rotTag = linTag.findListTag("Rotation", LinTagType.floatTag());
|
||||
if (idTag == null || posTag == null || rotTag == null) {
|
||||
LOGGER.error("Unknown entity tag: {}", nativeTag);
|
||||
continue;
|
||||
}
|
||||
final double x = posTag.getDouble(0);
|
||||
final double y = posTag.getDouble(1);
|
||||
final double z = posTag.getDouble(2);
|
||||
final float yaw = rotTag.getFloat(0);
|
||||
final float pitch = rotTag.getFloat(1);
|
||||
final String id = idTag.getValue();
|
||||
final double x = posTag.get(0).valueAsDouble();
|
||||
final double y = posTag.get(1).valueAsDouble();
|
||||
final double z = posTag.get(2).valueAsDouble();
|
||||
final float yaw = rotTag.get(0).valueAsFloat();
|
||||
final float pitch = rotTag.get(1).valueAsFloat();
|
||||
final String id = idTag.value();
|
||||
|
||||
EntityType<?> type = EntityType.byString(id).orElse(null);
|
||||
if (type != null) {
|
||||
Entity entity = type.create(nmsWorld);
|
||||
Entity entity = type.create(serverLevel);
|
||||
if (entity != null) {
|
||||
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative(
|
||||
nativeTag);
|
||||
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
|
||||
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||
tag.remove(name);
|
||||
}
|
||||
entity.load(tag);
|
||||
entity.absMoveTo(x, y, z, yaw, pitch);
|
||||
entity.setUUID(nativeTag.getUUID());
|
||||
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
||||
entity.setUUID(NbtUtils.uuid(nativeTag));
|
||||
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
||||
LOGGER.warn(
|
||||
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
|
||||
id,
|
||||
nmsWorld.getWorld().getName(),
|
||||
serverLevel.getWorld().getName(),
|
||||
x,
|
||||
y,
|
||||
z
|
||||
@ -740,30 +796,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
// set tiles
|
||||
Map<BlockVector3, CompoundTag> tiles = set.getTiles();
|
||||
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
|
||||
if (tiles != null && !tiles.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[1];
|
||||
}
|
||||
|
||||
syncTasks[0] = () -> {
|
||||
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
|
||||
final CompoundTag nativeTag = entry.getValue();
|
||||
for (final Map.Entry<BlockVector3, FaweCompoundTag> entry : tiles.entrySet()) {
|
||||
final FaweCompoundTag nativeTag = entry.getValue();
|
||||
final BlockVector3 blockHash = entry.getKey();
|
||||
final int x = blockHash.getX() + bx;
|
||||
final int y = blockHash.getY();
|
||||
final int z = blockHash.getZ() + bz;
|
||||
final int x = blockHash.x() + bx;
|
||||
final int y = blockHash.y();
|
||||
final int z = blockHash.z() + bz;
|
||||
final BlockPos pos = new BlockPos(x, y, z);
|
||||
|
||||
synchronized (nmsWorld) {
|
||||
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos);
|
||||
synchronized (serverLevel) {
|
||||
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
|
||||
if (tileEntity == null || tileEntity.isRemoved()) {
|
||||
nmsWorld.removeBlockEntity(pos);
|
||||
tileEntity = nmsWorld.getBlockEntity(pos);
|
||||
serverLevel.removeBlockEntity(pos);
|
||||
tileEntity = serverLevel.getBlockEntity(pos);
|
||||
}
|
||||
if (tileEntity != null) {
|
||||
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative(
|
||||
nativeTag);
|
||||
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
|
||||
tag.put("x", IntTag.valueOf(x));
|
||||
tag.put("y", IntTag.valueOf(y));
|
||||
tag.put("z", IntTag.valueOf(z));
|
||||
@ -779,15 +834,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
callback = null;
|
||||
} else {
|
||||
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
|
||||
boolean finalLightUpdate = lightUpdate;
|
||||
callback = () -> {
|
||||
// Set Modified
|
||||
nmsChunk.setLightCorrect(true); // Set Modified
|
||||
nmsChunk.mustNotSave = false;
|
||||
nmsChunk.setUnsaved(true);
|
||||
// send to player
|
||||
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING) {
|
||||
this.send(finalMask, finalLightUpdate);
|
||||
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
|
||||
this.send();
|
||||
}
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
@ -809,7 +863,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
if (callback == null) {
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
queueHandler.async(finalizer, null);
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
@ -875,9 +929,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
if (super.sections[layer] != null) {
|
||||
synchronized (super.sectionLocks[layer]) {
|
||||
if (super.sections[layer].isFull() && super.blocks[layer] != null) {
|
||||
char[] blocks = new char[4096];
|
||||
System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096);
|
||||
return blocks;
|
||||
return super.blocks[layer];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -885,8 +937,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void send(int mask, boolean lighting) {
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting);
|
||||
public void send() {
|
||||
synchronized (sendLock) {
|
||||
PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -997,9 +1051,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
public LevelChunkSection[] getSections(boolean force) {
|
||||
force &= forceLoadSections;
|
||||
sectionLock.readLock().lock();
|
||||
LevelChunkSection[] tmp = sections;
|
||||
sectionLock.readLock().unlock();
|
||||
if (tmp == null || force) {
|
||||
try {
|
||||
sectionLock.writeLock().lock();
|
||||
@ -1045,8 +1097,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(
|
||||
lightLayer,
|
||||
sectionPos,
|
||||
dataLayer,
|
||||
true
|
||||
dataLayer
|
||||
);
|
||||
}
|
||||
synchronized (dataLayer) {
|
||||
@ -1069,38 +1120,25 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
final int sectionIndex,
|
||||
final PalettedContainerRO<Holder<Biome>> data
|
||||
) {
|
||||
PalettedContainer<Holder<Biome>> biomeData;
|
||||
if (data instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
|
||||
biomeData = palettedContainer;
|
||||
} else {
|
||||
LOGGER.warn(
|
||||
"Cannot correctly set biomes to world, existing biomes may be lost. Expected class " +
|
||||
"type {} but got {}",
|
||||
PalettedContainer.class.getSimpleName(),
|
||||
data.getClass().getSimpleName()
|
||||
);
|
||||
biomeData = data.recreate();
|
||||
}
|
||||
BiomeType[] sectionBiomes;
|
||||
if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) {
|
||||
return biomeData;
|
||||
return null;
|
||||
}
|
||||
PalettedContainer<Holder<Biome>> biomeData = data.recreate();
|
||||
for (int y = 0, index = 0; y < 4; y++) {
|
||||
for (int z = 0; z < 4; z++) {
|
||||
for (int x = 0; x < 4; x++, index++) {
|
||||
BiomeType biomeType = sectionBiomes[index];
|
||||
if (biomeType == null) {
|
||||
continue;
|
||||
biomeData.set(x, y, z, data.get(x, y, z));
|
||||
} else {
|
||||
biomeData.set(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))
|
||||
);
|
||||
}
|
||||
biomeData.set(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(biomeType))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,22 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||
import com.fastasyncworldedit.core.queue.IBlocks;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
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.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
@ -24,8 +25,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.PalettedContainerRO;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@ -37,14 +41,14 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
|
||||
private final Set<FaweCompoundTag> entities = new HashSet<>();
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private PalettedContainer<Holder<Biome>>[] biomes = null;
|
||||
private Holder<Biome>[][] biomes = null;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
@ -55,44 +59,35 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
}
|
||||
|
||||
protected void storeTile(BlockEntity blockEntity) {
|
||||
@SuppressWarnings("unchecked")
|
||||
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
tiles.put(
|
||||
BlockVector3.at(
|
||||
blockEntity.getBlockPos().getX(),
|
||||
blockEntity.getBlockPos().getY(),
|
||||
blockEntity.getBlockPos().getZ()
|
||||
),
|
||||
new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId))
|
||||
FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId()))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
@SuppressWarnings("unchecked")
|
||||
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
entities.add((CompoundTag) adapter.toNative(compoundTag));
|
||||
entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> entities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
for (CompoundTag tag : entities) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
public @Nullable FaweCompoundTag entity(final UUID uuid) {
|
||||
for (FaweCompoundTag tag : entities) {
|
||||
if (uuid.equals(NbtUtils.uuid(tag))) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
@ -105,7 +100,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
public int setCreateCopy(boolean createCopy) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -142,7 +138,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()].get(x >> 2, (y & 15) >> 2, z >> 2);
|
||||
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()][(y & 12) << 2 | (z & 12) | (x & 12) >> 2];
|
||||
return PaperweightPlatformAdapter.adapt(biome, serverLevel);
|
||||
}
|
||||
|
||||
@ -171,10 +167,25 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
protected void storeBiomes(int layer, PalettedContainerRO<Holder<Biome>> biomeData) {
|
||||
if (biomes == null) {
|
||||
biomes = new PalettedContainer[getSectionCount()];
|
||||
biomes = new Holder[getSectionCount()][];
|
||||
}
|
||||
if (biomes[layer] == null) {
|
||||
biomes[layer] = new Holder[64];
|
||||
}
|
||||
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
|
||||
biomes[layer] = palettedContainer.copy();
|
||||
if (PaperLib.isPaper()) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
biomes[layer][i] = palettedContainer.get(i); // Only public on paper
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
biomes[layer][i] = (Holder<Biome>) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.error(
|
||||
"Cannot correctly save biomes to history. Expected class type {} but got {}",
|
||||
@ -187,7 +198,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
BlockState state = BlockTypesCache.states[get(x, y, z)];
|
||||
return state.toBaseBlock(this, x, y, z);
|
||||
return state.toBaseBlock((IBlocks) this, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -199,6 +210,10 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
if (blocks[layer] == null) {
|
||||
blocks[layer] = new char[4096];
|
||||
Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR);
|
||||
}
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@ -213,6 +228,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, FaweCompoundTag> tiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.destroystokyo.paper.util.maplist.EntityList;
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
@ -7,8 +7,8 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
@ -26,6 +26,7 @@ import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@ -57,8 +58,7 @@ import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
|
||||
import sun.misc.Unsafe;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftChunk;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -98,21 +98,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
private static final Field fieldTickingFluidCount;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldThreadingDetector;
|
||||
private static final long fieldThreadingDetectorOffset;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final MethodHandle methodRemoveGameEventListener;
|
||||
private static final MethodHandle methodremoveTickingBlockEntity;
|
||||
private static final Field fieldBiomes;
|
||||
|
||||
/*
|
||||
* This is a workaround for the changes from https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1fddefce1cdce44010927b888432bf70c0e88cde#src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
||||
@ -129,6 +123,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
private static Field LEVEL_CHUNK_ENTITIES;
|
||||
private static Field SERVER_LEVEL_ENTITY_MANAGER;
|
||||
|
||||
static final MethodHandle PALETTED_CONTAINER_GET;
|
||||
|
||||
static {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
try {
|
||||
@ -148,8 +144,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
fieldTickingFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f"));
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "e"));
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
Field tmpFieldBiomes;
|
||||
try {
|
||||
// It seems to actually be biomes, but is apparently obfuscated to "i"
|
||||
tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes");
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i");
|
||||
}
|
||||
fieldBiomes = tmpFieldBiomes;
|
||||
fieldBiomes.setAccessible(true);
|
||||
|
||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
||||
"getVisibleChunkIfPresent",
|
||||
@ -158,20 +161,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
|
||||
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector);
|
||||
|
||||
fieldThreadingDetector.setAccessible(true);
|
||||
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
fieldLock.setAccessible(true);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldThreadingDetector = null;
|
||||
fieldThreadingDetectorOffset = -1;
|
||||
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
|
||||
@ -194,12 +192,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
boolean chunkRewrite;
|
||||
try {
|
||||
ServerLevel.class.getDeclaredMethod("getEntityLookup");
|
||||
@ -222,6 +214,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
POST_CHUNK_REWRITE = chunkRewrite;
|
||||
|
||||
Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod(
|
||||
Refraction.pickName("get", "a"),
|
||||
int.class
|
||||
);
|
||||
palettedContaienrGet.setAccessible(true);
|
||||
PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet);
|
||||
} catch (RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
@ -244,16 +243,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
|
||||
static boolean setSectionAtomic(
|
||||
String worldName,
|
||||
IntPair pair,
|
||||
LevelChunkSection[] sections,
|
||||
LevelChunkSection expected,
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer);
|
||||
}
|
||||
|
||||
// There is no point in having a functional semaphore for paper servers.
|
||||
@ -266,19 +263,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject(
|
||||
blocks,
|
||||
fieldThreadingDetectorOffset
|
||||
);
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
|
||||
synchronized (currentThreadingDetector) {
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset);
|
||||
Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock);
|
||||
fieldLock.set(currentThreadingDetector, newLock);
|
||||
return newLock;
|
||||
}
|
||||
}
|
||||
@ -355,7 +348,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
||||
public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int chunkZ) {
|
||||
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||
if (chunkHolder == null) {
|
||||
return;
|
||||
@ -376,26 +369,35 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
if (levelChunk == null) {
|
||||
return;
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
ClientboundLevelChunkWithLightPacket packet;
|
||||
if (PaperLib.isPaper()) {
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null
|
||||
// last false is to not bother with x-ray
|
||||
);
|
||||
} else {
|
||||
// deprecated on paper - deprecation suppressed
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null
|
||||
);
|
||||
StampLockHolder lockHolder = new StampLockHolder();
|
||||
NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
|
||||
if (lockHolder.chunkLock == null) {
|
||||
return;
|
||||
}
|
||||
MinecraftServer.getServer().execute(() -> {
|
||||
try {
|
||||
ClientboundLevelChunkWithLightPacket packet;
|
||||
if (PaperLib.isPaper()) {
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
false // last false is to not bother with x-ray
|
||||
);
|
||||
} else {
|
||||
// deprecated on paper - deprecation suppressed
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
} finally {
|
||||
NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
|
||||
}
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
});
|
||||
}
|
||||
|
||||
@ -425,7 +427,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer, biomeRegistry, biomes);
|
||||
return newChunkSection(biomeRegistry, biomes);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
|
||||
@ -515,7 +517,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
@SuppressWarnings("deprecation") // Only deprecated in paper
|
||||
private static LevelChunkSection newChunkSection(
|
||||
int layer,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
@ -530,6 +531,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
return new LevelChunkSection(dataPaletteBlocks, biomes);
|
||||
}
|
||||
|
||||
public static void setBiomesToChunkSection(LevelChunkSection section, PalettedContainer<Holder<Biome>> biomes) {
|
||||
try {
|
||||
fieldBiomes.set(section, biomes);
|
||||
} catch (IllegalAccessException e) {
|
||||
LOGGER.error("Could not set biomes to chunk section", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PalettedContainer<Biome>}. Should only be used if no biome container existed beforehand.
|
||||
*/
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
@ -0,0 +1,77 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
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 java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
|
||||
|
||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
|
||||
super(serverLevel, queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkPos createChunkPos(final long chunkKey) {
|
||||
return new ChunkPos(chunkKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long asLong(final int chunkX, final int chunkZ) {
|
||||
return ChunkPos.asLong(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
|
||||
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
chunkPos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
));
|
||||
}
|
||||
|
||||
protected void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred on relighting", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
protected void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +1,20 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||
|
||||
@Override
|
||||
public @Nonnull
|
||||
@SuppressWarnings("rawtypes")
|
||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
||||
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||
if (w == null) {
|
||||
return NullRelighter.INSTANCE;
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.LazyCompoundTag;
|
||||
@ -6,8 +6,8 @@ import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -36,7 +36,7 @@ public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Tag> getValue() {
|
||||
public Map<String, Tag<?, ?>> getValue() {
|
||||
if (compoundTag == null) {
|
||||
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
|
||||
}
|
||||
@ -44,9 +44,9 @@ public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundBinaryTag asBinaryTag() {
|
||||
public LinCompoundTag toLinTag() {
|
||||
getValue();
|
||||
return compoundTag.asBinaryTag();
|
||||
return compoundTag.toLinTag();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
@ -94,10 +94,10 @@ public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> getList(String key) {
|
||||
public List<? extends Tag<?, ?>> getList(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
|
||||
ArrayList<Tag> list = new ArrayList<>();
|
||||
ArrayList<Tag<?, ?>> list = new ArrayList<>();
|
||||
for (net.minecraft.nbt.Tag elem : nbtList) {
|
||||
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
list.add(new PaperweightLazyCompoundTag(compoundTag));
|
||||
@ -120,7 +120,7 @@ public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
|
||||
public <T extends Tag<?, ?>> List<T> getList(String key, Class<T> listType) {
|
||||
ListTag listTag = getListTag(key);
|
||||
if (listTag.getType().equals(listType)) {
|
||||
return (List<T>) listTag.getValue();
|
@ -0,0 +1,303 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.ProgressListener;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightRegen extends Regenerator {
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
|
||||
|
||||
static {
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "e"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerLevel freshWorld;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
public PaperweightRegen(
|
||||
World originalBukkitWorld,
|
||||
Region region,
|
||||
Extent target,
|
||||
RegenOptions options
|
||||
) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
|
||||
while (shouldKeepTicking.getAsBoolean()) {
|
||||
if (!this.freshWorld.getChunkSource().pollTask()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldOptions originalOpts = originalWorldData.worldGenOptions();
|
||||
WorldOptions newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataConfiguration()
|
||||
);
|
||||
|
||||
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
|
||||
originalWorldData.isFlatWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.FLAT
|
||||
: originalWorldData.isDebugWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.DEBUG
|
||||
: PrimaryLevelData.SpecialWorldProperty.NONE;
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
new LevelStem(
|
||||
originalServerWorld.dimensionTypeRegistration(),
|
||||
originalServerWorld.getChunkSource().getGenerator()
|
||||
),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
originalServerWorld.getRandomSequences(),
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
|
||||
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(
|
||||
@org.jetbrains.annotations.Nullable final ProgressListener progressListener,
|
||||
final boolean flush,
|
||||
final boolean savingDisabled
|
||||
) {
|
||||
// noop, spigot
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(
|
||||
@Nullable final ProgressListener progressListener,
|
||||
final boolean flush,
|
||||
final boolean savingDisabled,
|
||||
final boolean close
|
||||
) {
|
||||
// noop, paper
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshWorld.getChunkSource().getDataStorage().cache.clear();
|
||||
freshWorld.getChunkSource().close(false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(@NotNull ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(
|
||||
final @NotNull ChunkPos pos,
|
||||
@org.jetbrains.annotations.Nullable final ChunkStatus status
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -11,7 +11,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.18.2-R0.1-20220920.010157-167")
|
||||
// url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.4-R0.1-20241030.192207-176")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
@ -17,17 +17,17 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3;
|
||||
|
||||
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.Lists;
|
||||
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.CompoundTag;
|
||||
import com.sk89q.jnbt.NBTConstants;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.blocks.BaseItem;
|
||||
@ -55,22 +55,9 @@ 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.BiomeCategory;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
@ -82,6 +69,8 @@ import com.sk89q.worldedit.world.item.ItemType;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
|
||||
@ -93,6 +82,7 @@ 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.RandomSource;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
import net.minecraft.world.Clearable;
|
||||
@ -123,21 +113,39 @@ import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.World.Environment;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.util.CraftMagicNumbers;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.util.CraftMagicNumbers;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.enginehub.linbus.common.LinTagId;
|
||||
import org.enginehub.linbus.tree.LinByteArrayTag;
|
||||
import org.enginehub.linbus.tree.LinByteTag;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
import org.enginehub.linbus.tree.LinDoubleTag;
|
||||
import org.enginehub.linbus.tree.LinEndTag;
|
||||
import org.enginehub.linbus.tree.LinFloatTag;
|
||||
import org.enginehub.linbus.tree.LinIntArrayTag;
|
||||
import org.enginehub.linbus.tree.LinIntTag;
|
||||
import org.enginehub.linbus.tree.LinListTag;
|
||||
import org.enginehub.linbus.tree.LinLongArrayTag;
|
||||
import org.enginehub.linbus.tree.LinLongTag;
|
||||
import org.enginehub.linbus.tree.LinShortTag;
|
||||
import org.enginehub.linbus.tree.LinStringTag;
|
||||
import org.enginehub.linbus.tree.LinTag;
|
||||
import org.enginehub.linbus.tree.LinTagType;
|
||||
import org.spigotmc.SpigotConfig;
|
||||
import org.spigotmc.WatchdogThread;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -145,6 +153,7 @@ import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -159,20 +168,21 @@ 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<net.minecraft.nbt.Tag> {
|
||||
|
||||
private final Logger LOGGER = Logger.getLogger(getClass().getCanonicalName());
|
||||
private final Logger logger = Logger.getLogger(getClass().getCanonicalName());
|
||||
|
||||
private final Field serverWorldsField;
|
||||
private final Method getChunkFutureMethod;
|
||||
private final Field chunkProviderExecutorField;
|
||||
private final Watchdog watchdog;
|
||||
|
||||
private static final RandomSource random = RandomSource.create();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that may break between versions of Minecraft
|
||||
// ------------------------------------------------------------------------
|
||||
@ -182,21 +192,21 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
CraftServer.class.cast(Bukkit.getServer());
|
||||
|
||||
int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion();
|
||||
if (dataVersion != 3463 && dataVersion != 3465) {
|
||||
throw new UnsupportedClassVersionError("Not 1.20(.1)!");
|
||||
if (dataVersion != 3698 && dataVersion != 3700) {
|
||||
throw new UnsupportedClassVersionError("Not 1.20.(3/4)!");
|
||||
}
|
||||
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
getChunkFutureMethod = ServerChunkCache.class.getDeclaredMethod(
|
||||
Refraction.pickName("getChunkFutureMainThread", "c"),
|
||||
int.class, int.class, ChunkStatus.class, boolean.class
|
||||
Refraction.pickName("getChunkFutureMainThread", "c"),
|
||||
int.class, int.class, ChunkStatus.class, boolean.class
|
||||
);
|
||||
getChunkFutureMethod.setAccessible(true);
|
||||
|
||||
chunkProviderExecutorField = ServerChunkCache.class.getDeclaredField(
|
||||
Refraction.pickName("mainThreadProcessor", "g")
|
||||
Refraction.pickName("mainThreadProcessor", "g")
|
||||
);
|
||||
chunkProviderExecutorField.setAccessible(true);
|
||||
|
||||
@ -281,12 +291,11 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
private static Block getBlockFromType(BlockType blockType) {
|
||||
|
||||
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK).get(ResourceLocation.tryParse(blockType.getId()));
|
||||
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK).get(ResourceLocation.tryParse(blockType.id()));
|
||||
}
|
||||
|
||||
private static Item getItemFromType(ItemType itemType) {
|
||||
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(itemType.getId()));
|
||||
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(itemType.id()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -306,6 +315,29 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
return combinedId == 0 && state.getBlockType() != BlockTypes.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId);
|
||||
}
|
||||
|
||||
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
|
||||
int internalId = Block.getId(blockState);
|
||||
BlockState state = BlockStateIdAccess.getBlockStateById(internalId);
|
||||
if (state == null) {
|
||||
state = BukkitAdapter.adapt(CraftBlockData.createData(blockState));
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public BiomeType adapt(Biome biome) {
|
||||
var mcBiome = ((CraftServer) Bukkit.getServer()).getServer().registryAccess().registryOrThrow(Registries.BIOME).getKey(biome);
|
||||
if (mcBiome == null) {
|
||||
return null;
|
||||
}
|
||||
return BiomeType.REGISTRY.get(mcBiome.toString());
|
||||
}
|
||||
|
||||
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
|
||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||
return Block.stateById(internalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(Location location) {
|
||||
checkNotNull(location);
|
||||
@ -319,14 +351,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
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;
|
||||
return adapt(blockData);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -346,21 +371,16 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
BlockEntity te = chunk.getBlockEntity(blockPos);
|
||||
if (te != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = te.saveWithId();
|
||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
||||
return state.toBaseBlock((LinCompoundTag) toNativeLin(tag));
|
||||
}
|
||||
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
/*
|
||||
@Override
|
||||
public boolean hasCustomBiomeSupport() {
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
private static final HashMap<BiomeType, Holder<Biome>> biomeTypeToNMSCache = new HashMap<>();
|
||||
private static final HashMap<Holder<Biome>, BiomeType> biomeTypeFromNMSCache = new HashMap<>();
|
||||
|
||||
/* @Override
|
||||
@Override
|
||||
public BiomeType getBiome(Location location) {
|
||||
checkNotNull(location);
|
||||
|
||||
@ -387,14 +407,13 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
|
||||
final ServerLevel handle = craftWorld.getHandle();
|
||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||
chunk.setBiome(x >> 2, y >> 2, z >> 2, biomeTypeToNMSCache.computeIfAbsent(biome, b -> ((CraftServer) Bukkit.getServer()).getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(ResourceKey.create(Registries.BIOME, new ResourceLocation(b.getId())))));
|
||||
chunk.setBiome(x >> 2, y >> 2, z >> 2, biomeTypeToNMSCache.computeIfAbsent(biome, b -> ((CraftServer) Bukkit.getServer()).getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(ResourceKey.create(Registries.BIOME, new ResourceLocation(b.id())))));
|
||||
chunk.setUnsaved(true);
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||
return new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1.PaperweightWorldNativeAccess(this,
|
||||
new WeakReference<>(((CraftWorld) world).getHandle()));
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(World world) {
|
||||
return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle()));
|
||||
}
|
||||
|
||||
private static net.minecraft.core.Direction adapt(Direction face) {
|
||||
@ -417,13 +436,13 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private net.minecraft.world.level.block.state.BlockState applyProperties(
|
||||
StateDefinition<Block, net.minecraft.world.level.block.state.BlockState> stateContainer,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
Map<Property<?>, Object> states
|
||||
StateDefinition<Block, net.minecraft.world.level.block.state.BlockState> stateContainer,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
Map<Property<?>, Object> states
|
||||
) {
|
||||
for (Map.Entry<Property<?>, Object> state : states.entrySet()) {
|
||||
net.minecraft.world.level.block.state.properties.Property<?> property =
|
||||
stateContainer.getProperty(state.getKey().getName());
|
||||
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) {
|
||||
@ -432,16 +451,16 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
} 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
|
||||
)
|
||||
);
|
||||
.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
|
||||
(net.minecraft.world.level.block.state.properties.Property) property,
|
||||
(Comparable) value
|
||||
);
|
||||
}
|
||||
return newState;
|
||||
@ -465,7 +484,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
readEntityIntoTag(mcEntity, tag);
|
||||
return new BaseEntity(
|
||||
com.sk89q.worldedit.world.entity.EntityTypes.get(id),
|
||||
LazyReference.from(() -> (CompoundBinaryTag) toNativeBinary(tag))
|
||||
LazyReference.from(() -> (LinCompoundTag) toNativeLin(tag))
|
||||
);
|
||||
}
|
||||
|
||||
@ -478,12 +497,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
ServerLevel worldServer = craftWorld.getHandle();
|
||||
|
||||
Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle());
|
||||
Entity createdEntity = createEntityFromId(state.getType().id(), craftWorld.getHandle());
|
||||
|
||||
if (createdEntity != null) {
|
||||
CompoundBinaryTag nativeTag = state.getNbt();
|
||||
LinCompoundTag nativeTag = state.getNbt();
|
||||
if (nativeTag != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(nativeTag);
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeLin(nativeTag);
|
||||
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||
tag.remove(name);
|
||||
}
|
||||
@ -531,24 +550,42 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private static final LoadingCache<net.minecraft.world.level.block.state.properties.Property, Property<?>> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<net.minecraft.world.level.block.state.properties.Property, Property<?>>() {
|
||||
@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<Direction>) 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<String>) 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());
|
||||
}
|
||||
}
|
||||
});
|
||||
private static final LoadingCache<net.minecraft.world.level.block.state.properties.Property, Property<?>> PROPERTY_CACHE = CacheBuilder
|
||||
.newBuilder()
|
||||
.build(new CacheLoader<>() {
|
||||
@Override
|
||||
public Property<?> load(net.minecraft.world.level.block.state.properties.Property state) {
|
||||
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(),
|
||||
new ArrayList<>((List<Direction>) state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> Direction.valueOf(((StringRepresentable) e)
|
||||
.getSerializedName()
|
||||
.toUpperCase(Locale.ROOT)))
|
||||
.toList())
|
||||
);
|
||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
|
||||
return new EnumProperty(
|
||||
state.getName(),
|
||||
new ArrayList<>((List<String>) state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> ((StringRepresentable) e).getSerializedName())
|
||||
.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
|
||||
@ -556,7 +593,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
Map<String, Property<?>> properties = new TreeMap<>();
|
||||
Block block = getBlockFromType(blockType);
|
||||
StateDefinition<Block, net.minecraft.world.level.block.state.BlockState> blockStateList =
|
||||
block.getStateDefinition();
|
||||
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);
|
||||
@ -565,39 +602,28 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) {
|
||||
public void sendFakeNBT(Player player, BlockVector3 pos, LinCompoundTag nbtData) {
|
||||
((CraftPlayer) player).getHandle().connection.send(ClientboundBlockEntityDataPacket.create(
|
||||
new StructureBlockEntity(
|
||||
new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()),
|
||||
new BlockPos(pos.x(), pos.y(), pos.z()),
|
||||
Blocks.STRUCTURE_BLOCK.defaultBlockState()
|
||||
),
|
||||
__ -> (net.minecraft.nbt.CompoundTag) fromNativeBinary(nbtData)
|
||||
__ -> (net.minecraft.nbt.CompoundTag) fromNativeLin(nbtData)
|
||||
));
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public void sendFakeNBT(Player player, BlockVector3 pos, CompoundTag 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) fromNative(nbtData)
|
||||
));
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void sendFakeOP(Player player) {
|
||||
((CraftPlayer) player).getHandle().connection.send(new ClientboundEntityEventPacket(
|
||||
((CraftPlayer) player).getHandle(), (byte) 28
|
||||
((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()
|
||||
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(item.getType().id())),
|
||||
item.getAmount()
|
||||
);
|
||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData())));
|
||||
return CraftItemStack.asCraftMirror(stack);
|
||||
@ -607,7 +633,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
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())));
|
||||
weStack.setNbt((LinCompoundTag) toNativeLin(nmsStack.getTag()));
|
||||
return weStack;
|
||||
}
|
||||
|
||||
@ -629,10 +655,10 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
return false;
|
||||
}
|
||||
fakePlayer.setItemInHand(InteractionHand.MAIN_HAND, stack);
|
||||
fakePlayer.absMoveTo(position.getBlockX(), position.getBlockY(), position.getBlockZ(),
|
||||
(float) face.toVector().toYaw(), (float) face.toVector().toPitch());
|
||||
fakePlayer.absMoveTo(position.x(), position.y(), position.z(),
|
||||
(float) face.toVector().toYaw(), (float) face.toVector().toPitch());
|
||||
|
||||
final BlockPos blockPos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
||||
final BlockPos blockPos = new BlockPos(position.x(), position.y(), position.z());
|
||||
final Vec3 blockVec = Vec3.atLowerCornerOf(blockPos);
|
||||
final net.minecraft.core.Direction enumFacing = adapt(face);
|
||||
BlockHitResult rayTrace = new BlockHitResult(blockVec, enumFacing, blockPos, false);
|
||||
@ -650,14 +676,14 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 position, BlockState blockState) {
|
||||
public boolean canPlaceAt(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()));
|
||||
return blockData.canSurvive(((CraftWorld) world).getHandle(), new BlockPos(position.x(), position.y(), position.z()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) {
|
||||
public boolean regenerate(World bukkitWorld, Region region, Extent extent, RegenOptions options) {
|
||||
try {
|
||||
doRegen(bukkitWorld, region, extent, options);
|
||||
} catch (Exception e) {
|
||||
@ -667,7 +693,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) throws Exception {
|
||||
private void doRegen(World bukkitWorld, Region region, Extent extent, RegenOptions options) throws Exception {
|
||||
Environment env = bukkitWorld.getEnvironment();
|
||||
ChunkGenerator gen = bukkitWorld.getGenerator();
|
||||
|
||||
@ -677,51 +703,52 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
try (LevelStorageSource.LevelStorageAccess session = levelStorage.createAccess("faweregentempworld", worldDimKey)) {
|
||||
ServerLevel originalWorld = ((CraftWorld) bukkitWorld).getHandle();
|
||||
PrimaryLevelData levelProperties = (PrimaryLevelData) originalWorld.getServer()
|
||||
.getWorldData().overworldData();
|
||||
.getWorldData().overworldData();
|
||||
WorldOptions originalOpts = levelProperties.worldGenOptions();
|
||||
|
||||
long seed = options.getSeed().orElse(originalWorld.getSeed());
|
||||
WorldOptions newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
? 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()
|
||||
"faweregentempworld",
|
||||
levelProperties.settings.gameType(),
|
||||
levelProperties.settings.hardcore(),
|
||||
levelProperties.settings.difficulty(),
|
||||
levelProperties.settings.allowCommands(),
|
||||
levelProperties.settings.gameRules(),
|
||||
levelProperties.settings.getDataConfiguration()
|
||||
);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
|
||||
levelProperties.isFlatWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.FLAT
|
||||
: levelProperties.isDebugWorld()
|
||||
? PrimaryLevelData.SpecialWorldProperty.DEBUG
|
||||
: PrimaryLevelData.SpecialWorldProperty.NONE;
|
||||
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,
|
||||
originalWorld.getRandomSequences(),
|
||||
env,
|
||||
gen,
|
||||
bukkitWorld.getBiomeProvider()
|
||||
originalWorld.getServer(),
|
||||
originalWorld.getServer().executor,
|
||||
session, newWorldData,
|
||||
originalWorld.dimension(),
|
||||
new LevelStem(
|
||||
originalWorld.dimensionTypeRegistration(),
|
||||
originalWorld.getChunkSource().getGenerator()
|
||||
),
|
||||
new NoOpWorldLoadListener(),
|
||||
originalWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
originalWorld.getRandomSequences(),
|
||||
env,
|
||||
gen,
|
||||
bukkitWorld.getBiomeProvider()
|
||||
);
|
||||
try {
|
||||
regenForWorld(region, extent, freshWorld, options);
|
||||
@ -731,7 +758,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
} finally {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
Map<String, World> map = (Map<String, World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
@ -759,7 +786,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
executor.managedBlock(() -> {
|
||||
// bail out early if a future fails
|
||||
if (chunkLoadings.stream().anyMatch(ftr ->
|
||||
ftr.isDone() && Futures.getUnchecked(ftr) == null
|
||||
ftr.isDone() && Futures.getUnchecked(ftr) == null
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
@ -774,7 +801,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
for (BlockVector3 vec : region) {
|
||||
BlockPos pos = new BlockPos(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ());
|
||||
BlockPos pos = new BlockPos(vec.x(), vec.y(), vec.z());
|
||||
ChunkAccess chunk = chunks.get(new ChunkPos(pos));
|
||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(pos);
|
||||
int internalId = Block.getId(blockData);
|
||||
@ -783,11 +810,11 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(pos);
|
||||
if (blockEntity != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
|
||||
state = state.toBaseBlock(((CompoundBinaryTag) toNativeBinary(tag)));
|
||||
state = state.toBaseBlock((LinCompoundTag) toNativeLin(tag));
|
||||
}
|
||||
extent.setBlock(vec, state.toBaseBlock());
|
||||
if (options.shouldRegenBiomes()) {
|
||||
Biome origBiome = chunk.getNoiseBiome(vec.getX(), vec.getY(), vec.getZ()).value();
|
||||
Biome origBiome = chunk.getNoiseBiome(vec.x(), vec.y(), vec.z()).value();
|
||||
BiomeType adaptedBiome = adapt(serverWorld, origBiome);
|
||||
if (adaptedBiome != null) {
|
||||
extent.setBiome(vec, adaptedBiome);
|
||||
@ -805,9 +832,9 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
try {
|
||||
//noinspection unchecked
|
||||
chunkLoadings.add(
|
||||
((CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>)
|
||||
getChunkFutureMethod.invoke(chunkManager, chunk.getX(), chunk.getZ(), ChunkStatus.FEATURES, true))
|
||||
.thenApply(either -> either.left().orElse(null))
|
||||
((CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>)
|
||||
getChunkFutureMethod.invoke(chunkManager, chunk.x(), chunk.z(), ChunkStatus.FEATURES, true))
|
||||
.thenApply(either -> either.left().orElse(null))
|
||||
);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IllegalStateException("Couldn't load chunk for regen.", e);
|
||||
@ -829,12 +856,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
private static final Set<SideEffect> SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet(
|
||||
SideEffect.NEIGHBORS,
|
||||
SideEffect.LIGHTING,
|
||||
SideEffect.VALIDATION,
|
||||
SideEffect.ENTITY_AI,
|
||||
SideEffect.EVENTS,
|
||||
SideEffect.UPDATE
|
||||
SideEffect.NEIGHBORS,
|
||||
SideEffect.LIGHTING,
|
||||
SideEffect.VALIDATION,
|
||||
SideEffect.ENTITY_AI,
|
||||
SideEffect.EVENTS,
|
||||
SideEffect.UPDATE
|
||||
);
|
||||
|
||||
@Override
|
||||
@ -843,10 +870,10 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clearContainerBlockContents(org.bukkit.World world, BlockVector3 pt) {
|
||||
public boolean clearContainerBlockContents(World world, BlockVector3 pt) {
|
||||
ServerLevel originalWorld = ((CraftWorld) world).getHandle();
|
||||
|
||||
BlockEntity entity = originalWorld.getBlockEntity(new BlockPos(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()));
|
||||
BlockEntity entity = originalWorld.getBlockEntity(new BlockPos(pt.x(), pt.y(), pt.z()));
|
||||
if (entity instanceof Clearable) {
|
||||
((Clearable) entity).clearContent();
|
||||
return true;
|
||||
@ -854,7 +881,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
return false;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
@Override
|
||||
public void initializeRegistries() {
|
||||
DedicatedServer server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
// Biomes
|
||||
@ -863,7 +890,35 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
BiomeType.REGISTRY.register(name.toString(), new BiomeType(name.toString()));
|
||||
}
|
||||
}
|
||||
}*
|
||||
|
||||
// BiomeCategories
|
||||
Registry<Biome> biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME);
|
||||
biomeRegistry.getTagNames().forEach(tagKey -> {
|
||||
String key = tagKey.location().toString();
|
||||
if (BiomeCategory.REGISTRY.get(key) == null) {
|
||||
BiomeCategory.REGISTRY.register(key, new BiomeCategory(
|
||||
key,
|
||||
() -> biomeRegistry.getTag(tagKey)
|
||||
.stream()
|
||||
.flatMap(HolderSet.Named::stream)
|
||||
.map(Holder::value)
|
||||
.map(this::adapt)
|
||||
.collect(Collectors.toSet()))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBiomeUpdates(World world, Iterable<BlockVector2> chunks) {
|
||||
ServerLevel originalWorld = ((CraftWorld) world).getHandle();
|
||||
|
||||
List<ChunkAccess> nativeChunks = chunks instanceof Collection<BlockVector2> chunkCollection ? Lists.newArrayListWithCapacity(chunkCollection.size()) : Lists.newArrayList();
|
||||
for (BlockVector2 chunk : chunks) {
|
||||
nativeChunks.add(originalWorld.getChunk(chunk.x(), chunk.z(), ChunkStatus.BIOMES, false));
|
||||
}
|
||||
originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that is less likely to break
|
||||
@ -877,51 +932,49 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
* @return native WorldEdit NBT structure
|
||||
*/
|
||||
@Override
|
||||
public BinaryTag toNativeBinary(net.minecraft.nbt.Tag foreign) {
|
||||
public LinTag<?> toNativeLin(net.minecraft.nbt.Tag foreign) {
|
||||
if (foreign == null) {
|
||||
return null;
|
||||
}
|
||||
if (foreign instanceof net.minecraft.nbt.CompoundTag) {
|
||||
Map<String, BinaryTag> values = new HashMap<>();
|
||||
Map<String, LinTag<?>> values = new HashMap<>();
|
||||
Set<String> 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));
|
||||
values.put(str, toNativeLin(base));
|
||||
}
|
||||
return CompoundBinaryTag.from(values);
|
||||
return LinCompoundTag.of(values);
|
||||
} else if (foreign instanceof net.minecraft.nbt.ByteTag) {
|
||||
return ByteBinaryTag.of(((net.minecraft.nbt.ByteTag) foreign).getAsByte());
|
||||
return LinByteTag.of(((net.minecraft.nbt.ByteTag) foreign).getAsByte());
|
||||
} else if (foreign instanceof net.minecraft.nbt.ByteArrayTag) {
|
||||
return ByteArrayBinaryTag.of(((net.minecraft.nbt.ByteArrayTag) foreign).getAsByteArray());
|
||||
return LinByteArrayTag.of(((net.minecraft.nbt.ByteArrayTag) foreign).getAsByteArray());
|
||||
} else if (foreign instanceof net.minecraft.nbt.DoubleTag) {
|
||||
return DoubleBinaryTag.of(((net.minecraft.nbt.DoubleTag) foreign).getAsDouble());
|
||||
return LinDoubleTag.of(((net.minecraft.nbt.DoubleTag) foreign).getAsDouble());
|
||||
} else if (foreign instanceof net.minecraft.nbt.FloatTag) {
|
||||
return FloatBinaryTag.of(((net.minecraft.nbt.FloatTag) foreign).getAsFloat());
|
||||
return LinFloatTag.of(((net.minecraft.nbt.FloatTag) foreign).getAsFloat());
|
||||
} else if (foreign instanceof net.minecraft.nbt.IntTag) {
|
||||
return IntBinaryTag.of(((net.minecraft.nbt.IntTag) foreign).getAsInt());
|
||||
return LinIntTag.of(((net.minecraft.nbt.IntTag) foreign).getAsInt());
|
||||
} else if (foreign instanceof net.minecraft.nbt.IntArrayTag) {
|
||||
return IntArrayBinaryTag.of(((net.minecraft.nbt.IntArrayTag) foreign).getAsIntArray());
|
||||
return LinIntArrayTag.of(((net.minecraft.nbt.IntArrayTag) foreign).getAsIntArray());
|
||||
} else if (foreign instanceof net.minecraft.nbt.LongArrayTag) {
|
||||
return LongArrayBinaryTag.of(((net.minecraft.nbt.LongArrayTag) foreign).getAsLongArray());
|
||||
return LinLongArrayTag.of(((net.minecraft.nbt.LongArrayTag) foreign).getAsLongArray());
|
||||
} else if (foreign instanceof net.minecraft.nbt.ListTag) {
|
||||
try {
|
||||
return toNativeList((net.minecraft.nbt.ListTag) foreign);
|
||||
return toNativeLinList((net.minecraft.nbt.ListTag) foreign);
|
||||
} catch (Throwable e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e);
|
||||
return ListBinaryTag.empty();
|
||||
logger.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e);
|
||||
}
|
||||
} else if (foreign instanceof net.minecraft.nbt.LongTag) {
|
||||
return LongBinaryTag.of(((net.minecraft.nbt.LongTag) foreign).getAsLong());
|
||||
return LinLongTag.of(((net.minecraft.nbt.LongTag) foreign).getAsLong());
|
||||
} else if (foreign instanceof net.minecraft.nbt.ShortTag) {
|
||||
return ShortBinaryTag.of(((net.minecraft.nbt.ShortTag) foreign).getAsShort());
|
||||
return LinShortTag.of(((net.minecraft.nbt.ShortTag) foreign).getAsShort());
|
||||
} else if (foreign instanceof net.minecraft.nbt.StringTag) {
|
||||
return StringBinaryTag.of(foreign.getAsString());
|
||||
return LinStringTag.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());
|
||||
return LinEndTag.instance();
|
||||
}
|
||||
throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -932,14 +985,16 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
* @throws SecurityException on error
|
||||
* @throws IllegalArgumentException on error
|
||||
*/
|
||||
private ListBinaryTag toNativeList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException {
|
||||
ListBinaryTag.Builder values = ListBinaryTag.builder();
|
||||
private LinListTag<?> toNativeLinList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException {
|
||||
LinListTag.Builder<LinTag<?>> builder = LinListTag.builder(
|
||||
LinTagType.fromId(LinTagId.fromId(foreign.getElementType()))
|
||||
);
|
||||
|
||||
for (net.minecraft.nbt.Tag tag : foreign) {
|
||||
values.add(toNativeBinary(tag));
|
||||
builder.add(toNativeLin(tag));
|
||||
}
|
||||
|
||||
return values.build();
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -949,44 +1004,43 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
* @return non-native structure
|
||||
*/
|
||||
@Override
|
||||
public net.minecraft.nbt.Tag fromNativeBinary(BinaryTag foreign) {
|
||||
public net.minecraft.nbt.Tag fromNativeLin(LinTag<?> foreign) {
|
||||
if (foreign == null) {
|
||||
return null;
|
||||
}
|
||||
if (foreign instanceof CompoundBinaryTag) {
|
||||
if (foreign instanceof LinCompoundTag compoundTag) {
|
||||
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
|
||||
for (String key : ((CompoundBinaryTag) foreign).keySet()) {
|
||||
tag.put(key, fromNativeBinary(((CompoundBinaryTag) foreign).get(key)));
|
||||
for (var entry : compoundTag.value().entrySet()) {
|
||||
tag.put(entry.getKey(), fromNativeLin(entry.getValue()));
|
||||
}
|
||||
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) {
|
||||
} else if (foreign instanceof LinByteTag byteTag) {
|
||||
return net.minecraft.nbt.ByteTag.valueOf(byteTag.valueAsByte());
|
||||
} else if (foreign instanceof LinByteArrayTag byteArrayTag) {
|
||||
return new net.minecraft.nbt.ByteArrayTag(byteArrayTag.value());
|
||||
} else if (foreign instanceof LinDoubleTag doubleTag) {
|
||||
return net.minecraft.nbt.DoubleTag.valueOf(doubleTag.valueAsDouble());
|
||||
} else if (foreign instanceof LinFloatTag floatTag) {
|
||||
return net.minecraft.nbt.FloatTag.valueOf(floatTag.valueAsFloat());
|
||||
} else if (foreign instanceof LinIntTag intTag) {
|
||||
return net.minecraft.nbt.IntTag.valueOf(intTag.valueAsInt());
|
||||
} else if (foreign instanceof LinIntArrayTag intArrayTag) {
|
||||
return new net.minecraft.nbt.IntArrayTag(intArrayTag.value());
|
||||
} else if (foreign instanceof LinLongArrayTag longArrayTag) {
|
||||
return new net.minecraft.nbt.LongArrayTag(longArrayTag.value());
|
||||
} else if (foreign instanceof LinListTag<?> listTag) {
|
||||
net.minecraft.nbt.ListTag tag = new net.minecraft.nbt.ListTag();
|
||||
ListBinaryTag foreignList = (ListBinaryTag) foreign;
|
||||
for (BinaryTag t : foreignList) {
|
||||
tag.add(fromNativeBinary(t));
|
||||
for (var t : listTag.value()) {
|
||||
tag.add(fromNativeLin(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) {
|
||||
} else if (foreign instanceof LinLongTag longTag) {
|
||||
return net.minecraft.nbt.LongTag.valueOf(longTag.valueAsLong());
|
||||
} else if (foreign instanceof LinShortTag shortTag) {
|
||||
return net.minecraft.nbt.ShortTag.valueOf(shortTag.valueAsShort());
|
||||
} else if (foreign instanceof LinStringTag stringTag) {
|
||||
return net.minecraft.nbt.StringTag.valueOf(stringTag.value());
|
||||
} else if (foreign instanceof LinEndTag) {
|
||||
return net.minecraft.nbt.EndTag.INSTANCE;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName());
|
||||
@ -1025,7 +1079,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
WatchdogThread.tick();
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to tick watchdog", e);
|
||||
logger.log(Level.WARNING, "Failed to tick watchdog", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1037,7 +1091,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
MojangWatchdog(DedicatedServer server) throws NoSuchFieldException {
|
||||
this.server = server;
|
||||
Field tickField = MinecraftServer.class.getDeclaredField(
|
||||
Refraction.pickName("nextTickTime", "ah")
|
||||
Refraction.pickName("nextTickTime", "ag")
|
||||
);
|
||||
if (tickField.getType() != long.class) {
|
||||
throw new IllegalStateException("nextTickTime is not a long field, mapping is likely incorrect");
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
@ -35,7 +35,6 @@ import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.datafixers.DataFixerBuilder;
|
||||
import com.mojang.datafixers.schemas.Schema;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@ -48,7 +47,9 @@ 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 org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
@ -62,7 +63,6 @@ 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)
|
||||
@ -76,18 +76,17 @@ import javax.annotation.Nullable;
|
||||
* receive the source version in the compound
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer {
|
||||
public class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer {
|
||||
|
||||
//FAWE start - BinaryTag
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T fixUp(FixType<T> type, T original, int srcVer) {
|
||||
if (type == FixTypes.CHUNK) {
|
||||
return (T) fixChunk((CompoundBinaryTag) original, srcVer);
|
||||
return (T) fixChunk((LinCompoundTag) original, srcVer);
|
||||
} else if (type == FixTypes.BLOCK_ENTITY) {
|
||||
return (T) fixBlockEntity((CompoundBinaryTag) original, srcVer);
|
||||
return (T) fixBlockEntity((LinCompoundTag) original, srcVer);
|
||||
} else if (type == FixTypes.ENTITY) {
|
||||
return (T) fixEntity((CompoundBinaryTag) original, srcVer);
|
||||
return (T) fixEntity((LinCompoundTag) original, srcVer);
|
||||
} else if (type == FixTypes.BLOCK_STATE) {
|
||||
return (T) fixBlockState((String) original, srcVer);
|
||||
} else if (type == FixTypes.ITEM_TYPE) {
|
||||
@ -98,24 +97,23 @@ class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.wo
|
||||
return original;
|
||||
}
|
||||
|
||||
private CompoundBinaryTag fixChunk(CompoundBinaryTag originalChunk, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(originalChunk);
|
||||
private LinCompoundTag fixChunk(LinCompoundTag originalChunk, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(originalChunk);
|
||||
net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.CHUNK, tag, srcVer);
|
||||
return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
|
||||
return (LinCompoundTag) adapter.toNativeLin(fixed);
|
||||
}
|
||||
|
||||
private CompoundBinaryTag fixBlockEntity(CompoundBinaryTag origTileEnt, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origTileEnt);
|
||||
private LinCompoundTag fixBlockEntity(LinCompoundTag origTileEnt, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(origTileEnt);
|
||||
net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer);
|
||||
return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
|
||||
return (LinCompoundTag) adapter.toNativeLin(fixed);
|
||||
}
|
||||
|
||||
private CompoundBinaryTag fixEntity(CompoundBinaryTag origEnt, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origEnt);
|
||||
private LinCompoundTag fixEntity(LinCompoundTag origEnt, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(origEnt);
|
||||
net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.ENTITY, tag, srcVer);
|
||||
return (CompoundBinaryTag) adapter.toNativeBinary(fixed);
|
||||
return (LinCompoundTag) adapter.toNativeLin(fixed);
|
||||
}
|
||||
//FAWE end
|
||||
|
||||
private String fixBlockState(String blockState, int srcVer) {
|
||||
net.minecraft.nbt.CompoundTag stateNBT = stateToNBT(blockState);
|
||||
@ -173,7 +171,7 @@ class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.wo
|
||||
private static final NbtOps OPS_NBT = NbtOps.INSTANCE;
|
||||
private static final int LEGACY_VERSION = 1343;
|
||||
private static int DATA_VERSION;
|
||||
static PaperweightDataConverters INSTANCE;
|
||||
public static PaperweightDataConverters INSTANCE;
|
||||
|
||||
private final Map<LegacyType, List<DataConverter>> converters = new EnumMap<>(LegacyType.class);
|
||||
private final Map<LegacyType, List<DataInspector>> inspectors = new EnumMap<>(LegacyType.class);
|
||||
@ -204,7 +202,7 @@ class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.wo
|
||||
}
|
||||
}
|
||||
|
||||
PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) {
|
||||
public PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) {
|
||||
super(dataVersion);
|
||||
DATA_VERSION = dataVersion;
|
||||
INSTANCE = this;
|
||||
@ -234,7 +232,6 @@ class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.wo
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public <T> Dynamic<T> update(TypeReference type, Dynamic<T> dynamic, int sourceVer, int targetVer) {
|
||||
LegacyType legacyType = DFU_TO_LEGACY.get(type.typeName());
|
||||
if (sourceVer < LEGACY_VERSION && legacyType != null) {
|
@ -17,17 +17,19 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
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.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
@ -38,9 +40,12 @@ 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);
|
||||
private static final ClientInformation FAKE_CLIENT_INFO = new ClientInformation(
|
||||
"en_US", 16, ChatVisiblity.FULL, true, 0, HumanoidArm.LEFT, false, false
|
||||
);
|
||||
|
||||
PaperweightFakePlayer(ServerLevel world) {
|
||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
|
||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE, FAKE_CLIENT_INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -67,7 +72,7 @@ class PaperweightFakePlayer extends ServerPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
||||
public void updateOptions(ClientInformation clientOptions) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -88,6 +93,6 @@ class PaperweightFakePlayer extends ServerPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openTextEdit(SignBlockEntity sign) {
|
||||
public void openTextEdit(SignBlockEntity sign, boolean front) {
|
||||
}
|
||||
}
|
@ -17,27 +17,30 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3;
|
||||
|
||||
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.server.level.ChunkHolder;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
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_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
private static final int UPDATE = 1;
|
||||
@ -70,8 +73,8 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
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();
|
||||
? Block.stateById(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -101,8 +104,15 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) {
|
||||
return false;
|
||||
public boolean updateTileEntity(BlockPos position, LinCompoundTag tag) {
|
||||
// We will assume that the tile entity was created for us
|
||||
BlockEntity tileEntity = getWorld().getBlockEntity(position);
|
||||
if (tileEntity == null) {
|
||||
return false;
|
||||
}
|
||||
Tag nativeTag = adapter.fromNativeLin(tag);
|
||||
PaperweightAdapter.readTagIntoTileEntity((net.minecraft.nbt.CompoundTag) nativeTag, tileEntity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,7 +124,7 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(LevelChunk chunk) {
|
||||
return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
||||
return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -144,6 +154,12 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
|
||||
ServerLevel world = getWorld();
|
||||
newState.onPlace(world, pos, oldState, false);
|
||||
}
|
||||
|
||||
// Not sure why neighborChanged is deprecated
|
||||
@SuppressWarnings("deprecation")
|
||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
||||
@ -153,8 +169,6 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
|
||||
@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();
|
@ -1,32 +1,27 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
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.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||
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.Fluids;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
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;
|
||||
private final FaweCompoundTag tile;
|
||||
|
||||
public PaperweightBlockMaterial(Block block) {
|
||||
this(block, block.defaultBlockState());
|
||||
@ -35,14 +30,8 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
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,
|
||||
@ -50,7 +39,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
);
|
||||
tile = tileEntity == null
|
||||
? null
|
||||
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
||||
: PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity);
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
@ -65,10 +54,6 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
return craftBlockData;
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAir() {
|
||||
return blockState.isAir();
|
||||
@ -81,7 +66,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return material.isSolidBlocking();
|
||||
return blockState.canOcclude();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -91,12 +76,13 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
|
||||
@Override
|
||||
public boolean isLiquid() {
|
||||
return material.isLiquid();
|
||||
return !blockState.getFluidState().is(Fluids.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid() {
|
||||
return material.isSolid();
|
||||
// No access to world -> EmptyBlockGetter
|
||||
return blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -126,12 +112,12 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
|
||||
@Override
|
||||
public boolean isFragileWhenPushed() {
|
||||
return material.getPushReaction() == PushReaction.DESTROY;
|
||||
return blockState.getPistonPushReaction() == PushReaction.DESTROY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnpushable() {
|
||||
return material.getPushReaction() == PushReaction.BLOCK;
|
||||
return blockState.getPistonPushReaction() == PushReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -139,14 +125,15 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
return block.isRandomlyTicking(blockState);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean isMovementBlocker() {
|
||||
return material.isSolid();
|
||||
return blockState.blocksMotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBurnable() {
|
||||
return material.isFlammable();
|
||||
return craftMaterial.isBurnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -157,12 +144,12 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
|
||||
@Override
|
||||
public boolean isReplacedDuringPlacement() {
|
||||
return material.isReplaceable();
|
||||
return blockState.canBeReplaced();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTranslucent() {
|
||||
return isTranslucent;
|
||||
return !blockState.canOcclude();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -176,14 +163,14 @@ public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getDefaultTile() {
|
||||
public @Nullable FaweCompoundTag defaultTile() {
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMapColor() {
|
||||
// rgb field
|
||||
return material.getColor().col;
|
||||
return block.defaultMapColor().col;
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +1,24 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
|
||||
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.nbt.FaweCompoundTag;
|
||||
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.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1.PaperweightAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.regen.PaperweightRegen;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.regen.PaperweightRegen;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
@ -38,11 +34,7 @@ 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;
|
||||
@ -77,19 +69,20 @@ import net.minecraft.world.level.chunk.LevelChunk;
|
||||
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.World;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.util.CraftNamespacedKey;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
import org.enginehub.linbus.tree.LinStringTag;
|
||||
import org.enginehub.linbus.tree.LinTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -105,14 +98,14 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
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<net.minecraft.nbt.Tag> {
|
||||
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
||||
@ -124,18 +117,16 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
}
|
||||
}
|
||||
|
||||
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<String, List<Property<?>>> allBlockProperties = null;
|
||||
|
||||
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
|
||||
this.parent = new PaperweightAdapter();
|
||||
super(new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter());
|
||||
}
|
||||
|
||||
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
|
||||
return blockEntity -> FaweCompoundTag.of(
|
||||
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId())
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -248,11 +239,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
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();
|
||||
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||
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);
|
||||
@ -268,12 +258,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
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();
|
||||
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||
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);
|
||||
@ -288,7 +277,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
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((LinCompoundTag) toNativeLin(tag));
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,10 +291,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||
return new PaperweightFaweWorldNativeAccess(
|
||||
this,
|
||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
||||
);
|
||||
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -319,14 +305,14 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
|
||||
if (id != null) {
|
||||
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
|
||||
Supplier<CompoundBinaryTag> saveTag = () -> {
|
||||
Supplier<LinCompoundTag> 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<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
|
||||
tags.put("Id", StringBinaryTag.of(id));
|
||||
return CompoundBinaryTag.from(tags);
|
||||
final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag);
|
||||
final Map<String, LinTag<?>> tags = NbtUtils.getLinCompoundTagValues(tag);
|
||||
tags.put("Id", LinStringTag.of(id));
|
||||
return LinCompoundTag.of(tags);
|
||||
};
|
||||
return new LazyBaseEntity(type, saveTag);
|
||||
} else {
|
||||
@ -450,7 +436,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
|
||||
@Override
|
||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
||||
ServerLevel nmsWorld = getServerLevel(world);
|
||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||
if (map != null && wasAccessibleSinceLastSave(map)) {
|
||||
boolean flag = false;
|
||||
@ -488,8 +474,8 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
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())
|
||||
getServerLevel(world),
|
||||
new BlockPos(blockVector3.x(), blockVector3.y(), blockVector3.z())
|
||||
);
|
||||
}
|
||||
|
||||
@ -497,7 +483,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
|
||||
ItemStack stack = new ItemStack(
|
||||
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM)
|
||||
.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
||||
.get(ResourceLocation.tryParse(baseItemStack.getType().id())),
|
||||
baseItemStack.getAmount()
|
||||
);
|
||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
|
||||
@ -505,54 +491,33 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
}
|
||||
|
||||
@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<BlockPos, CraftBlockState> 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;
|
||||
protected void preCaptureStates(final ServerLevel serverLevel) {
|
||||
serverLevel.captureTreeGeneration = true;
|
||||
serverLevel.captureBlockStates = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
|
||||
return new ArrayList<>(serverLevel.capturedBlockStates.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
|
||||
serverLevel.captureBlockStates = false;
|
||||
serverLevel.captureTreeGeneration = false;
|
||||
serverLevel.capturedBlockStates.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServerLevel getServerLevel(final World world) {
|
||||
return ((CraftWorld) world).getHandle();
|
||||
}
|
||||
|
||||
@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())));
|
||||
weStack.setNbt(((LinCompoundTag) toNativeLin(nmsStack.getTag())));
|
||||
return weStack;
|
||||
}
|
||||
|
||||
@ -585,7 +550,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.registryOrThrow(BIOME);
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.id());
|
||||
Biome biome = registry.get(resourceLocation);
|
||||
return registry.getId(biome);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
@ -9,21 +9,21 @@ 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.FullChunkStatus;
|
||||
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_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -132,14 +132,14 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) {
|
||||
public boolean updateTileEntity(BlockPos blockPos, LinCompoundTag 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);
|
||||
net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeLin(tag);
|
||||
blockEntity.load((CompoundTag) nativeTag);
|
||||
return true;
|
||||
}
|
||||
@ -157,7 +157,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(LevelChunk levelChunk) {
|
||||
return levelChunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
||||
return levelChunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -217,6 +217,12 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
|
||||
Level world = getLevel();
|
||||
newState.onPlace(world, pos, oldState, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(
|
||||
BlockPos blockPos,
|
||||
@ -246,7 +252,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
PaperweightPlatformAdapter.sendChunk(chunk, getLevel().getWorld().getHandle(), chunk.x(), chunk.z());
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -262,7 +268,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
PaperweightPlatformAdapter.sendChunk(chunk, getLevel().getWorld().getHandle(), chunk.x(), chunk.z());
|
||||
}
|
||||
}
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks;
|
||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
||||
@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
||||
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.internal.Constants;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
@ -34,6 +31,7 @@ import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
@ -58,14 +56,22 @@ import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlock;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlock;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
import org.enginehub.linbus.tree.LinDoubleTag;
|
||||
import org.enginehub.linbus.tree.LinFloatTag;
|
||||
import org.enginehub.linbus.tree.LinListTag;
|
||||
import org.enginehub.linbus.tree.LinStringTag;
|
||||
import org.enginehub.linbus.tree.LinTagType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.AbstractSet;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -75,12 +81,13 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
@ -89,29 +96,34 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
|
||||
private static final Function<BlockEntity, CompoundTag> nmsTile2We =
|
||||
tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
||||
public static final Function<BlockEntity, FaweCompoundTag> NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()).blockEntityToCompoundTag();
|
||||
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter());
|
||||
private final ReadWriteLock sectionLock = new ReentrantReadWriteLock();
|
||||
private final ReentrantLock callLock = new ReentrantLock();
|
||||
private final ServerLevel serverLevel;
|
||||
private final int chunkX;
|
||||
private final int chunkZ;
|
||||
private final IntPair chunkPos;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
private final int minSectionPosition;
|
||||
private final int maxSectionPosition;
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final IdMap<Holder<Biome>> biomeHolderIdMap;
|
||||
private final ConcurrentHashMap<Integer, PaperweightGetBlocks_Copy> copies = new ConcurrentHashMap<>();
|
||||
private final Object sendLock = new Object();
|
||||
private LevelChunkSection[] sections;
|
||||
private LevelChunk levelChunk;
|
||||
private DataLayer[] blockLight;
|
||||
private DataLayer[] skyLight;
|
||||
private boolean createCopy = false;
|
||||
private PaperweightGetBlocks_Copy copy = null;
|
||||
private boolean forceLoadSections = true;
|
||||
private boolean lightUpdate = false;
|
||||
private int copyKey = 0;
|
||||
|
||||
public PaperweightGetBlocks(World world, int chunkX, int chunkZ) {
|
||||
this(((CraftWorld) world).getHandle(), chunkX, chunkZ);
|
||||
@ -130,6 +142,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
this.blockLight = new DataLayer[getSectionCount()];
|
||||
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME);
|
||||
this.biomeHolderIdMap = biomeRegistry.asHolderIdMap();
|
||||
this.chunkPos = new IntPair(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
@ -146,13 +159,28 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
public int setCreateCopy(boolean createCopy) {
|
||||
if (!callLock.isHeldByCurrentThread()) {
|
||||
throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked.");
|
||||
}
|
||||
this.createCopy = createCopy;
|
||||
// Increment regardless of whether copy will be created or not to return null from getCopy()
|
||||
return ++this.copyKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkGet getCopy() {
|
||||
return copy;
|
||||
public IChunkGet getCopy(final int key) {
|
||||
return copies.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockCall() {
|
||||
this.callLock.lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlockCall() {
|
||||
this.callLock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -236,23 +264,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
public FaweCompoundTag tile(final int x, final int y, final int z) {
|
||||
BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + (
|
||||
chunkX << 4), y, (z & 15) + (
|
||||
chunkZ << 4)));
|
||||
if (blockEntity == null) {
|
||||
return null;
|
||||
}
|
||||
return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId));
|
||||
return NMS_TO_TILE.apply(blockEntity);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
public Map<BlockVector3, FaweCompoundTag> tiles() {
|
||||
Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities();
|
||||
if (nmsTiles.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
|
||||
return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -272,8 +301,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(
|
||||
LightLayer.BLOCK,
|
||||
sectionPos,
|
||||
dataLayer,
|
||||
true
|
||||
dataLayer
|
||||
);
|
||||
}
|
||||
skyLight[alayer] = dataLayer;
|
||||
@ -300,7 +328,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
Arrays.fill(LAYER_COUNT, (byte) 15);
|
||||
dataLayer = new DataLayer(LAYER_COUNT);
|
||||
((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos,
|
||||
dataLayer, true
|
||||
dataLayer
|
||||
);
|
||||
}
|
||||
blockLight[alayer] = dataLayer;
|
||||
@ -316,14 +344,22 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
Entity entity = serverLevel.getEntity(uuid);
|
||||
public @Nullable FaweCompoundTag entity(final UUID uuid) {
|
||||
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
|
||||
Entity entity = null;
|
||||
for (Entity e : entities) {
|
||||
if (e.getUUID().equals(uuid)) {
|
||||
entity = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (entity != null) {
|
||||
org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity();
|
||||
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData();
|
||||
return FaweCompoundTag.of(BukkitAdapter.adapt(bukkitEnt).getState().getNbt());
|
||||
}
|
||||
for (CompoundTag tag : getEntities()) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
for (FaweCompoundTag tag : entities()) {
|
||||
if (uuid.equals(NbtUtils.uuid(tag))) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
@ -331,14 +367,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> entities() {
|
||||
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
|
||||
if (entities.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
int size = entities.size();
|
||||
return new AbstractSet<>() {
|
||||
return new AbstractCollection<>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
@ -351,10 +387,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
@Override
|
||||
public boolean contains(Object get) {
|
||||
if (!(get instanceof CompoundTag getTag)) {
|
||||
if (!(get instanceof FaweCompoundTag getTag)) {
|
||||
return false;
|
||||
}
|
||||
UUID getUUID = getTag.getUUID();
|
||||
UUID getUUID = NbtUtils.uuid(getTag);
|
||||
for (Entity entity : entities) {
|
||||
UUID uuid = entity.getUUID();
|
||||
if (uuid.equals(getUUID)) {
|
||||
@ -366,12 +402,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<CompoundTag> iterator() {
|
||||
Iterable<CompoundTag> result = entities.stream().map(input -> {
|
||||
public Iterator<FaweCompoundTag> iterator() {
|
||||
Iterable<FaweCompoundTag> result = entities.stream().map(input -> {
|
||||
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
|
||||
input.save(tag);
|
||||
return (CompoundTag) adapter.toNative(tag);
|
||||
}).collect(Collectors.toList());
|
||||
return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag));
|
||||
})::iterator;
|
||||
return result.iterator();
|
||||
}
|
||||
};
|
||||
@ -388,12 +424,19 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
|
||||
if (!callLock.isHeldByCurrentThread()) {
|
||||
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
|
||||
}
|
||||
forceLoadSections = false;
|
||||
copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null;
|
||||
LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
||||
if (createCopy) {
|
||||
if (copies.containsKey(copyKey)) {
|
||||
throw new IllegalStateException("Copy key already used.");
|
||||
}
|
||||
copies.put(copyKey, copy);
|
||||
}
|
||||
try {
|
||||
ServerLevel nmsWorld = serverLevel;
|
||||
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||
|
||||
// Remove existing tiles. Create a copy so that we can remove blocks
|
||||
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||
List<BlockEntity> beacons = null;
|
||||
@ -465,6 +508,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
biomeData
|
||||
);
|
||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||
serverLevel.getWorld().getName(),
|
||||
chunkPos,
|
||||
levelChunkSections,
|
||||
null,
|
||||
newSection,
|
||||
@ -482,7 +527,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setBiomesToPalettedContainer(biomes, setSectionIndex, existingSection.getBiomes());
|
||||
PalettedContainer<Holder<Biome>> paletteBiomes = setBiomesToPalettedContainer(
|
||||
biomes,
|
||||
setSectionIndex,
|
||||
existingSection.getBiomes()
|
||||
);
|
||||
if (paletteBiomes != null) {
|
||||
PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -524,13 +576,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
if (existingSection == null) {
|
||||
PalettedContainer<Holder<Biome>> biomeData = biomes == null ? new PalettedContainer<>(
|
||||
biomeHolderIdMap,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(
|
||||
BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES
|
||||
) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap);
|
||||
newSection = PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
@ -540,6 +587,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
biomeData
|
||||
);
|
||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||
serverLevel.getWorld().getName(),
|
||||
chunkPos,
|
||||
levelChunkSections,
|
||||
null,
|
||||
newSection,
|
||||
@ -596,16 +645,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
existingSection.getBiomes()
|
||||
);
|
||||
|
||||
newSection =
|
||||
PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
this::loadPrivately,
|
||||
setArr,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData
|
||||
);
|
||||
newSection = PaperweightPlatformAdapter.newChunkSection(
|
||||
layerNo,
|
||||
this::loadPrivately,
|
||||
setArr,
|
||||
adapter,
|
||||
biomeRegistry,
|
||||
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
|
||||
);
|
||||
if (!PaperweightPlatformAdapter.setSectionAtomic(
|
||||
serverLevel.getWorld().getName(),
|
||||
chunkPos,
|
||||
levelChunkSections,
|
||||
existingSection,
|
||||
newSection,
|
||||
@ -679,7 +729,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
||||
for (UUID uuid : entityRemoves) {
|
||||
Entity entity = nmsWorld.getEntities().get(uuid);
|
||||
Entity entity = serverLevel.getEntities().get(uuid);
|
||||
if (entity != null) {
|
||||
removeEntity(entity);
|
||||
}
|
||||
@ -691,48 +741,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
};
|
||||
}
|
||||
|
||||
Set<CompoundTag> entities = set.getEntities();
|
||||
Collection<FaweCompoundTag> entities = set.entities();
|
||||
if (entities != null && !entities.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[2];
|
||||
}
|
||||
|
||||
syncTasks[1] = () -> {
|
||||
Iterator<CompoundTag> iterator = entities.iterator();
|
||||
Iterator<FaweCompoundTag> iterator = entities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final CompoundTag nativeTag = iterator.next();
|
||||
final Map<String, Tag> entityTagMap = nativeTag.getValue();
|
||||
final StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
final ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
final FaweCompoundTag nativeTag = iterator.next();
|
||||
final LinCompoundTag linTag = nativeTag.linTag();
|
||||
final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
|
||||
final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
|
||||
final LinListTag<LinFloatTag> rotTag = linTag.findListTag("Rotation", LinTagType.floatTag());
|
||||
if (idTag == null || posTag == null || rotTag == null) {
|
||||
LOGGER.error("Unknown entity tag: {}", nativeTag);
|
||||
continue;
|
||||
}
|
||||
final double x = posTag.getDouble(0);
|
||||
final double y = posTag.getDouble(1);
|
||||
final double z = posTag.getDouble(2);
|
||||
final float yaw = rotTag.getFloat(0);
|
||||
final float pitch = rotTag.getFloat(1);
|
||||
final String id = idTag.getValue();
|
||||
final double x = posTag.get(0).valueAsDouble();
|
||||
final double y = posTag.get(1).valueAsDouble();
|
||||
final double z = posTag.get(2).valueAsDouble();
|
||||
final float yaw = rotTag.get(0).valueAsFloat();
|
||||
final float pitch = rotTag.get(1).valueAsFloat();
|
||||
final String id = idTag.value();
|
||||
|
||||
EntityType<?> type = EntityType.byString(id).orElse(null);
|
||||
if (type != null) {
|
||||
Entity entity = type.create(nmsWorld);
|
||||
Entity entity = type.create(serverLevel);
|
||||
if (entity != null) {
|
||||
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative(
|
||||
nativeTag);
|
||||
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(linTag);
|
||||
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||
tag.remove(name);
|
||||
}
|
||||
entity.load(tag);
|
||||
entity.absMoveTo(x, y, z, yaw, pitch);
|
||||
entity.setUUID(nativeTag.getUUID());
|
||||
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
||||
entity.setUUID(NbtUtils.uuid(nativeTag));
|
||||
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
||||
LOGGER.warn(
|
||||
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
|
||||
id,
|
||||
nmsWorld.getWorld().getName(),
|
||||
serverLevel.getWorld().getName(),
|
||||
x,
|
||||
y,
|
||||
z
|
||||
@ -747,30 +796,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
// set tiles
|
||||
Map<BlockVector3, CompoundTag> tiles = set.getTiles();
|
||||
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
|
||||
if (tiles != null && !tiles.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[1];
|
||||
}
|
||||
|
||||
syncTasks[0] = () -> {
|
||||
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
|
||||
final CompoundTag nativeTag = entry.getValue();
|
||||
for (final Map.Entry<BlockVector3, FaweCompoundTag> entry : tiles.entrySet()) {
|
||||
final FaweCompoundTag nativeTag = entry.getValue();
|
||||
final BlockVector3 blockHash = entry.getKey();
|
||||
final int x = blockHash.getX() + bx;
|
||||
final int y = blockHash.getY();
|
||||
final int z = blockHash.getZ() + bz;
|
||||
final int x = blockHash.x() + bx;
|
||||
final int y = blockHash.y();
|
||||
final int z = blockHash.z() + bz;
|
||||
final BlockPos pos = new BlockPos(x, y, z);
|
||||
|
||||
synchronized (nmsWorld) {
|
||||
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos);
|
||||
synchronized (serverLevel) {
|
||||
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
|
||||
if (tileEntity == null || tileEntity.isRemoved()) {
|
||||
nmsWorld.removeBlockEntity(pos);
|
||||
tileEntity = nmsWorld.getBlockEntity(pos);
|
||||
serverLevel.removeBlockEntity(pos);
|
||||
tileEntity = serverLevel.getBlockEntity(pos);
|
||||
}
|
||||
if (tileEntity != null) {
|
||||
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative(
|
||||
nativeTag);
|
||||
final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
|
||||
tag.put("x", IntTag.valueOf(x));
|
||||
tag.put("y", IntTag.valueOf(y));
|
||||
tag.put("z", IntTag.valueOf(z));
|
||||
@ -786,15 +834,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
callback = null;
|
||||
} else {
|
||||
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
|
||||
boolean finalLightUpdate = lightUpdate;
|
||||
callback = () -> {
|
||||
// Set Modified
|
||||
nmsChunk.setLightCorrect(true); // Set Modified
|
||||
nmsChunk.mustNotSave = false;
|
||||
nmsChunk.setUnsaved(true);
|
||||
// send to player
|
||||
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING) {
|
||||
this.send(finalMask, finalLightUpdate);
|
||||
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
|
||||
this.send();
|
||||
}
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
@ -816,7 +863,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
if (callback == null) {
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
queueHandler.async(finalizer, null);
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
@ -882,9 +929,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
if (super.sections[layer] != null) {
|
||||
synchronized (super.sectionLocks[layer]) {
|
||||
if (super.sections[layer].isFull() && super.blocks[layer] != null) {
|
||||
char[] blocks = new char[4096];
|
||||
System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096);
|
||||
return blocks;
|
||||
return super.blocks[layer];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -892,8 +937,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void send(int mask, boolean lighting) {
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting);
|
||||
public void send() {
|
||||
PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1004,9 +1049,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
|
||||
public LevelChunkSection[] getSections(boolean force) {
|
||||
force &= forceLoadSections;
|
||||
sectionLock.readLock().lock();
|
||||
LevelChunkSection[] tmp = sections;
|
||||
sectionLock.readLock().unlock();
|
||||
if (tmp == null || force) {
|
||||
try {
|
||||
sectionLock.writeLock().lock();
|
||||
@ -1052,8 +1095,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(
|
||||
lightLayer,
|
||||
sectionPos,
|
||||
dataLayer,
|
||||
true
|
||||
dataLayer
|
||||
);
|
||||
}
|
||||
synchronized (dataLayer) {
|
||||
@ -1076,38 +1118,25 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
||||
final int sectionIndex,
|
||||
final PalettedContainerRO<Holder<Biome>> data
|
||||
) {
|
||||
PalettedContainer<Holder<Biome>> biomeData;
|
||||
if (data instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
|
||||
biomeData = palettedContainer;
|
||||
} else {
|
||||
LOGGER.warn(
|
||||
"Cannot correctly set biomes to world, existing biomes may be lost. Expected class " +
|
||||
"type {} but got {}",
|
||||
PalettedContainer.class.getSimpleName(),
|
||||
data.getClass().getSimpleName()
|
||||
);
|
||||
biomeData = data.recreate();
|
||||
}
|
||||
BiomeType[] sectionBiomes;
|
||||
if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) {
|
||||
return biomeData;
|
||||
return null;
|
||||
}
|
||||
PalettedContainer<Holder<Biome>> biomeData = data.recreate();
|
||||
for (int y = 0, index = 0; y < 4; y++) {
|
||||
for (int z = 0; z < 4; z++) {
|
||||
for (int x = 0; x < 4; x++, index++) {
|
||||
BiomeType biomeType = sectionBiomes[index];
|
||||
if (biomeType == null) {
|
||||
continue;
|
||||
biomeData.set(x, y, z, data.get(x, y, z));
|
||||
} else {
|
||||
biomeData.set(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))
|
||||
);
|
||||
}
|
||||
biomeData.set(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(biomeType))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,22 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||
import com.fastasyncworldedit.core.queue.IBlocks;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
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.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
@ -24,8 +25,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.PalettedContainerRO;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@ -37,14 +41,14 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
|
||||
private final Set<FaweCompoundTag> entities = new HashSet<>();
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private PalettedContainer<Holder<Biome>>[] biomes = null;
|
||||
private Holder<Biome>[][] biomes = null;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
@ -55,44 +59,35 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
}
|
||||
|
||||
protected void storeTile(BlockEntity blockEntity) {
|
||||
@SuppressWarnings("unchecked")
|
||||
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
tiles.put(
|
||||
BlockVector3.at(
|
||||
blockEntity.getBlockPos().getX(),
|
||||
blockEntity.getBlockPos().getY(),
|
||||
blockEntity.getBlockPos().getZ()
|
||||
),
|
||||
new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId))
|
||||
FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId()))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
@SuppressWarnings("unchecked")
|
||||
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
entities.add((CompoundTag) adapter.toNative(compoundTag));
|
||||
entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> entities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
for (CompoundTag tag : entities) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
public @Nullable FaweCompoundTag entity(final UUID uuid) {
|
||||
for (FaweCompoundTag tag : entities) {
|
||||
if (uuid.equals(NbtUtils.uuid(tag))) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
@ -105,7 +100,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
public int setCreateCopy(boolean createCopy) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -142,7 +138,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()].get(x >> 2, (y & 15) >> 2, z >> 2);
|
||||
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()][(y & 12) << 2 | (z & 12) | (x & 12) >> 2];
|
||||
return PaperweightPlatformAdapter.adapt(biome, serverLevel);
|
||||
}
|
||||
|
||||
@ -171,10 +167,25 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
protected void storeBiomes(int layer, PalettedContainerRO<Holder<Biome>> biomeData) {
|
||||
if (biomes == null) {
|
||||
biomes = new PalettedContainer[getSectionCount()];
|
||||
biomes = new Holder[getSectionCount()][];
|
||||
}
|
||||
if (biomes[layer] == null) {
|
||||
biomes[layer] = new Holder[64];
|
||||
}
|
||||
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
|
||||
biomes[layer] = palettedContainer.copy();
|
||||
if (PaperLib.isPaper()) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
biomes[layer][i] = palettedContainer.get(i); // Only public on paper
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
biomes[layer][i] = (Holder<Biome>) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.error(
|
||||
"Cannot correctly save biomes to history. Expected class type {} but got {}",
|
||||
@ -187,7 +198,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
BlockState state = BlockTypesCache.states[get(x, y, z)];
|
||||
return state.toBaseBlock(this, x, y, z);
|
||||
return state.toBaseBlock((IBlocks) this, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -199,6 +210,10 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
if (blocks[layer] == null) {
|
||||
blocks[layer] = new char[4096];
|
||||
Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR);
|
||||
}
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@ -213,6 +228,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, FaweCompoundTag> tiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.destroystokyo.paper.util.maplist.EntityList;
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
@ -7,8 +7,8 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
@ -26,6 +26,7 @@ import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@ -44,6 +45,7 @@ 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.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.GlobalPalette;
|
||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||
@ -56,13 +58,13 @@ import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftChunk;
|
||||
import sun.misc.Unsafe;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftChunk;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -82,6 +84,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
@ -95,22 +98,22 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
private static final Field fieldTickingFluidCount;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
private static final Field fieldBiomes;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldThreadingDetector;
|
||||
private static final long fieldThreadingDetectorOffset;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final MethodHandle methodRemoveGameEventListener;
|
||||
private static final MethodHandle methodremoveTickingBlockEntity;
|
||||
|
||||
/*
|
||||
* This is a workaround for the changes from https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1fddefce1cdce44010927b888432bf70c0e88cde#src/main/java/org/bukkit/craftbukkit/CraftChunk.java
|
||||
* and is only needed to support 1.19.4 versions before *and* after this change.
|
||||
*/
|
||||
private static final MethodHandle CRAFT_CHUNK_GET_HANDLE;
|
||||
|
||||
private static final Field fieldRemove;
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
@ -120,6 +123,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
private static Field LEVEL_CHUNK_ENTITIES;
|
||||
private static Field SERVER_LEVEL_ENTITY_MANAGER;
|
||||
|
||||
static final MethodHandle PALETTED_CONTAINER_GET;
|
||||
|
||||
static {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
try {
|
||||
@ -135,12 +140,19 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c"));
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "h"));
|
||||
fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g"));
|
||||
fieldTickingFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g"));
|
||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f"));
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f"));
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
Field tmpFieldBiomes;
|
||||
try {
|
||||
// It seems to actually be biomes, but is apparently obfuscated to "i"
|
||||
tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("biomes");
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
tmpFieldBiomes = LevelChunkSection.class.getDeclaredField("i");
|
||||
}
|
||||
fieldBiomes = tmpFieldBiomes;
|
||||
fieldBiomes.setAccessible(true);
|
||||
|
||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
||||
"getVisibleChunkIfPresent",
|
||||
@ -149,20 +161,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
|
||||
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector);
|
||||
|
||||
fieldThreadingDetector.setAccessible(true);
|
||||
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
fieldLock.setAccessible(true);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldThreadingDetector = null;
|
||||
fieldThreadingDetectorOffset = -1;
|
||||
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
|
||||
@ -185,12 +192,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
boolean chunkRewrite;
|
||||
try {
|
||||
ServerLevel.class.getDeclaredMethod("getEntityLookup");
|
||||
@ -208,29 +209,48 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
try {
|
||||
// Non-Paper
|
||||
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "L"));
|
||||
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "M"));
|
||||
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
POST_CHUNK_REWRITE = chunkRewrite;
|
||||
|
||||
Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod(
|
||||
Refraction.pickName("get", "a"),
|
||||
int.class
|
||||
);
|
||||
palettedContaienrGet.setAccessible(true);
|
||||
PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet);
|
||||
} catch (RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
MethodHandle craftChunkGetHandle;
|
||||
final MethodType type = methodType(LevelChunk.class);
|
||||
try {
|
||||
craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", type);
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
try {
|
||||
final MethodType newType = methodType(ChunkAccess.class, ChunkStatus.class);
|
||||
craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", newType);
|
||||
craftChunkGetHandle = MethodHandles.insertArguments(craftChunkGetHandle, 1, ChunkStatus.FULL);
|
||||
} catch (NoSuchMethodException | IllegalAccessException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
CRAFT_CHUNK_GET_HANDLE = craftChunkGetHandle;
|
||||
}
|
||||
|
||||
static boolean setSectionAtomic(
|
||||
String worldName,
|
||||
IntPair pair,
|
||||
LevelChunkSection[] sections,
|
||||
LevelChunkSection expected,
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer);
|
||||
}
|
||||
|
||||
// There is no point in having a functional semaphore for paper servers.
|
||||
@ -243,19 +263,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject(
|
||||
blocks,
|
||||
fieldThreadingDetectorOffset
|
||||
);
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
|
||||
synchronized (currentThreadingDetector) {
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset);
|
||||
Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock);
|
||||
fieldLock.set(currentThreadingDetector, newLock);
|
||||
return newLock;
|
||||
}
|
||||
}
|
||||
@ -307,7 +323,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
}
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return (LevelChunk) chunk.getHandle(ChunkStatus.FULL);
|
||||
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -332,7 +348,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
||||
public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int chunkZ) {
|
||||
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||
if (chunkHolder == null) {
|
||||
return;
|
||||
@ -353,28 +369,35 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
if (levelChunk == null) {
|
||||
return;
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
ClientboundLevelChunkWithLightPacket packet;
|
||||
if (PaperLib.isPaper()) {
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
false // last false is to not bother with x-ray
|
||||
);
|
||||
} else {
|
||||
// deprecated on paper - deprecation suppressed
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
true
|
||||
);
|
||||
StampLockHolder lockHolder = new StampLockHolder();
|
||||
NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
|
||||
if (lockHolder.chunkLock == null) {
|
||||
return;
|
||||
}
|
||||
MinecraftServer.getServer().execute(() -> {
|
||||
try {
|
||||
ClientboundLevelChunkWithLightPacket packet;
|
||||
if (PaperLib.isPaper()) {
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
false // last false is to not bother with x-ray
|
||||
);
|
||||
} else {
|
||||
// deprecated on paper - deprecation suppressed
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
} finally {
|
||||
NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
|
||||
}
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
});
|
||||
}
|
||||
|
||||
@ -404,7 +427,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer, biomeRegistry, biomes);
|
||||
return newChunkSection(biomeRegistry, biomes);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
|
||||
@ -477,12 +500,11 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(
|
||||
BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
PalettedContainer.Strategy.SECTION_BIOMES
|
||||
);
|
||||
}
|
||||
|
||||
return new LevelChunkSection(layer, blockStatePalettedContainer, biomes);
|
||||
return new LevelChunkSection(blockStatePalettedContainer, biomes);
|
||||
} catch (final Throwable e) {
|
||||
throw e;
|
||||
} finally {
|
||||
@ -495,20 +517,26 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
@SuppressWarnings("deprecation") // Only deprecated in paper
|
||||
private static LevelChunkSection newChunkSection(
|
||||
int layer,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return new LevelChunkSection(layer, biomeRegistry);
|
||||
return new LevelChunkSection(biomeRegistry);
|
||||
}
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
Blocks.AIR.defaultBlockState(),
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
null
|
||||
PalettedContainer.Strategy.SECTION_STATES
|
||||
);
|
||||
return new LevelChunkSection(layer, dataPaletteBlocks, biomes);
|
||||
return new LevelChunkSection(dataPaletteBlocks, biomes);
|
||||
}
|
||||
|
||||
public static void setBiomesToChunkSection(LevelChunkSection section, PalettedContainer<Holder<Biome>> biomes) {
|
||||
try {
|
||||
fieldBiomes.set(section, biomes);
|
||||
} catch (IllegalAccessException e) {
|
||||
LOGGER.error("Could not set biomes to chunk section", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -545,8 +573,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
PalettedContainer<Holder<Biome>> biomePalettedContainer = new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
PalettedContainer.Strategy.SECTION_BIOMES
|
||||
);
|
||||
|
||||
final Palette<Holder<Biome>> biomePalette;
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
@ -0,0 +1,77 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
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 java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
|
||||
|
||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
|
||||
super(serverLevel, queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkPos createChunkPos(final long chunkKey) {
|
||||
return new ChunkPos(chunkKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long asLong(final int chunkX, final int chunkZ) {
|
||||
return ChunkPos.asLong(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
|
||||
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
chunkPos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
));
|
||||
}
|
||||
|
||||
protected void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred on relighting", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
protected void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +1,20 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||
|
||||
@Override
|
||||
public @Nonnull
|
||||
@SuppressWarnings("rawtypes")
|
||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
||||
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||
if (w == null) {
|
||||
return NullRelighter.INSTANCE;
|
@ -1,4 +1,4 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.nbt;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.LazyCompoundTag;
|
||||
@ -6,8 +6,8 @@ import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -36,7 +36,7 @@ public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Tag> getValue() {
|
||||
public Map<String, Tag<?, ?>> getValue() {
|
||||
if (compoundTag == null) {
|
||||
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
|
||||
}
|
||||
@ -44,9 +44,9 @@ public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundBinaryTag asBinaryTag() {
|
||||
public LinCompoundTag toLinTag() {
|
||||
getValue();
|
||||
return compoundTag.asBinaryTag();
|
||||
return compoundTag.toLinTag();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
@ -94,10 +94,10 @@ public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> getList(String key) {
|
||||
public List<? extends Tag<?, ?>> getList(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
|
||||
ArrayList<Tag> list = new ArrayList<>();
|
||||
ArrayList<Tag<?, ?>> list = new ArrayList<>();
|
||||
for (net.minecraft.nbt.Tag elem : nbtList) {
|
||||
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
list.add(new PaperweightLazyCompoundTag(compoundTag));
|
||||
@ -120,7 +120,7 @@ public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
|
||||
public <T extends Tag<?, ?>> List<T> getList(String key, Class<T> listType) {
|
||||
ListTag listTag = getListTag(key);
|
||||
if (listTag.getType().equals(listType)) {
|
||||
return (List<T>) listTag.getValue();
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren
Wir brauchen unbedingt weiterhin: 1.18.2, 1.19.3, 1.20.1 Support!
Geht net, es gibt keinen Support für diese Versionen mehr, und finde es viel zu viel aufwand, diesen noch künstlich am Leben zu erhalten.
Habe deshalb von anfang an schon eher als >1.21 FaWe geplant, also wäre dieser Support auch nicht nötig, da wir noch das alte hätten.
So viel Aufwand war das bei meinen bisherigen Portierungen nicht (1.18.2, 1.19.3 Support gabs da auch schon lange nicht mehr). Problem ist dann eher der SchematicReader (siehe SpigotCore)