geforkt von Mirrors/FastAsyncWorldEdit
Commits vergleichen
2 Commits
main
...
feature/pa
Autor | SHA1 | Datum | |
---|---|---|---|
|
701237bdbd | ||
|
86ff1bb9d7 |
7
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
7
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -8,7 +8,7 @@ body:
|
|||||||
value: |
|
value: |
|
||||||
Thanks for taking the time to fill out this bug report for FastAsyncWorldEdit! Fill out the following form to your best ability to help us fix the problem.
|
Thanks for taking the time to fill out this bug report for FastAsyncWorldEdit! Fill out the following form to your best ability to help us fix the problem.
|
||||||
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/intellectualsites) or [the wiki](https://intellectualsites.github.io/fastasyncworldedit-documentation/).
|
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/intellectualsites) or [the wiki](https://intellectualsites.github.io/fastasyncworldedit-documentation/).
|
||||||
Do NOT use the public issue tracker to report security vulnerabilities! They are disclosed using [this](https://github.com/IntellectualSites/FastAsyncWorldEdit/security/policy) GitHub form!
|
Do NOT use the public issue tracker to report security vulnerabilities! They are disclosed using [this](https://forms.gle/btgdRn9yhGtzEiGW8) form!
|
||||||
|
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
@ -27,11 +27,10 @@ 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.
|
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
|
multiple: false
|
||||||
options:
|
options:
|
||||||
- '1.20.1'
|
- '1.19'
|
||||||
- '1.20'
|
|
||||||
- '1.19.4'
|
|
||||||
- '1.18.2'
|
- '1.18.2'
|
||||||
- '1.17.1'
|
- '1.17.1'
|
||||||
|
- '1.16.5'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
35
.github/renovate.json
vendored
35
.github/renovate.json
vendored
@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": [
|
|
||||||
"config:base",
|
|
||||||
":semanticCommitsDisabled"
|
|
||||||
],
|
|
||||||
"automerge": true,
|
|
||||||
"ignoreDeps": [
|
|
||||||
"guava",
|
|
||||||
"com.google.guava:guava",
|
|
||||||
"rhino-runtime",
|
|
||||||
"org.antlr",
|
|
||||||
"antlr4-runtime",
|
|
||||||
"fastutil",
|
|
||||||
"it.unimi.dsi:fastutil",
|
|
||||||
"auto-value-annotations",
|
|
||||||
"auto-value",
|
|
||||||
"com.google.code.gson:gson",
|
|
||||||
"net.fabricmc:fabric-loader",
|
|
||||||
"net.fabricmc.fabric-api:fabric-api",
|
|
||||||
"com.github.luben:zstd-jni",
|
|
||||||
"org.jetbrains.kotlin.jvm",
|
|
||||||
"log4j",
|
|
||||||
"org.apache.logging.log4j:log4j-api",
|
|
||||||
"org.apache.logging.log4j:log4j-bom",
|
|
||||||
"org.apache.logging.log4j:log4j-slf4j-impl",
|
|
||||||
"org.apache.logging.log4j:log4j-core",
|
|
||||||
"org.bstats:bstats-sponge",
|
|
||||||
"org.spongepowered:spongeapi",
|
|
||||||
"org.yaml:snakeyaml"
|
|
||||||
],
|
|
||||||
"labels": ["Renovate"],
|
|
||||||
"rebaseWhen": "conflicted",
|
|
||||||
"schedule": ["on the first day of the month"]
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
name: Announce release on discord
|
|
||||||
#on:
|
|
||||||
# workflow_run:
|
|
||||||
# workflows: ["Upload release assets"]
|
|
||||||
# types:
|
|
||||||
# - completed
|
|
||||||
jobs:
|
|
||||||
send_announcement:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: send custom message with args
|
|
||||||
env:
|
|
||||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
|
||||||
DISCORD_USERNAME: FastAsyncWorldEdit Release
|
|
||||||
DISCORD_AVATAR: https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.png
|
|
||||||
uses: Ilshidur/action-discord@0.3.2
|
|
||||||
with:
|
|
||||||
args: |
|
|
||||||
"<@&525015715300900875> <@&706463154804097105> <@&671372968462516240>"
|
|
||||||
""
|
|
||||||
"<:fawe:730750370984493106> **FastAsyncWorldEdit ${{ github.event.release.tag_name }} has been released!**"
|
|
||||||
""
|
|
||||||
"Click here to view changelog: https://github.com/IntellectualSites/FastAsyncWorldEdit/releases/tag/${{ github.event.release.tag_name }}"
|
|
||||||
""
|
|
||||||
"The download is available at:"
|
|
||||||
"- Spigot: <https://www.spigotmc.org/resources/13932/>"
|
|
||||||
"- Modrinth: <https://modrinth.com/plugin/fastasyncworldedit/version/${{ github.event.release.tag_name }}>"
|
|
||||||
"- CurseForge: <https://www.curseforge.com/minecraft/bukkit-plugins/fawe>"
|
|
27
.github/workflows/build-pr.yml
vendored
27
.github/workflows/build-pr.yml
vendored
@ -1,27 +0,0 @@
|
|||||||
name: Build PR
|
|
||||||
on: [pull_request]
|
|
||||||
jobs:
|
|
||||||
build_pr:
|
|
||||||
if: github.repository_owner == 'IntellectualSites'
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Validate Gradle Wrapper
|
|
||||||
uses: gradle/wrapper-validation-action@v1
|
|
||||||
- name: Setup Java
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
distribution: temurin
|
|
||||||
cache: gradle
|
|
||||||
java-version: 17
|
|
||||||
- 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
|
|
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@ -1,17 +1,16 @@
|
|||||||
name: Build
|
name: build
|
||||||
on:
|
|
||||||
push:
|
on: [pull_request, push]
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.repository_owner == 'IntellectualSites'
|
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Validate Gradle Wrapper
|
- name : Validate Gradle Wrapper
|
||||||
uses: gradle/wrapper-validation-action@v1
|
uses : gradle/wrapper-validation-action@v1
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
@ -71,8 +70,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: FastAsyncWorldEdit-Bukkit-SNAPSHOT
|
name: FastAsyncWorldEdit-Bukkit-SNAPSHOT
|
||||||
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar
|
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar
|
||||||
- name: Publish to Modrinth
|
|
||||||
if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
|
|
||||||
run: ./gradlew modrinth
|
|
||||||
env:
|
|
||||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
|
||||||
|
36
.github/workflows/codeql.yml
vendored
36
.github/workflows/codeql.yml
vendored
@ -1,36 +0,0 @@
|
|||||||
name: "CodeQL"
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [main]
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: ['java']
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Java
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
distribution: temurin
|
|
||||||
cache: gradle
|
|
||||||
java-version: 17
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v2
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v2
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
6
.github/workflows/release-drafter.yml
vendored
6
.github/workflows/release-drafter.yml
vendored
@ -1,12 +1,14 @@
|
|||||||
name: draft release
|
name: draft release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, reopened, synchronize]
|
types: [ opened, reopened, synchronize ]
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, reopened, synchronize]
|
types: [ opened, reopened, synchronize ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update_release_draft:
|
update_release_draft:
|
||||||
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
||||||
|
6
.github/workflows/upload-release-assets.yml
vendored
6
.github/workflows/upload-release-assets.yml
vendored
@ -1,15 +1,17 @@
|
|||||||
name: Upload release assets
|
name: Upload release assets
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upload_asset:
|
upload_asset:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Validate Gradle Wrapper
|
- name : Validate Gradle Wrapper
|
||||||
uses: gradle/wrapper-validation-action@v1
|
uses : gradle/wrapper-validation-action@v1
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -24,6 +24,7 @@ logs/
|
|||||||
worldedit-bukkit/src/main/java/ignore/*
|
worldedit-bukkit/src/main/java/ignore/*
|
||||||
todo.txt
|
todo.txt
|
||||||
mvn/*
|
mvn/*
|
||||||
|
docs/
|
||||||
*.sh
|
*.sh
|
||||||
# i18n
|
# i18n
|
||||||
worldedit-core/src/main/resources/lang/*
|
worldedit-core/src/main/resources/lang/*
|
||||||
@ -33,5 +34,3 @@ worldedit-core/src/main/resources/lang/*
|
|||||||
/worldedit-core/.factorypath
|
/worldedit-core/.factorypath
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
### Run server ignore
|
|
||||||
run-*
|
|
||||||
|
140
.idea/icon.svg
140
.idea/icon.svg
@ -1,112 +1,44 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" id="svg2" xml:space="preserve" width="4000" height="3333.3333" viewBox="0 0 4000 3333.3333" sodipodi:docname="FastAsyncWorldEdit.ai">
|
||||||
version="1.1"
|
<metadata id="metadata8">
|
||||||
id="svg2"
|
|
||||||
xml:space="preserve"
|
|
||||||
width="512"
|
|
||||||
height="512"
|
|
||||||
viewBox="0 0 512 512.00001"
|
|
||||||
sodipodi:docname="icon.svg"
|
|
||||||
inkscape:version="1.1.2 (b8e25be8, 2022-02-05)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
||||||
<metadata
|
|
||||||
id="metadata8">
|
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
<cc:Work
|
<cc:Work rdf:about="">
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<defs
|
<defs id="defs6">
|
||||||
id="defs6">
|
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath18">
|
||||||
<clipPath
|
<path d="M 0,2500 H 3000 V 0 H 0 Z" id="path16" />
|
||||||
clipPathUnits="userSpaceOnUse"
|
|
||||||
id="clipPath18">
|
|
||||||
<path
|
|
||||||
d="M 0,2500 H 3000 V 0 H 0 Z"
|
|
||||||
id="path16" />
|
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath
|
</defs>
|
||||||
clipPathUnits="userSpaceOnUse"
|
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview4" />
|
||||||
id="clipPath18-2"><path
|
<g id="g10" inkscape:groupmode="layer" inkscape:label="FastAsyncWorldEdit" transform="matrix(1.3333333,0,0,-1.3333333,0,3333.3333)">
|
||||||
d="M 0,2500 H 3000 V 0 H 0 Z"
|
<g id="g12">
|
||||||
id="path16-1" /></clipPath></defs>
|
<g id="g14" clip-path="url(#clipPath18)">
|
||||||
<sodipodi:namedview
|
<g id="g20" transform="translate(1643.0798,2025.9724)">
|
||||||
pagecolor="#ffffff"
|
<path d="m 0,0 c 34.839,11.026 70.869,18.313 107.28,21.408 18.861,-48.96 46.604,-93.992 77.514,-136.19 49.794,-66.487 110.495,-124.616 178.34,-172.506 78.156,-55.366 165.481,-97.254 256.496,-126.783 83.229,-27.124 169.601,-44.008 256.592,-53.295 -1.047,-153.146 -64.344,-303.076 -167.124,-415.762 -47.77,-52.914 -103.208,-99.136 -164.838,-135.047 -91.873,-53.843 -197.557,-83.896 -304.076,-85.705 -10.764,101.16 -31.91,201.534 -67.273,297.051 -28.243,76.537 -65.678,149.835 -112.853,216.465 -47.865,67.583 -105.685,128.332 -172.125,177.888 -42.102,31.22 -87.348,58.939 -136.404,77.823 3.834,48.628 15.479,96.636 33.625,141.906 19.169,46.769 46.96,90.968 85.752,123.926 C -91.706,-36.816 -46.722,-14.717 0,0" style="fill:#4c8fcc;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path22" />
|
||||||
bordercolor="#666666"
|
</g>
|
||||||
borderopacity="1"
|
<g id="g24" transform="translate(1887.3362,1969.7008)">
|
||||||
objecttolerance="10"
|
<path d="m 0,0 c 20.837,19.313 56.319,19.718 77.228,0.333 32.243,-32.053 64.272,-64.296 96.492,-96.373 17.313,-17.384 20.766,-46.27 8.93,-67.631 -9.073,-15.431 -23.813,-26.314 -35.744,-39.388 -37.339,25.29 -72.917,53.105 -106.637,83.015 -17.122,17.074 -36.316,32.076 -51.771,50.794 -8.525,10.74 -20.432,18.813 -26.457,31.458 C -25.242,-25.242 -12.788,-12.431 0,0" style="fill:#062f4c;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path26" />
|
||||||
gridtolerance="10"
|
</g>
|
||||||
guidetolerance="10"
|
<g id="g28" transform="translate(1463.3822,1497.8577)">
|
||||||
inkscape:pageopacity="0"
|
<path d="m 0,0 c -4.644,-0.786 -9.097,-2.786 -12.383,-6.168 -292.384,-292.645 -585.053,-585.029 -877.484,-877.627 -6.74,-6.382 -14.741,-13.145 -14.717,-23.337 -1.262,-12.383 8.668,-21.242 17.384,-28.291 9.882,-6.882 24.552,-4.215 32.481,4.525 293.718,293.693 587.411,587.387 881.104,881.104 10.74,9.049 12.241,27.219 1.929,37.101 C 21.48,-4.406 11.264,2.715 0,0 M -994.266,-946.234 C -647.088,-599.055 -299.885,-251.901 47.27,95.326 84.134,65.44 116.592,30.505 147.502,-5.382 c 23.266,-26.814 43.579,-55.986 64.749,-84.419 -346.679,-346.584 -693.215,-693.286 -1039.941,-1039.822 -20.623,-21.98 -58.439,-23.599 -80.514,-2.953 -32.172,32.149 -64.321,64.345 -96.493,96.493 -16.503,16.741 -19.932,44.007 -9.882,64.963 4.572,9.907 12.692,17.384 20.313,24.886" style="fill:#062f4c;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path30" />
|
||||||
inkscape:pageshadow="2"
|
</g>
|
||||||
inkscape:window-width="1440"
|
<g id="g32" transform="translate(1827.8735,1911.1907)">
|
||||||
inkscape:window-height="872"
|
<path d="M 0,0 C 7.859,6.168 14.288,13.883 21.504,20.718 27.529,8.073 39.435,0 47.961,-10.74 c 15.455,-18.717 34.648,-33.72 51.77,-50.794 33.721,-29.91 69.298,-57.725 106.638,-83.015 -9.383,-9.263 -18.67,-18.67 -28.029,-27.957 C 110.495,-124.617 49.794,-66.488 0,0" style="fill:#081d2d;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path34" />
|
||||||
id="namedview4"
|
</g>
|
||||||
inkscape:pagecheckerboard="0"
|
<g id="g36" transform="translate(1531.0129,1613.4966)">
|
||||||
showgrid="false"
|
<path d="m 0,0 c 66.44,-49.556 124.26,-110.305 172.125,-177.888 -9.145,-9.192 -18.36,-18.336 -27.505,-27.552 -21.17,28.433 -41.483,57.605 -64.749,84.419 C 48.961,-85.134 16.503,-50.199 -20.361,-20.313 -13.574,-13.55 -6.811,-6.763 0,0" style="fill:#081d2d;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path38" />
|
||||||
width="512px"
|
</g>
|
||||||
inkscape:snap-others="false"
|
<g id="g40" transform="translate(2519.302,1558.6062)">
|
||||||
inkscape:zoom="0.2263301"
|
<path d="M 0,0 C 21.408,-2.334 42.865,-3.763 64.321,-5.596 73.179,-84.086 63.178,-164.123 40.126,-239.47 3.072,-360.657 -72.393,-468.319 -168.696,-549.976 c -101.446,-87.038 -230.349,-142.429 -363.943,-152.121 -32.72,-2.548 -65.726,-2.453 -98.279,1.786 -0.857,21.313 -3.072,42.555 -5.12,63.797 106.518,1.81 212.203,31.862 304.076,85.705 61.63,35.911 117.068,82.133 164.838,135.047 C -64.344,-303.076 -1.048,-153.145 0,0" style="fill:#6fc4ee;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path42" />
|
||||||
inkscape:cx="797.50773"
|
</g>
|
||||||
inkscape:cy="547.87234"
|
<g id="g44" transform="translate(1463.3822,1497.8577)">
|
||||||
inkscape:window-x="0"
|
<path d="m 0,0 c 11.264,2.715 21.48,-4.406 28.314,-12.693 10.312,-9.882 8.811,-28.052 -1.929,-37.101 -293.693,-293.717 -587.386,-587.411 -881.104,-881.104 -7.929,-8.74 -22.599,-11.407 -32.481,-4.525 -8.716,7.049 -18.646,15.908 -17.384,28.291 -0.024,10.192 7.977,16.955 14.717,23.337 292.431,292.598 585.1,584.982 877.484,877.627 C -9.097,-2.786 -4.644,-0.786 0,0" style="fill:#105677;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path46" />
|
||||||
inkscape:window-y="28"
|
</g>
|
||||||
inkscape:window-maximized="0"
|
</g>
|
||||||
inkscape:current-layer="g10" />
|
</g>
|
||||||
<g
|
</g>
|
||||||
id="g10"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
inkscape:label="FastAsyncWorldEdit"
|
|
||||||
transform="matrix(1.3333333,0,0,-1.3333333,0,3333.3333)">
|
|
||||||
|
|
||||||
<g
|
|
||||||
id="g12"
|
|
||||||
transform="matrix(0.17875858,0,0,0.17875858,-78.817913,2093.6589)"><g
|
|
||||||
id="g14"
|
|
||||||
clip-path="url(#clipPath18-2)"><g
|
|
||||||
id="g20"
|
|
||||||
transform="translate(1643.0798,2025.9724)"><path
|
|
||||||
d="m 0,0 c 34.839,11.026 70.869,18.313 107.28,21.408 18.861,-48.96 46.604,-93.992 77.514,-136.19 49.794,-66.487 110.495,-124.616 178.34,-172.506 78.156,-55.366 165.481,-97.254 256.496,-126.783 83.229,-27.124 169.601,-44.008 256.592,-53.295 -1.047,-153.146 -64.344,-303.076 -167.124,-415.762 -47.77,-52.914 -103.208,-99.136 -164.838,-135.047 -91.873,-53.843 -197.557,-83.896 -304.076,-85.705 -10.764,101.16 -31.91,201.534 -67.273,297.051 -28.243,76.537 -65.678,149.835 -112.853,216.465 -47.865,67.583 -105.685,128.332 -172.125,177.888 -42.102,31.22 -87.348,58.939 -136.404,77.823 3.834,48.628 15.479,96.636 33.625,141.906 19.169,46.769 46.96,90.968 85.752,123.926 C -91.706,-36.816 -46.722,-14.717 0,0"
|
|
||||||
style="fill:#4c8fcc;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="path22" /></g><g
|
|
||||||
id="g24"
|
|
||||||
transform="translate(1887.3362,1969.7008)"><path
|
|
||||||
d="m 0,0 c 20.837,19.313 56.319,19.718 77.228,0.333 32.243,-32.053 64.272,-64.296 96.492,-96.373 17.313,-17.384 20.766,-46.27 8.93,-67.631 -9.073,-15.431 -23.813,-26.314 -35.744,-39.388 -37.339,25.29 -72.917,53.105 -106.637,83.015 -17.122,17.074 -36.316,32.076 -51.771,50.794 -8.525,10.74 -20.432,18.813 -26.457,31.458 C -25.242,-25.242 -12.788,-12.431 0,0"
|
|
||||||
style="fill:#062f4c;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="path26" /></g><g
|
|
||||||
id="g28"
|
|
||||||
transform="translate(1463.3822,1497.8577)"><path
|
|
||||||
d="m 0,0 c -4.644,-0.786 -9.097,-2.786 -12.383,-6.168 -292.384,-292.645 -585.053,-585.029 -877.484,-877.627 -6.74,-6.382 -14.741,-13.145 -14.717,-23.337 -1.262,-12.383 8.668,-21.242 17.384,-28.291 9.882,-6.882 24.552,-4.215 32.481,4.525 293.718,293.693 587.411,587.387 881.104,881.104 10.74,9.049 12.241,27.219 1.929,37.101 C 21.48,-4.406 11.264,2.715 0,0 M -994.266,-946.234 C -647.088,-599.055 -299.885,-251.901 47.27,95.326 84.134,65.44 116.592,30.505 147.502,-5.382 c 23.266,-26.814 43.579,-55.986 64.749,-84.419 -346.679,-346.584 -693.215,-693.286 -1039.941,-1039.822 -20.623,-21.98 -58.439,-23.599 -80.514,-2.953 -32.172,32.149 -64.321,64.345 -96.493,96.493 -16.503,16.741 -19.932,44.007 -9.882,64.963 4.572,9.907 12.692,17.384 20.313,24.886"
|
|
||||||
style="fill:#062f4c;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="path30" /></g><g
|
|
||||||
id="g32"
|
|
||||||
transform="translate(1827.8735,1911.1907)"><path
|
|
||||||
d="M 0,0 C 7.859,6.168 14.288,13.883 21.504,20.718 27.529,8.073 39.435,0 47.961,-10.74 c 15.455,-18.717 34.648,-33.72 51.77,-50.794 33.721,-29.91 69.298,-57.725 106.638,-83.015 -9.383,-9.263 -18.67,-18.67 -28.029,-27.957 C 110.495,-124.617 49.794,-66.488 0,0"
|
|
||||||
style="fill:#081d2d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="path34" /></g><g
|
|
||||||
id="g36"
|
|
||||||
transform="translate(1531.0129,1613.4966)"><path
|
|
||||||
d="m 0,0 c 66.44,-49.556 124.26,-110.305 172.125,-177.888 -9.145,-9.192 -18.36,-18.336 -27.505,-27.552 -21.17,28.433 -41.483,57.605 -64.749,84.419 C 48.961,-85.134 16.503,-50.199 -20.361,-20.313 -13.574,-13.55 -6.811,-6.763 0,0"
|
|
||||||
style="fill:#081d2d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="path38" /></g><g
|
|
||||||
id="g40"
|
|
||||||
transform="translate(2519.302,1558.6062)"><path
|
|
||||||
d="M 0,0 C 21.408,-2.334 42.865,-3.763 64.321,-5.596 73.179,-84.086 63.178,-164.123 40.126,-239.47 3.072,-360.657 -72.393,-468.319 -168.696,-549.976 c -101.446,-87.038 -230.349,-142.429 -363.943,-152.121 -32.72,-2.548 -65.726,-2.453 -98.279,1.786 -0.857,21.313 -3.072,42.555 -5.12,63.797 106.518,1.81 212.203,31.862 304.076,85.705 61.63,35.911 117.068,82.133 164.838,135.047 C -64.344,-303.076 -1.048,-153.145 0,0"
|
|
||||||
style="fill:#6fc4ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="path42" /></g><g
|
|
||||||
id="g44"
|
|
||||||
transform="translate(1463.3822,1497.8577)"><path
|
|
||||||
d="m 0,0 c 11.264,2.715 21.48,-4.406 28.314,-12.693 10.312,-9.882 8.811,-28.052 -1.929,-37.101 -293.693,-293.717 -587.386,-587.411 -881.104,-881.104 -7.929,-8.74 -22.599,-11.407 -32.481,-4.525 -8.716,7.049 -18.646,15.908 -17.384,28.291 -0.024,10.192 7.977,16.955 14.717,23.337 292.431,292.598 585.1,584.982 877.484,877.627 C -9.097,-2.786 -4.644,-0.786 0,0"
|
|
||||||
style="fill:#105677;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
|
||||||
id="path46" /></g></g></g></g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Vorher Breite: | Höhe: | Größe: 6.3 KiB Nachher Breite: | Höhe: | Größe: 5.6 KiB |
36
Jenkinsfile
vendored
36
Jenkinsfile
vendored
@ -1,36 +0,0 @@
|
|||||||
pipeline {
|
|
||||||
agent any
|
|
||||||
options {
|
|
||||||
disableConcurrentBuilds()
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage('Build') {
|
|
||||||
steps {
|
|
||||||
withEnv([
|
|
||||||
"PATH+JAVA=${tool 'Temurin-17.0.7_7'}/bin"
|
|
||||||
]) {
|
|
||||||
sh './gradlew clean build'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Archive artifacts') {
|
|
||||||
steps {
|
|
||||||
sh 'rm -rf artifacts'
|
|
||||||
sh 'mkdir artifacts'
|
|
||||||
sh 'cp worldedit-bukkit/build/libs/FastAsyncWorldEdit*.jar artifacts/'
|
|
||||||
sh 'cp worldedit-cli/build/libs/FastAsyncWorldEdit*.jar artifacts/'
|
|
||||||
archiveArtifacts artifacts: 'artifacts/*.jar', followSymlinks: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Fingerprint artifacts') {
|
|
||||||
steps {
|
|
||||||
fingerprint 'worldedit-bukkit/build/libs/FastAsyncWorldEdit*.jar'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Publish JUnit test results') {
|
|
||||||
steps {
|
|
||||||
junit 'worldedit-core/build/test-results/test/*.xml,worldedit-bukkit/build/test-results/test/*.xml'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
47
README.adoc
Normale Datei
47
README.adoc
Normale Datei
@ -0,0 +1,47 @@
|
|||||||
|
image::https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.svg[200,200,align=center]
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
FastAsyncWorldEdit is a fork of WorldEdit that has huge speed and memory improvements and considerably more features
|
||||||
|
|
||||||
|
== A Minecraft Map Editor... that runs in-game!
|
||||||
|
|
||||||
|
* With selections, schematics, copy and paste, brushes, and scripting!
|
||||||
|
* Use it in creative, survival in single player or on your server.
|
||||||
|
* Use it on your Minecraft server to fix grieving and mistakes.
|
||||||
|
|
||||||
|
Java Edition required. FastAsyncWorldEdit is compatible with Bukkit, Spigot and Paper.
|
||||||
|
|
||||||
|
=== Download FastAsyncWorldEdit
|
||||||
|
* Snapshot builds: https://ci.athion.net/job/FastAsyncWorldEdit/
|
||||||
|
* Spigot: https://www.spigotmc.org/resources/fast-async-worldedit.13932/
|
||||||
|
* Older, unsupported, versions: https://intellectualsites.github.io/download/fawe.html
|
||||||
|
|
||||||
|
=== Links
|
||||||
|
|
||||||
|
* link:https://discord.gg/intellectualsites[Discord]
|
||||||
|
* link:https://intellectualsites.github.io/fastasyncworldedit-documentation/[Wiki]
|
||||||
|
* link:https://github.com/IntellectualSites/FastAsyncWorldEdit/issues[Report Issue]
|
||||||
|
* link:https://intellectualsites.github.io/fastasyncworldedit-javadocs/[Javadocs]
|
||||||
|
* link:https://intellectualsites.crowdin.com/fastasyncworldedit[Crowdin (Translations)]
|
||||||
|
|
||||||
|
=== Edit The Code
|
||||||
|
|
||||||
|
Want to add new features to FastAsyncWorldEdit or fix bugs yourself? You can get the game running, with FastAsyncWorldEdit, from the code here:
|
||||||
|
|
||||||
|
For additional information about compiling FastAsyncWorldEdit, see link:COMPILING.adoc[COMPILING.adoc].
|
||||||
|
|
||||||
|
=== Submitting Your Changes
|
||||||
|
FastAsyncWorldEdit is open source (specifically licensed under GPL v3), so note that your contributions will also be open source. The best way to submit a change is to create a fork on GitHub, put your changes there, and then create a "pull request" on our FastAsyncWorldEdit repository.
|
||||||
|
|
||||||
|
Please read link:https://github.com/IntellectualSites/.github/blob/main/CONTRIBUTING.md[CONTRIBUTING.md] for important guidelines to follow.
|
||||||
|
|
||||||
|
=== Special thanks to:
|
||||||
|
image::https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg[150,150]
|
||||||
|
link:https://jb.gg/OpenSourceSupport[JetBrains], creators of the IntelliJ IDEA, supports us with their Open Source Licenses.
|
||||||
|
|
||||||
|
image::https://www.yourkit.com/images/yklogo.png[200,200,align=left]
|
||||||
|
|
||||||
|
Thank you to YourKit for supporting our product by providing us with their innovative and intelligent tools
|
||||||
|
for monitoring and profiling Java and .NET applications.
|
||||||
|
YourKit is the creator of link:https://www.yourkit.com/java/profiler/[YourKit Java Profiler], link:https://www.yourkit.com/.net/profiler/[YourKit .NET Profiler], and link:https://www.yourkit.com/youmonitor/[YourKit YouMonitor].
|
95
README.md
95
README.md
@ -1,95 +0,0 @@
|
|||||||
# FastAsyncWorldEdit
|
|
||||||
[![Join us on Discord](https://img.shields.io/discord/268444645527126017.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/intellectualsites)
|
|
||||||
[![bStats Servers](https://img.shields.io/bstats/servers/1403)](https://bstats.org/plugin/bukkit/FastAsyncWorldEdit/1403)
|
|
||||||
[![Crowdin](https://badges.crowdin.net/e/4a5819fae3fd88234a8ea13bfbb072bb/localized.svg)](https://intellectualsites.crowdin.com/fastasyncworldedit)
|
|
||||||
|
|
||||||
## What is FAWE and why should I use it?
|
|
||||||
|
|
||||||
FAWE is designed for efficient world editing.
|
|
||||||
* Simple to set up and use
|
|
||||||
* Extremely configurable
|
|
||||||
* Uses minimal CPU/Memory
|
|
||||||
* Safe for many players to use
|
|
||||||
* Insanely fast, when using the slowest mode
|
|
||||||
|
|
||||||
FastAsyncWorldEdit is a fork of WorldEdit that has huge speed and memory improvements and considerably more features.
|
|
||||||
If you use other plugins which depend on WorldEdit, simply having FAWE installed will boost their performance.
|
|
||||||
|
|
||||||
## Downloads
|
|
||||||
|
|
||||||
Downloads are available either on SpigotMC, Modrinth or on CurseForge.
|
|
||||||
- [SpigotMC](https://www.spigotmc.org/resources/13932/)
|
|
||||||
- [Modrinth](https://modrinth.com/plugin/fastasyncworldedit/)
|
|
||||||
- [CurseForge](https://dev.bukkit.org/projects/fawe)
|
|
||||||
|
|
||||||
Snapshots are available on [Jenkins](https://ci.athion.net/job/FastAsyncWorldEdit/).
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* Over 200 Commands
|
|
||||||
* Style and translate messages and commands
|
|
||||||
* (No setup required) Clipboard web integration (Clipboard)
|
|
||||||
* Unlimited //undo, per world history, instant lookups/rollback and cross server clipboards
|
|
||||||
* Advanced per player limits (entity, tiles, memory, changes, iterations, regions, inventory)
|
|
||||||
* Visualization, targeting modes/masks and scroll actions
|
|
||||||
* Adds lots of powerful new //brushes and //tools.
|
|
||||||
* Adds a lot more mask functionality. (new mask syntax, patterns, expressions, source masks)
|
|
||||||
* Adds a lot more pattern functionality. (a lot of new pattern syntax and patterns)
|
|
||||||
* Adds edit transforms (apply transforms to a source, e.g. on //paste)
|
|
||||||
* Adds support for new formats (e.g. Structure Blocks)
|
|
||||||
* Instant copying of arbitrary size with `//lazycopy`
|
|
||||||
* Auto repair partially corrupt schematic files
|
|
||||||
* Biome mixing, in-game world painting, dynamic view distance, vanilla cui, off axis rotation, image importing, cave generation,
|
|
||||||
multi-clipboards, interactive messages, schematic visualization, lag prevention, persistent brushes + A LOT MORE
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
|
|
||||||
There are several placement modes, each supporting higher throughput than the previous. All editing is processed
|
|
||||||
asynchronously, with
|
|
||||||
certain tasks being broken up on the main thread. The default mode is chunk placement.
|
|
||||||
* Blocks (Bukkit-API) - Only used if chunk placement isn't supported. Still faster than any other plugin on spigot.
|
|
||||||
* Chunks (NMS) - Places entire chunk sections
|
|
||||||
* World (CFI) - Used to generate new worlds / regions
|
|
||||||
|
|
||||||
### Protection Plugins
|
|
||||||
|
|
||||||
The following plugins are supported with Bukkit:
|
|
||||||
* [WorldGuard](https://dev.bukkit.org/projects/worldguard)
|
|
||||||
* [PlotSquared](https://www.spigotmc.org/resources/77506/)
|
|
||||||
|
|
||||||
### Logging and Rollback
|
|
||||||
|
|
||||||
By default you can use `//inspect` and `//history rollback` to search and restore changes. To reduce disk usage, increase the
|
|
||||||
compression level and buffer size. To bypass logging use `//fast`.
|
|
||||||
|
|
||||||
### Developer API
|
|
||||||
|
|
||||||
FAWE maintains API compatibility with WorldEdit, so you can use the normal WorldEdit API asynchronously.
|
|
||||||
FAWE also has some asynchronously wrappers for the Bukkit API.
|
|
||||||
The wiki has examples for various things like reading NBT, modifying world files, pasting schematics, splitting up tasks, lighting etc.
|
|
||||||
If you need help with anything, hop on discord (link on the left bar).
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
* [Wiki](https://intellectualsites.github.io/fastasyncworldedit-documentation/)
|
|
||||||
* [Javadocs](https://intellectualsites.github.io/fastasyncworldedit-javadocs/)
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Want to add new features to FastAsyncWorldEdit or fix bugs yourself? You can get the game running, with FastAsyncWorldEdit, from the code here:
|
|
||||||
|
|
||||||
For additional information about compiling FastAsyncWorldEdit, read the [compiling documentation](https://github.com/IntellectualSites/FastAsyncWorldEdit/blob/main/COMPILING.adoc).
|
|
||||||
|
|
||||||
## Special thanks
|
|
||||||
|
|
||||||
<a href="https://jb.gg/OpenSourceSupport"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg" width="150">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
[JetBrains](https://jb.gg/OpenSourceSupport), creators of the IntelliJ IDEA, supports us with their Open Source Licenses.
|
|
||||||
|
|
||||||
<a href="https://yourkit.com/"><img src="https://www.yourkit.com/images/yklogo.png" width="200">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
Thank you to YourKit for supporting our product by providing us with their innovative and intelligent tools
|
|
||||||
for monitoring and profiling Java and .NET applications.
|
|
||||||
YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/), and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/).
|
|
@ -1,23 +1,12 @@
|
|||||||
import org.ajoberstar.grgit.Grgit
|
import org.ajoberstar.grgit.Grgit
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
|
import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
import xyz.jpenilla.runpaper.task.RunServer
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
|
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
|
||||||
id("xyz.jpenilla.run-paper") version "2.1.0"
|
id("xyz.jpenilla.run-paper") version "1.0.6"
|
||||||
}
|
|
||||||
|
|
||||||
if (!File("$rootDir/.git").exists()) {
|
|
||||||
logger.lifecycle("""
|
|
||||||
**************************************************************************************
|
|
||||||
You need to fork and clone this repository! Don't download a .zip file.
|
|
||||||
If you need assistance, consult the GitHub docs: https://docs.github.com/get-started/quickstart/fork-a-repo
|
|
||||||
**************************************************************************************
|
|
||||||
""".trimIndent()
|
|
||||||
).also { kotlin.system.exitProcess(1) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.lifecycle("""
|
logger.lifecycle("""
|
||||||
@ -34,7 +23,7 @@ logger.lifecycle("""
|
|||||||
*******************************************
|
*******************************************
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var rootVersion by extra("2.7.1")
|
var rootVersion by extra("2.4.3")
|
||||||
var snapshot by extra("SNAPSHOT")
|
var snapshot by extra("SNAPSHOT")
|
||||||
var revision: String by extra("")
|
var revision: String by extra("")
|
||||||
var buildNumber by extra("")
|
var buildNumber by extra("")
|
||||||
@ -83,29 +72,17 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyCommonConfiguration()
|
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")
|
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
supportedVersions.forEach {
|
|
||||||
register<RunServer>("runServer-$it") {
|
|
||||||
minecraftVersion(it)
|
|
||||||
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
|
|
||||||
.toTypedArray())
|
|
||||||
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true")
|
|
||||||
group = "run paper"
|
|
||||||
runDirectory.set(file("run-$it"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runServer {
|
runServer {
|
||||||
minecraftVersion("1.20.1")
|
minecraftVersion("1.19")
|
||||||
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
|
pluginJars(project(":worldedit-bukkit").file("build/libs/FastAsyncWorldEdit-Bukkit-$version.jar"))
|
||||||
.toTypedArray())
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nexusPublishing {
|
nexusPublishing {
|
||||||
this.repositories {
|
repositories {
|
||||||
sonatype {
|
sonatype {
|
||||||
nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/"))
|
nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/"))
|
||||||
snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
|
snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
|
||||||
|
@ -12,6 +12,13 @@ repositories {
|
|||||||
name = "EngineHub"
|
name = "EngineHub"
|
||||||
url = uri("https://maven.enginehub.org/repo/")
|
url = uri("https://maven.enginehub.org/repo/")
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
name = "PaperMC"
|
||||||
|
url = uri("https://repo.papermc.io/repository/maven-public/")
|
||||||
|
content {
|
||||||
|
includeGroupByRegex("io\\.papermc\\..*")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val properties = Properties().also { props ->
|
val properties = Properties().also { props ->
|
||||||
@ -22,9 +29,9 @@ val properties = Properties().also { props ->
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(gradleApi())
|
implementation(gradleApi())
|
||||||
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0")
|
implementation("org.ajoberstar.grgit:grgit-gradle:4.1.1")
|
||||||
implementation("com.github.johnrengelman:shadow:8.1.1")
|
implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2")
|
||||||
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5")
|
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.3.8")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
@ -15,6 +15,7 @@ fun Project.applyPaperweightAdapterConfiguration() {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
"implementation"(project(":worldedit-bukkit"))
|
"implementation"(project(":worldedit-bukkit"))
|
||||||
|
"implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.9"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named("assemble") {
|
tasks.named("assemble") {
|
||||||
|
@ -45,15 +45,15 @@ fun Project.applyCommonConfiguration() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
add(conf.name, "com.google.guava:guava") {
|
add(conf.name, "com.google.guava:guava") {
|
||||||
version { require("31.1-jre") }
|
version { require("31.0.1-jre") }
|
||||||
because("Mojang provides Guava")
|
because("Mojang provides Guava")
|
||||||
}
|
}
|
||||||
add(conf.name, "com.google.code.gson:gson") {
|
add(conf.name, "com.google.code.gson:gson") {
|
||||||
version { require("2.10") }
|
version { require("2.8.8") }
|
||||||
because("Mojang provides Gson")
|
because("Mojang provides Gson")
|
||||||
}
|
}
|
||||||
add(conf.name, "it.unimi.dsi:fastutil") {
|
add(conf.name, "it.unimi.dsi:fastutil") {
|
||||||
version { require("8.5.9") }
|
version { require("8.5.6") }
|
||||||
because("Mojang provides FastUtil")
|
because("Mojang provides FastUtil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,12 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
"compileOnly"("com.google.code.findbugs:jsr305:3.0.2")
|
"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-api:5.8.1")
|
||||||
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.10.0")
|
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.8.1")
|
||||||
"testImplementation"("org.mockito:mockito-core:5.4.0")
|
"testImplementation"("org.mockito:mockito-core:3.12.4")
|
||||||
"testImplementation"("org.mockito:mockito-junit-jupiter:5.4.0")
|
"testImplementation"("org.mockito:mockito-junit-jupiter:3.12.4")
|
||||||
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.10.0")
|
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.8.1")
|
||||||
|
"implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.9"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Java 8 turns on doclint which we fail
|
// Java 8 turns on doclint which we fail
|
||||||
@ -58,12 +59,12 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
|
|||||||
)
|
)
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
links(
|
links(
|
||||||
"https://jd.advntr.dev/api/latest/",
|
"https://jd.adventure.kyori.net/api/latest/",
|
||||||
"https://logging.apache.org/log4j/2.x/log4j-api/apidocs/",
|
"https://logging.apache.org/log4j/2.x/log4j-api/apidocs/",
|
||||||
"https://www.antlr.org/api/Java/",
|
"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/core/0.5.7/",
|
||||||
"https://docs.enginehub.org/javadoc/org.enginehub.piston/default-impl/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.18/",
|
||||||
"https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/"
|
"https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/"
|
||||||
)
|
)
|
||||||
docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}"
|
docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}"
|
||||||
|
@ -204,7 +204,7 @@ fun Project.applyLibrariesConfiguration() {
|
|||||||
from(libsComponent)
|
from(libsComponent)
|
||||||
|
|
||||||
group = "com.fastasyncworldedit"
|
group = "com.fastasyncworldedit"
|
||||||
artifactId = "FastAsyncWorldEdit-Libs-${project.name.replaceFirstChar(Char::titlecase)}"
|
artifactId = "FastAsyncWorldEdit-Libs-${project.name.capitalize()}"
|
||||||
version = version
|
version = version
|
||||||
|
|
||||||
pom {
|
pom {
|
||||||
@ -224,7 +224,7 @@ fun Project.applyLibrariesConfiguration() {
|
|||||||
developer {
|
developer {
|
||||||
id.set("NotMyFault")
|
id.set("NotMyFault")
|
||||||
name.set("Alexander Brandes")
|
name.set("Alexander Brandes")
|
||||||
email.set("contact(at)notmyfault.dev")
|
email.set("contact@notmyfault.dev")
|
||||||
organization.set("IntellectualSites")
|
organization.set("IntellectualSites")
|
||||||
}
|
}
|
||||||
developer {
|
developer {
|
||||||
|
@ -85,7 +85,7 @@ fun Project.applyPlatformAndCoreConfiguration() {
|
|||||||
developer {
|
developer {
|
||||||
id.set("NotMyFault")
|
id.set("NotMyFault")
|
||||||
name.set("Alexander Brandes")
|
name.set("Alexander Brandes")
|
||||||
email.set("contact(at)notmyfault.dev")
|
email.set("contact@notmyfault.dev")
|
||||||
organization.set("IntellectualSites")
|
organization.set("IntellectualSites")
|
||||||
}
|
}
|
||||||
developer {
|
developer {
|
||||||
|
@ -1,66 +1,53 @@
|
|||||||
[versions]
|
[versions]
|
||||||
# Minecraft expectations
|
# Minecraft expectations
|
||||||
paper = "1.20.1-R0.1-SNAPSHOT"
|
fastutil = "8.5.6"
|
||||||
fastutil = "8.5.9"
|
guava = "31.0.1-jre"
|
||||||
guava = "31.1-jre"
|
log4j = "2.17.1"
|
||||||
log4j = "2.19.0"
|
|
||||||
gson = "2.10"
|
|
||||||
snakeyaml = "2.0"
|
|
||||||
|
|
||||||
# Plugins
|
# Plugins
|
||||||
dummypermscompat = "1.10"
|
dummypermscompat = "1.10"
|
||||||
worldguard-bukkit = "7.0.9"
|
worldguard-bukkit = "7.0.7"
|
||||||
mapmanager = "1.8.0-SNAPSHOT"
|
mapmanager = "1.8.0-SNAPSHOT"
|
||||||
griefprevention = "16.18.1"
|
griefprevention = "16.18"
|
||||||
griefdefender = "2.1.0-SNAPSHOT"
|
griefdefender = "2.1.0-SNAPSHOT"
|
||||||
|
mcore = "7.0.1"
|
||||||
residence = "4.5._13.1"
|
residence = "4.5._13.1"
|
||||||
towny = "0.99.5.10"
|
towny = "0.98.3.2"
|
||||||
plotsquared = "7.0.0-rc.4"
|
redprotect = "1.9.6"
|
||||||
|
|
||||||
# Third party
|
# Third party
|
||||||
bstats = "3.0.2"
|
bstats = "3.0.0"
|
||||||
sparsebitset = "1.2"
|
sparsebitset = "1.2"
|
||||||
parallelgzip = "1.0.5"
|
parallelgzip = "1.0.5"
|
||||||
adventure = "4.14.0"
|
adventure = "4.9.3"
|
||||||
adventure-bukkit = "4.3.0"
|
|
||||||
checkerqual = "3.37.0"
|
|
||||||
truezip = "6.8.4"
|
truezip = "6.8.4"
|
||||||
auto-value = "1.10.2"
|
auto-value = "1.9"
|
||||||
findbugs = "3.0.2"
|
findbugs = "3.0.2"
|
||||||
rhino-runtime = "1.7.14"
|
rhino-runtime = "1.7.14"
|
||||||
zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs
|
zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs
|
||||||
antlr4 = "4.13.0"
|
antlr4 = "4.10.1"
|
||||||
json-simple = "1.1.1"
|
json-simple = "1.1.1"
|
||||||
jlibnoise = "1.0.0"
|
jlibnoise = "1.0.0"
|
||||||
jchronic = "0.2.4a"
|
jchronic = "0.2.4a"
|
||||||
lz4-java = "1.8.0"
|
lz4-java = "1.8.0"
|
||||||
lz4-stream = "1.0.0"
|
lz4-stream = "1.0.0"
|
||||||
commons-cli = "1.5.0"
|
|
||||||
paperlib = "1.0.8"
|
|
||||||
paster = "1.1.5"
|
|
||||||
vault = "1.7.1"
|
|
||||||
serverlib = "2.3.1"
|
|
||||||
## Internal
|
## Internal
|
||||||
|
adventure-text-minimessage = "4.2.0-SNAPSHOT"
|
||||||
text-adapter = "3.0.6"
|
text-adapter = "3.0.6"
|
||||||
text = "3.0.4"
|
text = "3.0.4"
|
||||||
piston = "0.5.7"
|
piston = "0.5.7"
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
mockito = "5.4.0"
|
mockito = "4.6.1"
|
||||||
|
|
||||||
# Gradle plugins
|
# Gradle plugins
|
||||||
pluginyml = "0.6.0"
|
pluginyml = "0.5.2"
|
||||||
minotaur = "2.8.3"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Minecraft expectations
|
# Minecraft expectations
|
||||||
paper = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" }
|
|
||||||
fastutil = { group = "it.unimi.dsi", name = "fastutil", version.ref = "fastutil" }
|
fastutil = { group = "it.unimi.dsi", name = "fastutil", version.ref = "fastutil" }
|
||||||
log4jBom = { group = "org.apache.logging.log4j", name = "log4j-bom", version.ref = "log4j" }
|
log4jBom = { group = "org.apache.logging.log4j", name = "log4j-bom", version.ref = "log4j" }
|
||||||
log4jApi = { group = "org.apache.logging.log4j", name = "log4j-api", version.ref = "log4j" }
|
|
||||||
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
|
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
|
||||||
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
|
|
||||||
snakeyaml = { group = "org.yaml", name = "snakeyaml", version.ref = "snakeyaml" }
|
|
||||||
|
|
||||||
# Plugins
|
# Plugins
|
||||||
dummypermscompat = { group = "com.sk89q", name = "dummypermscompat", version.ref = "dummypermscompat" }
|
dummypermscompat = { group = "com.sk89q", name = "dummypermscompat", version.ref = "dummypermscompat" }
|
||||||
@ -68,14 +55,13 @@ worldguard = { group = "com.sk89q.worldguard", name = "worldguard-bukkit", versi
|
|||||||
mapmanager = { group = "com.github.InventivetalentDev", name = "MapManager", version.ref = "mapmanager" }
|
mapmanager = { group = "com.github.InventivetalentDev", name = "MapManager", version.ref = "mapmanager" }
|
||||||
griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention", version.ref = "griefprevention" }
|
griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention", version.ref = "griefprevention" }
|
||||||
griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" }
|
griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" }
|
||||||
|
mcore = { group = "com.massivecraft", name = "mcore", version.ref = "mcore" }
|
||||||
residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" }
|
residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" }
|
||||||
towny = { group = "com.palmergames.bukkit.towny", name = "towny", version.ref = "towny" }
|
towny = { group = "com.github.TownyAdvanced", name = "Towny", version.ref = "towny" }
|
||||||
plotSquaredCore = { group = "com.intellectualsites.plotsquared", name = "plotsquared-core", version.ref = "plotsquared" }
|
redprotect = { group = "net.fabiozumbi12", name = "redprotect", version.ref = "redprotect" }
|
||||||
plotSquaredBukkit = { group = "com.intellectualsites.plotsquared", name = "plotsquared-bukkit", version.ref = "plotsquared" }
|
|
||||||
|
|
||||||
# Third Party
|
# Third Party
|
||||||
bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" }
|
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" }
|
sparsebitset = { group = "com.zaxxer", name = "SparseBitSet", version.ref = "sparsebitset" }
|
||||||
parallelgzip = { group = "org.anarres", name = "parallelgzip", version.ref = "parallelgzip" }
|
parallelgzip = { group = "org.anarres", name = "parallelgzip", version.ref = "parallelgzip" }
|
||||||
adventureNbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" }
|
adventureNbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" }
|
||||||
@ -92,15 +78,6 @@ jlibnoise = { group = "com.sk89q.lib", name = "jlibnoise", version.ref = "jlibno
|
|||||||
jchronic = { group = "com.sk89q", name = "jchronic", version.ref = "jchronic" }
|
jchronic = { group = "com.sk89q", name = "jchronic", version.ref = "jchronic" }
|
||||||
lz4Java = { group = "org.lz4", name = "lz4-java", version.ref = "lz4-java" }
|
lz4Java = { group = "org.lz4", name = "lz4-java", version.ref = "lz4-java" }
|
||||||
lz4JavaStream = { group = "net.jpountz", name = "lz4-java-stream", version.ref = "lz4-stream" }
|
lz4JavaStream = { group = "net.jpountz", name = "lz4-java-stream", version.ref = "lz4-stream" }
|
||||||
commonsCli = { group = "commons-cli", name = "commons-cli", version.ref = "commons-cli" }
|
|
||||||
paperlib = { group = "io.papermc", name = "paperlib", version.ref = "paperlib" }
|
|
||||||
adventureApi = { group = "net.kyori", name = "adventure-api", version.ref = "adventure" }
|
|
||||||
adventureMiniMessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" }
|
|
||||||
adventureBukkit = { group = "net.kyori", name = "adventure-platform-bukkit", version.ref = "adventure-bukkit" }
|
|
||||||
paster = { group = "com.intellectualsites.paster", name = "Paster", version.ref = "paster" }
|
|
||||||
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" }
|
|
||||||
|
|
||||||
# Internal
|
# Internal
|
||||||
## Text
|
## Text
|
||||||
@ -122,4 +99,3 @@ log4jCore = { group = "org.apache.logging.log4j", name = "log4j-core", version.r
|
|||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }
|
pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }
|
||||||
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }
|
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binäre Datei nicht angezeigt.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,7 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
||||||
networkTimeout=10000
|
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
24
gradlew
vendored
24
gradlew
vendored
@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@ -80,11 +80,14 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# This is normally unused
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
APP_BASE_NAME=${0##*/}
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
|
||||||
@ -130,29 +133,22 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
if ! command -v java >/dev/null 2>&1
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
then
|
|
||||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
|
||||||
# shellcheck disable=SC3045
|
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
|
||||||
# shellcheck disable=SC3045
|
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@ -197,10 +193,6 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
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;
|
# Collect all arguments for the java command;
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
1
gradlew.bat
vendored
1
gradlew.bat
vendored
@ -26,7 +26,6 @@ if "%OS%"=="Windows_NT" setlocal
|
|||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
@rem This is normally unused
|
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
28
renovate.json
Normale Datei
28
renovate.json
Normale Datei
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"config:base",
|
||||||
|
":disableDependencyDashboard"
|
||||||
|
],
|
||||||
|
"ignoreDeps": [
|
||||||
|
"guava",
|
||||||
|
"com.google.guava:guava",
|
||||||
|
"rhino-runtime",
|
||||||
|
"org.antlr",
|
||||||
|
"antlr4-runtime",
|
||||||
|
"fastutil",
|
||||||
|
"it.unimi.dsi:fastutil",
|
||||||
|
"auto-value-annotations",
|
||||||
|
"auto-value",
|
||||||
|
"com.google.code.gson:gson",
|
||||||
|
"net.fabricmc:fabric-loader",
|
||||||
|
"net.fabricmc.fabric-api:fabric-api",
|
||||||
|
"com.github.luben:zstd-jni",
|
||||||
|
"net.kyori",
|
||||||
|
"net.kyori:adventure-nbt",
|
||||||
|
"org.jetbrains.kotlin.jvm",
|
||||||
|
"log4j"
|
||||||
|
],
|
||||||
|
"labels": ["Renovate"],
|
||||||
|
"commitMessagePrefix": "build: ",
|
||||||
|
"rebaseWhen": "conflicted"
|
||||||
|
}
|
@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit"
|
|||||||
|
|
||||||
include("worldedit-libs")
|
include("worldedit-libs")
|
||||||
|
|
||||||
listOf("legacy", "1_17_1", "1_18_2", "1_19", "1_19_3","1_19_4", "1_20").forEach {
|
listOf("legacy", "1_17_1", "1_18_2", "1_19").forEach {
|
||||||
include("worldedit-bukkit:adapters:adapter-$it")
|
include("worldedit-bukkit:adapters:adapter-$it")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
build:
|
|
||||||
- "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew build"
|
|
||||||
- "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew --stop"
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
"/binarys/FastAsyncWorldEdit-1.18.jar": "worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-2.7.1-SNAPSHOT.jar"
|
|
||||||
|
|
||||||
release:
|
|
||||||
- "mvn deploy:deploy-file -DgroupId=de.steamwar -DartifactId=fastasyncworldedit -Dversion=1.18 -Dpackaging=jar -Dfile=/binarys/FastAsyncWorldEdit-1.18.jar -Durl=file:///var/www/html/maven/"
|
|
@ -1,5 +1,3 @@
|
|||||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
|
||||||
|
|
||||||
applyPaperweightAdapterConfiguration()
|
applyPaperweightAdapterConfiguration()
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@ -8,7 +6,10 @@ plugins {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
maven {
|
||||||
|
name = "PaperMC"
|
||||||
|
url = uri("https://repo.papermc.io/repository/maven-public/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@ -21,6 +22,6 @@ configurations.all {
|
|||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210")
|
paperDevBundle("1.17.1-R0.1-20220414.034903-210")
|
||||||
compileOnly(libs.paperlib)
|
compileOnly("io.papermc:paperlib")
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,6 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -659,17 +658,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
.registryAccess()
|
.registryAccess()
|
||||||
.ownedRegistryOrThrow(
|
.ownedRegistryOrThrow(
|
||||||
Registry.BIOME_REGISTRY);
|
Registry.BIOME_REGISTRY);
|
||||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
return biomeRegistry.stream()
|
||||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
.map(biomeRegistry::getKey)
|
||||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
.map(CraftNamespacedKey::fromMinecraft)
|
||||||
for (ResourceLocation key : keys) {
|
.collect(Collectors.toList());
|
||||||
try {
|
|
||||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return namespacedKeys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -415,7 +415,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
|
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
|
||||||
if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) {
|
if (ordinal != 0) {
|
||||||
BlockEntity tile = entry.getValue();
|
BlockEntity tile = entry.getValue();
|
||||||
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
|
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
|
||||||
if (beacons == null) {
|
if (beacons == null) {
|
||||||
@ -445,11 +445,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << layer;
|
bitMask |= 1 << layer;
|
||||||
|
|
||||||
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
|
// Changes may still be written to chunk SET
|
||||||
// this chunk GET when #updateGet is called. Future dords, please listen this time.
|
|
||||||
char[] tmp = set.load(layerNo);
|
char[] tmp = set.load(layerNo);
|
||||||
char[] setArr = new char[tmp.length];
|
char[] setArr = new char[4096];
|
||||||
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
|
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
||||||
|
|
||||||
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
// 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.
|
// submitted to keep loaded internal chunks to queue target size.
|
||||||
|
@ -12,7 +12,6 @@ import com.fastasyncworldedit.core.util.ReflectionUtils;
|
|||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
import com.mojang.datafixers.util.Either;
|
import com.mojang.datafixers.util.Either;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
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.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
@ -30,9 +29,7 @@ import net.minecraft.server.level.ChunkHolder;
|
|||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.util.BitStorage;
|
import net.minecraft.util.BitStorage;
|
||||||
import net.minecraft.util.Unit;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
@ -49,8 +46,6 @@ import net.minecraft.world.level.chunk.Palette;
|
|||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
||||||
import net.minecraft.world.level.gameevent.GameEventListener;
|
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 org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
@ -64,8 +59,6 @@ import java.util.Locale;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -96,8 +89,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
|
|
||||||
private static final Field fieldRemove;
|
private static final Field fieldRemove;
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l"));
|
fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l"));
|
||||||
@ -218,12 +209,10 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
// Avoid "async" methods from the main thread.
|
// Avoid "async" methods from the main thread.
|
||||||
@ -232,21 +221,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||||
try {
|
try {
|
||||||
CraftChunk chunk;
|
CraftChunk chunk = (CraftChunk) future.get();
|
||||||
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();
|
return chunk.getHandle();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -255,13 +230,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
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) {
|
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||||
try {
|
try {
|
||||||
@ -505,7 +473,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
static List<Entity> getEntities(LevelChunk chunk) {
|
||||||
return chunk.level.entityManager.getEntities(chunk.getPos());
|
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,6 @@ import java.util.Collections;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.OptionalLong;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -218,10 +217,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
BiomeProvider biomeProvider = getBiomeProvider();
|
BiomeProvider biomeProvider = getBiomeProvider();
|
||||||
|
|
||||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData();
|
||||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
|
||||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
WorldGenSettings newOpts = levelProperties.worldGenSettings()
|
||||||
: originalOpts;
|
.withSeed(originalWorldData.settings.hardcore(), options.getSeed());
|
||||||
LevelSettings newWorldSettings = new LevelSettings(
|
LevelSettings newWorldSettings = new LevelSettings(
|
||||||
"faweregentempworld",
|
"faweregentempworld",
|
||||||
originalWorldData.settings.gameType(),
|
originalWorldData.settings.gameType(),
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
}
|
}
|
||||||
@ -7,11 +5,14 @@ plugins {
|
|||||||
applyPaperweightAdapterConfiguration()
|
applyPaperweightAdapterConfiguration()
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
maven {
|
||||||
|
name = "PaperMC"
|
||||||
|
url = uri("https://repo.papermc.io/repository/maven-public/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
// 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")
|
paperDevBundle("1.18.2-R0.1-20220614.230913-165")
|
||||||
compileOnly(libs.paperlib)
|
compileOnly("io.papermc:paperlib")
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
|
|||||||
import com.sk89q.worldedit.world.entity.EntityType;
|
import com.sk89q.worldedit.world.entity.EntityType;
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
import com.sk89q.worldedit.world.item.ItemType;
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.WritableRegistry;
|
import net.minecraft.core.WritableRegistry;
|
||||||
@ -656,26 +655,25 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
.registryAccess()
|
.registryAccess()
|
||||||
.ownedRegistryOrThrow(
|
.ownedRegistryOrThrow(
|
||||||
Registry.BIOME_REGISTRY);
|
Registry.BIOME_REGISTRY);
|
||||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
return biomeRegistry.stream()
|
||||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
.map(biomeRegistry::getKey).filter(Objects::nonNull)
|
||||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
.map(CraftNamespacedKey::fromMinecraft)
|
||||||
for (ResourceLocation key : keys) {
|
.collect(Collectors.toList());
|
||||||
try {
|
|
||||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return namespacedKeys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RelighterFactory getRelighterFactory() {
|
public RelighterFactory getRelighterFactory() {
|
||||||
if (PaperLib.isPaper()) {
|
try {
|
||||||
return new PaperweightStarlightRelighterFactory();
|
Class.forName("ca.spottedleaf.starlight.common.light.StarLightEngine");
|
||||||
} else {
|
if (PaperweightStarlightRelighter.isUsable()) {
|
||||||
return new NMSRelighterFactory();
|
return new PaperweightStarlightRelighterFactory();
|
||||||
|
}
|
||||||
|
} catch (ThreadDeath td) {
|
||||||
|
throw td;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return new NMSRelighterFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -408,7 +408,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
|
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
|
||||||
if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) {
|
if (ordinal != 0) {
|
||||||
BlockEntity tile = entry.getValue();
|
BlockEntity tile = entry.getValue();
|
||||||
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
|
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
|
||||||
if (beacons == null) {
|
if (beacons == null) {
|
||||||
@ -491,11 +491,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
// 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[] tmp = set.load(layerNo);
|
||||||
char[] setArr = new char[tmp.length];
|
char[] setArr = new char[4096];
|
||||||
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
|
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
||||||
|
|
||||||
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
// 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.
|
// submitted to keep loaded internal chunks to queue target size.
|
||||||
|
@ -13,26 +13,25 @@ import com.mojang.datafixers.util.Either;
|
|||||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
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.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
import io.papermc.lib.PaperLib;
|
import io.papermc.lib.PaperLib;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.IdMap;
|
import net.minecraft.core.IdMap;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.util.BitStorage;
|
import net.minecraft.util.BitStorage;
|
||||||
import net.minecraft.util.SimpleBitStorage;
|
import net.minecraft.util.SimpleBitStorage;
|
||||||
import net.minecraft.util.ThreadingDetector;
|
import net.minecraft.util.ThreadingDetector;
|
||||||
import net.minecraft.util.Unit;
|
|
||||||
import net.minecraft.util.ZeroBitStorage;
|
import net.minecraft.util.ZeroBitStorage;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
@ -40,6 +39,7 @@ import net.minecraft.world.level.LevelAccessor;
|
|||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.chunk.GlobalPalette;
|
import net.minecraft.world.level.chunk.GlobalPalette;
|
||||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||||
@ -49,8 +49,8 @@ import net.minecraft.world.level.chunk.LinearPalette;
|
|||||||
import net.minecraft.world.level.chunk.Palette;
|
import net.minecraft.world.level.chunk.Palette;
|
||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||||
import org.apache.logging.log4j.Logger;
|
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
||||||
import org.bukkit.Bukkit;
|
import net.minecraft.world.level.gameevent.GameEventListener;
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
@ -73,8 +73,6 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||||
@ -106,8 +104,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
|
|
||||||
private static final Field fieldRemove;
|
private static final Field fieldRemove;
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
||||||
@ -241,12 +237,10 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
// Avoid "async" methods from the main thread.
|
// Avoid "async" methods from the main thread.
|
||||||
@ -255,21 +249,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||||
try {
|
try {
|
||||||
CraftChunk chunk;
|
CraftChunk chunk = (CraftChunk) future.get();
|
||||||
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();
|
return chunk.getHandle();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -278,13 +258,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
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) {
|
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||||
try {
|
try {
|
||||||
@ -615,7 +588,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
static List<Entity> getEntities(LevelChunk chunk) {
|
||||||
return chunk.level.entityManager.getEntities(chunk.getPos());
|
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||||
|
@ -14,12 +14,16 @@ import it.unimi.dsi.fastutil.longs.LongIterator;
|
|||||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
import net.minecraft.server.MCUtil;
|
import net.minecraft.server.MCUtil;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||||
import net.minecraft.server.level.TicketType;
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.Unit;
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import org.apache.logging.log4j.Logger;
|
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.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -31,6 +35,7 @@ import java.util.function.IntConsumer;
|
|||||||
|
|
||||||
public class PaperweightStarlightRelighter implements Relighter {
|
public class PaperweightStarlightRelighter implements Relighter {
|
||||||
|
|
||||||
|
public static final MethodHandle RELIGHT;
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
|
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 int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
|
||||||
@ -38,6 +43,26 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||||
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
|
private static final int LIGHT_LEVEL = 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 ServerLevel serverLevel;
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
@ -51,6 +76,10 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
this.delegate = new NMSRelighter(queue);
|
this.delegate = new NMSRelighter(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isUsable() {
|
||||||
|
return RELIGHT != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
||||||
areaLock.lock();
|
areaLock.lock();
|
||||||
@ -140,9 +169,14 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
IntConsumer processCallback
|
IntConsumer processCallback
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
int unused = (int) RELIGHT.invokeExact(
|
||||||
} catch (Exception e) {
|
serverLevel.getChunkSource().getLightEngine(),
|
||||||
LOGGER.error("Error occurred on relighting", e);
|
coords,
|
||||||
|
chunkCallback, // callback per chunk
|
||||||
|
processCallback // callback for all chunks
|
||||||
|
);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
LOGGER.error("Error occurred on relighting", throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,10 +146,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "K"));
|
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "K"));
|
||||||
chunkSourceField.setAccessible(true);
|
chunkSourceField.setAccessible(true);
|
||||||
|
|
||||||
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "h"));
|
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "i"));
|
||||||
ringPositionsField.setAccessible(true);
|
ringPositionsField.setAccessible(true);
|
||||||
|
|
||||||
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "i"));
|
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "j"));
|
||||||
hasGeneratedPositionsField.setAccessible(true);
|
hasGeneratedPositionsField.setAccessible(true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@ -213,9 +213,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
BiomeProvider biomeProvider = getBiomeProvider();
|
BiomeProvider biomeProvider = getBiomeProvider();
|
||||||
|
|
||||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData();
|
||||||
|
WorldGenSettings originalOpts = levelProperties.worldGenSettings();
|
||||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed))
|
||||||
: originalOpts;
|
: originalOpts;
|
||||||
LevelSettings newWorldSettings = new LevelSettings(
|
LevelSettings newWorldSettings = new LevelSettings(
|
||||||
"faweregentempworld",
|
"faweregentempworld",
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
}
|
}
|
||||||
@ -7,10 +5,13 @@ plugins {
|
|||||||
applyPaperweightAdapterConfiguration()
|
applyPaperweightAdapterConfiguration()
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
maven {
|
||||||
|
name = "PaperMC"
|
||||||
|
url = uri("https://repo.papermc.io/repository/maven-public/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.2-R0.1-20221206.184705-189")
|
paperDevBundle("1.19-R0.1-20220627.223747-42")
|
||||||
compileOnly(libs.paperlib)
|
compileOnly("io.papermc:paperlib")
|
||||||
}
|
}
|
||||||
|
@ -183,8 +183,8 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
|||||||
CraftServer.class.cast(Bukkit.getServer());
|
CraftServer.class.cast(Bukkit.getServer());
|
||||||
|
|
||||||
int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion();
|
int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion();
|
||||||
if (dataVersion != 3105 && dataVersion != 3117 && dataVersion != 3120) {
|
if (dataVersion != 3105 && dataVersion != 3117) {
|
||||||
throw new UnsupportedClassVersionError("Not 1.19, 1.19.1 or 1.19.2!");
|
throw new UnsupportedClassVersionError("Not 1.19 or 1.19.1!");
|
||||||
}
|
}
|
||||||
|
|
||||||
worldsField = CraftServer.class.getDeclaredField("worlds");
|
worldsField = CraftServer.class.getDeclaredField("worlds");
|
||||||
@ -511,7 +511,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) {
|
public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) {
|
||||||
((CraftPlayer) player).getHandle().connection.send(ClientboundBlockEntityDataPacket.create(
|
((CraftPlayer) player).getHandle().networkManager.send(ClientboundBlockEntityDataPacket.create(
|
||||||
new StructureBlockEntity(
|
new StructureBlockEntity(
|
||||||
new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()),
|
new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()),
|
||||||
Blocks.STRUCTURE_BLOCK.defaultBlockState()
|
Blocks.STRUCTURE_BLOCK.defaultBlockState()
|
||||||
@ -522,7 +522,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendFakeOP(Player player) {
|
public void sendFakeOP(Player player) {
|
||||||
((CraftPlayer) player).getHandle().connection.send(new ClientboundEntityEventPacket(
|
((CraftPlayer) player).getHandle().networkManager.send(new ClientboundEntityEventPacket(
|
||||||
((CraftPlayer) player).getHandle(), (byte) 28
|
((CraftPlayer) player).getHandle(), (byte) 28
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
|
|||||||
import com.sk89q.worldedit.world.entity.EntityType;
|
import com.sk89q.worldedit.world.entity.EntityType;
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
import com.sk89q.worldedit.world.item.ItemType;
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.WritableRegistry;
|
import net.minecraft.core.WritableRegistry;
|
||||||
@ -98,8 +97,6 @@ import org.bukkit.entity.Player;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -117,14 +114,6 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
|
|
||||||
} catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final PaperweightAdapter parent;
|
private final PaperweightAdapter parent;
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -501,7 +490,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
||||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||||
if (map != null && wasAccessibleSinceLastSave(map)) {
|
if (map != null && map.wasAccessibleSinceLastSave()) {
|
||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
// PlayerChunk.d players = map.players;
|
// PlayerChunk.d players = map.players;
|
||||||
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
|
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
|
||||||
@ -596,6 +585,23 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
return true;
|
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
|
@Override
|
||||||
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
||||||
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
||||||
@ -645,26 +651,25 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
.registryAccess()
|
.registryAccess()
|
||||||
.ownedRegistryOrThrow(
|
.ownedRegistryOrThrow(
|
||||||
Registry.BIOME_REGISTRY);
|
Registry.BIOME_REGISTRY);
|
||||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
return biomeRegistry.stream()
|
||||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
.map(biomeRegistry::getKey).filter(Objects::nonNull)
|
||||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
.map(CraftNamespacedKey::fromMinecraft)
|
||||||
for (ResourceLocation key : keys) {
|
.collect(Collectors.toList());
|
||||||
try {
|
|
||||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return namespacedKeys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RelighterFactory getRelighterFactory() {
|
public RelighterFactory getRelighterFactory() {
|
||||||
if (PaperLib.isPaper()) {
|
try {
|
||||||
return new PaperweightStarlightRelighterFactory();
|
Class.forName("ca.spottedleaf.starlight.common.light.StarLightEngine");
|
||||||
} else {
|
if (PaperweightStarlightRelighter.isUsable()) {
|
||||||
return new NMSRelighterFactory();
|
return new PaperweightStarlightRelighterFactory();
|
||||||
|
}
|
||||||
|
} catch (ThreadDeath td) {
|
||||||
|
throw td;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return new NMSRelighterFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -686,16 +691,4 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
return new PaperweightPostProcessor();
|
return new PaperweightPostProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
|
|
||||||
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
|
|
||||||
try {
|
|
||||||
return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException ignored) {
|
|
||||||
// fall-through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Papers new chunk system has no related replacement - therefor we assume true.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
|||||||
import com.fastasyncworldedit.core.util.MathMan;
|
import com.fastasyncworldedit.core.util.MathMan;
|
||||||
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
||||||
import com.google.common.base.Suppliers;
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.ListTag;
|
import com.sk89q.jnbt.ListTag;
|
||||||
import com.sk89q.jnbt.StringTag;
|
import com.sk89q.jnbt.StringTag;
|
||||||
@ -81,6 +82,7 @@ import java.util.concurrent.locks.ReadWriteLock;
|
|||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks {
|
public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks {
|
||||||
|
|
||||||
@ -406,7 +408,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
|
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
|
||||||
if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) {
|
if (ordinal != 0) {
|
||||||
BlockEntity tile = entry.getValue();
|
BlockEntity tile = entry.getValue();
|
||||||
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
|
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
|
||||||
if (beacons == null) {
|
if (beacons == null) {
|
||||||
@ -488,7 +490,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
char[] setArr = set.load(layerNo);
|
char[] tmp = set.load(layerNo);
|
||||||
|
char[] setArr = new char[4096];
|
||||||
|
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
||||||
|
|
||||||
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
// 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.
|
// submitted to keep loaded internal chunks to queue target size.
|
||||||
@ -672,7 +676,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
||||||
for (UUID uuid : entityRemoves) {
|
for (UUID uuid : entityRemoves) {
|
||||||
Entity entity = nmsWorld.getEntities().get(uuid);
|
Entity entity = nmsWorld.entityManager.getEntityGetter().get(uuid);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
removeEntity(entity);
|
removeEntity(entity);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||||
|
|
||||||
import com.destroystokyo.paper.util.maplist.EntityList;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||||
@ -19,22 +18,20 @@ import com.sk89q.worldedit.world.biome.BiomeTypes;
|
|||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
import io.papermc.lib.PaperLib;
|
import io.papermc.lib.PaperLib;
|
||||||
import io.papermc.paper.world.ChunkEntitySlices;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.IdMap;
|
import net.minecraft.core.IdMap;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.util.BitStorage;
|
import net.minecraft.util.BitStorage;
|
||||||
import net.minecraft.util.ExceptionCollector;
|
|
||||||
import net.minecraft.util.SimpleBitStorage;
|
import net.minecraft.util.SimpleBitStorage;
|
||||||
import net.minecraft.util.ThreadingDetector;
|
import net.minecraft.util.ThreadingDetector;
|
||||||
import net.minecraft.util.Unit;
|
|
||||||
import net.minecraft.util.ZeroBitStorage;
|
import net.minecraft.util.ZeroBitStorage;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
@ -52,7 +49,6 @@ import net.minecraft.world.level.chunk.LinearPalette;
|
|||||||
import net.minecraft.world.level.chunk.Palette;
|
import net.minecraft.world.level.chunk.Palette;
|
||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
|
||||||
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
||||||
import net.minecraft.world.level.gameevent.GameEventListener;
|
import net.minecraft.world.level.gameevent.GameEventListener;
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
|
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
|
||||||
@ -110,11 +106,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
|
|
||||||
private static final Field fieldRemove;
|
private static final Field fieldRemove;
|
||||||
|
|
||||||
static final boolean POST_CHUNK_REWRITE;
|
|
||||||
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
|
||||||
private static Field LEVEL_CHUNK_ENTITIES;
|
|
||||||
private static Field SERVER_LEVEL_ENTITY_MANAGER;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
||||||
@ -185,28 +176,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
throw new Error("data type scale not a power of two");
|
throw new Error("data type scale not a power of two");
|
||||||
}
|
}
|
||||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||||
boolean chunkRewrite;
|
|
||||||
try {
|
|
||||||
ServerLevel.class.getDeclaredMethod("getEntityLookup");
|
|
||||||
chunkRewrite = true;
|
|
||||||
PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities");
|
|
||||||
PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException ignored) {
|
|
||||||
chunkRewrite = false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Paper - Pre-Chunk-Update
|
|
||||||
LEVEL_CHUNK_ENTITIES = LevelChunk.class.getDeclaredField("entities");
|
|
||||||
LEVEL_CHUNK_ENTITIES.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Non-Paper
|
|
||||||
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "P"));
|
|
||||||
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
}
|
|
||||||
POST_CHUNK_REWRITE = chunkRewrite;
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Throwable rethrow) {
|
} catch (Throwable rethrow) {
|
||||||
@ -272,12 +241,10 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
// Avoid "async" methods from the main thread.
|
// Avoid "async" methods from the main thread.
|
||||||
@ -295,13 +262,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
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
|
|
||||||
io.papermc.paper.util.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) {
|
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||||
try {
|
try {
|
||||||
@ -630,32 +590,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
static List<Entity> getEntities(LevelChunk chunk) {
|
||||||
ExceptionCollector<RuntimeException> collector = new ExceptionCollector<>();
|
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ));
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
if (POST_CHUNK_REWRITE) {
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (List<Entity>) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ));
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk);
|
|
||||||
return List.of(entityList.getRawData());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
collector.add(new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=false]", e));
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return ((PersistentEntitySectionManager<Entity>) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e));
|
|
||||||
}
|
|
||||||
collector.throwIfPresent();
|
|
||||||
return List.of();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||||
|
@ -12,14 +12,18 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
|||||||
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.MCUtil;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||||
import net.minecraft.server.level.TicketType;
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.Unit;
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import org.apache.logging.log4j.Logger;
|
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.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -31,13 +35,34 @@ import java.util.function.IntConsumer;
|
|||||||
|
|
||||||
public class PaperweightStarlightRelighter implements Relighter {
|
public class PaperweightStarlightRelighter implements Relighter {
|
||||||
|
|
||||||
|
public static final MethodHandle RELIGHT;
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
|
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 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 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 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 ServerLevel serverLevel;
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
@ -51,6 +76,10 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
this.delegate = new NMSRelighter(queue);
|
this.delegate = new NMSRelighter(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isUsable() {
|
||||||
|
return RELIGHT != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
||||||
areaLock.lock();
|
areaLock.lock();
|
||||||
@ -140,9 +169,14 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
IntConsumer processCallback
|
IntConsumer processCallback
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
int unused = (int) RELIGHT.invokeExact(
|
||||||
} catch (Exception e) {
|
serverLevel.getChunkSource().getLightEngine(),
|
||||||
LOGGER.error("Error occurred on relighting", e);
|
coords,
|
||||||
|
chunkCallback, // callback per chunk
|
||||||
|
processCallback // callback for all chunks
|
||||||
|
);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
LOGGER.error("Error occurred on relighting", throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,9 +214,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||||
|
|
||||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData();
|
||||||
|
WorldGenSettings originalOpts = levelProperties.worldGenSettings();
|
||||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed))
|
||||||
: originalOpts;
|
: originalOpts;
|
||||||
LevelSettings newWorldSettings = new LevelSettings(
|
LevelSettings newWorldSettings = new LevelSettings(
|
||||||
"faweregentempworld",
|
"faweregentempworld",
|
||||||
@ -294,7 +295,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
generatorSettingBaseSupplier
|
generatorSettingBaseSupplier
|
||||||
);
|
);
|
||||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||||
chunkGenerator = customChunkGenerator.getDelegate();
|
chunkGenerator = customChunkGenerator.delegate;
|
||||||
} else {
|
} else {
|
||||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||||
return false;
|
return false;
|
||||||
@ -364,7 +365,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
|||||||
Fawe.instance().getQueueHandler().sync(() -> {
|
Fawe.instance().getQueueHandler().sync(() -> {
|
||||||
try {
|
try {
|
||||||
freshChunkProvider.close(false);
|
freshChunkProvider.close(false);
|
||||||
} catch (Exception e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
java
|
|
||||||
}
|
|
||||||
|
|
||||||
applyPaperweightAdapterConfiguration()
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
|
||||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.3-R0.1-20230312.180621-141")
|
|
||||||
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,93 +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_19_R2;
|
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.stats.Stat;
|
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.damagesource.DamageSource;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
|
||||||
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
class PaperweightFakePlayer extends ServerPlayer {
|
|
||||||
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]");
|
|
||||||
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
|
|
||||||
|
|
||||||
PaperweightFakePlayer(ServerLevel world) {
|
|
||||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3 position() {
|
|
||||||
return ORIGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void die(DamageSource damagesource) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OptionalInt openMenu(MenuProvider factory) {
|
|
||||||
return OptionalInt.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void displayClientMessage(Component message, boolean actionBar) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void awardStat(Stat<?> stat, int amount) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void awardStat(Stat<?> stat) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInvulnerableTo(DamageSource damageSource) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openTextEdit(SignBlockEntity sign) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,180 +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_19_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_19_R2.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Objects;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public class PaperweightWorldNativeAccess implements WorldNativeAccess<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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not sure why neighborChanged is deprecated
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
|
||||||
world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) {
|
|
||||||
ServerLevel world = getWorld();
|
|
||||||
// a == updateNeighbors
|
|
||||||
// b == updateDiagonalNeighbors
|
|
||||||
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
|
||||||
CraftWorld craftWorld = world.getWorld();
|
|
||||||
BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState));
|
|
||||||
world.getCraftServer().getPluginManager().callEvent(event);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
|
|
||||||
getWorld().onBlockStateChange(pos, oldState, newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
|
||||||
|
|
||||||
import com.google.common.base.Suppliers;
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
import com.sk89q.util.ReflectionUtil;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.nbt.PaperweightLazyCompoundTag;
|
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.world.level.EmptyBlockGetter;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.material.Material;
|
|
||||||
import net.minecraft.world.level.material.PushReaction;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
|
|
||||||
|
|
||||||
public class PaperweightBlockMaterial implements BlockMaterial {
|
|
||||||
|
|
||||||
private final Block block;
|
|
||||||
private final BlockState blockState;
|
|
||||||
private final Material material;
|
|
||||||
private final boolean isTranslucent;
|
|
||||||
private final CraftBlockData craftBlockData;
|
|
||||||
private final org.bukkit.Material craftMaterial;
|
|
||||||
private final int opacity;
|
|
||||||
private final CompoundTag tile;
|
|
||||||
|
|
||||||
public PaperweightBlockMaterial(Block block) {
|
|
||||||
this(block, block.defaultBlockState());
|
|
||||||
}
|
|
||||||
|
|
||||||
public PaperweightBlockMaterial(Block block, BlockState blockState) {
|
|
||||||
this.block = block;
|
|
||||||
this.blockState = blockState;
|
|
||||||
this.material = blockState.getMaterial();
|
|
||||||
this.craftBlockData = CraftBlockData.fromData(blockState);
|
|
||||||
this.craftMaterial = craftBlockData.getMaterial();
|
|
||||||
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block,
|
|
||||||
Refraction.pickName("properties", "aP"));
|
|
||||||
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
|
|
||||||
Refraction.pickName("canOcclude", "n")
|
|
||||||
);
|
|
||||||
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
|
||||||
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
|
|
||||||
BlockPos.ZERO,
|
|
||||||
blockState
|
|
||||||
);
|
|
||||||
tile = tileEntity == null
|
|
||||||
? null
|
|
||||||
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Block getBlock() {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockState getState() {
|
|
||||||
return blockState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CraftBlockData getCraftBlockData() {
|
|
||||||
return craftBlockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Material getMaterial() {
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAir() {
|
|
||||||
return blockState.isAir();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFullCube() {
|
|
||||||
return craftMaterial.isOccluding();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOpaque() {
|
|
||||||
return material.isSolidBlocking();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPowerSource() {
|
|
||||||
return blockState.isSignalSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLiquid() {
|
|
||||||
return material.isLiquid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSolid() {
|
|
||||||
return material.isSolid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getHardness() {
|
|
||||||
return craftBlockData.getState().destroySpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getResistance() {
|
|
||||||
return block.getExplosionResistance();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getSlipperiness() {
|
|
||||||
return block.getFriction();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightValue() {
|
|
||||||
return blockState.getLightEmission();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightOpacity() {
|
|
||||||
return opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFragileWhenPushed() {
|
|
||||||
return material.getPushReaction() == PushReaction.DESTROY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isUnpushable() {
|
|
||||||
return material.getPushReaction() == PushReaction.BLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTicksRandomly() {
|
|
||||||
return block.isRandomlyTicking(blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMovementBlocker() {
|
|
||||||
return material.isSolid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBurnable() {
|
|
||||||
return material.isFlammable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isToolRequired() {
|
|
||||||
// Removed in 1.16.1, this is not present in higher versions
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReplacedDuringPlacement() {
|
|
||||||
return material.isReplaceable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTranslucent() {
|
|
||||||
return isTranslucent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasContainer() {
|
|
||||||
return block instanceof EntityBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTile() {
|
|
||||||
return block instanceof EntityBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getDefaultTile() {
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMapColor() {
|
|
||||||
// rgb field
|
|
||||||
return material.getColor().col;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,706 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
|
||||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
|
||||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
|
||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
|
||||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.sk89q.jnbt.Tag;
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
|
||||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R2.PaperweightAdapter;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.nbt.PaperweightLazyCompoundTag;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.regen.PaperweightRegen;
|
|
||||||
import com.sk89q.worldedit.entity.BaseEntity;
|
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
|
||||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.regions.Region;
|
|
||||||
import com.sk89q.worldedit.registry.state.BooleanProperty;
|
|
||||||
import com.sk89q.worldedit.registry.state.DirectionalProperty;
|
|
||||||
import com.sk89q.worldedit.registry.state.EnumProperty;
|
|
||||||
import com.sk89q.worldedit.registry.state.IntegerProperty;
|
|
||||||
import com.sk89q.worldedit.registry.state.Property;
|
|
||||||
import com.sk89q.worldedit.util.Direction;
|
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
|
||||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
|
||||||
import com.sk89q.worldedit.util.nbt.StringBinaryTag;
|
|
||||||
import com.sk89q.worldedit.world.RegenOptions;
|
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
|
||||||
import com.sk89q.worldedit.world.entity.EntityType;
|
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.WritableRegistry;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.nbt.IntTag;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.dedicated.DedicatedServer;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.util.StringRepresentable;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
|
||||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.NamespacedKey;
|
|
||||||
import org.bukkit.TreeType;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.util.CraftNamespacedKey;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|
||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
|
|
||||||
} catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final PaperweightAdapter parent;
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Code that may break between versions of Minecraft
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
|
|
||||||
private char[] ibdToStateOrdinal = null;
|
|
||||||
private int[] ordinalToIbdID = null;
|
|
||||||
private boolean initialised = false;
|
|
||||||
private Map<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 DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK)
|
|
||||||
.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public BlockState getBlock(Location location) {
|
|
||||||
Preconditions.checkNotNull(location);
|
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
|
||||||
int y = location.getBlockY();
|
|
||||||
int z = location.getBlockZ();
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
|
||||||
BlockState state = adapt(blockData);
|
|
||||||
if (state == null) {
|
|
||||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
||||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBlock getFullBlock(final Location location) {
|
|
||||||
Preconditions.checkNotNull(location);
|
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
|
||||||
int y = location.getBlockY();
|
|
||||||
int z = location.getBlockZ();
|
|
||||||
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
|
||||||
BlockState state = adapt(blockData);
|
|
||||||
if (state == null) {
|
|
||||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
||||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
||||||
}
|
|
||||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
|
||||||
|
|
||||||
// Read the NBT data
|
|
||||||
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
|
|
||||||
if (blockEntity != null) {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
|
|
||||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.toBaseBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<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 && wasAccessibleSinceLastSave(map)) {
|
|
||||||
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) {
|
|
||||||
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(
|
|
||||||
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM)
|
|
||||||
.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
|
||||||
baseItemStack.getAmount()
|
|
||||||
);
|
|
||||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
|
|
||||||
return CraftItemStack.asCraftMirror(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean generateTree(
|
|
||||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
|
||||||
org.bukkit.World bukkitWorld
|
|
||||||
) {
|
|
||||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
|
||||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
|
||||||
blockVector3 = blockVector3.add(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
|
||||||
}
|
|
||||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
final BlockVector3 finalBlockVector = blockVector3;
|
|
||||||
// Sync to main thread to ensure no clashes occur
|
|
||||||
Map<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 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()
|
|
||||||
.registryOrThrow(BIOME);
|
|
||||||
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()
|
|
||||||
.registryOrThrow(BIOME);
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
|
|
||||||
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
|
|
||||||
try {
|
|
||||||
return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException ignored) {
|
|
||||||
// fall-through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Papers new chunk system has no related replacement - therefor we assume true.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,286 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.Fawe;
|
|
||||||
import com.fastasyncworldedit.core.math.IntPair;
|
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
|
||||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<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,248 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_19_R2.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 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 net.minecraft.world.level.chunk.PalettedContainerRO;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
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 static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
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, PalettedContainerRO<Holder<Biome>> biomeData) {
|
|
||||||
if (biomes == null) {
|
|
||||||
biomes = new PalettedContainer[getSectionCount()];
|
|
||||||
}
|
|
||||||
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
|
|
||||||
biomes[layer] = palettedContainer.copy();
|
|
||||||
} else {
|
|
||||||
LOGGER.error(
|
|
||||||
"Cannot correctly save biomes to history. Expected class type {} but got {}",
|
|
||||||
PalettedContainer.class.getSimpleName(),
|
|
||||||
biomeData.getClass().getSimpleName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,34 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
|
||||||
|
|
||||||
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,700 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
|
|
||||||
|
|
||||||
import com.destroystokyo.paper.util.maplist.EntityList;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
|
||||||
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.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 io.papermc.paper.world.ChunkEntitySlices;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.IdMap;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.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.ExceptionCollector;
|
|
||||||
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 net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_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.function.Function;
|
|
||||||
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
static final boolean POST_CHUNK_REWRITE;
|
|
||||||
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
|
||||||
private static Field LEVEL_CHUNK_ENTITIES;
|
|
||||||
private static Field SERVER_LEVEL_ENTITY_MANAGER;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
|
||||||
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", "a"),
|
|
||||||
BlockEntity.class,
|
|
||||||
ServerLevel.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);
|
|
||||||
boolean chunkRewrite;
|
|
||||||
try {
|
|
||||||
ServerLevel.class.getDeclaredMethod("getEntityLookup");
|
|
||||||
chunkRewrite = true;
|
|
||||||
PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities");
|
|
||||||
PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException ignored) {
|
|
||||||
chunkRewrite = false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Paper - Pre-Chunk-Update
|
|
||||||
LEVEL_CHUNK_ENTITIES = LevelChunk.class.getDeclaredField("entities");
|
|
||||||
LEVEL_CHUNK_ENTITIES.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Non-Paper
|
|
||||||
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "P"));
|
|
||||||
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
}
|
|
||||||
POST_CHUNK_REWRITE = chunkRewrite;
|
|
||||||
} 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 = (CraftChunk) future.get();
|
|
||||||
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
|
|
||||||
io.papermc.paper.util.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);
|
|
||||||
LevelChunk levelChunk;
|
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
// getChunkAtIfLoadedImmediately is paper only
|
|
||||||
levelChunk = nmsWorld
|
|
||||||
.getChunkSource()
|
|
||||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
|
||||||
} else {
|
|
||||||
levelChunk = ((Optional<LevelChunk>) ((Either) chunkHolder
|
|
||||||
.getTickingChunkFuture() // method is not present with new paper chunk system
|
|
||||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left())
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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().registryOrThrow(BIOME);
|
|
||||||
if (biomeRegistry.getKey(biome.value()) == null) {
|
|
||||||
return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
|
||||||
try {
|
|
||||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
|
||||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
|
||||||
if (blockEntity != null) {
|
|
||||||
if (!levelChunk.level.isClientSide) {
|
|
||||||
methodRemoveGameEventListener.invoke(levelChunk, beacon, levelChunk.level);
|
|
||||||
}
|
|
||||||
fieldRemove.set(beacon, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
|
||||||
ExceptionCollector<RuntimeException> collector = new ExceptionCollector<>();
|
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
if (POST_CHUNK_REWRITE) {
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (List<Entity>) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ));
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk);
|
|
||||||
return List.of(entityList.getRawData());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
collector.add(new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=false]", e));
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return ((PersistentEntitySectionManager<Entity>) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e));
|
|
||||||
}
|
|
||||||
collector.throwIfPresent();
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
record FakeIdMapBlock(int size) implements IdMap<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,175 +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.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_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,28 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_19_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) {
|
|
||||||
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_19_R2.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_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,16 +0,0 @@
|
|||||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
java
|
|
||||||
}
|
|
||||||
|
|
||||||
applyPaperweightAdapterConfiguration()
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.4-R0.1-20230608.201059-104")
|
|
||||||
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,93 +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_19_R3;
|
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.stats.Stat;
|
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.damagesource.DamageSource;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
|
||||||
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
class PaperweightFakePlayer extends ServerPlayer {
|
|
||||||
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]");
|
|
||||||
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
|
|
||||||
|
|
||||||
PaperweightFakePlayer(ServerLevel world) {
|
|
||||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3 position() {
|
|
||||||
return ORIGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void die(DamageSource damagesource) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OptionalInt openMenu(MenuProvider factory) {
|
|
||||||
return OptionalInt.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void displayClientMessage(Component message, boolean actionBar) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void awardStat(Stat<?> stat, int amount) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void awardStat(Stat<?> stat) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInvulnerableTo(DamageSource damageSource) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openTextEdit(SignBlockEntity sign) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,180 +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_19_R3;
|
|
||||||
|
|
||||||
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_19_R3.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not sure why neighborChanged is deprecated
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
|
||||||
world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) {
|
|
||||||
ServerLevel world = getWorld();
|
|
||||||
// a == updateNeighbors
|
|
||||||
// b == updateDiagonalNeighbors
|
|
||||||
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
|
||||||
CraftWorld craftWorld = world.getWorld();
|
|
||||||
BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState));
|
|
||||||
world.getCraftServer().getPluginManager().callEvent(event);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
|
|
||||||
getWorld().onBlockStateChange(pos, oldState, newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_R3.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_R3.block.data.CraftBlockData;
|
|
||||||
|
|
||||||
public class PaperweightBlockMaterial implements BlockMaterial {
|
|
||||||
|
|
||||||
private final Block block;
|
|
||||||
private final BlockState blockState;
|
|
||||||
private final Material material;
|
|
||||||
private final boolean isTranslucent;
|
|
||||||
private final CraftBlockData craftBlockData;
|
|
||||||
private final org.bukkit.Material craftMaterial;
|
|
||||||
private final int opacity;
|
|
||||||
private final CompoundTag tile;
|
|
||||||
|
|
||||||
public PaperweightBlockMaterial(Block block) {
|
|
||||||
this(block, block.defaultBlockState());
|
|
||||||
}
|
|
||||||
|
|
||||||
public PaperweightBlockMaterial(Block block, BlockState blockState) {
|
|
||||||
this.block = block;
|
|
||||||
this.blockState = blockState;
|
|
||||||
this.material = blockState.getMaterial();
|
|
||||||
this.craftBlockData = CraftBlockData.fromData(blockState);
|
|
||||||
this.craftMaterial = craftBlockData.getMaterial();
|
|
||||||
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block,
|
|
||||||
Refraction.pickName("properties", "aP"));
|
|
||||||
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
|
|
||||||
Refraction.pickName("canOcclude", "n")
|
|
||||||
);
|
|
||||||
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
|
||||||
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
|
|
||||||
BlockPos.ZERO,
|
|
||||||
blockState
|
|
||||||
);
|
|
||||||
tile = tileEntity == null
|
|
||||||
? null
|
|
||||||
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Block getBlock() {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockState getState() {
|
|
||||||
return blockState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CraftBlockData getCraftBlockData() {
|
|
||||||
return craftBlockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Material getMaterial() {
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAir() {
|
|
||||||
return blockState.isAir();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFullCube() {
|
|
||||||
return craftMaterial.isOccluding();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOpaque() {
|
|
||||||
return material.isSolidBlocking();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPowerSource() {
|
|
||||||
return blockState.isSignalSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLiquid() {
|
|
||||||
return material.isLiquid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSolid() {
|
|
||||||
return material.isSolid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getHardness() {
|
|
||||||
return craftBlockData.getState().destroySpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getResistance() {
|
|
||||||
return block.getExplosionResistance();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getSlipperiness() {
|
|
||||||
return block.getFriction();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightValue() {
|
|
||||||
return blockState.getLightEmission();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightOpacity() {
|
|
||||||
return opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFragileWhenPushed() {
|
|
||||||
return material.getPushReaction() == PushReaction.DESTROY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isUnpushable() {
|
|
||||||
return material.getPushReaction() == PushReaction.BLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTicksRandomly() {
|
|
||||||
return block.isRandomlyTicking(blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMovementBlocker() {
|
|
||||||
return material.isSolid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBurnable() {
|
|
||||||
return material.isFlammable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isToolRequired() {
|
|
||||||
// Removed in 1.16.1, this is not present in higher versions
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReplacedDuringPlacement() {
|
|
||||||
return material.isReplaceable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTranslucent() {
|
|
||||||
return isTranslucent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasContainer() {
|
|
||||||
return block instanceof EntityBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTile() {
|
|
||||||
return block instanceof EntityBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getDefaultTile() {
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMapColor() {
|
|
||||||
// rgb field
|
|
||||||
return material.getColor().col;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,652 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
|
||||||
|
|
||||||
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.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.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.BlockTypesCache;
|
|
||||||
import com.sk89q.worldedit.world.entity.EntityType;
|
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.WritableRegistry;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.dedicated.DedicatedServer;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.util.StringRepresentable;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.level.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 org.apache.logging.log4j.Logger;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.NamespacedKey;
|
|
||||||
import org.bukkit.TreeType;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_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.entity.Player;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|
||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
|
|
||||||
} catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final PaperweightAdapter parent;
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Code that may break between versions of Minecraft
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
|
|
||||||
private char[] ibdToStateOrdinal = null;
|
|
||||||
private int[] ordinalToIbdID = null;
|
|
||||||
private boolean initialised = false;
|
|
||||||
private Map<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 DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK)
|
|
||||||
.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public BlockState getBlock(Location location) {
|
|
||||||
Preconditions.checkNotNull(location);
|
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
|
||||||
int y = location.getBlockY();
|
|
||||||
int z = location.getBlockZ();
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
|
||||||
BlockState state = adapt(blockData);
|
|
||||||
if (state == null) {
|
|
||||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
||||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBlock getFullBlock(final Location location) {
|
|
||||||
Preconditions.checkNotNull(location);
|
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
|
||||||
int y = location.getBlockY();
|
|
||||||
int z = location.getBlockZ();
|
|
||||||
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
|
||||||
BlockState state = adapt(blockData);
|
|
||||||
if (state == null) {
|
|
||||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
||||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
||||||
}
|
|
||||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
|
||||||
|
|
||||||
// Read the NBT data
|
|
||||||
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
|
|
||||||
if (blockEntity != null) {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
|
|
||||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.toBaseBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<SideEffect> getSupportedSideEffects() {
|
|
||||||
return SideEffectSet.defaults().getSideEffectsToApply();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 && wasAccessibleSinceLastSave(map)) {
|
|
||||||
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) {
|
|
||||||
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(
|
|
||||||
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM)
|
|
||||||
.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
|
||||||
baseItemStack.getAmount()
|
|
||||||
);
|
|
||||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
|
|
||||||
return CraftItemStack.asCraftMirror(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean generateTree(
|
|
||||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
|
||||||
org.bukkit.World bukkitWorld
|
|
||||||
) {
|
|
||||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
|
||||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
|
||||||
blockVector3 = blockVector3.add(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
|
||||||
}
|
|
||||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
final BlockVector3 finalBlockVector = blockVector3;
|
|
||||||
// Sync to main thread to ensure no clashes occur
|
|
||||||
Map<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 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()
|
|
||||||
.registryOrThrow(BIOME);
|
|
||||||
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()
|
|
||||||
.registryOrThrow(BIOME);
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
|
|
||||||
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
|
|
||||||
try {
|
|
||||||
return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException ignored) {
|
|
||||||
// fall-through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Papers new chunk system has no related replacement - therefor we assume true.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,286 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
|
||||||
|
|
||||||
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_R3.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.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.locX, levelChunk.locZ));
|
|
||||||
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,248 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
|
||||||
|
|
||||||
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_19_R3.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 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 net.minecraft.world.level.chunk.PalettedContainerRO;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
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 static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
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, PalettedContainerRO<Holder<Biome>> biomeData) {
|
|
||||||
if (biomes == null) {
|
|
||||||
biomes = new PalettedContainer[getSectionCount()];
|
|
||||||
}
|
|
||||||
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
|
|
||||||
biomes[layer] = palettedContainer.copy();
|
|
||||||
} else {
|
|
||||||
LOGGER.error(
|
|
||||||
"Cannot correctly save biomes to history. Expected class type {} but got {}",
|
|
||||||
PalettedContainer.class.getSimpleName(),
|
|
||||||
biomeData.getClass().getSimpleName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,34 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
|
||||||
|
|
||||||
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,723 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
|
||||||
|
|
||||||
import com.destroystokyo.paper.util.maplist.EntityList;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
|
||||||
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 io.papermc.paper.world.ChunkEntitySlices;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.IdMap;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.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.ExceptionCollector;
|
|
||||||
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.ChunkStatus;
|
|
||||||
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 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 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;
|
|
||||||
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
|
||||||
|
|
||||||
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 final boolean POST_CHUNK_REWRITE;
|
|
||||||
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
|
||||||
private static Field LEVEL_CHUNK_ENTITIES;
|
|
||||||
private static Field SERVER_LEVEL_ENTITY_MANAGER;
|
|
||||||
|
|
||||||
static {
|
|
||||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
||||||
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 = 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", "a"),
|
|
||||||
BlockEntity.class,
|
|
||||||
ServerLevel.class
|
|
||||||
);
|
|
||||||
removeGameEventListener.setAccessible(true);
|
|
||||||
methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener);
|
|
||||||
|
|
||||||
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
|
|
||||||
Refraction.pickName(
|
|
||||||
"removeBlockEntityTicker",
|
|
||||||
"l"
|
|
||||||
), BlockPos.class
|
|
||||||
);
|
|
||||||
removeBlockEntityTicker.setAccessible(true);
|
|
||||||
methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker);
|
|
||||||
|
|
||||||
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");
|
|
||||||
chunkRewrite = true;
|
|
||||||
PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities");
|
|
||||||
PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException ignored) {
|
|
||||||
chunkRewrite = false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Paper - Pre-Chunk-Update
|
|
||||||
LEVEL_CHUNK_ENTITIES = LevelChunk.class.getDeclaredField("entities");
|
|
||||||
LEVEL_CHUNK_ENTITIES.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Non-Paper
|
|
||||||
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "L"));
|
|
||||||
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
}
|
|
||||||
POST_CHUNK_REWRITE = chunkRewrite;
|
|
||||||
} catch (RuntimeException | Error e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 + "!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return (LevelChunk) chunk.getHandle(ChunkStatus.FULL);
|
|
||||||
} 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
|
|
||||||
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
|
||||||
.getChunkSource()
|
|
||||||
.addRegionTicket(TicketType.UNLOAD_COOLDOWN, 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);
|
|
||||||
LevelChunk levelChunk;
|
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
// getChunkAtIfLoadedImmediately is paper only
|
|
||||||
levelChunk = nmsWorld
|
|
||||||
.getChunkSource()
|
|
||||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
|
||||||
} else {
|
|
||||||
levelChunk = ((Optional<LevelChunk>) ((Either) chunkHolder
|
|
||||||
.getTickingChunkFuture() // method is not present with new paper chunk system
|
|
||||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left())
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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().registryOrThrow(BIOME);
|
|
||||||
if (biomeRegistry.getKey(biome.value()) == null) {
|
|
||||||
return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
|
||||||
try {
|
|
||||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
|
||||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
|
||||||
if (blockEntity != null) {
|
|
||||||
if (!levelChunk.level.isClientSide) {
|
|
||||||
methodRemoveGameEventListener.invoke(levelChunk, beacon, levelChunk.level);
|
|
||||||
}
|
|
||||||
fieldRemove.set(beacon, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
|
||||||
ExceptionCollector<RuntimeException> collector = new ExceptionCollector<>();
|
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
if (POST_CHUNK_REWRITE) {
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (List<Entity>) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ));
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk);
|
|
||||||
return List.of(entityList.getRawData());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
collector.add(new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=false]", e));
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return ((PersistentEntitySectionManager<Entity>) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e));
|
|
||||||
}
|
|
||||||
collector.throwIfPresent();
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
record FakeIdMapBlock(int size) implements IdMap<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,175 +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.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_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,28 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_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) {
|
|
||||||
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_19_R3.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,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)
|
|
||||||
}
|
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,93 +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_20_R1;
|
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.stats.Stat;
|
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.damagesource.DamageSource;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
|
||||||
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
class PaperweightFakePlayer extends ServerPlayer {
|
|
||||||
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]");
|
|
||||||
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
|
|
||||||
|
|
||||||
PaperweightFakePlayer(ServerLevel world) {
|
|
||||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3 position() {
|
|
||||||
return ORIGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void die(DamageSource damagesource) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OptionalInt openMenu(MenuProvider factory) {
|
|
||||||
return OptionalInt.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void displayClientMessage(Component message, boolean actionBar) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void awardStat(Stat<?> stat, int amount) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void awardStat(Stat<?> stat) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInvulnerableTo(DamageSource damageSource) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openTextEdit(SignBlockEntity sign, boolean front) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,181 +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_20_R1;
|
|
||||||
|
|
||||||
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;
|
|
||||||
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.server.level.FullChunkStatus;
|
|
||||||
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_20_R1.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.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(BlockPos position, 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(FullChunkStatus.BLOCK_TICKING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void markBlockChanged(LevelChunk chunk, BlockPos position) {
|
|
||||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
|
||||||
getWorld().getChunkSource().blockChanged(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
|
|
||||||
ServerLevel world = getWorld();
|
|
||||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
|
||||||
world.updateNeighborsAt(pos, oldState.getBlock());
|
|
||||||
} else {
|
|
||||||
// When we don't want events, manually run the physics without them.
|
|
||||||
Block block = oldState.getBlock();
|
|
||||||
fireNeighborChanged(pos, world, block, pos.west());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.east());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.below());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.above());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.north());
|
|
||||||
fireNeighborChanged(pos, world, block, pos.south());
|
|
||||||
}
|
|
||||||
if (newState.hasAnalogOutputSignal()) {
|
|
||||||
world.updateNeighbourForOutputSignal(pos, newState.getBlock());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not sure why neighborChanged is deprecated
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
|
||||||
world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) {
|
|
||||||
ServerLevel world = getWorld();
|
|
||||||
// a == updateNeighbors
|
|
||||||
// b == updateDiagonalNeighbors
|
|
||||||
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
|
||||||
CraftWorld craftWorld = world.getWorld();
|
|
||||||
BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState));
|
|
||||||
world.getCraftServer().getPluginManager().callEvent(event);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
|
|
||||||
getWorld().onBlockStateChange(pos, oldState, newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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_20_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.LiquidBlock;
|
|
||||||
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;
|
|
||||||
|
|
||||||
public class PaperweightBlockMaterial implements BlockMaterial {
|
|
||||||
|
|
||||||
private final Block block;
|
|
||||||
private final BlockState blockState;
|
|
||||||
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.craftBlockData = CraftBlockData.fromData(blockState);
|
|
||||||
this.craftMaterial = craftBlockData.getMaterial();
|
|
||||||
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block,
|
|
||||||
Refraction.pickName("properties", "aN"));
|
|
||||||
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
|
|
||||||
Refraction.pickName("canOcclude", "m")
|
|
||||||
);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAir() {
|
|
||||||
return blockState.isAir();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFullCube() {
|
|
||||||
return craftMaterial.isOccluding();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOpaque() {
|
|
||||||
return blockState.isOpaque();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPowerSource() {
|
|
||||||
return blockState.isSignalSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLiquid() {
|
|
||||||
// TODO: Better check ?
|
|
||||||
return block instanceof LiquidBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSolid() {
|
|
||||||
// TODO: Replace
|
|
||||||
return blockState.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 blockState.getPistonPushReaction() == PushReaction.DESTROY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isUnpushable() {
|
|
||||||
return blockState.getPistonPushReaction() == PushReaction.BLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTicksRandomly() {
|
|
||||||
return block.isRandomlyTicking(blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMovementBlocker() {
|
|
||||||
return craftMaterial.isSolid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBurnable() {
|
|
||||||
return craftMaterial.isBurnable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isToolRequired() {
|
|
||||||
// Removed in 1.16.1, this is not present in higher versions
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReplacedDuringPlacement() {
|
|
||||||
return blockState.canBeReplaced();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 block.defaultMapColor().col;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,652 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
|
||||||
|
|
||||||
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.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.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.BlockTypesCache;
|
|
||||||
import com.sk89q.worldedit.world.entity.EntityType;
|
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.WritableRegistry;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.dedicated.DedicatedServer;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.util.StringRepresentable;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.level.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 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_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.entity.Player;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|
||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
|
|
||||||
} catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final PaperweightAdapter parent;
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Code that may break between versions of Minecraft
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
|
|
||||||
private char[] ibdToStateOrdinal = null;
|
|
||||||
private int[] ordinalToIbdID = null;
|
|
||||||
private boolean initialised = false;
|
|
||||||
private Map<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 DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK)
|
|
||||||
.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public BlockState getBlock(Location location) {
|
|
||||||
Preconditions.checkNotNull(location);
|
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
|
||||||
int y = location.getBlockY();
|
|
||||||
int z = location.getBlockZ();
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
|
||||||
BlockState state = adapt(blockData);
|
|
||||||
if (state == null) {
|
|
||||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
||||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseBlock getFullBlock(final Location location) {
|
|
||||||
Preconditions.checkNotNull(location);
|
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
|
||||||
int y = location.getBlockY();
|
|
||||||
int z = location.getBlockZ();
|
|
||||||
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
|
||||||
BlockState state = adapt(blockData);
|
|
||||||
if (state == null) {
|
|
||||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
||||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
||||||
}
|
|
||||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
|
||||||
|
|
||||||
// Read the NBT data
|
|
||||||
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
|
|
||||||
if (blockEntity != null) {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
|
|
||||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.toBaseBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<SideEffect> getSupportedSideEffects() {
|
|
||||||
return SideEffectSet.defaults().getSideEffectsToApply();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 && wasAccessibleSinceLastSave(map)) {
|
|
||||||
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) {
|
|
||||||
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(
|
|
||||||
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM)
|
|
||||||
.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
|
||||||
baseItemStack.getAmount()
|
|
||||||
);
|
|
||||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
|
|
||||||
return CraftItemStack.asCraftMirror(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean generateTree(
|
|
||||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
|
||||||
org.bukkit.World bukkitWorld
|
|
||||||
) {
|
|
||||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
|
||||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
|
||||||
blockVector3 = blockVector3.add(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
|
||||||
}
|
|
||||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
final BlockVector3 finalBlockVector = blockVector3;
|
|
||||||
// Sync to main thread to ensure no clashes occur
|
|
||||||
Map<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 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()
|
|
||||||
.registryOrThrow(BIOME);
|
|
||||||
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()
|
|
||||||
.registryOrThrow(BIOME);
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
|
|
||||||
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
|
|
||||||
try {
|
|
||||||
return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException ignored) {
|
|
||||||
// fall-through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Papers new chunk system has no related replacement - therefor we assume true.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,287 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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.server.level.FullChunkStatus;
|
|
||||||
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.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.locX, levelChunk.locZ));
|
|
||||||
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(FullChunkStatus.BLOCK_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,248 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
|
||||||
|
|
||||||
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_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 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 net.minecraft.world.level.chunk.PalettedContainerRO;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
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 static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
||||||
|
|
||||||
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, PalettedContainerRO<Holder<Biome>> biomeData) {
|
|
||||||
if (biomes == null) {
|
|
||||||
biomes = new PalettedContainer[getSectionCount()];
|
|
||||||
}
|
|
||||||
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
|
|
||||||
biomes[layer] = palettedContainer.copy();
|
|
||||||
} else {
|
|
||||||
LOGGER.error(
|
|
||||||
"Cannot correctly save biomes to history. Expected class type {} but got {}",
|
|
||||||
PalettedContainer.class.getSimpleName(),
|
|
||||||
biomeData.getClass().getSimpleName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,34 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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,741 +0,0 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
|
||||||
|
|
||||||
import com.destroystokyo.paper.util.maplist.EntityList;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
|
||||||
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 io.papermc.paper.world.ChunkEntitySlices;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.IdMap;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.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.ExceptionCollector;
|
|
||||||
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.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
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 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 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;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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();
|
|
||||||
|
|
||||||
static final boolean POST_CHUNK_REWRITE;
|
|
||||||
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
|
||||||
private static Field LEVEL_CHUNK_ENTITIES;
|
|
||||||
private static Field SERVER_LEVEL_ENTITY_MANAGER;
|
|
||||||
|
|
||||||
static {
|
|
||||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
||||||
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", "g"));
|
|
||||||
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);
|
|
||||||
|
|
||||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
|
||||||
"getVisibleChunkIfPresent",
|
|
||||||
"b"
|
|
||||||
), long.class);
|
|
||||||
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);
|
|
||||||
|
|
||||||
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", "a"),
|
|
||||||
BlockEntity.class,
|
|
||||||
ServerLevel.class
|
|
||||||
);
|
|
||||||
removeGameEventListener.setAccessible(true);
|
|
||||||
methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener);
|
|
||||||
|
|
||||||
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
|
|
||||||
Refraction.pickName(
|
|
||||||
"removeBlockEntityTicker",
|
|
||||||
"l"
|
|
||||||
), BlockPos.class
|
|
||||||
);
|
|
||||||
removeBlockEntityTicker.setAccessible(true);
|
|
||||||
methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker);
|
|
||||||
|
|
||||||
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");
|
|
||||||
chunkRewrite = true;
|
|
||||||
PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities");
|
|
||||||
PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException ignored) {
|
|
||||||
chunkRewrite = false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Paper - Pre-Chunk-Update
|
|
||||||
LEVEL_CHUNK_ENTITIES = LevelChunk.class.getDeclaredField("entities");
|
|
||||||
LEVEL_CHUNK_ENTITIES.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Non-Paper
|
|
||||||
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "M"));
|
|
||||||
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
}
|
|
||||||
POST_CHUNK_REWRITE = chunkRewrite;
|
|
||||||
} 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(
|
|
||||||
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 + "!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
|
||||||
} 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
|
|
||||||
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
|
||||||
.getChunkSource()
|
|
||||||
.addRegionTicket(TicketType.UNLOAD_COOLDOWN, 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);
|
|
||||||
LevelChunk levelChunk;
|
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
// getChunkAtIfLoadedImmediately is paper only
|
|
||||||
levelChunk = nmsWorld
|
|
||||||
.getChunkSource()
|
|
||||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
|
||||||
} else {
|
|
||||||
levelChunk = ((Optional<LevelChunk>) ((Either) chunkHolder
|
|
||||||
.getTickingChunkFuture() // method is not present with new paper chunk system
|
|
||||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left())
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new LevelChunkSection(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(biomeRegistry);
|
|
||||||
}
|
|
||||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
|
|
||||||
Block.BLOCK_STATE_REGISTRY,
|
|
||||||
Blocks.AIR.defaultBlockState(),
|
|
||||||
PalettedContainer.Strategy.SECTION_STATES
|
|
||||||
);
|
|
||||||
return new LevelChunkSection(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
|
|
||||||
);
|
|
||||||
|
|
||||||
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().registryOrThrow(BIOME);
|
|
||||||
if (biomeRegistry.getKey(biome.value()) == null) {
|
|
||||||
return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
|
||||||
try {
|
|
||||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
|
||||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
|
||||||
if (blockEntity != null) {
|
|
||||||
if (!levelChunk.level.isClientSide) {
|
|
||||||
methodRemoveGameEventListener.invoke(levelChunk, beacon, levelChunk.level);
|
|
||||||
}
|
|
||||||
fieldRemove.set(beacon, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Entity> getEntities(LevelChunk chunk) {
|
|
||||||
ExceptionCollector<RuntimeException> collector = new ExceptionCollector<>();
|
|
||||||
if (PaperLib.isPaper()) {
|
|
||||||
if (POST_CHUNK_REWRITE) {
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (List<Entity>) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ));
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk);
|
|
||||||
return List.of(entityList.getRawData());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
collector.add(new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=false]", e));
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return ((PersistentEntitySectionManager<Entity>) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e));
|
|
||||||
}
|
|
||||||
collector.throwIfPresent();
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
record FakeIdMapBlock(int size) implements IdMap<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,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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ import io.papermc.paperweight.userdev.attribute.Obfuscation
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
alias(libs.plugins.minotaur)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
project.description = "Bukkit"
|
project.description = "Bukkit"
|
||||||
@ -32,10 +31,6 @@ repositories {
|
|||||||
name = "OSS Sonatype Snapshots"
|
name = "OSS Sonatype Snapshots"
|
||||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||||
}
|
}
|
||||||
maven {
|
|
||||||
name = "Glaremasters"
|
|
||||||
url = uri("https://repo.glaremasters.me/repository/towny/")
|
|
||||||
}
|
|
||||||
flatDir { dir(File("src/main/resources")) }
|
flatDir { dir(File("src/main/resources")) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,13 +46,7 @@ val adapters = configurations.create("adapters") {
|
|||||||
isCanBeResolved = true
|
isCanBeResolved = true
|
||||||
shouldResolveConsistentlyWith(configurations["runtimeClasspath"])
|
shouldResolveConsistentlyWith(configurations["runtimeClasspath"])
|
||||||
attributes {
|
attributes {
|
||||||
attribute(Obfuscation.OBFUSCATION_ATTRIBUTE,
|
attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, objects.named(Obfuscation.OBFUSCATED))
|
||||||
if ((project.findProperty("enginehub.obf.none") as String?).toBoolean()) {
|
|
||||||
objects.named(Obfuscation.NONE)
|
|
||||||
} else {
|
|
||||||
objects.named(Obfuscation.OBFUSCATED)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,19 +63,19 @@ dependencies {
|
|||||||
implementation(libs.fastutil)
|
implementation(libs.fastutil)
|
||||||
|
|
||||||
// Platform expectations
|
// Platform expectations
|
||||||
compileOnly(libs.paper) {
|
compileOnly("io.papermc.paper:paper-api") {
|
||||||
exclude("junit", "junit")
|
exclude("junit", "junit")
|
||||||
exclude(group = "org.slf4j", module = "slf4j-api")
|
exclude(group = "org.slf4j", module = "slf4j-api")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
localImplementation(libs.log4jApi)
|
localImplementation("org.apache.logging.log4j:log4j-api")
|
||||||
localImplementation(libs.log4jBom) {
|
localImplementation(libs.log4jBom) {
|
||||||
because("Spigot provides Log4J (sort of, not in API, implicitly part of server)")
|
because("Spigot provides Log4J (sort of, not in API, implicitly part of server)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
compileOnly(libs.vault) { isTransitive = false }
|
compileOnly("com.github.MilkBowl:VaultAPI") { isTransitive = false }
|
||||||
compileOnly(libs.dummypermscompat) {
|
compileOnly(libs.dummypermscompat) {
|
||||||
exclude("com.github.MilkBowl", "VaultAPI")
|
exclude("com.github.MilkBowl", "VaultAPI")
|
||||||
}
|
}
|
||||||
@ -99,28 +88,29 @@ dependencies {
|
|||||||
compileOnly(libs.mapmanager) { isTransitive = false }
|
compileOnly(libs.mapmanager) { isTransitive = false }
|
||||||
compileOnly(libs.griefprevention) { isTransitive = false }
|
compileOnly(libs.griefprevention) { isTransitive = false }
|
||||||
compileOnly(libs.griefdefender) { isTransitive = false }
|
compileOnly(libs.griefdefender) { isTransitive = false }
|
||||||
|
compileOnly(libs.mcore) { isTransitive = false }
|
||||||
compileOnly(libs.residence) { isTransitive = false }
|
compileOnly(libs.residence) { isTransitive = false }
|
||||||
compileOnly(libs.towny) { isTransitive = false }
|
compileOnly(libs.towny) { isTransitive = false }
|
||||||
compileOnly(libs.plotSquaredBukkit) { isTransitive = false }
|
compileOnly("com.plotsquared:PlotSquared-Bukkit") { isTransitive = false }
|
||||||
compileOnly(libs.plotSquaredCore) { isTransitive = false }
|
compileOnly("com.plotsquared:PlotSquared-Core") { isTransitive = false }
|
||||||
|
|
||||||
// Third party
|
// Third party
|
||||||
implementation(libs.paperlib)
|
implementation("io.papermc:paperlib")
|
||||||
implementation(libs.bstatsBukkit) { isTransitive = false }
|
implementation("org.bstats:bstats-bukkit") { isTransitive = false }
|
||||||
implementation(libs.bstatsBase) { isTransitive = false }
|
implementation(libs.bstatsBase) { isTransitive = false }
|
||||||
implementation(libs.serverlib)
|
implementation("dev.notmyfault.serverlib:ServerLib")
|
||||||
implementation(libs.paster) { isTransitive = false }
|
implementation("com.intellectualsites.paster:Paster") { isTransitive = false }
|
||||||
api(libs.lz4Java) { isTransitive = false }
|
api(libs.lz4Java) { isTransitive = false }
|
||||||
api(libs.sparsebitset) { isTransitive = false }
|
api(libs.sparsebitset) { isTransitive = false }
|
||||||
api(libs.parallelgzip) { isTransitive = false }
|
api(libs.parallelgzip) { isTransitive = false }
|
||||||
compileOnly(libs.adventureApi)
|
compileOnly("net.kyori:adventure-api")
|
||||||
compileOnlyApi(libs.checkerqual)
|
compileOnlyApi("org.checkerframework:checker-qual")
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
testImplementation(libs.mockito)
|
testImplementation(libs.mockito)
|
||||||
testImplementation(libs.adventureApi)
|
testImplementation("net.kyori:adventure-api")
|
||||||
testImplementation(libs.checkerqual)
|
testImplementation("org.checkerframework:checker-qual")
|
||||||
testImplementation(libs.paper) { isTransitive = true }
|
testImplementation("io.papermc.paper:paper-api") { isTransitive = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named<Copy>("processResources") {
|
tasks.named<Copy>("processResources") {
|
||||||
@ -183,7 +173,7 @@ tasks.named<ShadowJar>("shadowJar") {
|
|||||||
include(dependency("org.lz4:lz4-java:1.8.0"))
|
include(dependency("org.lz4:lz4-java:1.8.0"))
|
||||||
}
|
}
|
||||||
relocate("net.kyori", "com.fastasyncworldedit.core.adventure") {
|
relocate("net.kyori", "com.fastasyncworldedit.core.adventure") {
|
||||||
include(dependency("net.kyori:adventure-nbt:4.14.0"))
|
include(dependency("net.kyori:adventure-nbt:4.9.3"))
|
||||||
}
|
}
|
||||||
relocate("com.zaxxer", "com.fastasyncworldedit.core.math") {
|
relocate("com.zaxxer", "com.fastasyncworldedit.core.math") {
|
||||||
include(dependency("com.zaxxer:SparseBitSet:1.2"))
|
include(dependency("com.zaxxer:SparseBitSet:1.2"))
|
||||||
@ -197,20 +187,3 @@ tasks.named<ShadowJar>("shadowJar") {
|
|||||||
tasks.named("assemble").configure {
|
tasks.named("assemble").configure {
|
||||||
dependsOn("shadowJar")
|
dependsOn("shadowJar")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
|
||||||
modrinth {
|
|
||||||
token.set(System.getenv("MODRINTH_TOKEN"))
|
|
||||||
projectId.set("fastasyncworldedit")
|
|
||||||
versionName.set("${project.version}")
|
|
||||||
versionNumber.set("${project.version}")
|
|
||||||
versionType.set("release")
|
|
||||||
uploadFile.set(file("build/libs/${rootProject.name}-Bukkit-${project.version}.jar"))
|
|
||||||
gameVersions.addAll(listOf("1.20.1", "1.20", "1.19.4", "1.19.3", "1.19.2", "1.19.1", "1.19", "1.18.2", "1.17.1", "1.16" +
|
|
||||||
".5"))
|
|
||||||
loaders.addAll(listOf("paper", "spigot"))
|
|
||||||
changelog.set("The changelog is available on GitHub: https://github.com/IntellectualSites/" +
|
|
||||||
"FastAsyncWorldEdit/releases/tag/${project.version}")
|
|
||||||
syncBodyFrom.set(rootProject.file("README.md").readText())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -36,6 +36,10 @@ public class BukkitPermissionAttachmentManager {
|
|||||||
if (p == null) {
|
if (p == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (p.hasMetadata("NPC") && noopAttachment != null) {
|
||||||
|
p.removeAttachment(noopAttachment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
PermissionAttachment attach = attachments.remove(p);
|
PermissionAttachment attach = attachments.remove(p);
|
||||||
if (attach != null) {
|
if (attach != null) {
|
||||||
p.removeAttachment(attach);
|
p.removeAttachment(attach);
|
||||||
|
@ -108,14 +108,6 @@ public class FaweBukkit implements IFawe, Listener {
|
|||||||
if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) {
|
if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) {
|
||||||
LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?");
|
LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version.isEqualOrLowerThan(MinecraftVersion.ONE_DOT_SIXTEEN_EOL)) {
|
|
||||||
LOGGER.warn("You are running Minecraft 1.16.5. This version has been released over two years ago (January 2021).");
|
|
||||||
LOGGER.warn("FastAsyncWorldEdit will stop operating on this version in the near future.");
|
|
||||||
LOGGER.warn("Neither Mojang, nor Spigot or other software vendors support this version anymore." +
|
|
||||||
"Please update your server to a newer version of Minecraft (1.20+) to continue receiving updates and " +
|
|
||||||
"support.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -233,7 +225,7 @@ public class FaweBukkit implements IFawe, Listener {
|
|||||||
final Plugin residencePlugin = Bukkit.getServer().getPluginManager().getPlugin("Residence");
|
final Plugin residencePlugin = Bukkit.getServer().getPluginManager().getPlugin("Residence");
|
||||||
if (residencePlugin != null && residencePlugin.isEnabled()) {
|
if (residencePlugin != null && residencePlugin.isEnabled()) {
|
||||||
try {
|
try {
|
||||||
managers.add(new ResidenceFeature(residencePlugin));
|
managers.add(new ResidenceFeature(residencePlugin, this));
|
||||||
LOGGER.info("Attempting to use plugin 'Residence'");
|
LOGGER.info("Attempting to use plugin 'Residence'");
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
@ -325,11 +317,11 @@ public class FaweBukkit implements IFawe, Listener {
|
|||||||
if (plotSquared == null) {
|
if (plotSquared == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (PlotSquared.get().getVersion().version[0] == 7) {
|
if (PlotSquared.get().getVersion().version[0] == 6) {
|
||||||
WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature());
|
WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature());
|
||||||
LOGGER.info("Plugin 'PlotSquared' v7 found. Using it now.");
|
LOGGER.info("Plugin 'PlotSquared' v6 found. Using it now.");
|
||||||
} else {
|
} else {
|
||||||
LOGGER.error("Incompatible version of PlotSquared found. Please use PlotSquared v7.");
|
LOGGER.error("Incompatible version of PlotSquared found. Please use PlotSquared v6.");
|
||||||
LOGGER.info("https://www.spigotmc.org/resources/77506/");
|
LOGGER.info("https://www.spigotmc.org/resources/77506/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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