Telesphoreo 2024-10-15 18:22:59 -05:00
commit 778517f1ea
No known key found for this signature in database
GPG Key ID: 9D1991811E093C02
205 changed files with 3688 additions and 3318 deletions

View File

@ -7,7 +7,7 @@ import xyz.jpenilla.runpaper.task.RunServer
plugins { plugins {
id("io.github.gradle-nexus.publish-plugin") version "2.0.0" id("io.github.gradle-nexus.publish-plugin") version "2.0.0"
id("xyz.jpenilla.run-paper") version "2.3.0" id("xyz.jpenilla.run-paper") version "2.3.1"
} }
if (!File("$rootDir/.git").exists()) { if (!File("$rootDir/.git").exists()) {
@ -34,7 +34,7 @@ logger.lifecycle("""
******************************************* *******************************************
""") """)
var rootVersion by extra("2.11.2") var rootVersion by extra("2.11.3")
var snapshot by extra("SNAPSHOT") var snapshot by extra("SNAPSHOT")
var revision: String by extra("") var revision: String by extra("")
var buildNumber by extra("") var buildNumber by extra("")
@ -91,13 +91,13 @@ tasks {
minecraftVersion(it) minecraftVersion(it)
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())
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true") jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true", "--add-modules=jdk.incubator.vector")
group = "run paper" group = "run paper"
runDirectory.set(file("run-$it")) runDirectory.set(file("run-$it"))
} }
} }
runServer { runServer {
minecraftVersion("1.20.4") minecraftVersion("1.21.1")
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

@ -26,9 +26,9 @@ val properties = Properties().also { props ->
dependencies { dependencies {
implementation(gradleApi()) implementation(gradleApi())
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.2") implementation("org.ajoberstar.grgit:grgit-gradle:5.3.0")
implementation("com.github.johnrengelman:shadow:8.1.1") implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.3")
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.7.2") implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.7.3")
constraints { constraints {
val asmVersion = "[9.7,)" val asmVersion = "[9.7,)"
implementation("org.ow2.asm:asm:$asmVersion") { implementation("org.ow2.asm:asm:$asmVersion") {

View File

@ -28,6 +28,7 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
options.isDeprecation = true options.isDeprecation = true
options.encoding = "UTF-8" options.encoding = "UTF-8"
options.compilerArgs.add("-parameters") options.compilerArgs.add("-parameters")
options.compilerArgs.add("--add-modules=jdk.incubator.vector")
} }
configurations.all { configurations.all {
@ -40,28 +41,30 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
dependencies { dependencies {
"compileOnly"("com.google.code.findbugs:jsr305:3.0.2") "compileOnly"("com.google.code.findbugs:jsr305:3.0.2")
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.10.0") "testImplementation"("org.junit.jupiter:junit-jupiter-api:5.11.1")
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.10.0") "testImplementation"("org.junit.jupiter:junit-jupiter-params:5.11.1")
"testImplementation"("org.mockito:mockito-core:5.4.0") "testImplementation"("org.mockito:mockito-core:5.14.0")
"testImplementation"("org.mockito:mockito-junit-jupiter:5.4.0") "testImplementation"("org.mockito:mockito-junit-jupiter:5.14.0")
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.10.0") "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.11.1")
} }
// Java 8 turns on doclint which we fail // Java 8 turns on doclint which we fail
tasks.withType<Javadoc>().configureEach { tasks.withType<Javadoc>().configureEach {
(options as StandardJavadocDocletOptions).apply { (options as StandardJavadocDocletOptions).apply {
addStringOption("Xdoclint:none", "-quiet") addStringOption("Xdoclint:none", "-quiet")
addStringOption("-add-modules", "jdk.incubator.vector")
tags( tags(
"apiNote:a:API Note:", "apiNote:a:API Note:",
"implSpec:a:Implementation Requirements:", "implSpec:a:Implementation Requirements:",
"implNote:a:Implementation Note:" "implNote:a:Implementation Note:"
) )
options.encoding = "UTF-8" options.encoding = "UTF-8"
links( links(
"https://jd.advntr.dev/api/latest/", "https://jd.advntr.dev/api/latest/",
"https://logging.apache.org/log4j/2.x/javadoc/log4j-api/", "https://logging.apache.org/log4j/2.x/javadoc/log4j-api/",
"https://www.antlr.org/api/Java/", "https://www.antlr.org/api/Java/",
"https://jd.papermc.io/paper/1.21/", "https://jd.papermc.io/paper/1.21.1/",
"https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/" "https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/"
) )
docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}" docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}"

View File

@ -29,7 +29,7 @@ fun Project.applyLibrariesConfiguration() {
applyCommonConfiguration() applyCommonConfiguration()
apply(plugin = "java-base") apply(plugin = "java-base")
apply(plugin = "maven-publish") apply(plugin = "maven-publish")
apply(plugin = "com.github.johnrengelman.shadow") apply(plugin = "com.gradleup.shadow")
apply(plugin = "signing") apply(plugin = "signing")
configurations { configurations {

View File

@ -20,7 +20,7 @@ fun Project.applyPlatformAndCoreConfiguration() {
apply(plugin = "eclipse") apply(plugin = "eclipse")
apply(plugin = "idea") apply(plugin = "idea")
apply(plugin = "maven-publish") apply(plugin = "maven-publish")
apply(plugin = "com.github.johnrengelman.shadow") apply(plugin = "com.gradleup.shadow")
apply(plugin = "signing") apply(plugin = "signing")
applyCommonJavaConfiguration( applyCommonJavaConfiguration(

View File

@ -9,21 +9,21 @@ snakeyaml = "2.0"
# Plugins # Plugins
dummypermscompat = "1.10" dummypermscompat = "1.10"
worldguard-bukkit = "7.0.10" worldguard-bukkit = "7.0.12"
mapmanager = "1.8.0-SNAPSHOT" mapmanager = "1.8.0-SNAPSHOT"
griefprevention = "17.0.0" griefprevention = "17.0.0"
griefdefender = "2.1.0-SNAPSHOT" griefdefender = "2.1.0-SNAPSHOT"
residence = "4.5._13.1" residence = "4.5._13.1"
towny = "0.100.3.12" towny = "0.100.4.4"
plotsquared = "7.3.9" plotsquared = "7.3.11"
# Third party # Third party
bstats = "3.0.2" bstats = "3.1.0"
sparsebitset = "1.3" sparsebitset = "1.3"
parallelgzip = "1.0.5" parallelgzip = "1.0.5"
adventure = "4.17.0" adventure = "4.17.0"
adventure-bukkit = "4.3.4" adventure-bukkit = "4.3.4"
checkerqual = "3.46.0" checkerqual = "3.47.0"
truezip = "6.8.4" truezip = "6.8.4"
auto-value = "1.11.0" auto-value = "1.11.0"
findbugs = "3.0.2" findbugs = "3.0.2"
@ -35,7 +35,7 @@ jlibnoise = "1.0.0"
jchronic = "0.2.4a" jchronic = "0.2.4a"
lz4-java = "1.8.0" lz4-java = "1.8.0"
lz4-stream = "1.0.0" lz4-stream = "1.0.0"
commons-cli = "1.8.0" commons-cli = "1.9.0"
paperlib = "1.0.8" paperlib = "1.0.8"
paster = "1.1.6" paster = "1.1.6"
vault = "1.7.1" vault = "1.7.1"
@ -47,7 +47,7 @@ text = "3.0.4"
piston = "0.5.10" piston = "0.5.10"
# Tests # Tests
mockito = "5.12.0" mockito = "5.14.1"
# Gradle plugins # Gradle plugins
pluginyml = "0.6.0" pluginyml = "0.6.0"

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.9-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

7
gradlew vendored
View File

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

2
gradlew.bat vendored
View File

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################

View File

@ -1,10 +1,8 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import com.google.common.base.Suppliers; import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.util.ReflectionUtil; import com.sk89q.util.ReflectionUtil;
import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.world.registry.BlockMaterial; import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.EmptyBlockGetter;
@ -17,6 +15,8 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.material.PushReaction;
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
import javax.annotation.Nullable;
public class PaperweightBlockMaterial implements BlockMaterial { public class PaperweightBlockMaterial implements BlockMaterial {
private final Block block; private final Block block;
@ -25,7 +25,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
private final CraftBlockData craftBlockData; private final CraftBlockData craftBlockData;
private final org.bukkit.Material craftMaterial; private final org.bukkit.Material craftMaterial;
private final int opacity; private final int opacity;
private final CompoundTag tile; private final FaweCompoundTag tile;
public PaperweightBlockMaterial(Block block) { public PaperweightBlockMaterial(Block block) {
this(block, block.defaultBlockState()); this(block, block.defaultBlockState());
@ -48,7 +48,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
); );
tile = tileEntity == null tile = tileEntity == null
? null ? null
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); : PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity);
} }
public Block getBlock() { public Block getBlock() {
@ -135,9 +135,10 @@ public class PaperweightBlockMaterial implements BlockMaterial {
return block.isRandomlyTicking(blockState); return block.isRandomlyTicking(blockState);
} }
@SuppressWarnings("deprecation")
@Override @Override
public boolean isMovementBlocker() { public boolean isMovementBlocker() {
return craftMaterial.isSolid(); return blockState.blocksMotion();
} }
@Override @Override
@ -172,7 +173,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
} }
@Override @Override
public CompoundTag getDefaultTile() { public @Nullable FaweCompoundTag defaultTile() {
return tile; return tile;
} }

View File

@ -5,6 +5,7 @@ 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;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.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;
@ -97,6 +98,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier; 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;
@ -129,6 +131,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2.PaperweightAdapter(); this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2.PaperweightAdapter();
} }
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
return blockEntity -> FaweCompoundTag.of(
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId())
);
}
@Nullable @Nullable
private static String getEntityId(Entity entity) { private static String getEntityId(Entity entity) {
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());

View File

@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.fastasyncworldedit.core.util.collection.AdaptedMap;
import com.google.common.base.Suppliers;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
@ -34,6 +31,7 @@ import net.minecraft.core.Holder;
import net.minecraft.core.IdMap; import net.minecraft.core.IdMap;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntTag; import net.minecraft.nbt.IntTag;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
@ -61,11 +59,19 @@ import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlock; import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlock;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinDoubleTag;
import org.enginehub.linbus.tree.LinFloatTag;
import org.enginehub.linbus.tree.LinListTag;
import org.enginehub.linbus.tree.LinStringTag;
import org.enginehub.linbus.tree.LinTagType;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.AbstractSet; import javax.annotation.Nullable;
import java.util.AbstractCollection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
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;
@ -82,7 +88,6 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
@ -91,8 +96,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
private static final Function<BlockEntity, CompoundTag> nmsTile2We = public static final Function<BlockEntity, FaweCompoundTag> NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin
tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); .getInstance()
.getBukkitImplAdapter()).blockEntityToCompoundTag();
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance() .getInstance()
.getBukkitImplAdapter()); .getBukkitImplAdapter());
@ -101,6 +107,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private final ServerLevel serverLevel; private final ServerLevel serverLevel;
private final int chunkX; private final int chunkX;
private final int chunkZ; private final int chunkZ;
private final IntPair chunkPos;
private final int minHeight; private final int minHeight;
private final int maxHeight; private final int maxHeight;
private final int minSectionPosition; private final int minSectionPosition;
@ -135,6 +142,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
this.blockLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()];
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME);
this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap();
this.chunkPos = new IntPair(chunkX, chunkZ);
} }
public int getChunkX() { public int getChunkX() {
@ -256,23 +264,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public CompoundTag getTile(int x, int y, int z) { public FaweCompoundTag tile(final int x, final int y, final int z) {
BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + ( BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + (
chunkX << 4), y, (z & 15) + ( chunkX << 4), y, (z & 15) + (
chunkZ << 4))); chunkZ << 4)));
if (blockEntity == null) { if (blockEntity == null) {
return null; return null;
} }
return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)); return NMS_TO_TILE.apply(blockEntity);
} }
@Override @Override
public Map<BlockVector3, CompoundTag> getTiles() { public Map<BlockVector3, FaweCompoundTag> tiles() {
Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities(); Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities();
if (nmsTiles.isEmpty()) { if (nmsTiles.isEmpty()) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE);
} }
@Override @Override
@ -335,7 +344,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public CompoundTag getEntity(UUID uuid) { public @Nullable FaweCompoundTag entity(final UUID uuid) {
ensureLoaded(serverLevel, chunkX, chunkZ); ensureLoaded(serverLevel, chunkX, chunkZ);
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk()); List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
Entity entity = null; Entity entity = null;
@ -347,10 +356,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (entity != null) { if (entity != null) {
org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity();
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); return FaweCompoundTag.of(BukkitAdapter.adapt(bukkitEnt).getState().getNbt());
} }
for (CompoundTag tag : getEntities()) { for (FaweCompoundTag tag : entities()) {
if (uuid.equals(tag.getUUID())) { if (uuid.equals(NbtUtils.uuid(tag))) {
return tag; return tag;
} }
} }
@ -358,14 +367,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public Set<CompoundTag> getEntities() { public Collection<FaweCompoundTag> entities() {
ensureLoaded(serverLevel, chunkX, chunkZ); ensureLoaded(serverLevel, chunkX, chunkZ);
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk()); List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
if (entities.isEmpty()) { if (entities.isEmpty()) {
return Collections.emptySet(); return Collections.emptyList();
} }
int size = entities.size(); int size = entities.size();
return new AbstractSet<>() { return new AbstractCollection<>() {
@Override @Override
public int size() { public int size() {
return size; return size;
@ -378,10 +387,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override @Override
public boolean contains(Object get) { public boolean contains(Object get) {
if (!(get instanceof CompoundTag getTag)) { if (!(get instanceof FaweCompoundTag getTag)) {
return false; return false;
} }
UUID getUUID = getTag.getUUID(); UUID getUUID = NbtUtils.uuid(getTag);
for (Entity entity : entities) { for (Entity entity : entities) {
UUID uuid = entity.getUUID(); UUID uuid = entity.getUUID();
if (uuid.equals(getUUID)) { if (uuid.equals(getUUID)) {
@ -393,12 +402,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Nonnull @Nonnull
@Override @Override
public Iterator<CompoundTag> iterator() { public Iterator<FaweCompoundTag> iterator() {
Iterable<CompoundTag> result = entities.stream().map(input -> { Iterable<FaweCompoundTag> result = entities.stream().map(input -> {
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); CompoundTag tag = new CompoundTag();
input.save(tag); input.save(tag);
return (CompoundTag) adapter.toNative(tag); return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag));
}).collect(Collectors.toList()); })::iterator;
return result.iterator(); return result.iterator();
} }
}; };
@ -419,7 +428,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
} }
forceLoadSections = false; forceLoadSections = false;
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
if (createCopy) { if (createCopy) {
if (copies.containsKey(copyKey)) { if (copies.containsKey(copyKey)) {
throw new IllegalStateException("Copy key already used."); throw new IllegalStateException("Copy key already used.");
@ -427,9 +437,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
copies.put(copyKey, copy); copies.put(copyKey, copy);
} }
try { try {
ServerLevel nmsWorld = serverLevel;
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
// Remove existing tiles. Create a copy so that we can remove blocks // Remove existing tiles. Create a copy so that we can remove blocks
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
List<BlockEntity> beacons = null; List<BlockEntity> beacons = null;
@ -501,6 +508,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData biomeData
); );
if (PaperweightPlatformAdapter.setSectionAtomic( if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
null, null,
newSection, newSection,
@ -578,6 +587,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData biomeData
); );
if (PaperweightPlatformAdapter.setSectionAtomic( if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
null, null,
newSection, newSection,
@ -643,6 +654,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes() biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
); );
if (!PaperweightPlatformAdapter.setSectionAtomic( if (!PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
existingSection, existingSection,
newSection, newSection,
@ -716,7 +729,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
for (UUID uuid : entityRemoves) { for (UUID uuid : entityRemoves) {
Entity entity = nmsWorld.getEntities().get(uuid); Entity entity = serverLevel.getEntities().get(uuid);
if (entity != null) { if (entity != null) {
removeEntity(entity); removeEntity(entity);
} }
@ -728,48 +741,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}; };
} }
Set<CompoundTag> entities = set.getEntities(); Collection<FaweCompoundTag> entities = set.entities();
if (entities != null && !entities.isEmpty()) { if (entities != null && !entities.isEmpty()) {
if (syncTasks == null) { if (syncTasks == null) {
syncTasks = new Runnable[2]; syncTasks = new Runnable[2];
} }
syncTasks[1] = () -> { syncTasks[1] = () -> {
Iterator<CompoundTag> iterator = entities.iterator(); Iterator<FaweCompoundTag> iterator = entities.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final CompoundTag nativeTag = iterator.next(); final FaweCompoundTag nativeTag = iterator.next();
final Map<String, Tag<?, ?>> entityTagMap = nativeTag.getValue(); final LinCompoundTag linTag = nativeTag.linTag();
final StringTag idTag = (StringTag) entityTagMap.get("Id"); final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
final ListTag posTag = (ListTag) entityTagMap.get("Pos"); final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); final LinListTag<LinFloatTag> rotTag = linTag.findListTag("Rotation", LinTagType.floatTag());
if (idTag == null || posTag == null || rotTag == null) { if (idTag == null || posTag == null || rotTag == null) {
LOGGER.error("Unknown entity tag: {}", nativeTag); LOGGER.error("Unknown entity tag: {}", nativeTag);
continue; continue;
} }
final double x = posTag.getDouble(0); final double x = posTag.get(0).valueAsDouble();
final double y = posTag.getDouble(1); final double y = posTag.get(1).valueAsDouble();
final double z = posTag.getDouble(2); final double z = posTag.get(2).valueAsDouble();
final float yaw = rotTag.getFloat(0); final float yaw = rotTag.get(0).valueAsFloat();
final float pitch = rotTag.getFloat(1); final float pitch = rotTag.get(1).valueAsFloat();
final String id = idTag.getValue(); final String id = idTag.value();
EntityType<?> type = EntityType.byString(id).orElse(null); EntityType<?> type = EntityType.byString(id).orElse(null);
if (type != null) { if (type != null) {
Entity entity = type.create(nmsWorld); Entity entity = type.create(serverLevel);
if (entity != null) { if (entity != null) {
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
nativeTag);
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name); tag.remove(name);
} }
entity.load(tag); entity.load(tag);
entity.absMoveTo(x, y, z, yaw, pitch); entity.absMoveTo(x, y, z, yaw, pitch);
entity.setUUID(nativeTag.getUUID()); entity.setUUID(NbtUtils.uuid(nativeTag));
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
LOGGER.warn( LOGGER.warn(
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`", "Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
id, id,
nmsWorld.getWorld().getName(), serverLevel.getWorld().getName(),
x, x,
y, y,
z z
@ -784,30 +796,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
// set tiles // set tiles
Map<BlockVector3, CompoundTag> tiles = set.getTiles(); Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
if (tiles != null && !tiles.isEmpty()) { if (tiles != null && !tiles.isEmpty()) {
if (syncTasks == null) { if (syncTasks == null) {
syncTasks = new Runnable[1]; syncTasks = new Runnable[1];
} }
syncTasks[0] = () -> { syncTasks[0] = () -> {
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) { for (final Map.Entry<BlockVector3, FaweCompoundTag> entry : tiles.entrySet()) {
final CompoundTag nativeTag = entry.getValue(); final FaweCompoundTag nativeTag = entry.getValue();
final BlockVector3 blockHash = entry.getKey(); final BlockVector3 blockHash = entry.getKey();
final int x = blockHash.x() + bx; final int x = blockHash.x() + bx;
final int y = blockHash.y(); final int y = blockHash.y();
final int z = blockHash.z() + bz; final int z = blockHash.z() + bz;
final BlockPos pos = new BlockPos(x, y, z); final BlockPos pos = new BlockPos(x, y, z);
synchronized (nmsWorld) { synchronized (serverLevel) {
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
if (tileEntity == null || tileEntity.isRemoved()) { if (tileEntity == null || tileEntity.isRemoved()) {
nmsWorld.removeBlockEntity(pos); serverLevel.removeBlockEntity(pos);
tileEntity = nmsWorld.getBlockEntity(pos); tileEntity = serverLevel.getBlockEntity(pos);
} }
if (tileEntity != null) { if (tileEntity != null) {
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
nativeTag);
tag.put("x", IntTag.valueOf(x)); tag.put("x", IntTag.valueOf(x));
tag.put("y", IntTag.valueOf(y)); tag.put("y", IntTag.valueOf(y));
tag.put("z", IntTag.valueOf(z)); tag.put("z", IntTag.valueOf(z));
@ -823,7 +834,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
callback = null; callback = null;
} else { } else {
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> { callback = () -> {
// Set Modified // Set Modified
nmsChunk.setLightCorrect(true); // Set Modified nmsChunk.setLightCorrect(true); // Set Modified
@ -929,7 +939,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override @Override
public void send() { public void send() {
synchronized (sendLock) { synchronized (sendLock) {
PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ); PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
} }
} }

View File

@ -1,21 +1,22 @@
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.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.google.common.base.Suppliers; import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
@ -24,9 +25,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO; import net.minecraft.world.level.chunk.PalettedContainerRO;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.enginehub.linbus.tree.LinCompoundTag;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -38,8 +41,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>(); private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
private final Set<CompoundTag> entities = new HashSet<>(); private final Set<FaweCompoundTag> entities = new HashSet<>();
private final char[][] blocks; private final char[][] blocks;
private final int minHeight; private final int minHeight;
private final int maxHeight; private final int maxHeight;
@ -56,44 +59,35 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
} }
protected void storeTile(BlockEntity blockEntity) { protected void storeTile(BlockEntity blockEntity) {
@SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
tiles.put( tiles.put(
BlockVector3.at( BlockVector3.at(
blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getX(),
blockEntity.getBlockPos().getY(), blockEntity.getBlockPos().getY(),
blockEntity.getBlockPos().getZ() blockEntity.getBlockPos().getZ()
), ),
new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)) FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId()))
); );
} }
@Override
public Map<BlockVector3, CompoundTag> getTiles() {
return tiles;
}
@Override
@Nullable
public CompoundTag getTile(int x, int y, int z) {
return tiles.get(BlockVector3.at(x, y, z));
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void storeEntity(Entity entity) { protected void storeEntity(Entity entity) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); @SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
entity.save(compoundTag); entity.save(compoundTag);
entities.add((CompoundTag) adapter.toNative(compoundTag)); entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag)));
} }
@Override @Override
public Set<CompoundTag> getEntities() { public Collection<FaweCompoundTag> entities() {
return this.entities; return this.entities;
} }
@Override @Override
public CompoundTag getEntity(UUID uuid) { public @Nullable FaweCompoundTag entity(final UUID uuid) {
for (CompoundTag tag : entities) { for (FaweCompoundTag tag : entities) {
if (uuid.equals(tag.getUUID())) { if (uuid.equals(NbtUtils.uuid(tag))) {
return tag; return tag;
} }
} }
@ -179,8 +173,18 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
biomes[layer] = new Holder[64]; biomes[layer] = new Holder[64];
} }
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) { if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
for (int i = 0; i < 64; i++) { if (PaperLib.isPaper()) {
biomes[layer][i] = palettedContainer.get(i); for (int i = 0; i < 64; i++) {
biomes[layer][i] = palettedContainer.get(i); // Only public on paper
}
} else {
try {
for (int i = 0; i < 64; i++) {
biomes[layer][i] = (Holder<Biome>) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
} }
} else { } else {
LOGGER.error( LOGGER.error(
@ -194,7 +198,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
@Override @Override
public BaseBlock getFullBlock(int x, int y, int z) { public BaseBlock getFullBlock(int x, int y, int z) {
BlockState state = BlockTypesCache.states[get(x, y, z)]; BlockState state = BlockTypesCache.states[get(x, y, z)];
return state.toBaseBlock(this, x, y, z); return state.toBaseBlock((IBlocks) this, x, y, z);
} }
@Override @Override
@ -224,6 +228,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
return BlockTypesCache.states[get(x, y, z)]; return BlockTypesCache.states[get(x, y, z)];
} }
@Override
public Map<BlockVector3, FaweCompoundTag> tiles() {
return tiles;
}
@Override
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
return tiles.get(BlockVector3.at(x, y, z));
}
@Override @Override
public int getSkyLight(int x, int y, int z) { public int getSkyLight(int x, int y, int z) {
return 0; return 0;

View File

@ -7,8 +7,8 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TaskManager;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
@ -123,6 +123,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static Field LEVEL_CHUNK_ENTITIES; private static Field LEVEL_CHUNK_ENTITIES;
private static Field SERVER_LEVEL_ENTITY_MANAGER; private static Field SERVER_LEVEL_ENTITY_MANAGER;
static final MethodHandle PALETTED_CONTAINER_GET;
static { static {
final MethodHandles.Lookup lookup = MethodHandles.lookup(); final MethodHandles.Lookup lookup = MethodHandles.lookup();
try { try {
@ -212,6 +214,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} catch (NoSuchFieldException ignored) { } catch (NoSuchFieldException ignored) {
} }
POST_CHUNK_REWRITE = chunkRewrite; POST_CHUNK_REWRITE = chunkRewrite;
Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod(
Refraction.pickName("get", "a"),
int.class
);
palettedContaienrGet.setAccessible(true);
PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet);
} catch (RuntimeException | Error e) { } catch (RuntimeException | Error e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
@ -234,15 +243,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
static boolean setSectionAtomic( static boolean setSectionAtomic(
String worldName,
IntPair pair,
LevelChunkSection[] sections, LevelChunkSection[] sections,
LevelChunkSection expected, LevelChunkSection expected,
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
if (layer >= 0 && layer < sections.length) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer);
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
}
return false;
} }
// There is no point in having a functional semaphore for paper servers. // There is no point in having a functional semaphore for paper servers.
@ -340,7 +348,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static void sendChunk(Object chunk, ServerLevel nmsWorld, int chunkX, int chunkZ) { public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int chunkZ) {
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ); ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
if (chunkHolder == null) { if (chunkHolder == null) {
return; return;
@ -361,26 +369,35 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
if (levelChunk == null) { if (levelChunk == null) {
return; return;
} }
StampLockHolder lockHolder = new StampLockHolder();
NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
if (lockHolder.chunkLock == null) {
return;
}
MinecraftServer.getServer().execute(() -> { MinecraftServer.getServer().execute(() -> {
ClientboundLevelChunkWithLightPacket packet; try {
if (PaperLib.isPaper()) { ClientboundLevelChunkWithLightPacket packet;
packet = new ClientboundLevelChunkWithLightPacket( if (PaperLib.isPaper()) {
levelChunk, packet = new ClientboundLevelChunkWithLightPacket(
nmsWorld.getChunkSource().getLightEngine(), levelChunk,
null, nmsWorld.getChunkSource().getLightEngine(),
null, null,
false // last false is to not bother with x-ray null,
); false // last false is to not bother with x-ray
} else { );
// deprecated on paper - deprecation suppressed } else {
packet = new ClientboundLevelChunkWithLightPacket( // deprecated on paper - deprecation suppressed
levelChunk, packet = new ClientboundLevelChunkWithLightPacket(
nmsWorld.getChunkSource().getLightEngine(), levelChunk,
null, nmsWorld.getChunkSource().getLightEngine(),
null null,
); null
);
}
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
} finally {
NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
} }
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
}); });
} }

View File

@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@ -67,7 +68,7 @@ public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLeve
int x = pos.x; int x = pos.x;
int z = pos.z; int z = pos.z;
if (delay) { // we still need to send the block changes of that chunk if (delay) { // we still need to send the block changes of that chunk
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z); PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
} }
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
} }

View File

@ -4,167 +4,74 @@ import com.fastasyncworldedit.bukkit.adapter.Regenerator;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Lifecycle; import com.mojang.serialization.Lifecycle;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.PaperweightGetBlocks;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.io.file.SafeFiles; import com.sk89q.worldedit.util.io.file.SafeFiles;
import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.RegenOptions;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.Holder; 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.resources.ResourceKey;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; 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.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.thread.ProcessorHandle; import net.minecraft.util.ProgressListener;
import net.minecraft.util.thread.ProcessorMailbox;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome; 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.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.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; 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.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.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.PrimaryLevelData;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk; import org.bukkit.World;
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.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> { public class PaperweightRegen extends Regenerator {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Field serverWorldsField; private static final Field serverWorldsField;
private static final Field paperConfigField; private static final Field paperConfigField;
private static final Field flatBedrockField;
private static final Field generatorSettingFlatField;
private static final Field generatorSettingBaseSupplierField; 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 { 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 { try {
serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField = CraftServer.class.getDeclaredField("worlds");
serverWorldsField.setAccessible(true); serverWorldsField.setAccessible(true);
Field tmpPaperConfigField; Field tmpPaperConfigField;
Field tmpFlatBedrockField;
try { //only present on paper try { //only present on paper
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
tmpPaperConfigField.setAccessible(true); tmpPaperConfigField.setAccessible(true);
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
tmpFlatBedrockField.setAccessible(true);
} catch (Exception e) { } catch (Exception e) {
tmpPaperConfigField = null; tmpPaperConfigField = null;
tmpFlatBedrockField = null;
} }
paperConfigField = tmpPaperConfigField; paperConfigField = tmpPaperConfigField;
flatBedrockField = tmpFlatBedrockField;
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
"settings", "e")); "settings", "e"));
generatorSettingBaseSupplierField.setAccessible(true); 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) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -172,43 +79,37 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
//runtime //runtime
private ServerLevel originalServerWorld; private ServerLevel originalServerWorld;
private ServerChunkCache originalChunkProvider;
private ServerLevel freshWorld; private ServerLevel freshWorld;
private ServerChunkCache freshChunkProvider;
private LevelStorageSource.LevelStorageAccess session; private LevelStorageSource.LevelStorageAccess session;
private StructureTemplateManager structureTemplateManager;
private ThreadedLevelLightEngine threadedLevelLightEngine;
private ChunkGenerator chunkGenerator;
private Path tempDir; private Path tempDir;
private boolean generateFlatBedrock = false; public PaperweightRegen(
World originalBukkitWorld,
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { Region region,
Extent target,
RegenOptions options
) {
super(originalBukkitWorld, region, target, options); super(originalBukkitWorld, region, target, options);
} }
@Override
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
while (shouldKeepTicking.getAsBoolean()) {
if (!this.freshWorld.getChunkSource().pollTask()) {
return;
}
}
}
@Override @Override
protected boolean prepare() { protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); 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()); seed = options.getSeed().orElse(originalServerWorld.getSeed());
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
return true; return true;
} }
@Override @Override
@SuppressWarnings("unchecked")
protected boolean initNewWorld() throws Exception { protected boolean initNewWorld() throws Exception {
//world folder //world folder
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
@ -254,8 +155,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
session, session,
newWorldData, newWorldData,
originalServerWorld.dimension(), originalServerWorld.dimension(),
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() new LevelStem(
.getOrThrow(levelStemResourceKey), originalServerWorld.dimensionTypeRegistration(),
originalServerWorld.getChunkSource().getGenerator()
),
new RegenNoOpWorldLoadListener(), new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(), originalServerWorld.isDebug(),
seed, seed,
@ -273,17 +176,30 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
) : null; ) : null;
@Override @Override
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
}
@Override
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
if (options.hasBiomeType()) { if (options.hasBiomeType()) {
return singleBiome; return singleBiome;
} }
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ);
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() }
);
@Override
public void save(
@org.jetbrains.annotations.Nullable final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled
) {
// noop, spigot
}
@Override
public void save(
@Nullable final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled,
final boolean close
) {
// noop, paper
} }
}).get(); }).get();
freshWorld.noSave = true; freshWorld.noSave = true;
@ -292,89 +208,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
if (paperConfigField != null) { if (paperConfigField != null) {
paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); 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; return true;
} }
@ -389,7 +222,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
try { try {
Fawe.instance().getQueueHandler().sync(() -> { Fawe.instance().getQueueHandler().sync(() -> {
try { try {
freshChunkProvider.close(false); freshWorld.getChunkSource().getDataStorage().cache.clear();
freshWorld.getChunkSource().close(false);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -410,63 +244,20 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
} }
@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 @Override
protected IChunkCache<IChunkGet> initSourceQueueCache() { protected IChunkCache<IChunkGet> initSourceQueueCache() {
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
@Override
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
return getChunkAt(x, z);
}
};
} }
//util //util
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void removeWorldFromWorldsMap() { private void removeWorldFromWorldsMap() {
Fawe.instance().getQueueHandler().sync(() -> { try {
try { Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer()); map.remove("faweregentempworld");
map.remove("faweregentempworld"); } catch (IllegalAccessException e) {
} catch (IllegalAccessException e) { throw new RuntimeException(e);
throw new RuntimeException(e); }
}
});
} }
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) { private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
@ -483,11 +274,15 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
@Override @Override
public void updateSpawnPos(ChunkPos spawnPos) { public void updateSpawnPos(@NotNull ChunkPos spawnPos) {
} }
@Override @Override
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { public void onStatusChange(
final @NotNull ChunkPos pos,
@org.jetbrains.annotations.Nullable final ChunkStatus status
) {
} }
@Override @Override
@ -505,87 +300,4 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
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,8 +1,6 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.google.common.base.Suppliers; import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.world.registry.BlockMaterial; import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.EmptyBlockGetter;
@ -14,6 +12,8 @@ import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.material.PushReaction;
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
import javax.annotation.Nullable;
public class PaperweightBlockMaterial implements BlockMaterial { public class PaperweightBlockMaterial implements BlockMaterial {
private final Block block; private final Block block;
@ -21,7 +21,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
private final CraftBlockData craftBlockData; private final CraftBlockData craftBlockData;
private final org.bukkit.Material craftMaterial; private final org.bukkit.Material craftMaterial;
private final int opacity; private final int opacity;
private final CompoundTag tile; private final FaweCompoundTag tile;
public PaperweightBlockMaterial(Block block) { public PaperweightBlockMaterial(Block block) {
this(block, block.defaultBlockState()); this(block, block.defaultBlockState());
@ -39,7 +39,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
); );
tile = tileEntity == null tile = tileEntity == null
? null ? null
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); : PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity);
} }
public Block getBlock() { public Block getBlock() {
@ -125,9 +125,10 @@ public class PaperweightBlockMaterial implements BlockMaterial {
return block.isRandomlyTicking(blockState); return block.isRandomlyTicking(blockState);
} }
@SuppressWarnings("deprecation")
@Override @Override
public boolean isMovementBlocker() { public boolean isMovementBlocker() {
return craftMaterial.isSolid(); return blockState.blocksMotion();
} }
@Override @Override
@ -162,7 +163,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
} }
@Override @Override
public CompoundTag getDefaultTile() { public @Nullable FaweCompoundTag defaultTile() {
return tile; return tile;
} }

View File

@ -5,6 +5,7 @@ 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;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.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;
@ -97,6 +98,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier; 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;
@ -129,6 +131,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter(); this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter();
} }
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
return blockEntity -> FaweCompoundTag.of(
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId())
);
}
@Nullable @Nullable
private static String getEntityId(Entity entity) { private static String getEntityId(Entity entity) {
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());

View File

@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.fastasyncworldedit.core.util.collection.AdaptedMap;
import com.google.common.base.Suppliers;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
@ -34,6 +31,7 @@ import net.minecraft.core.Holder;
import net.minecraft.core.IdMap; import net.minecraft.core.IdMap;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntTag; import net.minecraft.nbt.IntTag;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
@ -61,11 +59,19 @@ import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlock; import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlock;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinDoubleTag;
import org.enginehub.linbus.tree.LinFloatTag;
import org.enginehub.linbus.tree.LinListTag;
import org.enginehub.linbus.tree.LinStringTag;
import org.enginehub.linbus.tree.LinTagType;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.AbstractSet; import javax.annotation.Nullable;
import java.util.AbstractCollection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
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;
@ -82,7 +88,6 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
@ -91,8 +96,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
private static final Function<BlockEntity, CompoundTag> nmsTile2We = public static final Function<BlockEntity, FaweCompoundTag> NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin
tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); .getInstance()
.getBukkitImplAdapter()).blockEntityToCompoundTag();
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance() .getInstance()
.getBukkitImplAdapter()); .getBukkitImplAdapter());
@ -101,6 +107,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private final ServerLevel serverLevel; private final ServerLevel serverLevel;
private final int chunkX; private final int chunkX;
private final int chunkZ; private final int chunkZ;
private final IntPair chunkPos;
private final int minHeight; private final int minHeight;
private final int maxHeight; private final int maxHeight;
private final int minSectionPosition; private final int minSectionPosition;
@ -108,6 +115,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private final Registry<Biome> biomeRegistry; private final Registry<Biome> biomeRegistry;
private final IdMap<Holder<Biome>> biomeHolderIdMap; private final IdMap<Holder<Biome>> biomeHolderIdMap;
private final ConcurrentHashMap<Integer, PaperweightGetBlocks_Copy> copies = new ConcurrentHashMap<>(); private final ConcurrentHashMap<Integer, PaperweightGetBlocks_Copy> copies = new ConcurrentHashMap<>();
private final Object sendLock = new Object();
private LevelChunkSection[] sections; private LevelChunkSection[] sections;
private LevelChunk levelChunk; private LevelChunk levelChunk;
private DataLayer[] blockLight; private DataLayer[] blockLight;
@ -134,6 +142,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
this.blockLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()];
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME);
this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap();
this.chunkPos = new IntPair(chunkX, chunkZ);
} }
public int getChunkX() { public int getChunkX() {
@ -255,23 +264,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public CompoundTag getTile(int x, int y, int z) { public FaweCompoundTag tile(final int x, final int y, final int z) {
BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + ( BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + (
chunkX << 4), y, (z & 15) + ( chunkX << 4), y, (z & 15) + (
chunkZ << 4))); chunkZ << 4)));
if (blockEntity == null) { if (blockEntity == null) {
return null; return null;
} }
return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)); return NMS_TO_TILE.apply(blockEntity);
} }
@Override @Override
public Map<BlockVector3, CompoundTag> getTiles() { public Map<BlockVector3, FaweCompoundTag> tiles() {
Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities(); Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities();
if (nmsTiles.isEmpty()) { if (nmsTiles.isEmpty()) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE);
} }
@Override @Override
@ -334,7 +344,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public CompoundTag getEntity(UUID uuid) { public @Nullable FaweCompoundTag entity(final UUID uuid) {
ensureLoaded(serverLevel, chunkX, chunkZ); ensureLoaded(serverLevel, chunkX, chunkZ);
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk()); List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
Entity entity = null; Entity entity = null;
@ -346,10 +356,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (entity != null) { if (entity != null) {
org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity();
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); return FaweCompoundTag.of(BukkitAdapter.adapt(bukkitEnt).getState().getNbt());
} }
for (CompoundTag tag : getEntities()) { for (FaweCompoundTag tag : entities()) {
if (uuid.equals(tag.getUUID())) { if (uuid.equals(NbtUtils.uuid(tag))) {
return tag; return tag;
} }
} }
@ -357,14 +367,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public Set<CompoundTag> getEntities() { public Collection<FaweCompoundTag> entities() {
ensureLoaded(serverLevel, chunkX, chunkZ); ensureLoaded(serverLevel, chunkX, chunkZ);
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk()); List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
if (entities.isEmpty()) { if (entities.isEmpty()) {
return Collections.emptySet(); return Collections.emptyList();
} }
int size = entities.size(); int size = entities.size();
return new AbstractSet<>() { return new AbstractCollection<>() {
@Override @Override
public int size() { public int size() {
return size; return size;
@ -377,10 +387,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override @Override
public boolean contains(Object get) { public boolean contains(Object get) {
if (!(get instanceof CompoundTag getTag)) { if (!(get instanceof FaweCompoundTag getTag)) {
return false; return false;
} }
UUID getUUID = getTag.getUUID(); UUID getUUID = NbtUtils.uuid(getTag);
for (Entity entity : entities) { for (Entity entity : entities) {
UUID uuid = entity.getUUID(); UUID uuid = entity.getUUID();
if (uuid.equals(getUUID)) { if (uuid.equals(getUUID)) {
@ -392,12 +402,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Nonnull @Nonnull
@Override @Override
public Iterator<CompoundTag> iterator() { public Iterator<FaweCompoundTag> iterator() {
Iterable<CompoundTag> result = entities.stream().map(input -> { Iterable<FaweCompoundTag> result = entities.stream().map(input -> {
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
input.save(tag); input.save(tag);
return (CompoundTag) adapter.toNative(tag); return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag));
}).collect(Collectors.toList()); })::iterator;
return result.iterator(); return result.iterator();
} }
}; };
@ -418,7 +428,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
} }
forceLoadSections = false; forceLoadSections = false;
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
if (createCopy) { if (createCopy) {
if (copies.containsKey(copyKey)) { if (copies.containsKey(copyKey)) {
throw new IllegalStateException("Copy key already used."); throw new IllegalStateException("Copy key already used.");
@ -426,9 +437,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
copies.put(copyKey, copy); copies.put(copyKey, copy);
} }
try { try {
ServerLevel nmsWorld = serverLevel;
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
// Remove existing tiles. Create a copy so that we can remove blocks // Remove existing tiles. Create a copy so that we can remove blocks
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
List<BlockEntity> beacons = null; List<BlockEntity> beacons = null;
@ -500,6 +508,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData biomeData
); );
if (PaperweightPlatformAdapter.setSectionAtomic( if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
null, null,
newSection, newSection,
@ -577,6 +587,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData biomeData
); );
if (PaperweightPlatformAdapter.setSectionAtomic( if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
null, null,
newSection, newSection,
@ -642,6 +654,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes() biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
); );
if (!PaperweightPlatformAdapter.setSectionAtomic( if (!PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
existingSection, existingSection,
newSection, newSection,
@ -715,7 +729,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
for (UUID uuid : entityRemoves) { for (UUID uuid : entityRemoves) {
Entity entity = nmsWorld.getEntities().get(uuid); Entity entity = serverLevel.getEntities().get(uuid);
if (entity != null) { if (entity != null) {
removeEntity(entity); removeEntity(entity);
} }
@ -727,48 +741,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}; };
} }
Set<CompoundTag> entities = set.getEntities(); Collection<FaweCompoundTag> entities = set.entities();
if (entities != null && !entities.isEmpty()) { if (entities != null && !entities.isEmpty()) {
if (syncTasks == null) { if (syncTasks == null) {
syncTasks = new Runnable[2]; syncTasks = new Runnable[2];
} }
syncTasks[1] = () -> { syncTasks[1] = () -> {
Iterator<CompoundTag> iterator = entities.iterator(); Iterator<FaweCompoundTag> iterator = entities.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final CompoundTag nativeTag = iterator.next(); final FaweCompoundTag nativeTag = iterator.next();
final Map<String, Tag<?, ?>> entityTagMap = nativeTag.getValue(); final LinCompoundTag linTag = nativeTag.linTag();
final StringTag idTag = (StringTag) entityTagMap.get("Id"); final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
final ListTag posTag = (ListTag) entityTagMap.get("Pos"); final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); final LinListTag<LinFloatTag> rotTag = linTag.findListTag("Rotation", LinTagType.floatTag());
if (idTag == null || posTag == null || rotTag == null) { if (idTag == null || posTag == null || rotTag == null) {
LOGGER.error("Unknown entity tag: {}", nativeTag); LOGGER.error("Unknown entity tag: {}", nativeTag);
continue; continue;
} }
final double x = posTag.getDouble(0); final double x = posTag.get(0).valueAsDouble();
final double y = posTag.getDouble(1); final double y = posTag.get(1).valueAsDouble();
final double z = posTag.getDouble(2); final double z = posTag.get(2).valueAsDouble();
final float yaw = rotTag.getFloat(0); final float yaw = rotTag.get(0).valueAsFloat();
final float pitch = rotTag.getFloat(1); final float pitch = rotTag.get(1).valueAsFloat();
final String id = idTag.getValue(); final String id = idTag.value();
EntityType<?> type = EntityType.byString(id).orElse(null); EntityType<?> type = EntityType.byString(id).orElse(null);
if (type != null) { if (type != null) {
Entity entity = type.create(nmsWorld); Entity entity = type.create(serverLevel);
if (entity != null) { if (entity != null) {
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(linTag);
nativeTag);
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name); tag.remove(name);
} }
entity.load(tag); entity.load(tag);
entity.absMoveTo(x, y, z, yaw, pitch); entity.absMoveTo(x, y, z, yaw, pitch);
entity.setUUID(nativeTag.getUUID()); entity.setUUID(NbtUtils.uuid(nativeTag));
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
LOGGER.warn( LOGGER.warn(
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`", "Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
id, id,
nmsWorld.getWorld().getName(), serverLevel.getWorld().getName(),
x, x,
y, y,
z z
@ -783,30 +796,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
// set tiles // set tiles
Map<BlockVector3, CompoundTag> tiles = set.getTiles(); Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
if (tiles != null && !tiles.isEmpty()) { if (tiles != null && !tiles.isEmpty()) {
if (syncTasks == null) { if (syncTasks == null) {
syncTasks = new Runnable[1]; syncTasks = new Runnable[1];
} }
syncTasks[0] = () -> { syncTasks[0] = () -> {
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) { for (final Map.Entry<BlockVector3, FaweCompoundTag> entry : tiles.entrySet()) {
final CompoundTag nativeTag = entry.getValue(); final FaweCompoundTag nativeTag = entry.getValue();
final BlockVector3 blockHash = entry.getKey(); final BlockVector3 blockHash = entry.getKey();
final int x = blockHash.x() + bx; final int x = blockHash.x() + bx;
final int y = blockHash.y(); final int y = blockHash.y();
final int z = blockHash.z() + bz; final int z = blockHash.z() + bz;
final BlockPos pos = new BlockPos(x, y, z); final BlockPos pos = new BlockPos(x, y, z);
synchronized (nmsWorld) { synchronized (serverLevel) {
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
if (tileEntity == null || tileEntity.isRemoved()) { if (tileEntity == null || tileEntity.isRemoved()) {
nmsWorld.removeBlockEntity(pos); serverLevel.removeBlockEntity(pos);
tileEntity = nmsWorld.getBlockEntity(pos); tileEntity = serverLevel.getBlockEntity(pos);
} }
if (tileEntity != null) { if (tileEntity != null) {
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
nativeTag);
tag.put("x", IntTag.valueOf(x)); tag.put("x", IntTag.valueOf(x));
tag.put("y", IntTag.valueOf(y)); tag.put("y", IntTag.valueOf(y));
tag.put("z", IntTag.valueOf(z)); tag.put("z", IntTag.valueOf(z));
@ -822,7 +834,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
callback = null; callback = null;
} else { } else {
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> { callback = () -> {
// Set Modified // Set Modified
nmsChunk.setLightCorrect(true); // Set Modified nmsChunk.setLightCorrect(true); // Set Modified
@ -927,7 +938,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override @Override
public void send() { public void send() {
PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ); PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
} }
/** /**

View File

@ -1,21 +1,22 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.google.common.base.Suppliers; import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
@ -24,9 +25,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO; import net.minecraft.world.level.chunk.PalettedContainerRO;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.enginehub.linbus.tree.LinCompoundTag;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -38,8 +41,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>(); private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
private final Set<CompoundTag> entities = new HashSet<>(); private final Set<FaweCompoundTag> entities = new HashSet<>();
private final char[][] blocks; private final char[][] blocks;
private final int minHeight; private final int minHeight;
private final int maxHeight; private final int maxHeight;
@ -56,44 +59,35 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
} }
protected void storeTile(BlockEntity blockEntity) { protected void storeTile(BlockEntity blockEntity) {
@SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
tiles.put( tiles.put(
BlockVector3.at( BlockVector3.at(
blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getX(),
blockEntity.getBlockPos().getY(), blockEntity.getBlockPos().getY(),
blockEntity.getBlockPos().getZ() blockEntity.getBlockPos().getZ()
), ),
new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)) FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId()))
); );
} }
@Override
public Map<BlockVector3, CompoundTag> getTiles() {
return tiles;
}
@Override
@Nullable
public CompoundTag getTile(int x, int y, int z) {
return tiles.get(BlockVector3.at(x, y, z));
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void storeEntity(Entity entity) { protected void storeEntity(Entity entity) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); @SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
entity.save(compoundTag); entity.save(compoundTag);
entities.add((CompoundTag) adapter.toNative(compoundTag)); entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag)));
} }
@Override @Override
public Set<CompoundTag> getEntities() { public Collection<FaweCompoundTag> entities() {
return this.entities; return this.entities;
} }
@Override @Override
public CompoundTag getEntity(UUID uuid) { public @Nullable FaweCompoundTag entity(final UUID uuid) {
for (CompoundTag tag : entities) { for (FaweCompoundTag tag : entities) {
if (uuid.equals(tag.getUUID())) { if (uuid.equals(NbtUtils.uuid(tag))) {
return tag; return tag;
} }
} }
@ -179,8 +173,18 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
biomes[layer] = new Holder[64]; biomes[layer] = new Holder[64];
} }
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) { if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
for (int i = 0; i < 64; i++) { if (PaperLib.isPaper()) {
biomes[layer][i] = palettedContainer.get(i); for (int i = 0; i < 64; i++) {
biomes[layer][i] = palettedContainer.get(i); // Only public on paper
}
} else {
try {
for (int i = 0; i < 64; i++) {
biomes[layer][i] = (Holder<Biome>) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
} }
} else { } else {
LOGGER.error( LOGGER.error(
@ -194,7 +198,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
@Override @Override
public BaseBlock getFullBlock(int x, int y, int z) { public BaseBlock getFullBlock(int x, int y, int z) {
BlockState state = BlockTypesCache.states[get(x, y, z)]; BlockState state = BlockTypesCache.states[get(x, y, z)];
return state.toBaseBlock(this, x, y, z); return state.toBaseBlock((IBlocks) this, x, y, z);
} }
@Override @Override
@ -224,6 +228,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
return BlockTypesCache.states[get(x, y, z)]; return BlockTypesCache.states[get(x, y, z)];
} }
@Override
public Map<BlockVector3, FaweCompoundTag> tiles() {
return tiles;
}
@Override
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
return tiles.get(BlockVector3.at(x, y, z));
}
@Override @Override
public int getSkyLight(int x, int y, int z) { public int getSkyLight(int x, int y, int z) {
return 0; return 0;

View File

@ -7,8 +7,8 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TaskManager;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
@ -123,6 +123,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static Field LEVEL_CHUNK_ENTITIES; private static Field LEVEL_CHUNK_ENTITIES;
private static Field SERVER_LEVEL_ENTITY_MANAGER; private static Field SERVER_LEVEL_ENTITY_MANAGER;
static final MethodHandle PALETTED_CONTAINER_GET;
static { static {
final MethodHandles.Lookup lookup = MethodHandles.lookup(); final MethodHandles.Lookup lookup = MethodHandles.lookup();
try { try {
@ -212,6 +214,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} catch (NoSuchFieldException ignored) { } catch (NoSuchFieldException ignored) {
} }
POST_CHUNK_REWRITE = chunkRewrite; POST_CHUNK_REWRITE = chunkRewrite;
Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod(
Refraction.pickName("get", "a"),
int.class
);
palettedContaienrGet.setAccessible(true);
PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet);
} catch (RuntimeException | Error e) { } catch (RuntimeException | Error e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
@ -234,15 +243,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
static boolean setSectionAtomic( static boolean setSectionAtomic(
String worldName,
IntPair pair,
LevelChunkSection[] sections, LevelChunkSection[] sections,
LevelChunkSection expected, LevelChunkSection expected,
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
if (layer >= 0 && layer < sections.length) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer);
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
}
return false;
} }
// There is no point in having a functional semaphore for paper servers. // There is no point in having a functional semaphore for paper servers.
@ -340,7 +348,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static void sendChunk(Object chunk, ServerLevel nmsWorld, int chunkX, int chunkZ) { public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int chunkZ) {
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ); ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
if (chunkHolder == null) { if (chunkHolder == null) {
return; return;
@ -361,26 +369,35 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
if (levelChunk == null) { if (levelChunk == null) {
return; return;
} }
StampLockHolder lockHolder = new StampLockHolder();
NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
if (lockHolder.chunkLock == null) {
return;
}
MinecraftServer.getServer().execute(() -> { MinecraftServer.getServer().execute(() -> {
ClientboundLevelChunkWithLightPacket packet; try {
if (PaperLib.isPaper()) { ClientboundLevelChunkWithLightPacket packet;
packet = new ClientboundLevelChunkWithLightPacket( if (PaperLib.isPaper()) {
levelChunk, packet = new ClientboundLevelChunkWithLightPacket(
nmsWorld.getChunkSource().getLightEngine(), levelChunk,
null, nmsWorld.getChunkSource().getLightEngine(),
null, null,
false // last false is to not bother with x-ray null,
); false // last false is to not bother with x-ray
} else { );
// deprecated on paper - deprecation suppressed } else {
packet = new ClientboundLevelChunkWithLightPacket( // deprecated on paper - deprecation suppressed
levelChunk, packet = new ClientboundLevelChunkWithLightPacket(
nmsWorld.getChunkSource().getLightEngine(), levelChunk,
null, nmsWorld.getChunkSource().getLightEngine(),
null null,
); null
);
}
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
} finally {
NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
} }
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
}); });
} }

View File

@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@ -67,7 +68,7 @@ public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLeve
int x = pos.x; int x = pos.x;
int z = pos.z; int z = pos.z;
if (delay) { // we still need to send the block changes of that chunk if (delay) { // we still need to send the block changes of that chunk
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z); PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
} }
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
} }

View File

@ -4,166 +4,74 @@ import com.fastasyncworldedit.bukkit.adapter.Regenerator;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Lifecycle; import com.mojang.serialization.Lifecycle;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction; 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.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.io.file.SafeFiles; import com.sk89q.worldedit.util.io.file.SafeFiles;
import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.RegenOptions;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.Holder; 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.resources.ResourceKey;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; 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.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.thread.ProcessorHandle; import net.minecraft.util.ProgressListener;
import net.minecraft.util.thread.ProcessorMailbox;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome; 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.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.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; 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.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.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.PrimaryLevelData;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk; import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer; import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; 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.BiomeProvider;
import org.bukkit.generator.BlockPopulator; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> { public class PaperweightRegen extends Regenerator {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Field serverWorldsField; private static final Field serverWorldsField;
private static final Field paperConfigField; private static final Field paperConfigField;
private static final Field flatBedrockField;
private static final Field generatorSettingFlatField;
private static final Field generatorSettingBaseSupplierField; 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 { 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 { try {
serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField = CraftServer.class.getDeclaredField("worlds");
serverWorldsField.setAccessible(true); serverWorldsField.setAccessible(true);
Field tmpPaperConfigField; Field tmpPaperConfigField;
Field tmpFlatBedrockField;
try { //only present on paper try { //only present on paper
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
tmpPaperConfigField.setAccessible(true); tmpPaperConfigField.setAccessible(true);
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
tmpFlatBedrockField.setAccessible(true);
} catch (Exception e) { } catch (Exception e) {
tmpPaperConfigField = null; tmpPaperConfigField = null;
tmpFlatBedrockField = null;
} }
paperConfigField = tmpPaperConfigField; paperConfigField = tmpPaperConfigField;
flatBedrockField = tmpFlatBedrockField;
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
"settings", "e")); "settings", "e"));
generatorSettingBaseSupplierField.setAccessible(true); 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) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -171,43 +79,37 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
//runtime //runtime
private ServerLevel originalServerWorld; private ServerLevel originalServerWorld;
private ServerChunkCache originalChunkProvider;
private ServerLevel freshWorld; private ServerLevel freshWorld;
private ServerChunkCache freshChunkProvider;
private LevelStorageSource.LevelStorageAccess session; private LevelStorageSource.LevelStorageAccess session;
private StructureTemplateManager structureTemplateManager;
private ThreadedLevelLightEngine threadedLevelLightEngine;
private ChunkGenerator chunkGenerator;
private Path tempDir; private Path tempDir;
private boolean generateFlatBedrock = false; public PaperweightRegen(
World originalBukkitWorld,
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { Region region,
Extent target,
RegenOptions options
) {
super(originalBukkitWorld, region, target, options); super(originalBukkitWorld, region, target, options);
} }
@Override
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
while (shouldKeepTicking.getAsBoolean()) {
if (!this.freshWorld.getChunkSource().pollTask()) {
return;
}
}
}
@Override @Override
protected boolean prepare() { protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); 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()); seed = options.getSeed().orElse(originalServerWorld.getSeed());
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
return true; return true;
} }
@Override @Override
@SuppressWarnings("unchecked")
protected boolean initNewWorld() throws Exception { protected boolean initNewWorld() throws Exception {
//world folder //world folder
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
@ -253,8 +155,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
session, session,
newWorldData, newWorldData,
originalServerWorld.dimension(), originalServerWorld.dimension(),
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() new LevelStem(
.getOrThrow(levelStemResourceKey), originalServerWorld.dimensionTypeRegistration(),
originalServerWorld.getChunkSource().getGenerator()
),
new RegenNoOpWorldLoadListener(), new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(), originalServerWorld.isDebug(),
seed, seed,
@ -272,17 +176,30 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
) : null; ) : null;
@Override @Override
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
}
@Override
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
if (options.hasBiomeType()) { if (options.hasBiomeType()) {
return singleBiome; return singleBiome;
} }
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ);
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() }
);
@Override
public void save(
@org.jetbrains.annotations.Nullable final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled
) {
// noop, spigot
}
@Override
public void save(
@Nullable final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled,
final boolean close
) {
// noop, paper
} }
}).get(); }).get();
freshWorld.noSave = true; freshWorld.noSave = true;
@ -291,89 +208,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
if (paperConfigField != null) { if (paperConfigField != null) {
paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); 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; return true;
} }
@ -388,7 +222,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
try { try {
Fawe.instance().getQueueHandler().sync(() -> { Fawe.instance().getQueueHandler().sync(() -> {
try { try {
freshChunkProvider.close(false); freshWorld.getChunkSource().getDataStorage().cache.clear();
freshWorld.getChunkSource().close(false);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -409,63 +244,20 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
} }
@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 @Override
protected IChunkCache<IChunkGet> initSourceQueueCache() { protected IChunkCache<IChunkGet> initSourceQueueCache() {
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
@Override
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
return getChunkAt(x, z);
}
};
} }
//util //util
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void removeWorldFromWorldsMap() { private void removeWorldFromWorldsMap() {
Fawe.instance().getQueueHandler().sync(() -> { try {
try { Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer()); map.remove("faweregentempworld");
map.remove("faweregentempworld"); } catch (IllegalAccessException e) {
} catch (IllegalAccessException e) { throw new RuntimeException(e);
throw new RuntimeException(e); }
}
});
} }
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) { private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
@ -482,11 +274,15 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
@Override @Override
public void updateSpawnPos(ChunkPos spawnPos) { public void updateSpawnPos(@NotNull ChunkPos spawnPos) {
} }
@Override @Override
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { public void onStatusChange(
final @NotNull ChunkPos pos,
@org.jetbrains.annotations.Nullable final ChunkStatus status
) {
} }
@Override @Override
@ -504,87 +300,4 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
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

@ -12,6 +12,6 @@ repositories {
dependencies { dependencies {
// url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.6-R0.1-SNAPSHOT/ // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.6-R0.1-SNAPSHOT/
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.6-R0.1-20240702.153951-123") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.6-R0.1-20240916.192025-125")
compileOnly(libs.paperlib) compileOnly(libs.paperlib)
} }

View File

@ -1,11 +1,8 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4;
import com.google.common.base.Suppliers; import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.world.registry.BlockMaterial; import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
@ -15,6 +12,8 @@ import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.material.PushReaction;
import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.block.data.CraftBlockData;
import javax.annotation.Nullable;
public class PaperweightBlockMaterial implements BlockMaterial { public class PaperweightBlockMaterial implements BlockMaterial {
private final Block block; private final Block block;
@ -22,7 +21,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
private final CraftBlockData craftBlockData; private final CraftBlockData craftBlockData;
private final org.bukkit.Material craftMaterial; private final org.bukkit.Material craftMaterial;
private final int opacity; private final int opacity;
private final CompoundTag tile; private final FaweCompoundTag tile;
public PaperweightBlockMaterial(Block block) { public PaperweightBlockMaterial(Block block) {
this(block, block.defaultBlockState()); this(block, block.defaultBlockState());
@ -38,9 +37,9 @@ public class PaperweightBlockMaterial implements BlockMaterial {
BlockPos.ZERO, BlockPos.ZERO,
blockState blockState
); );
tile = tileEntity == null ? null : new PaperweightLazyCompoundTag( tile = tileEntity == null
Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess())) ? null
); : PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity);
} }
public Block getBlock() { public Block getBlock() {
@ -126,9 +125,10 @@ public class PaperweightBlockMaterial implements BlockMaterial {
return blockState.isRandomlyTicking(); return blockState.isRandomlyTicking();
} }
@SuppressWarnings("deprecation")
@Override @Override
public boolean isMovementBlocker() { public boolean isMovementBlocker() {
return craftMaterial.isSolid(); return blockState.blocksMotion();
} }
@Override @Override
@ -163,7 +163,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
} }
@Override @Override
public CompoundTag getDefaultTile() { public @Nullable FaweCompoundTag defaultTile() {
return tile; return tile;
} }

View File

@ -5,6 +5,7 @@ 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;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.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;
@ -103,6 +104,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier; 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;
@ -138,6 +140,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R4.PaperweightAdapter(); this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R4.PaperweightAdapter();
} }
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
return blockEntity -> FaweCompoundTag.of(
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))
);
}
@Nullable @Nullable
private static String getEntityId(Entity entity) { private static String getEntityId(Entity entity) {
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());

View File

@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.fastasyncworldedit.core.util.collection.AdaptedMap;
import com.google.common.base.Suppliers;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
@ -34,6 +31,7 @@ import net.minecraft.core.Holder;
import net.minecraft.core.IdMap; import net.minecraft.core.IdMap;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntTag; import net.minecraft.nbt.IntTag;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@ -62,11 +60,19 @@ import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.block.CraftBlock;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinDoubleTag;
import org.enginehub.linbus.tree.LinFloatTag;
import org.enginehub.linbus.tree.LinListTag;
import org.enginehub.linbus.tree.LinStringTag;
import org.enginehub.linbus.tree.LinTagType;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.AbstractSet; import javax.annotation.Nullable;
import java.util.AbstractCollection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
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;
@ -83,7 +89,6 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
@ -92,9 +97,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
private static final Function<BlockEntity, CompoundTag> nmsTile2We = tileEntity -> new PaperweightLazyCompoundTag( public static final Function<BlockEntity, FaweCompoundTag> NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin
Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess())) .getInstance()
); .getBukkitImplAdapter()).blockEntityToCompoundTag();
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance() .getInstance()
.getBukkitImplAdapter()); .getBukkitImplAdapter());
@ -103,6 +108,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private final ServerLevel serverLevel; private final ServerLevel serverLevel;
private final int chunkX; private final int chunkX;
private final int chunkZ; private final int chunkZ;
private final IntPair chunkPos;
private final int minHeight; private final int minHeight;
private final int maxHeight; private final int maxHeight;
private final int minSectionPosition; private final int minSectionPosition;
@ -137,6 +143,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
this.blockLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()];
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME);
this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap();
this.chunkPos = new IntPair(chunkX, chunkZ);
} }
public int getChunkX() { public int getChunkX() {
@ -258,23 +265,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public CompoundTag getTile(int x, int y, int z) { public FaweCompoundTag tile(final int x, final int y, final int z) {
BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + ( BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + (
chunkX << 4), y, (z & 15) + ( chunkX << 4), y, (z & 15) + (
chunkZ << 4))); chunkZ << 4)));
if (blockEntity == null) { if (blockEntity == null) {
return null; return null;
} }
return new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))); return NMS_TO_TILE.apply(blockEntity);
} }
@Override @Override
public Map<BlockVector3, CompoundTag> getTiles() { public Map<BlockVector3, FaweCompoundTag> tiles() {
Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities(); Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities();
if (nmsTiles.isEmpty()) { if (nmsTiles.isEmpty()) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE);
} }
@Override @Override
@ -337,7 +345,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public CompoundTag getEntity(UUID uuid) { public @Nullable FaweCompoundTag entity(final UUID uuid) {
ensureLoaded(serverLevel, chunkX, chunkZ); ensureLoaded(serverLevel, chunkX, chunkZ);
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk()); List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
Entity entity = null; Entity entity = null;
@ -349,10 +357,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (entity != null) { if (entity != null) {
org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity();
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); return FaweCompoundTag.of(BukkitAdapter.adapt(bukkitEnt).getState().getNbt());
} }
for (CompoundTag tag : getEntities()) { for (FaweCompoundTag tag : entities()) {
if (uuid.equals(tag.getUUID())) { if (uuid.equals(NbtUtils.uuid(tag))) {
return tag; return tag;
} }
} }
@ -360,14 +368,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public Set<CompoundTag> getEntities() { public Collection<FaweCompoundTag> entities() {
ensureLoaded(serverLevel, chunkX, chunkZ); ensureLoaded(serverLevel, chunkX, chunkZ);
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk()); List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
if (entities.isEmpty()) { if (entities.isEmpty()) {
return Collections.emptySet(); return Collections.emptyList();
} }
int size = entities.size(); int size = entities.size();
return new AbstractSet<>() { return new AbstractCollection<>() {
@Override @Override
public int size() { public int size() {
return size; return size;
@ -380,10 +388,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override @Override
public boolean contains(Object get) { public boolean contains(Object get) {
if (!(get instanceof CompoundTag getTag)) { if (!(get instanceof FaweCompoundTag getTag)) {
return false; return false;
} }
UUID getUUID = getTag.getUUID(); UUID getUUID = NbtUtils.uuid(getTag);
for (Entity entity : entities) { for (Entity entity : entities) {
UUID uuid = entity.getUUID(); UUID uuid = entity.getUUID();
if (uuid.equals(getUUID)) { if (uuid.equals(getUUID)) {
@ -395,12 +403,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Nonnull @Nonnull
@Override @Override
public Iterator<CompoundTag> iterator() { public Iterator<FaweCompoundTag> iterator() {
Iterable<CompoundTag> result = entities.stream().map(input -> { Iterable<FaweCompoundTag> result = entities.stream().map(input -> {
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
input.save(tag); input.save(tag);
return (CompoundTag) adapter.toNative(tag); return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag));
}).collect(Collectors.toList()); })::iterator;
return result.iterator(); return result.iterator();
} }
}; };
@ -421,7 +429,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
} }
forceLoadSections = false; forceLoadSections = false;
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
if (createCopy) { if (createCopy) {
if (copies.containsKey(copyKey)) { if (copies.containsKey(copyKey)) {
throw new IllegalStateException("Copy key already used."); throw new IllegalStateException("Copy key already used.");
@ -429,9 +438,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
copies.put(copyKey, copy); copies.put(copyKey, copy);
} }
try { try {
ServerLevel nmsWorld = serverLevel;
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
// Remove existing tiles. Create a copy so that we can remove blocks // Remove existing tiles. Create a copy so that we can remove blocks
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
List<BlockEntity> beacons = null; List<BlockEntity> beacons = null;
@ -503,6 +509,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData biomeData
); );
if (PaperweightPlatformAdapter.setSectionAtomic( if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
null, null,
newSection, newSection,
@ -580,6 +588,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData biomeData
); );
if (PaperweightPlatformAdapter.setSectionAtomic( if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
null, null,
newSection, newSection,
@ -644,7 +654,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeRegistry, biomeRegistry,
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes() biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
); );
if (!PaperweightPlatformAdapter.setSectionAtomic(levelChunkSections, existingSection, if (!PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections,
existingSection,
newSection, newSection,
getSectionIndex getSectionIndex
)) { )) {
@ -716,7 +730,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
for (UUID uuid : entityRemoves) { for (UUID uuid : entityRemoves) {
Entity entity = nmsWorld.getEntities().get(uuid); Entity entity = serverLevel.getEntities().get(uuid);
if (entity != null) { if (entity != null) {
removeEntity(entity); removeEntity(entity);
} }
@ -728,48 +742,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}; };
} }
Set<CompoundTag> entities = set.getEntities(); Collection<FaweCompoundTag> entities = set.entities();
if (entities != null && !entities.isEmpty()) { if (entities != null && !entities.isEmpty()) {
if (syncTasks == null) { if (syncTasks == null) {
syncTasks = new Runnable[2]; syncTasks = new Runnable[2];
} }
syncTasks[1] = () -> { syncTasks[1] = () -> {
Iterator<CompoundTag> iterator = entities.iterator(); Iterator<FaweCompoundTag> iterator = entities.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final CompoundTag nativeTag = iterator.next(); final FaweCompoundTag nativeTag = iterator.next();
final Map<String, Tag<?, ?>> entityTagMap = nativeTag.getValue(); final LinCompoundTag linTag = nativeTag.linTag();
final StringTag idTag = (StringTag) entityTagMap.get("Id"); final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
final ListTag posTag = (ListTag) entityTagMap.get("Pos"); final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); final LinListTag<LinFloatTag> rotTag = linTag.findListTag("Rotation", LinTagType.floatTag());
if (idTag == null || posTag == null || rotTag == null) { if (idTag == null || posTag == null || rotTag == null) {
LOGGER.error("Unknown entity tag: {}", nativeTag); LOGGER.error("Unknown entity tag: {}", nativeTag);
continue; continue;
} }
final double x = posTag.getDouble(0); final double x = posTag.get(0).valueAsDouble();
final double y = posTag.getDouble(1); final double y = posTag.get(1).valueAsDouble();
final double z = posTag.getDouble(2); final double z = posTag.get(2).valueAsDouble();
final float yaw = rotTag.getFloat(0); final float yaw = rotTag.get(0).valueAsFloat();
final float pitch = rotTag.getFloat(1); final float pitch = rotTag.get(1).valueAsFloat();
final String id = idTag.getValue(); final String id = idTag.value();
EntityType<?> type = EntityType.byString(id).orElse(null); EntityType<?> type = EntityType.byString(id).orElse(null);
if (type != null) { if (type != null) {
Entity entity = type.create(nmsWorld); Entity entity = type.create(serverLevel);
if (entity != null) { if (entity != null) {
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(linTag);
nativeTag);
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name); tag.remove(name);
} }
entity.load(tag); entity.load(tag);
entity.absMoveTo(x, y, z, yaw, pitch); entity.absMoveTo(x, y, z, yaw, pitch);
entity.setUUID(nativeTag.getUUID()); entity.setUUID(NbtUtils.uuid(nativeTag));
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
LOGGER.warn( LOGGER.warn(
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`", "Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
id, id,
nmsWorld.getWorld().getName(), serverLevel.getWorld().getName(),
x, x,
y, y,
z z
@ -784,30 +797,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
// set tiles // set tiles
Map<BlockVector3, CompoundTag> tiles = set.getTiles(); Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
if (tiles != null && !tiles.isEmpty()) { if (tiles != null && !tiles.isEmpty()) {
if (syncTasks == null) { if (syncTasks == null) {
syncTasks = new Runnable[1]; syncTasks = new Runnable[1];
} }
syncTasks[0] = () -> { syncTasks[0] = () -> {
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) { for (final Map.Entry<BlockVector3, FaweCompoundTag> entry : tiles.entrySet()) {
final CompoundTag nativeTag = entry.getValue(); final FaweCompoundTag nativeTag = entry.getValue();
final BlockVector3 blockHash = entry.getKey(); final BlockVector3 blockHash = entry.getKey();
final int x = blockHash.x() + bx; final int x = blockHash.x() + bx;
final int y = blockHash.y(); final int y = blockHash.y();
final int z = blockHash.z() + bz; final int z = blockHash.z() + bz;
final BlockPos pos = new BlockPos(x, y, z); final BlockPos pos = new BlockPos(x, y, z);
synchronized (nmsWorld) { synchronized (serverLevel) {
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
if (tileEntity == null || tileEntity.isRemoved()) { if (tileEntity == null || tileEntity.isRemoved()) {
nmsWorld.removeBlockEntity(pos); serverLevel.removeBlockEntity(pos);
tileEntity = nmsWorld.getBlockEntity(pos); tileEntity = serverLevel.getBlockEntity(pos);
} }
if (tileEntity != null) { if (tileEntity != null) {
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
nativeTag);
tag.put("x", IntTag.valueOf(x)); tag.put("x", IntTag.valueOf(x));
tag.put("y", IntTag.valueOf(y)); tag.put("y", IntTag.valueOf(y));
tag.put("z", IntTag.valueOf(z)); tag.put("z", IntTag.valueOf(z));
@ -823,7 +835,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
callback = null; callback = null;
} else { } else {
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> { callback = () -> {
// Set Modified // Set Modified
nmsChunk.setLightCorrect(true); // Set Modified nmsChunk.setLightCorrect(true); // Set Modified
@ -929,7 +940,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override @Override
public void send() { public void send() {
synchronized (sendLock) { synchronized (sendLock) {
PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ); PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
} }
} }

View File

@ -1,21 +1,22 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.google.common.base.Suppliers; import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.nbt.Tag;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@ -25,9 +26,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO; import net.minecraft.world.level.chunk.PalettedContainerRO;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.enginehub.linbus.tree.LinCompoundTag;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -39,8 +42,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>(); private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
private final Set<CompoundTag> entities = new HashSet<>(); private final Set<FaweCompoundTag> entities = new HashSet<>();
private final char[][] blocks; private final char[][] blocks;
private final int minHeight; private final int minHeight;
private final int maxHeight; private final int maxHeight;
@ -57,44 +60,37 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
} }
protected void storeTile(BlockEntity blockEntity) { protected void storeTile(BlockEntity blockEntity) {
@SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
tiles.put( tiles.put(
BlockVector3.at( BlockVector3.at(
blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getX(),
blockEntity.getBlockPos().getY(), blockEntity.getBlockPos().getY(),
blockEntity.getBlockPos().getZ() blockEntity.getBlockPos().getZ()
), ),
new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))) FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId(DedicatedServer
.getServer()
.registryAccess())))
); );
} }
@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) { protected void storeEntity(Entity entity) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); @SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
entity.save(compoundTag); entity.save(compoundTag);
entities.add((CompoundTag) adapter.toNative(compoundTag)); entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag)));
} }
@Override @Override
public Set<CompoundTag> getEntities() { public Collection<FaweCompoundTag> entities() {
return this.entities; return this.entities;
} }
@Override @Override
public CompoundTag getEntity(UUID uuid) { public @Nullable FaweCompoundTag entity(final UUID uuid) {
for (CompoundTag tag : entities) { for (FaweCompoundTag tag : entities) {
if (uuid.equals(tag.getUUID())) { if (uuid.equals(NbtUtils.uuid(tag))) {
return tag; return tag;
} }
} }
@ -180,8 +176,18 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
biomes[layer] = new Holder[64]; biomes[layer] = new Holder[64];
} }
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) { if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
for (int i = 0; i < 64; i++) { if (PaperLib.isPaper()) {
biomes[layer][i] = palettedContainer.get(i); for (int i = 0; i < 64; i++) {
biomes[layer][i] = palettedContainer.get(i); // Only public on paper
}
} else {
try {
for (int i = 0; i < 64; i++) {
biomes[layer][i] = (Holder<Biome>) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
} }
} else { } else {
LOGGER.error( LOGGER.error(
@ -195,7 +201,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
@Override @Override
public BaseBlock getFullBlock(int x, int y, int z) { public BaseBlock getFullBlock(int x, int y, int z) {
BlockState state = BlockTypesCache.states[get(x, y, z)]; BlockState state = BlockTypesCache.states[get(x, y, z)];
return state.toBaseBlock(this, x, y, z); return state.toBaseBlock((IBlocks) this, x, y, z);
} }
@Override @Override
@ -225,6 +231,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
return BlockTypesCache.states[get(x, y, z)]; return BlockTypesCache.states[get(x, y, z)];
} }
@Override
public Map<BlockVector3, FaweCompoundTag> tiles() {
return tiles;
}
@Override
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
return tiles.get(BlockVector3.at(x, y, z));
}
@Override @Override
public int getSkyLight(int x, int y, int z) { public int getSkyLight(int x, int y, int z) {
return 0; return 0;

View File

@ -7,9 +7,10 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TaskManager;
import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.Refraction;
@ -76,6 +77,7 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -121,6 +123,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static Field LEVEL_CHUNK_ENTITIES; private static Field LEVEL_CHUNK_ENTITIES;
private static Field SERVER_LEVEL_ENTITY_MANAGER; private static Field SERVER_LEVEL_ENTITY_MANAGER;
static final MethodHandle PALETTED_CONTAINER_GET;
static { static {
final MethodHandles.Lookup lookup = MethodHandles.lookup(); final MethodHandles.Lookup lookup = MethodHandles.lookup();
try { try {
@ -210,6 +214,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} catch (NoSuchFieldException ignored) { } catch (NoSuchFieldException ignored) {
} }
POST_CHUNK_REWRITE = chunkRewrite; POST_CHUNK_REWRITE = chunkRewrite;
Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod(
Refraction.pickName("get", "a"),
int.class
);
palettedContaienrGet.setAccessible(true);
PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet);
} catch (RuntimeException | Error e) { } catch (RuntimeException | Error e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
@ -232,15 +243,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
static boolean setSectionAtomic( static boolean setSectionAtomic(
String worldName,
IntPair pair,
LevelChunkSection[] sections, LevelChunkSection[] sections,
LevelChunkSection expected, LevelChunkSection expected,
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
if (layer >= 0 && layer < sections.length) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer);
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
}
return false;
} }
// There is no point in having a functional semaphore for paper servers. // There is no point in having a functional semaphore for paper servers.
@ -338,7 +348,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static void sendChunk(Object chunk, ServerLevel nmsWorld, int chunkX, int chunkZ) { public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int chunkZ) {
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ); ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
if (chunkHolder == null) { if (chunkHolder == null) {
return; return;
@ -347,36 +357,42 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunk levelChunk; LevelChunk levelChunk;
if (PaperLib.isPaper()) { if (PaperLib.isPaper()) {
// getChunkAtIfLoadedImmediately is paper only // getChunkAtIfLoadedImmediately is paper only
levelChunk = nmsWorld levelChunk = nmsWorld.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
.getChunkSource()
.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
} else { } else {
levelChunk = chunkHolder.getTickingChunkFuture() levelChunk = chunkHolder.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null);
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null);
} }
if (levelChunk == null) { if (levelChunk == null) {
return; return;
} }
StampLockHolder lockHolder = new StampLockHolder();
NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
if (lockHolder.chunkLock == null) {
return;
}
MinecraftServer.getServer().execute(() -> { MinecraftServer.getServer().execute(() -> {
ClientboundLevelChunkWithLightPacket packet; try {
if (PaperLib.isPaper()) { ClientboundLevelChunkWithLightPacket packet;
packet = new ClientboundLevelChunkWithLightPacket( if (PaperLib.isPaper()) {
levelChunk, packet = new ClientboundLevelChunkWithLightPacket(
nmsWorld.getChunkSource().getLightEngine(), levelChunk,
null, nmsWorld.getChunkSource().getLightEngine(),
null, null,
false // last false is to not bother with x-ray null,
); false // last false is to not bother with x-ray
} else { );
// deprecated on paper - deprecation suppressed } else {
packet = new ClientboundLevelChunkWithLightPacket( // deprecated on paper - deprecation suppressed
levelChunk, packet = new ClientboundLevelChunkWithLightPacket(
nmsWorld.getChunkSource().getLightEngine(), levelChunk,
null, nmsWorld.getChunkSource().getLightEngine(),
null null,
); null
);
}
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
} finally {
NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
} }
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
}); });
} }

View File

@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@ -67,7 +68,7 @@ public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLeve
int x = pos.x; int x = pos.x;
int z = pos.z; int z = pos.z;
if (delay) { // we still need to send the block changes of that chunk if (delay) { // we still need to send the block changes of that chunk
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z); PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
} }
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
} }

View File

@ -4,166 +4,73 @@ import com.fastasyncworldedit.bukkit.adapter.Regenerator;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle; import com.mojang.serialization.Lifecycle;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.PaperweightGetBlocks;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.io.file.SafeFiles; import com.sk89q.worldedit.util.io.file.SafeFiles;
import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.RegenOptions;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.Holder; 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.resources.ResourceKey;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; 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.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.thread.ProcessorHandle; import net.minecraft.util.ProgressListener;
import net.minecraft.util.thread.ProcessorMailbox;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome; 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.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.WorldGenContext;
import net.minecraft.world.level.dimension.LevelStem; 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.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.WorldOptions; 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.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.PrimaryLevelData;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk; import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> { public class PaperweightRegen extends Regenerator {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Field serverWorldsField; private static final Field serverWorldsField;
private static final Field paperConfigField; private static final Field paperConfigField;
private static final Field flatBedrockField;
private static final Field generatorSettingFlatField;
private static final Field generatorSettingBaseSupplierField; 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 { 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.NONE
); // structure refs: radius 8, but only writes to current chunk
chunkStati.put(ChunkStatus.BIOMES, Concurrency.NONE); // 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.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
chunkStati.put(
ChunkStatus.INITIALIZE_LIGHT,
Concurrency.FULL
); // initialize_light: radius 0
chunkStati.put(
ChunkStatus.LIGHT,
Concurrency.FULL
); // light: radius 1, but no writes to other chunks, only current chunk
chunkStati.put(ChunkStatus.SPAWN, Concurrency.NONE); // spawn: radius 0
try { try {
serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField = CraftServer.class.getDeclaredField("worlds");
serverWorldsField.setAccessible(true); serverWorldsField.setAccessible(true);
Field tmpPaperConfigField; Field tmpPaperConfigField;
Field tmpFlatBedrockField;
try { //only present on paper try { //only present on paper
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
tmpPaperConfigField.setAccessible(true); tmpPaperConfigField.setAccessible(true);
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
tmpFlatBedrockField.setAccessible(true);
} catch (Exception e) { } catch (Exception e) {
tmpPaperConfigField = null; tmpPaperConfigField = null;
tmpFlatBedrockField = null;
} }
paperConfigField = tmpPaperConfigField; paperConfigField = tmpPaperConfigField;
flatBedrockField = tmpFlatBedrockField;
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
"settings", "e")); "settings", "e"));
generatorSettingBaseSupplierField.setAccessible(true); 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) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -171,44 +78,37 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
//runtime //runtime
private ServerLevel originalServerWorld; private ServerLevel originalServerWorld;
private ServerChunkCache originalChunkProvider;
private ServerLevel freshWorld; private ServerLevel freshWorld;
private ServerChunkCache freshChunkProvider;
private LevelStorageSource.LevelStorageAccess session; private LevelStorageSource.LevelStorageAccess session;
private StructureTemplateManager structureTemplateManager;
private ThreadedLevelLightEngine threadedLevelLightEngine;
private ChunkGenerator chunkGenerator;
private WorldGenContext worldGenContext;
private Path tempDir; private Path tempDir;
private boolean generateFlatBedrock = false; public PaperweightRegen(
World originalBukkitWorld,
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { Region region,
Extent target,
RegenOptions options
) {
super(originalBukkitWorld, region, target, options); super(originalBukkitWorld, region, target, options);
} }
@Override
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
while (shouldKeepTicking.getAsBoolean()) {
if (!this.freshWorld.getChunkSource().pollTask()) {
return;
}
}
}
@Override @Override
protected boolean prepare() { protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); 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()); seed = options.getSeed().orElse(originalServerWorld.getSeed());
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
return true; return true;
} }
@Override @Override
@SuppressWarnings("unchecked")
protected boolean initNewWorld() throws Exception { protected boolean initNewWorld() throws Exception {
//world folder //world folder
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
@ -254,8 +154,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
session, session,
newWorldData, newWorldData,
originalServerWorld.dimension(), originalServerWorld.dimension(),
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() new LevelStem(
.getOrThrow(levelStemResourceKey), originalServerWorld.dimensionTypeRegistration(),
originalServerWorld.getChunkSource().getGenerator()
),
new RegenNoOpWorldLoadListener(), new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(), originalServerWorld.isDebug(),
seed, seed,
@ -272,20 +174,32 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
) : null; ) : null;
@Override
public void tick(@NotNull BooleanSupplier shouldKeepTicking) { //no ticking
}
@Override @Override
public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
if (options.hasBiomeType()) { if (options.hasBiomeType()) {
return singleBiome; return singleBiome;
} }
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ);
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
);
} }
@Override
public void save(
@org.jetbrains.annotations.Nullable final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled
) {
// noop, spigot
}
@Override
public void save(
@Nullable final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled,
final boolean close
) {
// noop, paper
}
}).get(); }).get();
freshWorld.noSave = true; freshWorld.noSave = true;
removeWorldFromWorldsMap(); removeWorldFromWorldsMap();
@ -293,93 +207,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
if (paperConfigField != null) { if (paperConfigField != null) {
paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); 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(noiseBasedChunkGenerator);
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, @NotNull 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);
this.worldGenContext = new WorldGenContext(freshWorld, chunkGenerator, structureTemplateManager,
threadedLevelLightEngine
);
return true; return true;
} }
@ -394,7 +221,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
try { try {
Fawe.instance().getQueueHandler().sync(() -> { Fawe.instance().getQueueHandler().sync(() -> {
try { try {
freshChunkProvider.close(false); freshWorld.getChunkSource().getDataStorage().cache.clear();
freshWorld.getChunkSource().close(false);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -415,50 +243,9 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
} }
@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 @Override
protected IChunkCache<IChunkGet> initSourceQueueCache() { protected IChunkCache<IChunkGet> initSourceQueueCache() {
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
@Override
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
return getChunkAt(x, z);
}
};
} }
//util //util
@ -512,83 +299,4 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
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
@SuppressWarnings("unused") // compatibility with spigot
public boolean generateFlatBedrock() {
return generateFlatBedrock;
}
// no one will ever see the entities!
@Override
public @NotNull 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(
worldGenContext,
Runnable::run,
CompletableFuture::completedFuture,
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 @NotNull CompletableFuture<ChunkAccess> lightChunk(final @NotNull ChunkAccess chunk, final boolean excludeBlocks) {
return CompletableFuture.completedFuture(chunk);
}
}
} }

View File

@ -12,6 +12,6 @@ repositories {
dependencies { dependencies {
// url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.21.1-R0.1-SNAPSHOT/ // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.21.1-R0.1-SNAPSHOT/
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.21.1-R0.1-20240811.223934-9") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.21.1-R0.1-20241012.212042-119")
compileOnly(libs.paperlib) compileOnly(libs.paperlib)
} }

View File

@ -1,11 +1,8 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1;
import com.google.common.base.Suppliers; import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.world.registry.BlockMaterial; import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
@ -15,6 +12,8 @@ import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.material.PushReaction;
import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.block.data.CraftBlockData;
import javax.annotation.Nullable;
public class PaperweightBlockMaterial implements BlockMaterial { public class PaperweightBlockMaterial implements BlockMaterial {
private final Block block; private final Block block;
@ -22,7 +21,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
private final CraftBlockData craftBlockData; private final CraftBlockData craftBlockData;
private final org.bukkit.Material craftMaterial; private final org.bukkit.Material craftMaterial;
private final int opacity; private final int opacity;
private final CompoundTag tile; private final FaweCompoundTag tile;
public PaperweightBlockMaterial(Block block) { public PaperweightBlockMaterial(Block block) {
this(block, block.defaultBlockState()); this(block, block.defaultBlockState());
@ -38,9 +37,9 @@ public class PaperweightBlockMaterial implements BlockMaterial {
BlockPos.ZERO, BlockPos.ZERO,
blockState blockState
); );
tile = tileEntity == null ? null : new PaperweightLazyCompoundTag( tile = tileEntity == null
Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess())) ? null
); : PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity);
} }
public Block getBlock() { public Block getBlock() {
@ -126,9 +125,10 @@ public class PaperweightBlockMaterial implements BlockMaterial {
return blockState.isRandomlyTicking(); return blockState.isRandomlyTicking();
} }
@SuppressWarnings("deprecation")
@Override @Override
public boolean isMovementBlocker() { public boolean isMovementBlocker() {
return craftMaterial.isSolid(); return blockState.blocksMotion();
} }
@Override @Override
@ -163,7 +163,7 @@ public class PaperweightBlockMaterial implements BlockMaterial {
} }
@Override @Override
public CompoundTag getDefaultTile() { public @Nullable FaweCompoundTag defaultTile() {
return tile; return tile;
} }

View File

@ -5,6 +5,7 @@ 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;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.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;
@ -18,6 +19,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
@ -102,6 +104,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier; 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;
@ -137,6 +140,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_R1.PaperweightAdapter(); this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_R1.PaperweightAdapter();
} }
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
return blockEntity -> FaweCompoundTag.of(
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))
);
}
@Nullable @Nullable
private static String getEntityId(Entity entity) { private static String getEntityId(Entity entity) {
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
@ -557,7 +566,7 @@ public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.
@Override @Override
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
throw new UnsupportedOperationException("Regen support for 1.21 not yet implemented."); return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
} }
@Override @Override

View File

@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.fastasyncworldedit.core.util.collection.AdaptedMap;
import com.google.common.base.Suppliers;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
@ -34,6 +31,7 @@ import net.minecraft.core.Holder;
import net.minecraft.core.IdMap; import net.minecraft.core.IdMap;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntTag; import net.minecraft.nbt.IntTag;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@ -62,11 +60,19 @@ import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.block.CraftBlock;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinDoubleTag;
import org.enginehub.linbus.tree.LinFloatTag;
import org.enginehub.linbus.tree.LinListTag;
import org.enginehub.linbus.tree.LinStringTag;
import org.enginehub.linbus.tree.LinTagType;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.AbstractSet; import javax.annotation.Nullable;
import java.util.AbstractCollection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
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;
@ -83,7 +89,6 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
@ -92,9 +97,9 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); private static final Function<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
private static final Function<BlockEntity, CompoundTag> nmsTile2We = tileEntity -> new PaperweightLazyCompoundTag( public static final Function<BlockEntity, FaweCompoundTag> NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin
Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess())) .getInstance()
); .getBukkitImplAdapter()).blockEntityToCompoundTag();
private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin
.getInstance() .getInstance()
.getBukkitImplAdapter()); .getBukkitImplAdapter());
@ -103,6 +108,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private final ServerLevel serverLevel; private final ServerLevel serverLevel;
private final int chunkX; private final int chunkX;
private final int chunkZ; private final int chunkZ;
private final IntPair chunkPos;
private final int minHeight; private final int minHeight;
private final int maxHeight; private final int maxHeight;
private final int minSectionPosition; private final int minSectionPosition;
@ -137,6 +143,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
this.blockLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()];
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME);
this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap();
this.chunkPos = new IntPair(chunkX, chunkZ);
} }
public int getChunkX() { public int getChunkX() {
@ -258,23 +265,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public CompoundTag getTile(int x, int y, int z) { public FaweCompoundTag tile(final int x, final int y, final int z) {
BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + ( BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + (
chunkX << 4), y, (z & 15) + ( chunkX << 4), y, (z & 15) + (
chunkZ << 4))); chunkZ << 4)));
if (blockEntity == null) { if (blockEntity == null) {
return null; return null;
} }
return new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))); return NMS_TO_TILE.apply(blockEntity);
} }
@Override @Override
public Map<BlockVector3, CompoundTag> getTiles() { public Map<BlockVector3, FaweCompoundTag> tiles() {
Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities(); Map<BlockPos, BlockEntity> nmsTiles = getChunk().getBlockEntities();
if (nmsTiles.isEmpty()) { if (nmsTiles.isEmpty()) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE);
} }
@Override @Override
@ -337,7 +345,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public CompoundTag getEntity(UUID uuid) { public @Nullable FaweCompoundTag entity(final UUID uuid) {
ensureLoaded(serverLevel, chunkX, chunkZ); ensureLoaded(serverLevel, chunkX, chunkZ);
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk()); List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
Entity entity = null; Entity entity = null;
@ -349,10 +357,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (entity != null) { if (entity != null) {
org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity();
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); return FaweCompoundTag.of(BukkitAdapter.adapt(bukkitEnt).getState().getNbt());
} }
for (CompoundTag tag : getEntities()) { for (FaweCompoundTag tag : entities()) {
if (uuid.equals(tag.getUUID())) { if (uuid.equals(NbtUtils.uuid(tag))) {
return tag; return tag;
} }
} }
@ -360,14 +368,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
@Override @Override
public Set<CompoundTag> getEntities() { public Collection<FaweCompoundTag> entities() {
ensureLoaded(serverLevel, chunkX, chunkZ); ensureLoaded(serverLevel, chunkX, chunkZ);
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk()); List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
if (entities.isEmpty()) { if (entities.isEmpty()) {
return Collections.emptySet(); return Collections.emptyList();
} }
int size = entities.size(); int size = entities.size();
return new AbstractSet<>() { return new AbstractCollection<>() {
@Override @Override
public int size() { public int size() {
return size; return size;
@ -380,10 +388,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override @Override
public boolean contains(Object get) { public boolean contains(Object get) {
if (!(get instanceof CompoundTag getTag)) { if (!(get instanceof FaweCompoundTag getTag)) {
return false; return false;
} }
UUID getUUID = getTag.getUUID(); UUID getUUID = NbtUtils.uuid(getTag);
for (Entity entity : entities) { for (Entity entity : entities) {
UUID uuid = entity.getUUID(); UUID uuid = entity.getUUID();
if (uuid.equals(getUUID)) { if (uuid.equals(getUUID)) {
@ -395,15 +403,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Nonnull @Nonnull
@Override @Override
public Iterator<CompoundTag> iterator() { public Iterator<FaweCompoundTag> iterator() {
Iterable<CompoundTag> result = entities.stream().map(input -> { Iterable<FaweCompoundTag> result = entities.stream().map(input -> {
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); CompoundTag tag = new CompoundTag();
input.save(tag); input.save(tag);
return (CompoundTag) adapter.toNative(tag); return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag));
}).collect(Collectors.toList()); })::iterator;
return result.iterator(); return result.iterator();
} }
}; };
} }
private void removeEntity(Entity entity) { private void removeEntity(Entity entity) {
@ -421,7 +430,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
} }
forceLoadSections = false; forceLoadSections = false;
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
if (createCopy) { if (createCopy) {
if (copies.containsKey(copyKey)) { if (copies.containsKey(copyKey)) {
throw new IllegalStateException("Copy key already used."); throw new IllegalStateException("Copy key already used.");
@ -429,9 +439,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
copies.put(copyKey, copy); copies.put(copyKey, copy);
} }
try { try {
ServerLevel nmsWorld = serverLevel;
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
// Remove existing tiles. Create a copy so that we can remove blocks // Remove existing tiles. Create a copy so that we can remove blocks
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
List<BlockEntity> beacons = null; List<BlockEntity> beacons = null;
@ -503,6 +510,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData biomeData
); );
if (PaperweightPlatformAdapter.setSectionAtomic( if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
null, null,
newSection, newSection,
@ -577,6 +586,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData biomeData
); );
if (PaperweightPlatformAdapter.setSectionAtomic( if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections, levelChunkSections,
null, null,
newSection, newSection,
@ -638,7 +649,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeRegistry, biomeRegistry,
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes() biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
); );
if (!PaperweightPlatformAdapter.setSectionAtomic(levelChunkSections, existingSection, if (!PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections,
existingSection,
newSection, newSection,
getSectionIndex getSectionIndex
)) { )) {
@ -710,7 +725,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
for (UUID uuid : entityRemoves) { for (UUID uuid : entityRemoves) {
Entity entity = nmsWorld.getEntities().get(uuid); Entity entity = serverLevel.getEntities().get(uuid);
if (entity != null) { if (entity != null) {
removeEntity(entity); removeEntity(entity);
} }
@ -722,48 +737,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}; };
} }
Set<CompoundTag> entities = set.getEntities(); Collection<FaweCompoundTag> entities = set.entities();
if (entities != null && !entities.isEmpty()) { if (entities != null && !entities.isEmpty()) {
if (syncTasks == null) { if (syncTasks == null) {
syncTasks = new Runnable[2]; syncTasks = new Runnable[2];
} }
syncTasks[1] = () -> { syncTasks[1] = () -> {
Iterator<CompoundTag> iterator = entities.iterator(); Iterator<FaweCompoundTag> iterator = entities.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final CompoundTag nativeTag = iterator.next(); final FaweCompoundTag nativeTag = iterator.next();
final Map<String, Tag<?, ?>> entityTagMap = nativeTag.getValue(); final LinCompoundTag linTag = nativeTag.linTag();
final StringTag idTag = (StringTag) entityTagMap.get("Id"); final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
final ListTag posTag = (ListTag) entityTagMap.get("Pos"); final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); final LinListTag<LinFloatTag> rotTag = linTag.findListTag("Rotation", LinTagType.floatTag());
if (idTag == null || posTag == null || rotTag == null) { if (idTag == null || posTag == null || rotTag == null) {
LOGGER.error("Unknown entity tag: {}", nativeTag); LOGGER.error("Unknown entity tag: {}", nativeTag);
continue; continue;
} }
final double x = posTag.getDouble(0); final double x = posTag.get(0).valueAsDouble();
final double y = posTag.getDouble(1); final double y = posTag.get(1).valueAsDouble();
final double z = posTag.getDouble(2); final double z = posTag.get(2).valueAsDouble();
final float yaw = rotTag.getFloat(0); final float yaw = rotTag.get(0).valueAsFloat();
final float pitch = rotTag.getFloat(1); final float pitch = rotTag.get(1).valueAsFloat();
final String id = idTag.getValue(); final String id = idTag.value();
EntityType<?> type = EntityType.byString(id).orElse(null); EntityType<?> type = EntityType.byString(id).orElse(null);
if (type != null) { if (type != null) {
Entity entity = type.create(nmsWorld); Entity entity = type.create(serverLevel);
if (entity != null) { if (entity != null) {
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
nativeTag);
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name); tag.remove(name);
} }
entity.load(tag); entity.load(tag);
entity.absMoveTo(x, y, z, yaw, pitch); entity.absMoveTo(x, y, z, yaw, pitch);
entity.setUUID(nativeTag.getUUID()); entity.setUUID(NbtUtils.uuid(nativeTag));
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
LOGGER.warn( LOGGER.warn(
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`", "Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
id, id,
nmsWorld.getWorld().getName(), serverLevel.getWorld().getName(),
x, x,
y, y,
z z
@ -778,30 +792,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
// set tiles // set tiles
Map<BlockVector3, CompoundTag> tiles = set.getTiles(); Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
if (tiles != null && !tiles.isEmpty()) { if (tiles != null && !tiles.isEmpty()) {
if (syncTasks == null) { if (syncTasks == null) {
syncTasks = new Runnable[1]; syncTasks = new Runnable[1];
} }
syncTasks[0] = () -> { syncTasks[0] = () -> {
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) { for (final Map.Entry<BlockVector3, FaweCompoundTag> entry : tiles.entrySet()) {
final CompoundTag nativeTag = entry.getValue(); final FaweCompoundTag nativeTag = entry.getValue();
final BlockVector3 blockHash = entry.getKey(); final BlockVector3 blockHash = entry.getKey();
final int x = blockHash.x() + bx; final int x = blockHash.x() + bx;
final int y = blockHash.y(); final int y = blockHash.y();
final int z = blockHash.z() + bz; final int z = blockHash.z() + bz;
final BlockPos pos = new BlockPos(x, y, z); final BlockPos pos = new BlockPos(x, y, z);
synchronized (nmsWorld) { synchronized (serverLevel) {
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
if (tileEntity == null || tileEntity.isRemoved()) { if (tileEntity == null || tileEntity.isRemoved()) {
nmsWorld.removeBlockEntity(pos); serverLevel.removeBlockEntity(pos);
tileEntity = nmsWorld.getBlockEntity(pos); tileEntity = serverLevel.getBlockEntity(pos);
} }
if (tileEntity != null) { if (tileEntity != null) {
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
nativeTag);
tag.put("x", IntTag.valueOf(x)); tag.put("x", IntTag.valueOf(x));
tag.put("y", IntTag.valueOf(y)); tag.put("y", IntTag.valueOf(y));
tag.put("z", IntTag.valueOf(z)); tag.put("z", IntTag.valueOf(z));
@ -817,7 +830,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
callback = null; callback = null;
} else { } else {
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> { callback = () -> {
// Set Modified // Set Modified
nmsChunk.setLightCorrect(true); // Set Modified nmsChunk.setLightCorrect(true); // Set Modified
@ -923,7 +935,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override @Override
public void send() { public void send() {
synchronized (sendLock) { synchronized (sendLock) {
PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ); PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
} }
} }

View File

@ -1,21 +1,22 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.google.common.base.Suppliers; import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.nbt.Tag;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@ -25,9 +26,11 @@ import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO; import net.minecraft.world.level.chunk.PalettedContainerRO;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.enginehub.linbus.tree.LinCompoundTag;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -39,8 +42,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>(); private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
private final Set<CompoundTag> entities = new HashSet<>(); private final Set<FaweCompoundTag> entities = new HashSet<>();
private final char[][] blocks; private final char[][] blocks;
private final int minHeight; private final int minHeight;
private final int maxHeight; private final int maxHeight;
@ -57,44 +60,37 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
} }
protected void storeTile(BlockEntity blockEntity) { protected void storeTile(BlockEntity blockEntity) {
@SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
tiles.put( tiles.put(
BlockVector3.at( BlockVector3.at(
blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getX(),
blockEntity.getBlockPos().getY(), blockEntity.getBlockPos().getY(),
blockEntity.getBlockPos().getZ() blockEntity.getBlockPos().getZ()
), ),
new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))) FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId(DedicatedServer
.getServer()
.registryAccess())))
); );
} }
@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) { protected void storeEntity(Entity entity) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); @SuppressWarnings("unchecked")
BukkitImplAdapter<Tag> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
entity.save(compoundTag); entity.save(compoundTag);
entities.add((CompoundTag) adapter.toNative(compoundTag)); entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag)));
} }
@Override @Override
public Set<CompoundTag> getEntities() { public Collection<FaweCompoundTag> entities() {
return this.entities; return this.entities;
} }
@Override @Override
public CompoundTag getEntity(UUID uuid) { public @Nullable FaweCompoundTag entity(final UUID uuid) {
for (CompoundTag tag : entities) { for (FaweCompoundTag tag : entities) {
if (uuid.equals(tag.getUUID())) { if (uuid.equals(NbtUtils.uuid(tag))) {
return tag; return tag;
} }
} }
@ -180,8 +176,18 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
biomes[layer] = new Holder[64]; biomes[layer] = new Holder[64];
} }
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) { if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
for (int i = 0; i < 64; i++) { if (PaperLib.isPaper()) {
biomes[layer][i] = palettedContainer.get(i); for (int i = 0; i < 64; i++) {
biomes[layer][i] = palettedContainer.get(i); // Only public on paper
}
} else {
try {
for (int i = 0; i < 64; i++) {
biomes[layer][i] = (Holder<Biome>) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
} }
} else { } else {
LOGGER.error( LOGGER.error(
@ -195,7 +201,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
@Override @Override
public BaseBlock getFullBlock(int x, int y, int z) { public BaseBlock getFullBlock(int x, int y, int z) {
BlockState state = BlockTypesCache.states[get(x, y, z)]; BlockState state = BlockTypesCache.states[get(x, y, z)];
return state.toBaseBlock(this, x, y, z); return state.toBaseBlock((IBlocks) this, x, y, z);
} }
@Override @Override
@ -225,6 +231,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
return BlockTypesCache.states[get(x, y, z)]; return BlockTypesCache.states[get(x, y, z)];
} }
@Override
public Map<BlockVector3, FaweCompoundTag> tiles() {
return tiles;
}
@Override
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
return tiles.get(BlockVector3.at(x, y, z));
}
@Override @Override
public int getSkyLight(int x, int y, int z) { public int getSkyLight(int x, int y, int z) {
return 0; return 0;

View File

@ -8,9 +8,10 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TaskManager;
import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.Refraction;
@ -31,7 +32,6 @@ import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.BitStorage; import net.minecraft.util.BitStorage;
import net.minecraft.util.ExceptionCollector;
import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.SimpleBitStorage;
import net.minecraft.util.ThreadingDetector; import net.minecraft.util.ThreadingDetector;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
@ -76,6 +76,7 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -119,6 +120,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES; private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
private static Field SERVER_LEVEL_ENTITY_MANAGER; private static Field SERVER_LEVEL_ENTITY_MANAGER;
static final MethodHandle PALETTED_CONTAINER_GET;
static { static {
final MethodHandles.Lookup lookup = MethodHandles.lookup(); final MethodHandles.Lookup lookup = MethodHandles.lookup();
try { try {
@ -195,6 +198,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "N")); SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "N"));
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
} }
Method palettedContaienrGet = PalettedContainer.class.getDeclaredMethod(
Refraction.pickName("get", "a"),
int.class
);
palettedContaienrGet.setAccessible(true);
PALETTED_CONTAINER_GET = lookup.unreflect(palettedContaienrGet);
} catch (RuntimeException | Error e) { } catch (RuntimeException | Error e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
@ -217,15 +227,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
static boolean setSectionAtomic( static boolean setSectionAtomic(
String worldName,
IntPair pair,
LevelChunkSection[] sections, LevelChunkSection[] sections,
LevelChunkSection expected, LevelChunkSection expected,
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
if (layer >= 0 && layer < sections.length) { return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer);
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
}
return false;
} }
// There is no point in having a functional semaphore for paper servers. // There is no point in having a functional semaphore for paper servers.
@ -323,7 +332,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static void sendChunk(Object chunk, ServerLevel nmsWorld, int chunkX, int chunkZ) { public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int chunkZ) {
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ); ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
if (chunkHolder == null) { if (chunkHolder == null) {
return; return;
@ -332,36 +341,42 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunk levelChunk; LevelChunk levelChunk;
if (PaperLib.isPaper()) { if (PaperLib.isPaper()) {
// getChunkAtIfLoadedImmediately is paper only // getChunkAtIfLoadedImmediately is paper only
levelChunk = nmsWorld levelChunk = nmsWorld.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
.getChunkSource()
.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
} else { } else {
levelChunk = chunkHolder.getTickingChunkFuture() levelChunk = chunkHolder.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null);
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null);
} }
if (levelChunk == null) { if (levelChunk == null) {
return; return;
} }
StampLockHolder lockHolder = new StampLockHolder();
NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
if (lockHolder.chunkLock == null) {
return;
}
MinecraftServer.getServer().execute(() -> { MinecraftServer.getServer().execute(() -> {
ClientboundLevelChunkWithLightPacket packet; try {
if (PaperLib.isPaper()) { ClientboundLevelChunkWithLightPacket packet;
packet = new ClientboundLevelChunkWithLightPacket( if (PaperLib.isPaper()) {
levelChunk, packet = new ClientboundLevelChunkWithLightPacket(
nmsWorld.getChunkSource().getLightEngine(), levelChunk,
null, nmsWorld.getChunkSource().getLightEngine(),
null, null,
false // last false is to not bother with x-ray null,
); false // last false is to not bother with x-ray
} else { );
// deprecated on paper - deprecation suppressed } else {
packet = new ClientboundLevelChunkWithLightPacket( // deprecated on paper - deprecation suppressed
levelChunk, packet = new ClientboundLevelChunkWithLightPacket(
nmsWorld.getChunkSource().getLightEngine(), levelChunk,
null, nmsWorld.getChunkSource().getLightEngine(),
null null,
); null
);
}
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
} finally {
NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
} }
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
}); });
} }

View File

@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@ -70,7 +71,7 @@ public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLeve
int x = pos.x; int x = pos.x;
int z = pos.z; int z = pos.z;
if (delay) { // we still need to send the block changes of that chunk if (delay) { // we still need to send the block changes of that chunk
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z); PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
} }
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
} }

View File

@ -1,175 +1,76 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import com.fastasyncworldedit.bukkit.adapter.Regenerator; import com.fastasyncworldedit.bukkit.adapter.Regenerator;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle; import com.mojang.serialization.Lifecycle;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.PaperweightGetBlocks;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.io.file.SafeFiles; import com.sk89q.worldedit.util.io.file.SafeFiles;
import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.RegenOptions;
import io.papermc.lib.PaperLib;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.Holder; 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.resources.ResourceKey;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.StaticCache2D; import net.minecraft.util.ProgressListener;
import net.minecraft.util.thread.ProcessorHandle;
import net.minecraft.util.thread.ProcessorMailbox;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome; 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.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.status.ChunkPyramid;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.WorldGenContext;
import net.minecraft.world.level.dimension.LevelStem; 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.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.WorldOptions; 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.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.PrimaryLevelData;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk; import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
import static net.minecraft.core.registries.Registries.BIOME; import static net.minecraft.core.registries.Registries.BIOME;
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> { public class PaperweightRegen extends Regenerator {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Field serverWorldsField; private static final Field serverWorldsField;
private static final Field paperConfigField; private static final Field paperConfigField;
private static final Field flatBedrockField;
private static final Field generatorSettingFlatField;
private static final Field generatorSettingBaseSupplierField; 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 { 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.NONE
); // structure refs: radius 8, but only writes to current chunk
chunkStati.put(ChunkStatus.BIOMES, Concurrency.NONE); // 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.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
chunkStati.put(
ChunkStatus.INITIALIZE_LIGHT,
Concurrency.FULL
); // initialize_light: radius 0
chunkStati.put(
ChunkStatus.LIGHT,
Concurrency.FULL
); // light: radius 1, but no writes to other chunks, only current chunk
chunkStati.put(ChunkStatus.SPAWN, Concurrency.NONE); // spawn: radius 0
try { try {
serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField = CraftServer.class.getDeclaredField("worlds");
serverWorldsField.setAccessible(true); serverWorldsField.setAccessible(true);
Field tmpPaperConfigField; Field tmpPaperConfigField;
Field tmpFlatBedrockField;
try { //only present on paper try { //only present on paper
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
tmpPaperConfigField.setAccessible(true); tmpPaperConfigField.setAccessible(true);
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
tmpFlatBedrockField.setAccessible(true);
} catch (Exception e) { } catch (Exception e) {
tmpPaperConfigField = null; tmpPaperConfigField = null;
tmpFlatBedrockField = null;
} }
paperConfigField = tmpPaperConfigField; paperConfigField = tmpPaperConfigField;
flatBedrockField = tmpFlatBedrockField;
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
"settings", "e")); "settings", "e"));
generatorSettingBaseSupplierField.setAccessible(true); 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", "w"));
generatorStructureStateField.setAccessible(true);
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
ringPositionsField.setAccessible(true);
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
Refraction.pickName("hasGeneratedPositions", "h")
);
hasGeneratedPositionsField.setAccessible(true);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -177,47 +78,37 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
//runtime //runtime
private ServerLevel originalServerWorld; private ServerLevel originalServerWorld;
private ServerChunkCache originalChunkProvider;
private ServerLevel freshWorld; private ServerLevel freshWorld;
private ServerChunkCache freshChunkProvider;
private LevelStorageSource.LevelStorageAccess session; private LevelStorageSource.LevelStorageAccess session;
private StructureTemplateManager structureTemplateManager;
private ThreadedLevelLightEngine threadedLevelLightEngine;
private ChunkGenerator chunkGenerator;
private WorldGenContext worldGenContext;
private Path tempDir; private Path tempDir;
private boolean generateFlatBedrock = false; public PaperweightRegen(
World originalBukkitWorld,
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { Region region,
Extent target,
RegenOptions options
) {
super(originalBukkitWorld, region, target, options); super(originalBukkitWorld, region, target, options);
if (PaperLib.isPaper()) { }
throw new UnsupportedOperationException("Regeneration currently not support on Paper due to the new generation system");
@Override
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
while (shouldKeepTicking.getAsBoolean()) {
if (!this.freshWorld.getChunkSource().pollTask()) {
return;
}
} }
} }
@Override @Override
protected boolean prepare() { protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); 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()); seed = options.getSeed().orElse(originalServerWorld.getSeed());
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
return true; return true;
} }
@Override @Override
@SuppressWarnings("unchecked")
protected boolean initNewWorld() throws Exception { protected boolean initNewWorld() throws Exception {
//world folder //world folder
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
@ -263,8 +154,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
session, session,
newWorldData, newWorldData,
originalServerWorld.dimension(), originalServerWorld.dimension(),
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() new LevelStem(
.getOrThrow(levelStemResourceKey), originalServerWorld.dimensionTypeRegistration(),
originalServerWorld.getChunkSource().getGenerator()
),
new RegenNoOpWorldLoadListener(), new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(), originalServerWorld.isDebug(),
seed, seed,
@ -281,20 +174,32 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
) : null; ) : null;
@Override
public void tick(@NotNull BooleanSupplier shouldKeepTicking) { //no ticking
}
@Override @Override
public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
if (options.hasBiomeType()) { if (options.hasBiomeType()) {
return singleBiome; return singleBiome;
} }
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ);
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
);
} }
@Override
public void save(
@Nullable final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled
) {
// noop, spigot
}
@Override
public void save(
@Nullable final ProgressListener progressListener,
final boolean flush,
final boolean savingDisabled,
final boolean close
) {
// noop, paper
}
}).get(); }).get();
freshWorld.noSave = true; freshWorld.noSave = true;
removeWorldFromWorldsMap(); removeWorldFromWorldsMap();
@ -302,97 +207,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
if (paperConfigField != null) { if (paperConfigField != null) {
paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); 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(noiseBasedChunkGenerator);
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, @NotNull 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);
this.worldGenContext = new WorldGenContext(
freshWorld,
chunkGenerator,
structureTemplateManager,
threadedLevelLightEngine,
originalChunkProvider.chunkMap.worldGenContext.mainThreadMailBox()
);
return true; return true;
} }
@ -407,7 +221,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
try { try {
Fawe.instance().getQueueHandler().sync(() -> { Fawe.instance().getQueueHandler().sync(() -> {
try { try {
freshChunkProvider.close(false); freshWorld.getChunkSource().getDataStorage().cache.clear();
freshWorld.getChunkSource().close(false);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -428,50 +243,9 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
} }
@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 @Override
protected IChunkCache<IChunkGet> initSourceQueueCache() { protected IChunkCache<IChunkGet> initSourceQueueCache() {
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
@Override
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
return getChunkAt(x, z);
}
};
} }
//util //util
@ -525,99 +299,4 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
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
@SuppressWarnings("unused") // compatibility with spigot
public boolean generateFlatBedrock() {
return generateFlatBedrock;
}
// no one will ever see the entities!
@Override
public @NotNull 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 ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FULL).getAccumulatedRadiusOf(chunkStatus);
}
@Override
public String name() {
return chunkStatus.toString();
}
@Override
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
ChunkAccess chunkAccess = accessibleChunks.get(accessibleChunks.size() / 2);
int chunkX = chunkAccess.getPos().x;
int chunkZ = chunkAccess.getPos().z;
getProtoChunkAt(chunkX, chunkZ);
StaticCache2D<GenerationChunkHolder> neighbours = StaticCache2D
.create(
chunkX,
chunkZ,
requiredNeighborChunkRadius(),
(final int nx, final int nz) -> new ChunkHolder(new ChunkPos(nx, nz),
ChunkHolderManager.MAX_TICKET_LEVEL,
freshWorld,
threadedLevelLightEngine,
null,
freshChunkProvider.chunkMap
)
);
return ChunkPyramid.GENERATION_PYRAMID.getStepTo(chunkStatus).apply(
worldGenContext,
neighbours,
chunkAccess
);
}
}
/**
* 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 @NotNull CompletableFuture<ChunkAccess> lightChunk(final @NotNull ChunkAccess chunk, final boolean excludeBlocks) {
return CompletableFuture.completedFuture(chunk);
}
}
} }

View File

@ -0,0 +1,67 @@
package com.fastasyncworldedit.bukkit;
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.bukkit.util.WorldUnloadedException;
import com.fastasyncworldedit.core.math.IntPair;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import org.bukkit.Bukkit;
import org.bukkit.World;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
public class FaweBukkitWorld extends BukkitWorld {
private static final Map<World, FaweBukkitWorld> CACHE = Collections.synchronizedMap(new WeakHashMap<>());
private final ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> SENDING_CHUNKS = new ConcurrentHashMap<>();
/**
* Construct the object.
*
* @param world the world
*/
private FaweBukkitWorld(final World world) {
super(world);
}
public static FaweBukkitWorld of(World world) {
return CACHE.compute(world, (__, val) -> {
if (val == null) {
return new FaweBukkitWorld(world);
}
val.updateReference();
return val;
});
}
public static FaweBukkitWorld of(String worldName) {
World world = Bukkit.getWorld(worldName);
if (world == null) {
throw new UnsupportedOperationException("Unable to find org.bukkit.World instance for " + worldName + ". Is it loaded?");
}
return of(world);
}
public static ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> getWorldSendingChunksMap(FaweBukkitWorld world) {
return world.SENDING_CHUNKS;
}
public static ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> getWorldSendingChunksMap(String worldName) {
return of(worldName).SENDING_CHUNKS;
}
private void updateReference() {
World world = getWorld();
World bukkitWorld = Bukkit.getWorld(worldNameRef);
if (bukkitWorld == null) {
throw new WorldUnloadedException(worldNameRef);
} else if (bukkitWorld != world) {
worldRef = new WeakReference<>(bukkitWorld);
}
}
}

View File

@ -1,12 +1,17 @@
package com.fastasyncworldedit.bukkit.adapter; package com.fastasyncworldedit.bukkit.adapter;
import com.fastasyncworldedit.bukkit.FaweBukkitWorld;
import com.fastasyncworldedit.core.FAWEPlatformAdapterImpl; import com.fastasyncworldedit.core.FAWEPlatformAdapterImpl;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Function; import java.util.function.Function;
public class NMSAdapter implements FAWEPlatformAdapterImpl { public class NMSAdapter implements FAWEPlatformAdapterImpl {
@ -140,4 +145,118 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl {
((BukkitGetBlocks) chunk).send(); ((BukkitGetBlocks) chunk).send();
} }
/**
* Atomically set the given chunk section to the chunk section array stored in the chunk, given the expected existing chunk
* section instance at the given layer position.
* <p>
* Acquires a (FAWE-implemented only) write-lock on the chunk packet lock, waiting if required before writing, then freeing
* the lock. Also sets a boolean to indicate a write is waiting and therefore reads should not occur.
* <p>
* Utilises ConcurrentHashMap#compute for easy synchronisation for all of the above. Only tryWriteLock is used in blocks
* synchronised using ConcurrentHashMap methods.
*
* @since TODO
*/
protected static <LevelChunkSection> boolean setSectionAtomic(
String worldName,
IntPair pair,
LevelChunkSection[] sections,
LevelChunkSection expected,
LevelChunkSection value,
int layer
) {
if (layer < 0 || layer >= sections.length) {
return false;
}
StampLockHolder holder = new StampLockHolder();
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
chunks.compute(pair, (k, lock) -> {
if (lock == null) {
lock = new ChunkSendLock();
} else if (lock.writeWaiting) {
throw new IllegalStateException("Attempting to write chunk section when write is already ongoing?!");
}
holder.stamp = lock.lock.tryWriteLock();
holder.chunkLock = lock;
lock.writeWaiting = true;
return lock;
});
try {
if (holder.stamp == 0) {
holder.stamp = holder.chunkLock.lock.writeLock();
}
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} finally {
chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
chunks.computeIfPresent(pair, (k, lock) -> {
if (lock != holder.chunkLock) {
throw new IllegalStateException("SENDING_CHUNKS stored lock does not equal lock attempted to be unlocked?!");
}
lock.lock.unlockWrite(holder.stamp);
lock.writeWaiting = false;
// Keep the lock, etc. in the map as we're going to be accessing again later when sending
return lock;
});
}
}
/**
* Called before sending a chunk packet, filling the given stamp and stampedLock arrays' zeroth indices if the chunk packet
* send should go ahead.
* <p>
* Chunk packets should be sent if both of the following are met:
* - There is no more than one current packet send ongoing
* - There is no chunk section "write" waiting or ongoing,
* which are determined by the number of readers currently locking the StampedLock (i.e. the number of sends), if the
* stamped lock is currently write-locked and if the boolean for waiting write is true.
* <p>
* Utilises ConcurrentHashMap#compute for easy synchronisation
*
* @since TODO
*/
protected static void beginChunkPacketSend(String worldName, IntPair pair, StampLockHolder stampedLock) {
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
chunks.compute(pair, (k, lock) -> {
if (lock == null) {
lock = new ChunkSendLock();
}
// Allow twice-read-locking, so if the packets have been created but not sent, we can queue another read
if (lock.writeWaiting || lock.lock.getReadLockCount() > 1 || lock.lock.isWriteLocked()) {
return lock;
}
stampedLock.stamp = lock.lock.readLock();
stampedLock.chunkLock = lock;
return lock;
});
}
/**
* Releases the read lock acquired when sending a chunk packet for a chunk
*
* @since TODO
*/
protected static void endChunkPacketSend(String worldName, IntPair pair, StampLockHolder lockHolder) {
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
chunks.computeIfPresent(pair, (k, lock) -> {
if (lock.lock != lockHolder.chunkLock.lock) {
throw new IllegalStateException("SENDING_CHUNKS stored lock does not equal lock attempted to be unlocked?!");
}
lock.lock.unlockRead(lockHolder.stamp);
// Do not continue to store the lock if we may not need it (i.e. chunk has been sent, may not be sent again)
return null;
});
}
public static final class StampLockHolder {
public long stamp;
public ChunkSendLock chunkLock = null;
}
public static final class ChunkSendLock {
public final StampedLock lock = new StampedLock();
public boolean writeWaiting = false;
}
} }

View File

@ -1,67 +1,32 @@
package com.fastasyncworldedit.bukkit.adapter; package com.fastasyncworldedit.bukkit.adapter;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent; import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.RegenOptions;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import jdk.jfr.Category;
import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.Name;
import org.apache.logging.log4j.Logger;
import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.WorldInfo; import org.bukkit.generator.WorldInfo;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.concurrent.TimeUnit;
import java.util.Objects; import java.util.function.BooleanSupplier;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.function.Function; import java.util.function.Function;
/** /**
* Represents an abstract regeneration handler. * Represents an abstract regeneration handler.
*
* @param <IChunkAccess> the type of the {@code IChunkAccess} of the current Minecraft implementation
* @param <ProtoChunk> the type of the {@code ProtoChunk} of the current Minecraft implementation
* @param <Chunk> the type of the {@code Chunk} of the current Minecraft implementation
* @param <ChunkStatus> the type of the {@code ChunkStatusWrapper} wrapping the {@code ChunkStatus} enum
*/ */
public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess, Chunk extends IChunkAccess, ChunkStatus extends Regenerator.ChunkStatusWrapper<IChunkAccess>> { public abstract class Regenerator {
private static final Logger LOGGER = LogManagerCompat.getLogger();
protected final org.bukkit.World originalBukkitWorld; protected final org.bukkit.World originalBukkitWorld;
protected final Region region; protected final Region region;
@ -69,13 +34,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
protected final RegenOptions options; protected final RegenOptions options;
//runtime //runtime
protected final LinkedHashMap<ChunkStatus, Concurrency> chunkStatuses = new LinkedHashMap<>(); // TODO (j21): use SequencedMap
private final Long2ObjectLinkedOpenHashMap<ProtoChunk> protoChunks = new Long2ObjectLinkedOpenHashMap<>();
private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
protected boolean generateConcurrent = true;
protected long seed; protected long seed;
private ExecutorService executor; protected SingleThreadQueueExtent source;
private SingleThreadQueueExtent source;
/** /**
* Initializes an abstract regeneration handler. * Initializes an abstract regeneration handler.
@ -92,15 +52,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
this.options = options; this.options = options;
} }
private static Random getChunkRandom(long worldSeed, int x, int z) {
Random random = new Random();
random.setSeed(worldSeed);
long xRand = random.nextLong() / 2L * 2L + 1L;
long zRand = random.nextLong() / 2L * 2L + 1L;
random.setSeed((long) x * xRand + (long) z * zRand ^ worldSeed);
return random;
}
/** /**
* Regenerates the selected {@code Region}. * Regenerates the selected {@code Region}.
* *
@ -122,16 +73,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
throw e; throw e;
} }
try {
if (!generate()) {
cleanup0();
return false;
}
} catch (Exception e) {
cleanup0();
throw e;
}
try { try {
copyToWorld(); copyToWorld();
} catch (Exception e) { } catch (Exception e) {
@ -144,193 +85,26 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
} }
/** /**
* Returns the {@code ProtoChunk} at the given chunk coordinates. * Execute tasks on the main thread during regen.
*
* @param x the chunk x coordinate
* @param z the chunk z coordinate
* @return the {@code ProtoChunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been initialized yet.
*/ */
protected ProtoChunk getProtoChunkAt(int x, int z) { protected abstract void runTasks(BooleanSupplier shouldKeepTicking);
return protoChunks.get(MathMan.pairInt(x, z));
}
/** private void createSource() {
* Returns the {@code Chunk} at the given chunk coordinates.
*
* @param x the chunk x coordinate
* @param z the chunk z coordinate
* @return the {@code Chunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been converted yet.
*/
protected Chunk getChunkAt(int x, int z) {
return chunks.get(MathMan.pairInt(x, z));
}
private boolean generate() throws Exception {
ThreadFactory factory = new ThreadFactoryBuilder()
.setNameFormat("FAWE Regenerator - %d")
.build();
if (generateConcurrent) {
//Using concurrent chunk generation
executor = Executors.newFixedThreadPool(Settings.settings().QUEUE.PARALLEL_THREADS, factory);
} else { // else using sequential chunk generation, concurrent not supported
executor = Executors.newSingleThreadExecutor(factory);
}
//TODO: can we get that required radius down without affecting chunk generation (e.g. strucures, features, ...)?
//for now it is working well and fast, if we are bored in the future we could do the research (a lot of it) to reduce the border radius
// to get the chunks we need to generate in the nth chunk status, we need to know how many chunks
// we need to generate in the n + 1 th chunk status. Summing up the margin solves that
LinkedHashMap<ChunkStatus, long[]> chunkCoordsForChunkStatus = new LinkedHashMap<>();
int borderSum = 1;
// TODO (j21): use SequencedMap#sequencedKeySet().reversed()
final List<ChunkStatus> reversedKeys = Lists.reverse(new ArrayList<>(chunkStatuses.keySet()));
for (final ChunkStatus status : reversedKeys) {
chunkCoordsForChunkStatus.put(status, getChunkCoordsRegen(region, borderSum));
borderSum += status.requiredNeighborChunkRadius();
}
//create chunks
// TODO (j21): use SequencedMap#firstEntry().getKey()
for (long xz : chunkCoordsForChunkStatus.get(chunkStatuses.keySet().iterator().next())) {
ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz));
protoChunks.put(xz, chunk);
}
// a memory-efficient, lightweight "list" that calculates index -> ChunkAccess
// as needed when accessed
class LazyChunkList extends AbstractList<IChunkAccess> {
private final int size;
private final int minX;
private final int minZ;
private final int sizeSqrt;
LazyChunkList(int radius, int centerX, int centerZ) {
this.sizeSqrt = radius + 1 + radius; // length of one side
this.size = this.sizeSqrt * this.sizeSqrt;
this.minX = centerX - radius;
this.minZ = centerZ - radius;
}
@Override
public IChunkAccess get(final int index) {
Objects.checkIndex(index, size);
int absX = (index % sizeSqrt) + minX;
int absZ = (index / sizeSqrt) + minZ;
return protoChunks.get(MathMan.pairInt(absX, absZ));
}
@Override
public int size() {
return size;
}
}
@Label("Regeneration")
@Category("FAWE")
@Name("fawe.regen")
class RegenerationEvent extends Event {
private String chunkStatus;
private int chunksToProcess;
}
//run generation tasks excluding FULL chunk status
for (Map.Entry<ChunkStatus, Concurrency> entry : chunkStatuses.entrySet()) {
ChunkStatus chunkStatus = entry.getKey();
final RegenerationEvent event = new RegenerationEvent();
event.begin();
event.chunkStatus = chunkStatus.name();
int radius = Math.max(1, chunkStatus.requiredNeighborChunkRadius0());
long[] coords = chunkCoordsForChunkStatus.get(chunkStatus);
event.chunksToProcess = coords.length;
if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) {
SequentialTasks<ConcurrentTasks<LongList>> tasks = getChunkStatusTaskRows(coords, radius);
for (ConcurrentTasks<LongList> para : tasks) {
List<Runnable> scheduled = new ArrayList<>(tasks.size());
for (LongList row : para) {
scheduled.add(() -> {
for (long xz : row) {
chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz),
MathMan.unpairIntY(xz)));
}
});
}
runAndWait(scheduled);
}
} else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) {
// every chunk can be processed individually
List<Runnable> scheduled = new ArrayList<>(coords.length);
for (long xz : coords) {
scheduled.add(() -> chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz),
MathMan.unpairIntY(xz))));
}
runAndWait(scheduled);
} else { // Concurrency.NONE or generateConcurrent == false
// run sequential but submit to different thread
// running regen on the main thread otherwise triggers async-only events on the main thread
executor.submit(() -> {
for (long xz : coords) {
chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz),
MathMan.unpairIntY(xz)));
}
}).get(); // wait until finished this step
}
event.commit();
}
//convert to proper chunks
// TODO (j21): use SequencedMap#firstEntry().getValue()
for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) {
ProtoChunk proto = protoChunks.get(xz);
chunks.put(xz, createChunk(proto));
}
//final chunkstatus
ChunkStatus FULL = getFullChunkStatus();
// TODO (j21): use SequencedMap#firstEntry().getValue()
for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) { //FULL.requiredNeighbourChunkRadius() == 0!
Chunk chunk = chunks.get(xz);
FULL.processChunkSave(xz, List.of(chunk));
}
//populate
List<BlockPopulator> populators = getBlockPopulators();
// TODO (j21): use SequencedMap#firstEntry().getValue()
for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) {
int x = MathMan.unpairIntX(xz);
int z = MathMan.unpairIntY(xz);
//prepare chunk seed
Random random = getChunkRandom(seed, x, z);
//actually populate
Chunk c = chunks.get(xz);
populators.forEach(pop -> {
populate(c, random, pop);
});
}
source = new SingleThreadQueueExtent( source = new SingleThreadQueueExtent(
BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMinHeight() : 0, BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMinHeight() : 0,
BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMaxHeight() : 256 BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMaxHeight() : 256
); );
source.init(target, initSourceQueueCache(), null); source.init(target, initSourceQueueCache(), null);
return true;
}
private void runAndWait(final List<Runnable> tasks) {
try {
List<Future<?>> futures = new ArrayList<>();
tasks.forEach(task -> futures.add(executor.submit(task)));
for (Future<?> future : futures) {
future.get();
}
} catch (Exception e) {
LOGGER.catching(e);
}
} }
private void copyToWorld() { private void copyToWorld() {
createSource();
final long timeoutPerTick = TimeUnit.MILLISECONDS.toNanos(10);
int taskId = TaskManager.taskManager().repeat(() -> {
final long startTime = System.nanoTime();
runTasks(() -> System.nanoTime() - startTime < timeoutPerTick);
}, 1);
//Setting Blocks //Setting Blocks
boolean genbiomes = options.shouldRegenBiomes(); boolean genbiomes = options.shouldRegenBiomes();
boolean hasBiome = options.hasBiomeType(); boolean hasBiome = options.hasBiomeType();
@ -343,6 +117,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
} else if (genbiomes) { } else if (genbiomes) {
target.setBlocks(region, new WithBiomePlacementPattern(vec -> source.getBiome(vec))); target.setBlocks(region, new WithBiomePlacementPattern(vec -> source.getBiome(vec)));
} }
TaskManager.taskManager().cancel(taskId);
} }
private class PlacementPattern implements Pattern { private class PlacementPattern implements Pattern {
@ -382,9 +157,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
//functions to be implemented by sub class //functions to be implemented by sub class
private void cleanup0() { private void cleanup0() {
if (executor != null) {
executor.shutdownNow();
}
cleanup(); cleanup();
} }
@ -416,47 +188,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
*/ */
protected abstract void cleanup(); protected abstract void cleanup();
/**
* Implement the initialization of a {@code ProtoChunk} here.
*
* @param x the x coorinate of the {@code ProtoChunk} to create
* @param z the z coorinate of the {@code ProtoChunk} to create
* @return an initialized {@code ProtoChunk}
*/
protected abstract ProtoChunk createProtoChunk(int x, int z);
/**
* Implement the convertion of a {@code ProtoChunk} to a {@code Chunk} here.
*
* @param protoChunk the {@code ProtoChunk} to be converted to a {@code Chunk}
* @return the converted {@code Chunk}
*/
protected abstract Chunk createChunk(ProtoChunk protoChunk);
/**
* Return the {@code ChunkStatus.FULL} here.
* ChunkStatus.FULL is the last step of vanilla chunk generation.
*
* @return {@code ChunkStatus.FULL}
*/
protected abstract ChunkStatus getFullChunkStatus();
/**
* Return a list of {@code BlockPopulator} used to populate the original world here.
*
* @return {@code ChunkStatus.FULL}
*/
protected abstract List<BlockPopulator> getBlockPopulators();
/**
* Implement the population of the {@code Chunk} with the given chunk random and {@code BlockPopulator} here.
*
* @param chunk the {@code Chunk} to populate
* @param random the chunk random to use for population
* @param pop the {@code BlockPopulator} to use
*/
protected abstract void populate(Chunk chunk, Random random, BlockPopulator pop);
/** /**
* Implement the initialization an {@code IChunkCache<IChunkGet>} here. Use will need the {@code getChunkAt} function * Implement the initialization an {@code IChunkCache<IChunkGet>} here. Use will need the {@code getChunkAt} function
* *
@ -464,106 +195,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
*/ */
protected abstract IChunkCache<IChunkGet> initSourceQueueCache(); protected abstract IChunkCache<IChunkGet> initSourceQueueCache();
//algorithms
private long[] getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks
BlockVector3 oldMin = region.getMinimumPoint();
BlockVector3 newMin = BlockVector3.at(
(oldMin.x() >> 4 << 4) - border * 16,
oldMin.y(),
(oldMin.z() >> 4 << 4) - border * 16
);
BlockVector3 oldMax = region.getMaximumPoint();
BlockVector3 newMax = BlockVector3.at(
(oldMax.x() >> 4 << 4) + (border + 1) * 16 - 1,
oldMax.y(),
(oldMax.z() >> 4 << 4) + (border + 1) * 16 - 1
);
Region adjustedRegion = new CuboidRegion(newMin, newMax);
return adjustedRegion.getChunks().stream()
.sorted(Comparator
.comparingInt(BlockVector2::z)
.thenComparingInt(BlockVector2::x)) //needed for RegionLimitedWorldAccess
.mapToLong(c -> MathMan.pairInt(c.x(), c.z()))
.toArray();
}
/**
* Creates a list of chunkcoord rows that may be executed concurrently
*
* @param allCoords the coords that should be sorted into rows, must be sorted by z and x
* @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to concurrently (ChunkStatus
* .requiredNeighborRadius)
* @return a list of chunkcoords rows that may be executed concurrently
*/
private SequentialTasks<ConcurrentTasks<LongList>> getChunkStatusTaskRows(
long[] allCoords,
int requiredNeighborChunkRadius
) {
int requiredNeighbors = Math.max(0, requiredNeighborChunkRadius);
final int coordsCount = allCoords.length;
long first = coordsCount == 0 ? 0 : allCoords[0];
long last = coordsCount == 0 ? 0 : allCoords[coordsCount - 1];
int minX = MathMan.unpairIntX(first);
int maxX = MathMan.unpairIntX(last);
int minZ = MathMan.unpairIntY(first);
int maxZ = MathMan.unpairIntY(last);
SequentialTasks<ConcurrentTasks<LongList>> tasks;
if (maxZ - minZ > maxX - minX) {
int numlists = Math.min(requiredNeighbors * 2 + 1, maxX - minX + 1);
Int2ObjectOpenHashMap<LongList> byX = new Int2ObjectOpenHashMap<>();
int expectedListLength = (coordsCount + 1) / (maxX - minX);
//init lists
for (int i = minX; i <= maxX; i++) {
byX.put(i, new LongArrayList(expectedListLength));
}
//sort into lists by x coord
for (long allCoord : allCoords) {
byX.get(MathMan.unpairIntX(allCoord)).add(allCoord);
}
//create parallel tasks
tasks = new SequentialTasks<>(numlists);
for (int offset = 0; offset < numlists; offset++) {
ConcurrentTasks<LongList> para = new ConcurrentTasks<>((maxZ - minZ + 1) / numlists + 1);
for (int i = 0; minX + i * numlists + offset <= maxX; i++) {
para.add(byX.get(minX + i * numlists + offset));
}
tasks.add(para);
}
} else {
int numlists = Math.min(requiredNeighbors * 2 + 1, maxZ - minZ + 1);
Int2ObjectOpenHashMap<LongList> byZ = new Int2ObjectOpenHashMap<>();
int expectedListLength = (coordsCount + 1) / (maxZ - minZ + 2);
//init lists
for (int i = minZ; i <= maxZ; i++) {
byZ.put(i, new LongArrayList(expectedListLength));
}
//sort into lists by x coord
for (long allCoord : allCoords) {
byZ.get(MathMan.unpairIntY(allCoord)).add(allCoord);
}
//create parallel tasks
tasks = new SequentialTasks<>(numlists);
for (int offset = 0; offset < numlists; offset++) {
ConcurrentTasks<LongList> para = new ConcurrentTasks<>((maxX - minX + 1) / numlists + 1);
for (int i = 0; minZ + i * numlists + offset <= maxZ; i++) {
para.add(byZ.get(minZ + i * numlists + offset));
}
tasks.add(para);
}
}
return tasks;
}
protected BiomeProvider getBiomeProvider() { protected BiomeProvider getBiomeProvider() {
if (options.hasBiomeType()) { if (options.hasBiomeType()) {
return new SingleBiomeProvider(); return new SingleBiomeProvider();
@ -579,103 +210,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
NONE NONE
} }
/**
* This class is used to wrap the ChunkStatus of the current Minecraft implementation and as the implementation to execute a chunk generation step.
*
* @param <IChunkAccess> the IChunkAccess class of the current Minecraft implementation
*/
public static abstract class ChunkStatusWrapper<IChunkAccess> {
/**
* Return the required neighbor chunk radius the wrapped {@code ChunkStatus} requires.
*
* @return the radius of required neighbor chunks
*/
public abstract int requiredNeighborChunkRadius();
int requiredNeighborChunkRadius0() {
return Math.max(0, requiredNeighborChunkRadius());
}
/**
* Return the name of the wrapped {@code ChunkStatus}.
*
* @return the radius of required neighbor chunks
*/
public abstract String name();
/**
* Return the name of the wrapped {@code ChunkStatus}.
*
* @param accessibleChunks a list of chunks that will be used during the execution of the wrapped {@code ChunkStatus}.
* This list is order in the correct order required by the {@code ChunkStatus}, unless Mojang suddenly decides to do things differently.
*/
public abstract CompletableFuture<?> processChunk(List<IChunkAccess> accessibleChunks);
void processChunkSave(long xz, List<IChunkAccess> accessibleChunks) {
try {
processChunk(accessibleChunks).get();
} catch (Exception e) {
LOGGER.error("Error while running {} on chunk {}/{}",
name(), MathMan.unpairIntX(xz), MathMan.unpairIntY(xz), e);
}
}
@Override
public String toString() {
return name();
}
}
public static class SequentialTasks<T> extends Tasks<T> {
public SequentialTasks(int expectedSize) {
super(expectedSize);
}
}
public static class ConcurrentTasks<T> extends Tasks<T> {
public ConcurrentTasks(int expectedSize) {
super(expectedSize);
}
}
public static class Tasks<T> implements Iterable<T> {
private final List<T> tasks;
public Tasks(int expectedSize) {
tasks = new ArrayList<>(expectedSize);
}
public void add(T task) {
tasks.add(task);
}
public List<T> list() {
return tasks;
}
public int size() {
return tasks.size();
}
@Override
public Iterator<T> iterator() {
return tasks.iterator();
}
@Override
public String toString() {
return tasks.toString();
}
}
public class SingleBiomeProvider extends BiomeProvider { public class SingleBiomeProvider extends BiomeProvider {
private final org.bukkit.block.Biome biome = BukkitAdapter.adapt(options.getBiomeType()); private final org.bukkit.block.Biome biome = BukkitAdapter.adapt(options.getBiomeType());

View File

@ -3,7 +3,9 @@ package com.fastasyncworldedit.bukkit.regions;
import com.fastasyncworldedit.core.regions.FaweMask; import com.fastasyncworldedit.core.regions.FaweMask;
import com.griefdefender.api.GriefDefender; import com.griefdefender.api.GriefDefender;
import com.griefdefender.api.claim.Claim; import com.griefdefender.api.claim.Claim;
import com.griefdefender.api.claim.ClaimManager;
import com.griefdefender.api.claim.TrustTypes; import com.griefdefender.api.claim.TrustTypes;
import com.griefdefender.lib.flowpowered.math.vector.Vector3i;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
@ -17,8 +19,8 @@ public class GriefDefenderFeature extends BukkitMaskManager implements Listener
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
public GriefDefenderFeature(final Plugin GriefDefenderPlugin) { public GriefDefenderFeature(final Plugin plugin) {
super(GriefDefenderPlugin.getName()); super(plugin.getName());
LOGGER.info("Plugin 'GriefDefender' found. Using it now."); LOGGER.info("Plugin 'GriefDefender' found. Using it now.");
} }
@ -44,9 +46,14 @@ public class GriefDefenderFeature extends BukkitMaskManager implements Listener
); );
return new FaweMask(new CuboidRegion(pos1, pos2)) { return new FaweMask(new CuboidRegion(pos1, pos2)) {
private final int[] bounds = new int[]{
pos1.x(), pos1.y(), pos1.z(),
pos2.x(), pos2.y(), pos2.z()
};
@Override @Override
public boolean isValid(com.sk89q.worldedit.entity.Player wePlayer, MaskType type) { public boolean isValid(com.sk89q.worldedit.entity.Player wePlayer, MaskType type) {
return isAllowed(player, claim, type); return validateClaimAgainstCache(claim, bounds) && isAllowed(player, claim, type);
} }
}; };
} }
@ -54,4 +61,20 @@ public class GriefDefenderFeature extends BukkitMaskManager implements Listener
return null; return null;
} }
private static boolean validateClaimAgainstCache(Claim claim, int[] bounds) {
Vector3i min = claim.getLesserBoundaryCorner();
Vector3i max = claim.getGreaterBoundaryCorner();
if (min.getX() != bounds[0] || min.getY() != bounds[1] || min.getZ() != bounds[2]) {
return false;
}
if (max.getX() != bounds[3] || max.getY() != bounds[4] || max.getZ() != bounds[5]) {
return false;
}
final ClaimManager manager = GriefDefender.getCore().getClaimManager(claim.getWorldUniqueId());
if (manager == null) {
return false;
}
return manager.getClaimByUUID(claim.getUniqueId()) != null;
}
} }

View File

@ -24,12 +24,12 @@ import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
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.TaskManager; import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItem;
@ -118,9 +118,9 @@ public class BukkitWorld extends AbstractWorld {
HAS_MIN_Y = temp; HAS_MIN_Y = temp;
} }
private WeakReference<World> worldRef; protected WeakReference<World> worldRef;
//FAWE start //FAWE start
private final String worldNameRef; protected final String worldNameRef;
//FAWE end //FAWE end
private final WorldNativeAccess<?, ?, ?> worldNativeAccess; private final WorldNativeAccess<?, ?, ?> worldNativeAccess;
@ -665,7 +665,7 @@ public class BukkitWorld extends AbstractWorld {
} }
@Override @Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { public boolean tile(int x, int y, int z, FaweCompoundTag tile) throws WorldEditException {
return false; return false;
} }

View File

@ -165,9 +165,11 @@ public class BukkitImplLoader {
* @throws AdapterLoadException thrown if no adapter could be found * @throws AdapterLoadException thrown if no adapter could be found
*/ */
public BukkitImplAdapter loadAdapter() throws AdapterLoadException { public BukkitImplAdapter loadAdapter() throws AdapterLoadException {
// FAWE - do not initialize classes on lookup
final ClassLoader classLoader = this.getClass().getClassLoader();
for (String className : adapterCandidates) { for (String className : adapterCandidates) {
try { try {
Class<?> cls = Class.forName(className); Class<?> cls = Class.forName(className, false, classLoader);
if (cls.isSynthetic()) { if (cls.isSynthetic()) {
continue; continue;
} }

View File

@ -19,10 +19,10 @@
package com.sk89q.worldedit.cli.schematic; package com.sk89q.worldedit.cli.schematic;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
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.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
@ -193,7 +193,7 @@ public class ClipboardWorld extends AbstractWorld implements Clipboard, CLIWorld
} }
@Override @Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { public boolean tile(int x, int y, int z, FaweCompoundTag tile) throws WorldEditException {
return false; return false;
} }

View File

@ -365,6 +365,18 @@ public class Fawe {
Settings.settings().QUEUE.PARALLEL_THREADS Settings.settings().QUEUE.PARALLEL_THREADS
); );
} }
if (Settings.settings().HISTORY.DELETE_DISK_ON_LOGOUT && Settings.settings().HISTORY.USE_DATABASE) {
LOGGER.warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
LOGGER.warn("!!! !!!");
LOGGER.warn("!!! Using history database whilst deleting disk history! !!!");
LOGGER.warn("!!! You will not be able to rollback edits after a user logs !!!");
LOGGER.warn("!!! out, recommended to disable delete-disk-on-logout if you !!!");
LOGGER.warn("!!! you want to have full history rollback functionality. !!!");
LOGGER.warn("!!! Disable use-database if you do not need to have rollback !!!");
LOGGER.warn("!!! functionality and wish to disable this warning. !!!");
LOGGER.warn("!!! !!!");
LOGGER.warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
try { try {
byte[] in = new byte[0]; byte[] in = new byte[0];
byte[] compressed = LZ4Factory.fastestJavaInstance().fastCompressor().compress(in); byte[] compressed = LZ4Factory.fastestJavaInstance().fastCompressor().compress(in);

View File

@ -192,17 +192,22 @@ public enum FaweCache implements Trimable {
Type.OUTSIDE_REGION Type.OUTSIDE_REGION
); );
public static final FaweException MAX_CHECKS = new FaweException( public static final FaweException MAX_CHECKS = new FaweException(
Caption.of("fawe.cancel.reason.max" + ".checks"), Caption.of("fawe.cancel.reason.max.checks"),
Type.MAX_CHECKS,
true
);
public static final FaweException MAX_FAILS = new FaweException(
Caption.of("fawe.cancel.reason.max.fails"),
Type.MAX_CHECKS, Type.MAX_CHECKS,
true true
); );
public static final FaweException MAX_CHANGES = new FaweException( public static final FaweException MAX_CHANGES = new FaweException(
Caption.of("fawe.cancel.reason.max" + ".changes"), Caption.of("fawe.cancel.reason.max.changes"),
Type.MAX_CHANGES, Type.MAX_CHANGES,
false false
); );
public static final FaweException LOW_MEMORY = new FaweException( public static final FaweException LOW_MEMORY = new FaweException(
Caption.of("fawe.cancel.reason.low" + ".memory"), Caption.of("fawe.cancel.reason.low.memory"),
Type.LOW_MEMORY, Type.LOW_MEMORY,
false false
); );

View File

@ -123,29 +123,31 @@ public class Settings extends Config {
limit.MAX_ACTIONS, limit.MAX_ACTIONS,
newLimit.MAX_ACTIONS != -1 ? newLimit.MAX_ACTIONS : Integer.MAX_VALUE newLimit.MAX_ACTIONS != -1 ? newLimit.MAX_ACTIONS : Integer.MAX_VALUE
); );
limit.MAX_CHANGES = Math.max( limit.MAX_CHANGES.set(Math.max(
limit.MAX_CHANGES, limit.MAX_CHANGES.get(),
newLimit.MAX_CHANGES != -1 ? newLimit.MAX_CHANGES : Long.MAX_VALUE newLimit.MAX_CHANGES != -1 ? newLimit.MAX_CHANGES : Long.MAX_VALUE
); ));
limit.MAX_BLOCKSTATES = Math.max( limit.MAX_BLOCKSTATES.set(Math.max(
limit.MAX_BLOCKSTATES, limit.MAX_BLOCKSTATES.get(),
newLimit.MAX_BLOCKSTATES != -1 ? newLimit.MAX_BLOCKSTATES : Integer.MAX_VALUE newLimit.MAX_BLOCKSTATES != -1 ? newLimit.MAX_BLOCKSTATES : Integer.MAX_VALUE
); ));
limit.MAX_CHECKS = Math.max( limit.MAX_CHECKS.set(Math.max(
limit.MAX_CHECKS, limit.MAX_CHECKS.get(),
newLimit.MAX_CHECKS != -1 ? newLimit.MAX_CHECKS : Long.MAX_VALUE newLimit.MAX_CHECKS != -1 ? newLimit.MAX_CHECKS : Long.MAX_VALUE
); ));
limit.MAX_ENTITIES = Math.max( limit.MAX_ENTITIES.set(Math.max(
limit.MAX_ENTITIES, limit.MAX_ENTITIES.get(),
newLimit.MAX_ENTITIES != -1 ? newLimit.MAX_ENTITIES : Integer.MAX_VALUE newLimit.MAX_ENTITIES != -1 ? newLimit.MAX_ENTITIES : Integer.MAX_VALUE
); ));
limit.MAX_FAILS = Math.max(limit.MAX_FAILS, newLimit.MAX_FAILS != -1 ? newLimit.MAX_FAILS : Integer.MAX_VALUE); limit.MAX_FAILS.set(Math.max(
limit.MAX_ITERATIONS = Math.max( limit.MAX_FAILS.get(),
limit.MAX_ITERATIONS, newLimit.MAX_ITERATIONS != -1 ? newLimit.MAX_ITERATIONS : Integer.MAX_VALUE); newLimit.MAX_FAILS != -1 ? newLimit.MAX_FAILS : Integer.MAX_VALUE
limit.MAX_RADIUS = Math.max( ));
limit.MAX_RADIUS, limit.MAX_ITERATIONS.set(Math.max(
newLimit.MAX_RADIUS != -1 ? newLimit.MAX_RADIUS : Integer.MAX_VALUE limit.MAX_ITERATIONS.get(),
); newLimit.MAX_ITERATIONS != -1 ? newLimit.MAX_ITERATIONS : Integer.MAX_VALUE
));
limit.MAX_RADIUS = Math.max(limit.MAX_RADIUS, newLimit.MAX_RADIUS != -1 ? newLimit.MAX_RADIUS : Integer.MAX_VALUE);
limit.MAX_SUPER_PICKAXE_SIZE = Math.max( limit.MAX_SUPER_PICKAXE_SIZE = Math.max(
limit.MAX_SUPER_PICKAXE_SIZE, limit.MAX_SUPER_PICKAXE_SIZE,
newLimit.MAX_SUPER_PICKAXE_SIZE != -1 ? newLimit.MAX_SUPER_PICKAXE_SIZE : Integer.MAX_VALUE newLimit.MAX_SUPER_PICKAXE_SIZE != -1 ? newLimit.MAX_SUPER_PICKAXE_SIZE : Integer.MAX_VALUE
@ -622,6 +624,13 @@ public class Settings extends Config {
}) })
public static class EXPERIMENTAL { public static class EXPERIMENTAL {
@Comment({
"Undo operation batch size",
" - The size defines the number of changes read at once.",
" - Larger numbers might reduce overhead but increase latency for edits with only few changes.",
" - 0 means undo operations are not batched."})
public int UNDO_BATCH_SIZE = 128;
@Comment({ @Comment({
"[UNSAFE] Directly modify the region files. (OBSOLETE - USE ANVIL COMMANDS)", "[UNSAFE] Directly modify the region files. (OBSOLETE - USE ANVIL COMMANDS)",
" - IMPROPER USE CAN CAUSE WORLD CORRUPTION!", " - IMPROPER USE CAN CAUSE WORLD CORRUPTION!",
@ -668,6 +677,11 @@ public class Settings extends Config {
}) })
public boolean ALLOW_TICK_FLUIDS = false; public boolean ALLOW_TICK_FLUIDS = false;
@Comment({
"Whether FAWE should use the incubator Vector API to accelerate some operations"
})
public boolean USE_VECTOR_API = false;
} }
@Comment({"Web/HTTP connection related settings"}) @Comment({"Web/HTTP connection related settings"})

View File

@ -19,9 +19,9 @@ public class AdjacentMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); return worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream();
} else if (index == 1 || index == 2) { } else if (index == 1 || index == 2) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput); return SuggestionHelper.suggestPositiveDoubles(argumentInput);
} }

View File

@ -22,7 +22,7 @@ public class AngleMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0 || index == 1) { if (index == 0 || index == 1) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d"));
} else if (index > 1 && index <= 1 + flags.length) { } else if (index > 1 && index <= 1 + flags.length) {

View File

@ -18,9 +18,9 @@ public class BesideMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(final String argumentInput, final int index) { protected Stream<String> getSuggestions(final String argumentInput, final int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); return worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream();
} else if (index == 1 || index == 2) { } else if (index == 1 || index == 2) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput); return SuggestionHelper.suggestPositiveDoubles(argumentInput);
} }

View File

@ -22,7 +22,7 @@ public class ExtremaMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0 || index == 1) { if (index == 0 || index == 1) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d"));
} else if (index > 1 && index <= 1 + flags.length) { } else if (index > 1 && index <= 1 + flags.length) {

View File

@ -22,7 +22,7 @@ public class ROCAngleMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0 || index == 1) { if (index == 0 || index == 1) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d"));
} else if (index > 1 && index <= 1 + flags.length) { } else if (index > 1 && index <= 1 + flags.length) {

View File

@ -18,7 +18,7 @@ public class RadiusMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0 || index == 1) { if (index == 0 || index == 1) {
return SuggestionHelper.suggestPositiveIntegers(argumentInput); return SuggestionHelper.suggestPositiveIntegers(argumentInput);
} }

View File

@ -24,12 +24,12 @@ public class RichOffsetMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index < 3) { if (index < 3) {
return SuggestionHelper.suggestPositiveIntegers(argumentInput); return SuggestionHelper.suggestPositiveIntegers(argumentInput);
} }
if (index == 3) { if (index == 3) {
return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); return worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -20,7 +20,7 @@ public class SimplexMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index < 3) { if (index < 3) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput); return SuggestionHelper.suggestPositiveDoubles(argumentInput);
} }

View File

@ -23,7 +23,7 @@ public class SurfaceAngleMaskParser extends RichParser<Mask> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index <= 2) { if (index <= 2) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput); return SuggestionHelper.suggestPositiveDoubles(argumentInput);
} }

View File

@ -25,7 +25,7 @@ public class AngleColorPatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index != 0) { if (index != 0) {
return Stream.empty(); return Stream.empty();
} }

View File

@ -25,7 +25,7 @@ public class AverageColorPatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index > 4) { if (index > 4) {
return Stream.empty(); return Stream.empty();
} }

View File

@ -54,7 +54,7 @@ public class BiomePatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return BiomeType.REGISTRY.getSuggestions(argumentInput); return BiomeType.REGISTRY.getSuggestions(argumentInput);
} }

View File

@ -26,9 +26,9 @@ public class BufferedPattern2DParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -26,9 +26,9 @@ public class BufferedPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -26,7 +26,7 @@ public class ColorPatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index > 4) { if (index > 4) {
return Stream.empty(); return Stream.empty();
} }

View File

@ -25,7 +25,7 @@ public class DesaturatePatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput); return SuggestionHelper.suggestPositiveDoubles(argumentInput);
} }

View File

@ -28,9 +28,9 @@ public class Linear2DPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
return switch (index) { return switch (index) {
case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
case 1, 2 -> SuggestionHelper.suggestPositiveIntegers(argumentInput); case 1, 2 -> SuggestionHelper.suggestPositiveIntegers(argumentInput);
default -> Stream.empty(); default -> Stream.empty();
}; };

View File

@ -28,9 +28,9 @@ public class Linear3DPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
return switch (index) { return switch (index) {
case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput); case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput);
default -> Stream.empty(); default -> Stream.empty();
}; };

View File

@ -28,9 +28,9 @@ public class LinearPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
return switch (index) { return switch (index) {
case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput); case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput);
default -> Stream.empty(); default -> Stream.empty();
}; };

View File

@ -25,10 +25,10 @@ public class MaskedPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
return switch (index) { return switch (index) {
case 0 -> this.worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); case 0 -> this.worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream();
case 1, 2 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); case 1, 2 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
default -> Stream.empty(); default -> Stream.empty();
}; };
} }

View File

@ -24,9 +24,9 @@ public class NoXPatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -24,9 +24,9 @@ public class NoYPatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -24,9 +24,9 @@ public class NoZPatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -36,12 +36,12 @@ public abstract class NoisePatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput); return SuggestionHelper.suggestPositiveDoubles(argumentInput);
} }
if (index == 1) { if (index == 1) {
return worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); return worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -25,9 +25,9 @@ public class OffsetPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
return switch (index) { return switch (index) {
case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput); case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput);
default -> Stream.empty(); default -> Stream.empty();
}; };

View File

@ -4,6 +4,7 @@ import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.extension.factory.parser.RichParser; import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
import com.fastasyncworldedit.core.function.pattern.RandomFullClipboardPattern; import com.fastasyncworldedit.core.function.pattern.RandomFullClipboardPattern;
import com.google.common.base.Function;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.InputParseException;
@ -33,7 +34,7 @@ public class RandomFullClipboardPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
switch (index) { switch (index) {
case 0: case 0:
if (argumentInput.equals("#") || argumentInput.equals("#c")) { if (argumentInput.equals("#") || argumentInput.equals("#c")) {

View File

@ -25,9 +25,9 @@ public class RandomOffsetPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
return switch (index) { return switch (index) {
case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput); case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput);
default -> Stream.empty(); default -> Stream.empty();
}; };

View File

@ -24,9 +24,9 @@ public class RelativePatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -25,7 +25,7 @@ public class SaturatePatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index > 3) { if (index > 3) {
return Stream.empty(); return Stream.empty();
} }

View File

@ -25,9 +25,9 @@ public class SolidRandomOffsetPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
return switch (index) { return switch (index) {
case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput); case 1, 2, 3 -> SuggestionHelper.suggestPositiveIntegers(argumentInput);
default -> Stream.empty(); default -> Stream.empty();
}; };

View File

@ -25,9 +25,9 @@ public class SurfaceRandomOffsetPatternParser extends RichParser<Pattern> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
return switch (index) { return switch (index) {
case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); case 0 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
case 1 -> SuggestionHelper.suggestPositiveIntegers(argumentInput); case 1 -> SuggestionHelper.suggestPositiveIntegers(argumentInput);
default -> Stream.empty(); default -> Stream.empty();
}; };

View File

@ -28,7 +28,7 @@ public class TypeSwapPatternParser extends RichParser<Pattern> {
} }
@Override @Override
public Stream<String> getSuggestions(String argumentInput, int index) { public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index > 2) { if (index > 2) {
return Stream.empty(); return Stream.empty();
} }

View File

@ -23,9 +23,9 @@ public class Linear3DTransformParser extends RichParser<ResettableExtent> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -23,9 +23,9 @@ public class LinearTransformParser extends RichParser<ResettableExtent> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -26,11 +26,11 @@ public class OffsetTransformParser extends RichParser<ResettableExtent> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index < 3) { if (index < 3) {
return SuggestionHelper.suggestPositiveIntegers(argumentInput); return SuggestionHelper.suggestPositiveIntegers(argumentInput);
} else if (index == 3) { } else if (index == 3) {
return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -24,11 +24,11 @@ public class PatternTransformParser extends RichParser<ResettableExtent> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index == 0) { if (index == 0) {
return worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); return worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream();
} else if (index == 1) { } else if (index == 1) {
return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -25,12 +25,12 @@ public class RotateTransformParser extends RichParser<ResettableExtent> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index < 3) { if (index < 3) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput); return SuggestionHelper.suggestPositiveDoubles(argumentInput);
} }
if (index == 3) { if (index == 3) {
return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -24,11 +24,11 @@ public class ScaleTransformParser extends RichParser<ResettableExtent> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index < 3) { if (index < 3) {
return SuggestionHelper.suggestPositiveDoubles(argumentInput); return SuggestionHelper.suggestPositiveDoubles(argumentInput);
} else if (index == 3) { } else if (index == 3) {
return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -24,11 +24,11 @@ public class SpreadTransformParser extends RichParser<ResettableExtent> {
} }
@Override @Override
protected Stream<String> getSuggestions(String argumentInput, int index) { protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
if (index < 3) { if (index < 3) {
return SuggestionHelper.suggestPositiveIntegers(argumentInput); return SuggestionHelper.suggestPositiveIntegers(argumentInput);
} else if (index == 3) { } else if (index == 3) {
return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream();
} }
return Stream.empty(); return Stream.empty();
} }

View File

@ -0,0 +1,7 @@
package com.fastasyncworldedit.core.extension.platform.binding;
import com.sk89q.worldedit.EditSession;
public record EditSessionHolder(EditSession session) {
}

View File

@ -3,7 +3,11 @@ package com.fastasyncworldedit.core.extension.platform.binding;
import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.database.DBHandler; import com.fastasyncworldedit.core.database.DBHandler;
import com.fastasyncworldedit.core.database.RollbackDatabase; import com.fastasyncworldedit.core.database.RollbackDatabase;
import com.fastasyncworldedit.core.extent.LimitExtent;
import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.regions.FaweMaskManager; import com.fastasyncworldedit.core.regions.FaweMaskManager;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.util.TextureUtil; import com.fastasyncworldedit.core.util.TextureUtil;
import com.fastasyncworldedit.core.util.image.ImageUtil; import com.fastasyncworldedit.core.util.image.ImageUtil;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
@ -11,6 +15,7 @@ import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.argument.Arguments; import com.sk89q.worldedit.command.argument.Arguments;
import com.sk89q.worldedit.command.util.annotation.AllowedRegion; import com.sk89q.worldedit.command.util.annotation.AllowedRegion;
import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
@ -25,6 +30,7 @@ import org.enginehub.piston.inject.Key;
import org.enginehub.piston.util.ValueProvider; import org.enginehub.piston.util.ValueProvider;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.lang.reflect.Method;
import java.net.URI; import java.net.URI;
import java.util.Optional; import java.util.Optional;
@ -52,11 +58,33 @@ public class ProvideBindings extends Bindings {
@Binding @Binding
public EditSession editSession(LocalSession localSession, Actor actor, InjectedValueAccess context) { public EditSession editSession(LocalSession localSession, Actor actor, InjectedValueAccess context) {
Method commandMethod =
context.injectedValue(Key.of(InjectedValueStore.class)).get().injectedValue(Key.of(Method.class)).get();
Arguments arguments = context.injectedValue(Key.of(Arguments.class)).orElse(null); Arguments arguments = context.injectedValue(Key.of(Arguments.class)).orElse(null);
String command = arguments == null ? null : arguments.get(); String command = arguments == null ? null : arguments.get();
EditSession editSession = localSession.createEditSession(actor, command); boolean synchronousSetting = commandMethod.getAnnotation(SynchronousSettingExpected.class) != null;
editSession.enableStandardMode(); EditSessionHolder holder = context.injectedValue(Key.of(EditSessionHolder.class)).orElse(null);
Request.request().setEditSession(editSession); EditSession editSession = holder != null ? holder.session() : null;
if (editSession == null) {
editSession = localSession.createEditSession(actor, command);
editSession.enableStandardMode();
} else {
LimitExtent limitExtent = new ExtentTraverser<>(editSession).findAndGet(LimitExtent.class);
if (limitExtent != null) {
limitExtent.setProcessing(!synchronousSetting);
if (!synchronousSetting) {
ExtentBatchProcessorHolder processorHolder = new ExtentTraverser<>(editSession).findAndGet(
ExtentBatchProcessorHolder.class);
if (processorHolder != null) {
processorHolder.addProcessor(limitExtent);
} else {
throw new FaweException(Caption.of("fawe.error.no-process-non-synchronous-edit"));
}
}
}
Request.request().setEditSession(editSession);
}
return editSession; return editSession;
} }

View File

@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.extent;
import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet;
import com.fastasyncworldedit.core.math.MutableBlockVector3; import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
@ -74,7 +75,7 @@ public class HistoryExtent extends AbstractDelegateExtent {
public Entity createEntity(Location location, BaseEntity state) { public Entity createEntity(Location location, BaseEntity state) {
final Entity entity = super.createEntity(location, state); final Entity entity = super.createEntity(location, state);
if (state != null) { if (state != null) {
this.changeSet.addEntityCreate(state.getNbtData()); this.changeSet.addEntityCreate(FaweCompoundTag.of(state.getNbt()));
} }
return entity; return entity;
} }
@ -84,7 +85,7 @@ public class HistoryExtent extends AbstractDelegateExtent {
public Entity createEntity(Location location, BaseEntity state, UUID uuid) { public Entity createEntity(Location location, BaseEntity state, UUID uuid) {
final Entity entity = super.createEntity(location, state, uuid); final Entity entity = super.createEntity(location, state, uuid);
if (state != null) { if (state != null) {
this.changeSet.addEntityCreate(state.getNbtData()); this.changeSet.addEntityCreate(FaweCompoundTag.of(state.getNbt()));
} }
return entity; return entity;
} }
@ -154,11 +155,10 @@ public class HistoryExtent extends AbstractDelegateExtent {
@Override @Override
public boolean remove() { public boolean remove() {
final Location location = this.entity.getLocation();
final BaseEntity state = this.entity.getState(); final BaseEntity state = this.entity.getState();
final boolean success = this.entity.remove(); final boolean success = this.entity.remove();
if (state != null && success) { if (state != null && success) {
HistoryExtent.this.changeSet.addEntityRemove(state.getNbtData()); HistoryExtent.this.changeSet.addEntityRemove(FaweCompoundTag.of(state.getNbt()));
} }
return success; return success;
} }

View File

@ -1,11 +1,15 @@
package com.fastasyncworldedit.core.extent; package com.fastasyncworldedit.core.extent;
import com.fastasyncworldedit.core.extent.filter.block.ExtentFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.ExtentFilterBlock;
import com.fastasyncworldedit.core.function.generator.GenBase; import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.fastasyncworldedit.core.function.generator.Resource;
import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.Filter;
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.util.ExtentTraverser;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
@ -17,7 +21,6 @@ import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Pattern;
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 com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
@ -37,18 +40,22 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
public class LimitExtent extends AbstractDelegateExtent { public class LimitExtent extends AbstractDelegateExtent implements IBatchProcessor {
private final FaweLimit limit; private final FaweLimit limit;
private final boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length]; private final boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
private final Consumer<Component> onErrorMessage; private final Consumer<Component> onErrorMessage;
private final int chunk_size;
private boolean processing;
/** /**
* Create a new instance. * Create a new instance.
* *
* @param extent the extent * @param extent the extent
* @param limit the limit * @param limit the limit
* @deprecated Use {@link LimitExtent#LimitExtent(Extent, FaweLimit, Consumer, boolean)}
*/ */
@Deprecated(forRemoval = true, since = "TODO")
public LimitExtent(Extent extent, FaweLimit limit) { public LimitExtent(Extent extent, FaweLimit limit) {
this(extent, limit, c -> { this(extent, limit, c -> {
}); });
@ -60,11 +67,33 @@ public class LimitExtent extends AbstractDelegateExtent {
* @param extent the extent * @param extent the extent
* @param limit the limit * @param limit the limit
* @param onErrorMessage consumer to handle a component generated by exceptions * @param onErrorMessage consumer to handle a component generated by exceptions
* @deprecated Use {@link LimitExtent#LimitExtent(Extent, FaweLimit, Consumer, boolean)}
*/ */
@Deprecated(forRemoval = true, since = "TODO")
public LimitExtent(Extent extent, FaweLimit limit, Consumer<Component> onErrorMessage) { public LimitExtent(Extent extent, FaweLimit limit, Consumer<Component> onErrorMessage) {
this(extent, limit, onErrorMessage, false);
}
/**
* Create a new instance.
*
* @param extent the extent
* @param limit the limit
* @param onErrorMessage consumer to handle a component generated by exceptions
* @param processing if this limit extent is expected to be processing
* @since TODO
*/
public LimitExtent(
Extent extent,
FaweLimit limit,
Consumer<Component> onErrorMessage,
boolean processing
) {
super(extent); super(extent);
this.limit = limit; this.limit = limit;
this.onErrorMessage = onErrorMessage; this.onErrorMessage = onErrorMessage;
this.chunk_size = 16 * 16 * (extent.getMaxY() - extent.getMinY());
this.processing = processing;
} }
private void handleException(FaweException e) { private void handleException(FaweException e) {
@ -81,7 +110,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public List<? extends Entity> getEntities(Region region) { public List<? extends Entity> getEntities(Region region) {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
try { try {
return super.getEntities(region); return extent.getEntities(region);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return Collections.emptyList(); return Collections.emptyList();
@ -92,7 +121,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public List<? extends Entity> getEntities() { public List<? extends Entity> getEntities() {
limit.THROW_MAX_CHECKS(); limit.THROW_MAX_CHECKS();
try { try {
return super.getEntities(); return extent.getEntities();
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return Collections.emptyList(); return Collections.emptyList();
@ -105,7 +134,7 @@ public class LimitExtent extends AbstractDelegateExtent {
limit.THROW_MAX_CHANGES(); limit.THROW_MAX_CHANGES();
limit.THROW_MAX_ENTITIES(); limit.THROW_MAX_ENTITIES();
try { try {
return super.createEntity(location, entity); return extent.createEntity(location, entity);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return null; return null;
@ -118,7 +147,7 @@ public class LimitExtent extends AbstractDelegateExtent {
limit.THROW_MAX_CHANGES(); limit.THROW_MAX_CHANGES();
limit.THROW_MAX_ENTITIES(); limit.THROW_MAX_ENTITIES();
try { try {
return super.createEntity(location, entity, uuid); return extent.createEntity(location, entity, uuid);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return null; return null;
@ -130,7 +159,7 @@ public class LimitExtent extends AbstractDelegateExtent {
limit.THROW_MAX_CHANGES(); limit.THROW_MAX_CHANGES();
limit.THROW_MAX_ENTITIES(); limit.THROW_MAX_ENTITIES();
try { try {
super.removeEntity(x, y, z, uuid); extent.removeEntity(x, y, z, uuid);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
} }
@ -138,9 +167,9 @@ public class LimitExtent extends AbstractDelegateExtent {
@Override @Override
public boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) { public boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) {
limit.THROW_MAX_CHANGES(Character.MAX_VALUE); limit.THROW_MAX_CHANGES(chunk_size);
try { try {
return super.regenerateChunk(x, z, type, seed); return extent.regenerateChunk(x, z, type, seed);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return false; return false;
@ -151,7 +180,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public int getHighestTerrainBlock(int x, int z, int minY, int maxY) { public int getHighestTerrainBlock(int x, int z, int minY, int maxY) {
limit.THROW_MAX_CHECKS(maxY - minY + 1); limit.THROW_MAX_CHECKS(maxY - minY + 1);
try { try {
return super.getHighestTerrainBlock(x, z, minY, maxY); return extent.getHighestTerrainBlock(x, z, minY, maxY);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return minY; return minY;
@ -162,7 +191,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) { public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) {
limit.THROW_MAX_CHECKS(maxY - minY + 1); limit.THROW_MAX_CHECKS(maxY - minY + 1);
try { try {
return super.getHighestTerrainBlock(x, z, minY, maxY, filter); return extent.getHighestTerrainBlock(x, z, minY, maxY, filter);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return minY; return minY;
@ -173,7 +202,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
limit.THROW_MAX_CHECKS(maxY - minY + 1); limit.THROW_MAX_CHECKS(maxY - minY + 1);
try { try {
return super.getNearestSurfaceLayer(x, z, y, minY, maxY); return extent.getNearestSurfaceLayer(x, z, y, minY, maxY);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return minY; return minY;
@ -184,7 +213,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) { public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) {
limit.THROW_MAX_CHECKS(maxY - minY + 1); limit.THROW_MAX_CHECKS(maxY - minY + 1);
try { try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir); return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return minY; return minY;
@ -195,7 +224,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
limit.THROW_MAX_CHECKS(maxY - minY + 1); limit.THROW_MAX_CHECKS(maxY - minY + 1);
try { try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return minY; return minY;
@ -206,7 +235,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) { public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) {
limit.THROW_MAX_CHECKS(maxY - minY + 1); limit.THROW_MAX_CHECKS(maxY - minY + 1);
try { try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax); return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return minY; return minY;
@ -217,7 +246,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) { public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) {
limit.THROW_MAX_CHECKS(maxY - minY + 1); limit.THROW_MAX_CHECKS(maxY - minY + 1);
try { try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask); return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return minY; return minY;
@ -237,91 +266,47 @@ public class LimitExtent extends AbstractDelegateExtent {
) { ) {
limit.THROW_MAX_CHECKS(maxY - minY + 1); limit.THROW_MAX_CHECKS(maxY - minY + 1);
try { try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir); return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return minY; return minY;
} }
} }
@Override
public void addCaves(Region region) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume());
super.addCaves(region);
}
@Override
public void generate(Region region, GenBase gen) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume());
super.generate(region, gen);
}
@Override
public void addSchems(Region region, Mask mask, List<ClipboardHolder> clipboards, int rarity, boolean rotate) throws
WorldEditException {
limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume());
super.addSchems(region, mask, clipboards, rarity, rotate);
}
@Override
public void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume());
super.spawnResource(region, gen, rarity, frequency);
}
@Override
public void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws
WorldEditException {
limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume());
super.addOre(region, mask, material, size, frequency, rarity, minY, maxY);
}
@Override
public void addOres(Region region, Mask mask) throws WorldEditException {
limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume());
super.addOres(region, mask);
}
@Override @Override
public List<Countable<BlockType>> getBlockDistribution(Region region) { public List<Countable<BlockType>> getBlockDistribution(Region region) {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
return super.getBlockDistribution(region); return extent.getBlockDistribution(region);
} }
@Override @Override
public List<Countable<BlockState>> getBlockDistributionWithData(Region region) { public List<Countable<BlockState>> getBlockDistributionWithData(Region region) {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
return super.getBlockDistributionWithData(region); return extent.getBlockDistributionWithData(region);
} }
@Override @Override
public int countBlocks(Region region, Set<BaseBlock> searchBlocks) { public int countBlocks(Region region, Set<BaseBlock> searchBlocks) {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
return super.countBlocks(region, searchBlocks); return extent.countBlocks(region, searchBlocks);
} }
@Override @Override
public int countBlocks(Region region, Mask searchMask) { public int countBlocks(Region region, Mask searchMask) {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
return super.countBlocks(region, searchMask); return extent.countBlocks(region, searchMask);
} }
@Override @Override
public <B extends BlockStateHolder<B>> int setBlocks(Region region, B block) throws MaxChangedBlocksException { public <B extends BlockStateHolder<B>> int setBlocks(Region region, B block) throws MaxChangedBlocksException {
limit.THROW_MAX_CHANGES(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume());
return super.setBlocks(region, block); return extent.setBlocks(region, block);
} }
@Override @Override
public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
limit.THROW_MAX_CHANGES(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume());
return super.setBlocks(region, pattern); return extent.setBlocks(region, pattern);
} }
@Override @Override
@ -329,41 +314,34 @@ public class LimitExtent extends AbstractDelegateExtent {
MaxChangedBlocksException { MaxChangedBlocksException {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume());
return super.replaceBlocks(region, filter, replacement); return extent.replaceBlocks(region, filter, replacement);
} }
@Override @Override
public int replaceBlocks(Region region, Set<BaseBlock> filter, Pattern pattern) throws MaxChangedBlocksException { public int replaceBlocks(Region region, Set<BaseBlock> filter, Pattern pattern) throws MaxChangedBlocksException {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume());
return super.replaceBlocks(region, filter, pattern); return extent.replaceBlocks(region, filter, pattern);
} }
@Override @Override
public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException { public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume());
return super.replaceBlocks(region, mask, pattern); return extent.replaceBlocks(region, mask, pattern);
}
@Override
public int center(Region region, Pattern pattern) throws MaxChangedBlocksException {
limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume());
return super.center(region, pattern);
} }
@Override @Override
public int setBlocks(Set<BlockVector3> vset, Pattern pattern) { public int setBlocks(Set<BlockVector3> vset, Pattern pattern) {
limit.THROW_MAX_CHANGES(vset.size()); limit.THROW_MAX_CHANGES(vset.size());
return super.setBlocks(vset, pattern); return extent.setBlocks(vset, pattern);
} }
@Override @Override
public <T extends Filter> T apply(Region region, T filter, boolean full) { public <T extends Filter> T apply(Region region, T filter, boolean full) {
limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHECKS(region.getVolume());
limit.THROW_MAX_CHANGES(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume());
return super.apply(region, filter, full); return extent.apply(region, filter, full);
} }
@Override @Override
@ -393,14 +371,14 @@ public class LimitExtent extends AbstractDelegateExtent {
} }
limit.THROW_MAX_CHECKS(size); limit.THROW_MAX_CHECKS(size);
limit.THROW_MAX_CHANGES(size); limit.THROW_MAX_CHANGES(size);
return super.apply(positions, filter); return extent.apply(positions, filter);
} }
@Override @Override
public BlockState getBlock(BlockVector3 position) { public BlockState getBlock(BlockVector3 position) {
limit.THROW_MAX_CHECKS(); limit.THROW_MAX_CHECKS();
try { try {
return super.getBlock(position); return extent.getBlock(position);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return BlockTypes.AIR.getDefaultState(); return BlockTypes.AIR.getDefaultState();
@ -411,7 +389,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public BlockState getBlock(int x, int y, int z) { public BlockState getBlock(int x, int y, int z) {
limit.THROW_MAX_CHECKS(); limit.THROW_MAX_CHECKS();
try { try {
return super.getBlock(x, y, z); return extent.getBlock(x, y, z);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return BlockTypes.AIR.getDefaultState(); return BlockTypes.AIR.getDefaultState();
@ -422,7 +400,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public BaseBlock getFullBlock(BlockVector3 position) { public BaseBlock getFullBlock(BlockVector3 position) {
limit.THROW_MAX_CHECKS(); limit.THROW_MAX_CHECKS();
try { try {
return super.getFullBlock(position); return extent.getFullBlock(position);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return BlockTypes.AIR.getDefaultState().toBaseBlock(); return BlockTypes.AIR.getDefaultState().toBaseBlock();
@ -433,7 +411,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public BaseBlock getFullBlock(int x, int y, int z) { public BaseBlock getFullBlock(int x, int y, int z) {
limit.THROW_MAX_CHECKS(); limit.THROW_MAX_CHECKS();
try { try {
return super.getFullBlock(x, y, z); return extent.getFullBlock(x, y, z);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return BlockTypes.AIR.getDefaultState().toBaseBlock(); return BlockTypes.AIR.getDefaultState().toBaseBlock();
@ -444,7 +422,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public BiomeType getBiome(BlockVector3 position) { public BiomeType getBiome(BlockVector3 position) {
limit.THROW_MAX_CHECKS(); limit.THROW_MAX_CHECKS();
try { try {
return super.getBiome(position); return extent.getBiome(position);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return BiomeTypes.FOREST; return BiomeTypes.FOREST;
@ -455,7 +433,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public BiomeType getBiomeType(int x, int y, int z) { public BiomeType getBiomeType(int x, int y, int z) {
limit.THROW_MAX_CHECKS(); limit.THROW_MAX_CHECKS();
try { try {
return super.getBiomeType(x, y, z); return extent.getBiomeType(x, y, z);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return BiomeTypes.FOREST; return BiomeTypes.FOREST;
@ -470,7 +448,7 @@ public class LimitExtent extends AbstractDelegateExtent {
limit.THROW_MAX_BLOCKSTATES(); limit.THROW_MAX_BLOCKSTATES();
} }
try { try {
return super.setBlock(position, block); return extent.setBlock(position, block);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return false; return false;
@ -484,7 +462,7 @@ public class LimitExtent extends AbstractDelegateExtent {
limit.THROW_MAX_BLOCKSTATES(); limit.THROW_MAX_BLOCKSTATES();
} }
try { try {
return super.setBlock(x, y, z, block); return extent.setBlock(x, y, z, block);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return false; return false;
@ -494,9 +472,9 @@ public class LimitExtent extends AbstractDelegateExtent {
@Override @Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
limit.THROW_MAX_CHANGES(); limit.THROW_MAX_CHANGES();
limit.MAX_BLOCKSTATES(); limit.THROW_MAX_BLOCKSTATES();
try { try {
return super.setTile(x, y, z, tile); return extent.setTile(x, y, z, tile);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return false; return false;
@ -507,7 +485,7 @@ public class LimitExtent extends AbstractDelegateExtent {
public boolean setBiome(BlockVector3 position, BiomeType biome) { public boolean setBiome(BlockVector3 position, BiomeType biome) {
limit.THROW_MAX_CHANGES(); limit.THROW_MAX_CHANGES();
try { try {
return super.setBiome(position, biome); return extent.setBiome(position, biome);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return false; return false;
@ -518,11 +496,41 @@ public class LimitExtent extends AbstractDelegateExtent {
public boolean setBiome(int x, int y, int z, BiomeType biome) { public boolean setBiome(int x, int y, int z, BiomeType biome) {
limit.THROW_MAX_CHANGES(); limit.THROW_MAX_CHANGES();
try { try {
return super.setBiome(x, y, z, biome); return extent.setBiome(x, y, z, biome);
} catch (FaweException e) { } catch (FaweException e) {
handleException(e); handleException(e);
return false; return false;
} }
} }
public void setProcessing(boolean processing) {
this.processing = processing;
}
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
if (!processing) {
return set;
}
int tiles = set.tiles().size();
int ents = set.entities().size() + set.getEntityRemoves().size();
limit.THROW_MAX_CHANGES(tiles + ents);
limit.THROW_MAX_BLOCKSTATES(tiles);
limit.THROW_MAX_ENTITIES(ents);
return set;
}
@Override
public Extent construct(final Extent child) {
if (extent != child) {
new ExtentTraverser<Extent>(this).setNext(child);
}
return this;
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
} }

View File

@ -2,14 +2,12 @@ package com.fastasyncworldedit.core.extent;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.fastasyncworldedit.core.math.BlockVector3ChunkMap; import com.fastasyncworldedit.core.math.BlockVector3ChunkMap;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.util.ExtentTraverser; import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.google.common.collect.ImmutableMap;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
@ -20,16 +18,17 @@ import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.NbtValued; import com.sk89q.worldedit.world.NbtValued;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinTag;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.HashMap; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProcessor { public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProcessor {
@ -75,79 +74,82 @@ public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProc
if (!(block instanceof BaseBlock localBlock)) { if (!(block instanceof BaseBlock localBlock)) {
return block; return block;
} }
if (!localBlock.hasNbtData()) { final LinCompoundTag nbt = localBlock.getNbt();
if (nbt == null) {
return block; return block;
} }
CompoundTag nbt = localBlock.getNbtData(); LinCompoundTag.Builder nbtBuilder = nbt.toBuilder();
Map<String, Tag<?, ?>> value = new HashMap<>(nbt.getValue());
for (String key : strip) { for (String key : strip) {
value.remove(key); nbtBuilder.remove(key);
} }
return (B) localBlock.toBaseBlock(new CompoundTag(value)); return (B) localBlock.toBaseBlock(nbtBuilder.build());
} }
public <T extends NbtValued> T stripEntityNBT(T entity) { public <T extends NbtValued> T stripEntityNBT(T entity) {
if (!entity.hasNbtData()) { LinCompoundTag nbt = entity.getNbt();
if (nbt == null) {
return entity; return entity;
} }
CompoundTag nbt = entity.getNbtData(); LinCompoundTag.Builder nbtBuilder = nbt.toBuilder();
Map<String, Tag<?, ?>> value = new HashMap<>(nbt.getValue());
for (String key : strip) { for (String key : strip) {
value.remove(key); nbtBuilder.remove(key);
} }
entity.setNbtData(new CompoundTag(value)); entity.setNbt(nbtBuilder.build());
return entity; return entity;
} }
@Override @Override
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
Map<BlockVector3, CompoundTag> tiles = set.getTiles(); Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
Set<CompoundTag> entities = set.getEntities(); Collection<FaweCompoundTag> entities = set.entities();
if (tiles.isEmpty() && entities.isEmpty()) { if (tiles.isEmpty() && entities.isEmpty()) {
return set; return set;
} }
boolean isBv3ChunkMap = tiles instanceof BlockVector3ChunkMap; boolean isBv3ChunkMap = tiles instanceof BlockVector3ChunkMap;
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) { for (final var entry : tiles.entrySet()) {
ImmutableMap.Builder<String, Tag<?, ?>> map = ImmutableMap.builder(); FaweCompoundTag original = entry.getValue();
final AtomicBoolean isStripped = new AtomicBoolean(false); FaweCompoundTag result = stripNbt(original);
entry.getValue().getValue().forEach((k, v) -> { if (original != result) {
if (strip.contains(k.toLowerCase())) {
isStripped.set(true);
} else {
map.put(k, v);
}
});
if (isStripped.get()) {
if (isBv3ChunkMap) { if (isBv3ChunkMap) {
// Replace existing value with stripped value // Replace existing value with stripped value
tiles.put(entry.getKey(), new CompoundTag(map.build())); tiles.put(entry.getKey(), result);
} else { } else {
entry.setValue(new CompoundTag(map.build())); entry.setValue(result);
} }
} }
} }
Set<CompoundTag> stripped = new HashSet<>(); Set<FaweCompoundTag> stripped = new HashSet<>();
Iterator<CompoundTag> iterator = entities.iterator(); Iterator<FaweCompoundTag> iterator = entities.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
CompoundTag entity = iterator.next(); FaweCompoundTag original = iterator.next();
ImmutableMap.Builder<String, Tag<?, ?>> map = ImmutableMap.builder(); FaweCompoundTag result = stripNbt(original);
final AtomicBoolean isStripped = new AtomicBoolean(false); if (original != result) {
entity.getValue().forEach((k, v) -> {
if (strip.contains(k.toUpperCase(Locale.ROOT))) {
isStripped.set(true);
} else {
map.put(k, v);
}
});
if (isStripped.get()) {
iterator.remove(); iterator.remove();
stripped.add(new CompoundTag(map.build())); stripped.add(result);
} }
} }
set.getEntities().addAll(stripped); // this relies on entities.addAll(...) not throwing an exception if empty+unmodifiable (=> stripped is empty too)
entities.addAll(stripped);
return set; return set;
} }
private FaweCompoundTag stripNbt(
FaweCompoundTag compoundTag
) {
LinCompoundTag.Builder builder = LinCompoundTag.builder();
boolean stripped = false;
for (var entry : compoundTag.linTag().value().entrySet()) {
String k = entry.getKey();
LinTag<?> v = entry.getValue();
if (strip.contains(k.toLowerCase(Locale.ROOT))) {
stripped = true;
} else {
builder.put(k, v);
}
}
return stripped ? FaweCompoundTag.of(builder.build()) : compoundTag;
}
@Nullable @Nullable
@Override @Override
public Extent construct(final Extent child) { public Extent construct(final Extent child) {

View File

@ -2,9 +2,11 @@ package com.fastasyncworldedit.core.extent.clipboard;
import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader; import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader;
import com.fastasyncworldedit.core.math.IntTriple; import com.fastasyncworldedit.core.math.IntTriple;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException;
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 com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
@ -175,6 +177,12 @@ public class CPUOptimizedClipboard extends LinearClipboard {
return true; return true;
} }
@Override
public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tile) throws WorldEditException {
// TODO replace
return setTile(x, y, z, new CompoundTag(tile.linTag()));
}
private boolean setTile(int index, CompoundTag tag) { private boolean setTile(int index, CompoundTag tag) {
final Map<String, Tag<?, ?>> values = new HashMap<>(tag.getValue()); final Map<String, Tag<?, ?>> values = new HashMap<>(tag.getValue());
values.remove("x"); values.remove("x");

View File

@ -6,15 +6,18 @@ import com.fastasyncworldedit.core.internal.exception.FaweClipboardVersionMismat
import com.fastasyncworldedit.core.internal.io.ByteBufferInputStream; import com.fastasyncworldedit.core.internal.io.ByteBufferInputStream;
import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader; import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader;
import com.fastasyncworldedit.core.math.IntTriple; import com.fastasyncworldedit.core.math.IntTriple;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.google.common.collect.Collections2;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag; import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
@ -28,6 +31,8 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinTagType;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
@ -64,7 +69,7 @@ public class DiskOptimizedClipboard extends LinearClipboard {
private static final int VERSION_2_HEADER_SIZE = 27; // Header size of "version 2" i.e. when NBT/entities could be saved private static final int VERSION_2_HEADER_SIZE = 27; // Header size of "version 2" i.e. when NBT/entities could be saved
private static final Map<String, LockHolder> LOCK_HOLDER_CACHE = new ConcurrentHashMap<>(); private static final Map<String, LockHolder> LOCK_HOLDER_CACHE = new ConcurrentHashMap<>();
private final HashMap<IntTriple, CompoundTag> nbtMap; private final HashMap<IntTriple, FaweCompoundTag> nbtMap;
private final File file; private final File file;
private final int headerSize; private final int headerSize;
@ -248,12 +253,12 @@ public class DiskOptimizedClipboard extends LinearClipboard {
try (NBTInputStream nbtIS = new NBTInputStream(MainUtil.getCompressedIS(new ByteBufferInputStream(tmp)))) { try (NBTInputStream nbtIS = new NBTInputStream(MainUtil.getCompressedIS(new ByteBufferInputStream(tmp)))) {
Iterator<CompoundTag> iter = nbtIS.toIterator(); Iterator<CompoundTag> iter = nbtIS.toIterator();
while (nbtCount > 0 && iter.hasNext()) { // TileEntities are stored "before" entities while (nbtCount > 0 && iter.hasNext()) { // TileEntities are stored "before" entities
CompoundTag tag = iter.next(); LinCompoundTag tag = iter.next().toLinTag();
int x = tag.getInt("x"); int x = tag.getTag("x", LinTagType.intTag()).valueAsInt();
int y = tag.getInt("y"); int y = tag.getTag("y", LinTagType.intTag()).valueAsInt();
int z = tag.getInt("z"); int z = tag.getTag("z", LinTagType.intTag()).valueAsInt();
IntTriple pos = new IntTriple(x, y, z); IntTriple pos = new IntTriple(x, y, z);
nbtMap.put(pos, tag); nbtMap.put(pos, FaweCompoundTag.of(tag));
nbtCount--; nbtCount--;
} }
while (entitiesCount > 0 && iter.hasNext()) { while (entitiesCount > 0 && iter.hasNext()) {
@ -559,8 +564,8 @@ public class DiskOptimizedClipboard extends LinearClipboard {
))) { ))) {
if (!nbtMap.isEmpty()) { if (!nbtMap.isEmpty()) {
try { try {
for (CompoundTag tag : nbtMap.values()) { for (FaweCompoundTag tag : nbtMap.values()) {
nbtOS.writeTag(tag); nbtOS.writeTag(new CompoundTag(tag.linTag()));
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -638,7 +643,7 @@ public class DiskOptimizedClipboard extends LinearClipboard {
@Override @Override
public Collection<CompoundTag> getTileEntities() { public Collection<CompoundTag> getTileEntities() {
return nbtMap.values(); return Collections2.transform(nbtMap.values(), fct -> new CompoundTag(fct.linTag()));
} }
public int getIndex(int x, int y, int z) { public int getIndex(int x, int y, int z) {
@ -656,10 +661,10 @@ public class DiskOptimizedClipboard extends LinearClipboard {
private BaseBlock toBaseBlock(BlockState state, int i) { private BaseBlock toBaseBlock(BlockState state, int i) {
if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) { if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) {
CompoundTag nbt; FaweCompoundTag nbt;
if (nbtMap.size() < 4) { if (nbtMap.size() < 4) {
nbt = null; nbt = null;
for (Map.Entry<IntTriple, CompoundTag> entry : nbtMap.entrySet()) { for (Map.Entry<IntTriple, FaweCompoundTag> entry : nbtMap.entrySet()) {
IntTriple key = entry.getKey(); IntTriple key = entry.getKey();
int index = getIndex(key.x(), key.y(), key.z()); int index = getIndex(key.x(), key.y(), key.z());
if (index == i) { if (index == i) {
@ -674,15 +679,15 @@ public class DiskOptimizedClipboard extends LinearClipboard {
int x = newI - z * getWidth(); int x = newI - z * getWidth();
nbt = nbtMap.get(new IntTriple(x, y, z)); nbt = nbtMap.get(new IntTriple(x, y, z));
} }
return state.toBaseBlock(nbt); return state.toBaseBlock(nbt == null ? null : nbt.linTag());
} }
return state.toBaseBlock(); return state.toBaseBlock();
} }
private BaseBlock toBaseBlock(BlockState state, int x, int y, int z) { private BaseBlock toBaseBlock(BlockState state, int x, int y, int z) {
if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) { if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) {
CompoundTag nbt = nbtMap.get(new IntTriple(x, y, z)); FaweCompoundTag nbt = nbtMap.get(new IntTriple(x, y, z));
return state.toBaseBlock(nbt); return state.toBaseBlock(nbt == null ? null : nbt.linTag());
} }
return state.toBaseBlock(); return state.toBaseBlock();
} }
@ -709,12 +714,8 @@ public class DiskOptimizedClipboard extends LinearClipboard {
} }
@Override @Override
public boolean setTile(int x, int y, int z, CompoundTag tag) { public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tile) throws WorldEditException {
final Map<String, Tag<?, ?>> values = new HashMap<>(tag.getValue()); nbtMap.put(new IntTriple(x, y, z), NbtUtils.withPosition(tile, x, y, z));
values.put("x", new IntTag(x));
values.put("y", new IntTag(y));
values.put("z", new IntTag(z));
nbtMap.put(new IntTriple(x, y, z), new CompoundTag(values));
return true; return true;
} }

View File

@ -1,7 +1,7 @@
package com.fastasyncworldedit.core.extent.clipboard; package com.fastasyncworldedit.core.extent.clipboard;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.sk89q.jnbt.CompoundTag; import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
@ -84,7 +84,8 @@ public final class EmptyClipboard implements Clipboard {
return false; return false;
} }
public boolean setTile(int x, int y, int z, @Nonnull CompoundTag tile) throws WorldEditException { @Override
public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tile) throws WorldEditException {
return false; return false;
} }

View File

@ -57,6 +57,10 @@ public abstract class LinearClipboard extends SimpleClipboard {
*/ */
public abstract void streamBiomes(IntValueReader task); public abstract void streamBiomes(IntValueReader task);
/**
* @deprecated will be removed as it is unused and uses outdated types
*/
@Deprecated(forRemoval = true, since = "TODO")
public abstract Collection<CompoundTag> getTileEntities(); public abstract Collection<CompoundTag> getTileEntities();
@Override @Override

View File

@ -3,10 +3,12 @@ package com.fastasyncworldedit.core.extent.clipboard;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader; import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader;
import com.fastasyncworldedit.core.math.IntTriple; import com.fastasyncworldedit.core.math.IntTriple;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MainUtil;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException;
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 com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
@ -262,6 +264,12 @@ public class MemoryOptimizedClipboard extends LinearClipboard {
return true; return true;
} }
@Override
public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tile) throws WorldEditException {
// TODO replace
return setTile(x, y, z, new CompoundTag(tile.linTag()));
}
@Override @Override
public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) { public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) {
return setBlock(getIndex(x, y, z), block); return setBlock(getIndex(x, y, z), block);

View File

@ -1,7 +1,7 @@
package com.fastasyncworldedit.core.extent.clipboard; package com.fastasyncworldedit.core.extent.clipboard;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.sk89q.jnbt.CompoundTag; import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
@ -106,7 +106,7 @@ public abstract class ReadOnlyClipboard extends SimpleClipboard {
} }
@Override @Override
public boolean setTile(int x, int y, int z, CompoundTag tag) { public boolean tile(int x, int y, int z, FaweCompoundTag tag) {
throw new UnsupportedOperationException("Clipboard is immutable"); throw new UnsupportedOperationException("Clipboard is immutable");
} }

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