This commit is contained in:
MattBDev 2020-12-14 18:23:14 -05:00
commit fdeaa8ad41
115 changed files with 4369 additions and 705 deletions

View File

@ -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

View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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")
}

View File

@ -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/)

View File

@ -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.

View File

@ -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/

View File

@ -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")

View File

@ -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);
}

View File

@ -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;
});

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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];
}
}

View File

@ -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));
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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("************************************************************");
}
}
}

View File

@ -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

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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<>();

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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}.
*

View File

@ -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"

View File

@ -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();

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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) {
}
}
}

View File

@ -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) {
}
}
}

View File

@ -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) {
}
}
}

View File

@ -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) {
}
}
}

View File

@ -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:

View File

@ -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;

View File

@ -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;
}

View File

@ -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) {

View File

@ -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();
}
/**

View File

@ -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 {

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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
*/

View File

@ -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();

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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(

View File

@ -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);
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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"));
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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, () -> {

View File

@ -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))

View File

@ -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;

View File

@ -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());

View File

@ -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;
};

View File

@ -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() {

View File

@ -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();
}

View File

@ -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,

View File

@ -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];

View File

@ -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 {

View File

@ -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(),

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;

View File

@ -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())));

View File

@ -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();

View File

@ -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 {

View File

@ -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

View File

@ -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()))

View File

@ -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();

View File

@ -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.
*/

View File

@ -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;
}
}

View File

@ -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

View File

@ -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.

Some files were not shown because too many files have changed in this diff Show More