Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-09-06 08:02:50 +02:00
Merge branch 'main' of https://github.com/IntellectualSites/FastAsyncWorldEdit into main
Dieser Commit ist enthalten in:
Commit
fdeaa8ad41
4
.github/ISSUE_TEMPLATE/bug-report.md
vendored
4
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -24,7 +24,7 @@ assignees: ''
|
||||
**Required Information**
|
||||
- FAWE Version Number (`/version FastAsyncWorldEdit`):
|
||||
- Spigot/Paper Version Number (`/version`):
|
||||
- Minecraft Version: [e.g. 1.16.3]
|
||||
- Minecraft Version: [e.g. 1.16.4]
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
@ -43,5 +43,5 @@ Steps to reproduce the behavior:
|
||||
<!--- Make sure you've completed the following steps (put an "X" between of brackets): -->
|
||||
- [] I included all information required in the sections above
|
||||
- [] I made sure there are no duplicates of this report [(Use Search)](https://github.com/IntellectualSites/FastAsyncWorldEdit/issues?q=is%3Aissue)
|
||||
- [] I made sure I am using an up-to-date version of [FastAsyncWorldEdit for 1.16.3](https://ci.athion.net/job/FastAsyncWorldEdit-1.16/)
|
||||
- [] I made sure I am using an up-to-date version of [FastAsyncWorldEdit for 1.16.4](https://ci.athion.net/job/FastAsyncWorldEdit-1.16/)
|
||||
- [] I made sure the bug/error is not caused by any other plugin
|
||||
|
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: "[+] Enhancement"
|
||||
labels: "Enhancement"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
18
.github/stale.yml
vendored
Normale Datei
18
.github/stale.yml
vendored
Normale Datei
@ -0,0 +1,18 @@
|
||||
daysUntilStale: 60
|
||||
daysUntilClose: 7
|
||||
only: issues
|
||||
exemptLabels:
|
||||
- "Bug"
|
||||
- "Enhancement"
|
||||
- "Approved"
|
||||
- "Priority"
|
||||
- "Under investigation"
|
||||
staleLabel: "resolution: stale"
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
closeComment: >
|
||||
This issue has been automatically closed because it has not had activity in
|
||||
a long time. If the issue still applies to the most recent supported
|
||||
version, please open a new issue referencing this original issue.
|
50
.github/workflows/build.yml
vendored
Normale Datei
50
.github/workflows/build.yml
vendored
Normale Datei
@ -0,0 +1,50 @@
|
||||
name: "build"
|
||||
|
||||
on: ["pull_request", "push"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
java: ["1.8", "11"]
|
||||
os: ["ubuntu-18.04"]
|
||||
runs-on: "${{ matrix.os }}"
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: "actions/checkout@v2.3.4"
|
||||
- name: "Setup JDK ${{ matrix.java }}"
|
||||
uses: "actions/setup-java@v1.4.3"
|
||||
with:
|
||||
java-version: "${{ matrix.java }}"
|
||||
- name: "Cache Gradle"
|
||||
uses: "actions/cache@v2.1.3"
|
||||
with:
|
||||
path: |
|
||||
"~/.gradle/caches"
|
||||
"~/.gradle/wrapper"
|
||||
key: "${{ runner.os }}-${{ matrix.java }}-gradle-${{ hashFiles('**/*.gradle*') }}"
|
||||
restore-keys: |
|
||||
"${{ runner.os }}-${{ matrix.java }}-gradle-"
|
||||
- name: "Cache Local Maven Repository"
|
||||
uses: "actions/cache@v2.1.3"
|
||||
with:
|
||||
path: "~/.m2/repository"
|
||||
key: "${{ runner.os }}-${{ matrix.java }}-maven-${{ hashFiles('**/pom.xml') }}"
|
||||
restore-keys: |
|
||||
"${{ runner.os }}-${{ matrix.java }}-maven-"
|
||||
- name: "Cache BuildTools Decompiled Code"
|
||||
uses: "actions/cache@v2.1.3"
|
||||
with:
|
||||
path: "$GITHUB_WORKSPACE/work"
|
||||
key: "${{ runner.os }}-buildtools"
|
||||
restore-keys: |
|
||||
"${{ runner.os }}-buildtools"
|
||||
- name: "Test Enviornment"
|
||||
run: "echo $GITHUB_WORKSPACE"
|
||||
- name: "Download BuildTools"
|
||||
run: "wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar"
|
||||
- name: "Run BuildTools"
|
||||
run: "java -jar BuildTools.jar --rev 1.16.4"
|
||||
- name: "Clean Build"
|
||||
run: "./gradlew clean build sourcesJar javadocJar"
|
||||
|
56
.github/workflows/gradle.yml
vendored
56
.github/workflows/gradle.yml
vendored
@ -1,56 +0,0 @@
|
||||
name: Java CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [1.8, 1.11]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.2
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v1.4.2
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-${{ matrix.java }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.java }}-gradle-
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-${{ matrix.java }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.java }}-maven-
|
||||
- name: Cache BuildTools decompiled code
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: $GITHUB_WORKSPACE/work
|
||||
key: ${{ runner.os }}-buildtools
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildtools
|
||||
- name: Test enviornment
|
||||
run: echo $GITHUB_WORKSPACE
|
||||
- name: Download BuildTools
|
||||
run: wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar
|
||||
- name: Run BuildTools
|
||||
run: java -jar BuildTools.jar --rev 1.16.2
|
||||
- name: Test with Gradle
|
||||
run: ./gradlew clean build sourcesJar javadocJar
|
||||
|
12
.github/workflows/validate-gradle-wrapper.yml
vendored
Normale Datei
12
.github/workflows/validate-gradle-wrapper.yml
vendored
Normale Datei
@ -0,0 +1,12 @@
|
||||
name: "validate gradle wrapper"
|
||||
|
||||
on: ["pull_request", "push"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-18.04"
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: "actions/checkout@v2.3.4"
|
||||
- name: "Validate Gradle Wrapper"
|
||||
uses: "gradle/wrapper-validation-action@v1.0.3"
|
@ -25,6 +25,11 @@ fun Project.applyLibrariesConfiguration() {
|
||||
|
||||
group = "${rootProject.group}.worldedit-libs"
|
||||
|
||||
val relocations = mapOf(
|
||||
"net.kyori.text" to "com.sk89q.worldedit.util.formatting.text",
|
||||
"net.kyori.minecraft" to "com.sk89q.worldedit.util.kyori"
|
||||
)
|
||||
|
||||
tasks.register<ShadowJar>("jar") {
|
||||
configurations = listOf(project.configurations["shade"])
|
||||
archiveClassifier.set("")
|
||||
@ -36,13 +41,15 @@ fun Project.applyLibrariesConfiguration() {
|
||||
exclude(dependency("org.slf4j:slf4j-api"))
|
||||
}
|
||||
|
||||
relocate("net.kyori.text", "com.sk89q.worldedit.util.formatting.text")
|
||||
relocations.forEach { (from, to) ->
|
||||
relocate(from, to)
|
||||
}
|
||||
}
|
||||
val altConfigFiles = { artifactType: String ->
|
||||
val deps = configurations["shade"].incoming.dependencies
|
||||
.filterIsInstance<ModuleDependency>()
|
||||
.map { it.copy() }
|
||||
.map { dependency: ModuleDependency ->
|
||||
.map { dependency ->
|
||||
dependency.artifact {
|
||||
name = dependency.name
|
||||
type = artifactType
|
||||
@ -61,13 +68,15 @@ fun Project.applyLibrariesConfiguration() {
|
||||
from({
|
||||
altConfigFiles("sources")
|
||||
})
|
||||
val filePattern = Regex("(.*)net/kyori/text((?:/|$).*)")
|
||||
val textPattern = Regex("net\\.kyori\\.text")
|
||||
eachFile {
|
||||
filter {
|
||||
it.replaceFirst(textPattern, "com.sk89q.worldedit.util.formatting.text")
|
||||
relocations.forEach { (from, to) ->
|
||||
val filePattern = Regex("(.*)${from.replace('.', '/')}((?:/|$).*)")
|
||||
val textPattern = Regex.fromLiteral(from)
|
||||
eachFile {
|
||||
filter {
|
||||
it.replaceFirst(textPattern, to)
|
||||
}
|
||||
path = path.replaceFirst(filePattern, "$1${to.replace('.', '/')}$2")
|
||||
}
|
||||
path = path.replaceFirst(filePattern, "$1com/sk89q/worldedit/util/formatting/text$2")
|
||||
}
|
||||
archiveClassifier.set("sources")
|
||||
}
|
||||
|
@ -5,10 +5,9 @@ Example usage:
|
||||
/cs maze.js glowstone 10 10
|
||||
|
||||
You may or may not install these scripts -- it is optional. If you are, however,
|
||||
place the entire craftscripts/ folder into the respective directory for the platform
|
||||
place the entire `craftscripts/` folder into the respective directory for the platform
|
||||
that you have installed WorldEdit.
|
||||
|
||||
In order to be able to use CraftScripts, you must install the Rhino JavaScript library.
|
||||
The installation page linked above has information about that. More information
|
||||
about scripts in general can be found at
|
||||
https://worldedit.enginehub.org/en/latest/usage/other/craftscripts/
|
||||
about scripts in general can be found [on the WorldEdit docs](https://worldedit.enginehub.org/en/latest/usage/other/craftscripts/)
|
5
contrib/craftscripts/SUBMITTING.md
Normale Datei
5
contrib/craftscripts/SUBMITTING.md
Normale Datei
@ -0,0 +1,5 @@
|
||||
Write a cool script? You can submit a pull request to [our GitHub Repository](https://github.com/IntellectualSites/FastAsyncWorldEdit).
|
||||
We will consider your script for inclusion in the FastAsyncWorldEdit repository. CraftScripts in the FastAsyncWorldEdit repository are
|
||||
licensed under GPLv3, like the rest of FastAsybcWorldEdit.
|
||||
|
||||
You can also post your scripts on [our Discord](https://discord.gg/KxkjDVg) in the `#sharing-is-caring` channel.
|
@ -1,10 +0,0 @@
|
||||
Write a cool script? Send it to sk89q (somehow) or you can submit a pull
|
||||
request on https://github.com/sk89q/worldedit. He will consider your script
|
||||
for inclusion in WorldEdit releases. Please license your script with a
|
||||
permissive open source license such as GPLv2, MIT, BSD, WTFPL, etc.
|
||||
|
||||
Note: Legally you should not release things to the public domain as not
|
||||
all countries have the concept of public domain in their copyright law.
|
||||
|
||||
You can also post your scripts here:
|
||||
https://discord.gg/KxkjDVg or http://forum.sk89q.com/forums/craftscripts.6/
|
@ -39,16 +39,23 @@ dependencies {
|
||||
"api"(project(":worldedit-core"))
|
||||
"api"(project(":worldedit-libs:bukkit"))
|
||||
"compile"(":worldedit-adapters:")
|
||||
// Paper-patched NMS jars
|
||||
"compile"("com.destroystokyo.paperv1_15_r1:paperv1_15_r1:1_15_r1")
|
||||
"compile"("com.destroystokyo.paperv1_16_r1:paperv1_16_r1:1_16_r1")
|
||||
"compile"("com.destroystokyo.paperv1_16_r2:paperv1_16_r2:1_16_r2")
|
||||
"compile"("com.destroystokyo.paperv1_16_r3:paperv1_16_r3:1_16_r3")
|
||||
"compile"("org.spigotmcv1_15_r1:spigotmcv1_15_r1:1_15_r1")
|
||||
"compile"("org.spigotmcv1_16_r1:spigotmcv1_16_r1:1_16_r1")
|
||||
"compile"("org.spigotmcv1_16_r2:spigotmcv1_16_r2:1_16_r2")
|
||||
"compile"("org.spigotmcv1_16_r3:spigotmcv1_16_r3:1_16_r3")
|
||||
"implementation"("it.unimi.dsi:fastutil:${Versions.FAST_UTIL}")
|
||||
"api"("com.destroystokyo.paper:paper-api:1.16.2-R0.1-SNAPSHOT") {
|
||||
"api"("com.destroystokyo.paper:paper-api:1.16.4-R0.1-SNAPSHOT") {
|
||||
exclude("junit", "junit")
|
||||
isTransitive = false
|
||||
}
|
||||
"compileOnly"("org.jetbrains:annotations:20.1.0")
|
||||
"testCompileOnly"("org.jetbrains:annotations:20.1.0")
|
||||
"compileOnly"("org.spigotmc:spigot:1.16.2-R0.1-SNAPSHOT")
|
||||
"compileOnly"("org.spigotmc:spigot:1.16.4-R0.1-SNAPSHOT")
|
||||
"implementation"("io.papermc:paperlib:1.0.4")
|
||||
"compileOnly"("com.sk89q:dummypermscompat:1.10") {
|
||||
exclude("com.github.MilkBowl", "VaultAPI")
|
||||
|
@ -21,7 +21,7 @@ import com.boydti.fawe.bukkit.util.ItemUtil;
|
||||
import com.boydti.fawe.bukkit.util.image.BukkitImageViewer;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.regions.FaweMaskManager;
|
||||
import com.boydti.fawe.util.Jars;
|
||||
import com.boydti.fawe.util.ThirdPartyManager;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.boydti.fawe.util.WEManager;
|
||||
import com.boydti.fawe.util.image.ImageViewer;
|
||||
@ -123,7 +123,7 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
if (manager.getPlugin("PacketListenerApi") == null) {
|
||||
File output = new File(plugin.getDataFolder().getParentFile(),
|
||||
"PacketListenerAPI_v3.7.6-SNAPSHOT.jar");
|
||||
byte[] jarData = Jars.PL_v3_7_6.download();
|
||||
byte[] jarData = ThirdPartyManager.PacketListenerAPI.download();
|
||||
try (FileOutputStream fos = new FileOutputStream(output)) {
|
||||
fos.write(jarData);
|
||||
}
|
||||
@ -131,7 +131,7 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
if (manager.getPlugin("MapManager") == null) {
|
||||
File output = new File(plugin.getDataFolder().getParentFile(),
|
||||
"MapManager_v1.7.8-SNAPSHOT.jar");
|
||||
byte[] jarData = Jars.MM_v1_7_8.download();
|
||||
byte[] jarData = ThirdPartyManager.MapManager.download();
|
||||
try (FileOutputStream fos = new FileOutputStream(output)) {
|
||||
fos.write(jarData);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.destroystokyo.paper.util.misc.PooledLinkedHashSets;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
@ -25,6 +26,7 @@ import net.minecraft.server.v1_16_R2.DataBits;
|
||||
import net.minecraft.server.v1_16_R2.DataPalette;
|
||||
import net.minecraft.server.v1_16_R2.DataPaletteBlock;
|
||||
import net.minecraft.server.v1_16_R2.DataPaletteLinear;
|
||||
import net.minecraft.server.v1_16_R2.EntityPlayer;
|
||||
import net.minecraft.server.v1_16_R2.GameProfileSerializer;
|
||||
import net.minecraft.server.v1_16_R2.IBlockData;
|
||||
import net.minecraft.server.v1_16_R2.PacketPlayOutLightUpdate;
|
||||
@ -205,14 +207,40 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
|
||||
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
|
||||
p.playerConnection.sendPacket(chunkpacket);
|
||||
});
|
||||
}
|
||||
|
||||
if (lighting) {
|
||||
boolean trustEdges = true; //This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
|
||||
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
|
||||
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
|
||||
p.playerConnection.sendPacket(packet);
|
||||
});
|
||||
if (lighting) {
|
||||
boolean trustEdges = true; //This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
|
||||
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
|
||||
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
|
||||
p.playerConnection.sendPacket(packet);
|
||||
});
|
||||
}
|
||||
} else if (PaperLib.isPaper()) {
|
||||
//Require generic here to work with multiple dependencies trying to take control.
|
||||
PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<?> objects =
|
||||
nmsWorld.getChunkProvider().playerChunkMap.playerViewDistanceNoTickMap.getObjectsInRange(chunkX, chunkZ);
|
||||
if (objects == null) {
|
||||
return null;
|
||||
}
|
||||
for (Object obj : objects.getBackingSet()) {
|
||||
if (obj == null) {
|
||||
continue;
|
||||
}
|
||||
EntityPlayer p = (EntityPlayer) obj;
|
||||
Chunk chunk = nmsWorld.getChunkProvider().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
if (chunk != null) {
|
||||
PacketPlayOutMapChunk chunkpacket = new PacketPlayOutMapChunk(chunk, 65535);
|
||||
p.playerConnection.sendPacket(chunkpacket);
|
||||
|
||||
if (lighting) {
|
||||
boolean trustEdges =
|
||||
true; //This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
|
||||
PacketPlayOutLightUpdate packet =
|
||||
new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
|
||||
p.playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
@ -0,0 +1,160 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_16_4;
|
||||
|
||||
import com.sk89q.util.ReflectionUtil;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.server.v1_16_R3.Block;
|
||||
import net.minecraft.server.v1_16_R3.BlockAccessAir;
|
||||
import net.minecraft.server.v1_16_R3.BlockBase;
|
||||
import net.minecraft.server.v1_16_R3.BlockPosition;
|
||||
import net.minecraft.server.v1_16_R3.EnumPistonReaction;
|
||||
import net.minecraft.server.v1_16_R3.IBlockData;
|
||||
import net.minecraft.server.v1_16_R3.ITileEntity;
|
||||
import net.minecraft.server.v1_16_R3.Material;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.block.data.CraftBlockData;
|
||||
|
||||
public class BlockMaterial_1_16_4 implements BlockMaterial {
|
||||
private final Block block;
|
||||
private final IBlockData defaultState;
|
||||
private final Material material;
|
||||
private final boolean isTranslucent;
|
||||
private final CraftBlockData craftBlockData;
|
||||
private final org.bukkit.Material craftMaterial;
|
||||
private final int opacity;
|
||||
|
||||
public BlockMaterial_1_16_4(Block block) {
|
||||
this(block, block.getBlockData());
|
||||
}
|
||||
|
||||
public BlockMaterial_1_16_4(Block block, IBlockData defaultState) {
|
||||
this.block = block;
|
||||
this.defaultState = defaultState;
|
||||
this.material = defaultState.getMaterial();
|
||||
this.craftBlockData = CraftBlockData.fromData(defaultState);
|
||||
this.craftMaterial = craftBlockData.getMaterial();
|
||||
BlockBase.Info blockInfo = ReflectionUtil.getField(Block.class, block, "aB");
|
||||
this.isTranslucent = !(boolean)ReflectionUtil.getField(BlockBase.Info.class, blockInfo, "n");
|
||||
opacity = defaultState.b(BlockAccessAir.INSTANCE, BlockPosition.ZERO);
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public IBlockData getState() {
|
||||
return defaultState;
|
||||
}
|
||||
|
||||
public CraftBlockData getCraftBlockData() {
|
||||
return craftBlockData;
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAir() {
|
||||
return defaultState.isAir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube() {
|
||||
return craftMaterial.isOccluding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return material.f();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSource() {
|
||||
return defaultState.isPowerSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLiquid() {
|
||||
return material.isLiquid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid() {
|
||||
return material.isBuildable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHardness() {
|
||||
return craftBlockData.getState().strength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getResistance() {
|
||||
return block.getDurability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSlipperiness() {
|
||||
return block.getFrictionFactor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightValue() {
|
||||
return defaultState.f();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightOpacity() {
|
||||
return opacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFragileWhenPushed() {
|
||||
return material.getPushReaction() == EnumPistonReaction.DESTROY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnpushable() {
|
||||
return material.getPushReaction() == EnumPistonReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTicksRandomly() {
|
||||
return block.isTicking(defaultState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMovementBlocker() {
|
||||
return material.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBurnable() {
|
||||
return material.isBurnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToolRequired() {
|
||||
//TODO Removed in 1.16.1 Replacement not found.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplacedDuringPlacement() {
|
||||
return material.isReplaceable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTranslucent() {
|
||||
return isTranslucent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContainer() {
|
||||
return block instanceof ITileEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMapColor() {
|
||||
return material.h().rgb;
|
||||
}
|
||||
}
|
@ -0,0 +1,354 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_16_4;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.bukkit.adapter.DelegateLock;
|
||||
import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.destroystokyo.paper.util.misc.PooledLinkedHashSets;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.jpountz.util.UnsafeUtils;
|
||||
import net.minecraft.server.v1_16_R3.BiomeBase;
|
||||
import net.minecraft.server.v1_16_R3.BiomeStorage;
|
||||
import net.minecraft.server.v1_16_R3.Block;
|
||||
import net.minecraft.server.v1_16_R3.Chunk;
|
||||
import net.minecraft.server.v1_16_R3.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_16_R3.ChunkSection;
|
||||
import net.minecraft.server.v1_16_R3.DataBits;
|
||||
import net.minecraft.server.v1_16_R3.DataPalette;
|
||||
import net.minecraft.server.v1_16_R3.DataPaletteBlock;
|
||||
import net.minecraft.server.v1_16_R3.DataPaletteLinear;
|
||||
import net.minecraft.server.v1_16_R3.EntityPlayer;
|
||||
import net.minecraft.server.v1_16_R3.GameProfileSerializer;
|
||||
import net.minecraft.server.v1_16_R3.IBlockData;
|
||||
import net.minecraft.server.v1_16_R3.PacketPlayOutLightUpdate;
|
||||
import net.minecraft.server.v1_16_R3.PacketPlayOutMapChunk;
|
||||
import net.minecraft.server.v1_16_R3.PlayerChunk;
|
||||
import net.minecraft.server.v1_16_R3.PlayerChunkMap;
|
||||
import net.minecraft.server.v1_16_R3.World;
|
||||
import net.minecraft.server.v1_16_R3.WorldServer;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class BukkitAdapter_1_16_4 extends NMSAdapter {
|
||||
/*
|
||||
NMS fields
|
||||
*/
|
||||
public static final Field fieldBits;
|
||||
public static final Field fieldPalette;
|
||||
public static final Field fieldSize;
|
||||
|
||||
public static final Field fieldBitsPerEntry;
|
||||
|
||||
public static final Field fieldFluidCount;
|
||||
public static final Field fieldTickingBlockCount;
|
||||
public static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final Field fieldDirty;
|
||||
private static final Field fieldDirtyBlocks;
|
||||
|
||||
private static final Field fieldBiomeArray;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldLock;
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldSize = DataPaletteBlock.class.getDeclaredField("i");
|
||||
fieldSize.setAccessible(true);
|
||||
fieldBits = DataPaletteBlock.class.getDeclaredField("a");
|
||||
fieldBits.setAccessible(true);
|
||||
fieldPalette = DataPaletteBlock.class.getDeclaredField("h");
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldBitsPerEntry = DataBits.class.getDeclaredField("c");
|
||||
fieldBitsPerEntry.setAccessible(true);
|
||||
|
||||
fieldFluidCount = ChunkSection.class.getDeclaredField("e");
|
||||
fieldFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount");
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
fieldDirty = PlayerChunk.class.getDeclaredField("p");
|
||||
fieldDirty.setAccessible(true);
|
||||
fieldDirtyBlocks = PlayerChunk.class.getDeclaredField("dirtyBlocks");
|
||||
fieldDirtyBlocks.setAccessible(true);
|
||||
|
||||
fieldBiomeArray = BiomeStorage.class.getDeclaredField("h");
|
||||
fieldBiomeArray.setAccessible(true);
|
||||
|
||||
Method declaredGetVisibleChunk = PlayerChunkMap.class.getDeclaredMethod("getVisibleChunk", long.class);
|
||||
declaredGetVisibleChunk.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
|
||||
|
||||
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
|
||||
ReflectionUtils.setAccessibleNonFinal(tmp);
|
||||
fieldLock = tmp;
|
||||
fieldLock.setAccessible(true);
|
||||
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
|
||||
Class<?> clsShortArraySet;
|
||||
try { //paper
|
||||
clsShortArraySet = Class.forName(new String(new char[]{'i', 't', '.', 'u', 'n', 'i', 'm', 'i', '.', 'd', 's', 'i', '.', 'f', 'a', 's', 't', 'u', 't', 'i', 'l', '.', 's', 'h', 'o', 'r', 't', 's', '.', 'S', 'h', 'o', 'r', 't', 'A', 'r', 'r', 'a', 'y', 'S', 'e', 't'}));
|
||||
} catch (Throwable t) { // still using spigot boo
|
||||
clsShortArraySet = Class.forName(new String(new char[]{'o', 'r', 'g', '.', 'b', 'u', 'k', 'k', 'i', 't', '.', 'c', 'r', 'a', 'f', 't', 'b', 'u', 'k', 'k', 'i', 't', '.', 'l', 'i', 'b', 's', '.', 'i', 't', '.', 'u', 'n', 'i', 'm', 'i', '.', 'd', 's', 'i', '.', 'f', 'a', 's', 't', 'u', 't', 'i', 'l', '.', 's', 'h', 'o', 'r', 't', 's', '.', 'S', 'h', 'o', 'r', 't', 'A', 'r', 'r', 'a', 'y', 'S', 'e', 't'}));
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable rethrow) {
|
||||
rethrow.printStackTrace();
|
||||
throw new RuntimeException(rethrow);
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean setSectionAtomic(ChunkSection[] sections, ChunkSection expected, ChunkSection value, int layer) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return UnsafeUtils.getUNSAFE().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static DelegateLock applyLock(ChunkSection section) {
|
||||
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
|
||||
try {
|
||||
synchronized (section) {
|
||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
ReentrantLock currentLock = (ReentrantLock) fieldLock.get(blocks);
|
||||
if (currentLock instanceof DelegateLock) {
|
||||
return (DelegateLock) currentLock;
|
||||
}
|
||||
DelegateLock newLock = new DelegateLock(currentLock);
|
||||
fieldLock.set(blocks, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Chunk ensureLoaded(World nmsWorld, int chunkX, int chunkZ) {
|
||||
Chunk nmsChunk = nmsWorld.getChunkProvider().getChunkAt(chunkX, chunkZ, false);
|
||||
if (nmsChunk != null) {
|
||||
return nmsChunk;
|
||||
}
|
||||
if (Fawe.isMainThread()) {
|
||||
return nmsWorld.getChunkAt(chunkX, chunkZ);
|
||||
}
|
||||
if (PaperLib.isPaper()) {
|
||||
CraftWorld craftWorld = nmsWorld.getWorld();
|
||||
CompletableFuture<org.bukkit.Chunk> future = craftWorld.getChunkAtAsync(chunkX, chunkZ, true);
|
||||
try {
|
||||
CraftChunk chunk = (CraftChunk) future.get();
|
||||
return chunk.getHandle();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// TODO optimize
|
||||
return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
public static PlayerChunk getPlayerChunk(WorldServer nmsWorld, final int chunkX, final int chunkZ) {
|
||||
PlayerChunkMap chunkMap = nmsWorld.getChunkProvider().playerChunkMap;
|
||||
try {
|
||||
return (PlayerChunk) methodGetVisibleChunk.invoke(chunkMap, ChunkCoordIntPair.pair(chunkX, chunkZ));
|
||||
} catch (Throwable thr) {
|
||||
throw new RuntimeException(thr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendChunk(WorldServer nmsWorld, int chunkX, int chunkZ, int mask, boolean lighting) {
|
||||
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||
if (playerChunk == null) {
|
||||
return;
|
||||
}
|
||||
if (playerChunk.hasBeenLoaded()) {
|
||||
TaskManager.IMP.sync(() -> {
|
||||
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
|
||||
Optional<Chunk> optional = ((Either) playerChunk.a().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
|
||||
if (optional.isPresent()) {
|
||||
PacketPlayOutMapChunk chunkpacket = new PacketPlayOutMapChunk(optional.get(), 65535);
|
||||
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
|
||||
p.playerConnection.sendPacket(chunkpacket);
|
||||
});
|
||||
|
||||
if (lighting) {
|
||||
boolean trustEdges = true; //This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
|
||||
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
|
||||
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
|
||||
p.playerConnection.sendPacket(packet);
|
||||
});
|
||||
}
|
||||
} else if (PaperLib.isPaper()) {
|
||||
//Require generic here to work with multiple dependencies trying to take control.
|
||||
PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<?> objects =
|
||||
nmsWorld.getChunkProvider().playerChunkMap.playerViewDistanceNoTickMap.getObjectsInRange(chunkX, chunkZ);
|
||||
if (objects == null) {
|
||||
return null;
|
||||
}
|
||||
for (Object obj : objects.getBackingSet()) {
|
||||
if (obj == null) {
|
||||
continue;
|
||||
}
|
||||
EntityPlayer p = (EntityPlayer) obj;
|
||||
Chunk chunk = nmsWorld.getChunkProvider().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
if (chunk != null) {
|
||||
PacketPlayOutMapChunk chunkpacket = new PacketPlayOutMapChunk(chunk, 65535);
|
||||
p.playerConnection.sendPacket(chunkpacket);
|
||||
|
||||
if (lighting) {
|
||||
boolean trustEdges =
|
||||
true; //This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
|
||||
PacketPlayOutLightUpdate packet =
|
||||
new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
|
||||
p.playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
NMS conversion
|
||||
*/
|
||||
public static ChunkSection newChunkSection(final int layer, final char[] blocks, boolean fastmode) {
|
||||
return newChunkSection(layer, null, blocks, fastmode);
|
||||
}
|
||||
|
||||
public static ChunkSection newChunkSection(final int layer, final Function<Integer, char[]> get, char[] set, boolean fastmode) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get();
|
||||
final long[] blockStates = FaweCache.IMP.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int[] num_palette_buffer = new int[1];
|
||||
Map<BlockVector3, Integer> ticking_blocks = new HashMap<>();
|
||||
int air;
|
||||
if (get == null) {
|
||||
air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer,
|
||||
set, ticking_blocks, fastmode);
|
||||
} else {
|
||||
air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy,
|
||||
num_palette_buffer, get, set, ticking_blocks, fastmode);
|
||||
}
|
||||
int num_palette = num_palette_buffer[0];
|
||||
// BlockStates
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
|
||||
} else {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
|
||||
}
|
||||
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry);
|
||||
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
|
||||
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) {
|
||||
blockStates[i] = 0;
|
||||
}
|
||||
} else {
|
||||
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, 4096, blockStates);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
ChunkSection section = newChunkSection(layer);
|
||||
// set palette & data bits
|
||||
final DataPaletteBlock<IBlockData> dataPaletteBlocks = section.getBlocks();
|
||||
// private DataPalette<T> h;
|
||||
// protected DataBits a;
|
||||
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
|
||||
final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits);
|
||||
final DataPalette<IBlockData> palette;
|
||||
palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::c);
|
||||
|
||||
// set palette
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
final int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
final BlockState state = BlockTypesCache.states[ordinal];
|
||||
final IBlockData ibd = ((BlockMaterial_1_16_4) state.getMaterial()).getState();
|
||||
palette.a(ibd);
|
||||
}
|
||||
try {
|
||||
fieldBits.set(dataPaletteBlocks, nmsBits);
|
||||
fieldPalette.set(dataPaletteBlocks, palette);
|
||||
fieldSize.set(dataPaletteBlocks, bitsPerEntry);
|
||||
setCount(ticking_blocks.size(), 4096 - air, section);
|
||||
if (!fastmode) {
|
||||
ticking_blocks.forEach((pos, ordinal) -> section
|
||||
.setType(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(),
|
||||
Block.getByCombinedId(ordinal)));
|
||||
}
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return section;
|
||||
} catch (final Throwable e) {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static ChunkSection newChunkSection(int layer) {
|
||||
return new ChunkSection(layer << 4);
|
||||
}
|
||||
|
||||
public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws IllegalAccessException {
|
||||
fieldFluidCount.setShort(section, (short) 0); // TODO FIXME
|
||||
fieldTickingBlockCount.setShort(section, (short) tickingBlockCount);
|
||||
fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount);
|
||||
}
|
||||
|
||||
public static BiomeBase[] getBiomeArray(BiomeStorage storage) {
|
||||
try {
|
||||
return (BiomeBase[]) fieldBiomeArray.get(storage);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,855 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_16_4;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.IChunkSet;
|
||||
import com.boydti.fawe.beta.implementation.blocks.CharBlocks;
|
||||
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
|
||||
import com.boydti.fawe.beta.implementation.lighting.HeightMapType;
|
||||
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
|
||||
import com.boydti.fawe.bukkit.adapter.DelegateLock;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.nbt.LazyCompoundTag_1_16_4;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.collection.AdaptedMap;
|
||||
import com.boydti.fawe.object.collection.BitArrayUnstretched;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R3;
|
||||
import com.sk89q.worldedit.internal.Constants;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import net.minecraft.server.v1_16_R3.BiomeBase;
|
||||
import net.minecraft.server.v1_16_R3.BiomeStorage;
|
||||
import net.minecraft.server.v1_16_R3.BlockPosition;
|
||||
import net.minecraft.server.v1_16_R3.Chunk;
|
||||
import net.minecraft.server.v1_16_R3.ChunkSection;
|
||||
import net.minecraft.server.v1_16_R3.DataBits;
|
||||
import net.minecraft.server.v1_16_R3.DataPalette;
|
||||
import net.minecraft.server.v1_16_R3.DataPaletteBlock;
|
||||
import net.minecraft.server.v1_16_R3.DataPaletteHash;
|
||||
import net.minecraft.server.v1_16_R3.DataPaletteLinear;
|
||||
import net.minecraft.server.v1_16_R3.Entity;
|
||||
import net.minecraft.server.v1_16_R3.EntityTypes;
|
||||
import net.minecraft.server.v1_16_R3.EnumSkyBlock;
|
||||
import net.minecraft.server.v1_16_R3.HeightMap;
|
||||
import net.minecraft.server.v1_16_R3.IBlockData;
|
||||
import net.minecraft.server.v1_16_R3.IRegistry;
|
||||
import net.minecraft.server.v1_16_R3.LightEngine;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagInt;
|
||||
import net.minecraft.server.v1_16_R3.NibbleArray;
|
||||
import net.minecraft.server.v1_16_R3.SectionPosition;
|
||||
import net.minecraft.server.v1_16_R3.TileEntity;
|
||||
import net.minecraft.server.v1_16_R3.WorldServer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.block.CraftBlock;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
public class BukkitGetBlocks_1_16_4 extends CharGetBlocks {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_4.class);
|
||||
|
||||
private static final Function<BlockPosition, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
|
||||
private static final Function<TileEntity, CompoundTag> nmsTile2We = tileEntity -> new LazyCompoundTag_1_16_4(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound())));
|
||||
public ChunkSection[] sections;
|
||||
public Chunk nmsChunk;
|
||||
public WorldServer world;
|
||||
public int chunkX;
|
||||
public int chunkZ;
|
||||
public NibbleArray[] blockLight = new NibbleArray[16];
|
||||
public NibbleArray[] skyLight = new NibbleArray[16];
|
||||
private boolean createCopy = false;
|
||||
private BukkitGetBlocks_1_16_4_Copy copy = null;
|
||||
|
||||
public BukkitGetBlocks_1_16_4(World world, int chunkX, int chunkZ) {
|
||||
this(((CraftWorld) world).getHandle(), chunkX, chunkZ);
|
||||
}
|
||||
|
||||
public BukkitGetBlocks_1_16_4(WorldServer world, int chunkX, int chunkZ) {
|
||||
this.world = world;
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
return chunkX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
this.createCopy = createCopy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCreateCopy() {
|
||||
return createCopy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkGet getCopy() {
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int getChunkZ() {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
BiomeStorage index = getChunk().getBiomeIndex();
|
||||
BiomeBase base = null;
|
||||
if (y == -1) {
|
||||
for (y = 0; y < FaweCache.IMP.WORLD_HEIGHT; y++) {
|
||||
base = index.getBiome(x >> 2, y >> 2, z >> 2);
|
||||
if (base != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
base = index.getBiome(x >> 2, y >> 2, z >> 2);
|
||||
}
|
||||
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + (
|
||||
chunkX << 4), y, (z & 15) + (
|
||||
chunkZ << 4)));
|
||||
if (tileEntity == null) {
|
||||
return null;
|
||||
}
|
||||
return new LazyCompoundTag_1_16_4(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
Map<BlockPosition, TileEntity> nmsTiles = getChunk().getTileEntities();
|
||||
if (nmsTiles.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
int layer = y >> 4;
|
||||
if (skyLight[layer] == null) {
|
||||
SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer);
|
||||
NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPosition);
|
||||
// If the server hasn't generated the section's NibbleArray yet, it will be null
|
||||
if (nibbleArray == null) {
|
||||
byte[] a = new byte[2048];
|
||||
// Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway.
|
||||
Arrays.fill(a, (byte) 15);
|
||||
nibbleArray = new NibbleArray(a);
|
||||
((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.SKY, sectionPosition, nibbleArray, true);
|
||||
}
|
||||
skyLight[layer] = nibbleArray;
|
||||
}
|
||||
long l = BlockPosition.a(x, y, z);
|
||||
return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmmittedLight(int x, int y, int z) {
|
||||
int layer = y >> 4;
|
||||
if (blockLight[layer] == null) {
|
||||
SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer);
|
||||
NibbleArray nibbleArray = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition);
|
||||
// If the server hasn't generated the section's NibbleArray yet, it will be null
|
||||
if (nibbleArray == null) {
|
||||
byte[] a = new byte[2048];
|
||||
// Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway.
|
||||
Arrays.fill(a, (byte) 15);
|
||||
nibbleArray = new NibbleArray(a);
|
||||
((LightEngine) world.getChunkProvider().getLightEngine()).a(EnumSkyBlock.BLOCK, sectionPosition, nibbleArray, true);
|
||||
}
|
||||
blockLight[layer] = nibbleArray;
|
||||
}
|
||||
long l = BlockPosition.a(x, y, z);
|
||||
return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getHeightMap(HeightMapType type) {
|
||||
long[] longArray = getChunk().heightMap.get(HeightMap.Type.valueOf(type.name())).a();
|
||||
BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256, longArray);
|
||||
return bitArray.toRaw(new int[256]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
Entity entity = world.getEntity(uuid);
|
||||
if (entity != null) {
|
||||
org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity();
|
||||
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData();
|
||||
}
|
||||
for (List<Entity> entry : getChunk().getEntitySlices()) {
|
||||
if (entry != null) {
|
||||
for (Entity ent : entry) {
|
||||
if (uuid.equals(ent.getUniqueID())) {
|
||||
org.bukkit.entity.Entity bukkitEnt = ent.getBukkitEntity();
|
||||
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
List<Entity>[] slices = getChunk().getEntitySlices();
|
||||
int size = 0;
|
||||
for (List<Entity> slice : slices) {
|
||||
if (slice != null) {
|
||||
size += slice.size();
|
||||
}
|
||||
}
|
||||
if (slices.length == 0) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
int finalSize = size;
|
||||
return new AbstractSet<CompoundTag>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object get) {
|
||||
if (!(get instanceof CompoundTag)) {
|
||||
return false;
|
||||
}
|
||||
CompoundTag getTag = (CompoundTag) get;
|
||||
Map<String, Tag> value = getTag.getValue();
|
||||
CompoundTag getParts = (CompoundTag) value.get("UUID");
|
||||
UUID getUUID = new UUID(getParts.getLong("Most"), getParts.getLong("Least"));
|
||||
for (List<Entity> slice : slices) {
|
||||
if (slice != null) {
|
||||
for (Entity entity : slice) {
|
||||
UUID uuid = entity.getUniqueID();
|
||||
if (uuid.equals(getUUID)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<CompoundTag> iterator() {
|
||||
Iterable<CompoundTag> result = Iterables.transform(Iterables.concat(slices), new com.google.common.base.Function<Entity, CompoundTag>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public CompoundTag apply(@Nullable Entity input) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
return (CompoundTag) adapter.toNative(input.save(tag));
|
||||
}
|
||||
});
|
||||
return result.iterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void updateGet(BukkitGetBlocks_1_16_4 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) {
|
||||
synchronized (get) {
|
||||
if (this.nmsChunk != nmsChunk) {
|
||||
this.nmsChunk = nmsChunk;
|
||||
this.sections = sections.clone();
|
||||
this.reset();
|
||||
}
|
||||
if (this.sections == null) {
|
||||
this.sections = sections.clone();
|
||||
}
|
||||
if (this.sections[layer] != section) {
|
||||
this.sections[layer] = new ChunkSection[]{section}.clone()[0];
|
||||
}
|
||||
this.blocks[layer] = arr;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeEntity(Entity entity) {
|
||||
entity.die();
|
||||
}
|
||||
|
||||
public Chunk ensureLoaded(net.minecraft.server.v1_16_R3.World nmsWorld, int chunkX, int chunkZ) {
|
||||
return BukkitAdapter_1_16_4.ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
|
||||
copy = createCopy ? new BukkitGetBlocks_1_16_4_Copy(world, getChunkX(), getChunkZ()) : null;
|
||||
try {
|
||||
WorldServer nmsWorld = world;
|
||||
Chunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||
boolean fastmode = set.isFastMode() && Settings.IMP.QUEUE.NO_TICK_FASTMODE;
|
||||
|
||||
// Remove existing tiles
|
||||
{
|
||||
// Create a copy so that we can remove blocks
|
||||
Map<BlockPosition, TileEntity> tiles = new HashMap<>(nmsChunk.getTileEntities());
|
||||
if (!tiles.isEmpty()) {
|
||||
for (Map.Entry<BlockPosition, TileEntity> entry : tiles.entrySet()) {
|
||||
final BlockPosition pos = entry.getKey();
|
||||
final int lx = pos.getX() & 15;
|
||||
final int ly = pos.getY();
|
||||
final int lz = pos.getZ() & 15;
|
||||
final int layer = ly >> 4;
|
||||
if (!set.hasSection(layer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
|
||||
if (ordinal != 0) {
|
||||
TileEntity tile = entry.getValue();
|
||||
nmsChunk.removeTileEntity(tile.getPosition());
|
||||
if (createCopy) {
|
||||
copy.storeTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int bitMask = 0;
|
||||
synchronized (nmsChunk) {
|
||||
ChunkSection[] sections = nmsChunk.getSections();
|
||||
|
||||
for (int layer = 0; layer < 16; layer++) {
|
||||
if (!set.hasSection(layer)) {
|
||||
continue;
|
||||
}
|
||||
if (createCopy) {
|
||||
copy.storeSection(layer);
|
||||
}
|
||||
|
||||
bitMask |= 1 << layer;
|
||||
|
||||
char[] setArr = set.load(layer);
|
||||
ChunkSection newSection;
|
||||
ChunkSection existingSection = sections[layer];
|
||||
if (existingSection == null) {
|
||||
newSection = BukkitAdapter_1_16_4.newChunkSection(layer, setArr, fastmode);
|
||||
if (BukkitAdapter_1_16_4.setSectionAtomic(sections, null, newSection, layer)) {
|
||||
updateGet(this, nmsChunk, sections, newSection, setArr, layer);
|
||||
continue;
|
||||
} else {
|
||||
existingSection = sections[layer];
|
||||
if (existingSection == null) {
|
||||
log.error("Skipping invalid null section. chunk:" + chunkX + ","
|
||||
+ chunkZ + " layer: " + layer);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
BukkitAdapter_1_16_4.fieldTickingBlockCount.set(existingSection, (short) 0);
|
||||
|
||||
//ensure that the server doesn't try to tick the chunksection while we're editing it.
|
||||
DelegateLock lock = BukkitAdapter_1_16_4.applyLock(existingSection);
|
||||
|
||||
synchronized (this) {
|
||||
synchronized (lock) {
|
||||
lock.untilFree();
|
||||
if (this.nmsChunk != nmsChunk) {
|
||||
this.nmsChunk = nmsChunk;
|
||||
this.sections = null;
|
||||
this.reset();
|
||||
} else if (existingSection != getSections()[layer]) {
|
||||
this.sections[layer] = existingSection;
|
||||
this.reset();
|
||||
} else if (!Arrays.equals(update(layer, new char[4096]), load(layer))) {
|
||||
this.reset(layer);
|
||||
} else if (lock.isModified()) {
|
||||
this.reset(layer);
|
||||
}
|
||||
newSection = BukkitAdapter_1_16_4
|
||||
.newChunkSection(layer, this::load, setArr, fastmode);
|
||||
if (!BukkitAdapter_1_16_4
|
||||
.setSectionAtomic(sections, existingSection, newSection, layer)) {
|
||||
log.error("Failed to set chunk section:" + chunkX + "," + chunkZ + " layer: " + layer);
|
||||
continue;
|
||||
} else {
|
||||
updateGet(this, nmsChunk, sections, newSection, setArr, layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Biomes
|
||||
BiomeType[] biomes = set.getBiomes();
|
||||
if (biomes != null) {
|
||||
// set biomes
|
||||
BiomeStorage currentBiomes = nmsChunk.getBiomeIndex();
|
||||
if (createCopy) {
|
||||
copy.storeBiomes(currentBiomes);
|
||||
}
|
||||
for (int y = 0, i = 0; y < 64; y++) {
|
||||
for (int z = 0; z < 4; z++) {
|
||||
for (int x = 0; x < 4; x++, i++) {
|
||||
final BiomeType biome = biomes[i];
|
||||
if (biome != null) {
|
||||
final Biome craftBiome = BukkitAdapter.adapt(biome);
|
||||
BiomeBase nmsBiome = CraftBlock.biomeToBiomeBase(nmsWorld.r().b(IRegistry.ay), craftBiome);
|
||||
currentBiomes.setBiome(x, y, z, nmsBiome);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<HeightMapType, int[]> heightMaps = set.getHeightMaps();
|
||||
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
||||
BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256);
|
||||
bitArray.fromRaw(entry.getValue());
|
||||
nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData());
|
||||
}
|
||||
|
||||
boolean lightUpdate = false;
|
||||
|
||||
// Lighting
|
||||
char[][] light = set.getLight();
|
||||
if (light != null) {
|
||||
lightUpdate = true;
|
||||
try {
|
||||
fillLightNibble(light, EnumSkyBlock.BLOCK);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
char[][] skyLight = set.getSkyLight();
|
||||
if (skyLight != null) {
|
||||
lightUpdate = true;
|
||||
try {
|
||||
fillLightNibble(skyLight, EnumSkyBlock.SKY);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Runnable[] syncTasks = null;
|
||||
|
||||
int bx = chunkX << 4;
|
||||
int bz = chunkZ << 4;
|
||||
|
||||
Set<UUID> entityRemoves = set.getEntityRemoves();
|
||||
if (entityRemoves != null && !entityRemoves.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[3];
|
||||
}
|
||||
|
||||
syncTasks[2] = () -> {
|
||||
final List<Entity>[] entities = nmsChunk.getEntitySlices();
|
||||
|
||||
for (final Collection<Entity> ents : entities) {
|
||||
if (!ents.isEmpty()) {
|
||||
final Iterator<Entity> iter = ents.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final Entity entity = iter.next();
|
||||
if (entityRemoves.contains(entity.getUniqueID())) {
|
||||
if (createCopy) {
|
||||
copy.storeEntity(entity);
|
||||
}
|
||||
iter.remove();
|
||||
removeEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Set<CompoundTag> entities = set.getEntities();
|
||||
if (entities != null && !entities.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[2];
|
||||
}
|
||||
|
||||
syncTasks[1] = () -> {
|
||||
for (final CompoundTag nativeTag : entities) {
|
||||
final Map<String, Tag> entityTagMap = nativeTag.getValue();
|
||||
final StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
final ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
if (idTag == null || posTag == null || rotTag == null) {
|
||||
getLogger(
|
||||
BukkitGetBlocks_1_16_4.class).debug("Unknown entity tag: " + nativeTag);
|
||||
continue;
|
||||
}
|
||||
final double x = posTag.getDouble(0);
|
||||
final double y = posTag.getDouble(1);
|
||||
final double z = posTag.getDouble(2);
|
||||
final float yaw = rotTag.getFloat(0);
|
||||
final float pitch = rotTag.getFloat(1);
|
||||
final String id = idTag.getValue();
|
||||
|
||||
EntityTypes<?> type = EntityTypes.a(id).orElse(null);
|
||||
if (type != null) {
|
||||
Entity entity = type.a(nmsWorld);
|
||||
if (entity != null) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
final NBTTagCompound tag = (NBTTagCompound) adapter.fromNative(nativeTag);
|
||||
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||
tag.remove(name);
|
||||
}
|
||||
entity.load(tag);
|
||||
entity.setLocation(x, y, z, yaw, pitch);
|
||||
nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// set tiles
|
||||
Map<BlockVector3, CompoundTag> tiles = set.getTiles();
|
||||
if (tiles != null && !tiles.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[1];
|
||||
}
|
||||
|
||||
syncTasks[0] = () -> {
|
||||
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
|
||||
final CompoundTag nativeTag = entry.getValue();
|
||||
final BlockVector3 blockHash = entry.getKey();
|
||||
final int x = blockHash.getX() + bx;
|
||||
final int y = blockHash.getY();
|
||||
final int z = blockHash.getZ() + bz;
|
||||
final BlockPosition pos = new BlockPosition(x, y, z);
|
||||
|
||||
synchronized (nmsWorld) {
|
||||
TileEntity tileEntity = nmsWorld.getTileEntity(pos);
|
||||
if (tileEntity == null || tileEntity.isRemoved()) {
|
||||
nmsWorld.removeTileEntity(pos);
|
||||
tileEntity = nmsWorld.getTileEntity(pos);
|
||||
}
|
||||
if (tileEntity != null) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
final NBTTagCompound tag = (NBTTagCompound) adapter.fromNative(nativeTag);
|
||||
tag.set("x", NBTTagInt.a(x));
|
||||
tag.set("y", NBTTagInt.a(y));
|
||||
tag.set("z", NBTTagInt.a(z));
|
||||
tileEntity.load(tileEntity.getBlock(), tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Runnable callback;
|
||||
if (bitMask == 0 && biomes == null && !lightUpdate) {
|
||||
callback = null;
|
||||
} else {
|
||||
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
|
||||
boolean finalLightUpdate = lightUpdate;
|
||||
callback = () -> {
|
||||
// Set Modified
|
||||
nmsChunk.d(true); // Set Modified
|
||||
nmsChunk.mustNotSave = false;
|
||||
nmsChunk.markDirty();
|
||||
// send to player
|
||||
BukkitAdapter_1_16_4.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate);
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
if (syncTasks != null) {
|
||||
QueueHandler queueHandler = Fawe.get().getQueueHandler();
|
||||
Runnable[] finalSyncTasks = syncTasks;
|
||||
|
||||
// Chain the sync tasks and the callback
|
||||
Callable<Future> chain = () -> {
|
||||
try {
|
||||
// Run the sync tasks
|
||||
for (Runnable task : finalSyncTasks) {
|
||||
if (task != null) {
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
if (callback == null) {
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return queueHandler.async(callback, null);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
return (T) (Future) queueHandler.sync(chain);
|
||||
} else {
|
||||
if (callback == null) {
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
}
|
||||
} else {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized char[] update(int layer, char[] data) {
|
||||
ChunkSection section = getSections()[layer];
|
||||
// Section is null, return empty array
|
||||
if (section == null) {
|
||||
data = new char[4096];
|
||||
Arrays.fill(data, (char) 1);
|
||||
return data;
|
||||
}
|
||||
if (data == null || data == FaweCache.IMP.EMPTY_CHAR_4096) {
|
||||
data = new char[4096];
|
||||
Arrays.fill(data, (char) 1);
|
||||
}
|
||||
DelegateLock lock = BukkitAdapter_1_16_4.applyLock(section);
|
||||
synchronized (lock) {
|
||||
lock.untilFree();
|
||||
lock.setModified(false);
|
||||
// Efficiently convert ChunkSection to raw data
|
||||
try {
|
||||
FAWE_Spigot_v1_16_R3 adapter = ((FAWE_Spigot_v1_16_R3) WorldEditPlugin.getInstance().getBukkitImplAdapter());
|
||||
|
||||
final DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
final DataBits bits = (DataBits) BukkitAdapter_1_16_4.fieldBits.get(blocks);
|
||||
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitAdapter_1_16_4.fieldPalette.get(blocks);
|
||||
|
||||
final int bitsPerEntry = (int) BukkitAdapter_1_16_4.fieldBitsPerEntry.get(bits);
|
||||
final long[] blockStates = bits.a();
|
||||
|
||||
new BitArrayUnstretched(bitsPerEntry, 4096, blockStates).toRaw(data);
|
||||
|
||||
int num_palette;
|
||||
if (palette instanceof DataPaletteLinear) {
|
||||
num_palette = ((DataPaletteLinear<IBlockData>) palette).b();
|
||||
} else if (palette instanceof DataPaletteHash) {
|
||||
num_palette = ((DataPaletteHash<IBlockData>) palette).b();
|
||||
} else {
|
||||
num_palette = 0;
|
||||
int[] paletteToBlockInts = FaweCache.IMP.PALETTE_TO_BLOCK.get();
|
||||
char[] paletteToBlockChars = FaweCache.IMP.PALETTE_TO_BLOCK_CHAR.get();
|
||||
try {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char paletteVal = data[i];
|
||||
char ordinal = paletteToBlockChars[paletteVal];
|
||||
if (ordinal == Character.MAX_VALUE) {
|
||||
paletteToBlockInts[num_palette++] = paletteVal;
|
||||
IBlockData ibd = palette.a(data[i]);
|
||||
if (ibd == null) {
|
||||
ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar();
|
||||
} else {
|
||||
ordinal = adapter.adaptToChar(ibd);
|
||||
}
|
||||
paletteToBlockChars[paletteVal] = ordinal;
|
||||
}
|
||||
// Don't read "empty".
|
||||
if (ordinal == 0) {
|
||||
ordinal = 1;
|
||||
}
|
||||
data[i] = ordinal;
|
||||
}
|
||||
} finally {
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
int paletteVal = paletteToBlockInts[i];
|
||||
paletteToBlockChars[paletteVal] = Character.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
char[] paletteToOrdinal = FaweCache.IMP.PALETTE_TO_BLOCK_CHAR.get();
|
||||
try {
|
||||
if (num_palette != 1) {
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
char ordinal = ordinal(palette.a(i), adapter);
|
||||
paletteToOrdinal[i] = ordinal;
|
||||
}
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char paletteVal = data[i];
|
||||
char val = paletteToOrdinal[paletteVal];
|
||||
if (val == Character.MAX_VALUE) {
|
||||
val = ordinal(palette.a(i), adapter);
|
||||
paletteToOrdinal[i] = val;
|
||||
}
|
||||
// Don't read "empty".
|
||||
if (val == 0) {
|
||||
val = 1;
|
||||
}
|
||||
data[i] = val;
|
||||
}
|
||||
} else {
|
||||
char ordinal = ordinal(palette.a(0), adapter);
|
||||
// Don't read "empty".
|
||||
if (ordinal == 0) {
|
||||
ordinal = 1;
|
||||
}
|
||||
Arrays.fill(data, ordinal);
|
||||
}
|
||||
} finally {
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
paletteToOrdinal[i] = Character.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final char ordinal(IBlockData ibd, FAWE_Spigot_v1_16_R3 adapter) {
|
||||
if (ibd == null) {
|
||||
return BlockTypes.AIR.getDefaultState().getOrdinalChar();
|
||||
} else {
|
||||
return adapter.adaptToChar(ibd);
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkSection[] getSections() {
|
||||
ChunkSection[] tmp = sections;
|
||||
if (tmp == null) {
|
||||
synchronized (this) {
|
||||
tmp = sections;
|
||||
if (tmp == null) {
|
||||
Chunk chunk = getChunk();
|
||||
sections = tmp = chunk.getSections().clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
Chunk tmp = nmsChunk;
|
||||
if (tmp == null) {
|
||||
synchronized (this) {
|
||||
tmp = nmsChunk;
|
||||
if (tmp == null) {
|
||||
nmsChunk = tmp = ensureLoaded(this.world, chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
|
||||
for (int Y = 0; Y < 16; Y++) {
|
||||
if (light[Y] == null) {
|
||||
continue;
|
||||
}
|
||||
SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), Y);
|
||||
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(sectionPosition);
|
||||
if (nibble == null) {
|
||||
byte[] a = new byte[2048];
|
||||
Arrays.fill(a, skyBlock == EnumSkyBlock.SKY ? (byte) 15 : (byte) 0);
|
||||
nibble = new NibbleArray(a);
|
||||
((LightEngine) world.getChunkProvider().getLightEngine()).a(skyBlock, sectionPosition, nibble, true);
|
||||
}
|
||||
synchronized (nibble) {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
if (light[Y][i] < 16) {
|
||||
nibble.a(i, light[Y][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
return getSections()[layer] != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
skyLight = new NibbleArray[16];
|
||||
blockLight = new NibbleArray[16];
|
||||
if (aggressive) {
|
||||
sections = null;
|
||||
nmsChunk = null;
|
||||
return super.trim(true);
|
||||
} else {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (!hasSection(i) || super.sections[i] == CharBlocks.EMPTY) {
|
||||
continue;
|
||||
}
|
||||
ChunkSection existing = getSections()[i];
|
||||
try {
|
||||
final DataPaletteBlock<IBlockData> blocksExisting = existing.getBlocks();
|
||||
|
||||
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitAdapter_1_16_4.fieldPalette.get(blocksExisting);
|
||||
int paletteSize;
|
||||
|
||||
if (palette instanceof DataPaletteLinear) {
|
||||
paletteSize = ((DataPaletteLinear<IBlockData>) palette).b();
|
||||
} else if (palette instanceof DataPaletteHash) {
|
||||
paletteSize = ((DataPaletteHash<IBlockData>) palette).b();
|
||||
} else {
|
||||
super.trim(false, i);
|
||||
continue;
|
||||
}
|
||||
if (paletteSize == 1) {
|
||||
//If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks.
|
||||
continue;
|
||||
}
|
||||
super.trim(false, i);
|
||||
} catch (IllegalAccessException ignored) {
|
||||
super.trim(false, i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_16_4;
|
||||
|
||||
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.nbt.LazyCompoundTag_1_16_4;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.server.v1_16_R3.BiomeBase;
|
||||
import net.minecraft.server.v1_16_R3.BiomeStorage;
|
||||
import net.minecraft.server.v1_16_R3.Entity;
|
||||
import net.minecraft.server.v1_16_R3.IRegistry;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_16_R3.TileEntity;
|
||||
import net.minecraft.server.v1_16_R3.WorldServer;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.block.CraftBlock;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BukkitGetBlocks_1_16_4_Copy extends BukkitGetBlocks_1_16_4 {
|
||||
|
||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private BiomeStorage biomeStorage;
|
||||
private final char[][] blocks = new char[16][4096];
|
||||
|
||||
protected BukkitGetBlocks_1_16_4_Copy(WorldServer world, int X, int Z) {
|
||||
super(world, X, Z);
|
||||
}
|
||||
|
||||
protected void storeTile(TileEntity tile) {
|
||||
tiles.put(BlockVector3.at(tile.getPosition().getX(), tile.getPosition().getY(), tile.getPosition().getZ()),
|
||||
new LazyCompoundTag_1_16_4(Suppliers.memoize(() -> tile.save(new NBTTagCompound()))));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
entities.add((CompoundTag) adapter.toNative(entity.save(tag)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
for (CompoundTag tag : entities) {
|
||||
UUID tagUUID;
|
||||
if (tag.containsKey("UUID")) {
|
||||
int[] arr = tag.getIntArray("UUID");
|
||||
tagUUID = new UUID((long) arr[0] << 32 | (arr[1] & 0xFFFFFFFFL), (long) arr[2] << 32 | (arr[3] & 0xFFFFFFFFL));
|
||||
} else if (tag.containsKey("UUIDMost")) {
|
||||
tagUUID = new UUID(tag.getLong("UUIDMost"), tag.getLong("UUIDLeast"));
|
||||
} else if (tag.containsKey("PersistentIDMSB")) {
|
||||
tagUUID = new UUID(tag.getLong("PersistentIDMSB"), tag.getLong("PersistentIDLSB"));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (uuid.equals(tagUUID)) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void storeBiomes(BiomeStorage biomeStorage) {
|
||||
this.biomeStorage = new BiomeStorage(biomeStorage.g, BukkitAdapter_1_16_4.getBiomeArray(biomeStorage).clone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
BiomeBase base = null;
|
||||
if (y == -1) {
|
||||
for (y = 0; y < FaweCache.IMP.WORLD_HEIGHT; y++) {
|
||||
base = biomeStorage.getBiome(x >> 2, y >> 2, z >> 2);
|
||||
if (base != null) break;
|
||||
}
|
||||
} else {
|
||||
base = biomeStorage.getBiome(x >> 2, y >> 2, z >> 2);
|
||||
}
|
||||
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null;
|
||||
}
|
||||
|
||||
protected void storeSection(int layer) {
|
||||
blocks[layer] = update(layer, null).clone();
|
||||
}
|
||||
|
||||
@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 BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public char get(int x, int y, int z) {
|
||||
final int layer = y >> 4;
|
||||
final int index = (y & 15) << 8 | z << 4 | x;
|
||||
return blocks[layer][index];
|
||||
}
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_16_4;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R2;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R3;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import net.minecraft.server.v1_16_R3.Block;
|
||||
import net.minecraft.server.v1_16_R3.BlockPosition;
|
||||
import net.minecraft.server.v1_16_R3.Chunk;
|
||||
import net.minecraft.server.v1_16_R3.ChunkProviderServer;
|
||||
import net.minecraft.server.v1_16_R3.EnumDirection;
|
||||
import net.minecraft.server.v1_16_R3.IBlockData;
|
||||
import net.minecraft.server.v1_16_R3.NBTBase;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_16_R3.PlayerChunk;
|
||||
import net.minecraft.server.v1_16_R3.TileEntity;
|
||||
import net.minecraft.server.v1_16_R3.World;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
public class FAWEWorldNativeAccess_1_16 implements WorldNativeAccess<Chunk, IBlockData, BlockPosition> {
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
|
||||
private final FAWE_Spigot_v1_16_R3 adapter;
|
||||
private final WeakReference<World> world;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public FAWEWorldNativeAccess_1_16(FAWE_Spigot_v1_16_R3 adapter, WeakReference<World> world) {
|
||||
this.adapter = adapter;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
private World getWorld() {
|
||||
return Objects.requireNonNull(world.get(), "The reference to the world was lost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
||||
this.sideEffectSet = sideEffectSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getChunk(int x, int z) {
|
||||
return getWorld().getChunkAt(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockData toNative(BlockState state) {
|
||||
int stateId = BlockStateIdAccess.getBlockStateId(state);
|
||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
||||
? Block.getByCombinedId(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockData getBlockState(Chunk chunk, BlockPosition position) {
|
||||
return chunk.getType(position);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBlockData setBlockState(Chunk chunk, BlockPosition position, IBlockData state) {
|
||||
return chunk.setType(position, state, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockData getValidBlockForPosition(IBlockData block, BlockPosition position) {
|
||||
return Block.b(block, getWorld(), position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPosition getPosition(int x, int y, int z) {
|
||||
return new BlockPosition(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLightingForBlock(BlockPosition position) {
|
||||
getWorld().getChunkProvider().getLightEngine().a(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(BlockPosition position, CompoundTag tag) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the other versions
|
||||
TileEntity tileEntity = getWorld().getTileEntity(position);
|
||||
if (tileEntity == null) {
|
||||
return false;
|
||||
}
|
||||
NBTBase nativeTag = adapter.fromNative(tag);
|
||||
tileEntity.load(tileEntity.getBlock(), (NBTTagCompound) nativeTag);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyBlockUpdate(BlockPosition position, IBlockData oldState, IBlockData newState) {
|
||||
getWorld().notify(position, oldState, newState, UPDATE | NOTIFY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(Chunk chunk) {
|
||||
return chunk.getState().isAtLeast(PlayerChunk.State.TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockChanged(BlockPosition position) {
|
||||
((ChunkProviderServer) getWorld().getChunkProvider()).flagDirty(position);
|
||||
}
|
||||
|
||||
private static final EnumDirection[] NEIGHBOUR_ORDER = {
|
||||
EnumDirection.WEST, EnumDirection.EAST,
|
||||
EnumDirection.DOWN, EnumDirection.UP,
|
||||
EnumDirection.NORTH, EnumDirection.SOUTH
|
||||
};
|
||||
|
||||
@Override
|
||||
public void notifyNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState) {
|
||||
World world = getWorld();
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
world.update(pos, oldState.getBlock());
|
||||
} else {
|
||||
// When we don't want events, manually run the physics without them.
|
||||
// Un-nest neighbour updating
|
||||
for (EnumDirection direction : NEIGHBOUR_ORDER) {
|
||||
BlockPosition shifted = pos.shift(direction);
|
||||
world.getType(shifted).doPhysics(world, shifted, oldState.getBlock(), pos, false);
|
||||
}
|
||||
}
|
||||
if (newState.isComplexRedstone()) {
|
||||
world.updateAdjacentComparators(pos, newState.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNeighbors(BlockPosition pos, IBlockData oldState, IBlockData newState, int recursionLimit) {
|
||||
World world = getWorld();
|
||||
// a == updateNeighbors
|
||||
// b == updateDiagonalNeighbors
|
||||
oldState.b(world, pos, NOTIFY, recursionLimit);
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
CraftWorld craftWorld = world.getWorld();
|
||||
if (craftWorld != null) {
|
||||
BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState));
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
newState.a(world, pos, NOTIFY, recursionLimit);
|
||||
newState.b(world, pos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(BlockPosition pos, IBlockData oldState, IBlockData newState) {
|
||||
getWorld().a(pos, oldState, newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException {
|
||||
return this.adapter.setBlock(this.getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4).bukkitChunk, position.getBlockX(), position.getBlockY(), position.getBlockZ(), block, sideEffectSet.shouldApply(SideEffect.LIGHTING));
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_16_4;
|
||||
|
||||
import com.boydti.fawe.bukkit.adapter.MapChunkUtil;
|
||||
import net.minecraft.server.v1_16_R3.PacketPlayOutMapChunk;
|
||||
|
||||
public class MapChunkUtil_1_16_4 extends MapChunkUtil<PacketPlayOutMapChunk> {
|
||||
public MapChunkUtil_1_16_4() throws NoSuchFieldException {
|
||||
fieldX = PacketPlayOutMapChunk.class.getDeclaredField("a");
|
||||
fieldZ = PacketPlayOutMapChunk.class.getDeclaredField("b");
|
||||
fieldBitMask = PacketPlayOutMapChunk.class.getDeclaredField("c");
|
||||
fieldHeightMap = PacketPlayOutMapChunk.class.getDeclaredField("d");
|
||||
fieldChunkData = PacketPlayOutMapChunk.class.getDeclaredField("f");
|
||||
fieldBlockEntities = PacketPlayOutMapChunk.class.getDeclaredField("g");
|
||||
fieldFull = PacketPlayOutMapChunk.class.getDeclaredField("h");
|
||||
fieldX.setAccessible(true);
|
||||
fieldZ.setAccessible(true);
|
||||
fieldBitMask.setAccessible(true);
|
||||
fieldHeightMap.setAccessible(true);
|
||||
fieldChunkData.setAccessible(true);
|
||||
fieldBlockEntities.setAccessible(true);
|
||||
fieldFull.setAccessible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutMapChunk createPacket() {
|
||||
return new PacketPlayOutMapChunk();
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_16_4.nbt;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import net.minecraft.server.v1_16_R3.NBTBase;
|
||||
import net.minecraft.server.v1_16_R3.NBTNumber;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LazyCompoundTag_1_16_4 extends CompoundTag {
|
||||
private final Supplier<NBTTagCompound> nmsTag;
|
||||
|
||||
public LazyCompoundTag_1_16_4(Supplier<NBTTagCompound> tag) {
|
||||
super(null);
|
||||
this.nmsTag = tag;
|
||||
}
|
||||
|
||||
public LazyCompoundTag_1_16_4(NBTTagCompound tag) {
|
||||
this(() -> tag);
|
||||
}
|
||||
|
||||
public NBTTagCompound get() {
|
||||
return nmsTag.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Tag> getValue() {
|
||||
Map<String, Tag> value = super.getValue();
|
||||
if (value == null) {
|
||||
Tag tag = WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(nmsTag.get());
|
||||
setValue(((CompoundTag) tag).getValue());
|
||||
}
|
||||
return super.getValue();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return nmsTag.get().hasKey(key);
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
return nmsTag.get().getByteArray(key);
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
return nmsTag.get().getByte(key);
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
return nmsTag.get().getDouble(key);
|
||||
}
|
||||
|
||||
public double asDouble(String key) {
|
||||
NBTBase value = nmsTag.get().get(key);
|
||||
if (value instanceof NBTNumber) {
|
||||
return ((NBTNumber) value).asDouble();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
return nmsTag.get().getFloat(key);
|
||||
}
|
||||
|
||||
public int[] getIntArray(String key) {
|
||||
return nmsTag.get().getIntArray(key);
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
return nmsTag.get().getInt(key);
|
||||
}
|
||||
|
||||
public int asInt(String key) {
|
||||
NBTBase value = nmsTag.get().get(key);
|
||||
if (value instanceof NBTNumber) {
|
||||
return ((NBTNumber) value).asInt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public List<Tag> getList(String key) {
|
||||
NBTBase tag = nmsTag.get().get(key);
|
||||
if (tag instanceof NBTTagList) {
|
||||
ArrayList<Tag> list = new ArrayList<>();
|
||||
NBTTagList nbtList = (NBTTagList) tag;
|
||||
for (NBTBase elem : nbtList) {
|
||||
if (elem instanceof NBTTagCompound) {
|
||||
list.add(new LazyCompoundTag_1_16_4((NBTTagCompound) elem));
|
||||
} else {
|
||||
list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public ListTag getListTag(String key) {
|
||||
NBTBase tag = nmsTag.get().get(key);
|
||||
if (tag instanceof NBTTagList) {
|
||||
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 nmsTag.get().getLongArray(key);
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
return nmsTag.get().getLong(key);
|
||||
}
|
||||
|
||||
public long asLong(String key) {
|
||||
NBTBase value = nmsTag.get().get(key);
|
||||
if (value instanceof NBTNumber) {
|
||||
return ((NBTNumber) value).asLong();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
return nmsTag.get().getShort(key);
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return nmsTag.get().getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return nmsTag.get().toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.boydti.fawe.bukkit.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class JavaVersionCheck {
|
||||
|
||||
public static final Logger logger = LoggerFactory.getLogger(JavaVersionCheck.class);
|
||||
|
||||
private static int checkJavaVersion() {
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
final Matcher matcher = Pattern.compile("(?:1\\.)?(\\d+)").matcher(javaVersion);
|
||||
if (!matcher.find()) {
|
||||
logger.warn("Failed to determine Java version; Could not parse: {}", javaVersion);
|
||||
return -1;
|
||||
}
|
||||
|
||||
final String version = matcher.group(1);
|
||||
try {
|
||||
return Integer.parseInt(version);
|
||||
} catch (final NumberFormatException e) {
|
||||
logger.warn("Failed to determine Java version; Could not parse {} from {}", version, javaVersion, e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkJvm() {
|
||||
if (checkJavaVersion() < 11) {
|
||||
logger.warn("************************************************************");
|
||||
logger.warn("* WARNING - YOU ARE RUNNING AN OUTDATED VERSION OF JAVA.");
|
||||
logger.warn("* FASTASYNCWORLDEDIT WILL STOP BEING COMPATIBLE WITH THIS VERSION OF");
|
||||
logger.warn("* JAVA WHEN MINECRAFT 1.17 IS RELEASED.");
|
||||
logger.warn("*");
|
||||
logger.warn("* Please update the version of Java to 11. When Minecraft 1.17");
|
||||
logger.warn("* is released, support for versions of Java prior to 11 will");
|
||||
logger.warn("* be dropped.");
|
||||
logger.warn("*");
|
||||
logger.warn("* Current Java version: {}", System.getProperty("java.version"));
|
||||
logger.warn("************************************************************");
|
||||
}
|
||||
if (checkJavaVersion() >= 15) {
|
||||
logger.warn("************************************************************");
|
||||
logger.warn("* FastAsyncWorldEdit uses Nashorn for the craftscript engine.");
|
||||
logger.warn("* Within Java 15, Nashorn has been removed from Java.");
|
||||
logger.warn("* Until we add a suitable workaround, you should stick to Java 11");
|
||||
logger.warn("* to use all features of FastAsyncWorldEdit.");
|
||||
logger.warn("************************************************************");
|
||||
}
|
||||
}
|
||||
}
|
@ -398,13 +398,18 @@ public class AsyncBlock implements Block {
|
||||
}
|
||||
|
||||
public boolean applyBoneMeal(@NotNull BlockFace face) {
|
||||
throw new UnsupportedOperationException("FAWE does not support this method");
|
||||
throw new UnsupportedOperationException("FAWE does not support this yet");
|
||||
}
|
||||
|
||||
public String getTranslationKey() {
|
||||
throw new UnsupportedOperationException("FAWE does not support this yet");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public float getDestroySpeed(@NotNull ItemStack itemStack) {
|
||||
throw new UnsupportedOperationException("FAWE does not support this yet");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
|
@ -9,6 +9,7 @@ import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -51,6 +52,11 @@ public class AsyncChunk implements Chunk {
|
||||
return z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getChunkKey() {
|
||||
return Chunk.getChunkKey(getX(), getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncWorld getWorld() {
|
||||
return world;
|
||||
@ -140,7 +146,7 @@ public class AsyncChunk implements Chunk {
|
||||
|
||||
@Override
|
||||
public boolean isSlimeChunk() {
|
||||
return false;
|
||||
return TaskManager.IMP.sync(() -> world.getChunkAt(x, z).isSlimeChunk());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -170,17 +176,21 @@ public class AsyncChunk implements Chunk {
|
||||
|
||||
@Override
|
||||
public long getInhabitedTime() {
|
||||
return 0; //todo
|
||||
return TaskManager.IMP.sync(() -> world.getChunkAt(x, z).getInhabitedTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInhabitedTime(long ticks) {
|
||||
//todo
|
||||
world.getChunkAt(x, z).setInhabitedTime(ticks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull BlockData block) {
|
||||
//todo
|
||||
return false;
|
||||
return TaskManager.IMP.sync(() -> world.getChunkAt(x, z).contains(block));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PersistentDataContainer getPersistentDataContainer() {
|
||||
return TaskManager.IMP.sync(() -> world.getChunkAt(x, z).getPersistentDataContainer());
|
||||
}
|
||||
}
|
||||
|
@ -649,6 +649,21 @@ public class AsyncWorld extends PassthroughExtent implements World {
|
||||
parent.setThunderDuration(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClearWeather() {
|
||||
return parent.isClearWeather();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClearWeatherDuration(int duration) {
|
||||
parent.setClearWeatherDuration(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getClearWeatherDuration() {
|
||||
return parent.getClearWeatherDuration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createExplosion(double x, double y, double z, float power) {
|
||||
return this.createExplosion(x, y, z, power, false, true);
|
||||
|
@ -19,6 +19,9 @@
|
||||
|
||||
package com.sk89q.worldedit.bukkit;
|
||||
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.util.translation.TranslationManager;
|
||||
import com.sk89q.worldedit.world.biome.BiomeData;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.registry.BiomeRegistry;
|
||||
@ -34,6 +37,14 @@ class BukkitBiomeRegistry implements BiomeRegistry {
|
||||
BukkitBiomeRegistry() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichName(BiomeType biomeType) {
|
||||
return TranslatableComponent.of(
|
||||
TranslationManager.makeTranslationKey("biome", biomeType.getId())
|
||||
);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
@Override
|
||||
public BiomeData getData(BiomeType biome) {
|
||||
|
@ -20,6 +20,7 @@
|
||||
package com.sk89q.worldedit.bukkit;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
@ -39,6 +40,14 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
|
||||
|
||||
private BukkitBlockMaterial[] materialMap;
|
||||
|
||||
@Override
|
||||
public Component getRichName(BlockType blockType) {
|
||||
if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) {
|
||||
return WorldEditPlugin.getInstance().getBukkitImplAdapter().getRichBlockName(blockType);
|
||||
}
|
||||
return super.getRichName(blockType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BlockType blockType) {
|
||||
|
@ -40,6 +40,7 @@ import org.bukkit.entity.Projectile;
|
||||
import org.bukkit.entity.TNTPrimed;
|
||||
import org.bukkit.entity.Tameable;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.entity.WaterMob;
|
||||
import org.bukkit.entity.minecart.ExplosiveMinecart;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -147,4 +148,9 @@ class BukkitEntityProperties implements EntityProperties {
|
||||
public boolean isPasteable() {
|
||||
return !(entity instanceof Player || entity instanceof ComplexEntityPart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWaterCreature() {
|
||||
return entity instanceof WaterMob;
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,32 @@
|
||||
|
||||
package com.sk89q.worldedit.bukkit;
|
||||
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BundledItemRegistry;
|
||||
import org.bukkit.Material;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class BukkitItemRegistry extends BundledItemRegistry {
|
||||
class BukkitItemRegistry extends BundledItemRegistry {
|
||||
@Override
|
||||
public Component getRichName(ItemType itemType) {
|
||||
if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) {
|
||||
return WorldEditPlugin.getInstance().getBukkitImplAdapter().getRichItemName(itemType);
|
||||
}
|
||||
return super.getRichName(itemType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichName(BaseItemStack itemStack) {
|
||||
if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) {
|
||||
return WorldEditPlugin.getInstance().getBukkitImplAdapter().getRichItemName(itemStack);
|
||||
}
|
||||
return super.getRichName(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> values() {
|
||||
ArrayList<String> values = new ArrayList<>();
|
||||
|
@ -22,7 +22,6 @@ package com.sk89q.worldedit.bukkit;
|
||||
import com.sk89q.worldedit.world.registry.BiomeRegistry;
|
||||
import com.sk89q.worldedit.world.registry.BlockCategoryRegistry;
|
||||
import com.sk89q.worldedit.world.registry.BlockRegistry;
|
||||
import com.sk89q.worldedit.world.registry.BundledItemRegistry;
|
||||
import com.sk89q.worldedit.world.registry.BundledRegistries;
|
||||
import com.sk89q.worldedit.world.registry.EntityRegistry;
|
||||
import com.sk89q.worldedit.world.registry.ItemCategoryRegistry;
|
||||
@ -35,8 +34,8 @@ class BukkitRegistries extends BundledRegistries {
|
||||
|
||||
private static final BukkitRegistries INSTANCE = new BukkitRegistries();
|
||||
private final BlockRegistry blockRegistry = new BukkitBlockRegistry();
|
||||
private final ItemRegistry itemRegistry = new BukkitItemRegistry();
|
||||
private final BiomeRegistry biomeRegistry = new BukkitBiomeRegistry();
|
||||
private final ItemRegistry itemRegistry = new BukkitItemRegistry();
|
||||
private final EntityRegistry entityRegistry = new BukkitEntityRegistry();
|
||||
private final BlockCategoryRegistry blockCategoryRegistry = new BukkitBlockCategoryRegistry();
|
||||
private final ItemCategoryRegistry itemCategoryRegistry = new BukkitItemCategoryRegistry();
|
||||
@ -57,6 +56,11 @@ class BukkitRegistries extends BundledRegistries {
|
||||
return biomeRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemRegistry getItemRegistry() {
|
||||
return itemRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockCategoryRegistry getBlockCategoryRegistry() {
|
||||
return blockCategoryRegistry;
|
||||
@ -81,8 +85,4 @@ class BukkitRegistries extends BundledRegistries {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemRegistry getItemRegistry() {
|
||||
return itemRegistry;
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import com.sk89q.worldedit.bukkit.adapter.BukkitImplLoader;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_15_R2;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R1;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R2;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_16_R3;
|
||||
import com.sk89q.worldedit.event.platform.CommandEvent;
|
||||
import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
|
||||
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
|
||||
@ -83,6 +84,7 @@ import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static com.boydti.fawe.bukkit.util.JavaVersionCheck.checkJvm;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME;
|
||||
|
||||
@ -185,6 +187,9 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter
|
||||
|
||||
// Setup metrics
|
||||
new Metrics(this, BSTATS_PLUGIN_ID);
|
||||
|
||||
// Check whether the server runs on 11 or greater
|
||||
checkJvm();
|
||||
}
|
||||
|
||||
private void setupPreWorldData() {
|
||||
@ -299,6 +304,7 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter
|
||||
adapterLoader.addClass(FAWE_Spigot_v1_15_R2.class);
|
||||
adapterLoader.addClass(FAWE_Spigot_v1_16_R1.class);
|
||||
adapterLoader.addClass(FAWE_Spigot_v1_16_R2.class);
|
||||
adapterLoader.addClass(FAWE_Spigot_v1_16_R3.class);
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
|
@ -36,12 +36,14 @@ import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.DataFixer;
|
||||
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.BlockType;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
@ -52,10 +54,10 @@ import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An interface for adapters of various Bukkit implementations.
|
||||
@ -127,6 +129,30 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
|
||||
@Nullable
|
||||
Entity createEntity(Location location, BaseEntity state);
|
||||
|
||||
/**
|
||||
* Gets the name for the given block.
|
||||
*
|
||||
* @param blockType the block
|
||||
* @return The name
|
||||
*/
|
||||
Component getRichBlockName(BlockType blockType);
|
||||
|
||||
/**
|
||||
* Gets the name for the given item.
|
||||
*
|
||||
* @param itemType the item
|
||||
* @return The name
|
||||
*/
|
||||
Component getRichItemName(ItemType itemType);
|
||||
|
||||
/**
|
||||
* Gets the name for the given item stack.
|
||||
*
|
||||
* @param itemStack the item stack
|
||||
* @return The name
|
||||
*/
|
||||
Component getRichItemName(BaseItemStack itemStack);
|
||||
|
||||
/**
|
||||
* Get a map of {@code string -> property}.
|
||||
*
|
||||
|
@ -48,7 +48,7 @@ public class BukkitImplLoader {
|
||||
|
||||
private static final String LOAD_ERROR_MESSAGE =
|
||||
"\n**********************************************\n"
|
||||
+ "** This WorldEdit version does not fully support your version of Bukkit.\n"
|
||||
+ "** This FastAsyncWorldEdit version does not fully support your version of Bukkit.\n"
|
||||
+ "**\n" + "** When working with blocks or undoing, chests will be empty, signs\n"
|
||||
+ "** will be blank, and so on. There will be no support for entity\n"
|
||||
+ "** and block property-related functions.\n"
|
||||
|
@ -48,6 +48,7 @@ import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
@ -57,6 +58,7 @@ import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.server.v1_15_R1.BiomeBase;
|
||||
import net.minecraft.server.v1_15_R1.Block;
|
||||
@ -279,6 +281,21 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
BlockMaterial_1_15_2 material = (BlockMaterial_1_15_2) state.getMaterial();
|
||||
|
@ -47,6 +47,7 @@ import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
@ -56,6 +57,7 @@ import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.server.v1_16_R1.BiomeBase;
|
||||
import net.minecraft.server.v1_16_R1.Block;
|
||||
@ -278,6 +280,21 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
BlockMaterial_1_16_1 material = (BlockMaterial_1_16_1) state.getMaterial();
|
||||
|
@ -47,6 +47,7 @@ import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
@ -56,6 +57,7 @@ import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.server.v1_16_R2.BiomeBase;
|
||||
import net.minecraft.server.v1_16_R2.Block;
|
||||
@ -279,6 +281,21 @@ public final class FAWE_Spigot_v1_16_R2 extends CachedBukkitAdapter implements I
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
BlockMaterial_1_16_2 material = (BlockMaterial_1_16_2) state.getMaterial();
|
||||
@ -413,7 +430,7 @@ public final class FAWE_Spigot_v1_16_R2 extends CachedBukkitAdapter implements I
|
||||
|
||||
@Override
|
||||
public int getInternalBiomeId(BiomeType biome) {
|
||||
BiomeBase base = CraftBlock.biomeToBiomeBase(MinecraftServer.getServer().aX().b(IRegistry.ay), BukkitAdapter.adapt(biome));
|
||||
return MinecraftServer.getServer().aX().b(IRegistry.ay).a(base);
|
||||
BiomeBase base = CraftBlock.biomeToBiomeBase(MinecraftServer.getServer().getCustomRegistry().b(IRegistry.ay), BukkitAdapter.adapt(biome));
|
||||
return MinecraftServer.getServer().getCustomRegistry().b(IRegistry.ay).a(base);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,437 @@
|
||||
/*
|
||||
* 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.impl;
|
||||
|
||||
import com.bekvon.bukkit.residence.commands.material;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.BlockMaterial_1_16_4;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.BukkitAdapter_1_16_4;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.BukkitGetBlocks_1_16_4;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.FAWEWorldNativeAccess_1_16;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.MapChunkUtil_1_16_4;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.nbt.LazyCompoundTag_1_16_4;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.regen.Regen_v1_16_R3;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.LazyBaseEntity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.server.v1_16_R3.BiomeBase;
|
||||
import net.minecraft.server.v1_16_R3.Block;
|
||||
import net.minecraft.server.v1_16_R3.BlockPosition;
|
||||
import net.minecraft.server.v1_16_R3.Chunk;
|
||||
import net.minecraft.server.v1_16_R3.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_16_R3.ChunkSection;
|
||||
import net.minecraft.server.v1_16_R3.Entity;
|
||||
import net.minecraft.server.v1_16_R3.EntityPlayer;
|
||||
import net.minecraft.server.v1_16_R3.EntityTypes;
|
||||
import net.minecraft.server.v1_16_R3.IBlockData;
|
||||
import net.minecraft.server.v1_16_R3.IRegistry;
|
||||
import net.minecraft.server.v1_16_R3.ItemStack;
|
||||
import net.minecraft.server.v1_16_R3.MinecraftKey;
|
||||
import net.minecraft.server.v1_16_R3.MinecraftServer;
|
||||
import net.minecraft.server.v1_16_R3.NBTBase;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagInt;
|
||||
import net.minecraft.server.v1_16_R3.PacketPlayOutMapChunk;
|
||||
import net.minecraft.server.v1_16_R3.PlayerChunk;
|
||||
import net.minecraft.server.v1_16_R3.TileEntity;
|
||||
import net.minecraft.server.v1_16_R3.World;
|
||||
import net.minecraft.server.v1_16_R3.WorldServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.block.CraftBlock;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
public final class FAWE_Spigot_v1_16_R3 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter<NBTBase> {
|
||||
private final Spigot_v1_16_R3 parent;
|
||||
private char[] ibdToStateOrdinal;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that may break between versions of Minecraft
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public FAWE_Spigot_v1_16_R3() throws NoSuchFieldException, NoSuchMethodException {
|
||||
this.parent = new Spigot_v1_16_R3();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitImplAdapter<NBTBase> getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
private synchronized boolean init() {
|
||||
if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) {
|
||||
return false;
|
||||
}
|
||||
ibdToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size
|
||||
for (int i = 0; i < ibdToStateOrdinal.length; i++) {
|
||||
BlockState state = BlockTypesCache.states[i];
|
||||
BlockMaterial_1_16_4 material = (BlockMaterial_1_16_4) state.getMaterial();
|
||||
int id = Block.REGISTRY_ID.getId(material.getState());
|
||||
ibdToStateOrdinal[id] = state.getOrdinalChar();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BlockType blockType) {
|
||||
Block block = getBlock(blockType);
|
||||
return new BlockMaterial_1_16_4(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BlockState state) {
|
||||
IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
||||
return new BlockMaterial_1_16_4(bs.getBlock(), bs);
|
||||
}
|
||||
|
||||
public Block getBlock(BlockType blockType) {
|
||||
return IRegistry.BLOCK.get(new MinecraftKey(blockType.getNamespace(), blockType.getResource()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public BaseBlock getBlock(Location location) {
|
||||
Preconditions.checkNotNull(location);
|
||||
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
|
||||
final WorldServer handle = craftWorld.getHandle();
|
||||
Chunk chunk = handle.getChunkAt(x >> 4, z >> 4);
|
||||
final BlockPosition blockPos = new BlockPosition(x, y, z);
|
||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
||||
BlockState state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
||||
|
||||
// Read the NBT data
|
||||
TileEntity te = chunk.a(blockPos, Chunk.EnumTileEntityState.CHECK);
|
||||
if (te != null) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
te.save(tag); // readTileEntityIntoTag - load data
|
||||
return state.toBaseBlock((CompoundTag) toNative(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;
|
||||
Chunk nmsChunk = craftChunk.getHandle();
|
||||
World nmsWorld = nmsChunk.getWorld();
|
||||
|
||||
BlockPosition blockPos = new BlockPosition(x, y, z);
|
||||
IBlockData blockData = ((BlockMaterial_1_16_4) state.getMaterial()).getState();
|
||||
ChunkSection[] sections = nmsChunk.getSections();
|
||||
int y4 = y >> 4;
|
||||
ChunkSection section = sections[y4];
|
||||
|
||||
IBlockData existing;
|
||||
if (section == null) {
|
||||
existing = ((BlockMaterial_1_16_4) BlockTypes.AIR.getDefaultState().getMaterial()).getState();
|
||||
} else {
|
||||
existing = section.getType(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
|
||||
nmsChunk.removeTileEntity(blockPos); // Force delete the old tile entity
|
||||
|
||||
CompoundTag nativeTag = state instanceof BaseBlock ? ((BaseBlock)state).getNbtData() : null;
|
||||
if (nativeTag != null || existing instanceof TileEntityBlock) {
|
||||
nmsWorld.setTypeAndData(blockPos, blockData, 0);
|
||||
// remove tile
|
||||
if (nativeTag != null) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the Forge version
|
||||
TileEntity tileEntity = nmsWorld.getTileEntity(blockPos);
|
||||
if (tileEntity != null) {
|
||||
NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag);
|
||||
tag.set("x", NBTTagInt.a(x));
|
||||
tag.set("y", NBTTagInt.a(y));
|
||||
tag.set("z", NBTTagInt.a(z));
|
||||
tileEntity.load(tileEntity.getBlock(), tag); // readTagIntoTileEntity - load data
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (existing == blockData) {
|
||||
return true;
|
||||
}
|
||||
if (section == null) {
|
||||
if (blockData.isAir()) {
|
||||
return true;
|
||||
}
|
||||
sections[y4] = section = new ChunkSection(y4 << 4);
|
||||
}
|
||||
nmsChunk.setType(blockPos, blockData, false);
|
||||
}
|
||||
if (update) {
|
||||
nmsWorld.getMinecraftWorld().notify(blockPos, existing, blockData, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||
return new FAWEWorldNativeAccess_1_16(this,
|
||||
new WeakReference<>(((CraftWorld)world).getHandle()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getEntityId(Entity entity) {
|
||||
MinecraftKey minecraftkey = EntityTypes.getName(entity.getEntityType());
|
||||
return minecraftkey == null ? null : minecraftkey.toString();
|
||||
}
|
||||
|
||||
private static void readEntityIntoTag(Entity entity, NBTTagCompound tag) {
|
||||
entity.save(tag);
|
||||
}
|
||||
|
||||
@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<CompoundTag> saveTag = () -> {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
readEntityIntoTag(mcEntity, tag);
|
||||
|
||||
//add Id for AbstractChangeSet to work
|
||||
CompoundTag natve = (CompoundTag) toNative(tag);
|
||||
natve.getValue().put("Id", new StringTag(id));
|
||||
return natve;
|
||||
};
|
||||
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) {
|
||||
BlockMaterial_1_16_4 material = (BlockMaterial_1_16_4) state.getMaterial();
|
||||
IBlockData mcState = material.getCraftBlockData().getState();
|
||||
return OptionalInt.of(Block.REGISTRY_ID.getId(mcState));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState adapt(BlockData blockData) {
|
||||
CraftBlockData cbd = ((CraftBlockData) blockData);
|
||||
IBlockData ibd = cbd.getState();
|
||||
return adapt(ibd);
|
||||
}
|
||||
|
||||
public BlockState adapt(IBlockData ibd) {
|
||||
return BlockTypesCache.states[adaptToChar(ibd)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Method unused. Use #adaptToChar(IBlockData).
|
||||
*/
|
||||
@Deprecated
|
||||
public int adaptToInt(IBlockData ibd) {
|
||||
synchronized (this) {
|
||||
try {
|
||||
int id = Block.REGISTRY_ID.getId(ibd);
|
||||
return ibdToStateOrdinal[id];
|
||||
} catch (NullPointerException e) {
|
||||
init();
|
||||
return adaptToInt(ibd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public char adaptToChar(IBlockData ibd) {
|
||||
synchronized (this) {
|
||||
try {
|
||||
int id = Block.REGISTRY_ID.getId(ibd);
|
||||
return ibdToStateOrdinal[id];
|
||||
} catch (NullPointerException e) {
|
||||
init();
|
||||
return adaptToChar(ibd);
|
||||
} catch (ArrayIndexOutOfBoundsException e1) {
|
||||
getLogger(FAWE_Spigot_v1_16_R3.class)
|
||||
.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!",
|
||||
ibd.getBlock(), Block.REGISTRY_ID.getId(ibd), ibdToStateOrdinal.length, e1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> BlockData adapt(B state) {
|
||||
BlockMaterial_1_16_4 material = (BlockMaterial_1_16_4) state.getMaterial();
|
||||
return material.getCraftBlockData();
|
||||
}
|
||||
|
||||
private MapChunkUtil_1_16_4 mapUtil = new MapChunkUtil_1_16_4();
|
||||
|
||||
@Override
|
||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket packet) {
|
||||
WorldServer nmsWorld = ((CraftWorld) world).getHandle();
|
||||
PlayerChunk map = BukkitAdapter_1_16_4.getPlayerChunk(nmsWorld, packet.getChunkX(), packet.getChunkZ());
|
||||
if (map != null && map.hasBeenLoaded()) {
|
||||
boolean flag = false;
|
||||
PlayerChunk.d players = map.players;
|
||||
Stream<EntityPlayer> stream = players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag);
|
||||
|
||||
EntityPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
|
||||
stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
|
||||
.forEach(entityPlayer -> {
|
||||
synchronized (packet) {
|
||||
PacketPlayOutMapChunk nmsPacket = (PacketPlayOutMapChunk) packet.getNativePacket();
|
||||
if (nmsPacket == null) {
|
||||
nmsPacket = mapUtil.create( this, packet);
|
||||
packet.setNativePacket(nmsPacket);
|
||||
}
|
||||
try {
|
||||
FaweCache.IMP.CHUNK_FLAG.get().set(true);
|
||||
entityPlayer.playerConnection.sendPacket(nmsPacket);
|
||||
} finally {
|
||||
FaweCache.IMP.CHUNK_FLAG.get().set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Property<?>> getProperties(BlockType blockType) {
|
||||
return getParent().getProperties(blockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack adapt(BaseItemStack item) {
|
||||
ItemStack stack = new ItemStack(IRegistry.ITEM.get(MinecraftKey.a(item.getType().getId())), item.getAmount());
|
||||
stack.setTag(((NBTTagCompound) fromNative(item.getNbtData())));
|
||||
return CraftItemStack.asCraftMirror(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
||||
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
||||
final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount());
|
||||
weStack.setNbtData(((CompoundTag) toNative(nmsStack.getTag())));
|
||||
return weStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toNative(NBTBase foreign) {
|
||||
return parent.toNative(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTBase fromNative(Tag foreign) {
|
||||
if (foreign instanceof LazyCompoundTag_1_16_4) {
|
||||
return ((LazyCompoundTag_1_16_4) foreign).get();
|
||||
}
|
||||
return parent.fromNative(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
||||
return new Regen_v1_16_R3(bukkitWorld, region, target, options).regenerate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) {
|
||||
return new BukkitGetBlocks_1_16_4(world, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInternalBiomeId(BiomeType biome) {
|
||||
BiomeBase base = CraftBlock.biomeToBiomeBase(MinecraftServer.getServer().getCustomRegistry().b(IRegistry.ay), BukkitAdapter.adapt(biome));
|
||||
return MinecraftServer.getServer().getCustomRegistry().b(IRegistry.ay).a(base);
|
||||
}
|
||||
}
|
@ -505,5 +505,9 @@ public class Regen_v1_15_R2 extends Regenerator<IChunkAccess, ProtoChunk, Chunk,
|
||||
@Override
|
||||
public void b() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChunkRadius(int i) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -551,5 +551,9 @@ public class Regen_v1_16_R1 extends Regenerator<IChunkAccess, ProtoChunk, Chunk,
|
||||
@Override
|
||||
public void b() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChunkRadius(int i) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -574,5 +574,9 @@ public class Regen_v1_16_R2 extends Regenerator<IChunkAccess, ProtoChunk, Chunk,
|
||||
@Override
|
||||
public void b() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChunkRadius(int i) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,583 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.regen;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.beta.IChunkCache;
|
||||
import com.boydti.fawe.beta.IChunkGet;
|
||||
import com.boydti.fawe.bukkit.adapter.mc1_16_4.BukkitGetBlocks_1_16_4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Regenerator;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import net.minecraft.server.v1_16_R3.Area;
|
||||
import net.minecraft.server.v1_16_R3.AreaContextTransformed;
|
||||
import net.minecraft.server.v1_16_R3.AreaFactory;
|
||||
import net.minecraft.server.v1_16_R3.AreaTransformer8;
|
||||
import net.minecraft.server.v1_16_R3.BiomeBase;
|
||||
import net.minecraft.server.v1_16_R3.BiomeRegistry;
|
||||
import net.minecraft.server.v1_16_R3.Chunk;
|
||||
import net.minecraft.server.v1_16_R3.ChunkConverter;
|
||||
import net.minecraft.server.v1_16_R3.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_16_R3.ChunkGenerator;
|
||||
import net.minecraft.server.v1_16_R3.ChunkGeneratorAbstract;
|
||||
import net.minecraft.server.v1_16_R3.ChunkProviderFlat;
|
||||
import net.minecraft.server.v1_16_R3.ChunkProviderServer;
|
||||
import net.minecraft.server.v1_16_R3.ChunkStatus;
|
||||
import net.minecraft.server.v1_16_R3.Convertable;
|
||||
import net.minecraft.server.v1_16_R3.DefinedStructureManager;
|
||||
import net.minecraft.server.v1_16_R3.DynamicOpsNBT;
|
||||
import net.minecraft.server.v1_16_R3.GenLayer;
|
||||
import net.minecraft.server.v1_16_R3.GenLayers;
|
||||
import net.minecraft.server.v1_16_R3.GeneratorSettingBase;
|
||||
import net.minecraft.server.v1_16_R3.GeneratorSettings;
|
||||
import net.minecraft.server.v1_16_R3.GeneratorSettingsFlat;
|
||||
import net.minecraft.server.v1_16_R3.IChunkAccess;
|
||||
import net.minecraft.server.v1_16_R3.IRegistry;
|
||||
import net.minecraft.server.v1_16_R3.IRegistryCustom;
|
||||
import net.minecraft.server.v1_16_R3.LightEngineThreaded;
|
||||
import net.minecraft.server.v1_16_R3.LinearCongruentialGenerator;
|
||||
import net.minecraft.server.v1_16_R3.MinecraftKey;
|
||||
import net.minecraft.server.v1_16_R3.MinecraftServer;
|
||||
import net.minecraft.server.v1_16_R3.NBTBase;
|
||||
import net.minecraft.server.v1_16_R3.NBTTagCompound;
|
||||
import net.minecraft.server.v1_16_R3.NoiseGeneratorPerlin;
|
||||
import net.minecraft.server.v1_16_R3.ProtoChunk;
|
||||
import net.minecraft.server.v1_16_R3.RegistryGeneration;
|
||||
import net.minecraft.server.v1_16_R3.RegistryMaterials;
|
||||
import net.minecraft.server.v1_16_R3.RegistryReadOps;
|
||||
import net.minecraft.server.v1_16_R3.ResourceKey;
|
||||
import net.minecraft.server.v1_16_R3.World;
|
||||
import net.minecraft.server.v1_16_R3.WorldChunkManager;
|
||||
import net.minecraft.server.v1_16_R3.WorldChunkManagerOverworld;
|
||||
import net.minecraft.server.v1_16_R3.WorldDataServer;
|
||||
import net.minecraft.server.v1_16_R3.WorldDimension;
|
||||
import net.minecraft.server.v1_16_R3.WorldLoadListener;
|
||||
import net.minecraft.server.v1_16_R3.WorldServer;
|
||||
import net.minecraft.server.v1_16_R3.WorldSettings;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_16_R3.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.LongFunction;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Regen_v1_16_R3 extends Regenerator<IChunkAccess, ProtoChunk, Chunk, Regen_v1_16_R3.ChunkStatusWrap> {
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field worldPaperConfigField;
|
||||
private static final Field flatBedrockField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkProviderField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Regenerator.Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Regenerator.Concurrency.FULL); // radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Regenerator.Concurrency.NONE); // uses unsynchronized maps
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_REFERENCES, Regenerator.Concurrency.FULL); // radius 8, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Regenerator.Concurrency.FULL); // radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Regenerator.Concurrency.RADIUS); // radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Regenerator.Concurrency.FULL); // radius 0
|
||||
chunkStati.put(ChunkStatus.CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.LIQUID_CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Regenerator.Concurrency.NONE); // uses unsynchronized maps
|
||||
chunkStati.put(ChunkStatus.LIGHT, Regenerator.Concurrency.FULL); // radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Regenerator.Concurrency.FULL); // radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Regenerator.Concurrency.FULL); // radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField = null;
|
||||
Field tmpFlatBedrockField = null;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = World.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
worldPaperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = ChunkGeneratorAbstract.class.getDeclaredField("h");
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = ChunkProviderFlat.class.getDeclaredField("e");
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkProviderField = WorldServer.class.getDeclaredField("chunkProvider");
|
||||
chunkProviderField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private WorldServer originalNMSWorld;
|
||||
private ChunkProviderServer originalChunkProvider;
|
||||
private WorldServer freshNMSWorld;
|
||||
private ChunkProviderServer freshChunkProvider;
|
||||
private Convertable.ConversionSession session;
|
||||
private DefinedStructureManager structureManager;
|
||||
private LightEngineThreaded lightEngine;
|
||||
private ChunkGenerator generator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public Regen_v1_16_R3(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalNMSWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalNMSWorld.getChunkProvider();
|
||||
if (!(originalChunkProvider instanceof ChunkProviderServer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalNMSWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalNMSWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("WorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment env = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator gen = originalBukkitWorld.getGenerator();
|
||||
Convertable convertable = Convertable.a(tempDir);
|
||||
ResourceKey<WorldDimension> worldDimKey = getWorldDimKey(env);
|
||||
session = convertable.c("worldeditregentempworld", worldDimKey);
|
||||
WorldDataServer originalWorldData = originalNMSWorld.worldDataServer;
|
||||
|
||||
MinecraftServer server = originalNMSWorld.getServer().getServer();
|
||||
WorldDataServer levelProperties = (WorldDataServer) server.getSaveData();
|
||||
RegistryReadOps<NBTBase> nbtRegOps = RegistryReadOps.a(DynamicOpsNBT.a, server.dataPackResources.h(), IRegistryCustom.b());
|
||||
GeneratorSettings newOpts = GeneratorSettings.a.encodeStart(nbtRegOps, levelProperties.getGeneratorSettings()).flatMap(tag -> GeneratorSettings.a.parse(this.recursivelySetSeed(new Dynamic<>(nbtRegOps, tag), seed, new HashSet<>()))).result().orElseThrow(() -> new IllegalStateException("Unable to map GeneratorOptions"));
|
||||
WorldSettings newWorldSettings = new WorldSettings("worldeditregentempworld", originalWorldData.b.getGameType(), originalWorldData.b.hardcore, originalWorldData.b.getDifficulty(), originalWorldData.b.e(), originalWorldData.b.getGameRules(), originalWorldData.b.g());
|
||||
WorldDataServer newWorldData = new WorldDataServer(newWorldSettings, newOpts, Lifecycle.stable());
|
||||
|
||||
//init world
|
||||
freshNMSWorld = Fawe.get().getQueueHandler().sync((Supplier<WorldServer>) () -> new WorldServer(server, server.executorService, session, newWorldData, originalNMSWorld.getDimensionKey(), originalNMSWorld.getDimensionManager(), new RegenNoOpWorldLoadListener(), ((WorldDimension) newOpts.d().a(worldDimKey)).c(), originalNMSWorld.isDebugWorld(), seed, ImmutableList.of(), false, env, gen) {
|
||||
@Override
|
||||
public void doTick(BooleanSupplier booleansupplier) { //no ticking
|
||||
}
|
||||
|
||||
private final BiomeBase singleBiome = options.hasBiomeType() ? RegistryGeneration.WORLDGEN_BIOME.get(MinecraftKey.a(options.getBiomeType().getId())) : null;
|
||||
|
||||
@Override
|
||||
public BiomeBase a(int i, int j, int k) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return this.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(i, j, k);
|
||||
}
|
||||
}).get();
|
||||
freshNMSWorld.savingDisabled = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalNMSWorld.worldDataServer.getName()); //rename to original world name
|
||||
|
||||
freshChunkProvider = new ChunkProviderServer(freshNMSWorld, session, server.getDataFixer(), server.getDefinedStructureManager(), server.executorService, originalChunkProvider.chunkGenerator, freshNMSWorld.spigotConfig.viewDistance, server.isSyncChunkWrites(), new RegenNoOpWorldLoadListener(), () -> server.E().getWorldPersistentData()) {
|
||||
// redirect to our protoChunks list
|
||||
@Override
|
||||
public IChunkAccess getChunkAt(int x, int z, ChunkStatus chunkstatus, boolean flag) {
|
||||
return getProtoChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
chunkProviderField.set(freshNMSWorld, freshChunkProvider);
|
||||
|
||||
//generator
|
||||
if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderFlat) {
|
||||
GeneratorSettingsFlat generatorSettingFlat = (GeneratorSettingsFlat) generatorSettingFlatField.get(originalChunkProvider.getChunkGenerator());
|
||||
generator = new ChunkProviderFlat(generatorSettingFlat);
|
||||
} else if (originalChunkProvider.getChunkGenerator() instanceof ChunkGeneratorAbstract) {
|
||||
Supplier<GeneratorSettingBase> generatorSettingBaseSupplier = (Supplier<GeneratorSettingBase>) generatorSettingBaseSupplierField.get(originalChunkProvider.getChunkGenerator());
|
||||
WorldChunkManager chunkManager = originalChunkProvider.getChunkGenerator().getWorldChunkManager();
|
||||
if (chunkManager instanceof WorldChunkManagerOverworld) {
|
||||
chunkManager = fastOverWorldChunkManager(chunkManager);
|
||||
}
|
||||
generator = new ChunkGeneratorAbstract(chunkManager, seed, generatorSettingBaseSupplier);
|
||||
} else if (originalChunkProvider.getChunkGenerator() instanceof CustomChunkGenerator) {
|
||||
ChunkGenerator delegate = (ChunkGenerator) delegateField.get(originalChunkProvider.getChunkGenerator());
|
||||
generator = delegate;
|
||||
} else {
|
||||
System.out.println("Unsupported generator type " + originalChunkProvider.getChunkGenerator().getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (originalNMSWorld.generator != null) {
|
||||
// wrap custom world generator
|
||||
generator = new CustomChunkGenerator(freshNMSWorld, generator, originalNMSWorld.generator);
|
||||
generateConcurrent = originalNMSWorld.generator.isParallelCapable();
|
||||
}
|
||||
|
||||
//lets start then
|
||||
structureManager = server.getDefinedStructureManager();
|
||||
lightEngine = freshChunkProvider.getLightEngine();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.get().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.get().getQueueHandler().sync(() -> {
|
||||
removeWorldFromWorldsMap();
|
||||
});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new ProtoChunk(new ChunkCoordIntPair(x, z), ChunkConverter.a) {
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Chunk createChunk(ProtoChunk protoChunk) {
|
||||
return new Chunk(freshNMSWorld, protoChunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalNMSWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(Chunk chunk, Random random, BlockPopulator pop) {
|
||||
pop.populate(freshNMSWorld.getWorld(), random, chunk.bukkitChunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new BukkitGetBlocks_1_16_4(freshNMSWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public Chunk ensureLoaded(World nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends Regenerator.ChunkStatusWrapper<IChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeigborChunkRadius() {
|
||||
return chunkStatus.f();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.d();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processChunk(Long xz, List<IChunkAccess> accessibleChunks) {
|
||||
chunkStatus.a(freshNMSWorld,
|
||||
generator,
|
||||
structureManager,
|
||||
lightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks);
|
||||
}
|
||||
}
|
||||
|
||||
//util
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.get().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("worldeditregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<WorldDimension> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
switch (env) {
|
||||
case NETHER:
|
||||
return WorldDimension.THE_NETHER;
|
||||
case THE_END:
|
||||
return WorldDimension.THE_END;
|
||||
case NORMAL:
|
||||
default:
|
||||
return WorldDimension.OVERWORLD;
|
||||
}
|
||||
}
|
||||
|
||||
private Dynamic<NBTBase> recursivelySetSeed(Dynamic<NBTBase> dynamic, long seed, Set<Dynamic<NBTBase>> seen) {
|
||||
return !seen.add(dynamic) ? dynamic : dynamic.updateMapValues((pair) -> {
|
||||
if (((Dynamic) pair.getFirst()).asString("").equals("seed")) {
|
||||
return pair.mapSecond((v) -> {
|
||||
return v.createLong(seed);
|
||||
});
|
||||
} else {
|
||||
return ((Dynamic) pair.getSecond()).getValue() instanceof NBTTagCompound ? pair.mapSecond((v) -> {
|
||||
return this.recursivelySetSeed((Dynamic) v, seed, seen);
|
||||
}) : pair;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private WorldChunkManager fastOverWorldChunkManager(WorldChunkManager chunkManager) throws Exception {
|
||||
Field legacyBiomeInitLayerField = WorldChunkManagerOverworld.class.getDeclaredField("i");
|
||||
legacyBiomeInitLayerField.setAccessible(true);
|
||||
Field largeBiomesField = WorldChunkManagerOverworld.class.getDeclaredField("j");
|
||||
largeBiomesField.setAccessible(true);
|
||||
Field biomeRegistryField = WorldChunkManagerOverworld.class.getDeclaredField("k");
|
||||
biomeRegistryField.setAccessible(true);
|
||||
Field areaLazyField = GenLayer.class.getDeclaredField("b");
|
||||
areaLazyField.setAccessible(true);
|
||||
Method initAreaFactoryMethod = GenLayers.class.getDeclaredMethod("a", boolean.class, int.class, int.class, LongFunction.class);
|
||||
initAreaFactoryMethod.setAccessible(true);
|
||||
|
||||
//init new WorldChunkManagerOverworld
|
||||
boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(chunkManager);
|
||||
boolean largebiomes = largeBiomesField.getBoolean(chunkManager);
|
||||
IRegistry<BiomeBase> biomeRegistrynms = (IRegistry<BiomeBase>) biomeRegistryField.get(chunkManager);
|
||||
IRegistry<BiomeBase> biomeRegistry;
|
||||
if (options.hasBiomeType()) {
|
||||
BiomeBase biome = RegistryGeneration.WORLDGEN_BIOME.get(MinecraftKey.a(options.getBiomeType().getId()));
|
||||
biomeRegistry = new RegistryMaterials<>(ResourceKey.a(new MinecraftKey("fawe_biomes")), Lifecycle.experimental());
|
||||
((RegistryMaterials) biomeRegistry).a(0, RegistryGeneration.WORLDGEN_BIOME.c(biome).get(), biome, Lifecycle.experimental());
|
||||
} else {
|
||||
biomeRegistry = biomeRegistrynms;
|
||||
}
|
||||
chunkManager = new FastWorldChunkManagerOverworld(seed, legacyBiomeInitLayer, largebiomes, biomeRegistry);
|
||||
|
||||
//replace genLayer
|
||||
AreaFactory<FastAreaLazy> factory = (AreaFactory<FastAreaLazy>) initAreaFactoryMethod.invoke(null, legacyBiomeInitLayer, largebiomes ? 6 : 4, 4, (LongFunction) (l -> new FastWorldGenContextArea(seed, l)));
|
||||
((FastWorldChunkManagerOverworld) chunkManager).genLayer = new FastGenLayer(factory);
|
||||
|
||||
return chunkManager;
|
||||
}
|
||||
|
||||
private static class FastWorldChunkManagerOverworld extends WorldChunkManager {
|
||||
|
||||
private GenLayer genLayer;
|
||||
private final IRegistry<BiomeBase> k;
|
||||
private final boolean isSingleRegistry;
|
||||
|
||||
public FastWorldChunkManagerOverworld(long seed, boolean legacyBiomeInitLayer, boolean largeBiomes, IRegistry<BiomeBase> biomeRegistry) {
|
||||
super(biomeRegistry.g().collect(Collectors.toList()));
|
||||
this.k = biomeRegistry;
|
||||
this.isSingleRegistry = biomeRegistry.d().size() == 1;
|
||||
this.genLayer = GenLayers.a(seed, legacyBiomeInitLayer, largeBiomes ? 6 : 4, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Codec<? extends WorldChunkManager> a() {
|
||||
return WorldChunkManagerOverworld.e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeBase getBiome(int i, int i1, int i2) {
|
||||
if (this.isSingleRegistry) {
|
||||
return this.k.fromId(0);
|
||||
}
|
||||
return this.genLayer.a(this.k, i, i2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class FastWorldGenContextArea implements AreaContextTransformed<FastAreaLazy> {
|
||||
|
||||
private final ConcurrentHashMap<Long, Integer> sharedAreaMap = new ConcurrentHashMap<>();
|
||||
private final NoiseGeneratorPerlin perlinNoise;
|
||||
private final long magicrandom;
|
||||
private final ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(); //needed for multithreaded generation
|
||||
|
||||
public FastWorldGenContextArea(long seed, long lconst) {
|
||||
this.magicrandom = mix(seed, lconst);
|
||||
this.perlinNoise = new NoiseGeneratorPerlin(new Random(seed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastAreaLazy a(AreaTransformer8 var0) {
|
||||
return new FastAreaLazy(sharedAreaMap, var0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(long x, long z) {
|
||||
long l = this.magicrandom;
|
||||
l = LinearCongruentialGenerator.a(l, x);
|
||||
l = LinearCongruentialGenerator.a(l, z);
|
||||
l = LinearCongruentialGenerator.a(l, x);
|
||||
l = LinearCongruentialGenerator.a(l, z);
|
||||
this.map.put(Thread.currentThread().getId(), l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int a(int y) {
|
||||
long tid = Thread.currentThread().getId();
|
||||
long e = this.map.computeIfAbsent(tid, i -> 0L);
|
||||
int mod = (int) Math.floorMod(e >> 24L, (long) y);
|
||||
this.map.put(tid, LinearCongruentialGenerator.a(e, this.magicrandom));
|
||||
return mod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoiseGeneratorPerlin b() {
|
||||
return this.perlinNoise;
|
||||
}
|
||||
|
||||
private static long mix(long seed, long lconst) {
|
||||
long l1 = lconst;
|
||||
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||
long l2 = seed;
|
||||
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||
return l2;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FastGenLayer extends GenLayer {
|
||||
|
||||
private final FastAreaLazy areaLazy;
|
||||
|
||||
public FastGenLayer(AreaFactory<FastAreaLazy> factory) throws Exception {
|
||||
super(() -> null);
|
||||
this.areaLazy = factory.make();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeBase a(IRegistry<BiomeBase> registry, int x, int z) {
|
||||
ResourceKey<BiomeBase> key = BiomeRegistry.a(this.areaLazy.a(x, z));
|
||||
if (key == null)
|
||||
return registry.a(BiomeRegistry.a(0));
|
||||
BiomeBase biome = registry.a(key);
|
||||
if (biome == null)
|
||||
return registry.a(BiomeRegistry.a(0));
|
||||
return biome;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FastAreaLazy implements Area {
|
||||
|
||||
private final AreaTransformer8 transformer;
|
||||
//ConcurrentHashMap is 50% faster that Long2IntLinkedOpenHashMap in a syncronized context
|
||||
//using a map for each thread worsens the performance significantly due to cache misses (factor 5)
|
||||
private final ConcurrentHashMap<Long, Integer> sharedMap;
|
||||
|
||||
public FastAreaLazy(ConcurrentHashMap<Long, Integer> sharedMap, AreaTransformer8 transformer) {
|
||||
this.sharedMap = sharedMap;
|
||||
this.transformer = transformer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int a(int x, int z) {
|
||||
long zx = ChunkCoordIntPair.pair(x, z);
|
||||
return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z));
|
||||
}
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements WorldLoadListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(ChunkCoordIntPair chunkCoordIntPair) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(ChunkCoordIntPair chunkCoordIntPair, @Nullable ChunkStatus chunkStatus) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void b() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChunkRadius(int i) {
|
||||
}
|
||||
}
|
||||
}
|
@ -5,10 +5,10 @@ load: STARTUP
|
||||
api-version: 1.15
|
||||
softdepend: [Vault]
|
||||
provides: [WorldEdit]
|
||||
website: https://intellectualsites.github.io/download/fawe.html
|
||||
website: https://www.spigotmc.org/resources/13932/
|
||||
description: Blazingly fast world manipulation for builders, large networks and developers.
|
||||
authors: [Empire92, MattBDev, IronApollo, dordsor21, NotMyFault]
|
||||
loadbefore: [BannerBoard, WorldGuard, PlotSquared]
|
||||
loadbefore: [WorldGuard, PlotSquared]
|
||||
database: false
|
||||
permissions:
|
||||
fawe.plotsquared:
|
||||
|
Binäre Datei nicht angezeigt.
@ -1,5 +1,6 @@
|
||||
package com.boydti.fawe;
|
||||
|
||||
/** An internal FAWE class not meant for public use. **/
|
||||
public class FaweVersion {
|
||||
public final int year;
|
||||
public final int month;
|
||||
|
@ -57,7 +57,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
||||
if (biomes == null) {
|
||||
return null;
|
||||
}
|
||||
return biomes[y << 2 | z & 12 | x >> 2];
|
||||
return biomes[(y >> 2) << 4 | (z & 3) << 2 | x & 3];
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -90,7 +90,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
||||
if (biomes == null) {
|
||||
biomes = new BiomeType[1024];
|
||||
}
|
||||
biomes[y << 2 | z & 12 | x >> 2] = biome;
|
||||
biomes[(y >> 2) << 4 | (z & 3) << 2 | x & 3] = biome;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,11 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
|
||||
|
||||
@Override
|
||||
public final void applyBlock(FilterBlock block) {
|
||||
counter[block.getOrdinal()]++;
|
||||
int ordinal = block.getOrdinal();
|
||||
if (ordinal == 0) {
|
||||
ordinal = 1;
|
||||
}
|
||||
counter[ordinal]++;
|
||||
}
|
||||
|
||||
public int getTotal(ABlockMask mask) {
|
||||
|
@ -178,12 +178,12 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
|
||||
|
||||
@Override
|
||||
public List<Countable<BlockState>> getBlockDistributionWithData(Region region) {
|
||||
return apply(region, new DistrFilter(), false).getDistribution();
|
||||
return apply(region, new DistrFilter(), true).getDistribution();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Countable<BlockType>> getBlockDistribution(Region region) {
|
||||
return apply(region, new DistrFilter(), false).getTypeDistribution();
|
||||
return apply(region, new DistrFilter(), true).getTypeDistribution();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -394,6 +394,18 @@ public class Settings extends Config {
|
||||
" - Requires combine_stages = true"
|
||||
})
|
||||
public boolean SEND_BEFORE_HISTORY = false;
|
||||
|
||||
@Comment({
|
||||
"Sets a maximum limit (in kb) for the size of a player's schematics directory (per-player mode only)",
|
||||
"Set to -1 to disable"
|
||||
})
|
||||
public int PER_PLAYER_FILE_SIZE_LIMIT = -1;
|
||||
|
||||
@Comment({
|
||||
"Sets a maximum limit for the amount of schematics in a player's schematics directory (per-player mode only)",
|
||||
"Set to -1 to disable"
|
||||
})
|
||||
public int PER_PLAYER_FILE_NUM_LIMIT = -1;
|
||||
}
|
||||
|
||||
public static class PLOTSQUARED_INTEGRATION {
|
||||
|
@ -481,7 +481,7 @@ public class MCAChunk implements IChunk {
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
return this.biomes[y << 2 | z & 12 | x >> 2];
|
||||
return this.biomes[(y >> 2) << 4 | (z & 3) << 2 | x & 3];
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -505,7 +505,7 @@ public class MCAChunk implements IChunk {
|
||||
@Override
|
||||
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||
setModified();
|
||||
biomes[y << 2 | z & 12 | x >> 2] = biome;
|
||||
biomes[(y >> 2) << 4 | (z & 3) << 2 | x & 3] = biome;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -116,14 +116,8 @@ public class CPUOptimizedClipboard extends LinearClipboard {
|
||||
return nbtMapIndex.get(index);
|
||||
}
|
||||
|
||||
private int yLast;
|
||||
private int yLastI;
|
||||
private int zLast;
|
||||
private int zLastI;
|
||||
|
||||
public int getIndex(int x, int y, int z) {
|
||||
return x + ((yLast == y) ? yLastI : (yLastI = (yLast = y) * getArea())) + ((zLast == z) ? zLastI
|
||||
: (zLastI = (zLast = z) * getWidth()));
|
||||
return x + y * getArea() + z * getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -324,8 +324,7 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
|
||||
}
|
||||
|
||||
public int getIndex(int x, int y, int z) {
|
||||
return x + (ylast == y ? ylasti : (ylasti = (ylast = y) * getArea())) + (zlast == z
|
||||
? zlasti : (zlasti = (zlast = z) * getWidth()));
|
||||
return x + y * getArea() + z * getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -193,13 +193,8 @@ public class MemoryOptimizedClipboard extends LinearClipboard {
|
||||
return nbtMap.values();
|
||||
}
|
||||
|
||||
private int ylast;
|
||||
private int ylasti;
|
||||
private int zlast;
|
||||
private int zlasti;
|
||||
|
||||
public int getIndex(int x, int y, int z) {
|
||||
return x + ((ylast == y) ? ylasti : (ylasti = (ylast = y) * getArea())) + ((zlast == z) ? zlasti : (zlasti = (zlast = z) * getWidth()));
|
||||
return x + y * getArea() + z * getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,13 +9,14 @@ import java.util.Base64;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
public enum Jars {
|
||||
/** An internal FAWE class not meant for public use. **/
|
||||
public enum ThirdPartyManager {
|
||||
|
||||
MM_v1_7_8(
|
||||
MapManager(
|
||||
"https://github.com/InventivetalentDev/MapManager/releases/download/1.7.8-SNAPSHOT/MapManager_v1.7.8-SNAPSHOT.jar",
|
||||
"m3YLUqZz66k2DmvdcYLeu38u3zKRKhrAXqGGpVKfO6g=", 544000),
|
||||
|
||||
PL_v3_7_6(
|
||||
PacketListenerAPI(
|
||||
"https://github.com/InventivetalentDev/PacketListenerAPI/releases/download/3.7.6-SNAPSHOT/PacketListenerAPI_v3.7.6-SNAPSHOT.jar",
|
||||
"etdBRzLn5pRVDfr/mSQdPm6Jjer3wQOKhcn8fUxo5zM=", 143000),
|
||||
|
||||
@ -30,7 +31,7 @@ public enum Jars {
|
||||
* @param digest The Base64-encoded SHA-256 digest
|
||||
* @param fileSize Size of this jar in bytes
|
||||
*/
|
||||
Jars(String url, String digest, int fileSize) {
|
||||
ThirdPartyManager(String url, String digest, int fileSize) {
|
||||
this.url = url;
|
||||
this.digest = digest;
|
||||
this.fileSize = fileSize;
|
||||
@ -45,7 +46,7 @@ public enum Jars {
|
||||
try (DataInputStream dis = new DataInputStream(url.openConnection().getInputStream())) {
|
||||
dis.readFully(jarBytes);
|
||||
if (dis.read() != -1) { // assert that we've read everything
|
||||
throw new IllegalStateException("downloaded jar is longer than expected");
|
||||
throw new IllegalStateException("Downloaded jar is longer than expected");
|
||||
}
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] jarDigestBytes = md.digest(jarBytes);
|
||||
@ -53,12 +54,12 @@ public enum Jars {
|
||||
String jarDigest = Base64.getEncoder().encodeToString(jarDigestBytes);
|
||||
|
||||
if (this.digest.equals(jarDigest)) {
|
||||
getLogger(Jars.class).debug("++++ HASH CHECK ++++");
|
||||
getLogger(Jars.class).debug(this.url);
|
||||
getLogger(Jars.class).debug(this.digest);
|
||||
getLogger(ThirdPartyManager.class).debug("++++ HASH CHECK ++++");
|
||||
getLogger(ThirdPartyManager.class).debug(this.url);
|
||||
getLogger(ThirdPartyManager.class).debug(this.digest);
|
||||
return jarBytes;
|
||||
} else {
|
||||
getLogger(Jars.class).debug(jarDigest + " | " + url);
|
||||
getLogger(ThirdPartyManager.class).debug(jarDigest + " | " + url);
|
||||
throw new IllegalStateException("The downloaded jar does not match the hash");
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
@ -3,6 +3,8 @@ package com.boydti.fawe.wrappers;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
|
||||
|
||||
//TODO: Add proper documenation for this class describing what it is. Is it just a wrapper for a player so no messages are printed anywhere?
|
||||
/**
|
||||
* Avoids printing any messages
|
||||
*/
|
||||
|
@ -1326,7 +1326,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
*/
|
||||
@Deprecated
|
||||
public <B extends BlockStateHolder<B>> int makeCuboidFaces(Region region, B block) throws MaxChangedBlocksException {
|
||||
return makeCuboidFaces(region, block);
|
||||
return makeCuboidFaces(region, (Pattern) block);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2559,7 +2559,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
||||
int zv = (int) (z.getValue() * unit.getZ() + zero2.getZ());
|
||||
|
||||
BlockState get;
|
||||
if (yv >= 0 && yv < 265) {
|
||||
if (yv >= 0 && yv < 256) {
|
||||
get = getBlock(xv, yv, zv);
|
||||
} else {
|
||||
get = BlockTypes.AIR.getDefaultState();
|
||||
|
@ -1066,13 +1066,13 @@ public class LocalSession implements TextureHolder {
|
||||
private transient boolean loadDefaults = true;
|
||||
|
||||
public Tool getTool(BaseItem item, Player player) {
|
||||
loadDefaults(player, false);
|
||||
if (Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES && item.getNativeItem() != null) {
|
||||
BrushTool tool = BrushCache.getTool(player, this, item);
|
||||
if (tool != null) {
|
||||
return tool;
|
||||
}
|
||||
}
|
||||
loadDefaults(player, false);
|
||||
return getTool(item.getType());
|
||||
}
|
||||
|
||||
@ -1122,6 +1122,9 @@ public class LocalSession implements TextureHolder {
|
||||
}
|
||||
|
||||
public BrushTool getBrushTool(BaseItem item, Player player, boolean create) throws InvalidToolBindException {
|
||||
if (item.getType().hasBlockType()) {
|
||||
throw new InvalidToolBindException(item.getType(), "Blocks can't be used");
|
||||
}
|
||||
Tool tool = getTool(item, player);
|
||||
if (!(tool instanceof BrushTool)) {
|
||||
if (create) {
|
||||
|
@ -591,15 +591,17 @@ public final class WorldEdit {
|
||||
Map<BlockType, Integer> missingBlocks = editSession.popMissingBlocks();
|
||||
|
||||
if (!missingBlocks.isEmpty()) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
TextComponent.Builder str = TextComponent.builder();
|
||||
str.append("Missing these blocks: ");
|
||||
int size = missingBlocks.size();
|
||||
int i = 0;
|
||||
|
||||
for (Map.Entry<BlockType, Integer> blockTypeIntegerEntry : missingBlocks.entrySet()) {
|
||||
str.append((blockTypeIntegerEntry.getKey()).getName());
|
||||
str.append((blockTypeIntegerEntry.getKey()).getRichName());
|
||||
|
||||
str.append(" [Amt: ").append(blockTypeIntegerEntry.getValue()).append("]");
|
||||
str.append(" [Amt: ")
|
||||
.append(String.valueOf(blockTypeIntegerEntry.getValue()))
|
||||
.append("]");
|
||||
|
||||
++i;
|
||||
|
||||
@ -608,7 +610,7 @@ public final class WorldEdit {
|
||||
}
|
||||
}
|
||||
|
||||
actor.printError(str.toString());
|
||||
actor.printError(str.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,9 @@
|
||||
package com.sk89q.worldedit.blocks;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
|
||||
/**
|
||||
@ -65,7 +68,7 @@ public class BaseItemStack extends BaseItem {
|
||||
|
||||
/**
|
||||
* Get the number of items in the stack.
|
||||
*
|
||||
*
|
||||
* @return the amount
|
||||
*/
|
||||
public int getAmount() {
|
||||
@ -74,10 +77,15 @@ public class BaseItemStack extends BaseItem {
|
||||
|
||||
/**
|
||||
* Set the amount of items in the stack.
|
||||
*
|
||||
*
|
||||
* @param amount the amount to set
|
||||
*/
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public Component getRichName() {
|
||||
return WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
||||
.getRegistries().getItemRegistry().getRichName(this);
|
||||
}
|
||||
}
|
||||
|
@ -32,11 +32,14 @@ import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.function.FlatRegionFunction;
|
||||
import com.sk89q.worldedit.function.FlatRegionMaskingFilter;
|
||||
import com.sk89q.worldedit.function.RegionFunction;
|
||||
import com.sk89q.worldedit.function.RegionMaskingFilter;
|
||||
import com.sk89q.worldedit.function.biome.BiomeReplace;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.Mask2D;
|
||||
import com.sk89q.worldedit.function.operation.Operations;
|
||||
import com.sk89q.worldedit.function.visitor.FlatRegionVisitor;
|
||||
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
@ -49,7 +52,6 @@ import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.biome.BiomeData;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.registry.BiomeRegistry;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
@ -87,23 +89,19 @@ public class BiomeCommands {
|
||||
@ArgFlag(name = 'p', desc = "Page number.", def = "1")
|
||||
int page) {
|
||||
WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> {
|
||||
BiomeRegistry biomeRegistry =
|
||||
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
||||
.getRegistries().getBiomeRegistry();
|
||||
BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager()
|
||||
.queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry();
|
||||
|
||||
PaginationBox paginationBox = PaginationBox
|
||||
.fromStrings("Available Biomes", "/biomelist -p %page%",
|
||||
BiomeType.REGISTRY.values().stream().map(biomeType -> {
|
||||
String id = biomeType.getId();
|
||||
final BiomeData data = biomeRegistry.getData(biomeType);
|
||||
if (data != null) {
|
||||
String name = data.getName();
|
||||
return id + " (" + name + ")";
|
||||
} else {
|
||||
return id;
|
||||
}
|
||||
}).collect(Collectors.toList()));
|
||||
return paginationBox.create(page);
|
||||
PaginationBox paginationBox = PaginationBox.fromComponents("Available Biomes", "/biomelist -p %page%",
|
||||
BiomeType.REGISTRY.values().stream()
|
||||
.map(biomeType -> TextComponent.builder()
|
||||
.append(biomeType.getId())
|
||||
.append(" (")
|
||||
.append(biomeRegistry.getRichName(biomeType))
|
||||
.append(")")
|
||||
.build())
|
||||
.collect(Collectors.toList()));
|
||||
return paginationBox.create(page);
|
||||
}, (Component) null);
|
||||
}
|
||||
|
||||
@ -150,14 +148,11 @@ public class BiomeCommands {
|
||||
messageKey = "worldedit.biomeinfo.selection";
|
||||
}
|
||||
|
||||
List<Component> components = biomes.stream().map(biome -> {
|
||||
BiomeData data = biomeRegistry.getData(biome);
|
||||
if (data != null) {
|
||||
return TextComponent.of(data.getName()).hoverEvent(HoverEvent.showText(TextComponent.of(biome.getId())));
|
||||
} else {
|
||||
return TextComponent.of(biome.getId());
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
List<Component> components = biomes.stream().map(biome ->
|
||||
biomeRegistry.getRichName(biome).hoverEvent(
|
||||
HoverEvent.showText(TextComponent.of(biome.getId()))
|
||||
)
|
||||
).collect(Collectors.toList());
|
||||
player.printInfo(TranslatableComponent.of(messageKey, TextUtils.join(components, TextComponent.of(", "))));
|
||||
}
|
||||
|
||||
@ -175,7 +170,6 @@ public class BiomeCommands {
|
||||
World world = player.getWorld();
|
||||
Region region;
|
||||
Mask mask = editSession.getMask();
|
||||
Mask2D mask2d = mask != null ? mask.toMask2D() : null;
|
||||
|
||||
if (atPosition) {
|
||||
final BlockVector3 pos = player.getLocation().toVector().toBlockPoint();
|
||||
@ -184,11 +178,11 @@ public class BiomeCommands {
|
||||
region = session.getSelection(world);
|
||||
}
|
||||
|
||||
FlatRegionFunction replace = new BiomeReplace(editSession, target);
|
||||
if (mask2d != null) {
|
||||
replace = new FlatRegionMaskingFilter(mask2d, replace);
|
||||
RegionFunction replace = new BiomeReplace(editSession, target);
|
||||
if (mask != null) {
|
||||
replace = new RegionMaskingFilter(editSession, mask, replace);
|
||||
}
|
||||
FlatRegionVisitor visitor = new FlatRegionVisitor(Regions.asFlatRegion(region), replace);
|
||||
RegionVisitor visitor = new RegionVisitor(region, replace);
|
||||
Operations.completeLegacy(visitor);
|
||||
|
||||
player.printInfo(TranslatableComponent.of(
|
||||
|
@ -153,15 +153,6 @@ public class BrushCommands {
|
||||
this.worldEdit = worldEdit;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "none",
|
||||
aliases = "unbind",
|
||||
desc = "Unbind a bound brush from your current item"
|
||||
)
|
||||
void none(Player player, LocalSession session) throws WorldEditException {
|
||||
ToolCommands.setToolNone(player, session, true);
|
||||
}
|
||||
|
||||
@Command(name = "blendball",
|
||||
aliases = {
|
||||
"bb",
|
||||
@ -1012,7 +1003,9 @@ public class BrushCommands {
|
||||
@Switch(name = 'f', desc = "Also kill all friendly mobs (Applies the flags `-abgnpt`)")
|
||||
boolean killFriendly,
|
||||
@Switch(name = 'r', desc = "Also destroy armor stands")
|
||||
boolean killArmorStands, InjectedValueAccess context) throws WorldEditException {
|
||||
boolean killArmorStands,
|
||||
@Switch(name = 'w', desc = "Also kill water mobs")
|
||||
boolean killWater, InjectedValueAccess context) throws WorldEditException {
|
||||
worldEdit.checkMaxBrushRadius(radius);
|
||||
|
||||
CreatureButcher flags = new CreatureButcher(player);
|
||||
@ -1024,6 +1017,7 @@ public class BrushCommands {
|
||||
flags.or(CreatureButcher.Flags.AMBIENT, killAmbient, "worldedit.butcher.ambient");
|
||||
flags.or(CreatureButcher.Flags.TAGGED, killWithName, "worldedit.butcher.tagged");
|
||||
flags.or(CreatureButcher.Flags.ARMOR_STAND, killArmorStands, "worldedit.butcher.armorstands");
|
||||
flags.or(CreatureButcher.Flags.WATER, killWater, "worldedit.butcher.water");
|
||||
|
||||
set(context, new ButcherBrush(flags)).setSize(radius);
|
||||
}
|
||||
|
@ -174,17 +174,22 @@ public class ChunkCommands {
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, "/stop"))));
|
||||
}
|
||||
|
||||
private static class ChunkListPaginationBox extends PaginationBox.ListPaginationBox {
|
||||
private static class ChunkListPaginationBox extends PaginationBox {
|
||||
//private final Region region;
|
||||
private final List<BlockVector2> chunks = null;
|
||||
|
||||
ChunkListPaginationBox(Region region) {
|
||||
super("Selected Chunks", "/listchunks -p %page%", region.getChunks());
|
||||
super("Selected Chunks", "/listchunks -p %page%");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent(int number) {
|
||||
return create(number);
|
||||
return TextComponent.of(chunks.get(number).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getComponentsSize() {
|
||||
return chunks.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.RandomTextureUtil;
|
||||
import com.boydti.fawe.util.StringMan;
|
||||
import com.boydti.fawe.util.TextureUtil;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
@ -36,6 +37,7 @@ import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissions;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
|
||||
import com.sk89q.worldedit.command.util.HookMode;
|
||||
import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.input.DisallowedUsageException;
|
||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||
@ -44,6 +46,8 @@ import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.internal.command.CommandRegistrationHandler;
|
||||
import com.sk89q.worldedit.internal.command.CommandUtil;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.formatting.component.PaginationBox;
|
||||
@ -54,6 +58,9 @@ import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import org.enginehub.piston.CommandManager;
|
||||
import org.enginehub.piston.CommandManagerService;
|
||||
import org.enginehub.piston.CommandParameters;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
@ -65,8 +72,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@ -76,6 +85,65 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class)
|
||||
public class GeneralCommands {
|
||||
|
||||
public static void register(CommandRegistrationHandler registration,
|
||||
CommandManager commandManager,
|
||||
CommandManagerService commandManagerService,
|
||||
WorldEdit worldEdit) {
|
||||
// Collect the tool commands
|
||||
CommandManager collect = commandManagerService.newCommandManager();
|
||||
|
||||
registration.register(
|
||||
collect,
|
||||
GeneralCommandsRegistration.builder(),
|
||||
new GeneralCommands(worldEdit)
|
||||
);
|
||||
|
||||
|
||||
Set<org.enginehub.piston.Command> commands = collect.getAllCommands()
|
||||
.collect(Collectors.toSet());
|
||||
for (org.enginehub.piston.Command command : commands) {
|
||||
/*if in FAWE, //fast will remain for now
|
||||
(command.getName().equals("/fast")) {
|
||||
|
||||
// deprecate to `//perf`
|
||||
commandManager.register(CommandUtil.deprecate(
|
||||
command, "//fast duplicates //perf " +
|
||||
"and will be removed in WorldEdit 8",
|
||||
GeneralCommands::replaceFastForPerf
|
||||
));
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
|
||||
commandManager.register(command);
|
||||
}
|
||||
}
|
||||
|
||||
private static Component replaceFastForPerf(org.enginehub.piston.Command oldCmd,
|
||||
CommandParameters oldParams) {
|
||||
if (oldParams.getMetadata() == null) {
|
||||
return CommandUtil.createNewCommandReplacementText("//perf");
|
||||
}
|
||||
ImmutableList<String> args = oldParams.getMetadata().getArguments();
|
||||
if (args.isEmpty()) {
|
||||
return TextComponent.of("There is not yet a replacement for //fast" +
|
||||
" with no arguments");
|
||||
}
|
||||
String arg0 = args.get(0).toLowerCase(Locale.ENGLISH);
|
||||
String flipped;
|
||||
switch (arg0) {
|
||||
case "on":
|
||||
flipped = "off";
|
||||
break;
|
||||
case "off":
|
||||
flipped = "on";
|
||||
break;
|
||||
default:
|
||||
return TextComponent.of("There is no replacement for //fast " + arg0);
|
||||
}
|
||||
return CommandUtil.createNewCommandReplacementText("//perf " + flipped);
|
||||
}
|
||||
|
||||
private final WorldEdit worldEdit;
|
||||
|
||||
/**
|
||||
@ -145,22 +213,48 @@ public class GeneralCommands {
|
||||
|
||||
@Command(
|
||||
name = "/fast",
|
||||
desc = "Toggle fast mode side effects"
|
||||
desc = "Toggle fast mode"
|
||||
)
|
||||
@CommandPermissions("worldedit.fast")
|
||||
public void fast(Actor actor, LocalSession session,
|
||||
@Arg(desc = "The side effect", def = "")
|
||||
SideEffect sideEffect,
|
||||
@Arg(desc = "The new side effect state", def = "")
|
||||
SideEffect.State newState,
|
||||
@Switch(name = 'h', desc = "Show the info box")
|
||||
boolean showInfoBox) throws WorldEditException {
|
||||
@Deprecated
|
||||
void fast(Actor actor, LocalSession session,
|
||||
@Arg(desc = "The new fast mode state", def = "")
|
||||
Boolean fastMode) {
|
||||
boolean hasFastMode = session.hasFastMode();
|
||||
if (fastMode != null && fastMode == hasFastMode) {
|
||||
actor.printError(TranslatableComponent.of(fastMode ? "worldedit.fast.enabled.already" : "worldedit.fast.disabled.already"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasFastMode) {
|
||||
session.setFastMode(false);
|
||||
actor.printInfo(TranslatableComponent.of("worldedit.fast.disabled"));
|
||||
} else {
|
||||
session.setFastMode(true);
|
||||
actor.printInfo(TranslatableComponent.of("worldedit.fast.enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "/perf",
|
||||
desc = "Toggle side effects for performance",
|
||||
descFooter = "Note that this command is GOING to change in the future." +
|
||||
" Do not depend on the exact format of this command yet."
|
||||
)
|
||||
@CommandPermissions("worldedit.perf")
|
||||
void perf(Actor actor, LocalSession session,
|
||||
@Arg(desc = "The side effect", def = "")
|
||||
SideEffect sideEffect,
|
||||
@Arg(desc = "The new side effect state", def = "")
|
||||
SideEffect.State newState,
|
||||
@Switch(name = 'h', desc = "Show the info box")
|
||||
boolean showInfoBox) throws WorldEditException {
|
||||
if (sideEffect != null) {
|
||||
SideEffect.State currentState = session.getSideEffectSet().getState(sideEffect);
|
||||
if (newState != null && newState == currentState) {
|
||||
if (!showInfoBox) {
|
||||
actor.printError(TranslatableComponent.of(
|
||||
"worldedit.fast.sideeffect.already-set",
|
||||
"worldedit.perf.sideeffect.already-set",
|
||||
TranslatableComponent.of(sideEffect.getDisplayName()),
|
||||
TranslatableComponent.of(newState.getDisplayName())
|
||||
));
|
||||
@ -172,14 +266,14 @@ public class GeneralCommands {
|
||||
session.setSideEffectSet(session.getSideEffectSet().with(sideEffect, newState));
|
||||
if (!showInfoBox) {
|
||||
actor.printInfo(TranslatableComponent.of(
|
||||
"worldedit.fast.sideeffect.set",
|
||||
"worldedit.perf.sideeffect.set",
|
||||
TranslatableComponent.of(sideEffect.getDisplayName()),
|
||||
TranslatableComponent.of(newState.getDisplayName())
|
||||
));
|
||||
}
|
||||
} else {
|
||||
actor.printInfo(TranslatableComponent.of(
|
||||
"worldedit.fast.sideeffect.get",
|
||||
"worldedit.perf.sideeffect.get",
|
||||
TranslatableComponent.of(sideEffect.getDisplayName()),
|
||||
TranslatableComponent.of(currentState.getDisplayName())
|
||||
));
|
||||
@ -192,7 +286,7 @@ public class GeneralCommands {
|
||||
session.setSideEffectSet(applier);
|
||||
if (!showInfoBox) {
|
||||
actor.printInfo(TranslatableComponent.of(
|
||||
"worldedit.fast.sideeffect.set-all",
|
||||
"worldedit.perf.sideeffect.set-all",
|
||||
TranslatableComponent.of(newState.getDisplayName())
|
||||
));
|
||||
}
|
||||
@ -309,6 +403,7 @@ public class GeneralCommands {
|
||||
aliases = {"/toggleplace"},
|
||||
desc = "Switch between your position and pos1 for placement"
|
||||
)
|
||||
@CommandPermissions("worldedit.toggleplace")
|
||||
public void togglePlace(Player player, LocalSession session) {
|
||||
if (session.togglePlacementPosition()) {
|
||||
player.printInfo(TranslatableComponent.of("worldedit.toggleplace.pos1"));
|
||||
@ -331,7 +426,7 @@ public class GeneralCommands {
|
||||
@ArgFlag(name = 'p', desc = "Page of results to return", def = "1")
|
||||
int page,
|
||||
@Arg(desc = "Search query", variable = true)
|
||||
List<String> query) throws Exception {
|
||||
List<String> query) {
|
||||
String search = String.join(" ", query);
|
||||
if (search.length() <= 2) {
|
||||
actor.printError(TranslatableComponent.of("worldedit.searchitem.too-short"));
|
||||
@ -342,7 +437,8 @@ public class GeneralCommands {
|
||||
return;
|
||||
}
|
||||
|
||||
actor.print(new ItemSearcher(search, blocksOnly, itemsOnly, page).call());
|
||||
WorldEditAsyncCommandBuilder.createAndSendMessage(actor, new ItemSearcher(search, blocksOnly, itemsOnly, page),
|
||||
TranslatableComponent.of("worldedit.searchitem.searching"));
|
||||
}
|
||||
|
||||
private static class ItemSearcher implements Callable<Component> {
|
||||
@ -361,7 +457,7 @@ public class GeneralCommands {
|
||||
@Override
|
||||
public Component call() throws Exception {
|
||||
String command = "/searchitem " + (blocksOnly ? "-b " : "") + (itemsOnly ? "-i " : "") + "-p %page% " + search;
|
||||
Map<String, String> results = new TreeMap<>();
|
||||
Map<String, Component> results = new TreeMap<>();
|
||||
String idMatch = search.replace(' ', '_');
|
||||
String nameMatch = search.toLowerCase(Locale.ROOT);
|
||||
for (ItemType searchType : ItemType.REGISTRY) {
|
||||
@ -373,15 +469,17 @@ public class GeneralCommands {
|
||||
continue;
|
||||
}
|
||||
final String id = searchType.getId();
|
||||
String name = searchType.getName();
|
||||
final boolean hasName = !name.equals(id);
|
||||
name = name.toLowerCase(Locale.ROOT);
|
||||
if (id.contains(idMatch) || (hasName && name.contains(nameMatch))) {
|
||||
results.put(id, name + (hasName ? " (" + id + ")" : ""));
|
||||
if (id.contains(idMatch)) {
|
||||
Component name = searchType.getRichName();
|
||||
results.put(id, TextComponent.builder()
|
||||
.append(name)
|
||||
.append(" (" + id + ")")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
List<String> list = new ArrayList<>(results.values());
|
||||
return PaginationBox.fromStrings("Search results for '" + search + "'", command, list).create(page);
|
||||
List<Component> list = new ArrayList<>(results.values());
|
||||
return PaginationBox.fromComponents("Search results for '" + search + "'", command, list)
|
||||
.create(page);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
|
||||
import com.sk89q.worldedit.command.util.annotation.Confirm;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
@ -60,7 +61,7 @@ public class HistoryCommands {
|
||||
desc = "Undoes the last action (from history)"
|
||||
)
|
||||
@CommandPermissions({"worldedit.history.undo", "worldedit.history.undo.self"})
|
||||
public void undo(Player player, LocalSession session,
|
||||
public void undo(Actor actor, LocalSession session,
|
||||
@Confirm(Confirm.Processor.LIMIT) @Arg(desc = "Number of undoes to perform", def = "1")
|
||||
int times,
|
||||
@Arg(name = "player", desc = "Undo this player's operations", def = "")
|
||||
@ -68,31 +69,32 @@ public class HistoryCommands {
|
||||
times = Math.max(1, times);
|
||||
LocalSession undoSession = session;
|
||||
if (session.hasFastMode()) {
|
||||
player.print(TranslatableComponent.of("fawe.worldedit.history.command.undo.disabled"));
|
||||
actor.print(TranslatableComponent.of("fawe.worldedit.history.command.undo.disabled"));
|
||||
return;
|
||||
}
|
||||
if (playerName != null) {
|
||||
player.checkPermission("worldedit.history.undo.other");
|
||||
actor.checkPermission("worldedit.history.undo.other");
|
||||
undoSession = worldEdit.getSessionManager().findByName(playerName);
|
||||
if (undoSession == null) {
|
||||
player.printError(TranslatableComponent.of("worldedit.session.cant-find-session", TextComponent.of(playerName)));
|
||||
actor.printError(TranslatableComponent.of("worldedit.session.cant-find-session", TextComponent.of(playerName)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
int timesUndone = 0;
|
||||
for (int i = 0; i < times; ++i) {
|
||||
EditSession undone = undoSession.undo(undoSession.getBlockBag(player), player);
|
||||
BlockBag blockBag = actor instanceof Player ? undoSession.getBlockBag((Player) actor) : null;
|
||||
EditSession undone = undoSession.undo(blockBag, actor);
|
||||
if (undone != null) {
|
||||
timesUndone++;
|
||||
worldEdit.flushBlockBag(player, undone);
|
||||
worldEdit.flushBlockBag(actor, undone);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (timesUndone > 0) {
|
||||
player.printInfo(TranslatableComponent.of("worldedit.undo.undone", TextComponent.of(timesUndone)));
|
||||
actor.printInfo(TranslatableComponent.of("worldedit.undo.undone", TextComponent.of(timesUndone)));
|
||||
} else {
|
||||
player.printError(TranslatableComponent.of("worldedit.undo.none"));
|
||||
actor.printError(TranslatableComponent.of("worldedit.undo.none"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,7 +104,7 @@ public class HistoryCommands {
|
||||
desc = "Redoes the last action (from history)"
|
||||
)
|
||||
@CommandPermissions({"worldedit.history.redo", "worldedit.history.redo.self"})
|
||||
public void redo(Player player, LocalSession session,
|
||||
public void redo(Actor actor, LocalSession session,
|
||||
@Confirm(Confirm.Processor.LIMIT) @Arg(desc = "Number of redoes to perform", def = "1")
|
||||
int times,
|
||||
@Arg(name = "player", desc = "Redo this player's operations", def = "")
|
||||
@ -110,27 +112,28 @@ public class HistoryCommands {
|
||||
times = Math.max(1, times);
|
||||
LocalSession redoSession = session;
|
||||
if (playerName != null) {
|
||||
player.checkPermission("worldedit.history.redo.other");
|
||||
actor.checkPermission("worldedit.history.redo.other");
|
||||
redoSession = worldEdit.getSessionManager().findByName(playerName);
|
||||
if (redoSession == null) {
|
||||
player.printError(TranslatableComponent.of("worldedit.session.cant-find-session", TextComponent.of(playerName)));
|
||||
actor.printError(TranslatableComponent.of("worldedit.session.cant-find-session", TextComponent.of(playerName)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
int timesRedone = 0;
|
||||
for (int i = 0; i < times; ++i) {
|
||||
EditSession redone = redoSession.redo(redoSession.getBlockBag(player), player);
|
||||
BlockBag blockBag = actor instanceof Player ? redoSession.getBlockBag((Player) actor) : null;
|
||||
EditSession redone = redoSession.redo(blockBag, actor);
|
||||
if (redone != null) {
|
||||
timesRedone++;
|
||||
worldEdit.flushBlockBag(player, redone);
|
||||
worldEdit.flushBlockBag(actor, redone);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (timesRedone > 0) {
|
||||
player.printInfo(TranslatableComponent.of("worldedit.redo.redone", TextComponent.of(timesRedone)));
|
||||
actor.printInfo(TranslatableComponent.of("worldedit.redo.redone", TextComponent.of(timesRedone)));
|
||||
} else {
|
||||
player.printError(TranslatableComponent.of("worldedit.redo.none"));
|
||||
actor.printError(TranslatableComponent.of("worldedit.redo.none"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,7 @@ import java.net.URL;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -94,7 +95,6 @@ import static com.boydti.fawe.util.ReflectionUtils.as;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
//import com.boydti.fawe.object.schematic.visualizer.SchemVis;
|
||||
|
||||
/**
|
||||
* Commands that work with schematic files.
|
||||
@ -115,6 +115,31 @@ public class SchematicCommands {
|
||||
this.worldEdit = worldEdit;
|
||||
}
|
||||
|
||||
//TODO filtering for directories, global, and private scheamtics needs to be reimplemented here
|
||||
private static List<File> getFiles(File root, String filter, ClipboardFormat format) {
|
||||
File[] files = root.listFiles();
|
||||
if (files == null) {
|
||||
return null;
|
||||
}
|
||||
//Only get the files that match the format parameter
|
||||
if (format != null) {
|
||||
files = Arrays.stream(files).filter(format::isFormat).toArray(File[]::new);
|
||||
}
|
||||
List<File> fileList = new ArrayList<>();
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
List<File> subFiles = getFiles(f, filter, format);
|
||||
if (subFiles == null) {
|
||||
continue; // empty subdir
|
||||
}
|
||||
fileList.addAll(subFiles);
|
||||
} else {
|
||||
fileList.add(f);
|
||||
}
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "loadall",
|
||||
desc = "Load multiple clipboards (paste will randomly choose one)"
|
||||
@ -122,12 +147,12 @@ public class SchematicCommands {
|
||||
@Deprecated
|
||||
@CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load", "worldedit.schematic.load.web", "worldedit.schematic.load.asset"})
|
||||
public void loadall(Player player, LocalSession session,
|
||||
@Arg(desc = "Format name.", def = "schematic")
|
||||
String formatName,
|
||||
@Arg(desc = "File name.")
|
||||
String filename,
|
||||
@Switch(name = 'r', desc = "Apply random rotation")
|
||||
boolean randomRotate) throws FilenameException {
|
||||
@Arg(desc = "Format name.", def = "schematic")
|
||||
String formatName,
|
||||
@Arg(desc = "File name.")
|
||||
String filename,
|
||||
@Switch(name = 'r', desc = "Apply random rotation")
|
||||
boolean randomRotate) throws FilenameException {
|
||||
final ClipboardFormat format = ClipboardFormats.findByAlias(formatName);
|
||||
if (format == null) {
|
||||
player.print(Caption.of("fawe.worldedit.clipboard.clipboard.invalid.format", formatName));
|
||||
@ -355,19 +380,19 @@ public class SchematicCommands {
|
||||
if (parent != null && !parent.exists()) {
|
||||
if (!parent.mkdirs()) {
|
||||
throw new StopExecutionException(TranslatableComponent.of(
|
||||
"worldedit.schematic.save.failed-directory"));
|
||||
"worldedit.schematic.save.failed-directory"));
|
||||
}
|
||||
}
|
||||
|
||||
ClipboardHolder holder = session.getClipboard();
|
||||
|
||||
SchematicSaveTask task = new SchematicSaveTask(actor, f, format, holder, overwrite);
|
||||
SchematicSaveTask task = new SchematicSaveTask(actor, f, dir, format, holder, overwrite);
|
||||
AsyncCommandBuilder.wrap(task, actor)
|
||||
.registerWithSupervisor(worldEdit.getSupervisor(), "Saving schematic " + filename)
|
||||
.sendMessageAfterDelay(TranslatableComponent.of("worldedit.schematic.save.saving"))
|
||||
.onSuccess(filename + " saved" + (overwrite ? " (overwriting previous file)." : "."), null)
|
||||
.onFailure("Failed to save schematic", worldEdit.getPlatformManager().getPlatformCommandManager().getExceptionConverter())
|
||||
.buildAndExec(worldEdit.getExecutorService());
|
||||
.registerWithSupervisor(worldEdit.getSupervisor(), "Saving schematic " + filename)
|
||||
.sendMessageAfterDelay(TranslatableComponent.of("worldedit.schematic.save.saving"))
|
||||
.onSuccess(filename + " saved" + (overwrite ? " (overwriting previous file)." : "."), null)
|
||||
.onFailure("Failed to save schematic", worldEdit.getPlatformManager().getPlatformCommandManager().getExceptionConverter())
|
||||
.buildAndExec(worldEdit.getExecutorService());
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -531,22 +556,22 @@ public class SchematicCommands {
|
||||
|
||||
if (loaded) {
|
||||
msg.append(TextComponent.of("[-]", TextColor.RED)
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, unload + " " + path))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Unload"))));
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, unload + " " + path))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Unload"))));
|
||||
} else {
|
||||
msg.append(TextComponent.of("[+]", TextColor.GREEN)
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, loadMulti + " " + path))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Add to clipboard"))));
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, loadMulti + " " + path))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Add to clipboard"))));
|
||||
}
|
||||
if (type != UtilityCommands.URIType.DIRECTORY) {
|
||||
msg.append(TextComponent.of("[X]", TextColor.DARK_RED)
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, delete + " " + path))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("delete")))
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, delete + " " + path))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("delete")))
|
||||
);
|
||||
} else if (hasShow) {
|
||||
msg.append(TextComponent.of("[O]", TextColor.DARK_AQUA)
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, showCmd + " " + path))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("visualize")))
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, showCmd + " " + path))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("visualize")))
|
||||
);
|
||||
}
|
||||
TextComponent msgElem = TextComponent.of(name, color);
|
||||
@ -559,173 +584,47 @@ public class SchematicCommands {
|
||||
}
|
||||
msg.append(msgElem);
|
||||
|
||||
if (type == UtilityCommands.URIType.FILE) {
|
||||
long filesize = 0;
|
||||
try {
|
||||
filesize = Files.size(Paths.get(dir.getAbsolutePath() + File.separator
|
||||
+ (playerFolder ? (uuid.toString() + File.separator) : "") + path));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
TextComponent sizeElem = TextComponent.of(String.format(" (%.1f kb)", filesize / 1000.0), TextColor.GRAY);
|
||||
msg.append(sizeElem);
|
||||
}
|
||||
return msg.create();
|
||||
});
|
||||
PaginationBox paginationBox = PaginationBox.fromStrings("Available schematics", pageCommand, components);
|
||||
|
||||
long totalBytes = 0;
|
||||
File parentDir = new File(dir.getAbsolutePath() + (playerFolder ? File.separator + uuid.toString() : ""));
|
||||
try {
|
||||
List<File> toAddUp = getFiles(parentDir, null, null);
|
||||
if (toAddUp != null && toAddUp.size() != 0) {
|
||||
for (File schem : toAddUp) {
|
||||
if (schem.getName().endsWith(".schem") || schem.getName().endsWith(".schematic")) {
|
||||
totalBytes += Files.size(Paths.get(schem.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
String headerBytesElem = String.format("%.1fkb", totalBytes / 1000.0);
|
||||
|
||||
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && Settings.IMP.EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT > -1) {
|
||||
headerBytesElem += String.format(" / %dkb",
|
||||
Settings.IMP.EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT );
|
||||
}
|
||||
|
||||
String fullHeader = "| Schematics: " + headerBytesElem + " |";
|
||||
PaginationBox paginationBox = PaginationBox.fromComponents(fullHeader, pageCommand, components);
|
||||
actor.print(paginationBox.create(page));
|
||||
}
|
||||
|
||||
private static class SchematicLoadTask implements Callable<ClipboardHolder> {
|
||||
private final Actor actor;
|
||||
private final File file;
|
||||
private final ClipboardFormat format;
|
||||
|
||||
SchematicLoadTask(Actor actor, File file, ClipboardFormat format) {
|
||||
this.actor = actor;
|
||||
this.file = file;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClipboardHolder call() throws Exception {
|
||||
try (Closer closer = Closer.create()) {
|
||||
FileInputStream fis = closer.register(new FileInputStream(file));
|
||||
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
|
||||
ClipboardReader reader = closer.register(format.getReader(bis));
|
||||
|
||||
Clipboard clipboard = reader.read();
|
||||
log.info(actor.getName() + " loaded " + file.getCanonicalPath());
|
||||
return new ClipboardHolder(clipboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicSaveTask implements Callable<Void> {
|
||||
private final Actor actor;
|
||||
private final File file;
|
||||
private final ClipboardFormat format;
|
||||
private final ClipboardHolder holder;
|
||||
private final boolean overwrite;
|
||||
|
||||
SchematicSaveTask(Actor actor, File file, ClipboardFormat format, ClipboardHolder holder, boolean overwrite) {
|
||||
this.actor = actor;
|
||||
this.file = file;
|
||||
this.format = format;
|
||||
this.holder = holder;
|
||||
this.overwrite = overwrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Clipboard clipboard = holder.getClipboard();
|
||||
Transform transform = holder.getTransform();
|
||||
Clipboard target;
|
||||
|
||||
// If we have a transform, bake it into the copy
|
||||
if (transform.isIdentity()) {
|
||||
target = clipboard;
|
||||
} else {
|
||||
FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform);
|
||||
target = new BlockArrayClipboard(result.getTransformedRegion());
|
||||
target.setOrigin(clipboard.getOrigin());
|
||||
Operations.completeLegacy(result.copyTo(target));
|
||||
}
|
||||
|
||||
try (Closer closer = Closer.create()) {
|
||||
FileOutputStream fos = closer.register(new FileOutputStream(file));
|
||||
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
|
||||
ClipboardWriter writer = closer.register(format.getWriter(bos));
|
||||
URI uri = null;
|
||||
if (holder instanceof URIClipboardHolder) {
|
||||
uri = ((URIClipboardHolder) holder).getURI(clipboard);
|
||||
}
|
||||
if (new ActorSaveClipboardEvent(actor, clipboard, uri, file.toURI()).call()) {
|
||||
if (writer instanceof MinecraftStructure) {
|
||||
((MinecraftStructure) writer).write(target, actor.getName());
|
||||
} else {
|
||||
writer.write(target);
|
||||
}
|
||||
log.info(actor.getName() + " saved " + file.getCanonicalPath());
|
||||
actor.print(Caption.of("fawe.worldedit.schematic.schematic.saved", file.getName()));
|
||||
} else {
|
||||
actor.printError(TranslatableComponent.of("fawe.cancel.worldedit.cancel.reason.manual"));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicListTask implements Callable<Component> {
|
||||
private final String prefix;
|
||||
private final int sortType;
|
||||
private final int page;
|
||||
private final File rootDir;
|
||||
private final String pageCommand;
|
||||
private final String filter;
|
||||
private String formatName;
|
||||
|
||||
SchematicListTask(String prefix, int sortType, int page, String pageCommand,
|
||||
String filter, String formatName) {
|
||||
this.prefix = prefix;
|
||||
this.sortType = sortType;
|
||||
this.page = page;
|
||||
this.rootDir = WorldEdit.getInstance().getWorkingDirectoryFile(prefix);
|
||||
this.pageCommand = pageCommand;
|
||||
this.filter = filter;
|
||||
this.formatName = formatName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component call() throws Exception {
|
||||
ClipboardFormat format = ClipboardFormats.findByAlias(formatName);
|
||||
List<File> fileList = getFiles(rootDir,filter,format);
|
||||
|
||||
if (fileList == null || fileList.isEmpty()) {
|
||||
return ErrorFormat.wrap("No schematics found.");
|
||||
}
|
||||
|
||||
File[] files = new File[fileList.size()];
|
||||
fileList.toArray(files);
|
||||
// cleanup file list
|
||||
Arrays.sort(files, (f1, f2) -> {
|
||||
// http://stackoverflow.com/questions/203030/best-way-to-list-files-in-java-sorted-by-date-modified
|
||||
int res;
|
||||
if (sortType == 0) { // use name by default
|
||||
int p = f1.getParent().compareTo(f2.getParent());
|
||||
if (p == 0) { // same parent, compare names
|
||||
res = f1.getName().compareTo(f2.getName());
|
||||
} else { // different parent, sort by that
|
||||
res = p;
|
||||
}
|
||||
} else {
|
||||
res = Long.compare(f1.lastModified(), f2.lastModified()); // use date if there is a flag
|
||||
if (sortType == 1) {
|
||||
res = -res; // flip date for newest first instead of oldest first
|
||||
}
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
PaginationBox paginationBox = new SchematicPaginationBox(prefix, files, pageCommand);
|
||||
return paginationBox.create(page);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO filtering for directories, global, and private scheamtics needs to be reimplemented here
|
||||
private static List<File> getFiles(File root, String filter, ClipboardFormat format) {
|
||||
File[] files = root.listFiles();
|
||||
if (files == null) {
|
||||
return null;
|
||||
}
|
||||
//Only get the files that match the format parameter
|
||||
if (format != null) {
|
||||
files = Arrays.stream(files).filter(format::isFormat).toArray(File[]::new);
|
||||
}
|
||||
List<File> fileList = new ArrayList<>();
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
List<File> subFiles = getFiles(f, filter, format);
|
||||
if (subFiles == null) {
|
||||
continue; // empty subdir
|
||||
}
|
||||
fileList.addAll(subFiles);
|
||||
} else {
|
||||
fileList.add(f);
|
||||
}
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "delete",
|
||||
aliases = {"d"},
|
||||
@ -776,6 +675,249 @@ public class SchematicCommands {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class SchematicLoadTask implements Callable<ClipboardHolder> {
|
||||
private final Actor actor;
|
||||
private final ClipboardFormat format;
|
||||
private final File file;
|
||||
|
||||
SchematicLoadTask(Actor actor, File file, ClipboardFormat format) {
|
||||
this.actor = actor;
|
||||
this.file = file;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClipboardHolder call() throws Exception {
|
||||
try (Closer closer = Closer.create()) {
|
||||
FileInputStream fis = closer.register(new FileInputStream(file));
|
||||
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
|
||||
ClipboardReader reader = closer.register(format.getReader(bis));
|
||||
|
||||
Clipboard clipboard = reader.read();
|
||||
log.info(actor.getName() + " loaded " + file.getCanonicalPath());
|
||||
return new ClipboardHolder(clipboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicSaveTask implements Callable<Void> {
|
||||
private final Actor actor;
|
||||
private final ClipboardFormat format;
|
||||
private final ClipboardHolder holder;
|
||||
private final boolean overwrite;
|
||||
private final File rootDir;
|
||||
private File file;
|
||||
|
||||
SchematicSaveTask(Actor actor, File file, File rootDir, ClipboardFormat format, ClipboardHolder holder, boolean overwrite) {
|
||||
this.actor = actor;
|
||||
this.file = file;
|
||||
this.rootDir = rootDir;
|
||||
this.format = format;
|
||||
this.holder = holder;
|
||||
this.overwrite = overwrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Clipboard clipboard = holder.getClipboard();
|
||||
Transform transform = holder.getTransform();
|
||||
Clipboard target;
|
||||
|
||||
boolean checkFilesize = false;
|
||||
|
||||
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS
|
||||
&& Settings.IMP.EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT > -1) {
|
||||
checkFilesize = true;
|
||||
}
|
||||
|
||||
double directorysizeKb = 0;
|
||||
String curFilepath = file.getAbsolutePath();
|
||||
final String SCHEMATIC_NAME = file.getName();
|
||||
|
||||
double oldKbOverwritten = 0;
|
||||
String overwrittenPath = curFilepath;
|
||||
|
||||
int numFiles = -1;
|
||||
if (checkFilesize) {
|
||||
List<File> toAddUp = getFiles(rootDir, null, null);
|
||||
if (toAddUp != null && toAddUp.size() != 0) {
|
||||
for (File child : toAddUp) {
|
||||
if (child.getName().endsWith(".schem") || child.getName().endsWith(".schematic")) {
|
||||
directorysizeKb += Files.size(Paths.get(child.getAbsolutePath())) / 1000.0;
|
||||
numFiles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (overwrite) {
|
||||
oldKbOverwritten = Files.size(Paths.get(file.getAbsolutePath())) / 1000.0;
|
||||
int iter = 1;
|
||||
while (new File(overwrittenPath + "." + iter + "." + format.getPrimaryFileExtension()).exists()) {
|
||||
iter++;
|
||||
}
|
||||
file = new File(overwrittenPath + "." + iter + "." + format.getPrimaryFileExtension());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && Settings.IMP.EXPERIMENTAL.PER_PLAYER_FILE_NUM_LIMIT > -1) {
|
||||
|
||||
if (numFiles == -1) {
|
||||
numFiles = 0;
|
||||
List<File> toAddUp = getFiles(rootDir, null, null);
|
||||
if (toAddUp != null && toAddUp.size() != 0) {
|
||||
for (File child : toAddUp) {
|
||||
if (child.getName().endsWith(".schem") || child.getName().endsWith(".schematic")) {
|
||||
numFiles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int limit = Settings.IMP.EXPERIMENTAL.PER_PLAYER_FILE_NUM_LIMIT;
|
||||
|
||||
if (numFiles >= limit) {
|
||||
TextComponent noSlotsErr = TextComponent.of( //TODO - to be moved into captions/translatablecomponents
|
||||
String.format("You have " + numFiles + "/" + limit + " saved schematics. Delete some to save this one!",
|
||||
TextColor.RED));
|
||||
log.info(actor.getName() + " failed to save " + file.getCanonicalPath() + " - too many schematics!");
|
||||
throw new WorldEditException(noSlotsErr) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a transform, bake it into the copy
|
||||
if (transform.isIdentity()) {
|
||||
target = clipboard;
|
||||
} else {
|
||||
FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform);
|
||||
target = new BlockArrayClipboard(result.getTransformedRegion());
|
||||
target.setOrigin(clipboard.getOrigin());
|
||||
Operations.completeLegacy(result.copyTo(target));
|
||||
}
|
||||
|
||||
try (Closer closer = Closer.create()) {
|
||||
FileOutputStream fos = closer.register(new FileOutputStream(file));
|
||||
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
|
||||
ClipboardWriter writer = closer.register(format.getWriter(bos));
|
||||
URI uri = null;
|
||||
if (holder instanceof URIClipboardHolder) {
|
||||
uri = ((URIClipboardHolder) holder).getURI(clipboard);
|
||||
}
|
||||
if (new ActorSaveClipboardEvent(actor, clipboard, uri, file.toURI()).call()) {
|
||||
if (writer instanceof MinecraftStructure) {
|
||||
((MinecraftStructure) writer).write(target, actor.getName());
|
||||
} else {
|
||||
writer.write(target);
|
||||
}
|
||||
|
||||
closer.close(); // release the new .schem file so that its size can be measured
|
||||
double filesizeKb = Files.size(Paths.get(file.getAbsolutePath())) / 1000.0;
|
||||
|
||||
TextComponent filesizeNotif = TextComponent.of( //TODO - to be moved into captions/translatablecomponents
|
||||
SCHEMATIC_NAME + " size: " + String.format("%.1f", filesizeKb) + "kb", TextColor.GRAY);
|
||||
actor.print(filesizeNotif);
|
||||
|
||||
if (checkFilesize) {
|
||||
|
||||
double curKb = filesizeKb + directorysizeKb;
|
||||
int allocatedKb = Settings.IMP.EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT;
|
||||
|
||||
if (overwrite) {
|
||||
curKb -= oldKbOverwritten;
|
||||
}
|
||||
|
||||
if ((curKb) > allocatedKb) {
|
||||
file.delete();
|
||||
TextComponent notEnoughKbErr = TextComponent.of( //TODO - to be moved into captions/translatablecomponents
|
||||
"You're about to be at " + String.format("%.1f", curKb) + "kb of schematics. ("
|
||||
+ String.format("%dkb", allocatedKb) + " available) Delete some first to save this one!",
|
||||
TextColor.RED);
|
||||
log.info(actor.getName() + " failed to save " + SCHEMATIC_NAME + " - not enough space!");
|
||||
throw new WorldEditException(notEnoughKbErr) {
|
||||
};
|
||||
}
|
||||
if (overwrite) {
|
||||
new File(curFilepath).delete();
|
||||
file.renameTo(new File(curFilepath));
|
||||
} else {
|
||||
numFiles++;
|
||||
}
|
||||
TextComponent kbRemainingNotif = TextComponent.of( //TODO - to be moved into captions/translatablecomponents
|
||||
"You have " + String.format("%.1f", (allocatedKb - curKb)) + "kb left for schematics.", TextColor.GRAY);
|
||||
actor.print(kbRemainingNotif);
|
||||
}
|
||||
|
||||
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && Settings.IMP.EXPERIMENTAL.PER_PLAYER_FILE_NUM_LIMIT > -1) {
|
||||
|
||||
TextComponent slotsRemainingNotif = TextComponent.of( //TODO - to be moved into captions/translatablecomponents
|
||||
"You have " + (Settings.IMP.EXPERIMENTAL.PER_PLAYER_FILE_NUM_LIMIT - numFiles)
|
||||
+ " schematic file slots left.", TextColor.GRAY);
|
||||
actor.print(slotsRemainingNotif);
|
||||
}
|
||||
log.info(actor.getName() + " saved " + file.getCanonicalPath());
|
||||
} else {
|
||||
actor.printError(TranslatableComponent.of("fawe.cancel.worldedit.cancel.reason.manual"));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicListTask implements Callable<Component> {
|
||||
private final String prefix;
|
||||
private final int sortType;
|
||||
private final int page;
|
||||
private final File rootDir;
|
||||
private final String pageCommand;
|
||||
private final String filter;
|
||||
private final String formatName;
|
||||
|
||||
SchematicListTask(String prefix, int sortType, int page, String pageCommand,
|
||||
String filter, String formatName) {
|
||||
this.prefix = prefix;
|
||||
this.sortType = sortType;
|
||||
this.page = page;
|
||||
this.rootDir = WorldEdit.getInstance().getWorkingDirectoryFile(prefix);
|
||||
this.pageCommand = pageCommand;
|
||||
this.filter = filter;
|
||||
this.formatName = formatName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component call() throws Exception {
|
||||
ClipboardFormat format = ClipboardFormats.findByAlias(formatName);
|
||||
List<File> fileList = getFiles(rootDir, filter, format);
|
||||
|
||||
if (fileList == null || fileList.isEmpty()) {
|
||||
return ErrorFormat.wrap("No schematics found.");
|
||||
}
|
||||
|
||||
File[] files = new File[fileList.size()];
|
||||
fileList.toArray(files);
|
||||
// cleanup file list
|
||||
Arrays.sort(files, (f1, f2) -> {
|
||||
// http://stackoverflow.com/questions/203030/best-way-to-list-files-in-java-sorted-by-date-modified
|
||||
int res;
|
||||
if (sortType == 0) { // use name by default
|
||||
int p = f1.getParent().compareTo(f2.getParent());
|
||||
if (p == 0) { // same parent, compare names
|
||||
res = f1.getName().compareTo(f2.getName());
|
||||
} else { // different parent, sort by that
|
||||
res = p;
|
||||
}
|
||||
} else {
|
||||
res = Long.compare(f1.lastModified(), f2.lastModified()); // use date if there is a flag
|
||||
if (sortType == 1) {
|
||||
res = -res; // flip date for newest first instead of oldest first
|
||||
}
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
PaginationBox paginationBox = new SchematicPaginationBox(prefix, files, pageCommand);
|
||||
return paginationBox.create(page);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicPaginationBox extends PaginationBox {
|
||||
private final String prefix;
|
||||
private final File[] files;
|
||||
|
@ -212,7 +212,8 @@ public class SelectionCommands {
|
||||
|
||||
@Command(
|
||||
name = "/chunk",
|
||||
desc = "Set the selection to your current chunk."
|
||||
desc = "Set the selection to your current chunk.",
|
||||
descFooter = "This command selects 256-block-tall areas,\nwhich can be specified by the y-coordinate.\nE.g. -c x,1,z will select from y=256 to y=511."
|
||||
)
|
||||
@Logging(POSITION)
|
||||
@CommandPermissions("worldedit.selection.chunk")
|
||||
@ -308,19 +309,21 @@ public class SelectionCommands {
|
||||
@Command(
|
||||
name = "toggleeditwand",
|
||||
aliases = { "/toggleeditwand" },
|
||||
desc = "Remind the user that the wand is now a tool and can be unbound with /none."
|
||||
desc = "Remind the user that the wand is now a tool and can be unbound with /tool none."
|
||||
)
|
||||
@CommandPermissions("worldedit.wand.toggle")
|
||||
public void toggleWand(Player player) {
|
||||
player.printInfo(TextComponent.of("The selection wand is now a normal tool. You can disable it with ")
|
||||
.append(TextComponent.of("/none", TextColor.AQUA).clickEvent(
|
||||
ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "/none")))
|
||||
.append(TextComponent.of(" and rebind it to any item with "))
|
||||
.append(TextComponent.of("//selwand", TextColor.AQUA).clickEvent(
|
||||
ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "//selwand")))
|
||||
.append(TextComponent.of(" or get a new wand with "))
|
||||
.append(TextComponent.of("//wand", TextColor.AQUA).clickEvent(
|
||||
ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "//wand"))));
|
||||
player.printInfo(
|
||||
TranslatableComponent.of(
|
||||
"worldedit.wand.selwand.now.tool",
|
||||
TextComponent.of("/tool none", TextColor.AQUA).clickEvent(
|
||||
ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "/tool none")),
|
||||
TextComponent.of("/tool selwand", TextColor.AQUA).clickEvent(
|
||||
ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "/tool selwand")),
|
||||
TextComponent.of("//wand", TextColor.AQUA).clickEvent(
|
||||
ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "//wand"))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -570,6 +573,7 @@ public class SelectionCommands {
|
||||
aliases = { ";", "/desel", "/deselect" },
|
||||
desc = "Choose a region selector"
|
||||
)
|
||||
@CommandPermissions("worldedit.analysis.sel")
|
||||
public void select(Actor actor, World world, LocalSession session,
|
||||
@Arg(desc = "Selector to switch to", def = "")
|
||||
SelectorChoice selector,
|
||||
@ -715,7 +719,7 @@ public class SelectionCommands {
|
||||
|
||||
final BlockState state = c.getID();
|
||||
final BlockType blockType = state.getBlockType();
|
||||
TextComponent blockName = TextComponent.of(blockType.getName(), TextColor.LIGHT_PURPLE);
|
||||
Component blockName = blockType.getRichName().color(TextColor.LIGHT_PURPLE);
|
||||
TextComponent toolTip;
|
||||
if (separateStates && state != blockType.getDefaultState()) {
|
||||
toolTip = TextComponent.of(state.getAsString(), TextColor.GRAY);
|
||||
|
@ -25,8 +25,10 @@ import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.command.tool.BlockDataCyler;
|
||||
import com.sk89q.worldedit.command.tool.BlockReplacer;
|
||||
import com.sk89q.worldedit.command.tool.BrushTool;
|
||||
import com.sk89q.worldedit.command.tool.DistanceWand;
|
||||
import com.sk89q.worldedit.command.tool.FloatingTreeRemover;
|
||||
import com.sk89q.worldedit.command.tool.FloodFillTool;
|
||||
@ -35,19 +37,23 @@ import com.sk89q.worldedit.command.tool.LongRangeBuildTool;
|
||||
import com.sk89q.worldedit.command.tool.NavigationWand;
|
||||
import com.sk89q.worldedit.command.tool.QueryTool;
|
||||
import com.sk89q.worldedit.command.tool.SelectionWand;
|
||||
import com.sk89q.worldedit.command.tool.StackTool;
|
||||
import com.sk89q.worldedit.command.tool.Tool;
|
||||
import com.sk89q.worldedit.command.tool.TreePlanter;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissions;
|
||||
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
|
||||
import com.sk89q.worldedit.command.util.SubCommandPermissionCondition;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.internal.command.CommandRegistrationHandler;
|
||||
import com.sk89q.worldedit.internal.command.CommandUtil;
|
||||
import com.sk89q.worldedit.util.HandSide;
|
||||
import com.sk89q.worldedit.util.TreeGenerator;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import org.enginehub.piston.CommandManager;
|
||||
import org.enginehub.piston.CommandManagerService;
|
||||
import org.enginehub.piston.CommandMetadata;
|
||||
@ -82,14 +88,19 @@ public class ToolCommands {
|
||||
.collect(Collectors.toSet());
|
||||
for (org.enginehub.piston.Command command : commands) {
|
||||
if (command.getAliases().contains("unbind")) {
|
||||
// Don't register new /tool unbind alias
|
||||
// Don't register new /tool <whatever> alias
|
||||
command = command.toBuilder().aliases(
|
||||
Collections2.filter(command.getAliases(), alias -> !"unbind".equals(alias))
|
||||
).build();
|
||||
}
|
||||
if (command.getName().equals("stacker")) {
|
||||
// Don't register /stacker
|
||||
continue;
|
||||
}
|
||||
commandManager.register(CommandUtil.deprecate(
|
||||
command, "Global tool names cause conflicts "
|
||||
+ "and will be removed in WorldEdit 8", ToolCommands::asNonGlobal
|
||||
+ "and will be removed in WorldEdit 8",
|
||||
CommandUtil.ReplacementMessageGenerator.forNewCommand(ToolCommands::asNonGlobal)
|
||||
));
|
||||
}
|
||||
|
||||
@ -110,6 +121,8 @@ public class ToolCommands {
|
||||
.required()
|
||||
.build());
|
||||
command.description(TextComponent.of("Binds a tool to the item in your hand"));
|
||||
|
||||
command.condition(new SubCommandPermissionCondition.Generator(nonGlobalCommands).build());
|
||||
});
|
||||
}
|
||||
|
||||
@ -124,15 +137,34 @@ public class ToolCommands {
|
||||
|
||||
static void setToolNone(Player player, LocalSession session, boolean isBrush)
|
||||
throws InvalidToolBindException {
|
||||
session.setTool(player, null);
|
||||
isBrush = session.getTool(player) instanceof BrushTool;
|
||||
session.setTool(player.getItemInHand(HandSide.MAIN_HAND).getType(), null);
|
||||
player.printInfo(TranslatableComponent.of(isBrush ? "worldedit.brush.none.equip" : "worldedit.tool.none.equip"));
|
||||
}
|
||||
|
||||
private static void setTool(Player player, LocalSession session, Tool tool,
|
||||
String translationKey) throws InvalidToolBindException {
|
||||
BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND);
|
||||
session.setTool(itemStack.getType(), tool);
|
||||
player.printInfo(TranslatableComponent.of(translationKey, itemStack.getRichName()));
|
||||
}
|
||||
|
||||
private final WorldEdit we;
|
||||
|
||||
public ToolCommands(WorldEdit we) {
|
||||
this.we = we;
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "none",
|
||||
aliases = "unbind",
|
||||
desc = "Unbind a bound tool from your current item"
|
||||
)
|
||||
@CommandPermissions("worldedit.tool.none")
|
||||
public void none(Player player, LocalSession session) throws WorldEditException {
|
||||
setToolNone(player, session, false);
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "selwand",
|
||||
aliases = "/selwand",
|
||||
@ -140,9 +172,7 @@ public class ToolCommands {
|
||||
)
|
||||
@CommandPermissions("worldedit.setwand")
|
||||
public void selwand(Player player, LocalSession session) throws WorldEditException {
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, SelectionWand.INSTANCE);
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.selwand.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, SelectionWand.INSTANCE, "worldedit.tool.selwand.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -152,10 +182,7 @@ public class ToolCommands {
|
||||
)
|
||||
@CommandPermissions("worldedit.setwand")
|
||||
public void navwand(Player player, LocalSession session) throws WorldEditException {
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, NavigationWand.INSTANCE);
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.navwand.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, NavigationWand.INSTANCE, "worldedit.tool.navwand.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -165,10 +192,7 @@ public class ToolCommands {
|
||||
)
|
||||
@CommandPermissions("worldedit.tool.info")
|
||||
public void info(Player player, LocalSession session) throws WorldEditException {
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new QueryTool());
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.info.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, new QueryTool(), "worldedit.tool.info.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -178,9 +202,7 @@ public class ToolCommands {
|
||||
)
|
||||
@CommandPermissions("worldedit.tool.inspect")
|
||||
public void inspectBrush(Player player, LocalSession session) throws WorldEditException {
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new InspectBrush());
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.inspect.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, new InspectBrush(), "worldedit.tool.info.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -192,10 +214,20 @@ public class ToolCommands {
|
||||
public void tree(Player player, LocalSession session,
|
||||
@Arg(desc = "Type of tree to generate", def = "tree")
|
||||
TreeGenerator.TreeType type) throws WorldEditException {
|
||||
setTool(player, session, new TreePlanter(type), "worldedit.tool.tree.equip");
|
||||
}
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new TreePlanter(type));
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.tree.equip", TextComponent.of(itemType.getName())));
|
||||
@Command(
|
||||
name = "stacker",
|
||||
desc = "Block stacker tool"
|
||||
)
|
||||
@CommandPermissions("worldedit.tool.stack")
|
||||
public void stacker(Player player, LocalSession session,
|
||||
@Arg(desc = "The max range of the stack", def = "10")
|
||||
int range,
|
||||
@Arg(desc = "The mask to stack until", def = "!#existing")
|
||||
Mask mask) throws WorldEditException {
|
||||
setTool(player, session, new StackTool(range, mask), "worldedit.tool.stack.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -207,10 +239,7 @@ public class ToolCommands {
|
||||
public void repl(Player player, LocalSession session,
|
||||
@Arg(desc = "The pattern of blocks to place")
|
||||
Pattern pattern) throws WorldEditException {
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new BlockReplacer(pattern));
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.repl.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, new BlockReplacer(pattern), "worldedit.tool.repl.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -220,10 +249,7 @@ public class ToolCommands {
|
||||
)
|
||||
@CommandPermissions("worldedit.tool.data-cycler")
|
||||
public void cycler(Player player, LocalSession session) throws WorldEditException {
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new BlockDataCyler());
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.data-cycler.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, new BlockDataCyler(), "worldedit.tool.data-cycler.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -241,13 +267,10 @@ public class ToolCommands {
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
|
||||
if (range > config.maxSuperPickaxeSize) {
|
||||
player.printError(TranslatableComponent.of("worldedit.superpickaxe.max-range", TextComponent.of(config.maxSuperPickaxeSize)));
|
||||
player.printError(TranslatableComponent.of("worldedit.tool.superpickaxe.max-range", TextComponent.of(config.maxSuperPickaxeSize)));
|
||||
return;
|
||||
}
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new FloodFillTool(range, pattern));
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.floodfill.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, new FloodFillTool(range, pattern), "worldedit.tool.floodfill.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -257,10 +280,7 @@ public class ToolCommands {
|
||||
)
|
||||
@CommandPermissions("worldedit.tool.deltree")
|
||||
public void deltree(Player player, LocalSession session) throws WorldEditException {
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new FloatingTreeRemover());
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.deltree.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, new FloatingTreeRemover(), "worldedit.tool.deltree.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -270,9 +290,7 @@ public class ToolCommands {
|
||||
)
|
||||
@CommandPermissions("worldedit.tool.farwand")
|
||||
public void farwand(Player player, LocalSession session) throws WorldEditException {
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new DistanceWand());
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.farwand.equip", TextComponent.of(itemType.getName())));
|
||||
setTool(player, session, new DistanceWand(), "worldedit.tool.farwand.equip");
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -286,18 +304,19 @@ public class ToolCommands {
|
||||
Pattern primary,
|
||||
@Arg(desc = "Pattern to set on right-click")
|
||||
Pattern secondary) throws WorldEditException {
|
||||
|
||||
final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType();
|
||||
session.setTool(player, new LongRangeBuildTool(primary, secondary));
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.lrbuild.equip", TextComponent.of(itemType.getName())));
|
||||
String primaryName = "pattern";
|
||||
String secondaryName = "pattern";
|
||||
setTool(player, session, new LongRangeBuildTool(primary, secondary), "worldedit.tool.lrbuild.equip");
|
||||
Component primaryName;
|
||||
Component secondaryName;
|
||||
if (primary instanceof BlockStateHolder) {
|
||||
primaryName = ((BlockStateHolder<?>) primary).getBlockType().getName();
|
||||
primaryName = ((BlockStateHolder<?>) primary).getBlockType().getRichName();
|
||||
} else {
|
||||
primaryName = TextComponent.of("pattern");
|
||||
}
|
||||
if (secondary instanceof BlockStateHolder) {
|
||||
secondaryName = ((BlockStateHolder<?>) secondary).getBlockType().getName();
|
||||
secondaryName = ((BlockStateHolder<?>) secondary).getBlockType().getRichName();
|
||||
} else {
|
||||
secondaryName = TextComponent.of("pattern");
|
||||
}
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.lrbuild.set", TextComponent.of(primaryName), TextComponent.of(secondaryName)));
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.lrbuild.set", primaryName, secondaryName));
|
||||
}
|
||||
}
|
||||
|
@ -569,7 +569,9 @@ public class UtilityCommands {
|
||||
@Switch(name = 'f', desc = "Also kill all friendly mobs (Applies the flags `-abgnpt`)")
|
||||
boolean killFriendly,
|
||||
@Switch(name = 'r', desc = "Also destroy armor stands")
|
||||
boolean killArmorStands) throws WorldEditException {
|
||||
boolean killArmorStands,
|
||||
@Switch(name = 'w', desc = "Also kill water mobs")
|
||||
boolean killWater) throws WorldEditException {
|
||||
LocalConfiguration config = we.getConfiguration();
|
||||
|
||||
if (radius == null) {
|
||||
@ -595,6 +597,7 @@ public class UtilityCommands {
|
||||
flags.or(CreatureButcher.Flags.AMBIENT, killAmbient, "worldedit.butcher.ambient");
|
||||
flags.or(CreatureButcher.Flags.TAGGED, killWithName, "worldedit.butcher.tagged");
|
||||
flags.or(CreatureButcher.Flags.ARMOR_STAND, killArmorStands, "worldedit.butcher.armorstands");
|
||||
flags.or(CreatureButcher.Flags.WATER, killWater, "worldedit.butcher.water");
|
||||
|
||||
int killed = killMatchingEntities(radius, actor, flags::createFunction);
|
||||
|
||||
@ -675,7 +678,11 @@ public class UtilityCommands {
|
||||
try {
|
||||
expression = Expression.compile(String.join(" ", input));
|
||||
} catch (ExpressionException e) {
|
||||
actor.printError(TranslatableComponent.of("worldedit.calc.invalid", TextComponent.of(String.join(" ", input))));
|
||||
actor.printError(TranslatableComponent.of(
|
||||
"worldedit.calc.invalid.with-error",
|
||||
TextComponent.of(String.join(" ", input)),
|
||||
TextComponent.of(e.getMessage())
|
||||
));
|
||||
return;
|
||||
}
|
||||
WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> {
|
||||
|
@ -45,6 +45,7 @@ import org.enginehub.piston.inject.Key;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class RegistryConverter<V extends Keyed> implements ArgumentConverter<V> {
|
||||
@ -98,7 +99,7 @@ public final class RegistryConverter<V extends Keyed> implements ArgumentConvert
|
||||
|
||||
@Override
|
||||
public ConversionResult<V> convert(String argument, InjectedValueAccess injectedValueAccess) {
|
||||
V result = registry.get(argument);
|
||||
V result = registry.get(argument.toLowerCase(Locale.ROOT));
|
||||
return result == null
|
||||
? FailedConversion.from(new IllegalArgumentException(
|
||||
"Not a valid " + registry.getName() + ": " + argument))
|
||||
|
@ -78,7 +78,7 @@ public class BlockReplacer implements DoubleActionBlockTool {
|
||||
|
||||
if (targetBlock != null) {
|
||||
pattern = targetBlock;
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.repl.switched", TextComponent.of(targetBlock.getBlockType().getName())));
|
||||
player.printInfo(TranslatableComponent.of("worldedit.tool.repl.switched", targetBlock.getBlockType().getRichName()));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -59,7 +59,7 @@ public class QueryTool implements BlockTool {
|
||||
|
||||
TextComponent.Builder builder = TextComponent.builder();
|
||||
builder.append(TextComponent.of("@" + clicked.toVector().toBlockPoint() + ": ", TextColor.BLUE));
|
||||
builder.append(TextComponent.of(block.getBlockType().getName(), TextColor.YELLOW));
|
||||
builder.append(block.getBlockType().getRichName().color(TextColor.YELLOW));
|
||||
builder.append(TextComponent.of(" (" + block + ") ", TextColor.GRAY)
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TranslatableComponent.of("worldedit.tool.info.blockstate.hover"))));
|
||||
final int internalId = BlockStateIdAccess.getBlockStateId(block.toImmutableState());
|
||||
|
@ -36,8 +36,9 @@ public class CreatureButcher {
|
||||
public static final int GOLEMS = 1 << 3;
|
||||
public static final int AMBIENT = 1 << 4;
|
||||
public static final int TAGGED = 1 << 5;
|
||||
public static final int FRIENDLY = PETS | NPCS | ANIMALS | GOLEMS | AMBIENT | TAGGED;
|
||||
public static final int ARMOR_STAND = 1 << 6;
|
||||
public static final int WATER = 1 << 7;
|
||||
public static final int FRIENDLY = PETS | NPCS | ANIMALS | GOLEMS | AMBIENT | TAGGED | WATER;
|
||||
|
||||
private Flags() {
|
||||
}
|
||||
@ -73,6 +74,7 @@ public class CreatureButcher {
|
||||
boolean killAmbient = (flags & Flags.AMBIENT) != 0;
|
||||
boolean killTagged = (flags & Flags.TAGGED) != 0;
|
||||
boolean killArmorStands = (flags & Flags.ARMOR_STAND) != 0;
|
||||
boolean killWaterCreatures = (flags & Flags.WATER) != 0;
|
||||
|
||||
EntityProperties type = entity.getFacet(EntityProperties.class);
|
||||
|
||||
@ -116,6 +118,10 @@ public class CreatureButcher {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!killWaterCreatures && type.isWaterCreature()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entity.remove();
|
||||
return true;
|
||||
};
|
||||
|
@ -19,10 +19,12 @@
|
||||
|
||||
package com.sk89q.worldedit.command.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.enginehub.piston.Command;
|
||||
import org.enginehub.piston.inject.InjectedValueAccess;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@ -45,8 +47,8 @@ public final class SubCommandPermissionCondition extends PermissionCondition {
|
||||
public static class Generator {
|
||||
private final List<Command> subCommands;
|
||||
|
||||
public Generator(List<Command> subCommands) {
|
||||
this.subCommands = subCommands;
|
||||
public Generator(Collection<? extends Command> subCommands) {
|
||||
this.subCommands = ImmutableList.copyOf(subCommands);
|
||||
}
|
||||
|
||||
public Command.Condition build() {
|
||||
|
@ -161,4 +161,11 @@ public interface EntityProperties {
|
||||
* @return true if pasteable
|
||||
*/
|
||||
boolean isPasteable();
|
||||
|
||||
/**
|
||||
* Test whether the entity is a water creature.
|
||||
*
|
||||
* @return true if water creature
|
||||
*/
|
||||
boolean isWaterCreature();
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ import com.sk89q.worldedit.command.WorldEditCommands;
|
||||
import com.sk89q.worldedit.command.WorldEditCommandsRegistration;
|
||||
import com.sk89q.worldedit.command.argument.Arguments;
|
||||
import com.sk89q.worldedit.command.argument.BooleanConverter;
|
||||
import com.sk89q.worldedit.command.argument.Chunk3dVectorConverter;
|
||||
import com.sk89q.worldedit.command.argument.CommaSeparatedValuesConverter;
|
||||
import com.sk89q.worldedit.command.argument.DirectionConverter;
|
||||
import com.sk89q.worldedit.command.argument.DirectionVectorConverter;
|
||||
@ -90,7 +91,9 @@ import com.sk89q.worldedit.command.argument.EntityRemoverConverter;
|
||||
import com.sk89q.worldedit.command.argument.EnumConverter;
|
||||
import com.sk89q.worldedit.command.argument.ExpressionConverter;
|
||||
import com.sk89q.worldedit.command.argument.FactoryConverter;
|
||||
import com.sk89q.worldedit.command.argument.HeightConverter;
|
||||
import com.sk89q.worldedit.command.argument.LocationConverter;
|
||||
import com.sk89q.worldedit.command.argument.OffsetConverter;
|
||||
import com.sk89q.worldedit.command.argument.RegionFactoryConverter;
|
||||
import com.sk89q.worldedit.command.argument.RegistryConverter;
|
||||
import com.sk89q.worldedit.command.argument.SideEffectConverter;
|
||||
@ -250,6 +253,7 @@ public final class PlatformCommandManager {
|
||||
);
|
||||
}
|
||||
VectorConverter.register(commandManager);
|
||||
Chunk3dVectorConverter.register(commandManager);
|
||||
EnumConverter.register(commandManager);
|
||||
RegistryConverter.register(commandManager);
|
||||
ZonedDateTimeConverter.register(commandManager);
|
||||
@ -260,6 +264,8 @@ public final class PlatformCommandManager {
|
||||
LocationConverter.register(commandManager);
|
||||
ExpressionConverter.register(commandManager);
|
||||
SideEffectConverter.register(commandManager);
|
||||
HeightConverter.register(commandManager);
|
||||
OffsetConverter.register(worldEdit, commandManager);
|
||||
|
||||
registerBindings(new ConsumeBindings(worldEdit, this));
|
||||
registerBindings(new PrimitiveBindings(worldEdit));
|
||||
@ -359,9 +365,8 @@ public final class PlatformCommandManager {
|
||||
}
|
||||
|
||||
private <CI> void registerSubCommands(String name, List<String> aliases, String desc,
|
||||
CommandManager commandManager,
|
||||
Consumer<BiConsumer<CommandRegistration, CI>> handlerInstance,
|
||||
@NotNull Consumer<CommandManager> additionalConfig) {
|
||||
Consumer<BiConsumer<CommandRegistration, CI>> handlerInstance,
|
||||
@NotNull Consumer<CommandManager> additionalConfig) {
|
||||
commandManager.register(name, cmd -> {
|
||||
cmd.aliases(aliases);
|
||||
cmd.description(TextComponent.of(desc));
|
||||
@ -438,7 +443,6 @@ public final class PlatformCommandManager {
|
||||
"brush",
|
||||
Lists.newArrayList("br", "/brush", "/br", "/tool", "tool"),
|
||||
"Brushing commands",
|
||||
commandManager,
|
||||
c -> {
|
||||
c.accept(BrushCommandsRegistration.builder(), new BrushCommands(worldEdit));
|
||||
c.accept(ToolCommandsRegistration.builder(), new ToolCommands(worldEdit));
|
||||
@ -486,10 +490,11 @@ public final class PlatformCommandManager {
|
||||
ClipboardCommandsRegistration.builder(),
|
||||
new ClipboardCommands()
|
||||
);
|
||||
this.registration.register(
|
||||
commandManager,
|
||||
GeneralCommandsRegistration.builder(),
|
||||
new GeneralCommands(worldEdit)
|
||||
GeneralCommands.register(
|
||||
registration,
|
||||
commandManager,
|
||||
commandManagerService,
|
||||
worldEdit
|
||||
);
|
||||
this.registration.register(
|
||||
commandManager,
|
||||
|
@ -421,6 +421,10 @@ public interface Extent extends InputExtent, OutputExtent {
|
||||
|
||||
for (final BlockVector3 pt : region) {
|
||||
BlockType type = getBlock(pt).getBlockType();
|
||||
if (type == BlockTypes.__RESERVED__) {
|
||||
counter[1]++;
|
||||
continue;
|
||||
}
|
||||
counter[type.getInternalId()]++;
|
||||
}
|
||||
List<Countable<BlockType>> distribution = new ArrayList<>();
|
||||
@ -446,6 +450,13 @@ public interface Extent extends InputExtent, OutputExtent {
|
||||
for (final BlockVector3 pt : region) {
|
||||
BlockState blk = this.getBlock(pt);
|
||||
BlockType type = blk.getBlockType();
|
||||
if (type == BlockTypes.__RESERVED__) {
|
||||
int[] stateCounter = counter[1];
|
||||
if (stateCounter == null) {
|
||||
counter[1] = stateCounter = new int[BlockTypes.AIR.getMaxStateId() + 1];
|
||||
}
|
||||
stateCounter[BlockTypes.AIR.getDefaultState().getInternalPropertiesId()]++;
|
||||
}
|
||||
int[] stateCounter = counter[type.getInternalId()];
|
||||
if (stateCounter == null) {
|
||||
counter[type.getInternalId()] = stateCounter = new int[type.getMaxStateId() + 1];
|
||||
|
@ -22,7 +22,6 @@ import com.sk89q.worldedit.world.block.BlockType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PassthroughExtent extends AbstractDelegateExtent {
|
||||
|
@ -159,15 +159,16 @@ public class FastSchematicWriter implements ClipboardWriter {
|
||||
if (nbt != null) {
|
||||
Map<String, Tag> values = nbt.getValue();
|
||||
|
||||
values.remove("id"); // Remove 'id' if it exists. We want 'Id'
|
||||
|
||||
// Positions are kept in NBT, we don't want that.
|
||||
values.remove("x");
|
||||
values.remove("y");
|
||||
values.remove("z");
|
||||
if (!values.containsKey("Id")) {
|
||||
values.put("Id", new StringTag(block.getNbtId()));
|
||||
}
|
||||
values.put("Id", new StringTag(block.getNbtId()));
|
||||
|
||||
// Remove 'id' if it exists. We want 'Id'.
|
||||
// Do this after we get "getNbtId" cos otherwise "getNbtId" doesn't work.
|
||||
// Dum.
|
||||
values.remove("id");
|
||||
values.put("Pos", new IntArrayTag(new int[]{
|
||||
pos.getX(),
|
||||
pos.getY(),
|
||||
|
@ -32,12 +32,14 @@ public abstract class NBTSchematicReader implements ClipboardReader {
|
||||
|
||||
protected static <T extends Tag> T requireTag(Map<String, Tag> items, String key, Class<T> expected) throws IOException {
|
||||
if (!items.containsKey(key)) {
|
||||
throw new IOException("Schematic file is missing a \"" + key + "\" tag");
|
||||
throw new IOException("Schematic file is missing a \"" + key + "\" tag of type "
|
||||
+ expected.getName());
|
||||
}
|
||||
|
||||
Tag tag = items.get(key);
|
||||
if (!expected.isInstance(tag)) {
|
||||
throw new IOException(key + " tag is not of tag type " + expected.getName());
|
||||
throw new IOException(key + " tag is not of tag type " + expected.getName() + ", got "
|
||||
+ tag.getClass().getName() + " instead");
|
||||
}
|
||||
|
||||
return expected.cast(tag);
|
||||
|
@ -54,28 +54,22 @@ public class ForestGenerator implements RegionFunction {
|
||||
BlockState block = editSession.getBlock(position);
|
||||
BlockType t = block.getBlockType();
|
||||
|
||||
switch (t.getInternalId()) {
|
||||
case BlockID.GRASS_BLOCK:
|
||||
case BlockID.DIRT:
|
||||
case BlockID.PODZOL:
|
||||
case BlockID.COARSE_DIRT:
|
||||
if (t.getMaterial().isSolid()) {
|
||||
return treeType.generate(editSession, position.add(0, 1, 0));
|
||||
default:
|
||||
if (t.getMaterial().isReplacedDuringPlacement()) {
|
||||
// since the implementation's tree generators generally don't generate in non-air spots,
|
||||
// we trick editsession history here in the first call
|
||||
editSession.setBlock(position, BlockTypes.AIR.getDefaultState());
|
||||
// and then trick the generator here by directly setting into the world
|
||||
editSession.getWorld().setBlock(position, BlockTypes.AIR.getDefaultState());
|
||||
// so that now the generator can generate the tree
|
||||
boolean success = treeType.generate(editSession, position);
|
||||
if (!success) {
|
||||
editSession.setBlock(position, block); // restore on failure
|
||||
}
|
||||
return success;
|
||||
} else { // Trees won't grow on this!
|
||||
return false;
|
||||
} else if (t.getMaterial().isReplacedDuringPlacement()) {
|
||||
// since the implementation's tree generators generally don't generate in non-air spots,
|
||||
// we trick editsession history here in the first call
|
||||
editSession.setBlock(position, BlockTypes.AIR.getDefaultState());
|
||||
// and then trick the generator here by directly setting into the world
|
||||
editSession.getWorld().setBlock(position, BlockTypes.AIR.getDefaultState());
|
||||
// so that now the generator can generate the tree
|
||||
boolean success = treeType.generate(editSession, position);
|
||||
if (!success) {
|
||||
editSession.setBlock(position, block); // restore on failure
|
||||
}
|
||||
return success;
|
||||
} else { // Trees won't grow on this!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
public class RepeatingExtentPattern extends AbstractExtentPattern {
|
||||
|
||||
private final BlockVector3 size;
|
||||
private final MutableBlockVector3 mutable;
|
||||
private BlockVector3 origin;
|
||||
private BlockVector3 offset;
|
||||
|
||||
@ -47,7 +46,6 @@ public class RepeatingExtentPattern extends AbstractExtentPattern {
|
||||
setOrigin(origin);
|
||||
setOffset(offset);
|
||||
size = extent.getMaximumPoint().subtract(extent.getMinimumPoint()).add(1, 1, 1);
|
||||
this.mutable = new MutableBlockVector3();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,7 +91,7 @@ public class RepeatingExtentPattern extends AbstractExtentPattern {
|
||||
int x = Math.abs(position.getX() + offset.getX()) % size.getBlockX() + origin.getX();
|
||||
int y = Math.abs(position.getY() + offset.getY()) % size.getBlockY() + origin.getY();
|
||||
int z = Math.abs(position.getZ() + offset.getZ()) % size.getBlockZ() + origin.getZ();
|
||||
return getExtent().getFullBlock(mutable.setComponents(x, y, z));
|
||||
return getExtent().getFullBlock(x, y, z);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -52,14 +52,12 @@ public class CommandUtil {
|
||||
|
||||
private static final Component DEPRECATION_MARKER = TextComponent.of("This command is deprecated.");
|
||||
|
||||
private static Component makeDeprecatedFooter(String reason, Component newCommand) {
|
||||
private static Component makeDeprecatedFooter(String reason, Component replacement) {
|
||||
return TextComponent.builder()
|
||||
.append(DEPRECATION_MARKER)
|
||||
.append(" " + reason + ".")
|
||||
.append(TextComponent.newline())
|
||||
.append(TextComponent.of("Use ", TextColor.GOLD, TextDecoration.ITALIC))
|
||||
.append(newCommand)
|
||||
.append(TextComponent.of(" instead.", TextColor.GOLD, TextDecoration.ITALIC))
|
||||
.append(replacement.color(TextColor.GOLD).decoration(TextDecoration.ITALIC, true))
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -69,20 +67,48 @@ public class CommandUtil {
|
||||
|
||||
}
|
||||
|
||||
public interface ReplacementMessageGenerator {
|
||||
|
||||
/**
|
||||
* Generate text that says "Please use [cmd] instead." and allows clicking to dump
|
||||
* the command to the text box.
|
||||
*/
|
||||
static ReplacementMessageGenerator forNewCommand(NewCommandGenerator generator) {
|
||||
return (oldCommand, oldParameters) -> {
|
||||
String suggestedCommand = generator.newCommand(oldCommand, oldParameters);
|
||||
return createNewCommandReplacementText(suggestedCommand);
|
||||
};
|
||||
}
|
||||
|
||||
Component getReplacement(Command oldCommand, CommandParameters oldParameters);
|
||||
|
||||
}
|
||||
|
||||
public static Component createNewCommandReplacementText(String suggestedCommand) {
|
||||
return TextComponent.builder("Please use ", TextColor.GOLD)
|
||||
.append(TextComponent.of(suggestedCommand)
|
||||
.decoration(TextDecoration.UNDERLINED, true)
|
||||
.clickEvent(ClickEvent.suggestCommand(suggestedCommand)))
|
||||
.append(" instead.")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Command deprecate(Command command, String reason,
|
||||
NewCommandGenerator newCommandGenerator) {
|
||||
ReplacementMessageGenerator replacementMessageGenerator) {
|
||||
Component deprecatedWarning = makeDeprecatedFooter(
|
||||
reason,
|
||||
newCommandSuggestion(newCommandGenerator,
|
||||
NoInputCommandParameters.builder().build(),
|
||||
command)
|
||||
replacementMessageGenerator.getReplacement(
|
||||
command,
|
||||
NoInputCommandParameters.builder().build()
|
||||
)
|
||||
);
|
||||
return command.toBuilder()
|
||||
.action(parameters ->
|
||||
deprecatedCommandWarning(parameters, command, reason, newCommandGenerator))
|
||||
deprecatedCommandWarning(parameters, command, reason, replacementMessageGenerator))
|
||||
.footer(command.getFooter()
|
||||
.map(existingFooter -> existingFooter
|
||||
.append(TextComponent.newline()).append(deprecatedWarning))
|
||||
.append(TextComponent.newline())
|
||||
.append(deprecatedWarning))
|
||||
.orElse(deprecatedWarning))
|
||||
.build();
|
||||
}
|
||||
@ -139,26 +165,28 @@ public class CommandUtil {
|
||||
CommandParameters parameters,
|
||||
Command command,
|
||||
String reason,
|
||||
NewCommandGenerator generator
|
||||
ReplacementMessageGenerator generator
|
||||
) throws Exception {
|
||||
parameters.injectedValue(Key.of(Actor.class))
|
||||
.ifPresent(actor -> {
|
||||
Component suggestion = newCommandSuggestion(generator, parameters, command);
|
||||
actor.print(TextComponent.of(reason + ". Please use ", TextColor.GOLD)
|
||||
.append(suggestion)
|
||||
.append(TextComponent.of(" instead."))
|
||||
);
|
||||
});
|
||||
.ifPresent(actor ->
|
||||
sendDeprecationMessage(parameters, command, reason, generator, actor)
|
||||
);
|
||||
return command.getAction().run(parameters);
|
||||
}
|
||||
|
||||
private static Component newCommandSuggestion(NewCommandGenerator generator,
|
||||
CommandParameters parameters,
|
||||
Command command) {
|
||||
String suggestedCommand = generator.newCommand(command, parameters);
|
||||
return TextComponent.of(suggestedCommand)
|
||||
.decoration(TextDecoration.UNDERLINED, true)
|
||||
.clickEvent(ClickEvent.suggestCommand(suggestedCommand));
|
||||
private static void sendDeprecationMessage(
|
||||
CommandParameters parameters,
|
||||
Command command,
|
||||
String reason,
|
||||
ReplacementMessageGenerator generator,
|
||||
Actor actor
|
||||
) {
|
||||
Component replacement = generator.getReplacement(command, parameters);
|
||||
actor.print(
|
||||
TextComponent.builder(reason + ". ", TextColor.GOLD)
|
||||
.append(replacement)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public static Map<String, Command> getSubCommands(Command currentCommand) {
|
||||
|
@ -36,6 +36,7 @@ import com.sk89q.worldedit.command.InsufficientArgumentsException;
|
||||
import com.sk89q.worldedit.command.tool.InvalidToolBindException;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
import com.sk89q.worldedit.regions.RegionOperationException;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.io.file.FileSelectionAbortedException;
|
||||
import com.sk89q.worldedit.util.io.file.FilenameResolutionException;
|
||||
@ -62,7 +63,11 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper {
|
||||
}
|
||||
|
||||
private CommandException newCommandException(String message, Throwable cause) {
|
||||
return new CommandException(TextComponent.of(String.valueOf(message)), cause, ImmutableList.of());
|
||||
return newCommandException(TextComponent.of(String.valueOf(message)), cause);
|
||||
}
|
||||
|
||||
private CommandException newCommandException(Component message, Throwable cause) {
|
||||
return new CommandException(message, cause, ImmutableList.of());
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
@ -158,7 +163,13 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper {
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(InvalidToolBindException e) throws CommandException {
|
||||
throw newCommandException("Can't bind tool to " + e.getItemType().getName() + ": " + e.getMessage(), e);
|
||||
throw newCommandException(
|
||||
TextComponent.builder("Can't bind tool to ")
|
||||
.append(e.getItemType().getRichName())
|
||||
.append(": " + e.getMessage())
|
||||
.build(),
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
|
@ -65,9 +65,22 @@ public class ExpressionHelper {
|
||||
Set<MethodHandle> matchingFns = functions.getMap().get(fnName);
|
||||
check(!matchingFns.isEmpty(), ctx, "Unknown function '" + fnName + "'");
|
||||
for (MethodHandle function : matchingFns) {
|
||||
if (function.isVarargsCollector()) {
|
||||
int nParams = function.type().parameterCount();
|
||||
// last param is the array, turn that varargs
|
||||
int keptParams = nParams - 1;
|
||||
function = function.asCollector(
|
||||
// collect into the last array
|
||||
function.type().parameterType(nParams - 1),
|
||||
// collect the variable args (args over kept)
|
||||
ctx.args.size() - keptParams
|
||||
);
|
||||
// re-wrap it for the inner arguments
|
||||
function = function.asType(function.type().wrap());
|
||||
}
|
||||
MethodType type = function.type();
|
||||
// Validate argc if not varargs
|
||||
if (!function.isVarargsCollector() && type.parameterCount() != ctx.args.size()) {
|
||||
if (type.parameterCount() != ctx.args.size()) {
|
||||
// skip non-matching function
|
||||
continue;
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ public final class Functions {
|
||||
}
|
||||
|
||||
private static MethodHandle clean(MethodHandle handle) {
|
||||
boolean wasVarargs = handle.isVarargsCollector();
|
||||
// box it all first
|
||||
handle = handle.asType(handle.type().wrap());
|
||||
if (handle.type().returnType() != Double.class) {
|
||||
@ -72,6 +73,12 @@ public final class Functions {
|
||||
handle = handle.asType(handle.type().changeReturnType(Number.class));
|
||||
handle = filterReturnValue(handle, DOUBLE_VALUE);
|
||||
}
|
||||
// return vararg-ity
|
||||
if (wasVarargs) {
|
||||
handle = handle.asVarargsCollector(
|
||||
handle.type().parameterType(handle.type().parameterCount() - 1)
|
||||
);
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ public interface WorldNativeAccess<NC, NBS, NP> {
|
||||
}
|
||||
|
||||
// Make connection updates optional
|
||||
if (sideEffectSet.shouldApply(SideEffect.VALIDATION)) {
|
||||
if (sideEffectSet.shouldApply(SideEffect.NEIGHBORS)) {
|
||||
updateNeighbors(pos, oldState, newState, 512);
|
||||
}
|
||||
|
||||
|
@ -610,4 +610,12 @@ public class BlockVector2 {
|
||||
public String toString() {
|
||||
return "(" + x + ", " + z + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation that is supported by the parser.
|
||||
* @return string
|
||||
*/
|
||||
public String toParserString() {
|
||||
return x + "," + z;
|
||||
}
|
||||
}
|
||||
|
@ -787,6 +787,14 @@ public abstract class BlockVector3 {
|
||||
return "(" + getX() + ", " + getY() + ", " + getZ() + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation that is supported by the parser.
|
||||
* @return string
|
||||
*/
|
||||
public String toParserString() {
|
||||
return getX() + "," + getY() + "," + getZ();
|
||||
}
|
||||
|
||||
//Used by VS fork
|
||||
public BlockVector3 plus(BlockVector3 other) {
|
||||
return add(other);
|
||||
|
@ -478,4 +478,12 @@ public final class Vector2 {
|
||||
return "(" + x + ", " + z + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation that is supported by the parser.
|
||||
* @return string
|
||||
*/
|
||||
public String toParserString() {
|
||||
return x + "," + z;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -639,4 +639,12 @@ public abstract class Vector3 {
|
||||
return "(" + x + ", " + y + ", " + z + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation that is supported by the parser.
|
||||
* @return string
|
||||
*/
|
||||
public String toParserString() {
|
||||
return getX() + "," + getY() + "," + getZ();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -211,11 +211,11 @@ public abstract class AbstractRegion extends AbstractSet<BlockVector3> implement
|
||||
// Sub-class utilities
|
||||
|
||||
protected final int getWorldMinY() {
|
||||
return world == null ? 0 : world.getMinY();
|
||||
return world == null ? Integer.MIN_VALUE : world.getMinY();
|
||||
}
|
||||
|
||||
protected final int getWorldMaxY() {
|
||||
return world == null ? 255 : world.getMaxY();
|
||||
return world == null ? Integer.MAX_VALUE : world.getMaxY();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,6 +33,8 @@ import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -256,11 +258,15 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
|
||||
final List<Component> lines = new ArrayList<>();
|
||||
|
||||
if (position1 != null) {
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.cuboid.info.pos1", TextComponent.of(position1.toString())));
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.cuboid.info.pos1", TextComponent.of(position1.toString())
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.COPY_TO_CLIPBOARD, position1.toParserString()))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to copy")))));
|
||||
}
|
||||
|
||||
if (position2 != null) {
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.cuboid.info.pos2", TextComponent.of(position2.toString())));
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.cuboid.info.pos2", TextComponent.of(position2.toString())
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.COPY_TO_CLIPBOARD, position2.toParserString()))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to copy")))));
|
||||
}
|
||||
|
||||
return lines;
|
||||
|
@ -37,6 +37,8 @@ import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
@ -249,7 +251,10 @@ public class CylinderRegionSelector implements RegionSelector, CUIRegion {
|
||||
final List<Component> lines = new ArrayList<>();
|
||||
|
||||
if (!region.getCenter().equals(Vector3.ZERO)) {
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.cylinder.info.center", TextComponent.of(region.getCenter().toString())));
|
||||
Vector3 center = region.getCenter();
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.cylinder.info.center", TextComponent.of(center.toString())
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.COPY_TO_CLIPBOARD, center.toParserString()))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to copy")))));
|
||||
}
|
||||
if (!region.getRadius().equals(Vector2.ZERO)) {
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.cylinder.info.radius", TextComponent.of(region.getRadius().toString())));
|
||||
|
@ -34,6 +34,8 @@ import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -225,7 +227,9 @@ public class EllipsoidRegionSelector implements RegionSelector, CUIRegion {
|
||||
|
||||
final Vector3 center = region.getCenter();
|
||||
if (center.lengthSq() > 0) {
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.ellipsoid.info.center", TextComponent.of(center.toString())));
|
||||
lines.add(TranslatableComponent.of("worldedit.selection.ellipsoid.info.center", TextComponent.of(center.toString())
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.COPY_TO_CLIPBOARD, center.toParserString()))
|
||||
.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to copy")))));
|
||||
}
|
||||
|
||||
final Vector3 radius = region.getRadius();
|
||||
|
@ -82,10 +82,12 @@ public class TreeGenerator {
|
||||
JUNGLE_BUSH("Jungle bush", "junglebush", "jungleshrub"),
|
||||
RED_MUSHROOM("Red mushroom", "redmushroom", "redgiantmushroom"),
|
||||
BROWN_MUSHROOM("Brown mushroom", "brownmushroom", "browngiantmushroom"),
|
||||
CRIMSON_FUNGUS("Crimson fungus", "crimsonfungus", "rednethermushroom"),
|
||||
WARPED_FUNGUS("Warped fungus", "warpedfungus", "greennethermushroom"),
|
||||
RANDOM_MUSHROOM("Random mushroom", "randmushroom", "randommushroom") {
|
||||
@Override
|
||||
public boolean generate(EditSession editSession, BlockVector3 pos) throws MaxChangedBlocksException {
|
||||
TreeType[] choices = { RED_MUSHROOM, BROWN_MUSHROOM };
|
||||
TreeType[] choices = { RED_MUSHROOM, BROWN_MUSHROOM, CRIMSON_FUNGUS, WARPED_FUNGUS };
|
||||
return choices[TreeGenerator.RANDOM.nextInt(choices.length)].generate(editSession, pos);
|
||||
}
|
||||
},
|
||||
@ -99,6 +101,13 @@ public class TreeGenerator {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
CHORUS_PLANT("Chorus plant", "chorusplant") {
|
||||
@Override
|
||||
public boolean generate(EditSession editSession, BlockVector3 pos) throws MaxChangedBlocksException {
|
||||
// chorus plants have to generate starting in the end stone itself, not the air above the ground
|
||||
return editSession.getWorld().generateTree(this, editSession, pos.subtract(0, 1, 0));
|
||||
}
|
||||
},
|
||||
RANDOM("Random tree", "rand", "random") {
|
||||
@Override
|
||||
public boolean generate(EditSession editSession, BlockVector3 pos) throws MaxChangedBlocksException {
|
||||
|
@ -21,17 +21,19 @@ package com.sk89q.worldedit.util.formatting.component;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.sk89q.worldedit.history.changeset.ChangeSet;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class PaginationBox extends MessageBox {
|
||||
|
||||
@ -138,53 +140,30 @@ public abstract class PaginationBox extends MessageBox {
|
||||
}
|
||||
|
||||
public static <T> PaginationBox fromStrings(String header, @Nullable String pageCommand, Collection<T> lines, Function<T, Component> adapt) {
|
||||
return fromStrings(header, pageCommand, Collections2.transform(lines, adapt));
|
||||
return fromComponents(header, pageCommand, Collections2.transform(lines, adapt));
|
||||
}
|
||||
|
||||
public static PaginationBox fromStrings(String header, @Nullable String pageCommand, Collection lines) {
|
||||
public static PaginationBox fromStrings(String header, @Nullable String pageCommand, Collection<String> lines) {
|
||||
return fromComponents(header, pageCommand, lines.stream()
|
||||
.map(TextComponent::of)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static PaginationBox fromComponents(String header, @Nullable String pageCommand, Collection<Component> lines) {
|
||||
return new ListPaginationBox(header, pageCommand, lines);
|
||||
}
|
||||
|
||||
public static PaginationBox fromStrings(String header, @Nullable String pageCommand, List<String> lines) {
|
||||
return fromStrings(header, pageCommand, (Collection) lines);
|
||||
}
|
||||
private static class ListPaginationBox extends PaginationBox {
|
||||
private final List<Component> lines;
|
||||
|
||||
public static class ListPaginationBox extends PaginationBox {
|
||||
private final Collection lines;
|
||||
private int iterIndex;
|
||||
private Iterator iterator;
|
||||
|
||||
public ListPaginationBox(String header, String pageCommand, List<String> lines) {
|
||||
this(header, pageCommand, (Collection) lines);
|
||||
}
|
||||
|
||||
public ListPaginationBox(String header, String pageCommand, Collection lines) {
|
||||
ListPaginationBox(String header, String pageCommand, Collection<Component> lines) {
|
||||
super(header, pageCommand);
|
||||
this.lines = lines;
|
||||
this.lines = ImmutableList.copyOf(lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent(int number) {
|
||||
Object obj;
|
||||
if (lines instanceof List) {
|
||||
obj = ((List) lines).get(number);
|
||||
} else {
|
||||
if (iterator == null || iterIndex > number) {
|
||||
iterator = lines.iterator();
|
||||
iterIndex = 0;
|
||||
}
|
||||
do {
|
||||
obj = iterator.next();
|
||||
iterIndex++;
|
||||
} while (iterIndex < number);
|
||||
}
|
||||
if (obj instanceof Supplier) {
|
||||
obj = ((Supplier) obj).get();
|
||||
}
|
||||
if (obj instanceof Component) {
|
||||
return (Component) obj;
|
||||
}
|
||||
return TextComponent.of(obj + "");
|
||||
return lines.get(number);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,7 +70,7 @@ public class SideEffectBox extends PaginationBox {
|
||||
for (SideEffect.State uiState : SHOWN_VALUES) {
|
||||
builder = builder.append(TextComponent.space());
|
||||
builder = builder.append(TranslatableComponent.of(uiState.getDisplayName(), uiState == state ? TextColor.WHITE : TextColor.GRAY)
|
||||
.clickEvent(ClickEvent.runCommand("//fast -h " + effect.name().toLowerCase(Locale.US) + " " + uiState.name().toLowerCase(Locale.US)))
|
||||
.clickEvent(ClickEvent.runCommand("//perf -h " + effect.name().toLowerCase(Locale.US) + " " + uiState.name().toLowerCase(Locale.US)))
|
||||
.hoverEvent(HoverEvent.showText(uiState == state
|
||||
? TranslatableComponent.of("worldedit.sideeffect.box.current")
|
||||
: TranslatableComponent.of("worldedit.sideeffect.box.change-to", TranslatableComponent.of(uiState.getDisplayName()))
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
package com.sk89q.worldedit.world.biome;
|
||||
|
||||
import com.sk89q.worldedit.world.registry.BiomeRegistry;
|
||||
|
||||
/**
|
||||
* Provides information about a biome.
|
||||
*
|
||||
@ -32,6 +34,8 @@ public interface BiomeData {
|
||||
* particular convention.
|
||||
*
|
||||
* @return the biome's name
|
||||
* @deprecated This method does not work on the server.
|
||||
* Use {@link BiomeRegistry#getRichName(BiomeType)}.
|
||||
*/
|
||||
@Deprecated
|
||||
String getName();
|
||||
|
@ -49,13 +49,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
*/
|
||||
public class BaseBlock implements BlockStateHolder<BaseBlock>, TileEntityBlock {
|
||||
|
||||
private BlockState blockState;
|
||||
@Nullable private CompoundTag nbtData;
|
||||
|
||||
@Deprecated
|
||||
public BaseBlock() {
|
||||
this(BlockTypes.AIR.getDefaultState());
|
||||
}
|
||||
private final BlockState blockState;
|
||||
@Nullable private final CompoundTag nbtData;
|
||||
|
||||
/**
|
||||
* Construct a block with the given type and default data.
|
||||
@ -151,6 +146,9 @@ public class BaseBlock implements BlockStateHolder<BaseBlock>, TileEntityBlock {
|
||||
return "";
|
||||
}
|
||||
Tag idTag = nbtData.getValue().get("id");
|
||||
if (idTag == null) {
|
||||
idTag = nbtData.getValue().get("Id");
|
||||
}
|
||||
if (idTag instanceof StringTag) {
|
||||
return ((StringTag) idTag).getValue();
|
||||
} else {
|
||||
@ -164,6 +162,11 @@ public class BaseBlock implements BlockStateHolder<BaseBlock>, TileEntityBlock {
|
||||
return this.nbtData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNbtData(@Nullable CompoundTag nbtData) {
|
||||
throw new UnsupportedOperationException("This class is immutable.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the type ID and data value are equal.
|
||||
*/
|
||||
|
@ -33,6 +33,7 @@ import com.sk89q.worldedit.registry.state.AbstractProperty;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.util.concurrency.LazyReference;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.item.ItemTypes;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
@ -96,6 +97,11 @@ public class BlockType implements Keyed, Pattern {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public Component getRichName() {
|
||||
return WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
||||
.getRegistries().getBlockRegistry().getRichName(this);
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
String id = getId();
|
||||
int i = id.indexOf(':');
|
||||
@ -111,15 +117,11 @@ public class BlockType implements Keyed, Pattern {
|
||||
* Gets the name of this block, or the ID if the name cannot be found.
|
||||
*
|
||||
* @return The name, or ID
|
||||
* @deprecated The name is now translatable, use {@link #getRichName()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public String getName() {
|
||||
String name = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry().getName(this);
|
||||
if (name == null) {
|
||||
return getId();
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
return getRichName().toString();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -274,9 +276,7 @@ public class BlockType implements Keyed, Pattern {
|
||||
/**
|
||||
* Gets the legacy ID. Needed for legacy reasons.
|
||||
*
|
||||
* <p>
|
||||
* DO NOT USE THIS.
|
||||
* </p>
|
||||
*
|
||||
* @return legacy id or 0, if unknown
|
||||
*/
|
||||
@ -286,11 +286,39 @@ public class BlockType implements Keyed, Pattern {
|
||||
return combinedId == null ? 0 : combinedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the legacy data. Needed for legacy reasons.
|
||||
*
|
||||
* DO NOT USE THIS.
|
||||
*
|
||||
* @return legacy data or 0, if unknown
|
||||
*/
|
||||
@Deprecated
|
||||
public int getLegacyId() {
|
||||
return computeLegacy(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the legacy data. Needed for legacy reasons.
|
||||
*
|
||||
* <p>
|
||||
* DO NOT USE THIS.
|
||||
* </p>
|
||||
*
|
||||
* @return legacy data or 0, if unknown
|
||||
*/
|
||||
@Deprecated
|
||||
public int getLegacyData() {
|
||||
return computeLegacy(1);
|
||||
}
|
||||
|
||||
private int computeLegacy(int index) {
|
||||
if (this.legacyCombinedId == null) {
|
||||
this.legacyCombinedId = LegacyMapper.getInstance().getLegacyCombined(this.getDefaultState());
|
||||
}
|
||||
return index == 0 ? legacyCombinedId >> 4 : legacyCombinedId & 15;
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal index of this type.
|
||||
*
|
||||
@ -336,25 +364,4 @@ public class BlockType implements Keyed, Pattern {
|
||||
public SingleBlockTypeMask toMask(Extent extent) {
|
||||
return new SingleBlockTypeMask(extent, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the legacy data. Needed for legacy reasons.
|
||||
*
|
||||
* <p>
|
||||
* DO NOT USE THIS.
|
||||
* </p>
|
||||
*
|
||||
* @return legacy data or 0, if unknown
|
||||
*/
|
||||
@Deprecated
|
||||
public int getLegacyData() {
|
||||
return computeLegacy(1);
|
||||
}
|
||||
|
||||
private int computeLegacy(int index) {
|
||||
if (this.legacyCombinedId == null) {
|
||||
this.legacyCombinedId = LegacyMapper.getInstance().getLegacyCombined(this.getDefaultState());
|
||||
}
|
||||
return index == 0 ? legacyCombinedId >> 4 : legacyCombinedId & 15;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.biome.BiomeData;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
|
||||
@ -29,11 +30,21 @@ import javax.annotation.Nullable;
|
||||
*/
|
||||
public interface BiomeRegistry {
|
||||
|
||||
/**
|
||||
* Get the name of the biome, usually as a translatable component.
|
||||
*
|
||||
* @param biomeType the biome type
|
||||
* @return the name of the biome
|
||||
*/
|
||||
Component getRichName(BiomeType biomeType);
|
||||
|
||||
/**
|
||||
* Get data about a biome.
|
||||
*
|
||||
* @param biome the biome
|
||||
* @return a data object or null if information is not known
|
||||
* @deprecated This method no longer returns any useful information.
|
||||
* Use {@link #getRichName(BiomeType)} for the name of the biome.
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
|
@ -20,29 +20,41 @@
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalInt;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Provides information on blocks and provides methods to create them.
|
||||
*/
|
||||
public interface BlockRegistry {
|
||||
|
||||
/**
|
||||
* Gets the name for the given block.
|
||||
*
|
||||
* @param blockType the block
|
||||
* @return The name
|
||||
*/
|
||||
Component getRichName(BlockType blockType);
|
||||
|
||||
/**
|
||||
* Gets the name for the given block.
|
||||
*
|
||||
* @param blockType the block
|
||||
* @return The name, or null if it's unknown
|
||||
* @deprecated Names are now translatable, use {@link #getRichName(BlockType)}.
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
String getName(BlockType blockType);
|
||||
default String getName(BlockType blockType) {
|
||||
return getRichName(blockType).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the material for the given block.
|
||||
|
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