diff --git a/build.gradle.kts b/build.gradle.kts index 12ad4a353..adab2bddb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ import xyz.jpenilla.runpaper.task.RunServer plugins { 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()) { @@ -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 revision: String by extra("") var buildNumber by extra("") @@ -91,13 +91,13 @@ tasks { minecraftVersion(it) pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } .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" runDirectory.set(file("run-$it")) } } runServer { - minecraftVersion("1.20.4") + minecraftVersion("1.21.1") pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } .toTypedArray()) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 6f8f97859..f0d72abd4 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -26,9 +26,9 @@ val properties = Properties().also { props -> dependencies { implementation(gradleApi()) - implementation("org.ajoberstar.grgit:grgit-gradle:5.2.2") - implementation("com.github.johnrengelman:shadow:8.1.1") - implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.7.2") + implementation("org.ajoberstar.grgit:grgit-gradle:5.3.0") + implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.3") + implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.7.3") constraints { val asmVersion = "[9.7,)" implementation("org.ow2.asm:asm:$asmVersion") { diff --git a/buildSrc/src/main/kotlin/CommonJavaConfig.kt b/buildSrc/src/main/kotlin/CommonJavaConfig.kt index d221a98d2..0b90d1e06 100644 --- a/buildSrc/src/main/kotlin/CommonJavaConfig.kt +++ b/buildSrc/src/main/kotlin/CommonJavaConfig.kt @@ -28,6 +28,7 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean options.isDeprecation = true options.encoding = "UTF-8" options.compilerArgs.add("-parameters") + options.compilerArgs.add("--add-modules=jdk.incubator.vector") } configurations.all { @@ -40,28 +41,30 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean dependencies { "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-params:5.10.0") - "testImplementation"("org.mockito:mockito-core:5.4.0") - "testImplementation"("org.mockito:mockito-junit-jupiter:5.4.0") - "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.10.0") + "testImplementation"("org.junit.jupiter:junit-jupiter-api:5.11.1") + "testImplementation"("org.junit.jupiter:junit-jupiter-params:5.11.1") + "testImplementation"("org.mockito:mockito-core:5.14.0") + "testImplementation"("org.mockito:mockito-junit-jupiter:5.14.0") + "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.11.1") } // Java 8 turns on doclint which we fail tasks.withType().configureEach { (options as StandardJavadocDocletOptions).apply { addStringOption("Xdoclint:none", "-quiet") + addStringOption("-add-modules", "jdk.incubator.vector") tags( "apiNote:a:API Note:", "implSpec:a:Implementation Requirements:", "implNote:a:Implementation Note:" ) options.encoding = "UTF-8" + links( "https://jd.advntr.dev/api/latest/", "https://logging.apache.org/log4j/2.x/javadoc/log4j-api/", "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/" ) docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}" diff --git a/buildSrc/src/main/kotlin/LibsConfig.kt b/buildSrc/src/main/kotlin/LibsConfig.kt index 4bf9ffca2..0599a8567 100644 --- a/buildSrc/src/main/kotlin/LibsConfig.kt +++ b/buildSrc/src/main/kotlin/LibsConfig.kt @@ -29,7 +29,7 @@ fun Project.applyLibrariesConfiguration() { applyCommonConfiguration() apply(plugin = "java-base") apply(plugin = "maven-publish") - apply(plugin = "com.github.johnrengelman.shadow") + apply(plugin = "com.gradleup.shadow") apply(plugin = "signing") configurations { diff --git a/buildSrc/src/main/kotlin/PlatformConfig.kt b/buildSrc/src/main/kotlin/PlatformConfig.kt index 8f737e9ce..128e7f09d 100644 --- a/buildSrc/src/main/kotlin/PlatformConfig.kt +++ b/buildSrc/src/main/kotlin/PlatformConfig.kt @@ -20,7 +20,7 @@ fun Project.applyPlatformAndCoreConfiguration() { apply(plugin = "eclipse") apply(plugin = "idea") apply(plugin = "maven-publish") - apply(plugin = "com.github.johnrengelman.shadow") + apply(plugin = "com.gradleup.shadow") apply(plugin = "signing") applyCommonJavaConfiguration( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 12f0abf24..a11448070 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,21 +9,21 @@ snakeyaml = "2.0" # Plugins dummypermscompat = "1.10" -worldguard-bukkit = "7.0.10" +worldguard-bukkit = "7.0.12" mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.3.12" -plotsquared = "7.3.9" +towny = "0.100.4.4" +plotsquared = "7.3.11" # Third party -bstats = "3.0.2" +bstats = "3.1.0" sparsebitset = "1.3" parallelgzip = "1.0.5" adventure = "4.17.0" adventure-bukkit = "4.3.4" -checkerqual = "3.46.0" +checkerqual = "3.47.0" truezip = "6.8.4" auto-value = "1.11.0" findbugs = "3.0.2" @@ -35,7 +35,7 @@ jlibnoise = "1.0.0" jchronic = "0.2.4a" lz4-java = "1.8.0" lz4-stream = "1.0.0" -commons-cli = "1.8.0" +commons-cli = "1.9.0" paperlib = "1.0.8" paster = "1.1.6" vault = "1.7.1" @@ -47,7 +47,7 @@ text = "3.0.4" piston = "0.5.10" # Tests -mockito = "5.12.0" +mockito = "5.14.1" # Gradle plugins pluginyml = "0.6.0" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f..a4b76b953 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 09523c0e5..df97d72b8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME 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 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426..f5feea6d6 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (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. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # 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. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 7101f8e46..9b42019c7 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightBlockMaterial.java index 2e1dd8279..1ecdb4116 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightBlockMaterial.java @@ -1,10 +1,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.util.ReflectionUtil; 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 net.minecraft.core.BlockPos; 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 org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; +import javax.annotation.Nullable; + public class PaperweightBlockMaterial implements BlockMaterial { private final Block block; @@ -25,7 +25,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { private final CraftBlockData craftBlockData; private final org.bukkit.Material craftMaterial; private final int opacity; - private final CompoundTag tile; + private final FaweCompoundTag tile; public PaperweightBlockMaterial(Block block) { this(block, block.defaultBlockState()); @@ -48,7 +48,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { ); tile = tileEntity == null ? null - : new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); + : PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity); } public Block getBlock() { @@ -135,9 +135,10 @@ public class PaperweightBlockMaterial implements BlockMaterial { return block.isRandomlyTicking(blockState); } + @SuppressWarnings("deprecation") @Override public boolean isMovementBlocker() { - return craftMaterial.isSolid(); + return blockState.blocksMotion(); } @Override @@ -172,7 +173,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { } @Override - public CompoundTag getDefaultTile() { + public @Nullable FaweCompoundTag defaultTile() { return tile; } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java index 53f8ef671..bb2d506f5 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java @@ -5,6 +5,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; @@ -97,6 +98,7 @@ import java.util.Map; import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -129,6 +131,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter blockEntityToCompoundTag() { + return blockEntity -> FaweCompoundTag.of( + () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId()) + ); + } + @Nullable private static String getEntityId(Entity entity) { ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java index 93f84a1ec..a325bb08b 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java @@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; 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.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; +import com.fastasyncworldedit.core.util.NbtUtils; 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.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.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; @@ -34,6 +31,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.IdMap; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; +import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.IntTag; import net.minecraft.server.level.ServerLevel; 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.block.CraftBlock; 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 java.util.AbstractSet; +import javax.annotation.Nullable; +import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; 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.ReentrantReadWriteLock; import java.util.function.Function; -import java.util.stream.Collectors; 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 Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); - private static final Function nmsTile2We = - tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); + public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin + .getInstance() + .getBukkitImplAdapter()).blockEntityToCompoundTag(); private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()); @@ -101,6 +107,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final ServerLevel serverLevel; private final int chunkX; private final int chunkZ; + private final IntPair chunkPos; private final int minHeight; private final int maxHeight; private final int minSectionPosition; @@ -135,6 +142,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); + this.chunkPos = new IntPair(chunkX, chunkZ); } public int getChunkX() { @@ -256,23 +264,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @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) + ( chunkX << 4), y, (z & 15) + ( chunkZ << 4))); if (blockEntity == null) { return null; } - return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)); + return NMS_TO_TILE.apply(blockEntity); + } @Override - public Map getTiles() { + public Map tiles() { Map nmsTiles = getChunk().getBlockEntities(); if (nmsTiles.isEmpty()) { return Collections.emptyMap(); } - return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); + return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE); } @Override @@ -335,7 +344,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public CompoundTag getEntity(UUID uuid) { + public @Nullable FaweCompoundTag entity(final UUID uuid) { ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; @@ -347,10 +356,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (entity != null) { 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()) { - if (uuid.equals(tag.getUUID())) { + for (FaweCompoundTag tag : entities()) { + if (uuid.equals(NbtUtils.uuid(tag))) { return tag; } } @@ -358,14 +367,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public Set getEntities() { + public Collection entities() { ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { - return Collections.emptySet(); + return Collections.emptyList(); } int size = entities.size(); - return new AbstractSet<>() { + return new AbstractCollection<>() { @Override public int size() { return size; @@ -378,10 +387,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public boolean contains(Object get) { - if (!(get instanceof CompoundTag getTag)) { + if (!(get instanceof FaweCompoundTag getTag)) { return false; } - UUID getUUID = getTag.getUUID(); + UUID getUUID = NbtUtils.uuid(getTag); for (Entity entity : entities) { UUID uuid = entity.getUUID(); if (uuid.equals(getUUID)) { @@ -393,12 +402,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Nonnull @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + public Iterator iterator() { + Iterable result = entities.stream().map(input -> { + CompoundTag tag = new CompoundTag(); input.save(tag); - return (CompoundTag) adapter.toNative(tag); - }).collect(Collectors.toList()); + return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag)); + })::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."); } 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 (copies.containsKey(copyKey)) { throw new IllegalStateException("Copy key already used."); @@ -427,9 +437,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc copies.put(copyKey, copy); } try { - ServerLevel nmsWorld = serverLevel; - LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); - // Remove existing tiles. Create a copy so that we can remove blocks Map chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); List beacons = null; @@ -501,6 +508,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, null, newSection, @@ -578,6 +587,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, null, newSection, @@ -643,6 +654,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() ); if (!PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, existingSection, newSection, @@ -716,7 +729,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { for (UUID uuid : entityRemoves) { - Entity entity = nmsWorld.getEntities().get(uuid); + Entity entity = serverLevel.getEntities().get(uuid); if (entity != null) { removeEntity(entity); } @@ -728,48 +741,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc }; } - Set entities = set.getEntities(); + Collection entities = set.entities(); if (entities != null && !entities.isEmpty()) { if (syncTasks == null) { syncTasks = new Runnable[2]; } syncTasks[1] = () -> { - Iterator iterator = entities.iterator(); + Iterator iterator = entities.iterator(); while (iterator.hasNext()) { - final CompoundTag nativeTag = iterator.next(); - final Map> entityTagMap = nativeTag.getValue(); - final StringTag idTag = (StringTag) entityTagMap.get("Id"); - final ListTag posTag = (ListTag) entityTagMap.get("Pos"); - final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + final FaweCompoundTag nativeTag = iterator.next(); + final LinCompoundTag linTag = nativeTag.linTag(); + final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag()); + final LinListTag posTag = linTag.findListTag("Pos", LinTagType.doubleTag()); + final LinListTag rotTag = linTag.findListTag("Rotation", LinTagType.floatTag()); if (idTag == null || posTag == null || rotTag == null) { LOGGER.error("Unknown entity tag: {}", nativeTag); continue; } - final double x = posTag.getDouble(0); - final double y = posTag.getDouble(1); - final double z = posTag.getDouble(2); - final float yaw = rotTag.getFloat(0); - final float pitch = rotTag.getFloat(1); - final String id = idTag.getValue(); + final double x = posTag.get(0).valueAsDouble(); + final double y = posTag.get(1).valueAsDouble(); + final double z = posTag.get(2).valueAsDouble(); + final float yaw = rotTag.get(0).valueAsFloat(); + final float pitch = rotTag.get(1).valueAsFloat(); + final String id = idTag.value(); EntityType type = EntityType.byString(id).orElse(null); if (type != null) { - Entity entity = type.create(nmsWorld); + Entity entity = type.create(serverLevel); if (entity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); + final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag); for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } entity.load(tag); entity.absMoveTo(x, y, z, yaw, pitch); - entity.setUUID(nativeTag.getUUID()); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + entity.setUUID(NbtUtils.uuid(nativeTag)); + if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { LOGGER.warn( "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", id, - nmsWorld.getWorld().getName(), + serverLevel.getWorld().getName(), x, y, z @@ -784,30 +796,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } // set tiles - Map tiles = set.getTiles(); + Map tiles = set.tiles(); if (tiles != null && !tiles.isEmpty()) { if (syncTasks == null) { syncTasks = new Runnable[1]; } syncTasks[0] = () -> { - for (final Map.Entry entry : tiles.entrySet()) { - final CompoundTag nativeTag = entry.getValue(); + for (final Map.Entry entry : tiles.entrySet()) { + final FaweCompoundTag nativeTag = entry.getValue(); final BlockVector3 blockHash = entry.getKey(); final int x = blockHash.x() + bx; final int y = blockHash.y(); final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + synchronized (serverLevel) { + BlockEntity tileEntity = serverLevel.getBlockEntity(pos); if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); + serverLevel.removeBlockEntity(pos); + tileEntity = serverLevel.getBlockEntity(pos); } if (tileEntity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); + final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag()); tag.put("x", IntTag.valueOf(x)); tag.put("y", IntTag.valueOf(y)); tag.put("z", IntTag.valueOf(z)); @@ -823,7 +834,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc callback = null; } else { int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; - boolean finalLightUpdate = lightUpdate; callback = () -> { // Set Modified nmsChunk.setLightCorrect(true); // Set Modified @@ -929,7 +939,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public void send() { synchronized (sendLock) { - PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ); + PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ); } } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java index b6f4c7d94..e655dd206 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java @@ -1,21 +1,22 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; 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.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; 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.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; +import io.papermc.lib.PaperLib; import net.minecraft.core.Holder; +import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; 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.PalettedContainerRO; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -38,8 +41,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private static final Logger LOGGER = LogManagerCompat.getLogger(); - private final Map tiles = new HashMap<>(); - private final Set entities = new HashSet<>(); + private final Map tiles = new HashMap<>(); + private final Set entities = new HashSet<>(); private final char[][] blocks; private final int minHeight; private final int maxHeight; @@ -56,44 +59,35 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } protected void storeTile(BlockEntity blockEntity) { + @SuppressWarnings("unchecked") + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); tiles.put( BlockVector3.at( blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getY(), blockEntity.getBlockPos().getZ() ), - new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)) + FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId())) ); } - @Override - public Map getTiles() { - return tiles; - } - - @Override - @Nullable - public CompoundTag getTile(int x, int y, int z) { - return tiles.get(BlockVector3.at(x, y, z)); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) protected void storeEntity(Entity entity) { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + @SuppressWarnings("unchecked") + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); entity.save(compoundTag); - entities.add((CompoundTag) adapter.toNative(compoundTag)); + entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag))); } @Override - public Set getEntities() { + public Collection entities() { return this.entities; } @Override - public CompoundTag getEntity(UUID uuid) { - for (CompoundTag tag : entities) { - if (uuid.equals(tag.getUUID())) { + public @Nullable FaweCompoundTag entity(final UUID uuid) { + for (FaweCompoundTag tag : entities) { + if (uuid.equals(NbtUtils.uuid(tag))) { return tag; } } @@ -179,8 +173,18 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { biomes[layer] = new Holder[64]; } if (biomeData instanceof PalettedContainer> palettedContainer) { - for (int i = 0; i < 64; i++) { - biomes[layer][i] = palettedContainer.get(i); + if (PaperLib.isPaper()) { + 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) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } } } else { LOGGER.error( @@ -194,7 +198,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public BaseBlock getFullBlock(int x, int y, int z) { BlockState state = BlockTypesCache.states[get(x, y, z)]; - return state.toBaseBlock(this, x, y, z); + return state.toBaseBlock((IBlocks) this, x, y, z); } @Override @@ -224,6 +228,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return BlockTypesCache.states[get(x, y, z)]; } + @Override + public Map 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 public int getSkyLight(int x, int y, int z) { return 0; diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java index 23f59e8bf..d7fab2dd6 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java @@ -7,8 +7,8 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.util.MathMan; -import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -123,6 +123,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static Field LEVEL_CHUNK_ENTITIES; private static Field SERVER_LEVEL_ENTITY_MANAGER; + static final MethodHandle PALETTED_CONTAINER_GET; + static { final MethodHandles.Lookup lookup = MethodHandles.lookup(); try { @@ -212,6 +214,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } catch (NoSuchFieldException ignored) { } 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) { throw e; } catch (Exception e) { @@ -234,15 +243,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } static boolean setSectionAtomic( + String worldName, + IntPair pair, LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, int layer ) { - if (layer >= 0 && layer < sections.length) { - return ReflectionUtils.compareAndSet(sections, expected, value, layer); - } - return false; + return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. @@ -340,7 +348,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } @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); if (chunkHolder == null) { return; @@ -361,26 +369,35 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { if (levelChunk == null) { return; } + StampLockHolder lockHolder = new StampLockHolder(); + NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + if (lockHolder.chunkLock == null) { + return; + } MinecraftServer.getServer().execute(() -> { - ClientboundLevelChunkWithLightPacket packet; - if (PaperLib.isPaper()) { - packet = new ClientboundLevelChunkWithLightPacket( - levelChunk, - nmsWorld.getChunkSource().getLightEngine(), - null, - null, - false // last false is to not bother with x-ray - ); - } else { - // deprecated on paper - deprecation suppressed - packet = new ClientboundLevelChunkWithLightPacket( - levelChunk, - nmsWorld.getChunkSource().getLightEngine(), - null, - null - ); + try { + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null + ); + } + 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)); }); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java index addf03867..b92835a82 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java @@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.queue.IQueueExtent; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; @@ -67,7 +68,7 @@ public class PaperweightStarlightRelighter extends StarlightRelighter { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public class PaperweightRegen extends Regenerator { private static final Field serverWorldsField; private static final Field paperConfigField; - private static final Field flatBedrockField; - private static final Field generatorSettingFlatField; private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - private static final Field generatorStructureStateField; - private static final Field ringPositionsField; - private static final Field hasGeneratedPositionsField; - //list of chunk stati in correct order without FULL - private static final Map chunkStati = new LinkedHashMap<>(); static { - chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing - chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps - chunkStati.put( - ChunkStatus.STRUCTURE_REFERENCES, - Concurrency.FULL - ); // structure refs: radius 8, but only writes to current chunk - chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0 - chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8 - chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE - chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results - /*chunkStati.put( - ChunkStatus.LIQUID_CARVERS, - Concurrency.NONE - ); // liquid carvers: radius 0, but RADIUS and FULL change results*/ - chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps - chunkStati.put( - ChunkStatus.LIGHT, - Concurrency.FULL - ); // light: radius 1, but no writes to other chunks, only current chunk - chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0 - // chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0 - try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - Field tmpFlatBedrockField; try { //only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); - - tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); - tmpFlatBedrockField.setAccessible(true); } catch (Exception e) { tmpPaperConfigField = null; - tmpFlatBedrockField = null; } paperConfigField = tmpPaperConfigField; - flatBedrockField = tmpFlatBedrockField; generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( "settings", "e")); generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); - chunkSourceField.setAccessible(true); - - generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v")); - generatorStructureStateField.setAccessible(true); - - ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); - ringPositionsField.setAccessible(true); - - hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( - Refraction.pickName("hasGeneratedPositions", "h") - ); - hasGeneratedPositionsField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } @@ -172,43 +79,37 @@ public class PaperweightRegen extends Regenerator super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - return true; } @Override - @SuppressWarnings("unchecked") protected boolean initNewWorld() throws Exception { //world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); @@ -254,8 +155,10 @@ public class PaperweightRegen extends Regenerator getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { + public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { if (options.hasBiomeType()) { return singleBiome; } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( - biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() - ); + return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); + } + + @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(); freshWorld.noSave = true; @@ -292,89 +208,6 @@ public class PaperweightRegen extends Regenerator generatorSettingBaseSupplier = (Holder) 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>> origPositions = - (Map>>) ringPositionsField.get(state); - Map>> 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; } @@ -389,7 +222,8 @@ public class PaperweightRegen extends Regenerator { try { - freshChunkProvider.close(false); + freshWorld.getChunkSource().getDataStorage().cache.clear(); + freshWorld.getChunkSource().close(false); } catch (Exception e) { throw new RuntimeException(e); } @@ -410,63 +244,20 @@ public class PaperweightRegen extends Regenerator getBlockPopulators() { - return originalServerWorld.getWorld().getPopulators(); - } - - @Override - protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { - // BlockPopulator#populate has to be called synchronously for TileEntity access - TaskManager.taskManager().task(() -> { - final CraftWorld world = freshWorld.getWorld(); - final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); - blockPopulator.populate(world, random, chunk); - }); - } - @Override protected IChunkCache initSourceQueueCache() { - return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { - @Override - public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) { - return getChunkAt(x, z); - } - }; + return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } //util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { - Fawe.instance().getQueueHandler().sync(() -> { - try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); - map.remove("faweregentempworld"); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("faweregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } } private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { @@ -483,11 +274,15 @@ public class PaperweightRegen extends Regenerator 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 getEntities() { - return Collections.emptyList(); - } - - } - - protected class ChunkStatusWrap extends ChunkStatusWrapper { - - 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 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 MAILBOX = ProcessorMailbox.create(task -> { - }, "fawe-no-op"); - private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { - }); - - public NoOpLightEngine(final ServerChunkCache chunkProvider) { - super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); - } - - @Override - public CompletableFuture lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) { - return CompletableFuture.completedFuture(chunk); - } - - } - } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java index 74b1c035c..d4082eb70 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java @@ -1,8 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.worldedit.world.registry.BlockMaterial; import net.minecraft.core.BlockPos; 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 org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; +import javax.annotation.Nullable; + public class PaperweightBlockMaterial implements BlockMaterial { private final Block block; @@ -21,7 +21,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { private final CraftBlockData craftBlockData; private final org.bukkit.Material craftMaterial; private final int opacity; - private final CompoundTag tile; + private final FaweCompoundTag tile; public PaperweightBlockMaterial(Block block) { this(block, block.defaultBlockState()); @@ -39,7 +39,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { ); tile = tileEntity == null ? null - : new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); + : PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity); } public Block getBlock() { @@ -125,9 +125,10 @@ public class PaperweightBlockMaterial implements BlockMaterial { return block.isRandomlyTicking(blockState); } + @SuppressWarnings("deprecation") @Override public boolean isMovementBlocker() { - return craftMaterial.isSolid(); + return blockState.blocksMotion(); } @Override @@ -162,7 +163,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { } @Override - public CompoundTag getDefaultTile() { + public @Nullable FaweCompoundTag defaultTile() { return tile; } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java index 30ef3423a..c4af78e95 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java @@ -5,6 +5,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; @@ -97,6 +98,7 @@ import java.util.Map; import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -129,6 +131,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter blockEntityToCompoundTag() { + return blockEntity -> FaweCompoundTag.of( + () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId()) + ); + } + @Nullable private static String getEntityId(Entity entity) { ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java index d2d18968a..b542b3e08 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; 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.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; +import com.fastasyncworldedit.core.util.NbtUtils; 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.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.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; @@ -34,6 +31,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.IdMap; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; +import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.IntTag; import net.minecraft.server.level.ServerLevel; 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.block.CraftBlock; 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 java.util.AbstractSet; +import javax.annotation.Nullable; +import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; 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.ReentrantReadWriteLock; import java.util.function.Function; -import java.util.stream.Collectors; 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 Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); - private static final Function nmsTile2We = - tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); + public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin + .getInstance() + .getBukkitImplAdapter()).blockEntityToCompoundTag(); private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()); @@ -101,6 +107,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final ServerLevel serverLevel; private final int chunkX; private final int chunkZ; + private final IntPair chunkPos; private final int minHeight; private final int maxHeight; private final int minSectionPosition; @@ -108,6 +115,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); + private final Object sendLock = new Object(); private LevelChunkSection[] sections; private LevelChunk levelChunk; private DataLayer[] blockLight; @@ -134,6 +142,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); + this.chunkPos = new IntPair(chunkX, chunkZ); } public int getChunkX() { @@ -255,23 +264,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @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) + ( chunkX << 4), y, (z & 15) + ( chunkZ << 4))); if (blockEntity == null) { return null; } - return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)); + return NMS_TO_TILE.apply(blockEntity); + } @Override - public Map getTiles() { + public Map tiles() { Map nmsTiles = getChunk().getBlockEntities(); if (nmsTiles.isEmpty()) { return Collections.emptyMap(); } - return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); + return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE); } @Override @@ -334,7 +344,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public CompoundTag getEntity(UUID uuid) { + public @Nullable FaweCompoundTag entity(final UUID uuid) { ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; @@ -346,10 +356,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (entity != null) { 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()) { - if (uuid.equals(tag.getUUID())) { + for (FaweCompoundTag tag : entities()) { + if (uuid.equals(NbtUtils.uuid(tag))) { return tag; } } @@ -357,14 +367,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public Set getEntities() { + public Collection entities() { ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { - return Collections.emptySet(); + return Collections.emptyList(); } int size = entities.size(); - return new AbstractSet<>() { + return new AbstractCollection<>() { @Override public int size() { return size; @@ -377,10 +387,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public boolean contains(Object get) { - if (!(get instanceof CompoundTag getTag)) { + if (!(get instanceof FaweCompoundTag getTag)) { return false; } - UUID getUUID = getTag.getUUID(); + UUID getUUID = NbtUtils.uuid(getTag); for (Entity entity : entities) { UUID uuid = entity.getUUID(); if (uuid.equals(getUUID)) { @@ -392,12 +402,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Nonnull @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { + public Iterator iterator() { + Iterable result = entities.stream().map(input -> { net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); input.save(tag); - return (CompoundTag) adapter.toNative(tag); - }).collect(Collectors.toList()); + return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag)); + })::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."); } 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 (copies.containsKey(copyKey)) { throw new IllegalStateException("Copy key already used."); @@ -426,9 +437,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc copies.put(copyKey, copy); } try { - ServerLevel nmsWorld = serverLevel; - LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); - // Remove existing tiles. Create a copy so that we can remove blocks Map chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); List beacons = null; @@ -500,6 +508,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, null, newSection, @@ -577,6 +587,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, null, newSection, @@ -642,6 +654,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() ); if (!PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, existingSection, newSection, @@ -715,7 +729,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { for (UUID uuid : entityRemoves) { - Entity entity = nmsWorld.getEntities().get(uuid); + Entity entity = serverLevel.getEntities().get(uuid); if (entity != null) { removeEntity(entity); } @@ -727,48 +741,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc }; } - Set entities = set.getEntities(); + Collection entities = set.entities(); if (entities != null && !entities.isEmpty()) { if (syncTasks == null) { syncTasks = new Runnable[2]; } syncTasks[1] = () -> { - Iterator iterator = entities.iterator(); + Iterator iterator = entities.iterator(); while (iterator.hasNext()) { - final CompoundTag nativeTag = iterator.next(); - final Map> entityTagMap = nativeTag.getValue(); - final StringTag idTag = (StringTag) entityTagMap.get("Id"); - final ListTag posTag = (ListTag) entityTagMap.get("Pos"); - final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + final FaweCompoundTag nativeTag = iterator.next(); + final LinCompoundTag linTag = nativeTag.linTag(); + final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag()); + final LinListTag posTag = linTag.findListTag("Pos", LinTagType.doubleTag()); + final LinListTag rotTag = linTag.findListTag("Rotation", LinTagType.floatTag()); if (idTag == null || posTag == null || rotTag == null) { LOGGER.error("Unknown entity tag: {}", nativeTag); continue; } - final double x = posTag.getDouble(0); - final double y = posTag.getDouble(1); - final double z = posTag.getDouble(2); - final float yaw = rotTag.getFloat(0); - final float pitch = rotTag.getFloat(1); - final String id = idTag.getValue(); + final double x = posTag.get(0).valueAsDouble(); + final double y = posTag.get(1).valueAsDouble(); + final double z = posTag.get(2).valueAsDouble(); + final float yaw = rotTag.get(0).valueAsFloat(); + final float pitch = rotTag.get(1).valueAsFloat(); + final String id = idTag.value(); EntityType type = EntityType.byString(id).orElse(null); if (type != null) { - Entity entity = type.create(nmsWorld); + Entity entity = type.create(serverLevel); if (entity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); + final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(linTag); for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } entity.load(tag); entity.absMoveTo(x, y, z, yaw, pitch); - entity.setUUID(nativeTag.getUUID()); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + entity.setUUID(NbtUtils.uuid(nativeTag)); + if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { LOGGER.warn( "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", id, - nmsWorld.getWorld().getName(), + serverLevel.getWorld().getName(), x, y, z @@ -783,30 +796,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } // set tiles - Map tiles = set.getTiles(); + Map tiles = set.tiles(); if (tiles != null && !tiles.isEmpty()) { if (syncTasks == null) { syncTasks = new Runnable[1]; } syncTasks[0] = () -> { - for (final Map.Entry entry : tiles.entrySet()) { - final CompoundTag nativeTag = entry.getValue(); + for (final Map.Entry entry : tiles.entrySet()) { + final FaweCompoundTag nativeTag = entry.getValue(); final BlockVector3 blockHash = entry.getKey(); final int x = blockHash.x() + bx; final int y = blockHash.y(); final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + synchronized (serverLevel) { + BlockEntity tileEntity = serverLevel.getBlockEntity(pos); if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); + serverLevel.removeBlockEntity(pos); + tileEntity = serverLevel.getBlockEntity(pos); } if (tileEntity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); + final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag()); tag.put("x", IntTag.valueOf(x)); tag.put("y", IntTag.valueOf(y)); tag.put("z", IntTag.valueOf(z)); @@ -822,7 +834,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc callback = null; } else { int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; - boolean finalLightUpdate = lightUpdate; callback = () -> { // Set Modified nmsChunk.setLightCorrect(true); // Set Modified @@ -927,7 +938,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public void send() { - PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ); + PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ); } /** diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java index 23c882284..1163c2d33 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java @@ -1,21 +1,22 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; 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.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; +import io.papermc.lib.PaperLib; import net.minecraft.core.Holder; +import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; 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.PalettedContainerRO; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -38,8 +41,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private static final Logger LOGGER = LogManagerCompat.getLogger(); - private final Map tiles = new HashMap<>(); - private final Set entities = new HashSet<>(); + private final Map tiles = new HashMap<>(); + private final Set entities = new HashSet<>(); private final char[][] blocks; private final int minHeight; private final int maxHeight; @@ -56,44 +59,35 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } protected void storeTile(BlockEntity blockEntity) { + @SuppressWarnings("unchecked") + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); tiles.put( BlockVector3.at( blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getY(), blockEntity.getBlockPos().getZ() ), - new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)) + FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(blockEntity.saveWithId())) ); } - @Override - public Map getTiles() { - return tiles; - } - - @Override - @Nullable - public CompoundTag getTile(int x, int y, int z) { - return tiles.get(BlockVector3.at(x, y, z)); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) protected void storeEntity(Entity entity) { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + @SuppressWarnings("unchecked") + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); entity.save(compoundTag); - entities.add((CompoundTag) adapter.toNative(compoundTag)); + entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag))); } @Override - public Set getEntities() { + public Collection entities() { return this.entities; } @Override - public CompoundTag getEntity(UUID uuid) { - for (CompoundTag tag : entities) { - if (uuid.equals(tag.getUUID())) { + public @Nullable FaweCompoundTag entity(final UUID uuid) { + for (FaweCompoundTag tag : entities) { + if (uuid.equals(NbtUtils.uuid(tag))) { return tag; } } @@ -179,8 +173,18 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { biomes[layer] = new Holder[64]; } if (biomeData instanceof PalettedContainer> palettedContainer) { - for (int i = 0; i < 64; i++) { - biomes[layer][i] = palettedContainer.get(i); + if (PaperLib.isPaper()) { + 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) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } } } else { LOGGER.error( @@ -194,7 +198,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public BaseBlock getFullBlock(int x, int y, int z) { BlockState state = BlockTypesCache.states[get(x, y, z)]; - return state.toBaseBlock(this, x, y, z); + return state.toBaseBlock((IBlocks) this, x, y, z); } @Override @@ -224,6 +228,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return BlockTypesCache.states[get(x, y, z)]; } + @Override + public Map 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 public int getSkyLight(int x, int y, int z) { return 0; diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java index b69f476d3..2c05d7466 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java @@ -7,8 +7,8 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.util.MathMan; -import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -123,6 +123,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static Field LEVEL_CHUNK_ENTITIES; private static Field SERVER_LEVEL_ENTITY_MANAGER; + static final MethodHandle PALETTED_CONTAINER_GET; + static { final MethodHandles.Lookup lookup = MethodHandles.lookup(); try { @@ -212,6 +214,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } catch (NoSuchFieldException ignored) { } 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) { throw e; } catch (Exception e) { @@ -234,15 +243,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } static boolean setSectionAtomic( + String worldName, + IntPair pair, LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, int layer ) { - if (layer >= 0 && layer < sections.length) { - return ReflectionUtils.compareAndSet(sections, expected, value, layer); - } - return false; + return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. @@ -340,7 +348,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } @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); if (chunkHolder == null) { return; @@ -361,26 +369,35 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { if (levelChunk == null) { return; } + StampLockHolder lockHolder = new StampLockHolder(); + NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + if (lockHolder.chunkLock == null) { + return; + } MinecraftServer.getServer().execute(() -> { - ClientboundLevelChunkWithLightPacket packet; - if (PaperLib.isPaper()) { - packet = new ClientboundLevelChunkWithLightPacket( - levelChunk, - nmsWorld.getChunkSource().getLightEngine(), - null, - null, - false // last false is to not bother with x-ray - ); - } else { - // deprecated on paper - deprecation suppressed - packet = new ClientboundLevelChunkWithLightPacket( - levelChunk, - nmsWorld.getChunkSource().getLightEngine(), - null, - null - ); + try { + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null + ); + } + 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)); }); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighter.java index d9109b4df..a1ce327f9 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighter.java @@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.queue.IQueueExtent; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; @@ -67,7 +68,7 @@ public class PaperweightStarlightRelighter extends StarlightRelighter { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public class PaperweightRegen extends Regenerator { private static final Field serverWorldsField; private static final Field paperConfigField; - private static final Field flatBedrockField; - private static final Field generatorSettingFlatField; private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - private static final Field generatorStructureStateField; - private static final Field ringPositionsField; - private static final Field hasGeneratedPositionsField; - //list of chunk stati in correct order without FULL - private static final Map chunkStati = new LinkedHashMap<>(); static { - chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing - chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps - chunkStati.put( - ChunkStatus.STRUCTURE_REFERENCES, - Concurrency.FULL - ); // structure refs: radius 8, but only writes to current chunk - chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0 - chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8 - chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE - chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results - /*chunkStati.put( - ChunkStatus.LIQUID_CARVERS, - Concurrency.NONE - ); // liquid carvers: radius 0, but RADIUS and FULL change results*/ - chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps - chunkStati.put( - ChunkStatus.LIGHT, - Concurrency.FULL - ); // light: radius 1, but no writes to other chunks, only current chunk - chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0 - // chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0 - try { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - Field tmpFlatBedrockField; try { //only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); - - tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); - tmpFlatBedrockField.setAccessible(true); } catch (Exception e) { tmpPaperConfigField = null; - tmpFlatBedrockField = null; } paperConfigField = tmpPaperConfigField; - flatBedrockField = tmpFlatBedrockField; generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( "settings", "e")); generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); - chunkSourceField.setAccessible(true); - - generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v")); - generatorStructureStateField.setAccessible(true); - - ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); - ringPositionsField.setAccessible(true); - - hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( - Refraction.pickName("hasGeneratedPositions", "h") - ); - hasGeneratedPositionsField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } @@ -171,43 +79,37 @@ public class PaperweightRegen extends Regenerator super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - return true; } @Override - @SuppressWarnings("unchecked") protected boolean initNewWorld() throws Exception { //world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); @@ -253,8 +155,10 @@ public class PaperweightRegen extends Regenerator getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { + public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { if (options.hasBiomeType()) { return singleBiome; } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( - biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() - ); + return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); + } + + @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(); freshWorld.noSave = true; @@ -291,89 +208,6 @@ public class PaperweightRegen extends Regenerator generatorSettingBaseSupplier = (Holder) 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>> origPositions = - (Map>>) ringPositionsField.get(state); - Map>> 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; } @@ -388,7 +222,8 @@ public class PaperweightRegen extends Regenerator { try { - freshChunkProvider.close(false); + freshWorld.getChunkSource().getDataStorage().cache.clear(); + freshWorld.getChunkSource().close(false); } catch (Exception e) { throw new RuntimeException(e); } @@ -409,63 +244,20 @@ public class PaperweightRegen extends Regenerator getBlockPopulators() { - return originalServerWorld.getWorld().getPopulators(); - } - - @Override - protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { - // BlockPopulator#populate has to be called synchronously for TileEntity access - TaskManager.taskManager().task(() -> { - final CraftWorld world = freshWorld.getWorld(); - final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); - blockPopulator.populate(world, random, chunk); - }); - } - @Override protected IChunkCache initSourceQueueCache() { - return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { - @Override - public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) { - return getChunkAt(x, z); - } - }; + return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } //util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { - Fawe.instance().getQueueHandler().sync(() -> { - try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); - map.remove("faweregentempworld"); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("faweregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } } private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { @@ -482,11 +274,15 @@ public class PaperweightRegen extends Regenerator 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 getEntities() { - return Collections.emptyList(); - } - - } - - protected class ChunkStatusWrap extends ChunkStatusWrapper { - - 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 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 MAILBOX = ProcessorMailbox.create(task -> { - }, "fawe-no-op"); - private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { - }); - - public NoOpLightEngine(final ServerChunkCache chunkProvider) { - super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); - } - - @Override - public CompletableFuture lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) { - return CompletableFuture.completedFuture(chunk); - } - - } - } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_5/build.gradle.kts index 1512dca87..1642d5a51 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_5/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.6-R0.1-SNAPSHOT/ - the().paperDevBundle("1.20.6-R0.1-20240702.153951-123") + the().paperDevBundle("1.20.6-R0.1-20240916.192025-125") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightBlockMaterial.java index 9c2292451..15f3abbaf 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightBlockMaterial.java @@ -1,11 +1,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.nbt.PaperweightLazyCompoundTag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.worldedit.world.registry.BlockMaterial; import net.minecraft.core.BlockPos; -import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; 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 org.bukkit.craftbukkit.block.data.CraftBlockData; +import javax.annotation.Nullable; + public class PaperweightBlockMaterial implements BlockMaterial { private final Block block; @@ -22,7 +21,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { private final CraftBlockData craftBlockData; private final org.bukkit.Material craftMaterial; private final int opacity; - private final CompoundTag tile; + private final FaweCompoundTag tile; public PaperweightBlockMaterial(Block block) { this(block, block.defaultBlockState()); @@ -38,9 +37,9 @@ public class PaperweightBlockMaterial implements BlockMaterial { BlockPos.ZERO, blockState ); - tile = tileEntity == null ? null : new PaperweightLazyCompoundTag( - Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess())) - ); + tile = tileEntity == null + ? null + : PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity); } public Block getBlock() { @@ -126,9 +125,10 @@ public class PaperweightBlockMaterial implements BlockMaterial { return blockState.isRandomlyTicking(); } + @SuppressWarnings("deprecation") @Override public boolean isMovementBlocker() { - return craftMaterial.isSolid(); + return blockState.blocksMotion(); } @Override @@ -163,7 +163,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { } @Override - public CompoundTag getDefaultTile() { + public @Nullable FaweCompoundTag defaultTile() { return tile; } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java index 76dc6f951..151ffd1f3 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java @@ -5,6 +5,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; @@ -103,6 +104,7 @@ import java.util.Map; import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -138,6 +140,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter blockEntityToCompoundTag() { + return blockEntity -> FaweCompoundTag.of( + () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess())) + ); + } + @Nullable private static String getEntityId(Entity entity) { ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java index 47466ac5e..ff05c453d 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java @@ -7,20 +7,17 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; 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.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; +import com.fastasyncworldedit.core.util.NbtUtils; 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.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.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; @@ -34,6 +31,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.IdMap; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; +import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.IntTag; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.ServerLevel; @@ -62,11 +60,19 @@ import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; 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 java.util.AbstractSet; +import javax.annotation.Nullable; +import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; 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.ReentrantReadWriteLock; import java.util.function.Function; -import java.util.stream.Collectors; 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 Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); - private static final Function nmsTile2We = tileEntity -> new PaperweightLazyCompoundTag( - Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess())) - ); + public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin + .getInstance() + .getBukkitImplAdapter()).blockEntityToCompoundTag(); private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()); @@ -103,6 +108,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final ServerLevel serverLevel; private final int chunkX; private final int chunkZ; + private final IntPair chunkPos; private final int minHeight; private final int maxHeight; private final int minSectionPosition; @@ -137,6 +143,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); + this.chunkPos = new IntPair(chunkX, chunkZ); } public int getChunkX() { @@ -258,23 +265,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @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) + ( chunkX << 4), y, (z & 15) + ( chunkZ << 4))); if (blockEntity == null) { return null; } - return new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))); + return NMS_TO_TILE.apply(blockEntity); + } @Override - public Map getTiles() { + public Map tiles() { Map nmsTiles = getChunk().getBlockEntities(); if (nmsTiles.isEmpty()) { return Collections.emptyMap(); } - return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); + return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE); } @Override @@ -337,7 +345,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public CompoundTag getEntity(UUID uuid) { + public @Nullable FaweCompoundTag entity(final UUID uuid) { ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; @@ -349,10 +357,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (entity != null) { 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()) { - if (uuid.equals(tag.getUUID())) { + for (FaweCompoundTag tag : entities()) { + if (uuid.equals(NbtUtils.uuid(tag))) { return tag; } } @@ -360,14 +368,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public Set getEntities() { + public Collection entities() { ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { - return Collections.emptySet(); + return Collections.emptyList(); } int size = entities.size(); - return new AbstractSet<>() { + return new AbstractCollection<>() { @Override public int size() { return size; @@ -380,10 +388,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public boolean contains(Object get) { - if (!(get instanceof CompoundTag getTag)) { + if (!(get instanceof FaweCompoundTag getTag)) { return false; } - UUID getUUID = getTag.getUUID(); + UUID getUUID = NbtUtils.uuid(getTag); for (Entity entity : entities) { UUID uuid = entity.getUUID(); if (uuid.equals(getUUID)) { @@ -395,12 +403,12 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Nonnull @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { + public Iterator iterator() { + Iterable result = entities.stream().map(input -> { net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); input.save(tag); - return (CompoundTag) adapter.toNative(tag); - }).collect(Collectors.toList()); + return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag)); + })::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."); } 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 (copies.containsKey(copyKey)) { throw new IllegalStateException("Copy key already used."); @@ -429,9 +438,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc copies.put(copyKey, copy); } try { - ServerLevel nmsWorld = serverLevel; - LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); - // Remove existing tiles. Create a copy so that we can remove blocks Map chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); List beacons = null; @@ -503,6 +509,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, null, newSection, @@ -580,6 +588,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, null, newSection, @@ -644,7 +654,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeRegistry, biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() ); - if (!PaperweightPlatformAdapter.setSectionAtomic(levelChunkSections, existingSection, + if (!PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, + levelChunkSections, + existingSection, newSection, getSectionIndex )) { @@ -716,7 +730,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { for (UUID uuid : entityRemoves) { - Entity entity = nmsWorld.getEntities().get(uuid); + Entity entity = serverLevel.getEntities().get(uuid); if (entity != null) { removeEntity(entity); } @@ -728,48 +742,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc }; } - Set entities = set.getEntities(); + Collection entities = set.entities(); if (entities != null && !entities.isEmpty()) { if (syncTasks == null) { syncTasks = new Runnable[2]; } syncTasks[1] = () -> { - Iterator iterator = entities.iterator(); + Iterator iterator = entities.iterator(); while (iterator.hasNext()) { - final CompoundTag nativeTag = iterator.next(); - final Map> entityTagMap = nativeTag.getValue(); - final StringTag idTag = (StringTag) entityTagMap.get("Id"); - final ListTag posTag = (ListTag) entityTagMap.get("Pos"); - final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + final FaweCompoundTag nativeTag = iterator.next(); + final LinCompoundTag linTag = nativeTag.linTag(); + final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag()); + final LinListTag posTag = linTag.findListTag("Pos", LinTagType.doubleTag()); + final LinListTag rotTag = linTag.findListTag("Rotation", LinTagType.floatTag()); if (idTag == null || posTag == null || rotTag == null) { LOGGER.error("Unknown entity tag: {}", nativeTag); continue; } - final double x = posTag.getDouble(0); - final double y = posTag.getDouble(1); - final double z = posTag.getDouble(2); - final float yaw = rotTag.getFloat(0); - final float pitch = rotTag.getFloat(1); - final String id = idTag.getValue(); + final double x = posTag.get(0).valueAsDouble(); + final double y = posTag.get(1).valueAsDouble(); + final double z = posTag.get(2).valueAsDouble(); + final float yaw = rotTag.get(0).valueAsFloat(); + final float pitch = rotTag.get(1).valueAsFloat(); + final String id = idTag.value(); EntityType type = EntityType.byString(id).orElse(null); if (type != null) { - Entity entity = type.create(nmsWorld); + Entity entity = type.create(serverLevel); if (entity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); + final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(linTag); for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } entity.load(tag); entity.absMoveTo(x, y, z, yaw, pitch); - entity.setUUID(nativeTag.getUUID()); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + entity.setUUID(NbtUtils.uuid(nativeTag)); + if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { LOGGER.warn( "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", id, - nmsWorld.getWorld().getName(), + serverLevel.getWorld().getName(), x, y, z @@ -784,30 +797,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } // set tiles - Map tiles = set.getTiles(); + Map tiles = set.tiles(); if (tiles != null && !tiles.isEmpty()) { if (syncTasks == null) { syncTasks = new Runnable[1]; } syncTasks[0] = () -> { - for (final Map.Entry entry : tiles.entrySet()) { - final CompoundTag nativeTag = entry.getValue(); + for (final Map.Entry entry : tiles.entrySet()) { + final FaweCompoundTag nativeTag = entry.getValue(); final BlockVector3 blockHash = entry.getKey(); final int x = blockHash.x() + bx; final int y = blockHash.y(); final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + synchronized (serverLevel) { + BlockEntity tileEntity = serverLevel.getBlockEntity(pos); if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); + serverLevel.removeBlockEntity(pos); + tileEntity = serverLevel.getBlockEntity(pos); } if (tileEntity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); + final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag()); tag.put("x", IntTag.valueOf(x)); tag.put("y", IntTag.valueOf(y)); tag.put("z", IntTag.valueOf(z)); @@ -823,7 +835,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc callback = null; } else { int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; - boolean finalLightUpdate = lightUpdate; callback = () -> { // Set Modified nmsChunk.setLightCorrect(true); // Set Modified @@ -929,7 +940,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public void send() { synchronized (sendLock) { - PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ); + PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ); } } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java index 146760020..7d199c7f6 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java @@ -1,21 +1,22 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4; 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.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; 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.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; +import io.papermc.lib.PaperLib; import net.minecraft.core.Holder; +import net.minecraft.nbt.Tag; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.ServerLevel; 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.PalettedContainerRO; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -39,8 +42,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private static final Logger LOGGER = LogManagerCompat.getLogger(); - private final Map tiles = new HashMap<>(); - private final Set entities = new HashSet<>(); + private final Map tiles = new HashMap<>(); + private final Set entities = new HashSet<>(); private final char[][] blocks; private final int minHeight; private final int maxHeight; @@ -57,44 +60,37 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } protected void storeTile(BlockEntity blockEntity) { + @SuppressWarnings("unchecked") + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); tiles.put( BlockVector3.at( blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getY(), 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 getTiles() { - return tiles; - } - - @Override - @Nullable - public CompoundTag getTile(int x, int y, int z) { - return tiles.get(BlockVector3.at(x, y, z)); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) protected void storeEntity(Entity entity) { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + @SuppressWarnings("unchecked") + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); entity.save(compoundTag); - entities.add((CompoundTag) adapter.toNative(compoundTag)); + entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag))); } @Override - public Set getEntities() { + public Collection entities() { return this.entities; } @Override - public CompoundTag getEntity(UUID uuid) { - for (CompoundTag tag : entities) { - if (uuid.equals(tag.getUUID())) { + public @Nullable FaweCompoundTag entity(final UUID uuid) { + for (FaweCompoundTag tag : entities) { + if (uuid.equals(NbtUtils.uuid(tag))) { return tag; } } @@ -180,8 +176,18 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { biomes[layer] = new Holder[64]; } if (biomeData instanceof PalettedContainer> palettedContainer) { - for (int i = 0; i < 64; i++) { - biomes[layer][i] = palettedContainer.get(i); + if (PaperLib.isPaper()) { + 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) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } } } else { LOGGER.error( @@ -195,7 +201,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public BaseBlock getFullBlock(int x, int y, int z) { BlockState state = BlockTypesCache.states[get(x, y, z)]; - return state.toBaseBlock(this, x, y, z); + return state.toBaseBlock((IBlocks) this, x, y, z); } @Override @@ -225,6 +231,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return BlockTypesCache.states[get(x, y, z)]; } + @Override + public Map 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 public int getSkyLight(int x, int y, int z) { return 0; diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java index 03eabc698..e7672ce81 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightPlatformAdapter.java @@ -7,9 +7,10 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.util.MathMan; -import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; +import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; @@ -76,6 +77,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -121,6 +123,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static Field LEVEL_CHUNK_ENTITIES; private static Field SERVER_LEVEL_ENTITY_MANAGER; + static final MethodHandle PALETTED_CONTAINER_GET; + static { final MethodHandles.Lookup lookup = MethodHandles.lookup(); try { @@ -210,6 +214,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } catch (NoSuchFieldException ignored) { } 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) { throw e; } catch (Exception e) { @@ -232,15 +243,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } static boolean setSectionAtomic( + String worldName, + IntPair pair, LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, int layer ) { - if (layer >= 0 && layer < sections.length) { - return ReflectionUtils.compareAndSet(sections, expected, value, layer); - } - return false; + return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. @@ -338,7 +348,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } @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); if (chunkHolder == null) { return; @@ -347,36 +357,42 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { LevelChunk levelChunk; if (PaperLib.isPaper()) { // getChunkAtIfLoadedImmediately is paper only - levelChunk = nmsWorld - .getChunkSource() - .getChunkAtIfLoadedImmediately(chunkX, chunkZ); + levelChunk = nmsWorld.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); } else { - levelChunk = chunkHolder.getTickingChunkFuture() - .getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null); + levelChunk = chunkHolder.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null); } if (levelChunk == null) { return; } + StampLockHolder lockHolder = new StampLockHolder(); + NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + if (lockHolder.chunkLock == null) { + return; + } MinecraftServer.getServer().execute(() -> { - ClientboundLevelChunkWithLightPacket packet; - if (PaperLib.isPaper()) { - packet = new ClientboundLevelChunkWithLightPacket( - levelChunk, - nmsWorld.getChunkSource().getLightEngine(), - null, - null, - false // last false is to not bother with x-ray - ); - } else { - // deprecated on paper - deprecation suppressed - packet = new ClientboundLevelChunkWithLightPacket( - levelChunk, - nmsWorld.getChunkSource().getLightEngine(), - null, - null - ); + try { + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null + ); + } + 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)); }); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightStarlightRelighter.java index ae09dcc58..c7b61575c 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightStarlightRelighter.java @@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4; import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.queue.IQueueExtent; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; @@ -67,7 +68,7 @@ public class PaperweightStarlightRelighter extends StarlightRelighter { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public class PaperweightRegen extends Regenerator { private static final Field serverWorldsField; private static final Field paperConfigField; - private static final Field flatBedrockField; - private static final Field generatorSettingFlatField; private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - private static final Field generatorStructureStateField; - private static final Field ringPositionsField; - private static final Field hasGeneratedPositionsField; - //list of chunk stati in correct order without FULL - private static final Map chunkStati = new LinkedHashMap<>(); static { - chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing - chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps - chunkStati.put( - ChunkStatus.STRUCTURE_REFERENCES, - Concurrency.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 { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - Field tmpFlatBedrockField; try { //only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); - - tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); - tmpFlatBedrockField.setAccessible(true); } catch (Exception e) { tmpPaperConfigField = null; - tmpFlatBedrockField = null; } paperConfigField = tmpPaperConfigField; - flatBedrockField = tmpFlatBedrockField; generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( "settings", "e")); generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); - chunkSourceField.setAccessible(true); - - generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v")); - generatorStructureStateField.setAccessible(true); - - ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); - ringPositionsField.setAccessible(true); - - hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( - Refraction.pickName("hasGeneratedPositions", "h") - ); - hasGeneratedPositionsField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } @@ -171,44 +78,37 @@ public class PaperweightRegen extends Regenerator super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - return true; } @Override - @SuppressWarnings("unchecked") protected boolean initNewWorld() throws Exception { //world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); @@ -254,8 +154,10 @@ public class PaperweightRegen extends Regenerator getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { if (options.hasBiomeType()) { return singleBiome; } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( - biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() - ); + return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); } + @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(); freshWorld.noSave = true; removeWorldFromWorldsMap(); @@ -293,93 +207,6 @@ public class PaperweightRegen extends Regenerator generatorSettingBaseSupplier = (Holder) - 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>> origPositions = - (Map>>) ringPositionsField.get(state); - Map>> 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; } @@ -394,7 +221,8 @@ public class PaperweightRegen extends Regenerator { try { - freshChunkProvider.close(false); + freshWorld.getChunkSource().getDataStorage().cache.clear(); + freshWorld.getChunkSource().close(false); } catch (Exception e) { throw new RuntimeException(e); } @@ -415,50 +243,9 @@ public class PaperweightRegen extends Regenerator getBlockPopulators() { - return originalServerWorld.getWorld().getPopulators(); - } - - @Override - protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { - // BlockPopulator#populate has to be called synchronously for TileEntity access - TaskManager.taskManager().task(() -> { - final CraftWorld world = freshWorld.getWorld(); - final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); - blockPopulator.populate(world, random, chunk); - }); - } - @Override protected IChunkCache initSourceQueueCache() { - return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { - @Override - public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) { - return getChunkAt(x, z); - } - }; + return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } //util @@ -512,83 +299,4 @@ public class PaperweightRegen extends Regenerator 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 getEntities() { - return Collections.emptyList(); - } - - } - - protected class ChunkStatusWrap extends ChunkStatusWrapper { - - 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 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 MAILBOX = ProcessorMailbox.create(task -> { - }, "fawe-no-op"); - private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { - }); - - public NoOpLightEngine(final ServerChunkCache chunkProvider) { - super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); - } - - @Override - public @NotNull CompletableFuture lightChunk(final @NotNull ChunkAccess chunk, final boolean excludeBlocks) { - return CompletableFuture.completedFuture(chunk); - } - - } - } diff --git a/worldedit-bukkit/adapters/adapter-1_21/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_21/build.gradle.kts index 253af67e6..4face9389 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_21/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.21.1-R0.1-SNAPSHOT/ - the().paperDevBundle("1.21.1-R0.1-20240811.223934-9") + the().paperDevBundle("1.21.1-R0.1-20241012.212042-119") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightBlockMaterial.java index 359527396..d48e5fe1d 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightBlockMaterial.java @@ -1,11 +1,8 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazyCompoundTag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.worldedit.world.registry.BlockMaterial; import net.minecraft.core.BlockPos; -import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; 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 org.bukkit.craftbukkit.block.data.CraftBlockData; +import javax.annotation.Nullable; + public class PaperweightBlockMaterial implements BlockMaterial { private final Block block; @@ -22,7 +21,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { private final CraftBlockData craftBlockData; private final org.bukkit.Material craftMaterial; private final int opacity; - private final CompoundTag tile; + private final FaweCompoundTag tile; public PaperweightBlockMaterial(Block block) { this(block, block.defaultBlockState()); @@ -38,9 +37,9 @@ public class PaperweightBlockMaterial implements BlockMaterial { BlockPos.ZERO, blockState ); - tile = tileEntity == null ? null : new PaperweightLazyCompoundTag( - Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess())) - ); + tile = tileEntity == null + ? null + : PaperweightGetBlocks.NMS_TO_TILE.apply(tileEntity); } public Block getBlock() { @@ -126,9 +125,10 @@ public class PaperweightBlockMaterial implements BlockMaterial { return blockState.isRandomlyTicking(); } + @SuppressWarnings("deprecation") @Override public boolean isMovementBlocker() { - return craftMaterial.isSolid(); + return blockState.blocksMotion(); } @Override @@ -163,7 +163,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { } @Override - public CompoundTag getDefaultTile() { + public @Nullable FaweCompoundTag defaultTile() { return tile; } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java index 11577ca9e..34fc70f8d 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java @@ -5,6 +5,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; 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.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.regen.PaperweightRegen; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; @@ -102,6 +104,7 @@ import java.util.Map; import java.util.Objects; import java.util.OptionalInt; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -137,6 +140,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter blockEntityToCompoundTag() { + return blockEntity -> FaweCompoundTag.of( + () -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess())) + ); + } + @Nullable private static String getEntityId(Entity entity) { ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); @@ -557,7 +566,7 @@ public final class PaperweightFaweAdapter extends FaweAdapter posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); - private static final Function nmsTile2We = tileEntity -> new PaperweightLazyCompoundTag( - Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess())) - ); + public static final Function NMS_TO_TILE = ((PaperweightFaweAdapter) WorldEditPlugin + .getInstance() + .getBukkitImplAdapter()).blockEntityToCompoundTag(); private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin .getInstance() .getBukkitImplAdapter()); @@ -103,6 +108,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc private final ServerLevel serverLevel; private final int chunkX; private final int chunkZ; + private final IntPair chunkPos; private final int minHeight; private final int maxHeight; private final int minSectionPosition; @@ -137,6 +143,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); + this.chunkPos = new IntPair(chunkX, chunkZ); } public int getChunkX() { @@ -258,23 +265,24 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @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) + ( chunkX << 4), y, (z & 15) + ( chunkZ << 4))); if (blockEntity == null) { return null; } - return new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))); + return NMS_TO_TILE.apply(blockEntity); + } @Override - public Map getTiles() { + public Map tiles() { Map nmsTiles = getChunk().getBlockEntities(); if (nmsTiles.isEmpty()) { return Collections.emptyMap(); } - return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); + return AdaptedMap.immutable(nmsTiles, posNms2We, NMS_TO_TILE); } @Override @@ -337,7 +345,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public CompoundTag getEntity(UUID uuid) { + public @Nullable FaweCompoundTag entity(final UUID uuid) { ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; @@ -349,10 +357,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (entity != null) { 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()) { - if (uuid.equals(tag.getUUID())) { + for (FaweCompoundTag tag : entities()) { + if (uuid.equals(NbtUtils.uuid(tag))) { return tag; } } @@ -360,14 +368,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } @Override - public Set getEntities() { + public Collection entities() { ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { - return Collections.emptySet(); + return Collections.emptyList(); } int size = entities.size(); - return new AbstractSet<>() { + return new AbstractCollection<>() { @Override public int size() { return size; @@ -380,10 +388,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public boolean contains(Object get) { - if (!(get instanceof CompoundTag getTag)) { + if (!(get instanceof FaweCompoundTag getTag)) { return false; } - UUID getUUID = getTag.getUUID(); + UUID getUUID = NbtUtils.uuid(getTag); for (Entity entity : entities) { UUID uuid = entity.getUUID(); if (uuid.equals(getUUID)) { @@ -395,15 +403,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Nonnull @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + public Iterator iterator() { + Iterable result = entities.stream().map(input -> { + CompoundTag tag = new CompoundTag(); input.save(tag); - return (CompoundTag) adapter.toNative(tag); - }).collect(Collectors.toList()); + return FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(tag)); + })::iterator; return result.iterator(); } }; + } 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."); } 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 (copies.containsKey(copyKey)) { throw new IllegalStateException("Copy key already used."); @@ -429,9 +439,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc copies.put(copyKey, copy); } try { - ServerLevel nmsWorld = serverLevel; - LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); - // Remove existing tiles. Create a copy so that we can remove blocks Map chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); List beacons = null; @@ -503,6 +510,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, null, newSection, @@ -577,6 +586,8 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, levelChunkSections, null, newSection, @@ -638,7 +649,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeRegistry, biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() ); - if (!PaperweightPlatformAdapter.setSectionAtomic(levelChunkSections, existingSection, + if (!PaperweightPlatformAdapter.setSectionAtomic( + serverLevel.getWorld().getName(), + chunkPos, + levelChunkSections, + existingSection, newSection, getSectionIndex )) { @@ -710,7 +725,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { for (UUID uuid : entityRemoves) { - Entity entity = nmsWorld.getEntities().get(uuid); + Entity entity = serverLevel.getEntities().get(uuid); if (entity != null) { removeEntity(entity); } @@ -722,48 +737,47 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc }; } - Set entities = set.getEntities(); + Collection entities = set.entities(); if (entities != null && !entities.isEmpty()) { if (syncTasks == null) { syncTasks = new Runnable[2]; } syncTasks[1] = () -> { - Iterator iterator = entities.iterator(); + Iterator iterator = entities.iterator(); while (iterator.hasNext()) { - final CompoundTag nativeTag = iterator.next(); - final Map> entityTagMap = nativeTag.getValue(); - final StringTag idTag = (StringTag) entityTagMap.get("Id"); - final ListTag posTag = (ListTag) entityTagMap.get("Pos"); - final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + final FaweCompoundTag nativeTag = iterator.next(); + final LinCompoundTag linTag = nativeTag.linTag(); + final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag()); + final LinListTag posTag = linTag.findListTag("Pos", LinTagType.doubleTag()); + final LinListTag rotTag = linTag.findListTag("Rotation", LinTagType.floatTag()); if (idTag == null || posTag == null || rotTag == null) { LOGGER.error("Unknown entity tag: {}", nativeTag); continue; } - final double x = posTag.getDouble(0); - final double y = posTag.getDouble(1); - final double z = posTag.getDouble(2); - final float yaw = rotTag.getFloat(0); - final float pitch = rotTag.getFloat(1); - final String id = idTag.getValue(); + final double x = posTag.get(0).valueAsDouble(); + final double y = posTag.get(1).valueAsDouble(); + final double z = posTag.get(2).valueAsDouble(); + final float yaw = rotTag.get(0).valueAsFloat(); + final float pitch = rotTag.get(1).valueAsFloat(); + final String id = idTag.value(); EntityType type = EntityType.byString(id).orElse(null); if (type != null) { - Entity entity = type.create(nmsWorld); + Entity entity = type.create(serverLevel); if (entity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); + final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag); for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } entity.load(tag); entity.absMoveTo(x, y, z, yaw, pitch); - entity.setUUID(nativeTag.getUUID()); - if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + entity.setUUID(NbtUtils.uuid(nativeTag)); + if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { LOGGER.warn( "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", id, - nmsWorld.getWorld().getName(), + serverLevel.getWorld().getName(), x, y, z @@ -778,30 +792,29 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } // set tiles - Map tiles = set.getTiles(); + Map tiles = set.tiles(); if (tiles != null && !tiles.isEmpty()) { if (syncTasks == null) { syncTasks = new Runnable[1]; } syncTasks[0] = () -> { - for (final Map.Entry entry : tiles.entrySet()) { - final CompoundTag nativeTag = entry.getValue(); + for (final Map.Entry entry : tiles.entrySet()) { + final FaweCompoundTag nativeTag = entry.getValue(); final BlockVector3 blockHash = entry.getKey(); final int x = blockHash.x() + bx; final int y = blockHash.y(); final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + synchronized (serverLevel) { + BlockEntity tileEntity = serverLevel.getBlockEntity(pos); if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); + serverLevel.removeBlockEntity(pos); + tileEntity = serverLevel.getBlockEntity(pos); } if (tileEntity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); + final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag()); tag.put("x", IntTag.valueOf(x)); tag.put("y", IntTag.valueOf(y)); tag.put("z", IntTag.valueOf(z)); @@ -817,7 +830,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc callback = null; } else { int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; - boolean finalLightUpdate = lightUpdate; callback = () -> { // Set Modified nmsChunk.setLightCorrect(true); // Set Modified @@ -923,7 +935,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public void send() { synchronized (sendLock) { - PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ); + PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ); } } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java index a0a4d02d9..18b557b97 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java @@ -1,21 +1,22 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; 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.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; 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.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; +import io.papermc.lib.PaperLib; import net.minecraft.core.Holder; +import net.minecraft.nbt.Tag; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.ServerLevel; 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.PalettedContainerRO; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -39,8 +42,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { private static final Logger LOGGER = LogManagerCompat.getLogger(); - private final Map tiles = new HashMap<>(); - private final Set entities = new HashSet<>(); + private final Map tiles = new HashMap<>(); + private final Set entities = new HashSet<>(); private final char[][] blocks; private final int minHeight; private final int maxHeight; @@ -57,44 +60,37 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } protected void storeTile(BlockEntity blockEntity) { + @SuppressWarnings("unchecked") + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); tiles.put( BlockVector3.at( blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getY(), 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 getTiles() { - return tiles; - } - - @Override - @Nullable - public CompoundTag getTile(int x, int y, int z) { - return tiles.get(BlockVector3.at(x, y, z)); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) protected void storeEntity(Entity entity) { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + @SuppressWarnings("unchecked") + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); entity.save(compoundTag); - entities.add((CompoundTag) adapter.toNative(compoundTag)); + entities.add(FaweCompoundTag.of((LinCompoundTag) adapter.toNativeLin(compoundTag))); } @Override - public Set getEntities() { + public Collection entities() { return this.entities; } @Override - public CompoundTag getEntity(UUID uuid) { - for (CompoundTag tag : entities) { - if (uuid.equals(tag.getUUID())) { + public @Nullable FaweCompoundTag entity(final UUID uuid) { + for (FaweCompoundTag tag : entities) { + if (uuid.equals(NbtUtils.uuid(tag))) { return tag; } } @@ -180,8 +176,18 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { biomes[layer] = new Holder[64]; } if (biomeData instanceof PalettedContainer> palettedContainer) { - for (int i = 0; i < 64; i++) { - biomes[layer][i] = palettedContainer.get(i); + if (PaperLib.isPaper()) { + 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) PaperweightPlatformAdapter.PALETTED_CONTAINER_GET.invoke(i); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } } } else { LOGGER.error( @@ -195,7 +201,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { @Override public BaseBlock getFullBlock(int x, int y, int z) { BlockState state = BlockTypesCache.states[get(x, y, z)]; - return state.toBaseBlock(this, x, y, z); + return state.toBaseBlock((IBlocks) this, x, y, z); } @Override @@ -225,6 +231,16 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { return BlockTypesCache.states[get(x, y, z)]; } + @Override + public Map 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 public int getSkyLight(int x, int y, int z) { return 0; diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java index ab55c7814..662140955 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java @@ -8,9 +8,10 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.util.MathMan; -import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; +import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; @@ -31,7 +32,6 @@ import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.BitStorage; -import net.minecraft.util.ExceptionCollector; import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.ThreadingDetector; import net.minecraft.util.Unit; @@ -76,6 +76,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -119,6 +120,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static Method PAPER_CHUNK_GEN_ALL_ENTITIES; private static Field SERVER_LEVEL_ENTITY_MANAGER; + static final MethodHandle PALETTED_CONTAINER_GET; + static { final MethodHandles.Lookup lookup = MethodHandles.lookup(); 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.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) { throw e; } catch (Exception e) { @@ -217,15 +227,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } static boolean setSectionAtomic( + String worldName, + IntPair pair, LevelChunkSection[] sections, LevelChunkSection expected, LevelChunkSection value, int layer ) { - if (layer >= 0 && layer < sections.length) { - return ReflectionUtils.compareAndSet(sections, expected, value, layer); - } - return false; + return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer); } // There is no point in having a functional semaphore for paper servers. @@ -323,7 +332,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } @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); if (chunkHolder == null) { return; @@ -332,36 +341,42 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { LevelChunk levelChunk; if (PaperLib.isPaper()) { // getChunkAtIfLoadedImmediately is paper only - levelChunk = nmsWorld - .getChunkSource() - .getChunkAtIfLoadedImmediately(chunkX, chunkZ); + levelChunk = nmsWorld.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); } else { - levelChunk = chunkHolder.getTickingChunkFuture() - .getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null); + levelChunk = chunkHolder.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).orElse(null); } if (levelChunk == null) { return; } + StampLockHolder lockHolder = new StampLockHolder(); + NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder); + if (lockHolder.chunkLock == null) { + return; + } MinecraftServer.getServer().execute(() -> { - ClientboundLevelChunkWithLightPacket packet; - if (PaperLib.isPaper()) { - packet = new ClientboundLevelChunkWithLightPacket( - levelChunk, - nmsWorld.getChunkSource().getLightEngine(), - null, - null, - false // last false is to not bother with x-ray - ); - } else { - // deprecated on paper - deprecation suppressed - packet = new ClientboundLevelChunkWithLightPacket( - levelChunk, - nmsWorld.getChunkSource().getLightEngine(), - null, - null - ); + try { + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null, + false // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null + ); + } + 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)); }); } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightStarlightRelighter.java index f9d06922e..aa7b19b39 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightStarlightRelighter.java @@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.queue.IQueueExtent; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; @@ -70,7 +71,7 @@ public class PaperweightStarlightRelighter extends StarlightRelighter { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public class PaperweightRegen extends Regenerator { private static final Field serverWorldsField; private static final Field paperConfigField; - private static final Field flatBedrockField; - private static final Field generatorSettingFlatField; private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - private static final Field generatorStructureStateField; - private static final Field ringPositionsField; - private static final Field hasGeneratedPositionsField; - //list of chunk stati in correct order without FULL - private static final Map chunkStati = new LinkedHashMap<>(); static { - chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing - chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps - chunkStati.put( - ChunkStatus.STRUCTURE_REFERENCES, - Concurrency.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 { serverWorldsField = CraftServer.class.getDeclaredField("worlds"); serverWorldsField.setAccessible(true); Field tmpPaperConfigField; - Field tmpFlatBedrockField; try { //only present on paper tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); tmpPaperConfigField.setAccessible(true); - - tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); - tmpFlatBedrockField.setAccessible(true); } catch (Exception e) { tmpPaperConfigField = null; - tmpFlatBedrockField = null; } paperConfigField = tmpPaperConfigField; - flatBedrockField = tmpFlatBedrockField; generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( "settings", "e")); generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); - chunkSourceField.setAccessible(true); - - generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "w")); - generatorStructureStateField.setAccessible(true); - - ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); - ringPositionsField.setAccessible(true); - - hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( - Refraction.pickName("hasGeneratedPositions", "h") - ); - hasGeneratedPositionsField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } @@ -177,47 +78,37 @@ public class PaperweightRegen extends Regenerator super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - return true; } @Override - @SuppressWarnings("unchecked") protected boolean initNewWorld() throws Exception { //world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); @@ -263,8 +154,10 @@ public class PaperweightRegen extends Regenerator getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { if (options.hasBiomeType()) { return singleBiome; } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( - biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() - ); + return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); } + @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(); freshWorld.noSave = true; removeWorldFromWorldsMap(); @@ -302,97 +207,6 @@ public class PaperweightRegen extends Regenerator generatorSettingBaseSupplier = (Holder) - 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>> origPositions = - (Map>>) ringPositionsField.get(state); - Map>> 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; } @@ -407,7 +221,8 @@ public class PaperweightRegen extends Regenerator { try { - freshChunkProvider.close(false); + freshWorld.getChunkSource().getDataStorage().cache.clear(); + freshWorld.getChunkSource().close(false); } catch (Exception e) { throw new RuntimeException(e); } @@ -428,50 +243,9 @@ public class PaperweightRegen extends Regenerator getBlockPopulators() { - return originalServerWorld.getWorld().getPopulators(); - } - - @Override - protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { - // BlockPopulator#populate has to be called synchronously for TileEntity access - TaskManager.taskManager().task(() -> { - final CraftWorld world = freshWorld.getWorld(); - final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); - blockPopulator.populate(world, random, chunk); - }); - } - @Override protected IChunkCache initSourceQueueCache() { - return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { - @Override - public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) { - return getChunkAt(x, z); - } - }; + return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } //util @@ -525,99 +299,4 @@ public class PaperweightRegen extends Regenerator 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 getEntities() { - return Collections.emptyList(); - } - - } - - protected class ChunkStatusWrap extends ChunkStatusWrapper { - - 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 accessibleChunks) { - ChunkAccess chunkAccess = accessibleChunks.get(accessibleChunks.size() / 2); - int chunkX = chunkAccess.getPos().x; - int chunkZ = chunkAccess.getPos().z; - getProtoChunkAt(chunkX, chunkZ); - StaticCache2D 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 MAILBOX = ProcessorMailbox.create(task -> { - }, "fawe-no-op"); - private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { - }); - - public NoOpLightEngine(final ServerChunkCache chunkProvider) { - super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); - } - - @Override - public @NotNull CompletableFuture lightChunk(final @NotNull ChunkAccess chunk, final boolean excludeBlocks) { - return CompletableFuture.completedFuture(chunk); - } - - } - } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkitWorld.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkitWorld.java new file mode 100644 index 000000000..375351d56 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkitWorld.java @@ -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 CACHE = Collections.synchronizedMap(new WeakHashMap<>()); + + private final ConcurrentHashMap 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 getWorldSendingChunksMap(FaweBukkitWorld world) { + return world.SENDING_CHUNKS; + } + + public static ConcurrentHashMap 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); + } + } + +} diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java index 711c0fecc..4fc00996a 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java @@ -1,12 +1,17 @@ package com.fastasyncworldedit.bukkit.adapter; +import com.fastasyncworldedit.bukkit.FaweBukkitWorld; import com.fastasyncworldedit.core.FAWEPlatformAdapterImpl; +import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.util.MathMan; +import com.fastasyncworldedit.core.util.ReflectionUtils; import com.sk89q.worldedit.world.block.BlockTypesCache; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.StampedLock; import java.util.function.Function; public class NMSAdapter implements FAWEPlatformAdapterImpl { @@ -140,4 +145,118 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl { ((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. + *

