Telesphoreo 2023-12-10 20:35:35 -06:00
commit 6c4d192aa9
66 changed files with 9041 additions and 426 deletions

View File

@ -27,7 +27,7 @@ body:
description: Which server version version you using? If your server version is not listed, it is not supported. Update to a supported version first. description: Which server version version you using? If your server version is not listed, it is not supported. Update to a supported version first.
multiple: false multiple: false
options: options:
- '1.20.2' - '1.20.4'
- '1.20' - '1.20'
- '1.19.4' - '1.19.4'
- '1.18.2' - '1.18.2'

View File

@ -13,7 +13,7 @@ jobs:
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1 uses: gradle/wrapper-validation-action@v1
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: temurin distribution: temurin
cache: gradle cache: gradle

View File

@ -13,7 +13,7 @@ jobs:
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1 uses: gradle/wrapper-validation-action@v1
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: temurin distribution: temurin
cache: gradle cache: gradle

View File

@ -21,7 +21,7 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: temurin distribution: temurin
cache: gradle cache: gradle

View File

@ -11,7 +11,7 @@ jobs:
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1 uses: gradle/wrapper-validation-action@v1
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: temurin distribution: temurin
cache: gradle cache: gradle
@ -19,7 +19,7 @@ jobs:
- name: Clean Build - name: Clean Build
run: ./gradlew clean build --no-daemon run: ./gradlew clean build --no-daemon
- name: Upload Release Assets - name: Upload Release Assets
uses: AButler/upload-release-assets@v2.0 uses: AButler/upload-release-assets@v3.0
with: with:
files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar' files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar'
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -7,7 +7,7 @@ import xyz.jpenilla.runpaper.task.RunServer
plugins { plugins {
id("io.github.gradle-nexus.publish-plugin") version "1.3.0" id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
id("xyz.jpenilla.run-paper") version "2.2.0" id("xyz.jpenilla.run-paper") version "2.2.2"
} }
if (!File("$rootDir/.git").exists()) { if (!File("$rootDir/.git").exists()) {
@ -34,7 +34,7 @@ logger.lifecycle("""
******************************************* *******************************************
""") """)
var rootVersion by extra("2.8.3") var rootVersion by extra("2.8.4")
var snapshot by extra("SNAPSHOT") var snapshot by extra("SNAPSHOT")
var revision: String by extra("") var revision: String by extra("")
var buildNumber by extra("") var buildNumber by extra("")
@ -83,7 +83,7 @@ allprojects {
} }
applyCommonConfiguration() applyCommonConfiguration()
val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.2") val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.4")
tasks { tasks {
supportedVersions.forEach { supportedVersions.forEach {
@ -97,7 +97,7 @@ tasks {
} }
} }
runServer { runServer {
minecraftVersion("1.20.2") minecraftVersion("1.20.4")
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
.toTypedArray()) .toTypedArray())

View File

@ -24,7 +24,7 @@ dependencies {
implementation(gradleApi()) implementation(gradleApi())
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.1") implementation("org.ajoberstar.grgit:grgit-gradle:5.2.1")
implementation("com.github.johnrengelman:shadow:8.1.1") implementation("com.github.johnrengelman:shadow:8.1.1")
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5") implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.10")
} }
kotlin { kotlin {

View File

@ -14,8 +14,8 @@ mapmanager = "1.8.0-SNAPSHOT"
griefprevention = "16.18.1" griefprevention = "16.18.1"
griefdefender = "2.1.0-SNAPSHOT" griefdefender = "2.1.0-SNAPSHOT"
residence = "4.5._13.1" residence = "4.5._13.1"
towny = "0.100.0.1" towny = "0.100.0.8"
plotsquared = "7.1.0" plotsquared = "7.2.0"
# Third party # Third party
bstats = "3.0.2" bstats = "3.0.2"
@ -23,7 +23,7 @@ sparsebitset = "1.3"
parallelgzip = "1.0.5" parallelgzip = "1.0.5"
adventure = "4.14.0" adventure = "4.14.0"
adventure-bukkit = "4.3.1" adventure-bukkit = "4.3.1"
checkerqual = "3.40.0" checkerqual = "3.41.0"
truezip = "6.8.4" truezip = "6.8.4"
auto-value = "1.10.4" auto-value = "1.10.4"
findbugs = "3.0.2" findbugs = "3.0.2"
@ -46,11 +46,11 @@ text = "3.0.4"
piston = "0.5.7" piston = "0.5.7"
# Tests # Tests
mockito = "5.6.0" mockito = "5.8.0"
# Gradle plugins # Gradle plugins
pluginyml = "0.6.0" pluginyml = "0.6.0"
minotaur = "2.8.4" minotaur = "2.8.7"
[libraries] [libraries]
# Minecraft expectations # Minecraft expectations

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit"
include("worldedit-libs") include("worldedit-libs")
listOf("legacy", "1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2").forEach { listOf("1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2", "1_20_4").forEach {
include("worldedit-bukkit:adapters:adapter-$it") include("worldedit-bukkit:adapters:adapter-$it")
} }

View File

@ -198,7 +198,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
getChunkFutureMainThreadMethod.setAccessible(true); getChunkFutureMainThreadMethod.setAccessible(true);
mainThreadProcessorField = ServerChunkCache.class.getDeclaredField( mainThreadProcessorField = ServerChunkCache.class.getDeclaredField(
Refraction.pickName("mainThreadProcessor", "h") Refraction.pickName("mainThreadProcessor", "g")
); );
mainThreadProcessorField.setAccessible(true); mainThreadProcessorField.setAccessible(true);

View File

@ -1,7 +1,6 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.entity.LazyBaseEntity;
@ -10,16 +9,13 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.blocks.TileEntityBlock;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
@ -39,7 +35,6 @@ import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
@ -79,14 +74,12 @@ import net.minecraft.world.level.chunk.LevelChunkSection;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.TreeType; import org.bukkit.World;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk; import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_17_R1.CraftServer; import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
@ -110,8 +103,7 @@ import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
@ -235,11 +227,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BlockState getBlock(Location location) { public BlockState getBlock(Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -255,12 +246,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BaseBlock getFullBlock(final Location location) { public BaseBlock getFullBlock(final Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -344,10 +334,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) { public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
return new PaperweightFaweWorldNativeAccess( return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
this,
new WeakReference<>(((CraftWorld) world).getHandle())
);
} }
@Override @Override
@ -492,7 +479,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); ServerLevel nmsWorld = getServerLevel(world);
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
if (map != null && map.wasAccessibleSinceLastSave()) { if (map != null && map.wasAccessibleSinceLastSave()) {
boolean flag = false; boolean flag = false;
@ -530,7 +517,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
int internalId = BlockStateIdAccess.getBlockStateId(blockState); int internalId = BlockStateIdAccess.getBlockStateId(blockState);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
return blockState1.hasPostProcess( return blockState1.hasPostProcess(
((CraftWorld) world).getHandle(), getServerLevel(world),
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
); );
} }
@ -546,54 +533,33 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
} }
@Override @Override
public boolean generateTree( protected void preCaptureStates(final ServerLevel serverLevel) {
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
org.bukkit.World bukkitWorld
) {
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
if (bukkitType == TreeType.CHORUS_PLANT) {
blockVector3 = blockVector3.add(
0,
1,
0
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
}
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
final BlockVector3 finalBlockVector = blockVector3;
// Sync to main thread to ensure no clashes occur
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
serverLevel.captureTreeGeneration = true; serverLevel.captureTreeGeneration = true;
serverLevel.captureBlockStates = true; serverLevel.captureBlockStates = true;
try {
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
return null;
} }
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
} finally { @Override
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
return new ArrayList<>(serverLevel.capturedBlockStates.values());
}
@Override
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
serverLevel.captureBlockStates = false; serverLevel.captureBlockStates = false;
serverLevel.captureTreeGeneration = false; serverLevel.captureTreeGeneration = false;
serverLevel.capturedBlockStates.clear(); serverLevel.capturedBlockStates.clear();
} }
});
if (placed == null || placed.isEmpty()) { @Override
return false; protected ServerLevel getServerLevel(final World world) {
} return ((CraftWorld) world).getHandle();
for (CraftBlockState craftBlockState : placed.values()) {
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
continue;
}
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
);
}
return true;
} }
@Override @Override
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) { public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
// Quickly add each entity to a list copy. // Quickly add each entity to a list copy.
List<Entity> mcEntities = new ArrayList<>(); List<Entity> mcEntities = new ArrayList<>();
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add); getServerLevel(world).entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
List<org.bukkit.entity.Entity> list = new ArrayList<>(); List<org.bukkit.entity.Entity> list = new ArrayList<>();
mcEntities.forEach((mcEnt) -> { mcEntities.forEach((mcEnt) -> {

View File

@ -1,7 +1,6 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.entity.LazyBaseEntity;
@ -10,16 +9,13 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.blocks.TileEntityBlock;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
@ -39,7 +35,6 @@ import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
@ -80,14 +75,12 @@ import net.minecraft.world.level.chunk.LevelChunkSection;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.TreeType; import org.bukkit.World;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk; import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer; import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
@ -111,8 +104,7 @@ import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
@ -235,11 +227,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BlockState getBlock(Location location) { public BlockState getBlock(Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -255,12 +246,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BaseBlock getFullBlock(final Location location) { public BaseBlock getFullBlock(final Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -337,10 +327,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) { public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
return new PaperweightFaweWorldNativeAccess( return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
this,
new WeakReference<>(((CraftWorld) world).getHandle())
);
} }
@Override @Override
@ -485,7 +472,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); ServerLevel nmsWorld = getServerLevel(world);
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
if (map != null && map.wasAccessibleSinceLastSave()) { if (map != null && map.wasAccessibleSinceLastSave()) {
// PlayerChunk.d players = map.players; // PlayerChunk.d players = map.players;
@ -522,7 +509,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
int internalId = BlockStateIdAccess.getBlockStateId(blockState); int internalId = BlockStateIdAccess.getBlockStateId(blockState);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
return blockState1.hasPostProcess( return blockState1.hasPostProcess(
((CraftWorld) world).getHandle(), getServerLevel(world),
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
); );
} }
@ -538,54 +525,33 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
} }
@Override @Override
public boolean generateTree( protected void preCaptureStates(final ServerLevel serverLevel) {
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
org.bukkit.World bukkitWorld
) {
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
if (bukkitType == TreeType.CHORUS_PLANT) {
blockVector3 = blockVector3.add(
0,
1,
0
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
}
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
final BlockVector3 finalBlockVector = blockVector3;
// Sync to main thread to ensure no clashes occur
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
serverLevel.captureTreeGeneration = true; serverLevel.captureTreeGeneration = true;
serverLevel.captureBlockStates = true; serverLevel.captureBlockStates = true;
try {
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
return null;
} }
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
} finally { @Override
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
return new ArrayList<>(serverLevel.capturedBlockStates.values());
}
@Override
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
serverLevel.captureBlockStates = false; serverLevel.captureBlockStates = false;
serverLevel.captureTreeGeneration = false; serverLevel.captureTreeGeneration = false;
serverLevel.capturedBlockStates.clear(); serverLevel.capturedBlockStates.clear();
} }
});
if (placed == null || placed.isEmpty()) { @Override
return false; protected ServerLevel getServerLevel(final World world) {
} return ((CraftWorld) world).getHandle();
for (CraftBlockState craftBlockState : placed.values()) {
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
continue;
}
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
);
}
return true;
} }
@Override @Override
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) { public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
// Quickly add each entity to a list copy. // Quickly add each entity to a list copy.
List<Entity> mcEntities = new ArrayList<>(); List<Entity> mcEntities = new ArrayList<>();
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add); getServerLevel(world).entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
List<org.bukkit.entity.Entity> list = new ArrayList<>(); List<org.bukkit.entity.Entity> list = new ArrayList<>();
mcEntities.forEach((mcEnt) -> { mcEntities.forEach((mcEnt) -> {

View File

@ -1,7 +1,6 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.entity.LazyBaseEntity;
@ -10,15 +9,12 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag;
@ -38,7 +34,6 @@ import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
@ -77,13 +72,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.TreeType; import org.bukkit.World;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer; import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
@ -111,8 +104,7 @@ import java.util.stream.Stream;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
@ -244,11 +236,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BlockState getBlock(Location location) { public BlockState getBlock(Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -264,12 +255,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BaseBlock getFullBlock(final Location location) { public BaseBlock getFullBlock(final Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -298,10 +288,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) { public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
return new PaperweightFaweWorldNativeAccess( return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
this,
new WeakReference<>(((CraftWorld) world).getHandle())
);
} }
@Override @Override
@ -446,7 +433,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); ServerLevel nmsWorld = getServerLevel(world);
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
if (map != null && wasAccessibleSinceLastSave(map)) { if (map != null && wasAccessibleSinceLastSave(map)) {
boolean flag = false; boolean flag = false;
@ -484,7 +471,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
int internalId = BlockStateIdAccess.getBlockStateId(blockState); int internalId = BlockStateIdAccess.getBlockStateId(blockState);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
return blockState1.hasPostProcess( return blockState1.hasPostProcess(
((CraftWorld) world).getHandle(), getServerLevel(world),
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
); );
} }
@ -501,47 +488,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
} }
@Override @Override
public boolean generateTree( protected void preCaptureStates(final ServerLevel serverLevel) {
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
org.bukkit.World bukkitWorld
) {
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
if (bukkitType == TreeType.CHORUS_PLANT) {
blockVector3 = blockVector3.add(
0,
1,
0
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
}
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
final BlockVector3 finalBlockVector = blockVector3;
// Sync to main thread to ensure no clashes occur
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
serverLevel.captureTreeGeneration = true; serverLevel.captureTreeGeneration = true;
serverLevel.captureBlockStates = true; serverLevel.captureBlockStates = true;
try {
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
return null;
} }
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
} finally { @Override
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
return new ArrayList<>(serverLevel.capturedBlockStates.values());
}
@Override
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
serverLevel.captureBlockStates = false; serverLevel.captureBlockStates = false;
serverLevel.captureTreeGeneration = false; serverLevel.captureTreeGeneration = false;
serverLevel.capturedBlockStates.clear(); serverLevel.capturedBlockStates.clear();
} }
});
if (placed == null || placed.isEmpty()) { @Override
return false; protected ServerLevel getServerLevel(final World world) {
} return ((CraftWorld) world).getHandle();
for (CraftBlockState craftBlockState : placed.values()) {
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
continue;
}
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
);
}
return true;
} }
@Override @Override

View File

@ -12,6 +12,6 @@ repositories {
dependencies { dependencies {
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.1-R0.1-20230916.212543-167") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.1-R0.1-20230921.165944-178")
compileOnly(libs.paperlib) compileOnly(libs.paperlib)
} }

View File

@ -1,7 +1,6 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.entity.LazyBaseEntity;
@ -10,15 +9,12 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag;
@ -38,7 +34,6 @@ import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
@ -77,13 +72,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.TreeType; import org.bukkit.World;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
@ -111,8 +104,7 @@ import java.util.stream.Stream;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
@ -244,11 +236,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BlockState getBlock(Location location) { public BlockState getBlock(Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -264,12 +255,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BaseBlock getFullBlock(final Location location) { public BaseBlock getFullBlock(final Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -298,10 +288,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) { public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
return new PaperweightFaweWorldNativeAccess( return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
this,
new WeakReference<>(((CraftWorld) world).getHandle())
);
} }
@Override @Override
@ -446,7 +433,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); ServerLevel nmsWorld = getServerLevel(world);
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
if (map != null && wasAccessibleSinceLastSave(map)) { if (map != null && wasAccessibleSinceLastSave(map)) {
boolean flag = false; boolean flag = false;
@ -484,7 +471,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
int internalId = BlockStateIdAccess.getBlockStateId(blockState); int internalId = BlockStateIdAccess.getBlockStateId(blockState);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
return blockState1.hasPostProcess( return blockState1.hasPostProcess(
((CraftWorld) world).getHandle(), getServerLevel(world),
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
); );
} }
@ -501,47 +488,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
} }
@Override @Override
public boolean generateTree( protected void preCaptureStates(final ServerLevel serverLevel) {
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
org.bukkit.World bukkitWorld
) {
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
if (bukkitType == TreeType.CHORUS_PLANT) {
blockVector3 = blockVector3.add(
0,
1,
0
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
}
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
final BlockVector3 finalBlockVector = blockVector3;
// Sync to main thread to ensure no clashes occur
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
serverLevel.captureTreeGeneration = true; serverLevel.captureTreeGeneration = true;
serverLevel.captureBlockStates = true; serverLevel.captureBlockStates = true;
try {
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
return null;
} }
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
} finally { @Override
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
return new ArrayList<>(serverLevel.capturedBlockStates.values());
}
@Override
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
serverLevel.captureBlockStates = false; serverLevel.captureBlockStates = false;
serverLevel.captureTreeGeneration = false; serverLevel.captureTreeGeneration = false;
serverLevel.capturedBlockStates.clear(); serverLevel.capturedBlockStates.clear();
} }
});
if (placed == null || placed.isEmpty()) { @Override
return false; protected ServerLevel getServerLevel(final World world) {
} return ((CraftWorld) world).getHandle();
for (CraftBlockState craftBlockState : placed.values()) {
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
continue;
}
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
);
}
return true;
} }
@Override @Override

View File

@ -12,6 +12,6 @@ repositories {
dependencies { dependencies {
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.2-R0.1-20231029.153906-63") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.2-R0.1-20231203.034718-121")
compileOnly(libs.paperlib) compileOnly(libs.paperlib)
} }

View File

@ -1,7 +1,6 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.entity.LazyBaseEntity;
@ -10,15 +9,12 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen;
@ -37,7 +33,6 @@ import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
@ -76,13 +71,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.TreeType; import org.bukkit.World;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R2.CraftServer; import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
@ -110,8 +103,7 @@ import java.util.stream.Stream;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
@ -247,11 +239,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BlockState getBlock(Location location) { public BlockState getBlock(Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -267,12 +258,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public BaseBlock getFullBlock(final Location location) { public BaseBlock getFullBlock(final Location location) {
Preconditions.checkNotNull(location); Preconditions.checkNotNull(location);
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
int x = location.getBlockX(); int x = location.getBlockX();
int y = location.getBlockY(); int y = location.getBlockY();
int z = location.getBlockZ(); int z = location.getBlockZ();
final ServerLevel handle = craftWorld.getHandle(); final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
@ -301,10 +291,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) { public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
return new PaperweightFaweWorldNativeAccess( return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
this,
new WeakReference<>(((CraftWorld) world).getHandle())
);
} }
@Override @Override
@ -449,7 +436,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
@Override @Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); ServerLevel nmsWorld = getServerLevel(world);
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
if (map != null && wasAccessibleSinceLastSave(map)) { if (map != null && wasAccessibleSinceLastSave(map)) {
boolean flag = false; boolean flag = false;
@ -487,7 +474,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
int internalId = BlockStateIdAccess.getBlockStateId(blockState); int internalId = BlockStateIdAccess.getBlockStateId(blockState);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
return blockState1.hasPostProcess( return blockState1.hasPostProcess(
((CraftWorld) world).getHandle(), getServerLevel(world),
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
); );
} }
@ -504,47 +491,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
} }
@Override @Override
public boolean generateTree( protected void preCaptureStates(final ServerLevel serverLevel) {
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
org.bukkit.World bukkitWorld
) {
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
if (bukkitType == TreeType.CHORUS_PLANT) {
blockVector3 = blockVector3.add(
0,
1,
0
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
}
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
final BlockVector3 finalBlockVector = blockVector3;
// Sync to main thread to ensure no clashes occur
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
serverLevel.captureTreeGeneration = true; serverLevel.captureTreeGeneration = true;
serverLevel.captureBlockStates = true; serverLevel.captureBlockStates = true;
try {
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
return null;
} }
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
} finally { @Override
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
return new ArrayList<>(serverLevel.capturedBlockStates.values());
}
@Override
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
serverLevel.captureBlockStates = false; serverLevel.captureBlockStates = false;
serverLevel.captureTreeGeneration = false; serverLevel.captureTreeGeneration = false;
serverLevel.capturedBlockStates.clear(); serverLevel.capturedBlockStates.clear();
} }
});
if (placed == null || placed.isEmpty()) { @Override
return false; protected ServerLevel getServerLevel(final World world) {
} return ((CraftWorld) world).getHandle();
for (CraftBlockState craftBlockState : placed.values()) {
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
continue;
}
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
);
}
return true;
} }
@Override @Override

View File

@ -0,0 +1,17 @@
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
plugins {
java
}
applyPaperweightAdapterConfiguration()
repositories {
gradlePluginPortal()
}
dependencies {
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.4-R0.1-20231207.202833-1")
compileOnly(libs.paperlib)
}

View File

@ -0,0 +1,98 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3;
import com.mojang.authlib.GameProfile;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.stats.Stat;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.world.phys.Vec3;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.OptionalInt;
import java.util.UUID;
class PaperweightFakePlayer extends ServerPlayer {
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]");
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
private static final ClientInformation FAKE_CLIENT_INFO = new ClientInformation(
"en_US", 16, ChatVisiblity.FULL, true, 0, HumanoidArm.LEFT, false, false
);
PaperweightFakePlayer(ServerLevel world) {
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE, FAKE_CLIENT_INFO);
}
@Override
public Vec3 position() {
return ORIGIN;
}
@Override
public void tick() {
}
@Override
public void die(DamageSource damagesource) {
}
@Override
public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
return this;
}
@Override
public OptionalInt openMenu(MenuProvider factory) {
return OptionalInt.empty();
}
@Override
public void updateOptions(ClientInformation clientOptions) {
}
@Override
public void displayClientMessage(Component message, boolean actionBar) {
}
@Override
public void awardStat(Stat<?> stat, int amount) {
}
@Override
public void awardStat(Stat<?> stat) {
}
@Override
public boolean isInvulnerableTo(DamageSource damageSource) {
return true;
}
@Override
public void openTextEdit(SignBlockEntity sign, boolean front) {
}
}

View File

@ -0,0 +1,181 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import com.sk89q.worldedit.world.block.BlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
import org.bukkit.event.block.BlockPhysicsEvent;
import java.lang.ref.WeakReference;
import java.util.Objects;
import javax.annotation.Nullable;
public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
private static final int UPDATE = 1;
private static final int NOTIFY = 2;
private final PaperweightAdapter adapter;
private final WeakReference<ServerLevel> world;
private SideEffectSet sideEffectSet;
public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> world) {
this.adapter = adapter;
this.world = world;
}
private ServerLevel getWorld() {
return Objects.requireNonNull(world.get(), "The reference to the world was lost");
}
@Override
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public LevelChunk getChunk(int x, int z) {
return getWorld().getChunk(x, z);
}
@Override
public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) {
int stateId = BlockStateIdAccess.getBlockStateId(state);
return BlockStateIdAccess.isValidInternalId(stateId)
? Block.stateById(stateId)
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
}
@Override
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) {
return chunk.getBlockState(position);
}
@Nullable
@Override
public net.minecraft.world.level.block.state.BlockState setBlockState(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState state) {
return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE));
}
@Override
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(net.minecraft.world.level.block.state.BlockState block, BlockPos position) {
return Block.updateFromNeighbourShapes(block, getWorld(), position);
}
@Override
public BlockPos getPosition(int x, int y, int z) {
return new BlockPos(x, y, z);
}
@Override
public void updateLightingForBlock(BlockPos position) {
getWorld().getChunkSource().getLightEngine().checkBlock(position);
}
@Override
public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) {
return false;
}
@Override
public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY);
}
}
@Override
public boolean isChunkTicking(LevelChunk chunk) {
return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING);
}
@Override
public void markBlockChanged(LevelChunk chunk, BlockPos position) {
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
getWorld().getChunkSource().blockChanged(position);
}
}
@Override
public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
ServerLevel world = getWorld();
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
world.updateNeighborsAt(pos, oldState.getBlock());
} else {
// When we don't want events, manually run the physics without them.
Block block = oldState.getBlock();
fireNeighborChanged(pos, world, block, pos.west());
fireNeighborChanged(pos, world, block, pos.east());
fireNeighborChanged(pos, world, block, pos.below());
fireNeighborChanged(pos, world, block, pos.above());
fireNeighborChanged(pos, world, block, pos.north());
fireNeighborChanged(pos, world, block, pos.south());
}
if (newState.hasAnalogOutputSignal()) {
world.updateNeighbourForOutputSignal(pos, newState.getBlock());
}
}
// Not sure why neighborChanged is deprecated
@SuppressWarnings("deprecation")
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
}
@Override
public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) {
ServerLevel world = getWorld();
// a == updateNeighbors
// b == updateDiagonalNeighbors
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
CraftWorld craftWorld = world.getWorld();
BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState));
world.getCraftServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
}
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
}
@Override
public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) {
getWorld().onBlockStateChange(pos, oldState, newState);
}
@Override
public void flush() {
}
}

View File

@ -0,0 +1,185 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.google.common.base.Suppliers;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.util.ReflectionUtil;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.PushReaction;
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
public class PaperweightBlockMaterial implements BlockMaterial {
private final Block block;
private final BlockState blockState;
private final boolean isTranslucent;
private final CraftBlockData craftBlockData;
private final org.bukkit.Material craftMaterial;
private final int opacity;
private final CompoundTag tile;
public PaperweightBlockMaterial(Block block) {
this(block, block.defaultBlockState());
}
public PaperweightBlockMaterial(Block block, BlockState blockState) {
this.block = block;
this.blockState = blockState;
this.craftBlockData = CraftBlockData.fromData(blockState);
this.craftMaterial = craftBlockData.getMaterial();
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block,
Refraction.pickName("properties", "aP"));
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
Refraction.pickName("canOcclude", "n")
);
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
BlockPos.ZERO,
blockState
);
tile = tileEntity == null
? null
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
}
public Block getBlock() {
return block;
}
public BlockState getState() {
return blockState;
}
public CraftBlockData getCraftBlockData() {
return craftBlockData;
}
@Override
public boolean isAir() {
return blockState.isAir();
}
@Override
public boolean isFullCube() {
return craftMaterial.isOccluding();
}
@Override
public boolean isOpaque() {
return blockState.isOpaque();
}
@Override
public boolean isPowerSource() {
return blockState.isSignalSource();
}
@Override
public boolean isLiquid() {
// TODO: Better check ?
return block instanceof LiquidBlock;
}
@Override
public boolean isSolid() {
// TODO: Replace
return blockState.isSolid();
}
@Override
public float getHardness() {
return craftBlockData.getState().destroySpeed;
}
@Override
public float getResistance() {
return block.getExplosionResistance();
}
@Override
public float getSlipperiness() {
return block.getFriction();
}
@Override
public int getLightValue() {
return blockState.getLightEmission();
}
@Override
public int getLightOpacity() {
return opacity;
}
@Override
public boolean isFragileWhenPushed() {
return blockState.getPistonPushReaction() == PushReaction.DESTROY;
}
@Override
public boolean isUnpushable() {
return blockState.getPistonPushReaction() == PushReaction.BLOCK;
}
@Override
public boolean isTicksRandomly() {
return block.isRandomlyTicking(blockState);
}
@Override
public boolean isMovementBlocker() {
return craftMaterial.isSolid();
}
@Override
public boolean isBurnable() {
return craftMaterial.isBurnable();
}
@Override
public boolean isToolRequired() {
// Removed in 1.16.1, this is not present in higher versions
return false;
}
@Override
public boolean isReplacedDuringPlacement() {
return blockState.canBeReplaced();
}
@Override
public boolean isTranslucent() {
return isTranslucent;
}
@Override
public boolean hasContainer() {
return block instanceof EntityBlock;
}
@Override
public boolean isTile() {
return block instanceof EntityBlock;
}
@Override
public CompoundTag getDefaultTile() {
return tile;
}
@Override
public int getMapColor() {
// rgb field
return block.defaultMapColor().col;
}
}

View File

@ -0,0 +1,617 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.state.BooleanProperty;
import com.sk89q.worldedit.registry.state.DirectionalProperty;
import com.sk89q.worldedit.registry.state.EnumProperty;
import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import com.sk89q.worldedit.util.nbt.StringBinaryTag;
import com.sk89q.worldedit.world.RegenOptions;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import io.papermc.lib.PaperLib;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.WritableRegistry;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.chunk.LevelChunk;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey;
import org.bukkit.entity.Player;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static net.minecraft.core.registries.Registries.BIOME;
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
static {
try {
CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
} catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
}
}
private final com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter parent;
// ------------------------------------------------------------------------
// Code that may break between versions of Minecraft
// ------------------------------------------------------------------------
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
private char[] ibdToStateOrdinal = null;
private int[] ordinalToIbdID = null;
private boolean initialised = false;
private Map<String, List<Property<?>>> allBlockProperties = null;
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter();
}
@Nullable
private static String getEntityId(Entity entity) {
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
return resourceLocation == null ? null : resourceLocation.toString();
}
private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) {
entity.save(compoundTag);
}
@Override
public BukkitImplAdapter<net.minecraft.nbt.Tag> getParent() {
return parent;
}
private synchronized boolean init() {
if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) {
return false;
}
ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size
ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size
for (int i = 0; i < ibdToStateOrdinal.length; i++) {
BlockState blockState = BlockTypesCache.states[i];
PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial();
int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState());
char ordinal = blockState.getOrdinalChar();
ibdToStateOrdinal[id] = ordinal;
ordinalToIbdID[ordinal] = id;
}
Map<String, List<Property<?>>> properties = new HashMap<>();
try {
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
Object obj = field.get(null);
if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property<?> state)) {
continue;
}
Property<?> property;
if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) {
property = new BooleanProperty(
state.getName(),
(List<Boolean>) ImmutableList.copyOf(state.getPossibleValues())
);
} else if (state instanceof DirectionProperty) {
property = new DirectionalProperty(
state.getName(),
state
.getPossibleValues()
.stream()
.map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase()))
.collect(Collectors.toList())
);
} else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
property = new EnumProperty(
state.getName(),
state
.getPossibleValues()
.stream()
.map(e -> ((StringRepresentable) e).getSerializedName())
.collect(Collectors.toList())
);
} else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) {
property = new IntegerProperty(
state.getName(),
(List<Integer>) ImmutableList.copyOf(state.getPossibleValues())
);
} else {
throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state
.getClass()
.getSimpleName());
}
properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> {
if (v == null) {
v = new ArrayList<>(Collections.singletonList(property));
} else {
v.add(property);
}
return v;
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} finally {
allBlockProperties = ImmutableMap.copyOf(properties);
}
initialised = true;
return true;
}
@Override
public BlockMaterial getMaterial(BlockType blockType) {
Block block = getBlock(blockType);
return new PaperweightBlockMaterial(block);
}
@Override
public synchronized BlockMaterial getMaterial(BlockState state) {
net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
return new PaperweightBlockMaterial(blockState.getBlock(), blockState);
}
public Block getBlock(BlockType blockType) {
return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK)
.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
}
@Deprecated
@Override
public BlockState getBlock(Location location) {
Preconditions.checkNotNull(location);
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
BlockState state = adapt(blockData);
if (state == null) {
org.bukkit.block.Block bukkitBlock = location.getBlock();
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
}
return state;
}
@Override
public BaseBlock getFullBlock(final Location location) {
Preconditions.checkNotNull(location);
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
final ServerLevel handle = getServerLevel(location.getWorld());
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
BlockState state = adapt(blockData);
if (state == null) {
org.bukkit.block.Block bukkitBlock = location.getBlock();
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
}
if (state.getBlockType().getMaterial().hasContainer()) {
// Read the NBT data
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
if (blockEntity != null) {
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
}
}
return state.toBaseBlock();
}
@Override
public Set<SideEffect> getSupportedSideEffects() {
return SideEffectSet.defaults().getSideEffectsToApply();
}
@Override
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
}
@Override
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
Preconditions.checkNotNull(entity);
CraftEntity craftEntity = ((CraftEntity) entity);
Entity mcEntity = craftEntity.getHandle();
String id = getEntityId(mcEntity);
if (id != null) {
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
Supplier<CompoundBinaryTag> saveTag = () -> {
final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag();
readEntityIntoTag(mcEntity, minecraftTag);
//add Id for AbstractChangeSet to work
final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag);
final Map<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
tags.put("Id", StringBinaryTag.of(id));
return CompoundBinaryTag.from(tags);
};
return new LazyBaseEntity(type, saveTag);
} else {
return null;
}
}
@Override
public Component getRichBlockName(BlockType blockType) {
return parent.getRichBlockName(blockType);
}
@Override
public Component getRichItemName(ItemType itemType) {
return parent.getRichItemName(itemType);
}
@Override
public Component getRichItemName(BaseItemStack itemStack) {
return parent.getRichItemName(itemStack);
}
@Override
public OptionalInt getInternalBlockStateId(BlockState state) {
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState();
return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState));
}
@Override
public BlockState adapt(BlockData blockData) {
CraftBlockData cbd = ((CraftBlockData) blockData);
net.minecraft.world.level.block.state.BlockState ibd = cbd.getState();
return adapt(ibd);
}
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
return BlockTypesCache.states[adaptToChar(blockState)];
}
public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) {
int id = Block.BLOCK_STATE_REGISTRY.getId(blockState);
if (initialised) {
return ibdToStateOrdinal[id];
}
synchronized (this) {
if (initialised) {
return ibdToStateOrdinal[id];
}
try {
init();
return ibdToStateOrdinal[id];
} catch (ArrayIndexOutOfBoundsException e1) {
LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!",
blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1
);
return BlockTypesCache.ReservedIDs.AIR;
}
}
}
public char ibdIDToOrdinal(int id) {
if (initialised) {
return ibdToStateOrdinal[id];
}
synchronized (this) {
if (initialised) {
return ibdToStateOrdinal[id];
}
init();
return ibdToStateOrdinal[id];
}
}
@Override
public char[] getIbdToStateOrdinal() {
if (initialised) {
return ibdToStateOrdinal;
}
synchronized (this) {
if (initialised) {
return ibdToStateOrdinal;
}
init();
return ibdToStateOrdinal;
}
}
public int ordinalToIbdID(char ordinal) {
if (initialised) {
return ordinalToIbdID[ordinal];
}
synchronized (this) {
if (initialised) {
return ordinalToIbdID[ordinal];
}
init();
return ordinalToIbdID[ordinal];
}
}
@Override
public int[] getOrdinalToIbdID() {
if (initialised) {
return ordinalToIbdID;
}
synchronized (this) {
if (initialised) {
return ordinalToIbdID;
}
init();
return ordinalToIbdID;
}
}
@Override
public <B extends BlockStateHolder<B>> BlockData adapt(B state) {
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
return material.getCraftBlockData();
}
@Override
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = getServerLevel(world);
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
if (map != null && wasAccessibleSinceLastSave(map)) {
boolean flag = false;
// PlayerChunk.d players = map.players;
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
*/ Stream.empty();
ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
.forEach(entityPlayer -> {
synchronized (chunkPacket) {
ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket();
if (nmsPacket == null) {
nmsPacket = mapUtil.create(this, chunkPacket);
chunkPacket.setNativePacket(nmsPacket);
}
try {
FaweCache.INSTANCE.CHUNK_FLAG.get().set(true);
entityPlayer.connection.send(nmsPacket);
} finally {
FaweCache.INSTANCE.CHUNK_FLAG.get().set(false);
}
}
});
}
}
@Override
public Map<String, ? extends Property<?>> getProperties(BlockType blockType) {
return getParent().getProperties(blockType);
}
@Override
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) {
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
return blockState1.hasPostProcess(
getServerLevel(world),
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
);
}
@Override
public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
ItemStack stack = new ItemStack(
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM)
.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
baseItemStack.getAmount()
);
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
return CraftItemStack.asCraftMirror(stack);
}
@Override
protected void preCaptureStates(final ServerLevel serverLevel) {
serverLevel.captureTreeGeneration = true;
serverLevel.captureBlockStates = true;
}
@Override
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
return new ArrayList<>(serverLevel.capturedBlockStates.values());
}
@Override
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
serverLevel.captureBlockStates = false;
serverLevel.captureTreeGeneration = false;
serverLevel.capturedBlockStates.clear();
}
@Override
protected ServerLevel getServerLevel(final World world) {
return ((CraftWorld) world).getHandle();
}
@Override
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount());
weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag())));
return weStack;
}
@Override
public Tag toNative(net.minecraft.nbt.Tag foreign) {
return parent.toNative(foreign);
}
@Override
public net.minecraft.nbt.Tag fromNative(Tag foreign) {
if (foreign instanceof PaperweightLazyCompoundTag) {
return ((PaperweightLazyCompoundTag) foreign).get();
}
return parent.fromNative(foreign);
}
@Override
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
}
@Override
public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) {
return new PaperweightGetBlocks(world, chunkX, chunkZ);
}
@Override
public int getInternalBiomeId(BiomeType biomeType) {
final Registry<Biome> registry = MinecraftServer
.getServer()
.registryAccess()
.registryOrThrow(BIOME);
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
Biome biome = registry.get(resourceLocation);
return registry.getId(biome);
}
@Override
public Iterable<NamespacedKey> getRegisteredBiomes() {
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) ((CraftServer) Bukkit.getServer())
.getServer()
.registryAccess()
.registryOrThrow(BIOME);
List<ResourceLocation> keys = biomeRegistry.stream()
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
List<NamespacedKey> namespacedKeys = new ArrayList<>();
for (ResourceLocation key : keys) {
try {
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
} catch (IllegalArgumentException e) {
LOGGER.error("Error converting biome key {}", key.toString(), e);
}
}
return namespacedKeys;
}
@Override
public RelighterFactory getRelighterFactory() {
if (PaperLib.isPaper()) {
return new PaperweightStarlightRelighterFactory();
} else {
return new NMSRelighterFactory();
}
}
@Override
public Map<String, List<Property<?>>> getAllProperties() {
if (initialised) {
return allBlockProperties;
}
synchronized (this) {
if (initialised) {
return allBlockProperties;
}
init();
return allBlockProperties;
}
}
@Override
public IBatchProcessor getTickingPostProcessor() {
return new PaperweightPostProcessor();
}
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
try {
return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
} catch (IllegalAccessException | InvocationTargetException ignored) {
// fall-through
}
}
// Papers new chunk system has no related replacement - therefor we assume true.
return true;
}
}

View File

@ -0,0 +1,286 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.TaskManager;
import com.fastasyncworldedit.core.util.task.RunnableVal;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import com.sk89q.worldedit.world.block.BlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
import org.bukkit.event.block.BlockPhysicsEvent;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<LevelChunk,
net.minecraft.world.level.block.state.BlockState, BlockPos> {
private static final int UPDATE = 1;
private static final int NOTIFY = 2;
private static final Direction[] NEIGHBOUR_ORDER = {
Direction.EAST,
Direction.WEST,
Direction.DOWN,
Direction.UP,
Direction.NORTH,
Direction.SOUTH
};
private final PaperweightFaweAdapter paperweightFaweAdapter;
private final WeakReference<Level> level;
private final AtomicInteger lastTick;
private final Set<CachedChange> cachedChanges = new HashSet<>();
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
private SideEffectSet sideEffectSet;
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
this.paperweightFaweAdapter = paperweightFaweAdapter;
this.level = level;
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
}
private Level getLevel() {
return Objects.requireNonNull(level.get(), "The reference to the world was lost");
}
@Override
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
this.sideEffectSet = sideEffectSet;
}
@Override
public LevelChunk getChunk(int x, int z) {
return getLevel().getChunk(x, z);
}
@Override
public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) {
int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar());
return BlockStateIdAccess.isValidInternalId(stateId)
? Block.stateById(stateId)
: ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState();
}
@Override
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) {
return levelChunk.getBlockState(blockPos);
}
@Nullable
@Override
public synchronized net.minecraft.world.level.block.state.BlockState setBlockState(
LevelChunk levelChunk, BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState blockState
) {
int currentTick = MinecraftServer.currentTick;
if (Fawe.isMainThread()) {
return levelChunk.setBlockState(blockPos, blockState,
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
);
}
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
cachedChunksToSend.add(new IntPair(levelChunk.locX, levelChunk.locZ));
boolean nextTick = lastTick.get() > currentTick;
if (nextTick || cachedChanges.size() >= 1024) {
if (nextTick) {
lastTick.set(currentTick);
}
flushAsync(nextTick);
}
return blockState;
}
@Override
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
net.minecraft.world.level.block.state.BlockState blockState,
BlockPos blockPos
) {
return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos);
}
@Override
public BlockPos getPosition(int x, int y, int z) {
return new BlockPos(x, y, z);
}
@Override
public void updateLightingForBlock(BlockPos blockPos) {
getLevel().getChunkSource().getLightEngine().checkBlock(blockPos);
}
@Override
public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) {
// We will assume that the tile entity was created for us,
// though we do not do this on the other versions
BlockEntity blockEntity = getLevel().getBlockEntity(blockPos);
if (blockEntity == null) {
return false;
}
net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeBinary(tag);
blockEntity.load((CompoundTag) nativeTag);
return true;
}
@Override
public void notifyBlockUpdate(
LevelChunk levelChunk, BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState oldState,
net.minecraft.world.level.block.state.BlockState newState
) {
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY);
}
}
@Override
public boolean isChunkTicking(LevelChunk levelChunk) {
return levelChunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING);
}
@Override
public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) {
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos);
}
}
@Override
public void notifyNeighbors(
BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState oldState,
net.minecraft.world.level.block.state.BlockState newState
) {
Level level = getLevel();
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
level.blockUpdated(blockPos, oldState.getBlock());
} else {
// When we don't want events, manually run the physics without them.
// Un-nest neighbour updating
for (Direction direction : NEIGHBOUR_ORDER) {
BlockPos shifted = blockPos.relative(direction);
level.getBlockState(shifted).neighborChanged(level, shifted, oldState.getBlock(), blockPos, false);
}
}
if (newState.hasAnalogOutputSignal()) {
level.updateNeighbourForOutputSignal(blockPos, newState.getBlock());
}
}
@Override
public void updateNeighbors(
BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState oldState,
net.minecraft.world.level.block.state.BlockState newState,
int recursionLimit
) {
Level level = getLevel();
// a == updateNeighbors
// b == updateDiagonalNeighbors
oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
CraftWorld craftWorld = level.getWorld();
if (craftWorld != null) {
BlockPhysicsEvent event = new BlockPhysicsEvent(
craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
CraftBlockData.fromData(newState)
);
level.getCraftServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
}
}
newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit);
newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
}
@Override
public void onBlockStateChange(
BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState oldState,
net.minecraft.world.level.block.state.BlockState newState
) {
getLevel().onBlockStateChange(blockPos, oldState, newState);
}
private synchronized void flushAsync(final boolean sendChunks) {
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
cachedChanges.clear();
final Set<IntPair> toSend;
if (sendChunks) {
toSend = Set.copyOf(cachedChunksToSend);
cachedChunksToSend.clear();
} else {
toSend = Collections.emptySet();
}
RunnableVal<Object> runnableVal = new RunnableVal<>() {
@Override
public void run(Object value) {
changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
));
if (!sendChunks) {
return;
}
for (IntPair chunk : toSend) {
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
}
}
};
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
}
@Override
public synchronized void flush() {
RunnableVal<Object> runnableVal = new RunnableVal<>() {
@Override
public void run(Object value) {
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
));
for (IntPair chunk : cachedChunksToSend) {
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
}
}
};
if (Fawe.isMainThread()) {
runnableVal.run();
} else {
TaskManager.taskManager().sync(runnableVal);
}
cachedChanges.clear();
cachedChunksToSend.clear();
}
private record CachedChange(
LevelChunk levelChunk,
BlockPos blockPos,
net.minecraft.world.level.block.state.BlockState blockState
) {
}
}

View File

@ -0,0 +1,254 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.google.common.base.Suppliers;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.Holder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
public class PaperweightGetBlocks_Copy implements IChunkGet {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
private final Set<CompoundTag> entities = new HashSet<>();
private final char[][] blocks;
private final int minHeight;
private final int maxHeight;
final ServerLevel serverLevel;
final LevelChunk levelChunk;
private PalettedContainer<Holder<Biome>>[] biomes = null;
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
this.levelChunk = levelChunk;
this.serverLevel = levelChunk.level;
this.minHeight = serverLevel.getMinBuildHeight();
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
this.blocks = new char[getSectionCount()][];
}
protected void storeTile(BlockEntity blockEntity) {
tiles.put(
BlockVector3.at(
blockEntity.getBlockPos().getX(),
blockEntity.getBlockPos().getY(),
blockEntity.getBlockPos().getZ()
),
new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId))
);
}
@Override
public Map<BlockVector3, CompoundTag> getTiles() {
return tiles;
}
@Override
@Nullable
public CompoundTag getTile(int x, int y, int z) {
return tiles.get(BlockVector3.at(x, y, z));
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void storeEntity(Entity entity) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
entity.save(compoundTag);
entities.add((CompoundTag) adapter.toNative(compoundTag));
}
@Override
public Set<CompoundTag> getEntities() {
return this.entities;
}
@Override
public CompoundTag getEntity(UUID uuid) {
for (CompoundTag tag : entities) {
if (uuid.equals(tag.getUUID())) {
return tag;
}
}
return null;
}
@Override
public boolean isCreateCopy() {
return false;
}
@Override
public int setCreateCopy(boolean createCopy) {
return -1;
}
@Override
public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
}
@Override
public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
}
@Override
public int getMaxY() {
return maxHeight;
}
@Override
public int getMinY() {
return minHeight;
}
@Override
public int getMaxSectionPosition() {
return maxHeight >> 4;
}
@Override
public int getMinSectionPosition() {
return minHeight >> 4;
}
@Override
public BiomeType getBiomeType(int x, int y, int z) {
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()].get(x >> 2, (y & 15) >> 2, z >> 2);
return PaperweightPlatformAdapter.adapt(biome, serverLevel);
}
@Override
public void removeSectionLighting(int layer, boolean sky) {
}
@Override
public boolean trim(boolean aggressive, int layer) {
return false;
}
@Override
public IBlocks reset() {
return null;
}
@Override
public int getSectionCount() {
return serverLevel.getSectionsCount();
}
protected void storeSection(int layer, char[] data) {
blocks[layer] = data;
}
protected void storeBiomes(int layer, PalettedContainerRO<Holder<Biome>> biomeData) {
if (biomes == null) {
biomes = new PalettedContainer[getSectionCount()];
}
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
biomes[layer] = palettedContainer.copy();
} else {
LOGGER.error(
"Cannot correctly save biomes to history. Expected class type {} but got {}",
PalettedContainer.class.getSimpleName(),
biomeData.getClass().getSimpleName()
);
}
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
BlockState state = BlockTypesCache.states[get(x, y, z)];
return state.toBaseBlock(this, x, y, z);
}
@Override
public boolean hasSection(int layer) {
layer -= getMinSectionPosition();
return blocks[layer] != null;
}
@Override
public char[] load(int layer) {
layer -= getMinSectionPosition();
if (blocks[layer] == null) {
blocks[layer] = new char[4096];
Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR);
}
return blocks[layer];
}
@Override
public char[] loadIfPresent(int layer) {
layer -= getMinSectionPosition();
return blocks[layer];
}
@Override
public BlockState getBlock(int x, int y, int z) {
return BlockTypesCache.states[get(x, y, z)];
}
@Override
public int getSkyLight(int x, int y, int z) {
return 0;
}
@Override
public int getEmittedLight(int x, int y, int z) {
return 0;
}
@Override
public int[] getHeightMap(HeightMapType type) {
return new int[0];
}
@Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
return null;
}
public char get(int x, int y, int z) {
final int layer = (y >> 4) - getMinSectionPosition();
final int index = (y & 15) << 8 | z << 4 | x;
return blocks[layer][index];
}
@Override
public boolean trim(boolean aggressive) {
return false;
}
}

View File

@ -0,0 +1,34 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
//TODO un-very-break-this
public class PaperweightMapChunkUtil extends MapChunkUtil<ClientboundLevelChunkWithLightPacket> {
public PaperweightMapChunkUtil() throws NoSuchFieldException {
fieldX = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a"));
fieldZ = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("x", "a"));
fieldBitMask = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("z", "b"));
fieldHeightMap = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("heightmaps", "b"));
fieldChunkData = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("chunkData", "c"));
fieldBlockEntities = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("buffer", "c"));
fieldFull = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("blockEntitiesData", "d"));
fieldX.setAccessible(true);
fieldZ.setAccessible(true);
fieldBitMask.setAccessible(true);
fieldHeightMap.setAccessible(true);
fieldChunkData.setAccessible(true);
fieldBlockEntities.setAccessible(true);
fieldFull.setAccessible(true);
}
@Override
public ClientboundLevelChunkWithLightPacket createPacket() {
// TODO ??? return new ClientboundLevelChunkPacket();
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,719 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.destroystokyo.paper.util.maplist.EntityList;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.fastasyncworldedit.core.util.TaskManager;
import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import io.papermc.paper.world.ChunkEntitySlices;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.IdMap;
import net.minecraft.core.Registry;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.BitStorage;
import net.minecraft.util.ExceptionCollector;
import net.minecraft.util.SimpleBitStorage;
import net.minecraft.util.ThreadingDetector;
import net.minecraft.util.Unit;
import net.minecraft.util.ZeroBitStorage;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.GlobalPalette;
import net.minecraft.world.level.chunk.HashMapPalette;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.LinearPalette;
import net.minecraft.world.level.chunk.Palette;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.SingleValuePalette;
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R3.CraftChunk;
import sun.misc.Unsafe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import static java.lang.invoke.MethodType.methodType;
import static net.minecraft.core.registries.Registries.BIOME;
public final class PaperweightPlatformAdapter extends NMSAdapter {
public static final Field fieldData;
public static final Constructor<?> dataConstructor;
public static final Field fieldStorage;
public static final Field fieldPalette;
private static final Field fieldTickingFluidCount;
private static final Field fieldTickingBlockCount;
private static final Field fieldNonEmptyBlockCount;
private static final MethodHandle methodGetVisibleChunk;
private static final Field fieldThreadingDetector;
private static final Field fieldLock;
private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity;
/*
* This is a workaround for the changes from https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1fddefce1cdce44010927b888432bf70c0e88cde#src/main/java/org/bukkit/craftbukkit/CraftChunk.java
* and is only needed to support 1.19.4 versions before *and* after this change.
*/
private static final MethodHandle CRAFT_CHUNK_GET_HANDLE;
private static final Field fieldRemove;
private static final Logger LOGGER = LogManagerCompat.getLogger();
static final boolean POST_CHUNK_REWRITE;
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
private static Field LEVEL_CHUNK_ENTITIES;
private static Field SERVER_LEVEL_ENTITY_MANAGER;
static {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
fieldData.setAccessible(true);
Class<?> dataClazz = fieldData.getType();
dataConstructor = dataClazz.getDeclaredConstructors()[0];
dataConstructor.setAccessible(true);
fieldStorage = dataClazz.getDeclaredField(Refraction.pickName("storage", "b"));
fieldStorage.setAccessible(true);
fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c"));
fieldPalette.setAccessible(true);
fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g"));
fieldTickingFluidCount.setAccessible(true);
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f"));
fieldTickingBlockCount.setAccessible(true);
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "e"));
fieldNonEmptyBlockCount.setAccessible(true);
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
"getVisibleChunkIfPresent",
"b"
), long.class);
getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
if (!PaperLib.isPaper()) {
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
fieldThreadingDetector.setAccessible(true);
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
fieldLock.setAccessible(true);
} else {
// in paper, the used methods are synchronized properly
fieldThreadingDetector = null;
fieldLock = null;
}
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
Refraction.pickName("removeGameEventListener", "a"),
BlockEntity.class,
ServerLevel.class
);
removeGameEventListener.setAccessible(true);
methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener);
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
Refraction.pickName(
"removeBlockEntityTicker",
"l"
), BlockPos.class
);
removeBlockEntityTicker.setAccessible(true);
methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker);
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
fieldRemove.setAccessible(true);
boolean chunkRewrite;
try {
ServerLevel.class.getDeclaredMethod("getEntityLookup");
chunkRewrite = true;
PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities");
PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true);
} catch (NoSuchMethodException ignored) {
chunkRewrite = false;
}
try {
// Paper - Pre-Chunk-Update
LEVEL_CHUNK_ENTITIES = LevelChunk.class.getDeclaredField("entities");
LEVEL_CHUNK_ENTITIES.setAccessible(true);
} catch (NoSuchFieldException ignored) {
}
try {
// Non-Paper
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "M"));
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
} catch (NoSuchFieldException ignored) {
}
POST_CHUNK_REWRITE = chunkRewrite;
} catch (RuntimeException | Error e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
MethodHandle craftChunkGetHandle;
final MethodType type = methodType(LevelChunk.class);
try {
craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", type);
} catch (NoSuchMethodException | IllegalAccessException e) {
try {
final MethodType newType = methodType(ChunkAccess.class, ChunkStatus.class);
craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", newType);
craftChunkGetHandle = MethodHandles.insertArguments(craftChunkGetHandle, 1, ChunkStatus.FULL);
} catch (NoSuchMethodException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
CRAFT_CHUNK_GET_HANDLE = craftChunkGetHandle;
}
static boolean setSectionAtomic(
LevelChunkSection[] sections,
LevelChunkSection expected,
LevelChunkSection value,
int layer
) {
if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
}
return false;
}
// There is no point in having a functional semaphore for paper servers.
private static final ThreadLocal<DelegateSemaphore> SEMAPHORE_THREAD_LOCAL =
ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null));
static DelegateSemaphore applyLock(LevelChunkSection section) {
if (PaperLib.isPaper()) {
return SEMAPHORE_THREAD_LOCAL.get();
}
try {
synchronized (section) {
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
synchronized (currentThreadingDetector) {
Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore;
}
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
fieldLock.set(currentThreadingDetector, newLock);
return newLock;
}
}
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
if (!PaperLib.isPaper()) {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
if (nmsChunk != null) {
return nmsChunk;
}
if (Fawe.isMainThread()) {
return serverLevel.getChunk(chunkX, chunkZ);
}
} else {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk;
}
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk;
}
// Avoid "async" methods from the main thread.
if (Fawe.isMainThread()) {
return serverLevel.getChunk(chunkX, chunkZ);
}
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
try {
CraftChunk chunk;
try {
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
String world = serverLevel.getWorld().getName();
// We've already taken 10 seconds we can afford to wait a little here.
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
if (loaded) {
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
// Retry chunk load
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
} else {
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
}
}
addTicket(serverLevel, chunkX, chunkZ);
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
} catch (Throwable e) {
e.printStackTrace();
}
}
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
}
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
// Ensure chunk is definitely loaded before applying a ticket
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
.getChunkSource()
.addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
}
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
try {
return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ));
} catch (Throwable thr) {
throw new RuntimeException(thr);
}
}
@SuppressWarnings("deprecation")
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
if (chunkHolder == null) {
return;
}
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
LevelChunk levelChunk;
if (PaperLib.isPaper()) {
// getChunkAtIfLoadedImmediately is paper only
levelChunk = nmsWorld
.getChunkSource()
.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
} else {
levelChunk = ((Optional<LevelChunk>) ((Either) chunkHolder
.getTickingChunkFuture() // method is not present with new paper chunk system
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left())
.orElse(null);
}
if (levelChunk == null) {
return;
}
TaskManager.taskManager().task(() -> {
ClientboundLevelChunkWithLightPacket packet;
if (PaperLib.isPaper()) {
packet = new ClientboundLevelChunkWithLightPacket(
levelChunk,
nmsWorld.getChunkSource().getLightEngine(),
null,
null
// last false is to not bother with x-ray
);
} else {
// deprecated on paper - deprecation suppressed
packet = new ClientboundLevelChunkWithLightPacket(
levelChunk,
nmsWorld.getChunkSource().getLightEngine(),
null,
null
);
}
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
});
}
private static List<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false);
}
/*
NMS conversion
*/
public static LevelChunkSection newChunkSection(
final int layer,
final char[] blocks,
CachedBukkitAdapter adapter,
Registry<Biome> biomeRegistry,
@Nullable PalettedContainer<Holder<Biome>> biomes
) {
return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes);
}
public static LevelChunkSection newChunkSection(
final int layer,
final Function<Integer, char[]> get,
char[] set,
CachedBukkitAdapter adapter,
Registry<Biome> biomeRegistry,
@Nullable PalettedContainer<Holder<Biome>> biomes
) {
if (set == null) {
return newChunkSection(layer, biomeRegistry, biomes);
}
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
try {
int num_palette;
if (get == null) {
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, null);
} else {
num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter, null);
}
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
if (bitsPerEntry > 0 && bitsPerEntry < 5) {
bitsPerEntry = 4;
} else if (bitsPerEntry > 8) {
bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1);
}
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
if (num_palette == 1) {
for (int i = 0; i < blockBitArrayEnd; i++) {
blockStates[i] = 0;
}
} else {
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntryNonZero, 4096, blockStates);
bitArray.fromRaw(blocksCopy);
}
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
final BitStorage nmsBits;
if (bitsPerEntry == 0) {
nmsBits = new ZeroBitStorage(4096);
} else {
nmsBits = new SimpleBitStorage(bitsPerEntry, 4096, bits);
}
List<net.minecraft.world.level.block.state.BlockState> palette;
if (bitsPerEntry < 9) {
palette = new ArrayList<>();
for (int i = 0; i < num_palette; i++) {
int ordinal = paletteToBlock[i];
blockToPalette[ordinal] = Integer.MAX_VALUE;
final BlockState state = BlockTypesCache.states[ordinal];
palette.add(((PaperweightBlockMaterial) state.getMaterial()).getState());
}
} else {
palette = List.of();
}
// Create palette with data
@SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot
final PalettedContainer<net.minecraft.world.level.block.state.BlockState> blockStatePalettedContainer =
new PalettedContainer<>(
Block.BLOCK_STATE_REGISTRY,
PalettedContainer.Strategy.SECTION_STATES,
PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry),
nmsBits,
palette
);
if (biomes == null) {
IdMap<Holder<Biome>> biomeHolderIdMap = biomeRegistry.asHolderIdMap();
biomes = new PalettedContainer<>(
biomeHolderIdMap,
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(
BiomeTypes.PLAINS)),
PalettedContainer.Strategy.SECTION_BIOMES
);
}
return new LevelChunkSection(blockStatePalettedContainer, biomes);
} catch (final Throwable e) {
throw e;
} finally {
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
Arrays.fill(paletteToBlock, Integer.MAX_VALUE);
Arrays.fill(blockStates, 0);
Arrays.fill(blocksCopy, 0);
}
}
@SuppressWarnings("deprecation") // Only deprecated in paper
private static LevelChunkSection newChunkSection(
int layer,
Registry<Biome> biomeRegistry,
@Nullable PalettedContainer<Holder<Biome>> biomes
) {
if (biomes == null) {
return new LevelChunkSection(biomeRegistry);
}
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
Block.BLOCK_STATE_REGISTRY,
Blocks.AIR.defaultBlockState(),
PalettedContainer.Strategy.SECTION_STATES
);
return new LevelChunkSection(dataPaletteBlocks, biomes);
}
/**
* Create a new {@link PalettedContainer<Biome>}. Should only be used if no biome container existed beforehand.
*/
public static PalettedContainer<Holder<Biome>> getBiomePalettedContainer(
BiomeType[] biomes,
IdMap<Holder<Biome>> biomeRegistry
) {
if (biomes == null) {
return null;
}
BukkitImplAdapter<?> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
// Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length
Map<BiomeType, Holder<Biome>> palette = new HashMap<>();
for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) {
Holder<Biome> biome;
if (biomeType == null) {
biome = biomeRegistry.byId(adapter.getInternalBiomeId(BiomeTypes.PLAINS));
} else {
biome = biomeRegistry.byId(adapter.getInternalBiomeId(biomeType));
}
palette.put(biomeType, biome);
}
int biomeCount = palette.size();
int bitsPerEntry = MathMan.log2nlz(biomeCount - 1);
Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration(
new FakeIdMapBiome(biomeCount),
bitsPerEntry
);
if (bitsPerEntry > 3) {
bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1);
}
PalettedContainer<Holder<Biome>> biomePalettedContainer = new PalettedContainer<>(
biomeRegistry,
biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
PalettedContainer.Strategy.SECTION_BIOMES
);
final Palette<Holder<Biome>> biomePalette;
if (bitsPerEntry == 0) {
biomePalette = new SingleValuePalette<>(
biomePalettedContainer.registry,
biomePalettedContainer,
new ArrayList<>(palette.values()) // Must be modifiable
);
} else if (bitsPerEntry == 4) {
biomePalette = LinearPalette.create(
4,
biomePalettedContainer.registry,
biomePalettedContainer,
new ArrayList<>(palette.values()) // Must be modifiable
);
} else if (bitsPerEntry < 9) {
biomePalette = HashMapPalette.create(
bitsPerEntry,
biomePalettedContainer.registry,
biomePalettedContainer,
new ArrayList<>(palette.values()) // Must be modifiable
);
} else {
biomePalette = GlobalPalette.create(
bitsPerEntry,
biomePalettedContainer.registry,
biomePalettedContainer,
null // unused
);
}
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
final int arrayLength = MathMan.ceilZero(64f / blocksPerLong);
BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage(
bitsPerEntry,
64,
new long[arrayLength]
);
try {
Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette);
fieldData.set(biomePalettedContainer, data);
int index = 0;
for (int y = 0; y < 4; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 4; x++, index++) {
BiomeType biomeType = biomes[index];
if (biomeType == null) {
continue;
}
Holder<Biome> biome = biomeRegistry.byId(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(biomeType));
if (biome == null) {
continue;
}
biomePalettedContainer.set(x, y, z, biome);
}
}
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
return biomePalettedContainer;
}
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
fieldTickingFluidCount.setShort(section, (short) 0);
fieldTickingBlockCount.setShort(section, (short) 0);
}
public static BiomeType adapt(Holder<Biome> biome, LevelAccessor levelAccessor) {
final Registry<Biome> biomeRegistry = levelAccessor.registryAccess().registryOrThrow(BIOME);
if (biomeRegistry.getKey(biome.value()) == null) {
return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN
: null;
}
return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString());
}
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
try {
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
if (blockEntity != null) {
if (!levelChunk.level.isClientSide) {
methodRemoveGameEventListener.invoke(levelChunk, beacon, levelChunk.level);
}
fieldRemove.set(beacon, true);
}
}
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
static List<Entity> getEntities(LevelChunk chunk) {
ExceptionCollector<RuntimeException> collector = new ExceptionCollector<>();
if (PaperLib.isPaper()) {
if (POST_CHUNK_REWRITE) {
try {
//noinspection unchecked
return (List<Entity>) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e);
}
}
try {
EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk);
return List.of(entityList.getRawData());
} catch (IllegalAccessException e) {
collector.add(new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=false]", e));
// fall through
}
}
try {
//noinspection unchecked
return ((PersistentEntitySectionManager<Entity>) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos());
} catch (IllegalAccessException e) {
collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e));
}
collector.throwIfPresent();
return List.of();
}
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
@Override
public int getId(final net.minecraft.world.level.block.state.BlockState entry) {
return 0;
}
@Nullable
@Override
public net.minecraft.world.level.block.state.BlockState byId(final int index) {
return null;
}
@Nonnull
@Override
public Iterator<net.minecraft.world.level.block.state.BlockState> iterator() {
return Collections.emptyIterator();
}
}
record FakeIdMapBiome(int size) implements IdMap<Biome> {
@Override
public int getId(final Biome entry) {
return 0;
}
@Nullable
@Override
public Biome byId(final int index) {
return null;
}
@Nonnull
@Override
public Iterator<Biome> iterator() {
return Collections.emptyIterator();
}
}
}

View File

@ -0,0 +1,175 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.registry.state.PropertyKey;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
import javax.annotation.Nullable;
public class PaperweightPostProcessor implements IBatchProcessor {
@Override
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
return set;
}
@SuppressWarnings("deprecation")
@Override
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
// The PostProcessor shouldn't be added, but just in case
if (!tickFluid) {
return;
}
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
layer:
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
char[] set = iChunkSet.loadIfPresent(layer);
if (set == null) {
// No edit means no need to process
continue;
}
char[] get = null;
for (int i = 0; i < 4096; i++) {
char ordinal = set[i];
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
boolean fromGet = false; // Used for liquids
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
if (get == null) {
get = getBlocks.load(layer);
}
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
// actually being set
if (get == null) {
continue layer;
}
fromGet = true;
ordinal = replacedOrdinal = get[i];
}
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
continue;
} else if (!fromGet) { // if fromGet, don't do the same again
if (get == null) {
get = getBlocks.load(layer);
}
replacedOrdinal = get[i];
}
boolean ticking = BlockTypesCache.ticking[ordinal];
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
boolean replacedWasLiquid = false;
BlockState replacedState = null;
if (!ticking) {
// If the block being replaced was not ticking, it cannot be a liquid
if (!replacedWasTicking) {
continue;
}
// If the block being replaced is not fluid, we do not need to worry
if (!(replacedWasLiquid =
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
continue;
}
}
BlockState state = BlockState.getFromOrdinal(ordinal);
boolean liquid = state.getMaterial().isLiquid();
int x = i & 15;
int y = (i >> 8) & 15;
int z = (i >> 4) & 15;
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
if (liquid || replacedWasLiquid) {
if (liquid) {
addFluid(getBlocks.serverLevel, state, position);
continue;
}
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
// being ticked anyway. We only need it to be "hit" once.
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
continue;
}
addFluid(getBlocks.serverLevel, replacedState, position);
}
}
}
}
@Nullable
@Override
public Extent construct(final Extent child) {
throw new UnsupportedOperationException("Processing only");
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
if (set == null || get == null) {
return false;
}
char ordinal;
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
if (x > 0 && set[i - 1] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
return true;
}
}
if (x < 15 && set[i + 1] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
return true;
}
}
if (z > 0 && set[i - 16] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
return true;
}
}
if (z < 15 && set[i + 16] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
return true;
}
}
if (y > 0 && set[i - 256] != reserved) {
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
return true;
}
}
if (y < 15 && set[i + 256] != reserved) {
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
}
return false;
}
@SuppressWarnings("deprecation")
private boolean isFluid(char ordinal) {
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
}
@SuppressWarnings("deprecation")
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
Fluid type;
if (replacedState.getBlockType() == BlockTypes.LAVA) {
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
} else {
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
}
serverLevel.scheduleTick(
position,
type,
type.getTickDelay(serverLevel)
);
}
}

View File

@ -0,0 +1,76 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
super(serverLevel, queue);
}
@Override
protected ChunkPos createChunkPos(final long chunkKey) {
return new ChunkPos(chunkKey);
}
@Override
protected long asLong(final int chunkX, final int chunkZ) {
return ChunkPos.asLong(chunkX, chunkZ);
}
@Override
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
FAWE_TICKET,
chunkPos,
LIGHT_LEVEL,
Unit.INSTANCE
));
}
protected void invokeRelight(
Set<ChunkPos> coords,
Consumer<ChunkPos> chunkCallback,
IntConsumer processCallback
) {
try {
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
} catch (Exception e) {
LOGGER.error("Error occurred on relighting", e);
}
}
/*
* Allow the server to unload the chunks again.
* Also, if chunk packets are sent delayed, we need to do that here
*/
protected void postProcessChunks(Set<ChunkPos> coords) {
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
for (ChunkPos pos : coords) {
int x = pos.x;
int z = pos.z;
if (delay) { // we still need to send the block changes of that chunk
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
}
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
}
}
}

View File

@ -0,0 +1,25 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import javax.annotation.Nonnull;
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
@Override
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
org.bukkit.World w = Bukkit.getWorld(world.getName());
if (w == null) {
return NullRelighter.INSTANCE;
}
return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue);
}
}

View File

@ -0,0 +1,161 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.LazyCompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import net.minecraft.nbt.NumericTag;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
public class PaperweightLazyCompoundTag extends LazyCompoundTag {
private final Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier;
private CompoundTag compoundTag;
public PaperweightLazyCompoundTag(Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier) {
super(new HashMap<>());
this.compoundTagSupplier = compoundTagSupplier;
}
public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) {
this(() -> compoundTag);
}
public net.minecraft.nbt.CompoundTag get() {
return compoundTagSupplier.get();
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Tag> getValue() {
if (compoundTag == null) {
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
}
return compoundTag.getValue();
}
@Override
public CompoundBinaryTag asBinaryTag() {
getValue();
return compoundTag.asBinaryTag();
}
public boolean containsKey(String key) {
return compoundTagSupplier.get().contains(key);
}
public byte[] getByteArray(String key) {
return compoundTagSupplier.get().getByteArray(key);
}
public byte getByte(String key) {
return compoundTagSupplier.get().getByte(key);
}
public double getDouble(String key) {
return compoundTagSupplier.get().getDouble(key);
}
public double asDouble(String key) {
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
if (tag instanceof NumericTag numTag) {
return numTag.getAsDouble();
}
return 0;
}
public float getFloat(String key) {
return compoundTagSupplier.get().getFloat(key);
}
public int[] getIntArray(String key) {
return compoundTagSupplier.get().getIntArray(key);
}
public int getInt(String key) {
return compoundTagSupplier.get().getInt(key);
}
public int asInt(String key) {
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
if (tag instanceof NumericTag numTag) {
return numTag.getAsInt();
}
return 0;
}
@SuppressWarnings("unchecked")
public List<Tag> getList(String key) {
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
ArrayList<Tag> list = new ArrayList<>();
for (net.minecraft.nbt.Tag elem : nbtList) {
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
list.add(new PaperweightLazyCompoundTag(compoundTag));
} else {
list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem));
}
}
return list;
}
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
public ListTag getListTag(String key) {
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
if (tag instanceof net.minecraft.nbt.ListTag) {
return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag);
}
return new ListTag(StringTag.class, Collections.emptyList());
}
@SuppressWarnings("unchecked")
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
ListTag listTag = getListTag(key);
if (listTag.getType().equals(listType)) {
return (List<T>) listTag.getValue();
} else {
return Collections.emptyList();
}
}
public long[] getLongArray(String key) {
return compoundTagSupplier.get().getLongArray(key);
}
public long getLong(String key) {
return compoundTagSupplier.get().getLong(key);
}
public long asLong(String key) {
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
if (tag instanceof NumericTag numTag) {
return numTag.getAsLong();
}
return 0;
}
public short getShort(String key) {
return compoundTagSupplier.get().getShort(key);
}
public String getString(String key) {
return compoundTagSupplier.get().getString(key);
}
@Override
public String toString() {
return compoundTagSupplier.get().toString();
}
}

View File

@ -0,0 +1,590 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.regen;
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Lifecycle;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightGetBlocks;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.io.file.SafeFiles;
import com.sk89q.worldedit.world.RegenOptions;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.thread.ProcessorHandle;
import net.minecraft.util.thread.ProcessorMailbox;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.FixedBiomeSource;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.levelgen.blending.BlendingData;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.OptionalLong;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import static net.minecraft.core.registries.Registries.BIOME;
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Field serverWorldsField;
private static final Field paperConfigField;
private static final Field flatBedrockField;
private static final Field generatorSettingFlatField;
private static final Field generatorSettingBaseSupplierField;
private static final Field delegateField;
private static final Field chunkSourceField;
private static final Field generatorStructureStateField;
private static final Field ringPositionsField;
private static final Field hasGeneratedPositionsField;
//list of chunk stati in correct order without FULL
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
static {
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
chunkStati.put(
ChunkStatus.STRUCTURE_REFERENCES,
Concurrency.FULL
); // structure refs: radius 8, but only writes to current chunk
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
/*chunkStati.put(
ChunkStatus.LIQUID_CARVERS,
Concurrency.NONE
); // liquid carvers: radius 0, but RADIUS and FULL change results*/
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
chunkStati.put(
ChunkStatus.LIGHT,
Concurrency.FULL
); // light: radius 1, but no writes to other chunks, only current chunk
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
// chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
try {
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
serverWorldsField.setAccessible(true);
Field tmpPaperConfigField;
Field tmpFlatBedrockField;
try { //only present on paper
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
tmpPaperConfigField.setAccessible(true);
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
tmpFlatBedrockField.setAccessible(true);
} catch (Exception e) {
tmpPaperConfigField = null;
tmpFlatBedrockField = null;
}
paperConfigField = tmpPaperConfigField;
flatBedrockField = tmpFlatBedrockField;
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
"settings", "e"));
generatorSettingBaseSupplierField.setAccessible(true);
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
generatorSettingFlatField.setAccessible(true);
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
delegateField.setAccessible(true);
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I"));
chunkSourceField.setAccessible(true);
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v"));
generatorStructureStateField.setAccessible(true);
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
ringPositionsField.setAccessible(true);
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
Refraction.pickName("hasGeneratedPositions", "h")
);
hasGeneratedPositionsField.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//runtime
private ServerLevel originalServerWorld;
private ServerChunkCache originalChunkProvider;
private ServerLevel freshWorld;
private ServerChunkCache freshChunkProvider;
private LevelStorageSource.LevelStorageAccess session;
private StructureTemplateManager structureTemplateManager;
private ThreadedLevelLightEngine threadedLevelLightEngine;
private ChunkGenerator chunkGenerator;
private Path tempDir;
private boolean generateFlatBedrock = false;
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
super(originalBukkitWorld, region, target, options);
}
@Override
protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
originalChunkProvider = originalServerWorld.getChunkSource();
//flat bedrock? (only on paper)
if (paperConfigField != null) {
try {
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
} catch (Exception ignored) {
}
}
seed = options.getSeed().orElse(originalServerWorld.getSeed());
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
return true;
}
@Override
@SuppressWarnings("unchecked")
protected boolean initNewWorld() throws Exception {
//world folder
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
//prepare for world init (see upstream implementation for reference)
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
WorldOptions originalOpts = originalWorldData.worldGenOptions();
WorldOptions newOpts = options.getSeed().isPresent()
? originalOpts.withSeed(OptionalLong.of(seed))
: originalOpts;
LevelSettings newWorldSettings = new LevelSettings(
"faweregentempworld",
originalWorldData.settings.gameType(),
originalWorldData.settings.hardcore(),
originalWorldData.settings.difficulty(),
originalWorldData.settings.allowCommands(),
originalWorldData.settings.gameRules(),
originalWorldData.settings.getDataConfiguration()
);
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
originalWorldData.isFlatWorld()
? PrimaryLevelData.SpecialWorldProperty.FLAT
: originalWorldData.isDebugWorld()
? PrimaryLevelData.SpecialWorldProperty.DEBUG
: PrimaryLevelData.SpecialWorldProperty.NONE;
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
BiomeProvider biomeProvider = getBiomeProvider();
//init world
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
server,
server.executor,
session,
newWorldData,
originalServerWorld.dimension(),
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
.getOrThrow(levelStemResourceKey),
new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(),
seed,
ImmutableList.of(),
false,
originalServerWorld.getRandomSequences(),
environment,
generator,
biomeProvider
) {
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
) : null;
@Override
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
}
@Override
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
if (options.hasBiomeType()) {
return singleBiome;
}
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
);
}
}).get();
freshWorld.noSave = true;
removeWorldFromWorldsMap();
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
if (paperConfigField != null) {
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
}
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
originalGenerator);
BiomeSource biomeSource;
if (options.hasBiomeType()) {
biomeSource = new FixedBiomeSource(
DedicatedServer.getServer().registryAccess()
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
)
);
} else {
biomeSource = originalGenerator.getBiomeSource();
}
chunkGenerator = new NoiseBasedChunkGenerator(
biomeSource,
generatorSettingBaseSupplier
);
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
chunkGenerator = customChunkGenerator.getDelegate();
} else {
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
return false;
}
if (generator != null) {
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
generateConcurrent = generator.isParallelCapable();
}
// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed
freshChunkProvider = new ServerChunkCache(
freshWorld,
session,
server.getFixerUpper(),
server.getStructureManager(),
server.executor,
chunkGenerator,
freshWorld.spigotConfig.viewDistance,
freshWorld.spigotConfig.simulationDistance,
server.forceSynchronousWrites(),
new RegenNoOpWorldLoadListener(),
(chunkCoordIntPair, state) -> {
},
() -> server.overworld().getDataStorage()
) {
// redirect to LevelChunks created in #createChunks
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
ChunkAccess chunkAccess = getChunkAt(x, z);
if (chunkAccess == null && create) {
chunkAccess = createChunk(getProtoChunkAt(x, z));
}
return chunkAccess;
}
};
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
// Optimisation for needless ring position calculation when the seed and biome is the same.
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
if (hasGeneratedPositions) {
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
origPositions);
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
ringPositionsField.set(newState, copy);
hasGeneratedPositionsField.setBoolean(newState, true);
}
}
chunkSourceField.set(freshWorld, freshChunkProvider);
//let's start then
structureTemplateManager = server.getStructureManager();
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
return true;
}
@Override
protected void cleanup() {
try {
session.close();
} catch (Exception ignored) {
}
//shutdown chunk provider
try {
Fawe.instance().getQueueHandler().sync(() -> {
try {
freshChunkProvider.close(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} catch (Exception ignored) {
}
//remove world from server
try {
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
} catch (Exception ignored) {
}
//delete directory
try {
SafeFiles.tryHardToDeleteDir(tempDir);
} catch (Exception ignored) {
}
}
@Override
protected ProtoChunk createProtoChunk(int x, int z) {
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
this.freshWorld.registryAccess().registryOrThrow(BIOME), null
);
}
@Override
protected LevelChunk createChunk(ProtoChunk protoChunk) {
return new LevelChunk(
freshWorld,
protoChunk,
null // we don't want to add entities
);
}
@Override
protected ChunkStatusWrap getFullChunkStatus() {
return new ChunkStatusWrap(ChunkStatus.FULL);
}
@Override
protected List<BlockPopulator> getBlockPopulators() {
return originalServerWorld.getWorld().getPopulators();
}
@Override
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
// BlockPopulator#populate has to be called synchronously for TileEntity access
TaskManager.taskManager().task(() -> {
final CraftWorld world = freshWorld.getWorld();
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
blockPopulator.populate(world, random, chunk);
});
}
@Override
protected IChunkCache<IChunkGet> initSourceQueueCache() {
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
@Override
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
return getChunkAt(x, z);
}
};
}
//util
@SuppressWarnings("unchecked")
private void removeWorldFromWorldsMap() {
Fawe.instance().getQueueHandler().sync(() -> {
try {
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
map.remove("faweregentempworld");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
return switch (env) {
case NETHER -> LevelStem.NETHER;
case THE_END -> LevelStem.END;
default -> LevelStem.OVERWORLD;
};
}
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
private RegenNoOpWorldLoadListener() {
}
@Override
public void updateSpawnPos(ChunkPos spawnPos) {
}
@Override
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
}
@Override
public void start() {
}
@Override
public void stop() {
}
// TODO Paper only(?) @Override
public void setChunkRadius(int radius) {
}
}
private class FastProtoChunk extends ProtoChunk {
public FastProtoChunk(
final ChunkPos pos,
final UpgradeData upgradeData,
final LevelHeightAccessor world,
final Registry<Biome> biomeRegistry,
@Nullable final BlendingData blendingData
) {
super(pos, upgradeData, world, biomeRegistry, blendingData);
}
// avoid warning on paper
// compatibility with spigot
public boolean generateFlatBedrock() {
return generateFlatBedrock;
}
// no one will ever see the entities!
@Override
public List<CompoundTag> getEntities() {
return Collections.emptyList();
}
}
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
private final ChunkStatus chunkStatus;
public ChunkStatusWrap(ChunkStatus chunkStatus) {
this.chunkStatus = chunkStatus;
}
@Override
public int requiredNeighborChunkRadius() {
return chunkStatus.getRange();
}
@Override
public String name() {
return chunkStatus.toString();
}
@Override
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
return chunkStatus.generate(
Runnable::run, // TODO revisit, we might profit from this somehow?
freshWorld,
chunkGenerator,
structureTemplateManager,
threadedLevelLightEngine,
c -> CompletableFuture.completedFuture(Either.left(c)),
accessibleChunks
);
}
}
/**
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
* work this way.
*/
static class NoOpLightEngine extends ThreadedLevelLightEngine {
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {
}, "fawe-no-op");
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {
});
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
}
@Override
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
return CompletableFuture.completedFuture(chunk);
}
}
}

View File

@ -1,7 +0,0 @@
plugins {
base
}
artifacts {
add("default", file("./src/main/resources/fastasyncworldedit-adapters.jar"))
}

View File

@ -206,7 +206,7 @@ tasks {
versionNumber.set("${project.version}") versionNumber.set("${project.version}")
versionType.set("release") versionType.set("release")
uploadFile.set(file("build/libs/${rootProject.name}-Bukkit-${project.version}.jar")) uploadFile.set(file("build/libs/${rootProject.name}-Bukkit-${project.version}.jar"))
gameVersions.addAll(listOf("1.20.2", "1.20.1", "1.20", "1.19.4", "1.18.2", "1.17.1", "1.16.5")) gameVersions.addAll(listOf("1.20.4", "1.20.3", "1.20.2", "1.20.1", "1.20", "1.19.4", "1.18.2", "1.17.1"))
loaders.addAll(listOf("paper", "spigot")) loaders.addAll(listOf("paper", "spigot"))
changelog.set("The changelog is available on GitHub: https://github.com/IntellectualSites/" + changelog.set("The changelog is available on GitHub: https://github.com/IntellectualSites/" +
"FastAsyncWorldEdit/releases/tag/${project.version}") "FastAsyncWorldEdit/releases/tag/${project.version}")

View File

@ -108,14 +108,6 @@ public class FaweBukkit implements IFawe, Listener {
if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) { if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) {
LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?"); LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?");
} }
if (version.isEqualOrLowerThan(MinecraftVersion.ONE_DOT_SIXTEEN_EOL)) {
LOGGER.warn("You are running Minecraft 1.16.5. This version has been released over two years ago (January 2021).");
LOGGER.warn("FastAsyncWorldEdit will stop operating on this version in the near future.");
LOGGER.warn("Neither Mojang, nor Spigot or other software vendors support this version anymore." +
"Please update your server to a newer version of Minecraft (1.20+) to continue receiving updates and " +
"support.");
}
} }
@Override @Override

View File

@ -0,0 +1,72 @@
package com.fastasyncworldedit.bukkit.adapter;
import com.fastasyncworldedit.core.util.TaskManager;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.TreeGenerator;
import org.bukkit.Material;
import org.bukkit.TreeType;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import java.util.List;
/**
* A base class for version-specific implementations of the BukkitImplAdapter
*
* @param <TAG> the version-specific NBT tag type
* @param <SERVER_LEVEL> the version-specific ServerLevel type
*/
public abstract class FaweAdapter<TAG, SERVER_LEVEL> extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter<TAG> {
@Override
public boolean generateTree(
final TreeGenerator.TreeType treeType,
final EditSession editSession,
BlockVector3 blockVector3,
final World world
) {
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
if (bukkitType == TreeType.CHORUS_PLANT) {
// bukkit skips the feature gen which does this offset normally, so we have to add it back
blockVector3 = blockVector3.add(BlockVector3.UNIT_Y);
}
BlockVector3 target = blockVector3;
SERVER_LEVEL serverLevel = getServerLevel(world);
List<BlockState> placed = TaskManager.taskManager().sync(() -> {
preCaptureStates(serverLevel);
try {
if (!world.generateTree(BukkitAdapter.adapt(world, target), bukkitType)) {
return null;
}
return getCapturedBlockStatesCopy(serverLevel);
} finally {
postCaptureBlockStates(serverLevel);
}
});
if (placed == null || placed.isEmpty()) {
return false;
}
for (BlockState blockState : placed) {
if (blockState == null || blockState.getType() == Material.AIR) {
continue;
}
editSession.setBlock(blockState.getX(), blockState.getY(), blockState.getZ(),
BukkitAdapter.adapt(blockState.getBlockData())
);
}
return true;
}
protected abstract void preCaptureStates(SERVER_LEVEL serverLevel);
protected abstract List<BlockState> getCapturedBlockStatesCopy(SERVER_LEVEL serverLevel);
protected abstract void postCaptureBlockStates(SERVER_LEVEL serverLevel);
protected abstract SERVER_LEVEL getServerLevel(World world);
}

View File

@ -160,6 +160,10 @@ public class FaweDelegateRegionManager {
); );
editSession.setBlocks(onTop, air); editSession.setBlocks(onTop, air);
} }
FlatRegionFunction replace = new BiomeReplace(editSession, biome);
FlatRegionVisitor visitor = new FlatRegionVisitor((CuboidRegion) floorRegion, replace, editSession);
Operations.completeLegacy(visitor);
} }
if (hybridPlotWorld.PLOT_SCHEMATIC) { if (hybridPlotWorld.PLOT_SCHEMATIC) {

View File

@ -13,7 +13,6 @@ import java.util.regex.Pattern;
public class MinecraftVersion implements Comparable<MinecraftVersion> { public class MinecraftVersion implements Comparable<MinecraftVersion> {
public static final MinecraftVersion NETHER = new MinecraftVersion(1, 16); public static final MinecraftVersion NETHER = new MinecraftVersion(1, 16);
public static final MinecraftVersion ONE_DOT_SIXTEEN_EOL = new MinecraftVersion(1, 16, 5);
public static final MinecraftVersion CAVES_17 = new MinecraftVersion(1, 17); public static final MinecraftVersion CAVES_17 = new MinecraftVersion(1, 17);
public static final MinecraftVersion CAVES_18 = new MinecraftVersion(1, 18); public static final MinecraftVersion CAVES_18 = new MinecraftVersion(1, 18);
private static MinecraftVersion current = null; private static MinecraftVersion current = null;

View File

@ -21,7 +21,7 @@ permissions:
default: op default: op
children: children:
fawe.bypass.regions: true fawe.bypass.regions: true
fawe.limit.*: true fawe.limit.unlimited: true
fawe.tips: fawe.tips:
default: op default: op
fawe.admin: fawe.admin:

View File

@ -102,7 +102,7 @@ public class Settings extends Config {
public FaweLimit getLimit(Actor actor) { public FaweLimit getLimit(Actor actor) {
FaweLimit limit; FaweLimit limit;
if (actor.hasPermission("fawe.bypass") || actor.hasPermission("fawe.limit.unlimited")) { if (actor.hasPermission("fawe.limit.unlimited")) {
return FaweLimit.MAX.copy(); return FaweLimit.MAX.copy();
} }
limit = new FaweLimit(); limit = new FaweLimit();

View File

@ -159,15 +159,20 @@ public class RollbackDatabase extends AsyncNotifyQueue {
Future<Integer> future = call(() -> { Future<Integer> future = call(() -> {
try { try {
int count = 0; int count = 0;
String stmtStr = ascending ? uuid == null ? "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND" + String stmtStr;
" `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` , `id`" : if (ascending) {
"SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND" + if (uuid == null) {
" `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC" : stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " +
uuid == null ? "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? " + "`y2`>=? AND `y1`<=? ORDER BY `time` , `id`";
"AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` DESC, `id` DESC" : } else {
"SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND" + stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " +
" `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC"; "`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC";
try (PreparedStatement stmt = connection.prepareStatement(stmtStr)) { }
} else {
stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " +
"`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC";
}
try (PreparedStatement stmt = connection.prepareStatement(stmtStr.formatted(this.prefix))) {
stmt.setInt(1, (int) (minTime / 1000)); stmt.setInt(1, (int) (minTime / 1000));
stmt.setInt(2, pos1.getBlockX()); stmt.setInt(2, pos1.getBlockX());
stmt.setInt(3, pos2.getBlockX()); stmt.setInt(3, pos2.getBlockX());
@ -193,20 +198,20 @@ public class RollbackDatabase extends AsyncNotifyQueue {
if (delete && uuid != null) { if (delete && uuid != null) {
try (PreparedStatement stmt = connection.prepareStatement("DELETE FROM`" + this.prefix + try (PreparedStatement stmt = connection.prepareStatement("DELETE FROM`" + this.prefix +
"edits` WHERE `player`=? AND `time`>? AND `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?")) { "edits` WHERE `player`=? AND `time`>? AND `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?")) {
stmt.setInt(1, (int) (minTime / 1000));
stmt.setInt(2, pos1.getBlockX());
stmt.setInt(3, pos2.getBlockX());
stmt.setInt(4, pos1.getBlockZ());
stmt.setInt(5, pos2.getBlockZ());
// Keep 128 offset for backwards-compatibility
stmt.setInt(6, pos1.getBlockY() - 128);
stmt.setInt(7, pos2.getBlockY() - 128);
byte[] uuidBytes = ByteBuffer byte[] uuidBytes = ByteBuffer
.allocate(16) .allocate(16)
.putLong(uuid.getMostSignificantBits()) .putLong(uuid.getMostSignificantBits())
.putLong(uuid.getLeastSignificantBits()) .putLong(uuid.getLeastSignificantBits())
.array(); .array();
stmt.setBytes(8, uuidBytes); stmt.setBytes(1, uuidBytes);
stmt.setInt(2, (int) (minTime / 1000));
stmt.setInt(3, pos1.getBlockX());
stmt.setInt(4, pos2.getBlockX());
stmt.setInt(5, pos1.getBlockZ());
stmt.setInt(6, pos2.getBlockZ());
// Keep 128 offset for backwards-compatibility
stmt.setInt(7, pos1.getBlockY() - 128);
stmt.setInt(8, pos2.getBlockY() - 128);
} }
} }
return count; return count;

View File

@ -15,8 +15,10 @@ import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.function.operation.ChangeSetExecutor; import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import org.apache.logging.log4j.Logger;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -35,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class DiskStorageHistory extends FaweStreamChangeSet { public class DiskStorageHistory extends FaweStreamChangeSet {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Map<String, Map<UUID, Integer>> NEXT_INDEX = new ConcurrentHashMap<>(); private static final Map<String, Map<UUID, Integer>> NEXT_INDEX = new ConcurrentHashMap<>();
private UUID uuid; private UUID uuid;
@ -141,9 +144,9 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
e.printStackTrace(); e.printStackTrace();
return; return;
} }
EditSession session = toEditSession(actor, regions); try (EditSession session = toEditSession(actor, regions)) {
session.setBlocks(this, ChangeSetExecutor.Type.UNDO); session.setBlocks(this, ChangeSetExecutor.Type.UNDO);
deleteFiles(); }
} }
public void undo(Actor actor) { public void undo(Actor actor) {
@ -371,9 +374,14 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!bdFile.exists()) { if (!bdFile.exists()) {
return null; return null;
} }
try {
FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile)); FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile));
readHeader(is); readHeader(is);
return is; return is;
} catch (IOException e) {
LOGGER.error("Could not load block history file {}", bdFile);
throw e;
}
} }
@Override @Override
@ -381,7 +389,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!bioFile.exists()) { if (!bioFile.exists()) {
return null; return null;
} }
try {
return MainUtil.getCompressedIS(new FileInputStream(bioFile)); return MainUtil.getCompressedIS(new FileInputStream(bioFile));
} catch (IOException e) {
LOGGER.error("Could not load biome history file {}", bdFile);
throw e;
}
} }
@Override @Override
@ -389,7 +402,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!enttFile.exists()) { if (!enttFile.exists()) {
return null; return null;
} }
try {
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile)));
} catch (IOException e) {
LOGGER.error("Could not load entity create history file {}", bdFile);
throw e;
}
} }
@Override @Override
@ -397,7 +415,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!entfFile.exists()) { if (!entfFile.exists()) {
return null; return null;
} }
try {
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile)));
} catch (IOException e) {
LOGGER.error("Could not load entity remove history file {}", bdFile);
throw e;
}
} }
@Override @Override
@ -405,7 +428,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!nbttFile.exists()) { if (!nbttFile.exists()) {
return null; return null;
} }
try {
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile)));
} catch (IOException e) {
LOGGER.error("Could not load tile create history file {}", bdFile);
throw e;
}
} }
@Override @Override
@ -413,7 +441,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!nbtfFile.exists()) { if (!nbtfFile.exists()) {
return null; return null;
} }
try {
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile)));
} catch (IOException e) {
LOGGER.error("Could not load tile remove history file {}", bdFile);
throw e;
}
} }
@Override @Override

View File

@ -7,6 +7,7 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.IOException; import java.io.IOException;
@ -125,6 +126,26 @@ public class RollbackOptimizedHistory extends DiskStorageHistory {
} }
} }
@Override
public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) {
super.addBiomeChange(x, y, z, from, to);
if (x < minX) {
minX = x;
} else if (x > maxX) {
maxX = x;
}
if (y < minY) {
minY = y;
} else if (y > maxY) {
maxY = y;
}
if (z < minZ) {
minZ = z;
} else if (z > maxZ) {
maxZ = z;
}
}
@Override @Override
public void writeHeader(OutputStream os, int x, int y, int z) throws IOException { public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
minX = x; minX = x;

View File

@ -257,12 +257,14 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
} }
public EditSession toEditSession(Actor actor, Region[] regions) { public EditSession toEditSession(Actor actor, Region[] regions) {
EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(getWorld()).actor(actor). EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(world)
fastMode(false).checkMemory(false).changeSet(this).limitUnlimited(); .checkMemory(false)
if (regions != null) { .changeSetNull()
builder.allowedRegions(regions); .fastMode(false)
} else { .limitUnprocessed(actor)
builder.allowedRegionsEverywhere(); .actor(actor);
if (!actor.getLimit().RESTRICT_HISTORY_TO_REGIONS) {
builder = builder.allowedRegionsEverywhere();
} }
EditSession editSession = builder.build(); EditSession editSession = builder.build();
editSession.setSize(1); editSession.setSize(1);

View File

@ -167,7 +167,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
@Override @Override
public int readX(FaweInputStream in) throws IOException { public int readX(FaweInputStream in) throws IOException {
in.readFully(buffer); in.readFully(buffer);
return lx = lx + ((((buffer[1] & 0xFF) + ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20); return lx = lx + ((((buffer[1] & 0xFF) | ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20);
} }
@Override @Override
@ -177,7 +177,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
@Override @Override
public int readZ(FaweInputStream in) throws IOException { public int readZ(FaweInputStream in) throws IOException {
return lz = lz + ((((buffer[2] & 0xFF) + ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20); return lz = lz + ((((buffer[2] & 0xFF) | ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20);
} }
}; };
} else { } else {
@ -203,17 +203,17 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
@Override @Override
public int readX(FaweInputStream is) throws IOException { public int readX(FaweInputStream is) throws IOException {
is.readFully(buffer); is.readFully(buffer);
return lx = (lx + (buffer[0] & 0xFF) + (buffer[1] << 8)); return lx = lx + ((buffer[0] & 0xFF) | (buffer[1] << 8));
} }
@Override @Override
public int readY(FaweInputStream is) throws IOException { public int readY(FaweInputStream is) throws IOException {
return ly = (ly + (buffer[4] & 0xFF) + (buffer[5] << 8)); return ly = ly + ((buffer[4] & 0xFF) | (buffer[5]) << 8);
} }
@Override @Override
public int readZ(FaweInputStream is) throws IOException { public int readZ(FaweInputStream is) throws IOException {
return lz = (lz + (buffer[2] & 0xFF) + (buffer[3] << 8)); return lz = lz + ((buffer[2] & 0xFF) | (buffer[3]) << 8);
} }
}; };
} }
@ -353,7 +353,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
os.write((byte) (z)); os.write((byte) (z));
// only need to store biomes in the 4x4x4 chunks so only need one byte for y still (signed byte -128 -> 127) // only need to store biomes in the 4x4x4 chunks so only need one byte for y still (signed byte -128 -> 127)
// means -512 -> 508. Add 128 to avoid negative value casting. // means -512 -> 508. Add 128 to avoid negative value casting.
os.write((byte) (y + 128)); os.write((byte) (y + 32));
os.writeVarInt(from.getInternalId()); os.writeVarInt(from.getInternalId());
os.writeVarInt(to.getInternalId()); os.writeVarInt(to.getInternalId());
} catch (IOException e) { } catch (IOException e) {

View File

@ -2,7 +2,6 @@ package com.fastasyncworldedit.core.queue;
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import org.jetbrains.annotations.Range;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -18,8 +17,8 @@ public interface Filter {
* @param chunkZ the z coordinate in the chunk * @param chunkZ the z coordinate in the chunk
*/ */
default boolean appliesChunk( default boolean appliesChunk(
@Range(from = 0, to = 15) int chunkX, int chunkX,
@Range(from = 0, to = 15) int chunkZ int chunkZ
) { ) {
return true; return true;
} }

View File

@ -2,7 +2,6 @@ package com.fastasyncworldedit.core.queue;
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import org.jetbrains.annotations.Range;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -25,7 +24,6 @@ public interface IChunk extends Trimable, IChunkGet, IChunkSet {
* *
* @return the x coordinate of the chunk * @return the x coordinate of the chunk
*/ */
@Range(from = 0, to = 15)
int getX(); int getX();
/** /**
@ -33,7 +31,6 @@ public interface IChunk extends Trimable, IChunkGet, IChunkSet {
* *
* @return the z coordinate of the chunk * @return the z coordinate of the chunk
*/ */
@Range(from = 0, to = 15)
int getZ(); int getZ();
/** /**

View File

@ -1,14 +1,12 @@
package com.fastasyncworldedit.core.queue; package com.fastasyncworldedit.core.queue;
import org.jetbrains.annotations.Range;
/** /**
* IGetBlocks may be cached by the WorldChunkCache so that it can be used between multiple * IGetBlocks may be cached by the WorldChunkCache so that it can be used between multiple
* IQueueExtents - avoids conversion between a palette and raw data on every block get * IQueueExtents - avoids conversion between a palette and raw data on every block get
*/ */
public interface IChunkCache<T> extends Trimable { public interface IChunkCache<T> extends Trimable {
T get(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); T get(int chunkX, int chunkZ);
@Override @Override
default boolean trim(boolean aggressive) { default boolean trim(boolean aggressive) {

View File

@ -7,7 +7,6 @@ import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import org.jetbrains.annotations.Range;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.Flushable; import java.io.Flushable;
@ -51,12 +50,12 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
* Get the cached get object. This is faster than getting the object using NMS and allows for * Get the cached get object. This is faster than getting the object using NMS and allows for
* wrapping. * wrapping.
*/ */
IChunkGet getCachedGet(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); IChunkGet getCachedGet(int chunkX, int chunkZ);
/** /**
* Get the cached chunk set object. * Get the cached chunk set object.
*/ */
IChunkSet getCachedSet(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); IChunkSet getCachedSet(int chunkX, int chunkZ);
/** /**
* Submit the chunk so that it's changes are applied to the world * Submit the chunk so that it's changes are applied to the world

View File

@ -13,20 +13,22 @@ import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.StringMan; import com.fastasyncworldedit.core.util.StringMan;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.EditSessionBuilder;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.command.argument.Arguments; import com.sk89q.worldedit.command.argument.Arguments;
import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissions;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.annotation.AllowedRegion;
import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Confirm;
import com.sk89q.worldedit.command.util.annotation.Time; import com.sk89q.worldedit.command.util.annotation.Time;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Identifiable; import com.sk89q.worldedit.util.Identifiable;
@ -80,7 +82,6 @@ public class HistorySubCommands {
@Confirm @Confirm
public synchronized void rerun( public synchronized void rerun(
Player player, World world, RollbackDatabase database, Player player, World world, RollbackDatabase database,
@AllowedRegion Region[] allowedRegions,
@ArgFlag(name = 'u', desc = "String user", def = "me") @ArgFlag(name = 'u', desc = "String user", def = "me")
UUID other, UUID other,
@ArgFlag(name = 'r', def = "0", desc = "radius") @ArgFlag(name = 'r', def = "0", desc = "radius")
@ -90,7 +91,7 @@ public class HistorySubCommands {
@Time @Time
long timeDiff long timeDiff
) throws WorldEditException { ) throws WorldEditException {
rollback(player, world, database, allowedRegions, other, radius, timeDiff, true); rollback(player, world, database, other, radius, timeDiff, true);
} }
@Command( @Command(
@ -102,7 +103,6 @@ public class HistorySubCommands {
@Confirm @Confirm
public synchronized void rollback( public synchronized void rollback(
Player player, World world, RollbackDatabase database, Player player, World world, RollbackDatabase database,
@AllowedRegion Region[] allowedRegions,
@ArgFlag(name = 'u', desc = "String user", def = "") @ArgFlag(name = 'u', desc = "String user", def = "")
UUID other, UUID other,
@ArgFlag(name = 'r', def = "0", desc = "radius") @ArgFlag(name = 'r', def = "0", desc = "radius")
@ -149,9 +149,9 @@ public class HistorySubCommands {
count++; count++;
RollbackOptimizedHistory edit = supplier.get(); RollbackOptimizedHistory edit = supplier.get();
if (restore) { if (restore) {
edit.redo(player, allowedRegions); edit.redo(player);
} else { } else {
edit.undo(player, allowedRegions); edit.undo(player);
} }
String path = edit.getWorld().getName() + "/" + finalOther + "-" + edit.getIndex(); String path = edit.getWorld().getName() + "/" + finalOther + "-" + edit.getIndex();
player.print(Caption.of("fawe.worldedit.rollback.rollback.element", path)); player.print(Caption.of("fawe.worldedit.rollback.rollback.element", path));
@ -201,8 +201,7 @@ public class HistorySubCommands {
.at(summary.maxX, world.getMaxY(), summary.maxZ) .at(summary.maxX, world.getMaxY(), summary.maxZ)
); );
rollback.setTime(historyFile.lastModified()); rollback.setTime(historyFile.lastModified());
RollbackDatabase db = DBHandler.dbHandler() RollbackDatabase db = DBHandler.dbHandler().getDatabase(world);
.getDatabase(world);
db.logEdit(rollback); db.logEdit(rollback);
actor.print(TextComponent.of("Logging: " + historyFile)); actor.print(TextComponent.of("Logging: " + historyFile));
} }

View File

@ -316,9 +316,10 @@ public class SchematicCommands {
Actor actor, LocalSession session, Actor actor, LocalSession session,
@Arg(desc = "File name.") @Arg(desc = "File name.")
String filename, String filename,
@Arg(desc = "Format name.", def = "fast")
String formatName,
//FAWE start - random rotation //FAWE start - random rotation
@Arg(desc = "Format name.", def = "")
String formatName,
@Switch(name = 'r', desc = "Apply random rotation to the clipboard") @Switch(name = 'r', desc = "Apply random rotation to the clipboard")
boolean randomRotate boolean randomRotate
//FAWE end //FAWE end
@ -328,6 +329,11 @@ public class SchematicCommands {
//FAWE start //FAWE start
ClipboardFormat format; ClipboardFormat format;
InputStream in = null; InputStream in = null;
// if format is set explicitly, do not look up by extension!
boolean noExplicitFormat = formatName == null;
if (noExplicitFormat) {
formatName = "fast";
}
try { try {
URI uri; URI uri;
@ -357,7 +363,7 @@ public class SchematicCommands {
actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.other")); actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.other"));
return; return;
} }
if (filename.matches(".*\\.[\\w].*")) { if (noExplicitFormat && filename.matches(".*\\.[\\w].*")) {
format = ClipboardFormats format = ClipboardFormats
.findByExtension(filename.substring(filename.lastIndexOf('.') + 1)); .findByExtension(filename.substring(filename.lastIndexOf('.') + 1));
} else { } else {
@ -387,7 +393,6 @@ public class SchematicCommands {
in = new FileInputStream(file); in = new FileInputStream(file);
uri = file.toURI(); uri = file.toURI();
format.hold(actor, uri, in); format.hold(actor, uri, in);
if (randomRotate) { if (randomRotate) {
AffineTransform transform = new AffineTransform(); AffineTransform transform = new AffineTransform();

View File

@ -26,7 +26,6 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream; import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MainUtil;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps; import com.google.common.collect.Multimaps;
import com.google.common.io.ByteSource; import com.google.common.io.ByteSource;
@ -51,6 +50,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -64,7 +64,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
public class ClipboardFormats { public class ClipboardFormats {
private static final Map<String, ClipboardFormat> aliasMap = new HashMap<>(); private static final Map<String, ClipboardFormat> aliasMap = new HashMap<>();
private static final Multimap<String, ClipboardFormat> fileExtensionMap = HashMultimap.create(); // FAWE start - keep order of ClipboardFormat entries -> prefer FAST over SPONGE_SCHEMATIC
private static final Multimap<String, ClipboardFormat> fileExtensionMap = Multimaps.newMultimap(new HashMap<>(), LinkedHashSet::new);
// FAWE end
private static final List<ClipboardFormat> registeredFormats = new ArrayList<>(); private static final List<ClipboardFormat> registeredFormats = new ArrayList<>();
public static void registerClipboardFormat(ClipboardFormat format) { public static void registerClipboardFormat(ClipboardFormat format) {

View File

@ -86,6 +86,11 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde
priorityMap.put(BlockTypes.WHITE_BED, PlacementPriority.LAST); priorityMap.put(BlockTypes.WHITE_BED, PlacementPriority.LAST);
priorityMap.put(BlockTypes.YELLOW_BED, PlacementPriority.LAST); priorityMap.put(BlockTypes.YELLOW_BED, PlacementPriority.LAST);
priorityMap.put(BlockTypes.GRASS, PlacementPriority.LAST); priorityMap.put(BlockTypes.GRASS, PlacementPriority.LAST);
// Keep "grass" for <1.20.3 compat
@SuppressWarnings("deprecation")
BlockType grass = BlockTypes.GRASS;
priorityMap.put(grass, PlacementPriority.LAST);
priorityMap.put(BlockTypes.SHORT_GRASS, PlacementPriority.LAST);
priorityMap.put(BlockTypes.TALL_GRASS, PlacementPriority.LAST); priorityMap.put(BlockTypes.TALL_GRASS, PlacementPriority.LAST);
priorityMap.put(BlockTypes.ROSE_BUSH, PlacementPriority.LAST); priorityMap.put(BlockTypes.ROSE_BUSH, PlacementPriority.LAST);
priorityMap.put(BlockTypes.DANDELION, PlacementPriority.LAST); priorityMap.put(BlockTypes.DANDELION, PlacementPriority.LAST);

View File

@ -29,6 +29,7 @@ import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
/** /**
@ -103,7 +104,12 @@ public class FloraGenerator implements RegionFunction {
*/ */
public static Pattern getTemperatePattern() { public static Pattern getTemperatePattern() {
RandomPattern pattern = new RandomPattern(); RandomPattern pattern = new RandomPattern();
pattern.add(BlockTypes.GRASS.getDefaultState(), 300); BlockType grass = BlockTypes.SHORT_GRASS;
if (grass == null) {
// Fallback for <1.20.3 compat
grass = BlockTypes.GRASS;
}
pattern.add(grass.getDefaultState(), 300);
pattern.add(BlockTypes.POPPY.getDefaultState(), 5); pattern.add(BlockTypes.POPPY.getDefaultState(), 5);
pattern.add(BlockTypes.DANDELION.getDefaultState(), 5); pattern.add(BlockTypes.DANDELION.getDefaultState(), 5);
return pattern; return pattern;

View File

@ -41,7 +41,7 @@ public class BoundedHeightMask extends AbstractMask {
* @param maxY the maximum Y (must be equal to or greater than minY) * @param maxY the maximum Y (must be equal to or greater than minY)
*/ */
public BoundedHeightMask(int minY, int maxY) { public BoundedHeightMask(int minY, int maxY) {
checkArgument(minY <= maxY, "minY <= maxY required"); checkArgument(minY <= maxY, "minY <= maxY required. minY:" + minY + " and maxY:" + maxY + " were given.");
this.minY = minY; this.minY = minY;
this.maxY = maxY; this.maxY = maxY;
} }

View File

@ -48,6 +48,7 @@ public final class BlockCategories {
public static final BlockCategory BIG_DRIPLEAF_PLACEABLE = get("minecraft:big_dripleaf_placeable"); public static final BlockCategory BIG_DRIPLEAF_PLACEABLE = get("minecraft:big_dripleaf_placeable");
public static final BlockCategory BIRCH_LOGS = get("minecraft:birch_logs"); public static final BlockCategory BIRCH_LOGS = get("minecraft:birch_logs");
public static final BlockCategory BUTTONS = get("minecraft:buttons"); public static final BlockCategory BUTTONS = get("minecraft:buttons");
public static final BlockCategory CAMEL_SAND_STEP_SOUND_BLOCKS = get("minecraft:camel_sand_step_sound_blocks");
public static final BlockCategory CAMPFIRES = get("minecraft:campfires"); public static final BlockCategory CAMPFIRES = get("minecraft:campfires");
public static final BlockCategory CANDLE_CAKES = get("minecraft:candle_cakes"); public static final BlockCategory CANDLE_CAKES = get("minecraft:candle_cakes");
public static final BlockCategory CANDLES = get("minecraft:candles"); public static final BlockCategory CANDLES = get("minecraft:candles");
@ -60,6 +61,7 @@ public final class BlockCategories {
public static final BlockCategory COAL_ORES = get("minecraft:coal_ores"); public static final BlockCategory COAL_ORES = get("minecraft:coal_ores");
public static final BlockCategory COMBINATION_STEP_SOUND_BLOCKS = get("minecraft:combination_step_sound_blocks"); public static final BlockCategory COMBINATION_STEP_SOUND_BLOCKS = get("minecraft:combination_step_sound_blocks");
public static final BlockCategory COMPLETES_FIND_TREE_TUTORIAL = get("minecraft:completes_find_tree_tutorial"); public static final BlockCategory COMPLETES_FIND_TREE_TUTORIAL = get("minecraft:completes_find_tree_tutorial");
public static final BlockCategory CONCRETE_POWDER = get("minecraft:concrete_powder");
public static final BlockCategory CONVERTABLE_TO_MUD = get("minecraft:convertable_to_mud"); public static final BlockCategory CONVERTABLE_TO_MUD = get("minecraft:convertable_to_mud");
public static final BlockCategory COPPER_ORES = get("minecraft:copper_ores"); public static final BlockCategory COPPER_ORES = get("minecraft:copper_ores");
public static final BlockCategory CORAL_BLOCKS = get("minecraft:coral_blocks"); public static final BlockCategory CORAL_BLOCKS = get("minecraft:coral_blocks");

View File

@ -415,6 +415,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType CHISELED_BOOKSHELF = init(); public static final BlockType CHISELED_BOOKSHELF = init();
@Nullable @Nullable
public static final BlockType CHISELED_COPPER = init();
@Nullable
public static final BlockType CHISELED_DEEPSLATE = init(); public static final BlockType CHISELED_DEEPSLATE = init();
@Nullable @Nullable
public static final BlockType CHISELED_NETHER_BRICKS = init(); public static final BlockType CHISELED_NETHER_BRICKS = init();
@ -429,6 +431,10 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType CHISELED_STONE_BRICKS = init(); public static final BlockType CHISELED_STONE_BRICKS = init();
@Nullable @Nullable
public static final BlockType CHISELED_TUFF = init();
@Nullable
public static final BlockType CHISELED_TUFF_BRICKS = init();
@Nullable
public static final BlockType CHORUS_FLOWER = init(); public static final BlockType CHORUS_FLOWER = init();
@Nullable @Nullable
public static final BlockType CHORUS_PLANT = init(); public static final BlockType CHORUS_PLANT = init();
@ -471,6 +477,12 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType COPPER_BLOCK = init(); public static final BlockType COPPER_BLOCK = init();
@Nullable @Nullable
public static final BlockType COPPER_BULB = init();
@Nullable
public static final BlockType COPPER_DOOR = init();
@Nullable
public static final BlockType COPPER_GRATE = init();
@Nullable
public static final BlockType CORNFLOWER = init(); public static final BlockType CORNFLOWER = init();
@Nullable @Nullable
public static final BlockType CRACKED_DEEPSLATE_BRICKS = init(); public static final BlockType CRACKED_DEEPSLATE_BRICKS = init();
@ -673,6 +685,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType DEEPSLATE_COPPER_ORE = init(); public static final BlockType DEEPSLATE_COPPER_ORE = init();
@Nullable @Nullable
public static final BlockType COPPER_TRAPDOOR = init();
@Nullable
public static final BlockType DEEPSLATE_DIAMOND_ORE = init(); public static final BlockType DEEPSLATE_DIAMOND_ORE = init();
@Nullable @Nullable
public static final BlockType DEEPSLATE_EMERALD_ORE = init(); public static final BlockType DEEPSLATE_EMERALD_ORE = init();
@ -733,6 +747,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType ENDER_CHEST = init(); public static final BlockType ENDER_CHEST = init();
@Nullable @Nullable
public static final BlockType EXPOSED_CHISELED_COPPER = init();
@Nullable
public static final BlockType END_GATEWAY = init(); public static final BlockType END_GATEWAY = init();
@Nullable @Nullable
public static final BlockType END_PORTAL = init(); public static final BlockType END_PORTAL = init();
@ -753,6 +769,14 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType EXPOSED_COPPER = init(); public static final BlockType EXPOSED_COPPER = init();
@Nullable @Nullable
public static final BlockType EXPOSED_COPPER_BULB = init();
@Nullable
public static final BlockType EXPOSED_COPPER_DOOR = init();
@Nullable
public static final BlockType EXPOSED_COPPER_GRATE = init();
@Nullable
public static final BlockType EXPOSED_COPPER_TRAPDOOR = init();
@Nullable
public static final BlockType EXPOSED_CUT_COPPER = init(); public static final BlockType EXPOSED_CUT_COPPER = init();
@Nullable @Nullable
public static final BlockType EXPOSED_CUT_COPPER_SLAB = init(); public static final BlockType EXPOSED_CUT_COPPER_SLAB = init();
@ -808,7 +832,7 @@ public final class BlockTypes {
public static final BlockType GRANITE_STAIRS = init(); public static final BlockType GRANITE_STAIRS = init();
@Nullable @Nullable
public static final BlockType GRANITE_WALL = init(); public static final BlockType GRANITE_WALL = init();
@Nullable @Nullable @Deprecated
public static final BlockType GRASS = init(); public static final BlockType GRASS = init();
@Nullable @Nullable
public static final BlockType GRASS_BLOCK = init(); public static final BlockType GRASS_BLOCK = init();
@ -901,6 +925,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType INFESTED_CRACKED_STONE_BRICKS = init(); public static final BlockType INFESTED_CRACKED_STONE_BRICKS = init();
@Nullable @Nullable
public static final BlockType CRAFTER = init();
@Nullable
public static final BlockType INFESTED_DEEPSLATE = init(); public static final BlockType INFESTED_DEEPSLATE = init();
@Nullable @Nullable
public static final BlockType INFESTED_MOSSY_STONE_BRICKS = init(); public static final BlockType INFESTED_MOSSY_STONE_BRICKS = init();
@ -1293,8 +1319,18 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType OXEYE_DAISY = init(); public static final BlockType OXEYE_DAISY = init();
@Nullable @Nullable
public static final BlockType OXIDIZED_CHISELED_COPPER = init();
@Nullable
public static final BlockType OXIDIZED_COPPER = init(); public static final BlockType OXIDIZED_COPPER = init();
@Nullable @Nullable
public static final BlockType OXIDIZED_COPPER_BULB = init();
@Nullable
public static final BlockType OXIDIZED_COPPER_DOOR = init();
@Nullable
public static final BlockType OXIDIZED_COPPER_GRATE = init();
@Nullable
public static final BlockType OXIDIZED_COPPER_TRAPDOOR = init();
@Nullable
public static final BlockType OXIDIZED_CUT_COPPER = init(); public static final BlockType OXIDIZED_CUT_COPPER = init();
@Nullable @Nullable
public static final BlockType OXIDIZED_CUT_COPPER_SLAB = init(); public static final BlockType OXIDIZED_CUT_COPPER_SLAB = init();
@ -1411,6 +1447,14 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType POLISHED_GRANITE_STAIRS = init(); public static final BlockType POLISHED_GRANITE_STAIRS = init();
@Nullable @Nullable
public static final BlockType POLISHED_TUFF = init();
@Nullable
public static final BlockType POLISHED_TUFF_SLAB = init();
@Nullable
public static final BlockType POLISHED_TUFF_STAIRS = init();
@Nullable
public static final BlockType POLISHED_TUFF_WALL = init();
@Nullable
public static final BlockType POPPY = init(); public static final BlockType POPPY = init();
@Nullable @Nullable
public static final BlockType POTATOES = init(); public static final BlockType POTATOES = init();
@ -1667,6 +1711,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType SEAGRASS = init(); public static final BlockType SEAGRASS = init();
@Nullable @Nullable
public static final BlockType SHORT_GRASS = init();
@Nullable
public static final BlockType SEA_LANTERN = init(); public static final BlockType SEA_LANTERN = init();
@Nullable @Nullable
public static final BlockType SEA_PICKLE = init(); public static final BlockType SEA_PICKLE = init();
@ -1874,6 +1920,8 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType TRAPPED_CHEST = init(); public static final BlockType TRAPPED_CHEST = init();
@Nullable @Nullable
public static final BlockType TRIAL_SPAWNER = init();
@Nullable
public static final BlockType TRIPWIRE = init(); public static final BlockType TRIPWIRE = init();
@Nullable @Nullable
public static final BlockType TRIPWIRE_HOOK = init(); public static final BlockType TRIPWIRE_HOOK = init();
@ -1888,6 +1936,20 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType TUFF = init(); public static final BlockType TUFF = init();
@Nullable @Nullable
public static final BlockType TUFF_BRICK_SLAB = init();
@Nullable
public static final BlockType TUFF_BRICK_STAIRS = init();
@Nullable
public static final BlockType TUFF_BRICK_WALL = init();
@Nullable
public static final BlockType TUFF_BRICKS = init();
@Nullable
public static final BlockType TUFF_SLAB = init();
@Nullable
public static final BlockType TUFF_STAIRS = init();
@Nullable
public static final BlockType TUFF_WALL = init();
@Nullable
public static final BlockType TURTLE_EGG = init(); public static final BlockType TURTLE_EGG = init();
@Nullable @Nullable
public static final BlockType TWISTING_VINES = init(); public static final BlockType TWISTING_VINES = init();
@ -1947,16 +2009,36 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType WATER_CAULDRON = init(); public static final BlockType WATER_CAULDRON = init();
@Nullable @Nullable
public static final BlockType WAXED_CHISELED_COPPER = init();
@Nullable
public static final BlockType WAXED_COPPER_BLOCK = init(); public static final BlockType WAXED_COPPER_BLOCK = init();
@Nullable @Nullable
public static final BlockType WAXED_COPPER_BULB = init();
@Nullable
public static final BlockType WAXED_COPPER_DOOR = init();
@Nullable
public static final BlockType WAXED_COPPER_GRATE = init();
@Nullable
public static final BlockType WAXED_COPPER_TRAPDOOR = init();
@Nullable
public static final BlockType WAXED_CUT_COPPER = init(); public static final BlockType WAXED_CUT_COPPER = init();
@Nullable @Nullable
public static final BlockType WAXED_CUT_COPPER_SLAB = init(); public static final BlockType WAXED_CUT_COPPER_SLAB = init();
@Nullable @Nullable
public static final BlockType WAXED_CUT_COPPER_STAIRS = init(); public static final BlockType WAXED_CUT_COPPER_STAIRS = init();
@ Nullable @ Nullable
public static final BlockType WAXED_EXPOSED_CHISELED_COPPER = init();
@Nullable
public static final BlockType WAXED_EXPOSED_COPPER = init(); public static final BlockType WAXED_EXPOSED_COPPER = init();
@Nullable @Nullable
public static final BlockType WAXED_EXPOSED_COPPER_BULB = init();
@Nullable
public static final BlockType WAXED_EXPOSED_COPPER_DOOR = init();
@Nullable
public static final BlockType WAXED_EXPOSED_COPPER_GRATE = init();
@Nullable
public static final BlockType WAXED_EXPOSED_COPPER_TRAPDOOR = init();
@Nullable
public static final BlockType WAXED_EXPOSED_CUT_COPPER = init(); public static final BlockType WAXED_EXPOSED_CUT_COPPER = init();
@Nullable @Nullable
public static final BlockType WAXED_EXPOSED_CUT_COPPER_SLAB = init(); public static final BlockType WAXED_EXPOSED_CUT_COPPER_SLAB = init();
@ -1965,22 +2047,52 @@ public final class BlockTypes {
@Nullable @Nullable
public static final BlockType WAXED_OXIDIZED_COPPER = init(); public static final BlockType WAXED_OXIDIZED_COPPER = init();
@Nullable @Nullable
public static final BlockType WAXED_OXIDIZED_COPPER_BULB = init();
@Nullable
public static final BlockType WAXED_OXIDIZED_COPPER_DOOR = init();
@Nullable
public static final BlockType WAXED_OXIDIZED_COPPER_GRATE = init();
@Nullable
public static final BlockType WAXED_OXIDIZED_COPPER_TRAPDOOR = init();
@Nullable
public static final BlockType WAXED_OXIDIZED_CHISELED_COPPER = init();
@Nullable
public static final BlockType WAXED_OXIDIZED_CUT_COPPER = init(); public static final BlockType WAXED_OXIDIZED_CUT_COPPER = init();
@Nullable @Nullable
public static final BlockType WAXED_OXIDIZED_CUT_COPPER_SLAB = init(); public static final BlockType WAXED_OXIDIZED_CUT_COPPER_SLAB = init();
@Nullable @Nullable
public static final BlockType WAXED_OXIDIZED_CUT_COPPER_STAIRS = init(); public static final BlockType WAXED_OXIDIZED_CUT_COPPER_STAIRS = init();
@Nullable @Nullable
public static final BlockType WAXED_WEATHERED_CHISELED_COPPER = init();
@Nullable
public static final BlockType WAXED_WEATHERED_COPPER = init(); public static final BlockType WAXED_WEATHERED_COPPER = init();
@Nullable @Nullable
public static final BlockType WAXED_WEATHERED_COPPER_BULB = init();
@Nullable
public static final BlockType WAXED_WEATHERED_COPPER_DOOR = init();
@Nullable
public static final BlockType WAXED_WEATHERED_COPPER_GRATE = init();
@Nullable
public static final BlockType WAXED_WEATHERED_COPPER_TRAPDOOR = init();
@Nullable
public static final BlockType WAXED_WEATHERED_CUT_COPPER = init(); public static final BlockType WAXED_WEATHERED_CUT_COPPER = init();
@Nullable @Nullable
public static final BlockType WAXED_WEATHERED_CUT_COPPER_SLAB = init(); public static final BlockType WAXED_WEATHERED_CUT_COPPER_SLAB = init();
@Nullable @Nullable
public static final BlockType WAXED_WEATHERED_CUT_COPPER_STAIRS = init(); public static final BlockType WAXED_WEATHERED_CUT_COPPER_STAIRS = init();
@Nullable @Nullable
public static final BlockType WEATHERED_CHISELED_COPPER = init();
@Nullable
public static final BlockType WEATHERED_COPPER = init(); public static final BlockType WEATHERED_COPPER = init();
@Nullable @Nullable
public static final BlockType WEATHERED_COPPER_BULB = init();
@Nullable
public static final BlockType WEATHERED_COPPER_DOOR = init();
@Nullable
public static final BlockType WEATHERED_COPPER_GRATE = init();
@Nullable
public static final BlockType WEATHERED_COPPER_TRAPDOOR = init();
@Nullable
public static final BlockType WEATHERED_CUT_COPPER = init(); public static final BlockType WEATHERED_CUT_COPPER = init();
@Nullable @Nullable
public static final BlockType WEATHERED_CUT_COPPER_SLAB = init(); public static final BlockType WEATHERED_CUT_COPPER_SLAB = init();

View File

@ -51,6 +51,8 @@ public final class EntityTypes {
@Nullable @Nullable
public static final EntityType BOAT = get("minecraft:boat"); public static final EntityType BOAT = get("minecraft:boat");
@Nullable @Nullable
public static final EntityType BREEZE = get("minecraft:breeze");
@Nullable
public static final EntityType CAMEL = get("minecraft:camel"); public static final EntityType CAMEL = get("minecraft:camel");
@Nullable @Nullable
public static final EntityType CAT = get("minecraft:cat"); public static final EntityType CAT = get("minecraft:cat");
@ -259,6 +261,8 @@ public final class EntityTypes {
@Nullable @Nullable
public static final EntityType WARDEN = get("minecraft:warden"); public static final EntityType WARDEN = get("minecraft:warden");
@Nullable @Nullable
public static final EntityType WIND_CHARGE = get("minecraft:wind_charge");
@Nullable
public static final EntityType WITCH = get("minecraft:witch"); public static final EntityType WITCH = get("minecraft:witch");
@Nullable @Nullable
public static final EntityType WITHER = get("minecraft:wither"); public static final EntityType WITHER = get("minecraft:wither");

View File

@ -319,6 +319,8 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType BREAD = init(); public static final ItemType BREAD = init();
@Nullable @Nullable
public static final ItemType BREEZE_SPAWN_EGG = init();
@Nullable
public static final ItemType BREWER_POTTERY_SHERD = init(); public static final ItemType BREWER_POTTERY_SHERD = init();
@Nullable @Nullable
public static final ItemType BREWING_STAND = init(); public static final ItemType BREWING_STAND = init();
@ -468,6 +470,8 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType CHISELED_BOOKSHELF = init(); public static final ItemType CHISELED_BOOKSHELF = init();
@Nullable @Nullable
public static final ItemType CHISELED_COPPER = init();
@Nullable
public static final ItemType CHISELED_DEEPSLATE = init(); public static final ItemType CHISELED_DEEPSLATE = init();
@Nullable @Nullable
public static final ItemType CHISELED_NETHER_BRICKS = init(); public static final ItemType CHISELED_NETHER_BRICKS = init();
@ -482,6 +486,10 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType CHISELED_STONE_BRICKS = init(); public static final ItemType CHISELED_STONE_BRICKS = init();
@Nullable @Nullable
public static final ItemType CHISELED_TUFF = init();
@Nullable
public static final ItemType CHISELED_TUFF_BRICKS = init();
@Nullable
public static final ItemType CHORUS_FLOWER = init(); public static final ItemType CHORUS_FLOWER = init();
@Nullable @Nullable
public static final ItemType CHORUS_FRUIT = init(); public static final ItemType CHORUS_FRUIT = init();
@ -560,10 +568,18 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType COPPER_BLOCK = init(); public static final ItemType COPPER_BLOCK = init();
@Nullable @Nullable
public static final ItemType COPPER_BULB = init();
@Nullable
public static final ItemType COPPER_DOOR = init();
@Nullable
public static final ItemType COPPER_GRATE = init();
@Nullable
public static final ItemType COPPER_INGOT = init(); public static final ItemType COPPER_INGOT = init();
@Nullable @Nullable
public static final ItemType COPPER_ORE = init(); public static final ItemType COPPER_ORE = init();
@Nullable @Nullable
public static final ItemType COPPER_TRAPDOOR = init();
@Nullable
public static final ItemType CORNFLOWER = init(); public static final ItemType CORNFLOWER = init();
@Nullable @Nullable
public static final ItemType COW_SPAWN_EGG = init(); public static final ItemType COW_SPAWN_EGG = init();
@ -578,6 +594,8 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType CRACKED_STONE_BRICKS = init(); public static final ItemType CRACKED_STONE_BRICKS = init();
@Nullable @Nullable
public static final ItemType CRAFTER = init();
@Nullable
public static final ItemType CRAFTING_TABLE = init(); public static final ItemType CRAFTING_TABLE = init();
@Nullable @Nullable
public static final ItemType CREEPER_BANNER_PATTERN = init(); public static final ItemType CREEPER_BANNER_PATTERN = init();
@ -903,8 +921,18 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType EXPLORER_POTTERY_SHERD = init(); public static final ItemType EXPLORER_POTTERY_SHERD = init();
@Nullable @Nullable
public static final ItemType EXPOSED_CHISELED_COPPER = init();
@Nullable
public static final ItemType EXPOSED_COPPER = init(); public static final ItemType EXPOSED_COPPER = init();
@Nullable @Nullable
public static final ItemType EXPOSED_COPPER_BULB = init();
@Nullable
public static final ItemType EXPOSED_COPPER_DOOR = init();
@Nullable
public static final ItemType EXPOSED_COPPER_GRATE = init();
@Nullable
public static final ItemType EXPOSED_COPPER_TRAPDOOR = init();
@Nullable
public static final ItemType EXPOSED_CUT_COPPER = init(); public static final ItemType EXPOSED_CUT_COPPER = init();
@Nullable @Nullable
public static final ItemType EXPOSED_CUT_COPPER_SLAB = init(); public static final ItemType EXPOSED_CUT_COPPER_SLAB = init();
@ -1036,7 +1064,7 @@ public final class ItemTypes {
public static final ItemType GRANITE_STAIRS = init(); public static final ItemType GRANITE_STAIRS = init();
@Nullable @Nullable
public static final ItemType GRANITE_WALL = init(); public static final ItemType GRANITE_WALL = init();
@Nullable @Nullable @Deprecated
public static final ItemType GRASS = init(); public static final ItemType GRASS = init();
@Nullable @Nullable
public static final ItemType GRASS_BLOCK = init(); public static final ItemType GRASS_BLOCK = init();
@ -1670,8 +1698,18 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType OXEYE_DAISY = init(); public static final ItemType OXEYE_DAISY = init();
@Nullable @Nullable
public static final ItemType OXIDIZED_CHISELED_COPPER = init();
@Nullable
public static final ItemType OXIDIZED_COPPER = init(); public static final ItemType OXIDIZED_COPPER = init();
@Nullable @Nullable
public static final ItemType OXIDIZED_COPPER_BULB = init();
@Nullable
public static final ItemType OXIDIZED_COPPER_DOOR = init();
@Nullable
public static final ItemType OXIDIZED_COPPER_GRATE = init();
@Nullable
public static final ItemType OXIDIZED_COPPER_TRAPDOOR = init();
@Nullable
public static final ItemType OXIDIZED_CUT_COPPER = init(); public static final ItemType OXIDIZED_CUT_COPPER = init();
@Nullable @Nullable
public static final ItemType OXIDIZED_CUT_COPPER_SLAB = init(); public static final ItemType OXIDIZED_CUT_COPPER_SLAB = init();
@ -1808,6 +1846,14 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType POLISHED_GRANITE_STAIRS = init(); public static final ItemType POLISHED_GRANITE_STAIRS = init();
@Nullable @Nullable
public static final ItemType POLISHED_TUFF = init();
@Nullable
public static final ItemType POLISHED_TUFF_SLAB = init();
@Nullable
public static final ItemType POLISHED_TUFF_STAIRS = init();
@Nullable
public static final ItemType POLISHED_TUFF_WALL = init();
@Nullable
public static final ItemType POPPED_CHORUS_FRUIT = init(); public static final ItemType POPPED_CHORUS_FRUIT = init();
@Nullable @Nullable
public static final ItemType POPPY = init(); public static final ItemType POPPY = init();
@ -2061,6 +2107,8 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType SHIELD = init(); public static final ItemType SHIELD = init();
@Nullable @Nullable
public static final ItemType SHORT_GRASS = init();
@Nullable
public static final ItemType SHROOMLIGHT = init(); public static final ItemType SHROOMLIGHT = init();
@Nullable @Nullable
public static final ItemType SHULKER_BOX = init(); public static final ItemType SHULKER_BOX = init();
@ -2336,6 +2384,10 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType TRAPPED_CHEST = init(); public static final ItemType TRAPPED_CHEST = init();
@Nullable @Nullable
public static final ItemType TRIAL_KEY = init();
@Nullable
public static final ItemType TRIAL_SPAWNER = init();
@Nullable
public static final ItemType TRIDENT = init(); public static final ItemType TRIDENT = init();
@Nullable @Nullable
public static final ItemType TRIPWIRE_HOOK = init(); public static final ItemType TRIPWIRE_HOOK = init();
@ -2354,6 +2406,20 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType TUFF = init(); public static final ItemType TUFF = init();
@Nullable @Nullable
public static final ItemType TUFF_BRICK_SLAB = init();
@Nullable
public static final ItemType TUFF_BRICK_STAIRS = init();
@Nullable
public static final ItemType TUFF_BRICK_WALL = init();
@Nullable
public static final ItemType TUFF_BRICKS = init();
@Nullable
public static final ItemType TUFF_SLAB = init();
@Nullable
public static final ItemType TUFF_STAIRS = init();
@Nullable
public static final ItemType TUFF_WALL = init();
@Nullable
public static final ItemType TURTLE_EGG = init(); public static final ItemType TURTLE_EGG = init();
@Nullable @Nullable
public static final ItemType TURTLE_HELMET = init(); public static final ItemType TURTLE_HELMET = init();
@ -2418,32 +2484,72 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType WATER_BUCKET = init(); public static final ItemType WATER_BUCKET = init();
@Nullable @Nullable
public static final ItemType WAXED_CHISELED_COPPER = init();
@Nullable
public static final ItemType WAXED_COPPER_BLOCK = init(); public static final ItemType WAXED_COPPER_BLOCK = init();
@Nullable @Nullable
public static final ItemType WAXED_COPPER_BULB = init();
@Nullable
public static final ItemType WAXED_COPPER_DOOR = init();
@Nullable
public static final ItemType WAXED_COPPER_GRATE = init();
@Nullable
public static final ItemType WAXED_COPPER_TRAPDOOR = init();
@Nullable
public static final ItemType WAXED_CUT_COPPER = init(); public static final ItemType WAXED_CUT_COPPER = init();
@Nullable @Nullable
public static final ItemType WAXED_CUT_COPPER_SLAB = init(); public static final ItemType WAXED_CUT_COPPER_SLAB = init();
@Nullable @Nullable
public static final ItemType WAXED_CUT_COPPER_STAIRS = init(); public static final ItemType WAXED_CUT_COPPER_STAIRS = init();
@Nullable @Nullable
public static final ItemType WAXED_EXPOSED_CHISELED_COPPER = init();
@Nullable
public static final ItemType WAXED_EXPOSED_COPPER = init(); public static final ItemType WAXED_EXPOSED_COPPER = init();
@Nullable @Nullable
public static final ItemType WAXED_EXPOSED_COPPER_BULB = init();
@Nullable
public static final ItemType WAXED_EXPOSED_COPPER_DOOR = init();
@Nullable
public static final ItemType WAXED_EXPOSED_COPPER_GRATE = init();
@Nullable
public static final ItemType WAXED_EXPOSED_COPPER_TRAPDOOR = init();
@Nullable
public static final ItemType WAXED_EXPOSED_CUT_COPPER = init(); public static final ItemType WAXED_EXPOSED_CUT_COPPER = init();
@Nullable @Nullable
public static final ItemType WAXED_EXPOSED_CUT_COPPER_SLAB = init(); public static final ItemType WAXED_EXPOSED_CUT_COPPER_SLAB = init();
@Nullable @Nullable
public static final ItemType WAXED_EXPOSED_CUT_COPPER_STAIRS = init(); public static final ItemType WAXED_EXPOSED_CUT_COPPER_STAIRS = init();
@Nullable @Nullable
public static final ItemType WAXED_OXIDIZED_CHISELED_COPPER = init();
@Nullable
public static final ItemType WAXED_OXIDIZED_COPPER = init(); public static final ItemType WAXED_OXIDIZED_COPPER = init();
@Nullable @Nullable
public static final ItemType WAXED_OXIDIZED_COPPER_BULB = init();
@Nullable
public static final ItemType WAXED_OXIDIZED_COPPER_DOOR = init();
@Nullable
public static final ItemType WAXED_OXIDIZED_COPPER_GRATE = init();
@Nullable
public static final ItemType WAXED_OXIDIZED_COPPER_TRAPDOOR = init();
@Nullable
public static final ItemType WAXED_OXIDIZED_CUT_COPPER = init(); public static final ItemType WAXED_OXIDIZED_CUT_COPPER = init();
@Nullable @Nullable
public static final ItemType WAXED_OXIDIZED_CUT_COPPER_SLAB = init(); public static final ItemType WAXED_OXIDIZED_CUT_COPPER_SLAB = init();
@Nullable @Nullable
public static final ItemType WAXED_OXIDIZED_CUT_COPPER_STAIRS = init(); public static final ItemType WAXED_OXIDIZED_CUT_COPPER_STAIRS = init();
@Nullable @Nullable
public static final ItemType WAXED_WEATHERED_CHISELED_COPPER = init();
@Nullable
public static final ItemType WAXED_WEATHERED_COPPER = init(); public static final ItemType WAXED_WEATHERED_COPPER = init();
@Nullable @Nullable
public static final ItemType WAXED_WEATHERED_COPPER_BULB = init();
@Nullable
public static final ItemType WAXED_WEATHERED_COPPER_DOOR = init();
@Nullable
public static final ItemType WAXED_WEATHERED_COPPER_GRATE = init();
@Nullable
public static final ItemType WAXED_WEATHERED_COPPER_TRAPDOOR = init();
@Nullable
public static final ItemType WAXED_WEATHERED_CUT_COPPER = init(); public static final ItemType WAXED_WEATHERED_CUT_COPPER = init();
@Nullable @Nullable
public static final ItemType WAXED_WEATHERED_CUT_COPPER_SLAB = init(); public static final ItemType WAXED_WEATHERED_CUT_COPPER_SLAB = init();
@ -2452,8 +2558,18 @@ public final class ItemTypes {
@Nullable @Nullable
public static final ItemType WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE = init(); public static final ItemType WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE = init();
@Nullable @Nullable
public static final ItemType WEATHERED_CHISELED_COPPER = init();
@Nullable
public static final ItemType WEATHERED_COPPER = init(); public static final ItemType WEATHERED_COPPER = init();
@Nullable @Nullable
public static final ItemType WEATHERED_COPPER_BULB = init();
@Nullable
public static final ItemType WEATHERED_COPPER_DOOR = init();
@Nullable
public static final ItemType WEATHERED_COPPER_GRATE = init();
@Nullable
public static final ItemType WEATHERED_COPPER_TRAPDOOR = init();
@Nullable
public static final ItemType WEATHERED_CUT_COPPER = init(); public static final ItemType WEATHERED_CUT_COPPER = init();
@Nullable @Nullable
public static final ItemType WEATHERED_CUT_COPPER_SLAB = init(); public static final ItemType WEATHERED_CUT_COPPER_SLAB = init();

View File

@ -54,7 +54,8 @@
"fawe.worldedit.brush.brush.source.mask": "Brush source mask set", "fawe.worldedit.brush.brush.source.mask": "Brush source mask set",
"fawe.worldedit.brush.brush.transform.disabled": "Brush transform disabled", "fawe.worldedit.brush.brush.transform.disabled": "Brush transform disabled",
"fawe.worldedit.brush.brush.transform": "Brush transform set", "fawe.worldedit.brush.brush.transform": "Brush transform set",
"fawe.worldedit.rollback.rollback.element": "Undoing {0}", "fawe.worldedit.rollback.rollingback.index": "Undoing {0} ...",
"fawe.worldedit.rollback.rollback.element": "{0} undone.",
"fawe.worldedit.tool.tool.inspect": "Inspect tool bound to {0}.", "fawe.worldedit.tool.tool.inspect": "Inspect tool bound to {0}.",
"fawe.worldedit.tool.tool.inspect.info": "{0} changed {1} to {2} {3} ago", "fawe.worldedit.tool.tool.inspect.info": "{0} changed {1} to {2} {3} ago",
"fawe.worldedit.tool.tool.inspect.info.footer": "Total: {0} changes", "fawe.worldedit.tool.tool.inspect.info.footer": "Total: {0} changes",

View File

@ -28,7 +28,7 @@ dependencies {
}) })
api("org.apache.logging.log4j:log4j-api") api("org.apache.logging.log4j:log4j-api")
api("org.bstats:bstats-sponge:1.7") api("org.bstats:bstats-sponge:1.7")
testImplementation("org.mockito:mockito-core:5.6.0") testImplementation("org.mockito:mockito-core:5.8.0")
} }
<<<<<<< HEAD <<<<<<< HEAD