+ * 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. + *

+ * 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 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 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. + *

+ * 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. + *

+ * Utilises ConcurrentHashMap#compute for easy synchronisation + * + * @since TODO + */ + protected static void beginChunkPacketSend(String worldName, IntPair pair, StampLockHolder stampedLock) { + ConcurrentHashMap 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 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; + + } + } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java index 4afbe03a2..ac75a29f6 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java @@ -1,67 +1,32 @@ package com.fastasyncworldedit.bukkit.adapter; -import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent; -import com.fastasyncworldedit.core.util.MathMan; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.extent.Extent; 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.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; 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.BlockPopulator; import org.bukkit.generator.WorldInfo; -import java.util.AbstractList; -import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Objects; -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.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; import java.util.function.Function; /** * Represents an abstract regeneration handler. - * - * @param the type of the {@code IChunkAccess} of the current Minecraft implementation - * @param the type of the {@code ProtoChunk} of the current Minecraft implementation - * @param the type of the {@code Chunk} of the current Minecraft implementation - * @param the type of the {@code ChunkStatusWrapper} wrapping the {@code ChunkStatus} enum */ -public abstract class Regenerator> { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public abstract class Regenerator { protected final org.bukkit.World originalBukkitWorld; protected final Region region; @@ -69,13 +34,8 @@ public abstract class Regenerator chunkStatuses = new LinkedHashMap<>(); // TODO (j21): use SequencedMap - private final Long2ObjectLinkedOpenHashMap protoChunks = new Long2ObjectLinkedOpenHashMap<>(); - private final Long2ObjectOpenHashMap chunks = new Long2ObjectOpenHashMap<>(); - protected boolean generateConcurrent = true; protected long seed; - private ExecutorService executor; - private SingleThreadQueueExtent source; + protected SingleThreadQueueExtent source; /** * Initializes an abstract regeneration handler. @@ -92,15 +52,6 @@ public abstract class Regenerator chunkCoordsForChunkStatus = new LinkedHashMap<>(); - int borderSum = 1; - // TODO (j21): use SequencedMap#sequencedKeySet().reversed() - final List 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 { - 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 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> tasks = getChunkStatusTaskRows(coords, radius); - for (ConcurrentTasks para : tasks) { - List 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 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 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); - }); - } + private void createSource() { source = new SingleThreadQueueExtent( BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMinHeight() : 0, BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMaxHeight() : 256 ); source.init(target, initSourceQueueCache(), null); - return true; - } - - private void runAndWait(final List tasks) { - try { - List> 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() { + 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 boolean genbiomes = options.shouldRegenBiomes(); boolean hasBiome = options.hasBiomeType(); @@ -343,6 +117,7 @@ public abstract class Regenerator source.getBiome(vec))); } + TaskManager.taskManager().cancel(taskId); } private class PlacementPattern implements Pattern { @@ -382,9 +157,6 @@ public abstract class Regenerator 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} here. Use will need the {@code getChunkAt} function * @@ -464,106 +195,6 @@ public abstract class Regenerator 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> 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> tasks; - if (maxZ - minZ > maxX - minX) { - int numlists = Math.min(requiredNeighbors * 2 + 1, maxX - minX + 1); - - Int2ObjectOpenHashMap 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 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 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 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() { if (options.hasBiomeType()) { return new SingleBiomeProvider(); @@ -579,103 +210,6 @@ public abstract class Regenerator the IChunkAccess class of the current Minecraft implementation - */ - public static abstract class ChunkStatusWrapper { - - /** - * 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 accessibleChunks); - - void processChunkSave(long xz, List 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 extends Tasks { - - public SequentialTasks(int expectedSize) { - super(expectedSize); - } - - } - - public static class ConcurrentTasks extends Tasks { - - public ConcurrentTasks(int expectedSize) { - super(expectedSize); - } - - } - - public static class Tasks implements Iterable { - - private final List tasks; - - public Tasks(int expectedSize) { - tasks = new ArrayList<>(expectedSize); - } - - public void add(T task) { - tasks.add(task); - } - - public List list() { - return tasks; - } - - public int size() { - return tasks.size(); - } - - @Override - public Iterator iterator() { - return tasks.iterator(); - } - - @Override - public String toString() { - return tasks.toString(); - } - - } - public class SingleBiomeProvider extends BiomeProvider { private final org.bukkit.block.Biome biome = BukkitAdapter.adapt(options.getBiomeType()); diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/GriefDefenderFeature.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/GriefDefenderFeature.java index 9ac2fd68a..f1baf3d9e 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/GriefDefenderFeature.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/GriefDefenderFeature.java @@ -3,7 +3,9 @@ package com.fastasyncworldedit.bukkit.regions; import com.fastasyncworldedit.core.regions.FaweMask; import com.griefdefender.api.GriefDefender; import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimManager; import com.griefdefender.api.claim.TrustTypes; +import com.griefdefender.lib.flowpowered.math.vector.Vector3i; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; @@ -17,8 +19,8 @@ public class GriefDefenderFeature extends BukkitMaskManager implements Listener private static final Logger LOGGER = LogManagerCompat.getLogger(); - public GriefDefenderFeature(final Plugin GriefDefenderPlugin) { - super(GriefDefenderPlugin.getName()); + public GriefDefenderFeature(final Plugin plugin) { + super(plugin.getName()); 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)) { + private final int[] bounds = new int[]{ + pos1.x(), pos1.y(), pos1.z(), + pos2.x(), pos2.y(), pos2.z() + }; + @Override 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; } + 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; + } + } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 66507c151..9e4da5401 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -24,12 +24,12 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.internal.exception.FaweException; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; @@ -118,9 +118,9 @@ public class BukkitWorld extends AbstractWorld { HAS_MIN_Y = temp; } - private WeakReference worldRef; + protected WeakReference worldRef; //FAWE start - private final String worldNameRef; + protected final String worldNameRef; //FAWE end private final WorldNativeAccess worldNativeAccess; @@ -665,7 +665,7 @@ public class BukkitWorld extends AbstractWorld { } @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; } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java index 5cf783350..f55029ac4 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java @@ -165,9 +165,11 @@ public class BukkitImplLoader { * @throws AdapterLoadException thrown if no adapter could be found */ public BukkitImplAdapter loadAdapter() throws AdapterLoadException { + // FAWE - do not initialize classes on lookup + final ClassLoader classLoader = this.getClass().getClassLoader(); for (String className : adapterCandidates) { try { - Class cls = Class.forName(className); + Class cls = Class.forName(className, false, classLoader); if (cls.isSynthetic()) { continue; } diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java index e0b4c64f2..1502fdbf3 100644 --- a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java +++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java @@ -19,10 +19,10 @@ package com.sk89q.worldedit.cli.schematic; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.google.common.collect.ImmutableSet; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; @@ -193,7 +193,7 @@ public class ClipboardWorld extends AbstractWorld implements Clipboard, CLIWorld } @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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java index 829f29da6..dac6fe9d6 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java @@ -365,6 +365,18 @@ public class Fawe { 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 { byte[] in = new byte[0]; byte[] compressed = LZ4Factory.fastestJavaInstance().fastCompressor().compress(in); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index 526570296..3ed494298 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -192,17 +192,22 @@ public enum FaweCache implements Trimable { Type.OUTSIDE_REGION ); 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, true ); 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, false ); 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, false ); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index 3b80dcdfa..60ec9311a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -123,29 +123,31 @@ public class Settings extends Config { limit.MAX_ACTIONS, newLimit.MAX_ACTIONS != -1 ? newLimit.MAX_ACTIONS : Integer.MAX_VALUE ); - limit.MAX_CHANGES = Math.max( - limit.MAX_CHANGES, + limit.MAX_CHANGES.set(Math.max( + limit.MAX_CHANGES.get(), newLimit.MAX_CHANGES != -1 ? newLimit.MAX_CHANGES : Long.MAX_VALUE - ); - limit.MAX_BLOCKSTATES = Math.max( - limit.MAX_BLOCKSTATES, + )); + limit.MAX_BLOCKSTATES.set(Math.max( + limit.MAX_BLOCKSTATES.get(), newLimit.MAX_BLOCKSTATES != -1 ? newLimit.MAX_BLOCKSTATES : Integer.MAX_VALUE - ); - limit.MAX_CHECKS = Math.max( - limit.MAX_CHECKS, + )); + limit.MAX_CHECKS.set(Math.max( + limit.MAX_CHECKS.get(), newLimit.MAX_CHECKS != -1 ? newLimit.MAX_CHECKS : Long.MAX_VALUE - ); - limit.MAX_ENTITIES = Math.max( - limit.MAX_ENTITIES, + )); + limit.MAX_ENTITIES.set(Math.max( + limit.MAX_ENTITIES.get(), 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_ITERATIONS = Math.max( - limit.MAX_ITERATIONS, 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_FAILS.set(Math.max( + limit.MAX_FAILS.get(), + newLimit.MAX_FAILS != -1 ? newLimit.MAX_FAILS : Integer.MAX_VALUE + )); + limit.MAX_ITERATIONS.set(Math.max( + 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, 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 { + @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({ "[UNSAFE] Directly modify the region files. (OBSOLETE - USE ANVIL COMMANDS)", " - IMPROPER USE CAN CAUSE WORLD CORRUPTION!", @@ -668,6 +677,11 @@ public class Settings extends Config { }) 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"}) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AdjacentMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AdjacentMaskParser.java index 8fbcb4ae0..7521eb10a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AdjacentMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AdjacentMaskParser.java @@ -19,9 +19,9 @@ public class AdjacentMaskParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream(); } else if (index == 1 || index == 2) { return SuggestionHelper.suggestPositiveDoubles(argumentInput); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AngleMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AngleMaskParser.java index dfd62b9b8..ea8e36665 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AngleMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AngleMaskParser.java @@ -22,7 +22,7 @@ public class AngleMaskParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0 || index == 1) { return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); } else if (index > 1 && index <= 1 + flags.length) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/BesideMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/BesideMaskParser.java index 954b9f53d..fd597a216 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/BesideMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/BesideMaskParser.java @@ -18,9 +18,9 @@ public class BesideMaskParser extends RichParser { } @Override - protected Stream getSuggestions(final String argumentInput, final int index) { + protected Stream getSuggestions(final String argumentInput, final int index, ParserContext context) { if (index == 0) { - return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream(); } else if (index == 1 || index == 2) { return SuggestionHelper.suggestPositiveDoubles(argumentInput); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/ExtremaMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/ExtremaMaskParser.java index a875ec802..6bab51981 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/ExtremaMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/ExtremaMaskParser.java @@ -22,7 +22,7 @@ public class ExtremaMaskParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0 || index == 1) { return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); } else if (index > 1 && index <= 1 + flags.length) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/ROCAngleMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/ROCAngleMaskParser.java index dff4e8f32..00a285b95 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/ROCAngleMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/ROCAngleMaskParser.java @@ -22,7 +22,7 @@ public class ROCAngleMaskParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0 || index == 1) { return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); } else if (index > 1 && index <= 1 + flags.length) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RadiusMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RadiusMaskParser.java index 8a57c73af..559b3bab3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RadiusMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RadiusMaskParser.java @@ -18,7 +18,7 @@ public class RadiusMaskParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0 || index == 1) { return SuggestionHelper.suggestPositiveIntegers(argumentInput); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichOffsetMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichOffsetMaskParser.java index f6a21ef6f..47b56cb56 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichOffsetMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichOffsetMaskParser.java @@ -24,12 +24,12 @@ public class RichOffsetMaskParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index < 3) { return SuggestionHelper.suggestPositiveIntegers(argumentInput); } if (index == 3) { - return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SimplexMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SimplexMaskParser.java index 4674057b2..f1d2e6c55 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SimplexMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SimplexMaskParser.java @@ -20,7 +20,7 @@ public class SimplexMaskParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index < 3) { return SuggestionHelper.suggestPositiveDoubles(argumentInput); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SurfaceAngleMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SurfaceAngleMaskParser.java index ab011f412..7a85e708b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SurfaceAngleMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/SurfaceAngleMaskParser.java @@ -23,7 +23,7 @@ public class SurfaceAngleMaskParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index <= 2) { return SuggestionHelper.suggestPositiveDoubles(argumentInput); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/AngleColorPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/AngleColorPatternParser.java index 7074fdb13..d471435e6 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/AngleColorPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/AngleColorPatternParser.java @@ -25,7 +25,7 @@ public class AngleColorPatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index != 0) { return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/AverageColorPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/AverageColorPatternParser.java index 35d28382d..1fd2e8b2f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/AverageColorPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/AverageColorPatternParser.java @@ -25,7 +25,7 @@ public class AverageColorPatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index > 4) { return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BiomePatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BiomePatternParser.java index 0adbe2cd7..74e1b5b65 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BiomePatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BiomePatternParser.java @@ -54,7 +54,7 @@ public class BiomePatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { return BiomeType.REGISTRY.getSuggestions(argumentInput); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BufferedPattern2DParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BufferedPattern2DParser.java index 246e024c6..4ae760918 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BufferedPattern2DParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BufferedPattern2DParser.java @@ -26,9 +26,9 @@ public class BufferedPattern2DParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BufferedPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BufferedPatternParser.java index 49fd27495..3660aa902 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BufferedPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/BufferedPatternParser.java @@ -26,9 +26,9 @@ public class BufferedPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/ColorPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/ColorPatternParser.java index 9b424d5eb..0363b2ec5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/ColorPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/ColorPatternParser.java @@ -26,7 +26,7 @@ public class ColorPatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index > 4) { return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/DesaturatePatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/DesaturatePatternParser.java index 2218f479e..d5ace9036 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/DesaturatePatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/DesaturatePatternParser.java @@ -25,7 +25,7 @@ public class DesaturatePatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { return SuggestionHelper.suggestPositiveDoubles(argumentInput); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear2DPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear2DPatternParser.java index 7da0b2377..3e77a6ef1 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear2DPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear2DPatternParser.java @@ -28,9 +28,9 @@ public class Linear2DPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { 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); default -> Stream.empty(); }; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear3DPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear3DPatternParser.java index cd3a7d8db..7f7ffd7aa 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear3DPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear3DPatternParser.java @@ -28,9 +28,9 @@ public class Linear3DPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { 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); default -> Stream.empty(); }; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/LinearPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/LinearPatternParser.java index 785e8261d..e0d5d3105 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/LinearPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/LinearPatternParser.java @@ -28,9 +28,9 @@ public class LinearPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { 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); default -> Stream.empty(); }; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/MaskedPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/MaskedPatternParser.java index 3aed039c3..92fcd6f5f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/MaskedPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/MaskedPatternParser.java @@ -25,10 +25,10 @@ public class MaskedPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { return switch (index) { - case 0 -> this.worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); - case 1, 2 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + case 0 -> this.worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream(); + case 1, 2 -> this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); default -> Stream.empty(); }; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoXPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoXPatternParser.java index 8e3ac729f..915963d4c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoXPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoXPatternParser.java @@ -24,9 +24,9 @@ public class NoXPatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoYPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoYPatternParser.java index 2cc00c8ad..34e3724a4 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoYPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoYPatternParser.java @@ -24,9 +24,9 @@ public class NoYPatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoZPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoZPatternParser.java index 927d13d0c..aaa9cf5fd 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoZPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoZPatternParser.java @@ -24,9 +24,9 @@ public class NoZPatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoisePatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoisePatternParser.java index 37ad97417..d91a66f44 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoisePatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/NoisePatternParser.java @@ -36,12 +36,12 @@ public abstract class NoisePatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { return SuggestionHelper.suggestPositiveDoubles(argumentInput); } if (index == 1) { - return worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/OffsetPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/OffsetPatternParser.java index 6ae72c7a0..ff203208c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/OffsetPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/OffsetPatternParser.java @@ -25,9 +25,9 @@ public class OffsetPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { 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); default -> Stream.empty(); }; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomFullClipboardPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomFullClipboardPatternParser.java index 2af68adc8..587d8d81f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomFullClipboardPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomFullClipboardPatternParser.java @@ -4,6 +4,7 @@ import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.extension.factory.parser.RichParser; import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.function.pattern.RandomFullClipboardPattern; +import com.google.common.base.Function; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.input.InputParseException; @@ -33,7 +34,7 @@ public class RandomFullClipboardPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { switch (index) { case 0: if (argumentInput.equals("#") || argumentInput.equals("#c")) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomOffsetPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomOffsetPatternParser.java index 2486ef119..6d910b0eb 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomOffsetPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomOffsetPatternParser.java @@ -25,9 +25,9 @@ public class RandomOffsetPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { 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); default -> Stream.empty(); }; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RelativePatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RelativePatternParser.java index bf56c1ddb..43c4d0fbf 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RelativePatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RelativePatternParser.java @@ -24,9 +24,9 @@ public class RelativePatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SaturatePatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SaturatePatternParser.java index bb40f029d..e93521529 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SaturatePatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SaturatePatternParser.java @@ -25,7 +25,7 @@ public class SaturatePatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index > 3) { return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SolidRandomOffsetPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SolidRandomOffsetPatternParser.java index 618c463d6..ebc6b74b5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SolidRandomOffsetPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SolidRandomOffsetPatternParser.java @@ -25,9 +25,9 @@ public class SolidRandomOffsetPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { 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); default -> Stream.empty(); }; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SurfaceRandomOffsetPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SurfaceRandomOffsetPatternParser.java index aa28b11d8..31a7060d5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SurfaceRandomOffsetPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SurfaceRandomOffsetPatternParser.java @@ -25,9 +25,9 @@ public class SurfaceRandomOffsetPatternParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { 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); default -> Stream.empty(); }; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java index 5bf33d3a0..1c4d25b91 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java @@ -28,7 +28,7 @@ public class TypeSwapPatternParser extends RichParser { } @Override - public Stream getSuggestions(String argumentInput, int index) { + public Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index > 2) { return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/Linear3DTransformParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/Linear3DTransformParser.java index 01d4868d3..ef72110c4 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/Linear3DTransformParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/Linear3DTransformParser.java @@ -23,9 +23,9 @@ public class Linear3DTransformParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/LinearTransformParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/LinearTransformParser.java index 026f38338..fb646b56b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/LinearTransformParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/LinearTransformParser.java @@ -23,9 +23,9 @@ public class LinearTransformParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/OffsetTransformParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/OffsetTransformParser.java index 471359b78..3aeebfd45 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/OffsetTransformParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/OffsetTransformParser.java @@ -26,11 +26,11 @@ public class OffsetTransformParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index < 3) { return SuggestionHelper.suggestPositiveIntegers(argumentInput); } else if (index == 3) { - return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/PatternTransformParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/PatternTransformParser.java index cf81db4ce..6405fa5ec 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/PatternTransformParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/PatternTransformParser.java @@ -24,11 +24,11 @@ public class PatternTransformParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index == 0) { - return worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getPatternFactory().getSuggestions(argumentInput, context).stream(); } else if (index == 1) { - return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/RotateTransformParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/RotateTransformParser.java index 19ae8a599..c7f756efd 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/RotateTransformParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/RotateTransformParser.java @@ -25,12 +25,12 @@ public class RotateTransformParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index < 3) { return SuggestionHelper.suggestPositiveDoubles(argumentInput); } if (index == 3) { - return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/ScaleTransformParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/ScaleTransformParser.java index 4bab68d0f..028c79f05 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/ScaleTransformParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/ScaleTransformParser.java @@ -24,11 +24,11 @@ public class ScaleTransformParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index < 3) { return SuggestionHelper.suggestPositiveDoubles(argumentInput); } else if (index == 3) { - return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/SpreadTransformParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/SpreadTransformParser.java index 2f71407b2..b2ad0acbd 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/SpreadTransformParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/transform/SpreadTransformParser.java @@ -24,11 +24,11 @@ public class SpreadTransformParser extends RichParser { } @Override - protected Stream getSuggestions(String argumentInput, int index) { + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { if (index < 3) { return SuggestionHelper.suggestPositiveIntegers(argumentInput); } else if (index == 3) { - return worldEdit.getTransformFactory().getSuggestions(argumentInput).stream(); + return worldEdit.getTransformFactory().getSuggestions(argumentInput, context).stream(); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/platform/binding/EditSessionHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/platform/binding/EditSessionHolder.java new file mode 100644 index 000000000..1fd588ec4 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/platform/binding/EditSessionHolder.java @@ -0,0 +1,7 @@ +package com.fastasyncworldedit.core.extension.platform.binding; + +import com.sk89q.worldedit.EditSession; + +public record EditSessionHolder(EditSession session) { + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/platform/binding/ProvideBindings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/platform/binding/ProvideBindings.java index bc1e2bfbf..9ac01d9c3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/platform/binding/ProvideBindings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/platform/binding/ProvideBindings.java @@ -3,7 +3,11 @@ package com.fastasyncworldedit.core.extension.platform.binding; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.database.DBHandler; 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.util.ExtentTraverser; import com.fastasyncworldedit.core.util.TextureUtil; import com.fastasyncworldedit.core.util.image.ImageUtil; import com.sk89q.worldedit.EditSession; @@ -11,6 +15,7 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.argument.Arguments; 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.extension.input.InputParseException; import com.sk89q.worldedit.extension.platform.Actor; @@ -25,6 +30,7 @@ import org.enginehub.piston.inject.Key; import org.enginehub.piston.util.ValueProvider; import java.awt.image.BufferedImage; +import java.lang.reflect.Method; import java.net.URI; import java.util.Optional; @@ -52,11 +58,33 @@ public class ProvideBindings extends Bindings { @Binding 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); String command = arguments == null ? null : arguments.get(); - EditSession editSession = localSession.createEditSession(actor, command); - editSession.enableStandardMode(); - Request.request().setEditSession(editSession); + boolean synchronousSetting = commandMethod.getAnnotation(SynchronousSettingExpected.class) != null; + EditSessionHolder holder = context.injectedValue(Key.of(EditSessionHolder.class)).orElse(null); + 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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HistoryExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HistoryExtent.java index daad170da..720b5e57b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HistoryExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HistoryExtent.java @@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.extent; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; import com.fastasyncworldedit.core.math.MutableBlockVector3; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; @@ -74,7 +75,7 @@ public class HistoryExtent extends AbstractDelegateExtent { public Entity createEntity(Location location, BaseEntity state) { final Entity entity = super.createEntity(location, state); if (state != null) { - this.changeSet.addEntityCreate(state.getNbtData()); + this.changeSet.addEntityCreate(FaweCompoundTag.of(state.getNbt())); } return entity; } @@ -84,7 +85,7 @@ public class HistoryExtent extends AbstractDelegateExtent { public Entity createEntity(Location location, BaseEntity state, UUID uuid) { final Entity entity = super.createEntity(location, state, uuid); if (state != null) { - this.changeSet.addEntityCreate(state.getNbtData()); + this.changeSet.addEntityCreate(FaweCompoundTag.of(state.getNbt())); } return entity; } @@ -154,11 +155,10 @@ public class HistoryExtent extends AbstractDelegateExtent { @Override public boolean remove() { - final Location location = this.entity.getLocation(); final BaseEntity state = this.entity.getState(); final boolean success = this.entity.remove(); if (state != null && success) { - HistoryExtent.this.changeSet.addEntityRemove(state.getNbtData()); + HistoryExtent.this.changeSet.addEntityRemove(FaweCompoundTag.of(state.getNbt())); } return success; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java index d02ff9320..0bef1afc2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java @@ -1,11 +1,15 @@ package com.fastasyncworldedit.core.extent; import com.fastasyncworldedit.core.extent.filter.block.ExtentFilterBlock; -import com.fastasyncworldedit.core.function.generator.GenBase; -import com.fastasyncworldedit.core.function.generator.Resource; +import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.limit.FaweLimit; 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.worldedit.MaxChangedBlocksException; 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.math.BlockVector3; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.formatting.text.Component; @@ -37,18 +40,22 @@ import java.util.Set; import java.util.UUID; import java.util.function.Consumer; -public class LimitExtent extends AbstractDelegateExtent { +public class LimitExtent extends AbstractDelegateExtent implements IBatchProcessor { private final FaweLimit limit; private final boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length]; private final Consumer onErrorMessage; + private final int chunk_size; + private boolean processing; /** * Create a new instance. * * @param extent the extent * @param limit the limit + * @deprecated Use {@link LimitExtent#LimitExtent(Extent, FaweLimit, Consumer, boolean)} */ + @Deprecated(forRemoval = true, since = "TODO") public LimitExtent(Extent extent, FaweLimit limit) { this(extent, limit, c -> { }); @@ -60,11 +67,33 @@ public class LimitExtent extends AbstractDelegateExtent { * @param extent the extent * @param limit the limit * @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 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 onErrorMessage, + boolean processing + ) { super(extent); this.limit = limit; this.onErrorMessage = onErrorMessage; + this.chunk_size = 16 * 16 * (extent.getMaxY() - extent.getMinY()); + this.processing = processing; } private void handleException(FaweException e) { @@ -81,7 +110,7 @@ public class LimitExtent extends AbstractDelegateExtent { public List getEntities(Region region) { limit.THROW_MAX_CHECKS(region.getVolume()); try { - return super.getEntities(region); + return extent.getEntities(region); } catch (FaweException e) { handleException(e); return Collections.emptyList(); @@ -92,7 +121,7 @@ public class LimitExtent extends AbstractDelegateExtent { public List getEntities() { limit.THROW_MAX_CHECKS(); try { - return super.getEntities(); + return extent.getEntities(); } catch (FaweException e) { handleException(e); return Collections.emptyList(); @@ -105,7 +134,7 @@ public class LimitExtent extends AbstractDelegateExtent { limit.THROW_MAX_CHANGES(); limit.THROW_MAX_ENTITIES(); try { - return super.createEntity(location, entity); + return extent.createEntity(location, entity); } catch (FaweException e) { handleException(e); return null; @@ -118,7 +147,7 @@ public class LimitExtent extends AbstractDelegateExtent { limit.THROW_MAX_CHANGES(); limit.THROW_MAX_ENTITIES(); try { - return super.createEntity(location, entity, uuid); + return extent.createEntity(location, entity, uuid); } catch (FaweException e) { handleException(e); return null; @@ -130,7 +159,7 @@ public class LimitExtent extends AbstractDelegateExtent { limit.THROW_MAX_CHANGES(); limit.THROW_MAX_ENTITIES(); try { - super.removeEntity(x, y, z, uuid); + extent.removeEntity(x, y, z, uuid); } catch (FaweException e) { handleException(e); } @@ -138,9 +167,9 @@ public class LimitExtent extends AbstractDelegateExtent { @Override 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 { - return super.regenerateChunk(x, z, type, seed); + return extent.regenerateChunk(x, z, type, seed); } catch (FaweException e) { handleException(e); return false; @@ -151,7 +180,7 @@ public class LimitExtent extends AbstractDelegateExtent { public int getHighestTerrainBlock(int x, int z, int minY, int maxY) { limit.THROW_MAX_CHECKS(maxY - minY + 1); try { - return super.getHighestTerrainBlock(x, z, minY, maxY); + return extent.getHighestTerrainBlock(x, z, minY, maxY); } catch (FaweException e) { handleException(e); return minY; @@ -162,7 +191,7 @@ public class LimitExtent extends AbstractDelegateExtent { public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) { limit.THROW_MAX_CHECKS(maxY - minY + 1); try { - return super.getHighestTerrainBlock(x, z, minY, maxY, filter); + return extent.getHighestTerrainBlock(x, z, minY, maxY, filter); } catch (FaweException e) { handleException(e); return minY; @@ -173,7 +202,7 @@ public class LimitExtent extends AbstractDelegateExtent { public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { limit.THROW_MAX_CHECKS(maxY - minY + 1); try { - return super.getNearestSurfaceLayer(x, z, y, minY, maxY); + return extent.getNearestSurfaceLayer(x, z, y, minY, maxY); } catch (FaweException e) { handleException(e); 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) { limit.THROW_MAX_CHECKS(maxY - minY + 1); try { - return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir); + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir); } catch (FaweException e) { handleException(e); return minY; @@ -195,7 +224,7 @@ public class LimitExtent extends AbstractDelegateExtent { public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { limit.THROW_MAX_CHECKS(maxY - minY + 1); try { - return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); } catch (FaweException e) { handleException(e); 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) { limit.THROW_MAX_CHECKS(maxY - minY + 1); try { - return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax); + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax); } catch (FaweException e) { handleException(e); 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) { limit.THROW_MAX_CHECKS(maxY - minY + 1); 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) { handleException(e); return minY; @@ -237,91 +266,47 @@ public class LimitExtent extends AbstractDelegateExtent { ) { limit.THROW_MAX_CHECKS(maxY - minY + 1); 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) { handleException(e); 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 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 public List> getBlockDistribution(Region region) { limit.THROW_MAX_CHECKS(region.getVolume()); - return super.getBlockDistribution(region); + return extent.getBlockDistribution(region); } @Override public List> getBlockDistributionWithData(Region region) { limit.THROW_MAX_CHECKS(region.getVolume()); - return super.getBlockDistributionWithData(region); + return extent.getBlockDistributionWithData(region); } @Override public int countBlocks(Region region, Set searchBlocks) { limit.THROW_MAX_CHECKS(region.getVolume()); - return super.countBlocks(region, searchBlocks); + return extent.countBlocks(region, searchBlocks); } @Override public int countBlocks(Region region, Mask searchMask) { limit.THROW_MAX_CHECKS(region.getVolume()); - return super.countBlocks(region, searchMask); + return extent.countBlocks(region, searchMask); } @Override public > int setBlocks(Region region, B block) throws MaxChangedBlocksException { limit.THROW_MAX_CHANGES(region.getVolume()); - return super.setBlocks(region, block); + return extent.setBlocks(region, block); } @Override public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { limit.THROW_MAX_CHANGES(region.getVolume()); - return super.setBlocks(region, pattern); + return extent.setBlocks(region, pattern); } @Override @@ -329,41 +314,34 @@ public class LimitExtent extends AbstractDelegateExtent { MaxChangedBlocksException { limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume()); - return super.replaceBlocks(region, filter, replacement); + return extent.replaceBlocks(region, filter, replacement); } @Override public int replaceBlocks(Region region, Set filter, Pattern pattern) throws MaxChangedBlocksException { limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume()); - return super.replaceBlocks(region, filter, pattern); + return extent.replaceBlocks(region, filter, pattern); } @Override public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException { limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume()); - return super.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); + return extent.replaceBlocks(region, mask, pattern); } @Override public int setBlocks(Set vset, Pattern pattern) { limit.THROW_MAX_CHANGES(vset.size()); - return super.setBlocks(vset, pattern); + return extent.setBlocks(vset, pattern); } @Override public T apply(Region region, T filter, boolean full) { limit.THROW_MAX_CHECKS(region.getVolume()); limit.THROW_MAX_CHANGES(region.getVolume()); - return super.apply(region, filter, full); + return extent.apply(region, filter, full); } @Override @@ -393,14 +371,14 @@ public class LimitExtent extends AbstractDelegateExtent { } limit.THROW_MAX_CHECKS(size); limit.THROW_MAX_CHANGES(size); - return super.apply(positions, filter); + return extent.apply(positions, filter); } @Override public BlockState getBlock(BlockVector3 position) { limit.THROW_MAX_CHECKS(); try { - return super.getBlock(position); + return extent.getBlock(position); } catch (FaweException e) { handleException(e); return BlockTypes.AIR.getDefaultState(); @@ -411,7 +389,7 @@ public class LimitExtent extends AbstractDelegateExtent { public BlockState getBlock(int x, int y, int z) { limit.THROW_MAX_CHECKS(); try { - return super.getBlock(x, y, z); + return extent.getBlock(x, y, z); } catch (FaweException e) { handleException(e); return BlockTypes.AIR.getDefaultState(); @@ -422,7 +400,7 @@ public class LimitExtent extends AbstractDelegateExtent { public BaseBlock getFullBlock(BlockVector3 position) { limit.THROW_MAX_CHECKS(); try { - return super.getFullBlock(position); + return extent.getFullBlock(position); } catch (FaweException e) { handleException(e); return BlockTypes.AIR.getDefaultState().toBaseBlock(); @@ -433,7 +411,7 @@ public class LimitExtent extends AbstractDelegateExtent { public BaseBlock getFullBlock(int x, int y, int z) { limit.THROW_MAX_CHECKS(); try { - return super.getFullBlock(x, y, z); + return extent.getFullBlock(x, y, z); } catch (FaweException e) { handleException(e); return BlockTypes.AIR.getDefaultState().toBaseBlock(); @@ -444,7 +422,7 @@ public class LimitExtent extends AbstractDelegateExtent { public BiomeType getBiome(BlockVector3 position) { limit.THROW_MAX_CHECKS(); try { - return super.getBiome(position); + return extent.getBiome(position); } catch (FaweException e) { handleException(e); return BiomeTypes.FOREST; @@ -455,7 +433,7 @@ public class LimitExtent extends AbstractDelegateExtent { public BiomeType getBiomeType(int x, int y, int z) { limit.THROW_MAX_CHECKS(); try { - return super.getBiomeType(x, y, z); + return extent.getBiomeType(x, y, z); } catch (FaweException e) { handleException(e); return BiomeTypes.FOREST; @@ -470,7 +448,7 @@ public class LimitExtent extends AbstractDelegateExtent { limit.THROW_MAX_BLOCKSTATES(); } try { - return super.setBlock(position, block); + return extent.setBlock(position, block); } catch (FaweException e) { handleException(e); return false; @@ -484,7 +462,7 @@ public class LimitExtent extends AbstractDelegateExtent { limit.THROW_MAX_BLOCKSTATES(); } try { - return super.setBlock(x, y, z, block); + return extent.setBlock(x, y, z, block); } catch (FaweException e) { handleException(e); return false; @@ -494,9 +472,9 @@ public class LimitExtent extends AbstractDelegateExtent { @Override public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { limit.THROW_MAX_CHANGES(); - limit.MAX_BLOCKSTATES(); + limit.THROW_MAX_BLOCKSTATES(); try { - return super.setTile(x, y, z, tile); + return extent.setTile(x, y, z, tile); } catch (FaweException e) { handleException(e); return false; @@ -507,7 +485,7 @@ public class LimitExtent extends AbstractDelegateExtent { public boolean setBiome(BlockVector3 position, BiomeType biome) { limit.THROW_MAX_CHANGES(); try { - return super.setBiome(position, biome); + return extent.setBiome(position, biome); } catch (FaweException e) { handleException(e); return false; @@ -518,11 +496,41 @@ public class LimitExtent extends AbstractDelegateExtent { public boolean setBiome(int x, int y, int z, BiomeType biome) { limit.THROW_MAX_CHANGES(); try { - return super.setBiome(x, y, z, biome); + return extent.setBiome(x, y, z, biome); } catch (FaweException e) { handleException(e); 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(this).setNext(child); + } + return this; + } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.READING_SET_BLOCKS; + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/StripNBTExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/StripNBTExtent.java index c0b49c333..6939743d7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/StripNBTExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/StripNBTExtent.java @@ -2,14 +2,12 @@ package com.fastasyncworldedit.core.extent; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.fastasyncworldedit.core.math.BlockVector3ChunkMap; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; 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.google.common.collect.ImmutableMap; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; 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.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinTag; import javax.annotation.Nullable; -import java.util.HashMap; +import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProcessor { @@ -75,79 +74,82 @@ public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProc if (!(block instanceof BaseBlock localBlock)) { return block; } - if (!localBlock.hasNbtData()) { + final LinCompoundTag nbt = localBlock.getNbt(); + if (nbt == null) { return block; } - CompoundTag nbt = localBlock.getNbtData(); - Map> value = new HashMap<>(nbt.getValue()); + LinCompoundTag.Builder nbtBuilder = nbt.toBuilder(); 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 stripEntityNBT(T entity) { - if (!entity.hasNbtData()) { + LinCompoundTag nbt = entity.getNbt(); + if (nbt == null) { return entity; } - CompoundTag nbt = entity.getNbtData(); - Map> value = new HashMap<>(nbt.getValue()); + LinCompoundTag.Builder nbtBuilder = nbt.toBuilder(); for (String key : strip) { - value.remove(key); + nbtBuilder.remove(key); } - entity.setNbtData(new CompoundTag(value)); + entity.setNbt(nbtBuilder.build()); return entity; } @Override public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { - Map tiles = set.getTiles(); - Set entities = set.getEntities(); + Map tiles = set.tiles(); + Collection entities = set.entities(); if (tiles.isEmpty() && entities.isEmpty()) { return set; } boolean isBv3ChunkMap = tiles instanceof BlockVector3ChunkMap; - for (final Map.Entry entry : tiles.entrySet()) { - ImmutableMap.Builder> map = ImmutableMap.builder(); - final AtomicBoolean isStripped = new AtomicBoolean(false); - entry.getValue().getValue().forEach((k, v) -> { - if (strip.contains(k.toLowerCase())) { - isStripped.set(true); - } else { - map.put(k, v); - } - }); - if (isStripped.get()) { + for (final var entry : tiles.entrySet()) { + FaweCompoundTag original = entry.getValue(); + FaweCompoundTag result = stripNbt(original); + if (original != result) { if (isBv3ChunkMap) { // Replace existing value with stripped value - tiles.put(entry.getKey(), new CompoundTag(map.build())); + tiles.put(entry.getKey(), result); } else { - entry.setValue(new CompoundTag(map.build())); + entry.setValue(result); } } } - Set stripped = new HashSet<>(); - Iterator iterator = entities.iterator(); + Set stripped = new HashSet<>(); + Iterator iterator = entities.iterator(); while (iterator.hasNext()) { - CompoundTag entity = iterator.next(); - ImmutableMap.Builder> map = ImmutableMap.builder(); - final AtomicBoolean isStripped = new AtomicBoolean(false); - entity.getValue().forEach((k, v) -> { - if (strip.contains(k.toUpperCase(Locale.ROOT))) { - isStripped.set(true); - } else { - map.put(k, v); - } - }); - if (isStripped.get()) { + FaweCompoundTag original = iterator.next(); + FaweCompoundTag result = stripNbt(original); + if (original != result) { 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; } + 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 @Override public Extent construct(final Extent child) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/CPUOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/CPUOptimizedClipboard.java index 77447ac4c..ef7cc0461 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/CPUOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/CPUOptimizedClipboard.java @@ -2,9 +2,11 @@ package com.fastasyncworldedit.core.extent.clipboard; import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader; import com.fastasyncworldedit.core.math.IntTriple; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; @@ -175,6 +177,12 @@ public class CPUOptimizedClipboard extends LinearClipboard { 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) { final Map> values = new HashMap<>(tag.getValue()); values.remove("x"); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java index dc7193394..dc2ba4505 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java @@ -6,15 +6,18 @@ import com.fastasyncworldedit.core.internal.exception.FaweClipboardVersionMismat import com.fastasyncworldedit.core.internal.io.ByteBufferInputStream; import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader; import com.fastasyncworldedit.core.math.IntTriple; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.util.MainUtil; +import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.ReflectionUtils; +import com.google.common.collect.Collections2; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; 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.BlockTypesCache; 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.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 Map LOCK_HOLDER_CACHE = new ConcurrentHashMap<>(); - private final HashMap nbtMap; + private final HashMap nbtMap; private final File file; private final int headerSize; @@ -248,12 +253,12 @@ public class DiskOptimizedClipboard extends LinearClipboard { try (NBTInputStream nbtIS = new NBTInputStream(MainUtil.getCompressedIS(new ByteBufferInputStream(tmp)))) { Iterator iter = nbtIS.toIterator(); while (nbtCount > 0 && iter.hasNext()) { // TileEntities are stored "before" entities - CompoundTag tag = iter.next(); - int x = tag.getInt("x"); - int y = tag.getInt("y"); - int z = tag.getInt("z"); + LinCompoundTag tag = iter.next().toLinTag(); + int x = tag.getTag("x", LinTagType.intTag()).valueAsInt(); + int y = tag.getTag("y", LinTagType.intTag()).valueAsInt(); + int z = tag.getTag("z", LinTagType.intTag()).valueAsInt(); IntTriple pos = new IntTriple(x, y, z); - nbtMap.put(pos, tag); + nbtMap.put(pos, FaweCompoundTag.of(tag)); nbtCount--; } while (entitiesCount > 0 && iter.hasNext()) { @@ -559,8 +564,8 @@ public class DiskOptimizedClipboard extends LinearClipboard { ))) { if (!nbtMap.isEmpty()) { try { - for (CompoundTag tag : nbtMap.values()) { - nbtOS.writeTag(tag); + for (FaweCompoundTag tag : nbtMap.values()) { + nbtOS.writeTag(new CompoundTag(tag.linTag())); } } catch (IOException e) { e.printStackTrace(); @@ -638,7 +643,7 @@ public class DiskOptimizedClipboard extends LinearClipboard { @Override public Collection getTileEntities() { - return nbtMap.values(); + return Collections2.transform(nbtMap.values(), fct -> new CompoundTag(fct.linTag())); } 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) { if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) { - CompoundTag nbt; + FaweCompoundTag nbt; if (nbtMap.size() < 4) { nbt = null; - for (Map.Entry entry : nbtMap.entrySet()) { + for (Map.Entry entry : nbtMap.entrySet()) { IntTriple key = entry.getKey(); int index = getIndex(key.x(), key.y(), key.z()); if (index == i) { @@ -674,15 +679,15 @@ public class DiskOptimizedClipboard extends LinearClipboard { int x = newI - z * getWidth(); nbt = nbtMap.get(new IntTriple(x, y, z)); } - return state.toBaseBlock(nbt); + return state.toBaseBlock(nbt == null ? null : nbt.linTag()); } return state.toBaseBlock(); } private BaseBlock toBaseBlock(BlockState state, int x, int y, int z) { if (state.getMaterial().hasContainer() && !nbtMap.isEmpty()) { - CompoundTag nbt = nbtMap.get(new IntTriple(x, y, z)); - return state.toBaseBlock(nbt); + FaweCompoundTag nbt = nbtMap.get(new IntTriple(x, y, z)); + return state.toBaseBlock(nbt == null ? null : nbt.linTag()); } return state.toBaseBlock(); } @@ -709,12 +714,8 @@ public class DiskOptimizedClipboard extends LinearClipboard { } @Override - public boolean setTile(int x, int y, int z, CompoundTag tag) { - final Map> values = new HashMap<>(tag.getValue()); - 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)); + public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tile) throws WorldEditException { + nbtMap.put(new IntTriple(x, y, z), NbtUtils.withPosition(tile, x, y, z)); return true; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/EmptyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/EmptyClipboard.java index 8e18c2c19..983ea79b0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/EmptyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/EmptyClipboard.java @@ -1,7 +1,7 @@ package com.fastasyncworldedit.core.extent.clipboard; 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.entity.Entity; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -84,7 +84,8 @@ public final class EmptyClipboard implements Clipboard { 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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/LinearClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/LinearClipboard.java index 2e65f59ca..cc0bb07f6 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/LinearClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/LinearClipboard.java @@ -57,6 +57,10 @@ public abstract class LinearClipboard extends SimpleClipboard { */ 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 getTileEntities(); @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/MemoryOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/MemoryOptimizedClipboard.java index 426cbc32a..c113c720c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/MemoryOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/MemoryOptimizedClipboard.java @@ -3,10 +3,12 @@ package com.fastasyncworldedit.core.extent.clipboard; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader; import com.fastasyncworldedit.core.math.IntTriple; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.util.MainUtil; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; @@ -262,6 +264,12 @@ public class MemoryOptimizedClipboard extends LinearClipboard { 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 public > boolean setBlock(int x, int y, int z, B block) { return setBlock(getIndex(x, y, z), block); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java index 2edb9cb51..aa608be05 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java @@ -1,7 +1,7 @@ package com.fastasyncworldedit.core.extent.clipboard; 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.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; @@ -106,7 +106,7 @@ public abstract class ReadOnlyClipboard extends SimpleClipboard { } @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"); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ResizableClipboardBuilder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ResizableClipboardBuilder.java index 05089a111..4efa4f456 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ResizableClipboardBuilder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ResizableClipboardBuilder.java @@ -58,6 +58,9 @@ public class ResizableClipboardBuilder extends MemoryOptimizedHistory { } public Clipboard build() { + if (longSize() == 0) { + return EmptyClipboard.getInstance(); + } BlockVector3 pos1 = BlockVector3.at(minX, minY, minZ); BlockVector3 pos2 = BlockVector3.at(maxX, maxY, maxZ); CuboidRegion region = new CuboidRegion(pos1, pos2); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicReaderV3.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicReaderV3.java index 8b6aa6cc8..0e48f32d0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicReaderV3.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicReaderV3.java @@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.extent.clipboard.SimpleClipboard; import com.fastasyncworldedit.core.internal.io.ResettableFileInputStream; import com.fastasyncworldedit.core.internal.io.VarIntStreamIterator; import com.fastasyncworldedit.core.math.MutableBlockVector3; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.util.IOUtil; import com.fastasyncworldedit.core.util.MathMan; import com.sk89q.jnbt.CompoundTag; @@ -40,6 +41,8 @@ import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinIntArrayTag; +import org.enginehub.linbus.tree.LinTagType; import org.jetbrains.annotations.ApiStatus; import java.io.BufferedInputStream; @@ -87,6 +90,7 @@ public class FastSchematicReaderV3 implements ClipboardReader { private VersionedDataFixer dataFixer; private BlockVector3 offset; + private BlockVector3 origin = BlockVector3.ZERO; private BlockState[] blockPalette; private BiomeType[] biomePalette; private int dataVersion = -1; @@ -136,6 +140,24 @@ public class FastSchematicReaderV3 implements ClipboardReader { this.dataVersion = this.dataInputStream.readInt(); this.dataFixer = ReaderUtil.getVersionedDataFixer(this.dataVersion, platform, platform.getDataVersion()); } + case "Metadata" -> { + LinCompoundTag metadataCompoundTag = + (LinCompoundTag) this.nbtInputStream.readTagPayload(NBTConstants.TYPE_COMPOUND, 0).toLinTag(); + + LinCompoundTag worldEditTag = metadataCompoundTag.findTag("WorldEdit", LinTagType.compoundTag()); + if (worldEditTag != null) { // allowed to be optional + LinIntArrayTag originTag = worldEditTag.findTag("Origin", LinTagType.intArrayTag()); + if (originTag != null) { // allowed to be optional + int[] parts = originTag.value(); + + if (parts.length != 3) { + throw new IOException("`Metadata > WorldEdit > Origin` int array length is invalid."); + } + + this.origin = BlockVector3.at(parts[0], parts[1], parts[2]); + } + } + } case "Offset" -> { this.dataInputStream.skipNBytes(4); // Array Length field (4 byte int) this.offset = BlockVector3.at( @@ -172,7 +194,7 @@ public class FastSchematicReaderV3 implements ClipboardReader { clipboard.setOrigin(this.offset.multiply(-1)); if (clipboard instanceof SimpleClipboard simpleClipboard && !this.offset.equals(BlockVector3.ZERO)) { - clipboard = new BlockArrayClipboard(simpleClipboard, this.offset); + clipboard = new BlockArrayClipboard(simpleClipboard, this.offset.add(this.origin)); } return clipboard; } @@ -626,12 +648,11 @@ public class FastSchematicReaderV3 implements ClipboardReader { } private EntityTransformer provideTileEntityTransformer(Clipboard clipboard) { - //noinspection deprecation - return (x, y, z, id, tag) -> clipboard.setTile( + return (x, y, z, id, tag) -> clipboard.tile( MathMan.roundInt(x + clipboard.getMinimumPoint().x()), MathMan.roundInt(y + clipboard.getMinimumPoint().y()), MathMan.roundInt(z + clipboard.getMinimumPoint().z()), - new CompoundTag(tag) + FaweCompoundTag.of(tag) ); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java index 72ee73013..f6844e684 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java @@ -1,8 +1,10 @@ package com.fastasyncworldedit.core.extent.filter; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; +import jdk.incubator.vector.ShortVector; -public class CountFilter extends ForkedFilter { +public class CountFilter extends ForkedFilter implements VectorizedFilter { private int total; @@ -33,4 +35,10 @@ public class CountFilter extends ForkedFilter { return total; } + @Override + public ShortVector applyVector(final ShortVector get, final ShortVector set) { + total += set.length(); + return set; + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java index 25627c451..26700ccc5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java @@ -1,37 +1,92 @@ package com.fastasyncworldedit.core.extent.filter; -import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; import com.fastasyncworldedit.core.queue.Filter; +import com.fastasyncworldedit.core.queue.IChunk; +import com.sk89q.worldedit.regions.Region; +import jdk.incubator.vector.ShortVector; +import org.jetbrains.annotations.Nullable; /** - * Filter which links two Filters together for single-filter-input operations. + * Filter which links two Filters together for single-filter-input operations. Left filter is operated first. * - * @param Parent which extends Filter - * @param Child which extends Filter + * @param Left filter + * @param Right filter */ -public final class LinkedFilter extends DelegateFilter { +public sealed class LinkedFilter implements Filter { - private final S child; + private final L left; + private final R right; - public LinkedFilter(T parent, S child) { - super(parent); - this.child = child; + public LinkedFilter(L left, R right) { + this.left = left; + this.right = right; } - public S getChild() { - return this.child; + @SuppressWarnings({"unchecked", "rawtypes"}) // we defeated the type system + public static LinkedFilter of(L left, R right) { + if (left instanceof VectorizedFilter l && right instanceof VectorizedFilter r) { + return new VectorizedLinkedFilter(l, r); + } + return new LinkedFilter<>(left, right); + } + + public L getLeft() { + return this.left; + } + + public R getRight() { + return this.right; + } + + @Override + public T applyChunk(T chunk, @Nullable Region region) { + chunk = getLeft().applyChunk(chunk, region); + return getRight().applyChunk(chunk, region); } @Override public void applyBlock(FilterBlock block) { - this.getParent().applyBlock(block); - this.getChild().applyBlock(block); + getLeft().applyBlock(block); + getRight().applyBlock(block); } @Override - public LinkedFilter, Filter> newInstance(Filter other) { - return new LinkedFilter<>(this, other); + public void finishChunk(IChunk chunk) { + getLeft().finishChunk(chunk); + getRight().finishChunk(chunk); + } + + @Override + public Filter fork() { + return new LinkedFilter<>(getLeft().fork(), getRight().fork()); + } + + @Override + public void join() { + getLeft().join(); + getRight().join(); + } + + private final static class VectorizedLinkedFilter + extends LinkedFilter implements VectorizedFilter { + + public VectorizedLinkedFilter(final L left, final R right) { + super(left, right); + } + + @Override + public ShortVector applyVector(final ShortVector get, final ShortVector set) { + ShortVector res = getLeft().applyVector(get, set); + return getRight().applyVector(get, res); + } + + @Override + public Filter fork() { + return new VectorizedLinkedFilter<>((L) getLeft().fork(), (R) getRight().fork()); + } + } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java index 9284f8378..ac31938e2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java @@ -2,10 +2,17 @@ package com.fastasyncworldedit.core.extent.filter; import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; +import com.fastasyncworldedit.core.internal.simd.VectorizedMask; import com.fastasyncworldedit.core.queue.Filter; import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.Mask; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** @@ -15,8 +22,8 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class MaskFilter extends DelegateFilter { - private final Mask mask; - private final AtomicInteger changes; + final Mask mask; + final AtomicInteger changes; public MaskFilter(T other, Mask root) { this(other, root, new AtomicInteger()); @@ -60,4 +67,45 @@ public class MaskFilter extends DelegateFilter { return new MaskFilter<>(getParent().fork(), mask.copy(), changes); } + public static class VectorizedMaskFilter extends MaskFilter implements VectorizedFilter { + + private final VectorizedMask vectorizedMask; + + public VectorizedMaskFilter(final T other, final Mask root) { + super(other, root); + this.vectorizedMask = Objects.requireNonNull(SimdSupport.vectorizedTargetMask(root), "invalid vectorizable mask"); + } + + public VectorizedMaskFilter(final T other, final Mask root, AtomicInteger changes) { + super(other, root, changes); + this.vectorizedMask = Objects.requireNonNull(SimdSupport.vectorizedTargetMask(root), "invalid vectorizable mask"); + } + + @Override + public ShortVector applyVector(final ShortVector get, final ShortVector set) { + final T parent = getParent(); + VectorMask masked = vectorizedMask.compareVector(set, get); + ShortVector res = parent.applyVector(get, set); + res = set.blend(res, masked); + VectorMask changed = res.compare(VectorOperators.NE, set); + changes.getAndAdd(changed.trueCount()); + return res; + } + + @Override + public MaskFilter newInstance(final Filter other) { + if (other instanceof VectorizedFilter o) { + return new VectorizedMaskFilter<>(o, mask); + } + return super.newInstance(other); + } + + @SuppressWarnings("unchecked") + @Override + public Filter fork() { + return new VectorizedMaskFilter<>((T) getParent().fork(), mask.copy(), changes); + } + + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ArrayFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ArrayFilterBlock.java index 362f869ac..2e9263cea 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ArrayFilterBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ArrayFilterBlock.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.core.extent.filter.block; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; @@ -105,6 +106,11 @@ public class ArrayFilterBlock extends AbstractExtentFilterBlock { return getExtent().setBlock(x, y, z, block); } + @Override + public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tile) throws WorldEditException { + return false; // class is unused + deprecated, do not care about impl + } + @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { return getExtent().setBiome(x, y, z, biome); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java index 579f04a9a..718171efd 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.core.extent.filter.block; import com.fastasyncworldedit.core.FaweCache; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.FilterBlockMask; import com.fastasyncworldedit.core.queue.IBlocks; @@ -8,11 +9,11 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.implementation.Flood; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.concurrency.LazyReference; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -20,12 +21,15 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.registry.BlockMaterial; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.jetbrains.annotations.ApiStatus; import javax.annotation.Nonnull; import javax.annotation.Nullable; import static com.sk89q.worldedit.world.block.BlockTypesCache.states; +@ApiStatus.NonExtendable public class CharFilterBlock extends ChunkFilterBlock { private static final SetDelegate FULL = (block, value) -> block.setArr[block.index] = value; @@ -35,10 +39,10 @@ public class CharFilterBlock extends ChunkFilterBlock { private int minLayer; private CharGetBlocks get; private IChunkSet set; - private char[] getArr; + protected char[] getArr; @Nullable - private char[] setArr; - private SetDelegate delegate; + protected char[] setArr; + protected SetDelegate delegate; // local private int layer; private int index; @@ -169,7 +173,7 @@ public class CharFilterBlock extends ChunkFilterBlock { } @Override - public synchronized final void filter(Filter filter) { + public synchronized void filter(Filter filter) { for (y = 0, index = 0; y < 16; y++) { for (z = 0; z < 16; z++) { for (x = 0; x < 16; x++, index++) { @@ -259,8 +263,8 @@ public class CharFilterBlock extends ChunkFilterBlock { final BlockState state = getBlock(); final BlockMaterial material = state.getMaterial(); if (material.hasContainer()) { - final CompoundTag tag = get.getTile(x, y + yy, z); - return state.toBaseBlock(tag); + final FaweCompoundTag tag = get.tile(x, y + yy, z); + return state.toBaseBlock(tag == null ? null : tag.linTag()); } return state.toBaseBlock(); } @@ -268,16 +272,28 @@ public class CharFilterBlock extends ChunkFilterBlock { @Override public void setFullBlock(BaseBlock block) { delegate.set(this, block.getOrdinalChar()); - final CompoundTag nbt = block.getNbtData(); + final LazyReference nbt = block.getNbtReference(); if (nbt != null) { // TODO optimize check via ImmutableBaseBlock - set.setTile(x, yy + y, z, nbt); + set.tile(x, yy + y, z, FaweCompoundTag.of(nbt)); } } @Override - public final CompoundTag getNbtData() { - return get.getTile(x, y + yy, z); + public @Nullable LinCompoundTag getNbt() { + final FaweCompoundTag tile = get.tile(x, y + yy, z); + if (tile == null) { + return null; + } + return tile.linTag(); } + + @Override + public void setNbt(@Nullable final LinCompoundTag nbtData) { + if (nbtData != null) { + set.tile(x, y + yy, z, FaweCompoundTag.of(nbtData)); + } + } + /* NORTH(Vector3.at(0, 0, -1), Flag.CARDINAL, 3, 1), EAST(Vector3.at(1, 0, 0), Flag.CARDINAL, 0, 2), @@ -286,9 +302,9 @@ public class CharFilterBlock extends ChunkFilterBlock { */ @Override - public void setNbtData(CompoundTag tag) { - if (tag != null) { - set.setTile(x, y + yy, z, tag); + public void setNbtReference(@Nullable final LazyReference nbtData) { + if (nbtData != null) { + set.tile(x, y + yy, z, FaweCompoundTag.of(nbtData)); } } @@ -380,7 +396,7 @@ public class CharFilterBlock extends ChunkFilterBlock { } //Set delegate - private SetDelegate initSet() { + protected final SetDelegate initSet() { setArr = set.load(layer); return delegate = FULL; } @@ -412,7 +428,8 @@ public class CharFilterBlock extends ChunkFilterBlock { return getExtent().setBiome(x, y, z, biome); } - private interface SetDelegate { + @ApiStatus.Internal + protected interface SetDelegate { void set(@Nonnull CharFilterBlock block, char value); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ChunkFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ChunkFilterBlock.java index 9e11021d2..830ff9ab1 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ChunkFilterBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ChunkFilterBlock.java @@ -83,12 +83,20 @@ public abstract class ChunkFilterBlock extends AbstractExtentFilterBlock { /** * Filter a chunk with a region / filter. */ - public synchronized final IChunkSet filter(IChunk chunk, IChunkGet get, IChunkSet set, Filter filter, Region region, boolean full) { + public synchronized final IChunkSet filter( + IChunk chunk, + IChunkGet get, + IChunkSet set, + Filter filter, + Region region, + boolean full + ) { if (region != null) { region.filter(chunk, filter, this, get, set, full); } else { for (int layer = get.getMinSectionPosition(); layer <= get.getMaxSectionPosition(); layer++) { - if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + //if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + if (!full && !get.hasSection(layer)) { continue; } initLayer(get, set, layer); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/FilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/FilterBlock.java index 3565c696f..c35f549a2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/FilterBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/FilterBlock.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.core.extent.filter.block; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.TileEntityBlock; @@ -35,17 +36,6 @@ public abstract class FilterBlock extends BlockVector3 implements Extent, TileEn public abstract BiomeType getBiome(); - @Override - public abstract CompoundTag getNbtData(); - - @Override - public abstract void setNbtData(@Nullable CompoundTag nbtData); - - @Override - public boolean hasNbtData() { - return getNbtData() != null; - } - @Override public BlockVector3 getMinimumPoint() { return getExtent().getMinimumPoint(); @@ -61,10 +51,9 @@ public abstract class FilterBlock extends BlockVector3 implements Extent, TileEn return getExtent().getBlock(x, y, z); } - @Override - public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { - return getExtent().setTile(x, y, z, tile); + public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tile) throws WorldEditException { + return getExtent().tile(x, y, z, tile); } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/ABlockMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/ABlockMask.java index 7fdebdded..ed5b028dd 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/ABlockMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/ABlockMask.java @@ -26,7 +26,7 @@ public abstract class ABlockMask extends AbstractExtentMask { @Override public boolean test(BlockVector3 vector) { - return test(getExtent().getBlock(vector)); + return test(vector.getBlock(getExtent())); } public abstract boolean test(BlockState state); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/DataMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/DataMask.java index 2b3ad7edb..84b8f77ad 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/DataMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/DataMask.java @@ -16,9 +16,9 @@ public class DataMask extends AbstractExtentMask implements ResettableMask { @Override public boolean test(BlockVector3 vector) { if (data != -1) { - return getExtent().getBlock(vector).getInternalPropertiesId() == data; + return vector.getBlock(getExtent()).getInternalPropertiesId() == data; } else { - data = getExtent().getBlock(vector).getInternalPropertiesId(); + data = vector.getBlock(getExtent()).getInternalPropertiesId(); return true; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/IdMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/IdMask.java index 49c90183a..b0ba4fb59 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/IdMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/IdMask.java @@ -24,7 +24,9 @@ public class IdMask extends AbstractExtentMask implements ResettableMask { @Override public boolean test(BlockVector3 vector) { - return test(getExtent(), vector); + int blockID = vector.getBlock(getExtent()).getInternalBlockTypeId(); + int testId = id.compareAndExchange(-1, blockID); + return blockID == testId || testId == -1; } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SingleBlockStateMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SingleBlockStateMask.java index 32d853bda..400f32163 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SingleBlockStateMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SingleBlockStateMask.java @@ -30,7 +30,7 @@ public class SingleBlockStateMask extends ABlockMask { @Override public boolean test(BlockVector3 vector) { - int test = getExtent().getBlock(vector).getOrdinal(); + int test = vector.getBlock(getExtent()).getOrdinal(); return ordinal == test || isAir && test == 0; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SplatterBrushMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SplatterBrushMask.java index 250969616..d80399be4 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SplatterBrushMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SplatterBrushMask.java @@ -40,7 +40,7 @@ public class SplatterBrushMask extends AbstractExtentMask { double dist = vector.distanceSq(position); synchronized (placed) { if (dist < size2 && !placed.contains(vector) && ThreadLocalRandom.current().nextInt(5) < 2 && surface.test(vector)) { - placed.add(vector); + placed.add(vector.toImmutable()); return true; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/ChangePopulator.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/ChangePopulator.java new file mode 100644 index 000000000..84424d948 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/ChangePopulator.java @@ -0,0 +1,65 @@ +package com.fastasyncworldedit.core.history.change; + +import com.sk89q.worldedit.history.change.Change; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @since 2.11.2 + */ +@ApiStatus.Internal +public interface ChangePopulator { + + static ChangePopulator empty() { + class Empty implements ChangePopulator { + private static final Empty EMPTY = new Empty(); + + @Override + public @NotNull C create() { + throw new UnsupportedOperationException("empty"); + } + + @Override + public @Nullable C populate(@NotNull final C change) { + return null; + } + + @Override + public @Nullable C updateOrCreate(@Nullable final Change change) { + return null; + } + + @Override + public boolean accepts(final Change change) { + return false; + } + } + return Empty.EMPTY; + } + + @SuppressWarnings("unchecked") + default @NotNull C update(@Nullable Change before) { + if (accepts(before)) { + return (C) before; + } + return create(); + } + + @NotNull + C create(); + + @Nullable + default C updateOrCreate(@Nullable Change change) { + C u = update(change); + return populate(u); + } + + @Nullable + C populate(@NotNull C change); + + @Contract("null->false") + boolean accepts(Change change); + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index 346543a0b..33931aac6 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -4,11 +4,12 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.extent.HistoryExtent; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; 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.MainUtil; +import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.util.concurrent.Futures; import com.sk89q.jnbt.CompoundTag; @@ -32,8 +33,12 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.jetbrains.annotations.ApiStatus; +import javax.annotation.Nonnull; import java.io.IOException; +import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Objects; @@ -122,37 +127,38 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { int bx = chunk.getX() << 4; int bz = chunk.getZ() << 4; - Map tilesFrom = get.getTiles(); - Map tilesTo = set.getTiles(); + Map tilesFrom = get.tiles(); + Map tilesTo = set.tiles(); if (!tilesFrom.isEmpty()) { - for (Map.Entry entry : tilesFrom.entrySet()) { + for (Map.Entry entry : tilesFrom.entrySet()) { BlockVector3 pos = entry.getKey(); BlockState fromBlock = get.getBlock(pos.x() & 15, pos.y(), pos.z() & 15); BlockState toBlock = set.getBlock(pos.x() & 15, pos.y(), pos.z() & 15); if (fromBlock != toBlock || tilesTo.containsKey(pos)) { - addTileRemove(MainUtil.setPosition(entry.getValue(), entry.getKey().x(), entry.getKey().y(), - entry.getKey().z())); + addTileRemove(NbtUtils.withPosition(entry.getValue(), entry.getKey().x(), entry.getKey().y(), + entry.getKey().z() + )); } } } if (!tilesTo.isEmpty()) { - for (Map.Entry entry : tilesTo.entrySet()) { + for (Map.Entry entry : tilesTo.entrySet()) { BlockVector3 pos = entry.getKey(); - addTileCreate(MainUtil.setPosition(entry.getValue(), pos.x() + bx, pos.y(), pos.z() + bz)); + addTileCreate(NbtUtils.withPosition(entry.getValue(), pos.x() + bx, pos.y(), pos.z() + bz)); } } Set entRemoves = set.getEntityRemoves(); if (!entRemoves.isEmpty()) { for (UUID uuid : entRemoves) { - CompoundTag found = get.getEntity(uuid); + FaweCompoundTag found = get.entity(uuid); if (found != null) { addEntityRemove(found); } } } - Set ents = set.getEntities(); + Collection ents = set.entities(); if (!ents.isEmpty()) { - for (CompoundTag tag : ents) { + for (FaweCompoundTag tag : ents) { addEntityCreate(tag); } } @@ -203,9 +209,9 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { BiomeType[] biomeSection = biomes[layer - set.getMinSectionPosition()]; int index = 0; int yy = layer << 4; - for (int y = 0; y < 16; y+= 4) { - for (int z = 0; z < 16; z+= 4) { - for (int x = 0; x < 16; x+= 4, index++) { + for (int y = 0; y < 16; y += 4) { + for (int z = 0; z < 16; z += 4) { + for (int x = 0; x < 16; x += 4, index++) { BiomeType newBiome = biomeSection[index]; if (newBiome != null) { BiomeType oldBiome = get.getBiomeType(x, yy + y, z); @@ -236,13 +242,62 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { return ProcessorScope.READING_SET_BLOCKS; } - public abstract void addTileCreate(CompoundTag tag); + @Deprecated(forRemoval = true, since = "2.11.2") + public void addTileCreate(CompoundTag tag) { + addTileCreate(adapt(tag)); + } - public abstract void addTileRemove(CompoundTag tag); + @SuppressWarnings({"deprecation"}) + private static @Nonnull FaweCompoundTag adapt(CompoundTag tag) { + return FaweCompoundTag.of(tag.toLinTag()); + } - public abstract void addEntityRemove(CompoundTag tag); + /** + * Creates a tile/block entity create change to this change set. + * + * @param tag the tile/block entity to add. + * @since 2.11.2 + */ + public abstract void addTileCreate(FaweCompoundTag tag); - public abstract void addEntityCreate(CompoundTag tag); + @Deprecated(forRemoval = true, since = "2.11.2") + public void addTileRemove(CompoundTag tag) { + addTileRemove(adapt(tag)); + } + + /** + * Creates a tile/block entity remove change to this change set. + * + * @param tag the tile/block entity to remove. + * @since 2.11.2 + */ + public abstract void addTileRemove(FaweCompoundTag tag); + + @Deprecated(forRemoval = true, since = "2.11.2") + public void addEntityRemove(CompoundTag tag) { + addEntityRemove(adapt(tag)); + } + + /** + * Creates an entity remove change to this change set. + * + * @param tag the entity to remove. + * @since 2.11.2 + */ + public abstract void addEntityRemove(FaweCompoundTag tag); + + @Deprecated(forRemoval = true, since = "2.11.2") + public void addEntityCreate(CompoundTag tag) { + addEntityCreate(adapt(tag)); + } + + /** + * Creates an entity create change to this change set. + * + * @param tag the entity to add. + * @since 2.11.2 + */ + public abstract void addEntityCreate(FaweCompoundTag tag); public abstract void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to); @@ -250,6 +305,13 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { return getIterator(redo); } + /** + * {@return a coordinator to exchange sets of changes between a producer and a consumer} + * @since 2.11.2 + */ + @ApiStatus.Internal + public abstract ChangeExchangeCoordinator getCoordinatedChanges(BlockBag blockBag, int mode, boolean dir); + public abstract Iterator getIterator(boolean redo); public EditSession toEditSession(Actor actor) { @@ -272,13 +334,15 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { } public void add(EntityCreate change) { - CompoundTag tag = change.state.getNbtData(); - addEntityCreate(MainUtil.setEntityInfo(tag, change.getEntity())); + LinCompoundTag tag = change.state.getNbt(); + assert tag != null; + addEntityCreate(FaweCompoundTag.of(NbtUtils.withEntityInfo(tag, change.getEntity()))); } public void add(EntityRemove change) { - CompoundTag tag = change.state.getNbtData(); - addEntityRemove(MainUtil.setEntityInfo(tag, change.getEntity())); + LinCompoundTag tag = change.state.getNbt(); + assert tag != null; + addEntityRemove(FaweCompoundTag.of(NbtUtils.withEntityInfo(tag, change.getEntity()))); } @Override @@ -296,9 +360,9 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { public void add(BlockChange change) { try { - BlockVector3 loc = change.getPosition(); + BlockVector3 loc = change.position(); BaseBlock from = change.previous(); - BaseBlock to = change.getCurrent(); + BaseBlock to = change.current(); add(loc, from, to); } catch (Exception e) { LOGGER.catching(e); @@ -318,31 +382,28 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { public void add(int x, int y, int z, BaseBlock from, BaseBlock to) { try { - if (from.hasNbtData()) { - CompoundTag nbt = from.getNbtData(); - assert nbt != null; - addTileRemove(MainUtil.setPosition(nbt, x, y, z)); + LinCompoundTag nbt = from.getNbt(); + if (nbt != null) { + addTileRemove(FaweCompoundTag.of(NbtUtils.withPosition(nbt, x, y, z))); } - if (to.hasNbtData()) { - CompoundTag nbt = to.getNbtData(); - assert nbt != null; - addTileCreate(MainUtil.setPosition(nbt, x, y, z)); + nbt = to.getNbt(); + if (nbt != null) { + addTileCreate(FaweCompoundTag.of(NbtUtils.withPosition(nbt, x, y, z))); } int combinedFrom = from.getOrdinal(); int combinedTo = to.getOrdinal(); add(x, y, z, combinedFrom, combinedTo); } catch (Exception e) { - e.printStackTrace(); + LOGGER.catching(e); } } public void add(int x, int y, int z, int combinedFrom, BaseBlock to) { try { - if (to.hasNbtData()) { - CompoundTag nbt = to.getNbtData(); - assert nbt != null; - addTileCreate(MainUtil.setPosition(nbt, x, y, z)); + LinCompoundTag nbt = to.getNbt(); + if (nbt != null) { + addTileCreate(FaweCompoundTag.of(NbtUtils.withPosition(nbt, x, y, z))); } int combinedTo = to.getInternalId(); add(x, y, z, combinedFrom, combinedTo); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractDelegateChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractDelegateChangeSet.java index a44e73013..e84888848 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractDelegateChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractDelegateChangeSet.java @@ -1,6 +1,6 @@ package com.fastasyncworldedit.core.history.changeset; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; @@ -46,26 +46,6 @@ public class AbstractDelegateChangeSet extends AbstractChangeSet { parent.flush(); } - @Override - public void addTileCreate(CompoundTag tag) { - parent.addTileCreate(tag); - } - - @Override - public void addTileRemove(CompoundTag tag) { - parent.addTileRemove(tag); - } - - @Override - public void addEntityRemove(CompoundTag tag) { - parent.addEntityRemove(tag); - } - - @Override - public void addEntityCreate(CompoundTag tag) { - parent.addEntityCreate(tag); - } - @Override public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) { parent.addBiomeChange(x, y, z, from, to); @@ -76,6 +56,11 @@ public class AbstractDelegateChangeSet extends AbstractChangeSet { return parent.getIterator(blockBag, mode, redo); } + @Override + public ChangeExchangeCoordinator getCoordinatedChanges(final BlockBag blockBag, final int mode, final boolean dir) { + return parent.getCoordinatedChanges(blockBag, mode, dir); + } + @Override public Iterator getIterator(boolean redo) { return parent.getIterator(redo); @@ -141,6 +126,26 @@ public class AbstractDelegateChangeSet extends AbstractChangeSet { return parent.forwardIterator(); } + @Override + public void addTileCreate(final FaweCompoundTag tag) { + parent.addTileCreate(tag); + } + + @Override + public void addTileRemove(final FaweCompoundTag tag) { + parent.addTileRemove(tag); + } + + @Override + public void addEntityRemove(final FaweCompoundTag tag) { + parent.addEntityRemove(tag); + } + + @Override + public void addEntityCreate(final FaweCompoundTag tag) { + parent.addEntityCreate(tag); + } + @Override public void close() throws IOException { parent.close(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/BlockBagChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/BlockBagChangeSet.java index e17a78765..8c65be084 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/BlockBagChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/BlockBagChangeSet.java @@ -1,8 +1,6 @@ package com.fastasyncworldedit.core.history.changeset; import com.fastasyncworldedit.core.FaweCache; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.extent.inventory.BlockBagException; import com.sk89q.worldedit.extent.inventory.UnplaceableBlockException; @@ -109,13 +107,4 @@ public class BlockBagChangeSet extends AbstractDelegateChangeSet { super.add(x, y, z, combinedFrom, combinedTo); } - @Override - public void addTileCreate(CompoundTag nbt) { - if (nbt.containsKey("items")) { - Map> map = new HashMap<>(nbt.getValue()); - map.remove("items"); - } - super.addTileCreate(nbt); - } - } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/ChangeExchangeCoordinator.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/ChangeExchangeCoordinator.java new file mode 100644 index 000000000..3c020d422 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/ChangeExchangeCoordinator.java @@ -0,0 +1,53 @@ +package com.fastasyncworldedit.core.history.changeset; + +import com.sk89q.worldedit.history.change.Change; +import org.jetbrains.annotations.ApiStatus; + +import java.util.concurrent.Exchanger; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.BiConsumer; + +/** + * @since 2.11.2 + */ +@ApiStatus.Internal +public class ChangeExchangeCoordinator implements AutoCloseable { + + private static final Thread.Builder.OfVirtual UNDO_VIRTUAL_THREAD_BUILDER = Thread.ofVirtual() + .name("FAWE undo", 0); + private final Exchanger exchanger; + private final BiConsumer, Change[]> runnerTask; + private boolean started = false; + private Thread runner; + + public ChangeExchangeCoordinator(BiConsumer, Change[]> runner) { + this.runnerTask = runner; + this.exchanger = new Exchanger<>(); + } + + public Change[] take(Change[] consumed) { + if (!this.started) { + this.started = true; + final int length = consumed.length; + this.runner = UNDO_VIRTUAL_THREAD_BUILDER + .start(() -> this.runnerTask.accept(this.exchanger, new Change[length])); + } + try { + // Allow a reasonable timeout in case of weirdness + return exchanger.exchange(consumed, 30, TimeUnit.SECONDS); + } catch (InterruptedException | TimeoutException e) { + this.runner.interrupt(); + Thread.currentThread().interrupt(); + return null; + } + } + + @Override + public void close() { + if (this.runner != null) { + this.runner.interrupt(); + } + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java index e7132bf5a..e50030d21 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.core.history.changeset; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.history.change.ChangePopulator; import com.fastasyncworldedit.core.history.change.MutableBiomeChange; import com.fastasyncworldedit.core.history.change.MutableBlockChange; import com.fastasyncworldedit.core.history.change.MutableEntityChange; @@ -9,6 +10,7 @@ import com.fastasyncworldedit.core.history.change.MutableTileChange; import com.fastasyncworldedit.core.internal.exception.FaweSmallEditUnsupportedException; import com.fastasyncworldedit.core.internal.io.FaweInputStream; import com.fastasyncworldedit.core.internal.io.FaweOutputStream; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MathMan; import com.sk89q.jnbt.CompoundTag; @@ -20,15 +22,22 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockTypes; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.Exchanger; +import java.util.function.BiConsumer; /** * FAWE stream ChangeSet offering support for extended-height worlds @@ -390,56 +399,44 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { } @Override - public void addTileCreate(CompoundTag tag) { - if (tag == null) { - return; - } + public void addTileCreate(final FaweCompoundTag tag) { blockSize++; try { NBTOutputStream nbtos = getTileCreateOS(); - nbtos.writeTag(tag); + nbtos.writeTag(new CompoundTag(tag.linTag())); } catch (IOException e) { e.printStackTrace(); } } @Override - public void addTileRemove(CompoundTag tag) { - if (tag == null) { - return; - } + public void addTileRemove(final FaweCompoundTag tag) { blockSize++; try { NBTOutputStream nbtos = getTileRemoveOS(); - nbtos.writeTag(tag); + nbtos.writeTag(new CompoundTag(tag.linTag())); } catch (IOException e) { e.printStackTrace(); } } @Override - public void addEntityRemove(CompoundTag tag) { - if (tag == null) { - return; - } + public void addEntityRemove(final FaweCompoundTag tag) { blockSize++; try { NBTOutputStream nbtos = getEntityRemoveOS(); - nbtos.writeTag(tag); + nbtos.writeTag(new CompoundTag(tag.linTag())); } catch (IOException e) { e.printStackTrace(); } } @Override - public void addEntityCreate(CompoundTag tag) { - if (tag == null) { - return; - } + public void addEntityCreate(final FaweCompoundTag tag) { blockSize++; try { NBTOutputStream nbtos = getEntityCreateOS(); - nbtos.writeTag(tag); + nbtos.writeTag(new CompoundTag(tag.linTag())); } catch (IOException e) { e.printStackTrace(); } @@ -711,6 +708,284 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { } } + @Override + public ChangeExchangeCoordinator getCoordinatedChanges(BlockBag blockBag, int mode, boolean dir) { + try { + return coordinatedChanges(blockBag, mode, dir); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private ChangeExchangeCoordinator coordinatedChanges(final BlockBag blockBag, final int mode, boolean dir) throws IOException { + close(); + var tileCreate = tileChangePopulator(getTileCreateIS(), true); + var tileRemove = tileChangePopulator(getTileRemoveIS(), false); + + var entityCreate = entityChangePopulator(getEntityCreateIS(), true); + var entityRemove = entityChangePopulator(getEntityRemoveIS(), false); + + var blockChange = blockBag != null && mode > 0 ? fullBlockChangePopulator(blockBag, mode, dir) : blockChangePopulator(dir); + + var biomeChange = biomeChangePopulator(dir); + + Queue> populators = new ArrayDeque<>(List.of( + tileCreate, + tileRemove, + entityCreate, + entityRemove, + blockChange, + biomeChange + )); + BiConsumer, Change[]> task = (exchanger, array) -> { + while (fillArray(array, populators)) { + try { + array = exchanger.exchange(array); + } catch (InterruptedException e) { + return; + } + } + }; + return new ChangeExchangeCoordinator(task); + } + + private boolean fillArray(Change[] changes, Queue> populators) { + ChangePopulator populator = populators.peek(); + if (populator == null) { + return false; + } + for (int i = 0; i < changes.length; i++) { + Change change = changes[i]; + do { + change = populator.updateOrCreate(change); + if (change == null) { + populators.remove(); + populator = populators.peek(); + if (populator == null) { + changes[i] = null; // mark end + return true; // still needs to consume the elements of the current round + } + } else { + break; + } + } while (true); + changes[i] = change; + } + return true; + } + + private static abstract class CompoundTagPopulator implements ChangePopulator { + private final NBTInputStream inputStream; + + private CompoundTagPopulator(final NBTInputStream stream) { + inputStream = stream; + } + + @Override + public @Nullable C populate(final @NotNull C change) { + try { + write(change, (CompoundTag) inputStream.readTag()); + return change; + } catch (Exception ignored) { + } + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + protected abstract void write(C change, CompoundTag tag); + } + + private ChangePopulator tileChangePopulator(NBTInputStream is, boolean create) { + if (is == null) { + return ChangePopulator.empty(); + } + class Populator extends CompoundTagPopulator { + + private Populator() { + super(is); + } + + @Override + public @NotNull MutableTileChange create() { + return new MutableTileChange(null, create); + } + + @Override + protected void write(final MutableTileChange change, final CompoundTag tag) { + change.tag = tag; + } + + @Override + public boolean accepts(final Change change) { + return change instanceof MutableTileChange; + } + + } + return new Populator(); + } + private ChangePopulator entityChangePopulator(NBTInputStream is, boolean create) { + if (is == null) { + return ChangePopulator.empty(); + } + class Populator extends CompoundTagPopulator { + + private Populator() { + super(is); + } + + @Override + public @NotNull MutableEntityChange create() { + return new MutableEntityChange(null, create); + } + + @Override + protected void write(final MutableEntityChange change, final CompoundTag tag) { + change.tag = tag; + } + + @Override + public boolean accepts(final Change change) { + return change instanceof MutableTileChange; + } + + } + return new Populator(); + } + + private ChangePopulator fullBlockChangePopulator(BlockBag blockBag, int mode, boolean dir) throws + IOException { + final FaweInputStream is = getBlockIS(); + if (is == null) { + return ChangePopulator.empty(); + } + class Populator implements ChangePopulator { + + @Override + public @NotNull MutableFullBlockChange create() { + return new MutableFullBlockChange(blockBag, mode, dir); + } + + @Override + public @Nullable MutableFullBlockChange populate(@NotNull final MutableFullBlockChange change) { + try { + change.x = posDel.readX(is) + originX; + change.y = posDel.readY(is); + change.z = posDel.readZ(is) + originZ; + idDel.readCombined(is, change); + return change; + } catch (EOFException ignored) { + } catch (Exception e) { + e.printStackTrace(); + } + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public boolean accepts(final Change change) { + return change instanceof MutableFullBlockChange; + } + + } + return new Populator(); + + } + + private ChangePopulator blockChangePopulator(boolean dir) throws IOException { + final FaweInputStream is = getBlockIS(); + if (is == null) { + return ChangePopulator.empty(); + } + class Populator implements ChangePopulator { + + @Override + public @NotNull MutableBlockChange create() { + return new MutableBlockChange(0, 0, 0, BlockTypes.AIR.getInternalId()); + } + + @Override + public @Nullable MutableBlockChange populate(@NotNull final MutableBlockChange change) { + try { + change.x = posDel.readX(is) + originX; + change.y = posDel.readY(is); + change.z = posDel.readZ(is) + originZ; + idDel.readCombined(is, change, dir); + return change; + } catch (EOFException ignored) { + } catch (Exception e) { + e.printStackTrace(); + } + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public boolean accepts(final Change change) { + return change instanceof MutableBlockChange; + } + + } + return new Populator(); + } + + private ChangePopulator biomeChangePopulator(boolean dir) throws IOException { + final FaweInputStream is = getBiomeIS(); + if (is == null) { + return ChangePopulator.empty(); + } + class Populator implements ChangePopulator { + + @Override + public @NotNull MutableBiomeChange create() { + return new MutableBiomeChange(); + } + + @Override + public @Nullable MutableBiomeChange populate(@NotNull final MutableBiomeChange change) { + try { + int int1 = is.read(); + if (int1 != -1) { + int x = ((int1 << 24) + (is.read() << 16) + (is.read() << 8) + is.read()) << 2; + int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read()) << 2; + int y = (is.read() - 128) << 2; + int from = is.readVarInt(); + int to = is.readVarInt(); + change.setBiome(x, y, z, from, to); + return change; + } + } catch (EOFException ignored) { + } catch (Exception e) { + e.printStackTrace(); + } + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public boolean accepts(final Change change) { + return change instanceof MutableBiomeChange; + } + + } + return new Populator(); + } + @Override public Iterator getIterator(final boolean dir) { try { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/NullChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/NullChangeSet.java index dd4fddf1a..99711b6cc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/NullChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/NullChangeSet.java @@ -1,6 +1,6 @@ package com.fastasyncworldedit.core.history.changeset; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.world.World; @@ -25,22 +25,22 @@ public class NullChangeSet extends AbstractChangeSet { } @Override - public final void addTileCreate(CompoundTag tag) { + public void addTileCreate(final FaweCompoundTag tag) { } @Override - public final void addTileRemove(CompoundTag tag) { + public void addTileRemove(final FaweCompoundTag tag) { } @Override - public final void addEntityRemove(CompoundTag tag) { + public void addEntityRemove(final FaweCompoundTag tag) { } @Override - public final void addEntityCreate(CompoundTag tag) { + public void addEntityCreate(final FaweCompoundTag tag) { } @@ -54,6 +54,17 @@ public class NullChangeSet extends AbstractChangeSet { return getIterator(redo); } + @Override + public ChangeExchangeCoordinator getCoordinatedChanges(final BlockBag blockBag, final int mode, final boolean dir) { + return new ChangeExchangeCoordinator(((exchanger, changes) -> { + try { + exchanger.exchange(null); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + })); + } + @Override public final Iterator getIterator(boolean undo) { return Collections.emptyIterator(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java new file mode 100644 index 000000000..5f2d6a711 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java @@ -0,0 +1,115 @@ +package com.fastasyncworldedit.core.internal.simd; + +import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter; +import com.fastasyncworldedit.core.function.mask.InverseMask; +import com.fastasyncworldedit.core.function.mask.SingleBlockStateMask; +import com.fastasyncworldedit.core.queue.Filter; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.InverseSingleBlockStateMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.VectorOperators; + +import javax.annotation.Nullable; + +public class SimdSupport { + + private static final boolean VECTOR_API_PRESENT; + + static { + boolean vectorApiPresent = false; + try { + Class.forName("jdk.incubator.vector.Vector"); + vectorApiPresent = true; + } catch (ClassNotFoundException ignored) { + } + VECTOR_API_PRESENT = vectorApiPresent; + if (!VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API) { + LogManagerCompat.getLogger() + .warn("FAWE use-vector-api is enabled but --add-modules=jdk.incubator.vector is not set."); + } + } + + public static boolean useVectorApi() { + return VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API; + } + + public static @Nullable VectorizedMask vectorizedTargetMask(Mask mask) { + if (!useVectorApi()) { + return null; + } + return switch (mask) { + case SingleBlockStateMask single -> vectorizedTargetMask(single.getBlockState().getOrdinalChar()); + case InverseSingleBlockStateMask inverse -> vectorizedTargetMaskInverse(inverse.getBlockState().getOrdinalChar()); + case ExistingBlockMask ignored -> vectorizedTargetMaskNonAir(); + case InverseMask inverse -> { + final VectorizedMask base = vectorizedTargetMask(inverse.inverse()); + if (base == null) { + yield null; + } + yield (set, get) -> base.compareVector(set, get).not(); + } + default -> null; + }; + } + + private static VectorizedMask vectorizedTargetMaskNonAir() { + // everything > VOID_AIR is not air + return (set, get) -> get.compare(VectorOperators.UNSIGNED_GT, BlockTypesCache.ReservedIDs.VOID_AIR); + } + + private static VectorizedMask vectorizedTargetMask(char ordinal) { + return (set, get) -> get.compare(VectorOperators.EQ, (short) ordinal); + } + + private static VectorizedMask vectorizedTargetMaskInverse(char ordinal) { + return (set, get) -> get.compare(VectorOperators.NE, (short) ordinal); + } + + public static @Nullable VectorizedFilter vectorizedPattern(Pattern pattern) { + if (!useVectorApi()) { + return null; + } + return switch (pattern) { + case BaseBlock block -> { + if (block.getNbtReference() == null) { + yield new VectorizedPattern<>(block, block.getOrdinalChar()); + } + yield null; + } + case BlockStateHolder blockStateHolder -> new VectorizedPattern<>( + blockStateHolder, + blockStateHolder.getOrdinalChar() + ); + default -> null; + }; + } + + private static final class VectorizedPattern extends DelegateFilter implements VectorizedFilter { + + private final char ordinal; + + public VectorizedPattern(final T parent, char ordinal) { + super(parent); + this.ordinal = ordinal; + } + + @Override + public ShortVector applyVector(final ShortVector get, final ShortVector set) { + return ShortVector.broadcast(ShortVector.SPECIES_PREFERRED, ordinal); + } + + @Override + public Filter newInstance(final Filter other) { + return new VectorizedPattern<>(other, ordinal); + } + + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java new file mode 100644 index 000000000..5c15da22a --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java @@ -0,0 +1,33 @@ +package com.fastasyncworldedit.core.internal.simd; + +import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock; +import com.fastasyncworldedit.core.queue.Filter; +import com.sk89q.worldedit.extent.Extent; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.VectorSpecies; + +public class VectorizedCharFilterBlock extends CharFilterBlock { + + public VectorizedCharFilterBlock(final Extent extent) { + super(extent); + } + + @Override + public synchronized void filter(final Filter filter) { + if (!(filter instanceof VectorizedFilter vecFilter)) { + throw new IllegalStateException("Unexpected VectorizedCharFilterBlock " + filter); + } + final VectorSpecies species = ShortVector.SPECIES_PREFERRED; + initSet(); // set array is null before + char[] setArr = this.setArr; + assert setArr != null; + char[] getArr = this.getArr; + // assume setArr.length == getArr.length == 4096 + for (int i = 0; i < 4096; i += species.length()) { + ShortVector set = ShortVector.fromCharArray(species, setArr, i); + ShortVector get = ShortVector.fromCharArray(species, getArr, i); + ShortVector res = vecFilter.applyVector(get, set); + res.intoCharArray(setArr, i); + } + } +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java new file mode 100644 index 000000000..7357bbd59 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java @@ -0,0 +1,8 @@ +package com.fastasyncworldedit.core.internal.simd; + +import com.fastasyncworldedit.core.queue.Filter; +import jdk.incubator.vector.ShortVector; + +public interface VectorizedFilter extends Filter { + ShortVector applyVector(ShortVector get, ShortVector set); +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java new file mode 100644 index 000000000..0f453f32b --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java @@ -0,0 +1,40 @@ +package com.fastasyncworldedit.core.internal.simd; + +import com.fastasyncworldedit.core.queue.IChunk; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.IChunkSet; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorSpecies; + +public interface VectorizedMask { + + default void processChunks(IChunk chunk, IChunkGet get, IChunkSet set) { + for (int layer = get.getMinSectionPosition(); layer <= get.getMaxSectionPosition(); layer++) { + final char[] sectionSet = set.loadIfPresent(layer); + if (sectionSet == null) { + continue; + } + final char[] sectionGet = get.load(layer); + processSection(layer, sectionSet, sectionGet); + } + } + + default void processSection(int layer, char[] set, char[] get) { + final VectorSpecies species = ShortVector.SPECIES_PREFERRED; + // assume that set.length % species.elementSize() == 0 + for (int i = 0; i < set.length; i += species.length()) { + ShortVector vectorSet = ShortVector.fromCharArray(species, set, i); + ShortVector vectorGet = ShortVector.fromCharArray(species, get, i); + vectorSet = processVector(vectorSet, vectorGet); + vectorSet.intoCharArray(set, i); + } + } + + default ShortVector processVector(ShortVector set, ShortVector get) { + return set.blend(0, compareVector(set, get).not()); + } + + VectorMask compareVector(ShortVector set, ShortVector get); + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java index d17787f03..2bb002914 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java @@ -5,16 +5,18 @@ import com.fastasyncworldedit.core.configuration.Settings; import java.util.Collections; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; public class FaweLimit { public int MAX_ACTIONS = 0; - public long MAX_CHANGES = 0; - public int MAX_FAILS = 0; - public long MAX_CHECKS = 0; - public int MAX_ITERATIONS = 0; - public int MAX_BLOCKSTATES = 0; - public int MAX_ENTITIES = 0; + public AtomicLong MAX_CHANGES = new AtomicLong(); + public AtomicInteger MAX_FAILS = new AtomicInteger(); + public AtomicLong MAX_CHECKS = new AtomicLong(); + public AtomicInteger MAX_ITERATIONS = new AtomicInteger(); + public AtomicInteger MAX_BLOCKSTATES = new AtomicInteger(); + public AtomicInteger MAX_ENTITIES = new AtomicInteger(); public int MAX_HISTORY = 0; public int SCHEM_FILE_SIZE_LIMIT = 0; public int SCHEM_FILE_NUM_LIMIT = 0; @@ -112,12 +114,12 @@ public class FaweLimit { MAX.SPEED_REDUCTION = 0; MAX.INVENTORY_MODE = 0; MAX.MAX_ACTIONS = 1; - MAX.MAX_CHANGES = Long.MAX_VALUE; - MAX.MAX_FAILS = Integer.MAX_VALUE; - MAX.MAX_CHECKS = Long.MAX_VALUE; - MAX.MAX_ITERATIONS = Integer.MAX_VALUE; - MAX.MAX_BLOCKSTATES = Integer.MAX_VALUE; - MAX.MAX_ENTITIES = Integer.MAX_VALUE; + MAX.MAX_CHANGES = new AtomicLong(Long.MAX_VALUE); + MAX.MAX_FAILS = new AtomicInteger(Integer.MAX_VALUE); + MAX.MAX_CHECKS = new AtomicLong(Long.MAX_VALUE); + MAX.MAX_ITERATIONS = new AtomicInteger(Integer.MAX_VALUE); + MAX.MAX_BLOCKSTATES = new AtomicInteger(Integer.MAX_VALUE); + MAX.MAX_ENTITIES = new AtomicInteger(Integer.MAX_VALUE); MAX.MAX_HISTORY = Integer.MAX_VALUE; MAX.SCHEM_FILE_NUM_LIMIT = Integer.MAX_VALUE; MAX.SCHEM_FILE_SIZE_LIMIT = Integer.MAX_VALUE; @@ -138,120 +140,144 @@ public class FaweLimit { } public boolean MAX_CHANGES() { - return MAX_CHANGES-- > 0; + return MAX_CHANGES.decrementAndGet() < 0; } public boolean MAX_FAILS() { - return MAX_FAILS-- > 0; + return MAX_FAILS.decrementAndGet() < 0; } public boolean MAX_CHECKS() { - return MAX_CHECKS-- > 0; + return MAX_CHECKS.decrementAndGet() < 0; } public boolean MAX_ITERATIONS() { - return MAX_ITERATIONS-- > 0; + return MAX_ITERATIONS.decrementAndGet() < 0; } public boolean MAX_BLOCKSTATES() { - return MAX_BLOCKSTATES-- > 0; + return MAX_BLOCKSTATES.decrementAndGet() < 0; } public boolean MAX_ENTITIES() { - return MAX_ENTITIES-- > 0; + return MAX_ENTITIES.decrementAndGet() < 0; } public void THROW_MAX_CHANGES() { - if (MAX_CHANGES-- <= 0) { + if (MAX_CHANGES.decrementAndGet() < 0) { throw FaweCache.MAX_CHANGES; } } public void THROW_MAX_FAILS() { - if (MAX_FAILS-- <= 0) { - throw FaweCache.MAX_CHECKS; + if (MAX_FAILS.decrementAndGet() < 0) { + throw FaweCache.MAX_FAILS; } } public void THROW_MAX_CHECKS() { - if (MAX_CHECKS-- <= 0) { + if (MAX_CHECKS.decrementAndGet() < 0) { throw FaweCache.MAX_CHECKS; } } public void THROW_MAX_ITERATIONS() { - if (MAX_ITERATIONS-- <= 0) { + if (MAX_ITERATIONS.decrementAndGet() < 0) { throw FaweCache.MAX_ITERATIONS; } } public void THROW_MAX_BLOCKSTATES() { - if (MAX_BLOCKSTATES-- <= 0) { + if (MAX_BLOCKSTATES.decrementAndGet() < 0) { throw FaweCache.MAX_TILES; } } public void THROW_MAX_ENTITIES() { - if (MAX_ENTITIES-- <= 0) { + if (MAX_ENTITIES.decrementAndGet() < 0) { throw FaweCache.MAX_ENTITIES; } } public void THROW_MAX_CHANGES(int amt) { - if ((MAX_CHANGES -= amt) <= 0) { + if (amt == 0) { + return; + } + if (MAX_CHANGES.addAndGet(-amt) < 0) { throw FaweCache.MAX_CHANGES; } } public void THROW_MAX_CHANGES(long amt) { - if ((MAX_CHANGES -= amt) <= 0) { + if (amt == 0) { + return; + } + if (MAX_CHANGES.addAndGet(-amt) < 0) { throw FaweCache.MAX_CHANGES; } } public void THROW_MAX_FAILS(int amt) { - if ((MAX_FAILS -= amt) <= 0) { - throw FaweCache.MAX_CHECKS; + if (amt == 0) { + return; + } + if (MAX_FAILS.addAndGet(-amt) < 0) { + throw FaweCache.MAX_FAILS; } } public void THROW_MAX_CHECKS(int amt) { - if ((MAX_CHECKS -= amt) <= 0) { + if (amt == 0) { + return; + } + if (MAX_CHECKS.addAndGet(-amt) < 0) { throw FaweCache.MAX_CHECKS; } } public void THROW_MAX_CHECKS(long amt) { - if ((MAX_CHECKS -= amt) <= 0) { + if (amt == 0) { + return; + } + if (MAX_CHECKS.addAndGet(-amt) < 0) { throw FaweCache.MAX_CHECKS; } } public void THROW_MAX_ITERATIONS(int amt) { - if ((MAX_ITERATIONS -= amt) <= 0) { + if (amt == 0) { + return; + } + if (MAX_ITERATIONS.addAndGet(-amt) < 0) { throw FaweCache.MAX_ITERATIONS; } } public void THROW_MAX_BLOCKSTATES(int amt) { - if ((MAX_BLOCKSTATES -= amt) <= 0) { + if (amt == 0) { + return; + } + if (MAX_BLOCKSTATES.addAndGet(-amt) < 0) { throw FaweCache.MAX_TILES; } } public void THROW_MAX_ENTITIES(int amt) { - if ((MAX_ENTITIES -= amt) <= 0) { + if (amt == 0) { + return; + } + if (MAX_ENTITIES.addAndGet(-amt) < 0) { throw FaweCache.MAX_ENTITIES; } } public boolean isUnlimited() { - return MAX_CHANGES == Long.MAX_VALUE - && MAX_FAILS == Integer.MAX_VALUE - && MAX_CHECKS == Long.MAX_VALUE - && MAX_ITERATIONS == Integer.MAX_VALUE - && MAX_BLOCKSTATES == Integer.MAX_VALUE - && MAX_ENTITIES == Integer.MAX_VALUE + return MAX_CHANGES.get() == Long.MAX_VALUE + && MAX_FAILS.get() == Integer.MAX_VALUE + && MAX_CHECKS.get() == Long.MAX_VALUE + && MAX_ITERATIONS.get() == Integer.MAX_VALUE + && MAX_BLOCKSTATES.get() == Integer.MAX_VALUE + && MAX_ENTITIES.get() == Integer.MAX_VALUE && MAX_HISTORY == Integer.MAX_VALUE && SCHEM_FILE_SIZE_LIMIT == Integer.MAX_VALUE && SCHEM_FILE_NUM_LIMIT == Integer.MAX_VALUE @@ -270,14 +296,30 @@ public class FaweLimit { && MAX_BUTCHER_RADIUS == Integer.MAX_VALUE; } + /** + * Get an {@link FaweLimit} representing the amount of a limit used from a given "original" limit + * + * @since TODO + */ + public FaweLimit getLimitUsed(FaweLimit originalLimit) { + FaweLimit newLimit = new FaweLimit(); + newLimit.MAX_CHANGES = new AtomicLong(originalLimit.MAX_CHANGES.get() - this.MAX_CHANGES.get()); + newLimit.MAX_FAILS = new AtomicInteger(originalLimit.MAX_FAILS.get() - this.MAX_FAILS.get()); + newLimit.MAX_CHECKS = new AtomicLong(originalLimit.MAX_CHECKS.get() - this.MAX_CHECKS.get()); + newLimit.MAX_ITERATIONS = new AtomicInteger(originalLimit.MAX_ITERATIONS.get() - this.MAX_ITERATIONS.get()); + newLimit.MAX_BLOCKSTATES = new AtomicInteger(originalLimit.MAX_BLOCKSTATES.get() - this.MAX_BLOCKSTATES.get()); + newLimit.MAX_ENTITIES = new AtomicInteger(originalLimit.MAX_ENTITIES.get() - this.MAX_ENTITIES.get()); + return newLimit; + } + public void set(FaweLimit limit) { MAX_ACTIONS = limit.MAX_ACTIONS; - MAX_CHANGES = limit.MAX_CHANGES; - MAX_BLOCKSTATES = limit.MAX_BLOCKSTATES; - MAX_CHECKS = limit.MAX_CHECKS; - MAX_ENTITIES = limit.MAX_ENTITIES; - MAX_FAILS = limit.MAX_FAILS; - MAX_ITERATIONS = limit.MAX_ITERATIONS; + MAX_CHANGES.set(limit.MAX_CHANGES.get()); + MAX_FAILS.set(limit.MAX_FAILS.get()); + MAX_CHECKS.set(limit.MAX_CHECKS.get()); + MAX_ITERATIONS.set(limit.MAX_ITERATIONS.get()); + MAX_BLOCKSTATES.set(limit.MAX_BLOCKSTATES.get()); + MAX_ENTITIES.set(limit.MAX_ENTITIES.get()); MAX_HISTORY = limit.MAX_HISTORY; SCHEM_FILE_NUM_LIMIT = limit.SCHEM_FILE_NUM_LIMIT; SCHEM_FILE_SIZE_LIMIT = limit.SCHEM_FILE_SIZE_LIMIT; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/IntPair.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/IntPair.java index f7f451e79..b5667ccef 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/IntPair.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/IntPair.java @@ -4,7 +4,9 @@ public record IntPair(int x, int z) { @Override public int hashCode() { - return (x << 16) | (z & 0xFFFF); + int i = 1664525 * x + 1013904223; + int j = 1664525 * (z ^ -559038737) + 1013904223; + return i ^ j; } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/EagerFaweCompoundTag.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/EagerFaweCompoundTag.java new file mode 100644 index 000000000..c41d8db1d --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/EagerFaweCompoundTag.java @@ -0,0 +1,7 @@ +package com.fastasyncworldedit.core.nbt; + +import org.enginehub.linbus.tree.LinCompoundTag; + +record EagerFaweCompoundTag(LinCompoundTag linTag) implements FaweCompoundTag { + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/FaweCompoundTag.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/FaweCompoundTag.java new file mode 100644 index 000000000..52e38908d --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/FaweCompoundTag.java @@ -0,0 +1,44 @@ +package com.fastasyncworldedit.core.nbt; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.sk89q.worldedit.util.concurrency.LazyReference; +import org.enginehub.linbus.tree.LinCompoundTag; + +/** + * A wrapper around compound tags, potentially lazily transformed. + * @since 2.11.2 + */ +public sealed interface FaweCompoundTag permits EagerFaweCompoundTag, LazyFaweCompoundTag { + + /** + * {@return a lazy compound component backed by a lazy reference} + * @param lazyReference the lazy reference to the actual compound tag + */ + static FaweCompoundTag of(LazyReference lazyReference) { + return new LazyFaweCompoundTag(lazyReference::getValue); + } + + /** + * {@return a lazy compound component backed by a supplier} + * Invocations to the supplier are memoized. + * @param supplier the supplier for the actual compound tag + */ + static FaweCompoundTag of(Supplier supplier) { + return new LazyFaweCompoundTag(Suppliers.memoize(supplier)); + } + + /** + * {@return a direct reference tho the given compound tag} + * @param linCompoundTag the tag to wrap + */ + static FaweCompoundTag of(LinCompoundTag linCompoundTag) { + return new EagerFaweCompoundTag(linCompoundTag); + } + + /** + * {@return the underlying tag} + */ + LinCompoundTag linTag(); + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/LazyFaweCompoundTag.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/LazyFaweCompoundTag.java new file mode 100644 index 000000000..afd0e4e48 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/nbt/LazyFaweCompoundTag.java @@ -0,0 +1,14 @@ +package com.fastasyncworldedit.core.nbt; + +import org.enginehub.linbus.tree.LinCompoundTag; + +import java.util.function.Supplier; + +record LazyFaweCompoundTag(Supplier linTagSupplier) implements FaweCompoundTag { + + @Override + public LinCompoundTag linTag() { + return this.linTagSupplier().get(); + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java index 366faa8c5..f308d4806 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java @@ -3,6 +3,7 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.sk89q.worldedit.regions.Region; +import javax.annotation.Nonnull; import javax.annotation.Nullable; /** @@ -10,30 +11,29 @@ import javax.annotation.Nullable; */ public interface Filter { - /** - * Checks whether a chunk should be read. - * - * @param chunkX the x coordinate in the chunk - * @param chunkZ the z coordinate in the chunk - */ - default boolean appliesChunk( - int chunkX, - int chunkZ - ) { - return true; - } +// /** +// * Checks whether a chunk should be read. +// * +// * @param chunkX the x coordinate in the chunk +// * @param chunkZ the z coordinate in the chunk +// */ +// default boolean appliesChunk( +// int chunkX, +// int chunkZ +// ) { +// return true; +// } /** - * Do something with the IChunk
- Return null if you don't want to filter blocks
- - * Return the chunk if you do want to filter blocks
+ * Do something with the IChunk
*/ - default T applyChunk(T chunk, @Nullable Region region) { + default @Nonnull T applyChunk(T chunk, @Nullable Region region) { return chunk; } - default boolean appliesLayer(IChunk chunk, int layer) { - return true; - } +// default boolean appliesLayer(IChunk chunk, int layer) { +// return true; +// } /** * Make changes to the block here
- e.g., block.setId(...)
- Note: Performance is diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java index 4ba91a4f3..7bce208fc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java @@ -3,15 +3,16 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockTypesCache; import javax.annotation.Nullable; +import java.util.Collection; import java.util.Map; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.function.Function; @@ -50,7 +51,7 @@ public interface IBatchProcessor { } /** - * Convert this processor into an Extent based processor instead of a queue batch based on. + * Convert this processor into an Extent based processor instead of a queue batch based one. */ @Nullable Extent construct(Extent child); @@ -62,40 +63,41 @@ public interface IBatchProcessor { * @return false if chunk is empty of blocks */ default boolean trimY(IChunkSet set, int minY, int maxY, final boolean keepInsideRange) { - int minLayer = (minY - 1) >> 4; - int maxLayer = (maxY + 1) >> 4; + int minLayer = minY >> 4; + int maxLayer = maxY >> 4; if (keepInsideRange) { - for (int layer = set.getMinSectionPosition(); layer <= minLayer; layer++) { - if (set.hasSection(layer)) { - if (layer == minLayer) { - char[] arr = set.loadIfPresent(layer); - if (arr != null) { - int index = (minY & 15) << 8; - for (int i = 0; i < index; i++) { - arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; - } - set.setBlocks(layer, arr); - } - } else { - set.setBlocks(layer, null); + for (int layer = set.getMinSectionPosition(); layer <= set.getMaxSectionPosition(); layer++) { + if (!set.hasSection(layer)) { + continue; + } + // wipe all data from chunk layers above or below the max / min layer + if (layer < minLayer || layer > maxLayer) { + set.setBlocks(layer, null); + continue; + } + // if chunk layer / section is fully enclosed by minY to maxY, keep as is + if (layer > minLayer && layer < maxLayer) { + continue; + } + char[] blocks = set.loadIfPresent(layer); + if (blocks == null) { + continue; + } + // When on the minimum layer (as defined by minY), remove blocks up to minY (exclusive) + if (layer == minLayer) { + int index = (minY & 15) << 8; + for (int i = 0; i < index; i++) { + blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__; } } - } - for (int layer = maxLayer; layer <= set.getMaxSectionPosition(); layer++) { - if (set.hasSection(layer)) { - if (layer == maxLayer) { - char[] arr = set.loadIfPresent(layer); - if (arr != null) { - int index = ((maxY + 1) & 15) << 8; - for (int i = index; i < arr.length; i++) { - arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; - } - set.setBlocks(layer, arr); - } - } else { - set.setBlocks(layer, null); + // When on the maximum layer (as defined by maxY), remove blocks above maxY (exclusive) + if (layer == maxLayer) { + int index = ((maxY & 15) + 1) << 8; + for (int i = index; i < blocks.length; i++) { + blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__; } } + set.setBlocks(layer, blocks); } try { int layer = (minY - 15) >> 4; @@ -155,11 +157,11 @@ public interface IBatchProcessor { */ @Deprecated(forRemoval = true, since = "2.8.4") default boolean trimNBT(IChunkSet set, Function contains) { - Set ents = set.getEntities(); + Collection ents = set.entities(); if (!ents.isEmpty()) { - ents.removeIf(ent -> !contains.apply(ent.getEntityPosition().toBlockPoint())); + ents.removeIf(ent -> !contains.apply(NbtUtils.entityPosition(ent).toBlockPoint())); } - Map tiles = set.getTiles(); + Map tiles = set.tiles(); if (!tiles.isEmpty()) { tiles.entrySet().removeIf(blockVector3CompoundTagEntry -> !contains .apply(blockVector3CompoundTagEntry.getKey())); @@ -176,11 +178,11 @@ public interface IBatchProcessor { default boolean trimNBT( IChunkSet set, Function containsEntity, Function containsTile ) { - Set ents = set.getEntities(); + Collection ents = set.entities(); if (!ents.isEmpty()) { - ents.removeIf(ent -> !containsEntity.apply(ent.getEntityPosition().toBlockPoint())); + ents.removeIf(ent -> !containsEntity.apply(NbtUtils.entityPosition(ent).toBlockPoint())); } - Map tiles = set.getTiles(); + Map tiles = set.tiles(); if (!tiles.isEmpty()) { tiles.entrySet().removeIf(blockVector3CompoundTagEntry -> !containsTile.apply(blockVector3CompoundTagEntry.getKey())); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java index ff6842c0a..3a9d71da1 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java @@ -3,6 +3,8 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream; import com.fastasyncworldedit.core.internal.io.FaweOutputStream; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; @@ -12,10 +14,13 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.registry.BlockRegistry; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; +import java.util.Collection; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.IntStream; /** @@ -54,11 +59,38 @@ public interface IBlocks extends Trimable { BlockState getBlock(int x, int y, int z); - Map getTiles(); + @Deprecated(forRemoval = true, since = "2.11.2") + default Map getTiles() { + return AdaptedMap.values(tiles(), ct -> FaweCompoundTag.of(ct.toLinTag()), IBlocks::toCompoundTag); + } - CompoundTag getTile(int x, int y, int z); + Map tiles(); - Set getEntities(); + @Deprecated(forRemoval = true, since = "2.11.2") + default CompoundTag getTile(int x, int y, int z) { + final FaweCompoundTag tile = tile(x, y, z); + if (tile == null) { + return null; + } + return toCompoundTag(tile); + } + + @SuppressWarnings({"deprecation"}) + private static @Nonnull CompoundTag toCompoundTag(FaweCompoundTag tile) { + return new CompoundTag(tile.linTag()); + } + + @Nullable + FaweCompoundTag tile(int x, int y, int z); + + @Deprecated(forRemoval = true, since = "2.11.2") + default Set getEntities() { + return entities().stream() + .map(IBlocks::toCompoundTag) + .collect(Collectors.toSet()); + } + + Collection entities(); BiomeType getBiomeType(int x, int y, int z); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java index 534a96fac..615f5fb50 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java @@ -1,11 +1,7 @@ package com.fastasyncworldedit.core.queue; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; @@ -16,6 +12,12 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinDoubleTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinStringTag; +import org.enginehub.linbus.tree.LinTag; +import org.enginehub.linbus.tree.LinTagType; import java.util.ArrayList; import java.util.HashMap; @@ -39,9 +41,9 @@ public interface IChunkExtent extends Extent { } @Override - default boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + default boolean tile(int x, int y, int z, FaweCompoundTag tile) throws WorldEditException { final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4); - return chunk.setTile(x & 15, y, z & 15, tile); + return chunk.tile(x & 15, y, z & 15, tile); } @Override @@ -124,19 +126,19 @@ public interface IChunkExtent extends Extent { @Override default Entity createEntity(Location location, BaseEntity entity, UUID uuid) { final IChunk chunk = getOrCreateChunk(location.getBlockX() >> 4, location.getBlockZ() >> 4); - Map> map = new HashMap<>(entity.getNbtData().getValue()); //do not modify original entity data - map.put("Id", new StringTag(entity.getType().getName())); + Map> map = new HashMap<>(entity.getNbt().value()); //do not modify original entity data + map.put("Id", LinStringTag.of(entity.getType().getName())); //Set pos - List posList = new ArrayList<>(); - posList.add(new DoubleTag(location.x())); - posList.add(new DoubleTag(location.y())); - posList.add(new DoubleTag(location.z())); - map.put("Pos", new ListTag(DoubleTag.class, posList)); + List posList = new ArrayList<>(); + posList.add(LinDoubleTag.of(location.x())); + posList.add(LinDoubleTag.of(location.y())); + posList.add(LinDoubleTag.of(location.z())); + map.put("Pos", LinListTag.of(LinTagType.doubleTag(), posList)); - NBTUtils.addUUIDToMap(map, uuid); + NbtUtils.addUUIDToMap(map, uuid); - chunk.setEntity(new CompoundTag(map)); + chunk.entity(FaweCompoundTag.of(LinCompoundTag.of(map))); return new IChunkEntity(this, location, uuid, entity); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java index b2ac59cb2..b72690029 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.InputExtent; import com.sk89q.worldedit.math.BlockVector3; @@ -46,7 +47,26 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { > T call(IChunkSet set, Runnable finalize); - CompoundTag getEntity(UUID uuid); + @Deprecated(forRemoval = true, since = "2.11.2") + default CompoundTag getEntity(UUID uuid) { + final FaweCompoundTag entity = entity(uuid); + if (entity == null) { + return null; + } + return new CompoundTag(entity.linTag()); + } + + /** + * {@return the compound tag describing the entity with the given UUID, if any} + * @param uuid the uuid of the entity + */ + @Nullable FaweCompoundTag entity(UUID uuid); + + @Override + @Deprecated(forRemoval = true, since = "2.11.2") + default CompoundTag getTile(int x, int y, int z) { + return IBlocks.super.getTile(x, y, z); + } boolean isCreateCopy(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java index bc621e66a..fc1482d6e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java @@ -1,7 +1,9 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.OutputExtent; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector3; @@ -36,7 +38,12 @@ public interface IChunkSet extends IBlocks, OutputExtent { boolean isEmpty(); @Override - boolean setTile(int x, int y, int z, CompoundTag tile); + @Deprecated(forRemoval = true, since = "2.11.2") + default boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + return tile(x, y, z, FaweCompoundTag.of(tile.toLinTag())); + } + + boolean tile(int x, int y, int z, FaweCompoundTag tag); @Override void setBlockLight(int x, int y, int z, int value); @@ -53,7 +60,12 @@ public interface IChunkSet extends IBlocks, OutputExtent { void setFullBright(int layer); - void setEntity(CompoundTag tag); + @Deprecated(forRemoval = true, since = "2.11.2") + default void setEntity(CompoundTag tag) { + entity(FaweCompoundTag.of(tag::toLinTag)); + } + + void entity(FaweCompoundTag tag); void removeEntity(UUID uuid); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IDelegateFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IDelegateFilter.java index a4bb54017..1e172dd19 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IDelegateFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IDelegateFilter.java @@ -9,21 +9,11 @@ public interface IDelegateFilter extends Filter { Filter getParent(); - @Override - default boolean appliesChunk(int chunkX, int chunkZ) { - return getParent().appliesChunk(chunkX, chunkZ); - } - @Override default V applyChunk(V chunk, @Nullable Region region) { return getParent().applyChunk(chunk, region); } - @Override - default boolean appliesLayer(IChunk chunk, int layer) { - return getParent().appliesLayer(chunk, layer); - } - @Override default void applyBlock(FilterBlock block) { getParent().applyBlock(block); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java index 3f104b35d..d09b7f04b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java @@ -2,6 +2,9 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.IBatchProcessorHolder; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedCharFilterBlock; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; @@ -137,16 +140,20 @@ public interface IQueueExtent extends Flushable, Trimable, ICh int chunkZ, boolean full ) { - if (!filter.appliesChunk(chunkX, chunkZ)) { - return block; - } +// if (!filter.appliesChunk(chunkX, chunkZ)) { +// return block; +// } T chunk = this.getOrCreateChunk(chunkX, chunkZ); T newChunk = filter.applyChunk(chunk, region); if (newChunk != null) { chunk = newChunk; if (block == null) { - block = this.createFilterBlock(); + if (SimdSupport.useVectorApi() && filter instanceof VectorizedFilter) { + block = new VectorizedCharFilterBlock(this); + } else { + block = this.createFilterBlock(); + } } block.initChunk(chunkX, chunkZ); chunk.filterBlocks(filter, block, region, full); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/ITileInput.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/ITileInput.java index 5d0641c81..4a88118ef 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/ITileInput.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/ITileInput.java @@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.queue; import com.sk89q.jnbt.CompoundTag; +@Deprecated(forRemoval = true, since = "2.11.2") public interface ITileInput { CompoundTag getTile(int x, int y, int z); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 1b56afdbc..56a238166 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -14,6 +14,8 @@ import com.fastasyncworldedit.core.extent.processor.BatchProcessorHolder; import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor; import com.fastasyncworldedit.core.function.mask.BlockMaskBuilder; import com.fastasyncworldedit.core.internal.exception.FaweException; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; @@ -28,7 +30,6 @@ 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.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.world.World; @@ -223,7 +224,9 @@ public class ParallelQueueExtent extends PassthroughExtent { @Override public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { - return this.changes = apply(region, new LinkedFilter<>(pattern, new CountFilter()), true).getChild().getTotal(); + VectorizedFilter vectorizedPattern = SimdSupport.vectorizedPattern(pattern); + var filter = LinkedFilter.of(vectorizedPattern == null ? pattern : vectorizedPattern, new CountFilter()); + return this.changes = apply(region, filter, true).getRight().getTotal(); } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java index ae976777c..76f2191ed 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java @@ -2,9 +2,9 @@ package com.fastasyncworldedit.core.queue.implementation.blocks; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.util.collection.MemBlockSet; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockState; @@ -13,6 +13,7 @@ import com.sk89q.worldedit.world.block.BlockTypesCache; import javax.annotation.Nullable; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -74,7 +75,7 @@ public class BitSetBlocks implements IChunkSet { } @Override - public boolean setTile(int x, int y, int z, CompoundTag tile) { + public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tag) { return false; } @@ -107,7 +108,7 @@ public class BitSetBlocks implements IChunkSet { } @Override - public void setEntity(CompoundTag tag) { + public void entity(final FaweCompoundTag tag) { } @Override @@ -181,18 +182,18 @@ public class BitSetBlocks implements IChunkSet { } @Override - public Map getTiles() { + public Map tiles() { return Collections.emptyMap(); } @Override - public CompoundTag getTile(int x, int y, int z) { + public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) { return null; } @Override - public Set getEntities() { - return Collections.emptySet(); + public Collection entities() { + return Collections.emptyList(); } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java index 2c89a7108..cd1c1b76f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.core.queue.implementation.blocks; +import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.sk89q.worldedit.world.block.BaseBlock; @@ -20,7 +21,7 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet { @Override public BaseBlock getFullBlock(int x, int y, int z) { BlockState state = BlockTypesCache.states[get(x, y, z)]; - return state.toBaseBlock(this, x, y, z); + return state.toBaseBlock((IBlocks) this, x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java index da28e5925..8d7c059ed 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java @@ -4,16 +4,18 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.math.BlockVector3ChunkMap; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.Pool; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypesCache; +import javax.annotation.Nullable; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.HashSet; @@ -36,8 +38,8 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { public BiomeType[][] biomes; public char[][] light; public char[][] skyLight; - public BlockVector3ChunkMap tiles; - public HashSet entities; + public BlockVector3ChunkMap tiles; + public HashSet entities; public HashSet entityRemoves; public EnumMap heightMaps; private boolean fastMode = false; @@ -71,17 +73,17 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { } @Override - public Map getTiles() { + public Map tiles() { return tiles == null ? Collections.emptyMap() : tiles; } @Override - public CompoundTag getTile(int x, int y, int z) { + public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) { return tiles == null ? null : tiles.get(x, y, z); } @Override - public Set getEntities() { + public Collection entities() { return entities == null ? Collections.emptySet() : entities; } @@ -132,12 +134,12 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { } @Override - public boolean setTile(int x, int y, int z, CompoundTag tile) { + public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tag) { if (tiles == null) { tiles = new BlockVector3ChunkMap<>(); } updateSectionIndexRange(y >> 4); - tiles.put(x, y, z, tile); + tiles.put(x, y, z, tag); return true; } @@ -259,7 +261,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { } @Override - public void setEntity(CompoundTag tag) { + public void entity(final FaweCompoundTag tag) { if (entities == null) { entities = new HashSet<>(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java index 042870666..273f44952 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java @@ -2,10 +2,10 @@ package com.fastasyncworldedit.core.queue.implementation.blocks; import com.fastasyncworldedit.core.FaweCache; 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.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; @@ -15,9 +15,9 @@ import com.sk89q.worldedit.world.block.BlockTypes; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collection; import java.util.Collections; import java.util.Map; -import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; @@ -48,24 +48,19 @@ public final class NullChunkGet implements IChunkGet { return BlockTypes.AIR.getDefaultState(); } - @Nonnull - public Map getTiles() { + @Override + public Map tiles() { return Collections.emptyMap(); } - @Nullable - public CompoundTag getTile(int x, int y, int z) { + @Override + public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) { return null; } - @Nullable - public Set getEntities() { - return null; - } - - @Nullable - public CompoundTag getEntity(@Nonnull UUID uuid) { - return null; + @Override + public Collection entities() { + return Collections.emptyList(); } @Override @@ -123,6 +118,11 @@ public final class NullChunkGet implements IChunkGet { return null; } + @Override + public @Nullable FaweCompoundTag entity(final UUID uuid) { + return null; + } + @Nonnull public char[] load(int layer) { return FaweCache.INSTANCE.EMPTY_CHAR_4096; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java index 90ae6b32e..c21e932a9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java @@ -4,9 +4,9 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.math.BlockVector3ChunkMap; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.IChunkSet; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; @@ -17,6 +17,7 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; @@ -44,8 +45,8 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { private BiomeType[][] biomes; private char[][] light; private char[][] skyLight; - private BlockVector3ChunkMap tiles; - private HashSet entities; + private BlockVector3ChunkMap tiles; + private HashSet entities; private HashSet entityRemoves; private Map heightMaps; private boolean fastMode; @@ -64,8 +65,8 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { int sectionCount, char[][] light, char[][] skyLight, - BlockVector3ChunkMap tiles, - HashSet entities, + BlockVector3ChunkMap tiles, + HashSet entities, HashSet entityRemoves, Map heightMaps, char defaultOrdinal, @@ -116,18 +117,18 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { } @Override - public Map getTiles() { + public Map tiles() { return tiles == null ? Collections.emptyMap() : tiles; } @Override - public CompoundTag getTile(int x, int y, int z) { + public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) { return tiles == null ? null : tiles.get(x, y, z); } @Override - public Set getEntities() { - return entities == null ? Collections.emptySet() : entities; + public Collection entities() { + return entities == null ? Collections.emptyList() : entities; } @Override @@ -268,12 +269,12 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { } @Override - public boolean setTile(int x, int y, int z, CompoundTag tile) { + public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tag) { updateSectionIndexRange(y >> 4); if (tiles == null) { tiles = new BlockVector3ChunkMap<>(); } - tiles.put(x, y, z, tile); + tiles.put(x, y, z, tag); return true; } @@ -358,7 +359,7 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { } @Override - public void setEntity(CompoundTag tag) { + public void entity(final FaweCompoundTag tag) { if (entities == null) { entities = new HashSet<>(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index a7417eddf..7c36eb94c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -3,6 +3,7 @@ package com.fastasyncworldedit.core.queue.implementation.chunk; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; @@ -11,7 +12,6 @@ import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; import com.fastasyncworldedit.core.util.MemUtil; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -22,6 +22,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; +import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -76,18 +77,8 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public boolean setTile(int x, int y, int z, CompoundTag tag) { - return delegate.set(this).setTile(x, y, z, tag); - } - - @Override - public CompoundTag getTile(int x, int y, int z) { - return delegate.set(this).getTile(x, y, z); - } - - @Override - public void setEntity(CompoundTag tag) { - delegate.set(this).setEntity(tag); + public void entity(final FaweCompoundTag tag) { + delegate.set(this).entity(tag); } @Override @@ -163,11 +154,6 @@ public class ChunkHolder> implements IQueueChunk { return isInit; } - @Override - public CompoundTag getEntity(UUID uuid) { - return delegate.get(this).getEntity(uuid); - } - @Override public int setCreateCopy(boolean createCopy) { this.createCopy = createCopy; @@ -879,16 +865,6 @@ public class ChunkHolder> implements IQueueChunk { } }; - @Override - public Map getTiles() { - return delegate.get(this).getTiles(); - } - - @Override - public Set getEntities() { - return delegate.get(this).getEntities(); - } - @Override public boolean hasSection(int layer) { return chunkExisting != null && chunkExisting.hasSection(layer); @@ -946,6 +922,11 @@ public class ChunkHolder> implements IQueueChunk { return chunkSet == null || chunkSet.isEmpty(); } + @Override + public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tag) { + return false; + } + /** * Get or create the existing part of this chunk. */ @@ -1093,6 +1074,21 @@ public class ChunkHolder> implements IQueueChunk { return delegate.getBlock(this, x, y, z); } + @Override + public Map tiles() { + return delegate.get(this).tiles(); + } + + @Override + public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) { + return delegate.get(this).tile(x, y, z); + } + + @Override + public Collection entities() { + return delegate.get(this).entities(); + } + @Override public BaseBlock getFullBlock(int x, int y, int z) { return delegate.getFullBlock(this, x, y, z); @@ -1158,6 +1154,11 @@ public class ChunkHolder> implements IQueueChunk { return delegate.getHeightMap(this, type); } + @Override + public @Nullable FaweCompoundTag entity(final UUID uuid) { + return delegate.get(this).entity(uuid); + } + public interface IBlockDelegate { > IChunkGet get(ChunkHolder chunk); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java index 8cc6471ba..14107e83a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java @@ -2,10 +2,10 @@ package com.fastasyncworldedit.core.queue.implementation.chunk; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IQueueChunk; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; @@ -16,6 +16,7 @@ import com.sk89q.worldedit.world.block.BlockTypes; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -53,13 +54,11 @@ public final class NullChunk implements IQueueChunk { return false; } - public boolean setTile(int x, int y, int z, @Nonnull CompoundTag tag) { + @Override + public boolean tile(final int x, final int y, final int z, final FaweCompoundTag tag) { return false; } - public void setEntity(@Nonnull CompoundTag tag) { - } - public void removeEntity(@Nonnull UUID uuid) { } @@ -112,6 +111,10 @@ public final class NullChunk implements IQueueChunk { public void setFullBright(int layer) { } + @Override + public void entity(final FaweCompoundTag tag) { + } + public void removeSectionLighting(int layer, boolean sky) { } @@ -147,25 +150,26 @@ public final class NullChunk implements IQueueChunk { return BlockTypes.__RESERVED__.getDefaultState(); } + @Override + public Map tiles() { + return Collections.emptyMap(); + } + + @Override + public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) { + return null; + } + + @Override + public Collection entities() { + return Collections.emptyList(); + } + @Nonnull public BaseBlock getFullBlock(int x, int y, int z) { return BlockTypes.__RESERVED__.getDefaultState().toBaseBlock(); } - @Nonnull - public Map getTiles() { - return Collections.emptyMap(); - } - - @Nullable - public CompoundTag getTile(int x, int y, int z) { - return null; - } - - @Nonnull - public Set getEntities() { - return Collections.emptySet(); - } @Nullable public char[] load(int layer) { @@ -178,11 +182,6 @@ public final class NullChunk implements IQueueChunk { return null; } - @Nullable - public CompoundTag getEntity(@Nonnull UUID uuid) { - return null; - } - @Override public int setCreateCopy(boolean createCopy) { return -1; @@ -230,6 +229,11 @@ public final class NullChunk implements IQueueChunk { return null; } + @Override + public @Nullable FaweCompoundTag entity(final UUID uuid) { + return null; + } + public boolean trim(boolean aggressive) { return true; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java index 0c323e47b..04da475e2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java @@ -34,6 +34,7 @@ import net.jpountz.lz4.LZ4Compressor; import net.jpountz.lz4.LZ4Factory; import net.jpountz.lz4.LZ4FastDecompressor; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -424,8 +425,10 @@ public class MainUtil { * @param y New Y coordinate * @param z New Z coordinate * @return New tag + * @deprecated use {@link NbtUtils#withPosition} instead */ @Nonnull + @Deprecated(forRemoval = true, since = "2.11.2") public static CompoundTag setPosition(@Nonnull CompoundTag tag, int x, int y, int z) { Map> value = new HashMap<>(tag.getValue()); value.put("x", new IntTag(x)); @@ -440,8 +443,10 @@ public class MainUtil { * @param tag Tag to copy * @param entity Entity * @return New tag + * @deprecated use {@link NbtUtils#withEntityInfo(LinCompoundTag, Entity)} instead */ @Nonnull + @Deprecated(forRemoval = true, since = "2.11.2") public static CompoundTag setEntityInfo(@Nonnull CompoundTag tag, @Nonnull Entity entity) { Map> map = new HashMap<>(tag.getValue()); map.put("Id", new StringTag(entity.getState().getType().id())); @@ -701,7 +706,7 @@ public class MainUtil { public static File resolve(File dir, String filename, @Nullable ClipboardFormat format, boolean allowDir) { if (format != null) { - if (!filename.matches(".*\\.[\\w].*")) { + if (!filename.matches(".*\\.\\w.*")) { filename = filename + "." + format.getPrimaryFileExtension(); } return MainUtil.resolveRelative(new File(dir, filename)); @@ -712,7 +717,7 @@ public class MainUtil { return file; } } - if (filename.matches(".*\\.[\\w].*")) { + if (filename.matches(".*\\.\\w.*")) { File file = MainUtil.resolveRelative(new File(dir, filename)); if (file.exists()) { return file; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/NbtUtils.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/NbtUtils.java index 07157e820..8b427c322 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/NbtUtils.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/NbtUtils.java @@ -1,17 +1,30 @@ package com.fastasyncworldedit.core.util; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.storage.InvalidFormatException; import org.enginehub.linbus.tree.LinByteTag; import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinDoubleTag; +import org.enginehub.linbus.tree.LinIntArrayTag; import org.enginehub.linbus.tree.LinIntTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinLongTag; import org.enginehub.linbus.tree.LinShortTag; import org.enginehub.linbus.tree.LinTag; import org.enginehub.linbus.tree.LinTagType; +import javax.annotation.Nonnull; import java.util.HashMap; import java.util.Map; +import java.util.UUID; -public class NbtUtils { +public final class NbtUtils { + + private NbtUtils() { + } /** * Get child tag of a NBT structure. @@ -79,4 +92,138 @@ public class NbtUtils { return value; } + /** + * Tries to extract UUID information from a compound tag + * + * @param compoundTag the compound tag to extract uuid information from + * @return the extracted UUID + * @since 2.11.2 + */ + public static UUID uuid(FaweCompoundTag compoundTag) { + final LinCompoundTag linTag = compoundTag.linTag(); + { + final LinIntArrayTag uuidTag = linTag.findTag("UUID", LinTagType.intArrayTag()); + if (uuidTag != null) { + int[] arr = uuidTag.value(); + return new UUID((long) arr[0] << 32 | (arr[1] & 0xFFFFFFFFL), (long) arr[2] << 32 | (arr[3] & 0xFFFFFFFFL)); + } + } + { + final LinLongTag uuidMostTag = linTag.findTag("UUIDMost", LinTagType.longTag()); + if (uuidMostTag != null) { + return new UUID(uuidMostTag.valueAsLong(), linTag.getTag("UUIDLeast", LinTagType.longTag()).valueAsLong()); + } + } + { + final LinLongTag uuidMostTag = linTag.findTag("WorldUUIDMost", LinTagType.longTag()); + if (uuidMostTag != null) { + return new UUID(uuidMostTag.valueAsLong(), linTag.getTag("WorldUUIDLeast", LinTagType.longTag()).valueAsLong()); + } + + } + { + final LinLongTag uuidMostTag = linTag.findTag("PersistentIDMSB", LinTagType.longTag()); + if (uuidMostTag != null) { + return new UUID(uuidMostTag.valueAsLong(), linTag.getTag("PersistentIDLSB", LinTagType.longTag()).valueAsLong()); + } + + } + throw new IllegalArgumentException("no uuid present"); + + } + + /** + * Create a copy of the tag and modify the (x, y, z) coordinates + * + * @param tag Tag to copy + * @param x New X coordinate + * @param y New Y coordinate + * @param z New Z coordinate + * @return New tag + * @since 2.11.2 + */ + public static @Nonnull LinCompoundTag withPosition(@Nonnull LinCompoundTag tag, int x, int y, int z) { + return tag.toBuilder() + .putInt("x", x) + .putInt("y", y) + .putInt("z", z) + .build(); + } + + /** + * Create a copy of the tag and modify the (x, y, z) coordinates + * + * @param tag Tag to copy + * @param x New X coordinate + * @param y New Y coordinate + * @param z New Z coordinate + * @return New tag + * @since 2.11.2 + */ + public static @Nonnull FaweCompoundTag withPosition(@Nonnull FaweCompoundTag tag, int x, int y, int z) { + return FaweCompoundTag.of(withPosition(tag.linTag(), x, y, z)); + } + + /** + * {@return a copy of the given tag with the Id and the Pos of the given entity} + * + * @param tag the tag to copy + * @param entity the entity to use the Id and the Pos from + * @since 2.11.2 + */ + public static @Nonnull LinCompoundTag withEntityInfo(@Nonnull LinCompoundTag tag, @Nonnull Entity entity) { + final LinCompoundTag.Builder builder = tag.toBuilder() + .putString("Id", entity.getState().getType().id()); + LinListTag pos = tag.findListTag("Pos", LinTagType.doubleTag()); + if (pos != null) { // TODO why only if pos != null? + Location loc = entity.getLocation(); + final LinListTag newPos = LinListTag.builder(LinTagType.doubleTag()) + .add(LinDoubleTag.of(loc.x())) + .add(LinDoubleTag.of(loc.y())) + .add(LinDoubleTag.of(loc.z())) + .build(); + builder.put("Pos", newPos); + } + return builder.build(); + } + + /** + * Adds a UUID to the given map + * + * @param map the map to insert to + * @param uuid the uuid to insert + * @since 2.11.2 + */ + public static void addUUIDToMap(Map> map, UUID uuid) { + int[] uuidArray = new int[4]; + uuidArray[0] = (int) (uuid.getMostSignificantBits() >> 32); + uuidArray[1] = (int) uuid.getMostSignificantBits(); + uuidArray[2] = (int) (uuid.getLeastSignificantBits() >> 32); + uuidArray[3] = (int) uuid.getLeastSignificantBits(); + map.put("UUID", LinIntArrayTag.of(uuidArray)); + + map.put("UUIDMost", LinLongTag.of(uuid.getMostSignificantBits())); + map.put("UUIDLeast", LinLongTag.of(uuid.getLeastSignificantBits())); + + map.put("WorldUUIDMost", LinLongTag.of(uuid.getMostSignificantBits())); + map.put("WorldUUIDLeast", LinLongTag.of(uuid.getLeastSignificantBits())); + + map.put("PersistentIDMSB", LinLongTag.of(uuid.getMostSignificantBits())); + map.put("PersistentIDLSB", LinLongTag.of(uuid.getLeastSignificantBits())); + } + + /** + * {@return the position data of the given tag} + * + * @param compoundTag the tag to extract position information from + * @since TODO + */ + public static Vector3 entityPosition(FaweCompoundTag compoundTag) { + LinListTag pos = compoundTag.linTag().getListTag("Pos", LinTagType.doubleTag()); + double x = pos.get(0).valueAsDouble(); + double y = pos.get(1).valueAsDouble(); + double z = pos.get(2).valueAsDouble(); + return Vector3.at(x, y, z); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/world/block/CompoundInput.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/world/block/CompoundInput.java index 4310d6523..37c63a042 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/world/block/CompoundInput.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/world/block/CompoundInput.java @@ -1,5 +1,7 @@ package com.fastasyncworldedit.core.world.block; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.ITileInput; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -11,9 +13,20 @@ public enum CompoundInput { public BaseBlock get(BlockState state, ITileInput input, int x, int y, int z) { return state.toBaseBlock(input.getTile(x, y, z)); } + + @Override + public BaseBlock get(final BlockState state, final IBlocks blocks, final int x, final int y, final int z) { + final FaweCompoundTag tile = blocks.tile(x, y, z); + return state.toBaseBlock(tile == null ? null : tile.linTag()); + } }; + @Deprecated(forRemoval = true, since = "2.11.2") public BaseBlock get(BlockState state, ITileInput input, int x, int y, int z) { return state.toBaseBlock(); } + + public BaseBlock get(BlockState state, IBlocks blocks, int x, int y, int z) { + return state.toBaseBlock(); + } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java index 05616a414..616e90b37 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.core.wrappers; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.ExtentTraverser; @@ -187,8 +188,8 @@ public class WorldWrapper extends AbstractWorld { } @Override - public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { - return parent.setTile(x, y, z, tile); + public boolean tile(int x, int y, int z, FaweCompoundTag tile) throws WorldEditException { + return parent.tile(x, y, z, tile); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTUtils.java b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTUtils.java index b742cb6f8..9b28a6d30 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTUtils.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTUtils.java @@ -172,7 +172,9 @@ public final class NBTUtils { * @param map Map to add uuid to * @param uuid {@link UUID} to add * @since 2.4.0 + * @deprecated use {@link com.fastasyncworldedit.core.util.NbtUtils#addUUIDToMap(Map, UUID)} instead */ + @Deprecated(forRemoval = true, since = "2.11.2") public static void addUUIDToMap(Map> map, UUID uuid) { int[] uuidArray = new int[4]; uuidArray[0] = (int) (uuid.getMostSignificantBits() >> 32); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 926265bfb..85fbde830 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -308,16 +308,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @return Limit remaining */ public FaweLimit getLimitUsed() { - FaweLimit newLimit = new FaweLimit(); - newLimit.MAX_ACTIONS = originalLimit.MAX_ACTIONS - limit.MAX_ACTIONS; - newLimit.MAX_CHANGES = originalLimit.MAX_CHANGES - limit.MAX_CHANGES; - newLimit.MAX_FAILS = originalLimit.MAX_FAILS - limit.MAX_FAILS; - newLimit.MAX_CHECKS = originalLimit.MAX_CHECKS - limit.MAX_CHECKS; - newLimit.MAX_ITERATIONS = originalLimit.MAX_ITERATIONS - limit.MAX_ITERATIONS; - newLimit.MAX_BLOCKSTATES = originalLimit.MAX_BLOCKSTATES - limit.MAX_BLOCKSTATES; - newLimit.MAX_ENTITIES = originalLimit.MAX_ENTITIES - limit.MAX_ENTITIES; - newLimit.MAX_HISTORY = limit.MAX_HISTORY; - return newLimit; + return originalLimit.getLimitUsed(limit); } /** @@ -472,7 +463,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { */ @Deprecated public long getBlockChangeLimit() { - return originalLimit.MAX_CHANGES; + return originalLimit.MAX_CHANGES.get(); } /** @@ -481,7 +472,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @param limit the limit (>= 0) or -1 for no limit */ public void setBlockChangeLimit(long limit) { - this.limit.MAX_CHANGES = limit; + this.limit.MAX_CHANGES.set(limit); } /** @@ -1293,8 +1284,8 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { Operations.completeBlindly(commit()); // Check fails FaweLimit used = getLimitUsed(); - if (used.MAX_FAILS > 0) { - if (used.MAX_CHANGES > 0 || used.MAX_ENTITIES > 0) { + if (used.MAX_FAILS.get() > 0) { + if (used.MAX_CHANGES.get() > 0 || used.MAX_ENTITIES.get() > 0) { actor.print(Caption.of("fawe.error.worldedit.some.fails", used.MAX_FAILS)); } else if (new ExtentTraverser<>(getExtent()).findAndGet(FaweRegionExtent.class) != null) { actor.print(Caption.of("fawe.cancel.reason.outside.region")); @@ -3611,7 +3602,8 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { } private void recurseHollow(Region region, BlockVector3 origin, Set outside, Mask mask) { - final LocalBlockVectorSet queue = new LocalBlockVectorSet(); + // FAWE start - use BlockVector3Set instead of LinkedList + final BlockVector3Set queue = BlockVector3Set.getAppropriateVectorSet(region); queue.add(origin); while (!queue.isEmpty()) { @@ -3634,6 +3626,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { } } } + // FAWE end } public int makeBiomeShape( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java index ca6f3b6a5..26650bf9c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java @@ -635,7 +635,11 @@ public final class EditSessionBuilder { }; } if (limit != null && !limit.isUnlimited()) { - this.extent = new LimitExtent(this.extent, limit, onErrorMessage); + this.extent = new LimitExtent(this.extent, limit, onErrorMessage, placeChunks && combineStages); + // Only process if we're not necessarily going to catch tiles via Extent#setBlock, e.g. because using PQE methods + if (placeChunks && combineStages) { + queue.addProcessor((LimitExtent) this.extent); + } } this.extent = wrapExtent(this.extent, eventBus, event, EditSession.Stage.BEFORE_HISTORY); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index d82bcd463..51a03119f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -1725,6 +1725,7 @@ public class LocalSession implements TextureHolder { * @return an edit session */ public EditSession createEditSession(Actor actor) { + //FAWE start return createEditSession(actor, null); } @@ -1739,7 +1740,6 @@ public class LocalSession implements TextureHolder { } // Create an edit session - //FAWE start - we don't use the edit session builder yet EditSession editSession; EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(world); if (actor.isPlayer() && actor instanceof Player) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index a60968da1..1f551aefa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -30,6 +30,7 @@ import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Preload; +import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; @@ -179,6 +180,7 @@ public class BiomeCommands { ) @Logging(REGION) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected // TODO improve using filter/chunk-based-placement @Confirm(Confirm.Processor.REGION) @CommandPermissions("worldedit.biome.set") public void setBiome( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 664e656c4..e6a211a4b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -992,15 +992,15 @@ public class BrushCommands { Expression radius, @Arg(desc = "Command to run") List input, - @Switch(name = 'p', desc = "Show any printed output") - boolean print + @Switch(name = 'h', desc = "Hide any printed output") + boolean hide ) throws WorldEditException { worldEdit.checkMaxBrushRadius( radius, context.injectedValue(Key.of(Player.class)).orElseThrow(() -> new IllegalStateException("No player")) ); String cmd = StringMan.join(input, " "); - set(context, new CommandBrush(cmd, print), "worldedit.brush.command").setSize(radius); + set(context, new CommandBrush(cmd, !hide), "worldedit.brush.command").setSize(radius); } @Command( @@ -1414,7 +1414,7 @@ public class BrushCommands { //FAWE start FaweLimit limit = Settings.settings().getLimit(player); - iterations = Math.min(limit.MAX_ITERATIONS, iterations); + iterations = Math.min(limit.MAX_ITERATIONS.get(), iterations); //FAWE end set(context, new SmoothBrush(iterations, mask), "worldedit.brush.smooth").setSize(radius); @@ -1452,7 +1452,7 @@ public class BrushCommands { //FAWE start FaweLimit limit = Settings.settings().getLimit(player); - iterations = Math.min(limit.MAX_ITERATIONS, iterations); + iterations = Math.min(limit.MAX_ITERATIONS.get(), iterations); //FAWE end set(context, new SnowSmoothBrush(iterations, snowBlockCount, mask), "worldedit.brush.snowsmooth").setSize(radius); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 00ea920a6..4446656ef 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -47,6 +47,7 @@ import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Preload; +import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; @@ -153,7 +154,7 @@ public class ClipboardCommands { ((long) max.x() - (long) min.x() + 1) * ((long) max.y() - (long) min.y() + 1) * ((long) max.z() - (long) min .z() + 1); FaweLimit limit = actor.getLimit(); - if (volume >= limit.MAX_CHECKS) { + if (volume >= limit.MAX_CHECKS.get()) { throw FaweCache.MAX_CHECKS; } session.setClipboard(null); @@ -187,7 +188,7 @@ public class ClipboardCommands { long volume = (((long) max.x() - (long) min.x() + 1) * ((long) max.y() - (long) min.y() + 1) * ((long) max.z() - (long) min .z() + 1)); FaweLimit limit = actor.getLimit(); - if (volume >= limit.MAX_CHECKS) { + if (volume >= limit.MAX_CHECKS.get()) { throw FaweCache.MAX_CHECKS; } session.setClipboard(null); @@ -260,10 +261,10 @@ public class ClipboardCommands { long volume = (((long) max.x() - (long) min.x() + 1) * ((long) max.y() - (long) min.y() + 1) * ((long) max.z() - (long) min .z() + 1)); FaweLimit limit = actor.getLimit(); - if (volume >= limit.MAX_CHECKS) { + if (volume >= limit.MAX_CHECKS.get()) { throw FaweCache.MAX_CHECKS; } - if (volume >= limit.MAX_CHANGES) { + if (volume >= limit.MAX_CHANGES.get()) { throw FaweCache.MAX_CHANGES; } session.setClipboard(null); @@ -339,6 +340,7 @@ public class ClipboardCommands { desc = "Place the clipboard's contents without applying transformations (e.g. rotate)" ) @CommandPermissions("worldedit.clipboard.place") + @SynchronousSettingExpected @Logging(PLACEMENT) public void place( Actor actor, World world, LocalSession session, final EditSession editSession, @@ -403,6 +405,7 @@ public class ClipboardCommands { desc = "Paste the clipboard's contents" ) @CommandPermissions("worldedit.clipboard.paste") + @SynchronousSettingExpected @Logging(PLACEMENT) public void paste( Actor actor, World world, LocalSession session, EditSession editSession, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 4b2f98e0c..f2a3c80ed 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -38,6 +38,7 @@ import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Preload; +import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.function.mask.Mask; @@ -104,6 +105,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) + @SynchronousSettingExpected public int hcyl( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to generate") @@ -152,6 +154,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) + @SynchronousSettingExpected public int cyl( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to generate") @@ -197,6 +200,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.cone") @Logging(PLACEMENT) + @SynchronousSettingExpected public int cone(Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to generate") Pattern pattern, @@ -243,6 +247,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.sphere") @Logging(PLACEMENT) + @SynchronousSettingExpected public int hsphere( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to generate") @@ -262,6 +267,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.sphere") @Logging(PLACEMENT) + @SynchronousSettingExpected public int sphere( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to generate") @@ -313,6 +319,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.forest") @Logging(POSITION) + @SynchronousSettingExpected public int forestGen( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The size of the forest, in blocks", def = "10") @@ -337,6 +344,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.pumpkins") @Logging(POSITION) + @SynchronousSettingExpected public int pumpkins( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The size of the patch", def = "10") @@ -357,6 +365,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.pyramid") @Logging(PLACEMENT) + @SynchronousSettingExpected public int hollowPyramid( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to set") @@ -373,6 +382,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.pyramid") @Logging(PLACEMENT) + @SynchronousSettingExpected public int pyramid( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to set") @@ -400,6 +410,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.shape") @Logging(ALL) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public int generate( Actor actor, LocalSession session, EditSession editSession, @@ -486,6 +497,7 @@ public class GenerationCommands { @CommandPermissions("worldedit.generation.shape.biome") @Logging(ALL) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public int generateBiome( Actor actor, LocalSession session, EditSession editSession, @@ -564,6 +576,7 @@ public class GenerationCommands { @CommandPermissions("worldedit.generation.caves") @Logging(PLACEMENT) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public void caves( Actor actor, LocalSession session, EditSession editSession, @Selection Region region, @@ -602,6 +615,7 @@ public class GenerationCommands { @CommandPermissions("worldedit.generation.ore") @Logging(PLACEMENT) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public void ores( Actor actor, @@ -621,6 +635,7 @@ public class GenerationCommands { desc = "Generate an image" ) @CommandPermissions("worldedit.generation.image") + @SynchronousSettingExpected @Logging(PLACEMENT) public void image( Actor actor, @@ -685,6 +700,7 @@ public class GenerationCommands { @CommandPermissions("worldedit.generation.ore") @Logging(PLACEMENT) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public void ore( Actor actor, @@ -719,8 +735,9 @@ public class GenerationCommands { desc = "Creates a distorted sphere" ) @Logging(PLACEMENT) + @SynchronousSettingExpected @CommandPermissions("worldedit.generation.blob") - public int blobBrush( + public int blob( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "Pattern") Pattern pattern, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java index d1a647587..ba736e45e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java @@ -27,6 +27,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.annotation.Confirm; +import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; @@ -61,6 +62,7 @@ public class HistoryCommands { desc = "Undoes the last action (from history)" ) @CommandPermissions({"worldedit.history.undo", "worldedit.history.undo.self"}) + @SynchronousSettingExpected public void undo( Actor actor, LocalSession session, @Confirm(Confirm.Processor.LIMIT) @Arg(desc = "Number of undoes to perform", def = "1") @@ -108,6 +110,7 @@ public class HistoryCommands { desc = "Redoes the last action (from history)" ) @CommandPermissions({"worldedit.history.redo", "worldedit.history.redo.self"}) + @SynchronousSettingExpected public void redo( Actor actor, LocalSession session, @Confirm(Confirm.Processor.LIMIT) @Arg(desc = "Number of redoes to perform", def = "1") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 6a121aa02..565aecb9a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -35,6 +35,7 @@ import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Preload; +import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.function.GroundFunction; @@ -225,6 +226,7 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.line") @Logging(REGION) + @SynchronousSettingExpected public int line( Actor actor, EditSession editSession, @Selection Region region, @@ -257,6 +259,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.curve") @Logging(REGION) @Confirm(Confirm.Processor.REGION) + @SynchronousSettingExpected public int curve( Actor actor, EditSession editSession, @Selection Region region, @@ -315,6 +318,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.overlay") @Logging(REGION) @Confirm(Confirm.Processor.REGION) + @SynchronousSettingExpected // TODO improve using filter/chunk-based-placement public int overlay( Actor actor, EditSession editSession, @Selection Region region, @Arg(desc = "The pattern of blocks to overlay") @@ -333,6 +337,7 @@ public class RegionCommands { @Logging(REGION) @Preload(Preload.PreloadCheck.PRELOAD) @Confirm(Confirm.Processor.REGION) + @SynchronousSettingExpected // TODO improve using filter/chunk-based-placement public void lay( Actor actor, EditSession editSession, @@ -369,6 +374,7 @@ public class RegionCommands { ) @Logging(REGION) @CommandPermissions("worldedit.region.center") + @SynchronousSettingExpected public int center( Actor actor, EditSession editSession, @Selection Region region, @Arg(desc = "The pattern of blocks to set") @@ -386,6 +392,8 @@ public class RegionCommands { @CommandPermissions("worldedit.region.naturalize") @Logging(REGION) @Confirm(Confirm.Processor.REGION) + @SynchronousSettingExpected // TODO improve using filter/chunk-based-placement + @Preload(Preload.PreloadCheck.PRELOAD) public int naturalize(Actor actor, EditSession editSession, @Selection Region region) throws WorldEditException { int affected = editSession.naturalizeCuboidBlocks(region); actor.print(Caption.of("worldedit.naturalize.naturalized", TextComponent.of(affected))); @@ -437,6 +445,7 @@ public class RegionCommands { @Logging(REGION) @Preload(Preload.PreloadCheck.PRELOAD) @Confirm(Confirm.Processor.REGION) + @SynchronousSettingExpected public int smooth( Actor actor, EditSession editSession, @Selection Region region, @Arg(desc = "# of iterations to perform", def = "1") @@ -452,7 +461,7 @@ public class RegionCommands { long volume = (((long) max.x() - (long) min.x() + 1) * ((long) max.y() - (long) min.y() + 1) * ((long) max.z() - (long) min .z() + 1)); FaweLimit limit = actor.getLimit(); - if (volume >= limit.MAX_CHECKS) { + if (volume >= limit.MAX_CHECKS.get()) { throw FaweCache.MAX_CHECKS; } int affected; @@ -510,6 +519,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.snowsmooth") @Logging(REGION) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public int snowSmooth( Actor actor, EditSession editSession, @Selection Region region, @@ -536,6 +546,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.move") @Logging(ORIENTATION_REGION) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public int move( Actor actor, World world, EditSession editSession, LocalSession session, @@ -599,6 +610,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.fall") @Logging(ORIENTATION_REGION) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public void fall( Actor actor, EditSession editSession, @@ -618,6 +630,7 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.stack") @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Logging(ORIENTATION_REGION) public int stack( Actor actor, World world, EditSession editSession, LocalSession session, @@ -683,6 +696,7 @@ public class RegionCommands { ) @CommandPermissions("worldedit.regen") @Logging(REGION) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) void regenerate( Actor actor, World world, LocalSession session, EditSession editSession, @@ -737,6 +751,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.deform") @Logging(ALL) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public int deform( Actor actor, LocalSession session, EditSession editSession, @@ -814,6 +829,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.hollow") @Logging(REGION) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public int hollow( Actor actor, EditSession editSession, @@ -848,6 +864,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.forest") @Logging(REGION) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public int forest( Actor actor, EditSession editSession, @Selection Region region, @@ -869,6 +886,7 @@ public class RegionCommands { @CommandPermissions("worldedit.region.flora") @Logging(REGION) @Preload(Preload.PreloadCheck.PRELOAD) + @SynchronousSettingExpected @Confirm(Confirm.Processor.REGION) public int flora( Actor actor, EditSession editSession, @Selection Region region, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index a9e2b7490..1b818786a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -69,18 +69,18 @@ import org.enginehub.piston.exception.StopExecutionException; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.Closeable; +import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; import java.io.OutputStream; +import java.net.URI; +import java.net.URL; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -91,8 +91,8 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.Callable; -import java.util.function.Consumer; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Pattern; @@ -154,11 +154,11 @@ public class SchematicCommands { public void loadall( Actor actor, LocalSession session, @Arg(desc = "Format name.", def = "fast") - String formatName, + String formatName, @Arg(desc = "File name.") - String filename, + String filename, @Switch(name = 'o', desc = "Overwrite/replace existing clipboard(s)") - boolean overwrite + boolean overwrite // @Switch(name = 'r', desc = "Apply random rotation") <- not implemented below. // boolean randomRotate ) throws FilenameException { @@ -200,7 +200,7 @@ public class SchematicCommands { public void unload( Actor actor, LocalSession session, @Arg(desc = "File name, requires extension.") - String fileName + String fileName ) throws WorldEditException { URI uri; if (fileName.startsWith("file:/") || fileName.startsWith("http://") || fileName.startsWith("https://")) { @@ -208,9 +208,7 @@ public class SchematicCommands { } else { final LocalConfiguration config = this.worldEdit.getConfiguration(); File working = this.worldEdit.getWorkingDirectoryPath(config.saveDir).toFile(); - File root = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS - ? new File(working, actor.getUniqueId().toString()) - : working; + File root = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS ? new File(working, actor.getUniqueId().toString()) : working; uri = new File(root, fileName).toURI(); } @@ -308,7 +306,6 @@ public class SchematicCommands { } //FAWE end - // From Allink @Command( name = "load", desc = "Load a schematic into your clipboard" @@ -317,8 +314,7 @@ public class SchematicCommands { public void load( Actor actor, LocalSession session, @Arg(desc = "File name.") - String filename, - + String filename, //FAWE start - use format-name, random rotation @Arg(desc = "Format name.", def = "") String formatName, @@ -330,75 +326,113 @@ public class SchematicCommands { //FAWE start ClipboardFormat format; - InputStream in = null; + InputStream in; // if format is set explicitly, do not look up by extension! boolean noExplicitFormat = formatName == null; if (noExplicitFormat) { formatName = "fast"; } - try { + try(final Closer closer = Closer.create()) { URI uri; - - File saveDir = worldEdit.getWorkingDirectoryPath(config.saveDir).toFile(); - File dir = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS - ? new File(saveDir, actor.getUniqueId().toString()) - : saveDir; - File file; - if (filename.startsWith("#")) { - format = noExplicitFormat ? null :ClipboardFormats.findByAlias(formatName); - String[] extensions; - if (format != null) { - extensions = format.getFileExtensions().toArray(new String[0]); - } else { - extensions = ClipboardFormats.getFileExtensionArray(); - } - file = actor.openFileOpenDialog(extensions); - if (file == null || !file.exists()) { - actor.print(Caption.of("worldedit.schematic.load.does-not-exist", TextComponent.of(filename))); - return; - } - } else { - if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && !actor.hasPermission("worldedit.schematic.load.other") && Pattern - .compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}") - .matcher(filename) - .find()) { - actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.other")); - return; - } - if (!noExplicitFormat) { - format = ClipboardFormats.findByAlias(formatName); - } else if ( filename.matches(".*\\.[\\w].*")) { - format = ClipboardFormats - .findByExplicitExtension(filename.substring(filename.lastIndexOf('.') + 1)); - } else { - format = null; - } - file = MainUtil.resolve(dir, filename, format, false); + if (formatName.startsWith("url:")) { + String t = filename; + filename = formatName; + formatName = t; } - if (file == null || !file.exists()) { - if (!filename.contains("../")) { - dir = this.worldEdit.getWorkingDirectoryPath(config.saveDir).toFile(); + if (filename.startsWith("url:")) { + if (!actor.hasPermission("worldedit.schematic.load.web")) { + actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.web")); + return; + } + UUID uuid = UUID.fromString(filename.substring(4)); + URL webUrl = new URL(Settings.settings().WEB.URL); + if ((format = ClipboardFormats.findByAlias(formatName)) == null) { + actor.print(Caption.of("worldedit.schematic.unknown-format", TextComponent.of(formatName))); + return; + } + // The interface requires the correct schematic extension - otherwise it can't be downloaded + // So it basically only supports .schem files (sponge v2 + v3) - or the correct extensions is specified manually + // Sadly it's not really an API endpoint but spits out the HTML source of the uploader - so no real handling + // can happen + URL url = new URL(webUrl, "uploads/" + uuid + "." + format.getPrimaryFileExtension()); + final Path temp = Files.createTempFile("faweremoteschem", null); + final File tempFile = temp.toFile(); + // delete temporary file when we're done + closer.register((Closeable) () -> Files.deleteIfExists(temp)); + // write schematic into temporary file + try (final InputStream urlIn = new BufferedInputStream(url.openStream()); + final OutputStream tempOut = new BufferedOutputStream(new FileOutputStream(tempFile))) { + urlIn.transferTo(tempOut); + } + // No format is specified -> try or fail + if (noExplicitFormat && (format = ClipboardFormats.findByFile(tempFile)) == null) { + actor.print(Caption.of("fawe.worldedit.schematic.schematic.load-failure", TextComponent.of(filename))); + return; + } + in = new FileInputStream(tempFile); + uri = temp.toUri(); + } else { + File saveDir = worldEdit.getWorkingDirectoryPath(config.saveDir).toFile(); + File dir = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS ? new File(saveDir, actor.getUniqueId().toString()) : saveDir; + File file; + if (filename.startsWith("#")) { + format = noExplicitFormat ? null : ClipboardFormats.findByAlias(formatName); + String[] extensions; + if (format != null) { + extensions = format.getFileExtensions().toArray(new String[0]); + } else { + extensions = ClipboardFormats.getFileExtensionArray(); + } + file = actor.openFileOpenDialog(extensions); + if (file == null || !file.exists()) { + actor.print(Caption.of("worldedit.schematic.load.does-not-exist", TextComponent.of(filename))); + return; + } + } else { + if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && !actor.hasPermission("worldedit.schematic.load.other") && Pattern + .compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}") + .matcher(filename) + .find()) { + actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.other")); + return; + } + if (!noExplicitFormat) { + format = ClipboardFormats.findByAlias(formatName); + } else if (filename.matches(".*\\.\\w.*")) { + format = ClipboardFormats + .findByExplicitExtension(filename.substring(filename.lastIndexOf('.') + 1)); + } else { + format = null; + } file = MainUtil.resolve(dir, filename, format, false); } - } - if (file == null || !file.exists() || !MainUtil.isInSubDirectory(saveDir, file)) { - actor.printError(TextComponent.of("Schematic " + filename + " does not exist! (" + (file != null && file.exists()) + - "|" + file + "|" + (file != null && !MainUtil - .isInSubDirectory(saveDir, file)) + ")")); - return; - } - if (format == null) { - format = ClipboardFormats.findByFile(file); - if (format == null) {if (noExplicitFormat) { - actor.print(Caption.of("fawe.worldedit.schematic.schematic.load-failure", TextComponent.of(file.getName()))); - } else { - actor.print(Caption.of("worldedit.schematic.unknown-format", TextComponent.of(formatName)));} + if (file == null || !file.exists()) { + if (!filename.contains("../")) { + dir = this.worldEdit.getWorkingDirectoryPath(config.saveDir).toFile(); + file = MainUtil.resolve(dir, filename, format, false); + } + } + if (file == null || !file.exists() || !MainUtil.isInSubDirectory(saveDir, file)) { + actor.printError(TextComponent.of("Schematic " + filename + " does not exist! (" + (file != null && file.exists()) + + "|" + file + "|" + (file != null && !MainUtil + .isInSubDirectory(saveDir, file)) + ")")); return; } + if (format == null) { + format = ClipboardFormats.findByFile(file); + if (format == null) { + if (noExplicitFormat) { + actor.print(Caption.of("fawe.worldedit.schematic.schematic.load-failure", TextComponent.of(file.getName()))); + } else { + actor.print(Caption.of("worldedit.schematic.unknown-format", TextComponent.of(formatName))); + } + return; + } + } + in = new FileInputStream(file); + uri = file.toURI(); } - - in = new FileInputStream(file); - uri = file.toURI(); + closer.register(in); format.hold(actor, uri, in); if (randomRotate) { AffineTransform transform = new AffineTransform(); @@ -409,19 +443,18 @@ public class SchematicCommands { actor.print(Caption.of("fawe.worldedit.schematic.schematic.loaded", filename)); } catch (IllegalArgumentException e) { actor.print(Caption.of("worldedit.schematic.unknown-filename", TextComponent.of(filename))); + } catch (EOFException e) { + // EOFException is extending IOException - but the IOException error is too generic. + // EOF mostly occurs when there was unexpected content in the schematic - due to the wrong reader (= version) + actor.print(Caption.of("fawe.worldedit.schematic.schematic.load-failure", + TextComponent.of(e.getMessage() != null ? e.getMessage() : "EOFException"))); // often null... + LOGGER.error("Error loading a schematic", e); } catch (IOException e) { actor.print(Caption.of("worldedit.schematic.file-not-exist", TextComponent.of(Objects.toString(e.getMessage())))); LOGGER.warn("Failed to load a saved clipboard", e); } catch (Exception e) { actor.print(Caption.of("fawe.worldedit.schematic.schematic.load-failure", TextComponent.of(e.getMessage()))); LOGGER.error("Error loading a schematic", e); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ignored) { - } - } } //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java index b98785631..074711521 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java @@ -28,6 +28,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.Logging; +import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.formatting.text.TextComponent; @@ -66,6 +67,7 @@ public class SnapshotUtilCommands { ) @Logging(REGION) @CommandPermissions("worldedit.snapshots.restore") + @SynchronousSettingExpected public void restore( Actor actor, World world, LocalSession session, EditSession editSession, @Arg(name = "snapshot", desc = "The snapshot to restore", def = "") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index c81db21d8..c7c6ab9a1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -44,6 +44,7 @@ import com.sk89q.worldedit.command.util.EntityRemover; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.PrintCommandHelp; import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder; +import com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; @@ -220,6 +221,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.fill") @Logging(PLACEMENT) + @SynchronousSettingExpected public int fill( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The blocks to fill with") @@ -311,6 +313,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.fill.recursive") @Logging(PLACEMENT) + @SynchronousSettingExpected public int fillr( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The blocks to fill with") @@ -343,6 +346,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.drain") @Logging(PLACEMENT) + @SynchronousSettingExpected public int drain( Actor actor, LocalSession session, EditSession editSession, //FAWE start - we take an expression over a double @@ -373,6 +377,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.fixlava") @Logging(PLACEMENT) + @SynchronousSettingExpected public int fixLava( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The radius to fix in") @@ -394,6 +399,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.fixwater") @Logging(PLACEMENT) + @SynchronousSettingExpected public int fixWater( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The radius to fix in") @@ -415,6 +421,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.removeabove") @Logging(PLACEMENT) + @SynchronousSettingExpected public int removeAbove( Actor actor, World world, LocalSession session, EditSession editSession, @Arg(desc = "The apothem of the square to remove from", def = "1") @@ -440,6 +447,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.removebelow") @Logging(PLACEMENT) + @SynchronousSettingExpected public int removeBelow( Actor actor, World world, LocalSession session, EditSession editSession, @Arg(desc = "The apothem of the square to remove from", def = "1") @@ -465,6 +473,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.removenear") @Logging(PLACEMENT) + @SynchronousSettingExpected public int removeNear( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The mask of blocks to remove") @@ -527,6 +536,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.snow") @Logging(PLACEMENT) + @SynchronousSettingExpected public int snow( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The radius of the cylinder to snow in", def = "10") @@ -566,6 +576,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.thaw") @Logging(PLACEMENT) + @SynchronousSettingExpected public int thaw( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The radius of the cylinder to thaw in", def = "10") @@ -595,6 +606,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.green") @Logging(PLACEMENT) + @SynchronousSettingExpected public int green( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The radius of the cylinder to convert in", def = "10") @@ -629,6 +641,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.extinguish") @Logging(PLACEMENT) + @SynchronousSettingExpected public int extinguish( Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The radius of the square to remove in", def = "") @@ -843,55 +856,6 @@ public class UtilityCommands { } } -// @Command( -// name = "/hollowr", -// desc = "Hollow out a space recursively with a pattern" -// ) -// @CommandPermissions("worldedit.hollowr") -// @Logging(PLACEMENT) -// public int hollowr( -// Actor actor, -// LocalSession session, -// EditSession editSession, -// @Arg(desc = "The radius to hollow out") Expression radiusExp, -// @ArgFlag(name = 'p', desc = "The blocks to fill with") Pattern pattern, -// @ArgFlag(name = 'm', desc = "The blocks remove", def = "") Mask mask -// ) throws WorldEditException { -// //FAWE start -// double radius = radiusExp.evaluate(); -// //FAWE end -// radius = Math.max(1, radius); -// we.checkMaxRadius(radius); -// if (mask == null) { -// Mask mask = new MaskIntersection( -// new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))), -// new BoundedHeightMask( -// Math.max(lowerBound, minY), -// Math.min(maxY, origin.getBlockY()) -// ), -// Masks.negate(new ExistingBlockMask(this)) -// ); -// } -// -// // Want to replace blocks -// BlockReplace replace = new BlockReplace(this, pattern); -// -// // Pick how we're going to visit blocks -// RecursiveVisitor visitor; -// //FAWE start - provide extent for preloading, min/max y -// if (recursive) { -// visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), minY, maxY, this); -// } else { -// visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1), minY, maxY, this); -// } -// //FAWE end -// -// BlockVector3 pos = session.getPlacementPosition(actor); -// int affected = editSession.res(pos, pattern, radius, depth, true); -// actor.print(Caption.of("worldedit.fillr.created", TextComponent.of(affected))); -// return affected; -// } - public static List> filesToEntry(final File root, final List files, final UUID uuid) { return files.stream() .map(input -> { // Keep this functional, as transform is evaluated lazily diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java index 2d35b2226..6a9a6fd95 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java @@ -89,7 +89,7 @@ public class BlockDataCyler implements DoubleActionBlockTool { Property objProp = (Property) currentProperty; BaseBlock newBlock = block.with(objProp, currentProperty.getValues().get(index)); - try (EditSession editSession = session.createEditSession(player)) { + try (EditSession editSession = session.createEditSession(player, null)) { try { editSession.setBlock(blockPoint, newBlock); player.print(Caption.of( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java index 75ef200c6..b37505d8b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java @@ -63,7 +63,7 @@ public class BlockReplacer implements DoubleActionBlockTool { ) { BlockBag bag = session.getBlockBag(player); - try (EditSession editSession = session.createEditSession(player)) { + try (EditSession editSession = session.createEditSession(player, null)) { try { BlockVector3 position = clicked.toVector().toBlockPoint(); editSession.setBlock(position, pattern); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java index b35865fb3..5cec2ec11 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java @@ -62,7 +62,7 @@ public class SinglePickaxe implements BlockTool { return false; } - try (EditSession editSession = session.createEditSession(player)) { + try (EditSession editSession = session.createEditSession(player, null)) { try { editSession.getSurvivalExtent().setToolUse(config.superPickaxeDrop); editSession.setBlock(blockPoint, BlockTypes.AIR.getDefaultState()); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/StackTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/StackTool.java index 614bbcf32..415cade93 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/StackTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/StackTool.java @@ -59,7 +59,7 @@ public class StackTool implements BlockTool { } BlockBag bag = session.getBlockBag(player); - try (EditSession editSession = session.createEditSession(player)) { + try (EditSession editSession = session.createEditSession(player, null)) { BlockStateHolder block = editSession.getFullBlock(clicked.toVector().toBlockPoint()); try { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java index 1f52ff103..db92f6af9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java @@ -60,7 +60,7 @@ public class TreePlanter implements BlockTool { @Nullable Direction face ) { - try (EditSession editSession = session.createEditSession(player)) { + try (EditSession editSession = session.createEditSession(player, null)) { try { boolean successful = false; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/Brush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/Brush.java index 8efe2ab77..e38ae0d99 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/Brush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/Brush.java @@ -40,4 +40,15 @@ public interface Brush { */ void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException; + //FAWE start + /** + * If this brush is expected to set blocks synchronously, i.e. from one thread (at a time) + * + * @since TODO + */ + default boolean setsSynchronously() { + return true; + } + //FAWE end + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/ConfirmHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/ConfirmHandler.java index 067f9cfeb..5d6b66483 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/ConfirmHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/ConfirmHandler.java @@ -12,7 +12,7 @@ import java.lang.reflect.Method; import java.util.Optional; /** - * Logs called commands to a logger. + * Handles commands indicated as requiring confirmation. */ public class ConfirmHandler implements CommandCallListener { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/PreloadHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/PreloadHandler.java index 9e1dc106f..037c94e5b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/PreloadHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/PreloadHandler.java @@ -11,7 +11,7 @@ import java.lang.reflect.Method; import java.util.Optional; /** - * Logs called commands to a logger. + * Initialises preloading of chunks. */ public class PreloadHandler implements CommandCallListener { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/SynchronousSettingExpected.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/SynchronousSettingExpected.java new file mode 100644 index 000000000..3cc936fda --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/SynchronousSettingExpected.java @@ -0,0 +1,22 @@ +package com.sk89q.worldedit.command.util.annotation; + +import org.enginehub.piston.inject.InjectAnnotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates it is expected that blocks will only be set synchronously, i.e. from one thread (at a time) + * + * @since TODO + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ + ElementType.METHOD +}) +@InjectAnnotation +public @interface SynchronousSettingExpected { + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/package-info.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/package-info.java index 006432a73..f7f293277 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/package-info.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/package-info.java @@ -8,6 +8,7 @@ * {@link com.sk89q.worldedit.command.util.annotation.PatternList}, * {@link com.sk89q.worldedit.command.util.annotation.Preload}, * {@link com.sk89q.worldedit.command.util.annotation.PreloadHandler}, + * {@link com.sk89q.worldedit.command.util.annotation.SynchronousSettingExpected}, * {@link com.sk89q.worldedit.command.util.annotation.Step}, * {@link com.sk89q.worldedit.command.util.annotation.Time} */ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 3f24fced6..d1c515af8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -24,6 +24,7 @@ import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extension.platform.binding.Bindings; import com.fastasyncworldedit.core.extension.platform.binding.ConsumeBindings; +import com.fastasyncworldedit.core.extension.platform.binding.EditSessionHolder; import com.fastasyncworldedit.core.extension.platform.binding.PrimitiveBindings; import com.fastasyncworldedit.core.extension.platform.binding.ProvideBindings; import com.fastasyncworldedit.core.internal.command.MethodInjector; @@ -152,7 +153,6 @@ import org.enginehub.piston.inject.MemoizingValueAccess; import org.enginehub.piston.inject.MergedValueAccess; import org.enginehub.piston.part.SubCommandPart; import org.enginehub.piston.suggestion.Suggestion; -import org.enginehub.piston.util.HelpGenerator; import org.enginehub.piston.util.ValueProvider; import javax.annotation.Nonnull; @@ -225,7 +225,6 @@ public final class PlatformCommandManager { new ConfirmHandler(), new PreloadHandler() //FAWE end - )); // setup separate from main constructor // ensures that everything is definitely assigned @@ -310,20 +309,6 @@ public final class PlatformCommandManager { } ); //FAWE start - /* - globalInjectedValues.injectValue(Key.of(EditSession.class), - context -> { - LocalSession localSession = context.injectedValue(Key.of(LocalSession.class)) - .orElseThrow(() -> new IllegalStateException("No LocalSession")); - return context.injectedValue(Key.of(Actor.class)) - .map(actor -> { - EditSession editSession = localSession.createEditSession(actor); - editSession.enableStandardMode(); - Request.request().setEditSession(editSession); - return editSession; - }); - }); - */ // TODO: Ping @MattBDev to reimplement 2020-02-04 // globalInjectedValues.injectValue(Key.of(CFICommands.CFISettings.class), // context -> context.injectedValue(Key.of(Actor.class)) @@ -859,10 +844,10 @@ public final class PlatformCommandManager { store.injectValue(Key.of(InjectedValueStore.class), ValueProvider.constant(store)); store.injectValue(Key.of(Event.class), ValueProvider.constant(event)); //FAWE start - allow giving editsessions - if (event instanceof CommandEvent) { - EditSession session = ((CommandEvent) event).getSession(); + if (event instanceof CommandEvent commandEvent) { + EditSession session = commandEvent.getSession(); if (session != null) { - store.injectValue(Key.of(EditSession.class), context -> Optional.of(session)); + store.injectValue(Key.of(EditSessionHolder.class), context -> Optional.of(new EditSessionHolder(session))); } } //FAWE end diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index f2d7dc884..89c450a10 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -25,6 +25,7 @@ import com.fastasyncworldedit.core.extent.HistoryExtent; import com.fastasyncworldedit.core.extent.NullExtent; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; import com.fastasyncworldedit.core.internal.exception.FaweException; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.util.ExtentTraverser; @@ -425,8 +426,8 @@ public class AbstractDelegateExtent implements Extent { } @Override - public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { - return setBlock(x, y, z, getBlock(x, y, z).toBaseBlock(tile)); + public boolean tile(int x, int y, int z, FaweCompoundTag tile) throws WorldEditException { + return setBlock(x, y, z, getBlock(x, y, z).toBaseBlock(tile == null ? null : tile.linTag())); } //FAWE end diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index 1b4420d27..9f73c8f5e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -736,7 +736,6 @@ public interface Extent extends InputExtent, OutputExtent { default > int setBlocks(Region region, B block) throws MaxChangedBlocksException { checkNotNull(region); checkNotNull(block); - boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); int changes = 0; for (BlockVector3 pos : region) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java index 0abc80153..77f9aa1c7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java @@ -24,6 +24,8 @@ import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedMask; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunk; @@ -35,6 +37,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; +import javax.annotation.Nullable; import java.util.function.LongFunction; import static com.google.common.base.Preconditions.checkNotNull; @@ -47,6 +50,7 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce //FAWE start private final LongFunction getOrCreateFilterBlock; private Mask mask; + private @Nullable VectorizedMask vectorizedMask; //FAWE end /** @@ -59,16 +63,23 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce super(extent); checkNotNull(mask); this.mask = mask; + this.vectorizedMask = SimdSupport.vectorizedTargetMask(mask); //FAWE start this.getOrCreateFilterBlock = FaweCache.INSTANCE.createMainThreadSafeCache(() -> new CharFilterBlock(getExtent())); //FAWE end } //FAWE start - private MaskingExtent(Extent extent, Mask mask, LongFunction getOrCreateFilterBlock) { + private MaskingExtent( + Extent extent, + Mask mask, + LongFunction getOrCreateFilterBlock, + @Nullable VectorizedMask vectorizedMask + ) { super(extent); checkNotNull(mask); this.mask = mask; + this.vectorizedMask = vectorizedMask; this.getOrCreateFilterBlock = getOrCreateFilterBlock; } //FAWE end @@ -90,6 +101,7 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce public void setMask(Mask mask) { checkNotNull(mask); this.mask = mask; + this.vectorizedMask = SimdSupport.vectorizedTargetMask(mask); } //FAWE start @@ -105,6 +117,10 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce @Override public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + if (this.vectorizedMask != null) { + this.vectorizedMask.processChunks(chunk, get, set); + return set; + } final ChunkFilterBlock filter = getOrCreateFilterBlock.apply(Thread.currentThread().getId()); filter.initChunk(chunk.getX(), chunk.getZ()); return filter.filter(chunk, get, set, this); @@ -122,12 +138,12 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce if (child == getExtent()) { return this; } - return new MaskingExtent(child, this.mask.copy(), this.getOrCreateFilterBlock); + return new MaskingExtent(child, this.mask.copy(), this.getOrCreateFilterBlock, this.vectorizedMask); } @Override public Filter fork() { - return new MaskingExtent(getExtent(), this.mask.copy(), this.getOrCreateFilterBlock); + return new MaskingExtent(getExtent(), this.mask.copy(), this.getOrCreateFilterBlock, this.vectorizedMask); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/NullExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/NullExtent.java index 934b52204..b98e0f4b8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/NullExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/NullExtent.java @@ -19,8 +19,8 @@ package com.sk89q.worldedit.extent; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBatchProcessor; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; @@ -108,7 +108,7 @@ public class NullExtent implements Extent { } @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; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java index 9346bf380..b38e2dc2d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.extent; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.math.MutableBlockVector3; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; @@ -68,7 +69,24 @@ public interface OutputExtent { return setBlock(MutableBlockVector3.get(x, y, z), block); } - boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException; + /** + * @deprecated use {@link #tile(int, int, int, FaweCompoundTag)} instead + */ + @Deprecated(forRemoval = true, since = "2.11.2") + default boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + return tile(x, y, z, FaweCompoundTag.of(tile.toLinTag())); + } + + /** + * Sets a tile/block entity at the given location. + * @param x the x position + * @param y the y position + * @param z the z position + * @param tile the tile/block entity to set + * @return {@code true} if the tile/block entity was placed + * @since 2.11.2 + */ + boolean tile(int x, int y, int z, FaweCompoundTag tile) throws WorldEditException; /** * Check if this extent fully supports 3D biomes. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index cf4623739..9ff666f79 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.extent.clipboard.SimpleClipboard; import com.fastasyncworldedit.core.function.visitor.Order; import com.fastasyncworldedit.core.math.MutableBlockVector2; import com.fastasyncworldedit.core.math.OffsetBlockVector3; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.google.common.collect.Iterators; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; @@ -200,16 +201,17 @@ public class BlockArrayClipboard implements Clipboard { //FAWE start @Override - public boolean setTile(int x, int y, int z, CompoundTag tag) { + public boolean tile(int x, int y, int z, FaweCompoundTag tag) { x -= offset.x(); y -= offset.y(); z -= offset.z(); - return getParent().setTile(x, y, z, tag); + return getParent().tile(x, y, z, tag); } + @Deprecated(forRemoval = true, since = "2.11.2") public boolean setTile(BlockVector3 position, CompoundTag tag) { - return setTile(position.x(), position.y(), position.z(), tag); + return tile(position.x(), position.y(), position.z(), FaweCompoundTag.of(tag.toLinTag())); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java index 945cd6981..8e54249cd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java @@ -57,6 +57,7 @@ import java.io.UncheckedIOException; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.NoSuchElementException; import java.util.Set; /** @@ -126,18 +127,25 @@ public class MCEditSchematicReader extends NBTSchematicReader { short height = schematicTag.getTag("Height", LinTagType.shortTag()).valueAsShort(); short length = schematicTag.getTag("Length", LinTagType.shortTag()).valueAsShort(); - int originX = schematicTag.getTag("WEOriginX", LinTagType.intTag()).valueAsInt(); - int originY = schematicTag.getTag("WEOriginY", LinTagType.intTag()).valueAsInt(); - int originZ = schematicTag.getTag("WEOriginZ", LinTagType.intTag()).valueAsInt(); - BlockVector3 min = BlockVector3.at(originX, originY, originZ); + try { + int originX = schematicTag.getTag("WEOriginX", LinTagType.intTag()).valueAsInt(); + int originY = schematicTag.getTag("WEOriginY", LinTagType.intTag()).valueAsInt(); + int originZ = schematicTag.getTag("WEOriginZ", LinTagType.intTag()).valueAsInt(); + BlockVector3 min = BlockVector3.at(originX, originY, originZ); + + int offsetX = schematicTag.getTag("WEOffsetX", LinTagType.intTag()).valueAsInt(); + int offsetY = schematicTag.getTag("WEOffsetY", LinTagType.intTag()).valueAsInt(); + int offsetZ = schematicTag.getTag("WEOffsetZ", LinTagType.intTag()).valueAsInt(); + BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); + + origin = min.subtract(offset); + region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); + } catch (NoSuchElementException e) { + origin = BlockVector3.ZERO; + region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE)); + } - int offsetX = schematicTag.getTag("WEOffsetX", LinTagType.intTag()).valueAsInt(); - int offsetY = schematicTag.getTag("WEOffsetY", LinTagType.intTag()).valueAsInt(); - int offsetZ = schematicTag.getTag("WEOffsetZ", LinTagType.intTag()).valueAsInt(); - BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); - origin = min.subtract(offset); - region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); // ==================================================================== // Blocks diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Reader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Reader.java index 978cb8ba5..15032be82 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Reader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Reader.java @@ -135,7 +135,7 @@ public class SpongeSchematicV3Reader implements ClipboardReader { Map palette = ReaderUtil.decodePalette(paletteObject, fixer); byte[] blocks = blockContainer.getTag("Data", LinTagType.byteArrayTag()).value(); - LinListTag blockEntities = blockContainer.getListTag("BlockEntities", LinTagType.compoundTag()); + LinListTag blockEntities = blockContainer.findListTag("BlockEntities", LinTagType.compoundTag()); ReaderUtil.initializeClipboardFromBlocks(clipboard, palette, blocks, blockEntities, fixer, true); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask.java index cd3ef56d5..ab919b35f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask.java @@ -94,7 +94,7 @@ public class BiomeMask extends AbstractExtentMask { @Override public boolean test(BlockVector3 vector) { - BiomeType biome = getExtent().getBiome(vector); + BiomeType biome = vector.getBiome(getExtent()); return biomes.contains(biome); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockCategoryMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockCategoryMask.java index bad9781ea..a1b869efb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockCategoryMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockCategoryMask.java @@ -46,7 +46,7 @@ public class BlockCategoryMask extends AbstractExtentMask { @Override public boolean test(BlockVector3 vector) { - return category.contains(getExtent().getBlock(vector)); + return category.contains(vector.getBlock(getExtent())); } //FAWE start diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java index 9f191a4cc..c7d3473f3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java @@ -204,7 +204,7 @@ public class BlockMask extends ABlockMask { @Override public boolean test(BlockVector3 vector) { - int test = getExtent().getBlock(vector).getOrdinal(); + int test = vector.getBlock(getExtent()).getOrdinal(); return ordinals[test] || replacesAir() && test == 0; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java index ac558520c..0c563cdf7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java @@ -56,7 +56,7 @@ public class BlockStateMask extends AbstractExtentMask { //FAWE start @Override public boolean test(BlockVector3 vector) { - return test(getExtent().getBlock(vector)); + return test(vector.getBlock(getExtent())); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java index 17f1419e5..c3567b6d5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java @@ -124,7 +124,7 @@ public class BlockTypeMask extends AbstractExtentMask { //FAWE start @Override public boolean test(BlockVector3 vector) { - return test(getExtent().getBlock(vector).getBlockType()); + return test(vector.getBlock(getExtent()).getBlockType()); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java index b75a4cd1e..be62c2eae 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java @@ -41,7 +41,7 @@ public class ExistingBlockMask extends AbstractExtentMask { @Override public boolean test(BlockVector3 vector) { - return !getExtent().getBlock(vector).getBlockType().getMaterial().isAir(); + return !vector.getBlock(getExtent()).getBlockType().getMaterial().isAir(); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java index e8b14b95a..6bb39b958 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java @@ -29,7 +29,7 @@ public class InverseSingleBlockStateMask extends ABlockMask { @Override public boolean test(BlockVector3 vector) { - int test = getExtent().getBlock(vector).getOrdinal(); + int test = vector.getBlock(getExtent()).getOrdinal(); if (isAir && test == 0) { return false; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java index 94141278d..85e85a8c2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java @@ -22,7 +22,11 @@ package com.sk89q.worldedit.function.mask; import com.fastasyncworldedit.core.extent.filter.MaskFilter; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.fastasyncworldedit.core.function.mask.InverseMask; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; +import com.fastasyncworldedit.core.internal.simd.VectorizedMask; import com.fastasyncworldedit.core.queue.Filter; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import javax.annotation.Nullable; @@ -64,7 +68,21 @@ public interface Mask { return null; } + @SuppressWarnings({"unchecked", "rawtypes"}) default MaskFilter toFilter(T filter) { + final VectorizedMask mask = SimdSupport.vectorizedTargetMask(this); + if (mask != null) { + VectorizedFilter vectorizedFilter = null; + if (filter instanceof VectorizedFilter vf) { + vectorizedFilter = vf; + } else if (filter instanceof Pattern p) { + vectorizedFilter = SimdSupport.vectorizedPattern(p); + } + if (vectorizedFilter != null) { + // also pass original? + return new MaskFilter.VectorizedMaskFilter(vectorizedFilter, this); + } + } return new MaskFilter<>(filter, this); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ChangeSetExecutor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ChangeSetExecutor.java index 12359fdee..ea52bf335 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ChangeSetExecutor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ChangeSetExecutor.java @@ -19,7 +19,9 @@ package com.sk89q.worldedit.function.operation; +import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; +import com.fastasyncworldedit.core.history.changeset.ChangeExchangeCoordinator; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.history.UndoContext; @@ -56,6 +58,7 @@ public class ChangeSetExecutor implements Operation { //FAWE end private final Iterator iterator; + private final ChangeExchangeCoordinator changeExchangeCoordinator; private final Type type; private final UndoContext context; @@ -74,18 +77,42 @@ public class ChangeSetExecutor implements Operation { this.type = type; this.context = context; - if (changeSet instanceof AbstractChangeSet) { - iterator = ((AbstractChangeSet) changeSet).getIterator(blockBag, inventory, type == Type.REDO); + if (changeSet instanceof AbstractChangeSet abstractChangeSet) { + if (Settings.settings().EXPERIMENTAL.UNDO_BATCH_SIZE > 0) { + this.changeExchangeCoordinator = abstractChangeSet.getCoordinatedChanges(blockBag, inventory, type == Type.REDO); + this.iterator = null; + } else { + this.iterator = abstractChangeSet.getIterator(blockBag, inventory, type == Type.REDO); + this.changeExchangeCoordinator = null; + } } else if (type == Type.UNDO) { iterator = changeSet.backwardIterator(); + this.changeExchangeCoordinator = null; } else { iterator = changeSet.forwardIterator(); + this.changeExchangeCoordinator = null; } } //FAWE end @Override public Operation resume(RunContext run) throws WorldEditException { + // FAWE start - ChangeExchangeCoordinator + if (this.changeExchangeCoordinator != null) { + try (this.changeExchangeCoordinator) { + Change[] changes = new Change[Settings.settings().EXPERIMENTAL.UNDO_BATCH_SIZE]; + while ((changes = this.changeExchangeCoordinator.take(changes)) != null) { + for (final Change change : changes) { + if (change == null) { + return null; // end + } + type.perform(change, context); + } + } + return null; + } + } + // FAWE end while (iterator.hasNext()) { Change change = iterator.next(); //FAWE start - types > individual history step diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java index ee2789a33..017ec7ca0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java @@ -852,6 +852,7 @@ public abstract class BlockVector3 { return orDefault.getBiome(this); } + @Deprecated(forRemoval = true, since = "2.11.2") public CompoundTag getNbtData(Extent orDefault) { return orDefault.getFullBlock(x(), y(), z()).getNbtData(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index 085274772..c236992cf 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -269,7 +269,8 @@ public interface Region extends Iterable, Cloneable, IBatchProcess int minSection = Math.max(get.getMinSectionPosition(), getMinimumY() >> 4); int maxSection = Math.min(get.getMaxSectionPosition(), getMaximumY() >> 4); for (int layer = minSection; layer <= maxSection; layer++) { - if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + //if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + if (!full && !get.hasSection(layer)) { return; } block = block.initLayer(get, set, layer); @@ -319,7 +320,8 @@ public interface Region extends Iterable, Cloneable, IBatchProcess int layer, boolean full ) { - if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + //if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + if (!full && !get.hasSection(layer)) { return; } block = block.initLayer(get, set, layer); @@ -341,7 +343,8 @@ public interface Region extends Iterable, Cloneable, IBatchProcess int maxZ, boolean full ) { - if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + //if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + if (!full && !get.hasSection(layer)) { return; } block = block.initLayer(get, set, layer); @@ -359,7 +362,8 @@ public interface Region extends Iterable, Cloneable, IBatchProcess int yEnd, boolean full ) { - if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + //if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + if (!full && !get.hasSection(layer)) { return; } block = block.initLayer(get, set, layer); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java index 2af8232af..5aedae6d9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.session.request; -import com.sk89q.jnbt.CompoundTag; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; @@ -119,8 +119,8 @@ public class RequestExtent implements Extent { //FAWE start @Override - public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { - return getExtent().setTile(x, y, z, tile); + public boolean tile(int x, int y, int z, FaweCompoundTag tile) throws WorldEditException { + return getExtent().tile(x, y, z, tile); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java index 0e962d746..d3ad7bfd9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java @@ -19,11 +19,11 @@ package com.sk89q.worldedit.world; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.blocks.NullChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.google.common.collect.ImmutableSet; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; @@ -204,7 +204,7 @@ public class NullWorld extends AbstractWorld { //FAWE start @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; } //FAWE end diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java index ab93a4016..ea54b1c7a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.world.block; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.registry.state.PropertyKey; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; @@ -262,15 +263,15 @@ public class BaseBlock implements BlockStateHolder, TileEntityBlock { @Override public void applyTileEntity(OutputExtent output, int x, int y, int z) { - CompoundTag nbt = getNbtData(); + LinCompoundTag nbt = getNbt(); if (nbt != null) { - output.setTile(x, y, z, nbt); + output.tile(x, y, z, FaweCompoundTag.of(nbt)); } } @Override public BaseBlock withPropertyId(int propertyId) { - return getBlockType().withPropertyId(propertyId).toBaseBlock(getNbtData()); + return getBlockType().withPropertyId(propertyId).toBaseBlock(getNbtReference()); } @Override @@ -285,7 +286,7 @@ public class BaseBlock implements BlockStateHolder, TileEntityBlock { @Override public BaseBlock with(PropertyKey property, V value) { - return toImmutableState().with(property, value).toBaseBlock(getNbtData()); + return toImmutableState().with(property, value).toBaseBlock(getNbtReference()); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java index caa8ba919..824ad2b85 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.world.block; import com.fastasyncworldedit.core.command.SuggestInputParseException; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.function.mask.SingleBlockStateMask; +import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.ITileInput; import com.fastasyncworldedit.core.registry.state.PropertyKey; import com.fastasyncworldedit.core.util.MutableCharSequence; @@ -476,6 +477,12 @@ public class BlockState implements BlockStateHolder, Pattern { public BaseBlock toBaseBlock(ITileInput input, int x, int y, int z) { return compoundInput.get(this, input, x, y, z); } + + @Override + public BaseBlock toBaseBlock(final IBlocks blocks, final int x, final int y, final int z) { + return compoundInput.get(this, blocks, x, y, z); + } + //FAWE end @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java index f2ea08b67..808f8f23f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.world.block; +import com.fastasyncworldedit.core.queue.IBlocks; import com.fastasyncworldedit.core.queue.ITileInput; import com.fastasyncworldedit.core.registry.state.PropertyKey; import com.sk89q.jnbt.CompoundTag; @@ -201,6 +202,10 @@ public interface BlockStateHolder> extends TileEnt default BaseBlock toBaseBlock(ITileInput input, int x, int y, int z) { throw new UnsupportedOperationException("State is immutable"); } + + default BaseBlock toBaseBlock(IBlocks blocks, int x, int y, int z) { + throw new UnsupportedOperationException("State is immutable"); + } //FAWE end default String getAsString() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java index c8baea757..243acb171 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockMaterial.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.world.registry; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; import javax.annotation.Nullable; @@ -185,7 +186,19 @@ public interface BlockMaterial { * @return default tile entity data */ @Nullable - CompoundTag getDefaultTile(); + default CompoundTag getDefaultTile() { + final FaweCompoundTag faweCompoundTag = defaultTile(); + if (faweCompoundTag == null) { + return null; + } + return new CompoundTag(faweCompoundTag.linTag()); + } + + /** + * {@return the default tile associated with this material, if any} + * @since 2.11.2 + */ + @Nullable FaweCompoundTag defaultTile(); /** * Get the map color. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java index 95743b80a..28b09a30e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.world.registry; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; import javax.annotation.Nullable; @@ -168,5 +169,11 @@ public class PassthroughBlockMaterial implements BlockMaterial { public CompoundTag getDefaultTile() { return blockMaterial.getDefaultTile(); } + + @Override + public @Nullable FaweCompoundTag defaultTile() { + return blockMaterial.defaultTile(); + } + //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java index 763ac81bc..737510816 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java @@ -19,8 +19,11 @@ package com.sk89q.worldedit.world.registry; +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.sk89q.jnbt.CompoundTag; +import javax.annotation.Nullable; + class SimpleBlockMaterial implements BlockMaterial { private boolean isAir; @@ -251,8 +254,12 @@ class SimpleBlockMaterial implements BlockMaterial { } @Override - public CompoundTag getDefaultTile() { - return tile; + public @Nullable FaweCompoundTag defaultTile() { + // this implementation is very lazy, but SimpleBlockMaterial isn't really used anyway + if (tile != null) { + return FaweCompoundTag.of(tile.toLinTag()); + } + return null; } //FAWE end diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index a56d98695..fea3e7dd5 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -141,6 +141,7 @@ "fawe.error.limit.max-brush-radius": "Maximum brush radius in limit: {0}", "fawe.error.limit.max-radius": "Maximum radius in limit: {0}", "fawe.error.no-valid-on-hotbar": "No valid block types on hotbar", + "fawe.error.no-process-non-synchronous-edit": "No processor holder was found but edit is non-synchronous", "fawe.cancel.count": "Cancelled {0} edits.", "fawe.cancel.reason.confirm": "Use //confirm to execute {0}", "fawe.cancel.reason.confirm.region": "Your selection is large ({0} -> {1}, containing {3} blocks). Use //confirm to execute {2}", @@ -151,6 +152,7 @@ "fawe.cancel.reason.low.memory": "Low memory", "fawe.cancel.reason.max.changes": "Too many blocks changed", "fawe.cancel.reason.max.checks": "Too many block checks", + "fawe.cancel.reason.max.fails": "Too many fails", "fawe.cancel.reason.max.tiles": "Too many block entities", "fawe.cancel.reason.max.entities": "Too many entities", "fawe.cancel.reason.max.iterations": "Max iterations", diff --git a/worldedit-core/src/test/java/com/fastasyncworldedit/core/queue/IBatchProcessorTest.java b/worldedit-core/src/test/java/com/fastasyncworldedit/core/queue/IBatchProcessorTest.java new file mode 100644 index 000000000..e28d0d236 --- /dev/null +++ b/worldedit-core/src/test/java/com/fastasyncworldedit/core/queue/IBatchProcessorTest.java @@ -0,0 +1,136 @@ +package com.fastasyncworldedit.core.queue; + +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.parallel.Isolated; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class IBatchProcessorTest { + + @Nested + @Isolated + class trimY { + + private static final char[] CHUNK_DATA = new char[16 * 16 * 16]; + private static final char[] SLICE_AIR = new char[16 * 16]; + private static final char[] SLICE_RESERVED = new char[16 * 16]; + private final IBatchProcessor processor = new NoopBatchProcessor(); + + static { + Arrays.fill(CHUNK_DATA, (char) BlockTypesCache.ReservedIDs.AIR); + Arrays.fill(SLICE_AIR, (char) BlockTypesCache.ReservedIDs.AIR); + Arrays.fill(SLICE_RESERVED, (char) BlockTypesCache.ReservedIDs.__RESERVED__); + } + + @ParameterizedTest + @MethodSource("provideTrimYInBoundsParameters") + void testFullChunkSelectedInBoundedRegion(int minY, int maxY, int minSection, int maxSection) { + final IChunkSet set = mock(); + + char[][] sections = new char[(320 + 64) >> 4][CHUNK_DATA.length]; + for (final char[] chars : sections) { + System.arraycopy(CHUNK_DATA, 0, chars, 0, CHUNK_DATA.length); + } + + when(set.getMinSectionPosition()).thenReturn(-64 >> 4); + when(set.getMaxSectionPosition()).thenReturn(319 >> 4); + when(set.hasSection(anyInt())).thenReturn(true); + when(set.loadIfPresent(anyInt())).thenAnswer(invocationOnMock -> sections[invocationOnMock.getArgument(0) + 4]); + doAnswer(invocationOnMock -> { + sections[invocationOnMock.getArgument(0) + 4] = invocationOnMock.getArgument(1); + return null; + }).when(set).setBlocks(anyInt(), any()); + + processor.trimY(set, minY, maxY, true); + + + for (int section = -64 >> 4; section < 320 >> 4; section++) { + int sectionIndex = section + 4; + char[] palette = sections[sectionIndex]; + if (section < minSection) { + assertNull(palette, "expected section below minimum section to be null"); + continue; + } + if (section > maxSection) { + assertNull(palette, "expected section above maximum section to be null"); + continue; + } + if (section == minSection) { + for (int slice = 0; slice < 16; slice++) { + boolean shouldContainBlocks = slice >= (minY % 16); + // If boundaries only span one section, the upper constraints have to be checked explicitly + if (section == maxSection) { + shouldContainBlocks &= slice <= (maxY % 16); + } + assertArrayEquals( + shouldContainBlocks ? SLICE_AIR : SLICE_RESERVED, + Arrays.copyOfRange(palette, slice << 8, (slice + 1) << 8), + ("[lower] slice %d (y=%d) expected to contain " + (shouldContainBlocks ? "air" : "nothing")) + .formatted(slice, ((section << 4) + slice)) + ); + } + continue; + } + if (section == maxSection) { + for (int slice = 0; slice < 16; slice++) { + boolean shouldContainBlocks = slice <= (maxY % 16); + assertArrayEquals( + shouldContainBlocks ? SLICE_AIR : SLICE_RESERVED, + Arrays.copyOfRange(palette, slice << 8, (slice + 1) << 8), + ("[upper] slice %d (y=%d) expected to contain " + (shouldContainBlocks ? "air" : "nothing")) + .formatted(slice, ((section << 4) + slice)) + ); + } + continue; + } + assertArrayEquals(CHUNK_DATA, palette, "full captured chunk @ %d should contain full data".formatted(section)); + } + + } + + /** + * Arguments explained: + * 1. minimum y coordinate (inclusive) + * 2. maximum y coordinate (inclusive) + * 3. chunk section which contains minimum y coordinate + * 4. chunk section which contains maximum y coordinate + */ + private static Stream provideTrimYInBoundsParameters() { + return Stream.of( + Arguments.of(64, 72, 4, 4), + Arguments.of(-64, 0, -4, 0), + Arguments.of(0, 128, 0, 8), + Arguments.of(16, 132, 1, 8), + Arguments.of(4, 144, 0, 9), + Arguments.of(12, 255, 0, 15), + Arguments.of(24, 103, 1, 6) + ); + } + + } + + private static final class NoopBatchProcessor implements IBatchProcessor { + + @Override + public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + return set; + } + + @Override + public @Nullable Extent construct(final Extent child) { + return null; + } + + } + +} diff --git a/worldedit-sponge/build.gradle.kts b/worldedit-sponge/build.gradle.kts index 1c3ad6b68..fedd48c00 100644 --- a/worldedit-sponge/build.gradle.kts +++ b/worldedit-sponge/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { }) api("org.apache.logging.log4j:log4j-api") api("org.bstats:bstats-sponge:1.7") - testImplementation("org.mockito:mockito-core:5.12.0") + testImplementation("org.mockito:mockito-core:5.14.1") } <<<<<<< HEAD