mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-11-01 02:17:11 +00:00
This commit is contained in:
commit
778517f1ea
@ -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())
|
||||
|
||||
|
@ -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") {
|
||||
|
@ -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<Javadoc>().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}"
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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"
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
|
||||
|
7
gradlew
vendored
7
gradlew
vendored
@ -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
|
||||
|
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@ -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 ##########################################################################
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<net.minecraft.nbt.
|
||||
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2.PaperweightAdapter();
|
||||
}
|
||||
|
||||
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
|
||||
return blockEntity -> FaweCompoundTag.of(
|
||||
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId())
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getEntityId(Entity entity) {
|
||||
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
|
||||
|
@ -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<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
|
||||
private static final Function<BlockEntity, CompoundTag> nmsTile2We =
|
||||
tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
||||
public static final Function<BlockEntity, FaweCompoundTag> 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<BlockVector3, CompoundTag> getTiles() {
|
||||
public Map<BlockVector3, FaweCompoundTag> tiles() {
|
||||
Map<BlockPos, BlockEntity> 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<Entity> 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<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> entities() {
|
||||
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
List<Entity> 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<CompoundTag> iterator() {
|
||||
Iterable<CompoundTag> result = entities.stream().map(input -> {
|
||||
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
|
||||
public Iterator<FaweCompoundTag> iterator() {
|
||||
Iterable<FaweCompoundTag> 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<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||
List<BlockEntity> 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<Holder<Biome>>) 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<CompoundTag> entities = set.getEntities();
|
||||
Collection<FaweCompoundTag> entities = set.entities();
|
||||
if (entities != null && !entities.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[2];
|
||||
}
|
||||
|
||||
syncTasks[1] = () -> {
|
||||
Iterator<CompoundTag> iterator = entities.iterator();
|
||||
Iterator<FaweCompoundTag> iterator = entities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final CompoundTag nativeTag = iterator.next();
|
||||
final Map<String, Tag<?, ?>> entityTagMap = nativeTag.getValue();
|
||||
final StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
final ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
final FaweCompoundTag nativeTag = iterator.next();
|
||||
final LinCompoundTag linTag = nativeTag.linTag();
|
||||
final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
|
||||
final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
|
||||
final LinListTag<LinFloatTag> 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<BlockVector3, CompoundTag> tiles = set.getTiles();
|
||||
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
|
||||
if (tiles != null && !tiles.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[1];
|
||||
}
|
||||
|
||||
syncTasks[0] = () -> {
|
||||
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
|
||||
final CompoundTag nativeTag = entry.getValue();
|
||||
for (final Map.Entry<BlockVector3, FaweCompoundTag> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
|
||||
private final Set<FaweCompoundTag> 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<Tag> 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<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
@SuppressWarnings("unchecked")
|
||||
BukkitImplAdapter<Tag> 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<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> 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<Holder<Biome>> 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<Biome>) 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<BlockVector3, FaweCompoundTag> tiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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<ServerLeve
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z);
|
||||
PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
|
@ -4,167 +4,74 @@ import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.util.ProgressListener;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
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<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
/*chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results*/
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
// chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "e"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v"));
|
||||
generatorStructureStateField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
|
||||
Refraction.pickName("hasGeneratedPositions", "h")
|
||||
);
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -172,43 +79,37 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
public PaperweightRegen(
|
||||
World originalBukkitWorld,
|
||||
Region region,
|
||||
Extent target,
|
||||
RegenOptions options
|
||||
) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
|
||||
while (shouldKeepTicking.getAsBoolean()) {
|
||||
if (!this.freshWorld.getChunkSource().pollTask()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
@ -254,8 +155,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
|
||||
.getOrThrow(levelStemResourceKey),
|
||||
new LevelStem(
|
||||
originalServerWorld.dimensionTypeRegistration(),
|
||||
originalServerWorld.getChunkSource().getGenerator()
|
||||
),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
@ -273,17 +176,30 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
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<ChunkAccess, ProtoChunk, Level
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
|
||||
originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
|
||||
biomeSource = new FixedBiomeSource(
|
||||
DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
|
||||
origPositions);
|
||||
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
|
||||
ringPositionsField.set(newState, copy);
|
||||
hasGeneratedPositionsField.setBoolean(newState, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -389,7 +222,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
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<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(BIOME), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> {
|
||||
final CraftWorld world = freshWorld.getWorld();
|
||||
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
|
||||
blockPopulator.populate(world, random, chunk);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
@ -483,11 +274,15 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
public void updateSpawnPos(@NotNull ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
public void onStatusChange(
|
||||
final @NotNull ChunkPos pos,
|
||||
@org.jetbrains.annotations.Nullable final ChunkStatus status
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -505,87 +300,4 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureTemplateManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {
|
||||
}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {
|
||||
});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<net.minecraft.nbt.
|
||||
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter();
|
||||
}
|
||||
|
||||
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
|
||||
return blockEntity -> FaweCompoundTag.of(
|
||||
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId())
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getEntityId(Entity entity) {
|
||||
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
|
||||
|
@ -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<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
|
||||
private static final Function<BlockEntity, CompoundTag> nmsTile2We =
|
||||
tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
||||
public static final Function<BlockEntity, FaweCompoundTag> 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<Biome> biomeRegistry;
|
||||
private final IdMap<Holder<Biome>> biomeHolderIdMap;
|
||||
private final ConcurrentHashMap<Integer, PaperweightGetBlocks_Copy> 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<BlockVector3, CompoundTag> getTiles() {
|
||||
public Map<BlockVector3, FaweCompoundTag> tiles() {
|
||||
Map<BlockPos, BlockEntity> 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<Entity> 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<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> entities() {
|
||||
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
List<Entity> 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<CompoundTag> iterator() {
|
||||
Iterable<CompoundTag> result = entities.stream().map(input -> {
|
||||
public Iterator<FaweCompoundTag> iterator() {
|
||||
Iterable<FaweCompoundTag> 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<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||
List<BlockEntity> 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<Holder<Biome>>) 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<CompoundTag> entities = set.getEntities();
|
||||
Collection<FaweCompoundTag> entities = set.entities();
|
||||
if (entities != null && !entities.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[2];
|
||||
}
|
||||
|
||||
syncTasks[1] = () -> {
|
||||
Iterator<CompoundTag> iterator = entities.iterator();
|
||||
Iterator<FaweCompoundTag> iterator = entities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final CompoundTag nativeTag = iterator.next();
|
||||
final Map<String, Tag<?, ?>> entityTagMap = nativeTag.getValue();
|
||||
final StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
final ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
final FaweCompoundTag nativeTag = iterator.next();
|
||||
final LinCompoundTag linTag = nativeTag.linTag();
|
||||
final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
|
||||
final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
|
||||
final LinListTag<LinFloatTag> 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<BlockVector3, CompoundTag> tiles = set.getTiles();
|
||||
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
|
||||
if (tiles != null && !tiles.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[1];
|
||||
}
|
||||
|
||||
syncTasks[0] = () -> {
|
||||
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
|
||||
final CompoundTag nativeTag = entry.getValue();
|
||||
for (final Map.Entry<BlockVector3, FaweCompoundTag> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
|
||||
private final Set<FaweCompoundTag> 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<Tag> 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<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
@SuppressWarnings("unchecked")
|
||||
BukkitImplAdapter<Tag> 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<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> 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<Holder<Biome>> 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<Biome>) 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<BlockVector3, FaweCompoundTag> tiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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<ServerLeve
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z);
|
||||
PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
|
@ -4,166 +4,74 @@ import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.util.ProgressListener;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
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<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
/*chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results*/
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
// chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "e"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v"));
|
||||
generatorStructureStateField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
|
||||
Refraction.pickName("hasGeneratedPositions", "h")
|
||||
);
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -171,43 +79,37 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
public PaperweightRegen(
|
||||
World originalBukkitWorld,
|
||||
Region region,
|
||||
Extent target,
|
||||
RegenOptions options
|
||||
) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
|
||||
while (shouldKeepTicking.getAsBoolean()) {
|
||||
if (!this.freshWorld.getChunkSource().pollTask()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
@ -253,8 +155,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
|
||||
.getOrThrow(levelStemResourceKey),
|
||||
new LevelStem(
|
||||
originalServerWorld.dimensionTypeRegistration(),
|
||||
originalServerWorld.getChunkSource().getGenerator()
|
||||
),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
@ -272,17 +176,30 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
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<ChunkAccess, ProtoChunk, Level
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
|
||||
originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
|
||||
biomeSource = new FixedBiomeSource(
|
||||
DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
|
||||
origPositions);
|
||||
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
|
||||
ringPositionsField.set(newState, copy);
|
||||
hasGeneratedPositionsField.setBoolean(newState, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -388,7 +222,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
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<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(BIOME), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> {
|
||||
final CraftWorld world = freshWorld.getWorld();
|
||||
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
|
||||
blockPopulator.populate(world, random, chunk);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
@ -482,11 +274,15 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
public void updateSpawnPos(@NotNull ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
public void onStatusChange(
|
||||
final @NotNull ChunkPos pos,
|
||||
@org.jetbrains.annotations.Nullable final ChunkStatus status
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -504,87 +300,4 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureTemplateManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {
|
||||
}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {
|
||||
});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.6-R0.1-20240702.153951-123")
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.6-R0.1-20240916.192025-125")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<net.minecraft.nbt.
|
||||
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R4.PaperweightAdapter();
|
||||
}
|
||||
|
||||
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
|
||||
return blockEntity -> FaweCompoundTag.of(
|
||||
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getEntityId(Entity entity) {
|
||||
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
|
||||
|
@ -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<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
|
||||
private static final Function<BlockEntity, CompoundTag> nmsTile2We = tileEntity -> new PaperweightLazyCompoundTag(
|
||||
Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess()))
|
||||
);
|
||||
public static final Function<BlockEntity, FaweCompoundTag> 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<BlockVector3, CompoundTag> getTiles() {
|
||||
public Map<BlockVector3, FaweCompoundTag> tiles() {
|
||||
Map<BlockPos, BlockEntity> 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<Entity> 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<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> entities() {
|
||||
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
List<Entity> 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<CompoundTag> iterator() {
|
||||
Iterable<CompoundTag> result = entities.stream().map(input -> {
|
||||
public Iterator<FaweCompoundTag> iterator() {
|
||||
Iterable<FaweCompoundTag> 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<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||
List<BlockEntity> 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<Holder<Biome>>) 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<CompoundTag> entities = set.getEntities();
|
||||
Collection<FaweCompoundTag> entities = set.entities();
|
||||
if (entities != null && !entities.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[2];
|
||||
}
|
||||
|
||||
syncTasks[1] = () -> {
|
||||
Iterator<CompoundTag> iterator = entities.iterator();
|
||||
Iterator<FaweCompoundTag> iterator = entities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final CompoundTag nativeTag = iterator.next();
|
||||
final Map<String, Tag<?, ?>> entityTagMap = nativeTag.getValue();
|
||||
final StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
final ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
final FaweCompoundTag nativeTag = iterator.next();
|
||||
final LinCompoundTag linTag = nativeTag.linTag();
|
||||
final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
|
||||
final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
|
||||
final LinListTag<LinFloatTag> 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<BlockVector3, CompoundTag> tiles = set.getTiles();
|
||||
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
|
||||
if (tiles != null && !tiles.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[1];
|
||||
}
|
||||
|
||||
syncTasks[0] = () -> {
|
||||
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
|
||||
final CompoundTag nativeTag = entry.getValue();
|
||||
for (final Map.Entry<BlockVector3, FaweCompoundTag> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
|
||||
private final Set<FaweCompoundTag> 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<Tag> 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<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
@SuppressWarnings("unchecked")
|
||||
BukkitImplAdapter<Tag> 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<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> 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<Holder<Biome>> 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<Biome>) 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<BlockVector3, FaweCompoundTag> tiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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<ServerLeve
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z);
|
||||
PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
|
@ -4,166 +4,73 @@ import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.util.ProgressListener;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
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<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.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<ChunkAccess, ProtoChunk, Level
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
private WorldGenContext worldGenContext;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
public PaperweightRegen(
|
||||
World originalBukkitWorld,
|
||||
Region region,
|
||||
Extent target,
|
||||
RegenOptions options
|
||||
) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
|
||||
while (shouldKeepTicking.getAsBoolean()) {
|
||||
if (!this.freshWorld.getChunkSource().pollTask()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
@ -254,8 +154,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
|
||||
.getOrThrow(levelStemResourceKey),
|
||||
new LevelStem(
|
||||
originalServerWorld.dimensionTypeRegistration(),
|
||||
originalServerWorld.getChunkSource().getGenerator()
|
||||
),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
@ -272,20 +174,32 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(@NotNull BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
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<ChunkAccess, ProtoChunk, Level
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>)
|
||||
generatorSettingBaseSupplierField.get(noiseBasedChunkGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
biomeSource = new FixedBiomeSource(
|
||||
DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, @NotNull ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(
|
||||
originalChunkProvider.chunkMap);
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
|
||||
origPositions);
|
||||
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(
|
||||
freshChunkProvider.chunkMap);
|
||||
ringPositionsField.set(newState, copy);
|
||||
hasGeneratedPositionsField.setBoolean(newState, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
this.worldGenContext = new WorldGenContext(freshWorld, chunkGenerator, structureTemplateManager,
|
||||
threadedLevelLightEngine
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -394,7 +221,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
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<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(BIOME), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> {
|
||||
final CraftWorld world = freshWorld.getWorld();
|
||||
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
|
||||
blockPopulator.populate(world, random, chunk);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
|
||||
}
|
||||
|
||||
//util
|
||||
@ -512,83 +299,4 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
@SuppressWarnings("unused") // compatibility with spigot
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public @NotNull List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
worldGenContext,
|
||||
Runnable::run,
|
||||
CompletableFuture::completedFuture,
|
||||
accessibleChunks
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {
|
||||
}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {
|
||||
});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<ChunkAccess> lightChunk(final @NotNull ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<PaperweightUserDependenciesExtension>().paperDevBundle("1.21.1-R0.1-20240811.223934-9")
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.21.1-R0.1-20241012.212042-119")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<net.minecraft.nbt.
|
||||
this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_R1.PaperweightAdapter();
|
||||
}
|
||||
|
||||
public Function<BlockEntity, FaweCompoundTag> blockEntityToCompoundTag() {
|
||||
return blockEntity -> FaweCompoundTag.of(
|
||||
() -> (LinCompoundTag) toNativeLin(blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()))
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
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<net.minecraft.nbt.
|
||||
|
||||
@Override
|
||||
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
||||
throw new UnsupportedOperationException("Regen support for 1.21 not yet implemented.");
|
||||
return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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_21_R1.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<BlockPos, BlockVector3> posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ());
|
||||
private static final Function<BlockEntity, CompoundTag> nmsTile2We = tileEntity -> new PaperweightLazyCompoundTag(
|
||||
Suppliers.memoize(() -> tileEntity.saveWithId(DedicatedServer.getServer().registryAccess()))
|
||||
);
|
||||
public static final Function<BlockEntity, FaweCompoundTag> 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<BlockVector3, CompoundTag> getTiles() {
|
||||
public Map<BlockVector3, FaweCompoundTag> tiles() {
|
||||
Map<BlockPos, BlockEntity> 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<Entity> 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<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> entities() {
|
||||
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||
List<Entity> 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<CompoundTag> iterator() {
|
||||
Iterable<CompoundTag> result = entities.stream().map(input -> {
|
||||
net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
|
||||
public Iterator<FaweCompoundTag> iterator() {
|
||||
Iterable<FaweCompoundTag> 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<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||
List<BlockEntity> 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<Holder<Biome>>) 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<CompoundTag> entities = set.getEntities();
|
||||
Collection<FaweCompoundTag> entities = set.entities();
|
||||
if (entities != null && !entities.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[2];
|
||||
}
|
||||
|
||||
syncTasks[1] = () -> {
|
||||
Iterator<CompoundTag> iterator = entities.iterator();
|
||||
Iterator<FaweCompoundTag> iterator = entities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final CompoundTag nativeTag = iterator.next();
|
||||
final Map<String, Tag<?, ?>> entityTagMap = nativeTag.getValue();
|
||||
final StringTag idTag = (StringTag) entityTagMap.get("Id");
|
||||
final ListTag posTag = (ListTag) entityTagMap.get("Pos");
|
||||
final ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
|
||||
final FaweCompoundTag nativeTag = iterator.next();
|
||||
final LinCompoundTag linTag = nativeTag.linTag();
|
||||
final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
|
||||
final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
|
||||
final LinListTag<LinFloatTag> 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<BlockVector3, CompoundTag> tiles = set.getTiles();
|
||||
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
|
||||
if (tiles != null && !tiles.isEmpty()) {
|
||||
if (syncTasks == null) {
|
||||
syncTasks = new Runnable[1];
|
||||
}
|
||||
|
||||
syncTasks[0] = () -> {
|
||||
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
|
||||
final CompoundTag nativeTag = entry.getValue();
|
||||
for (final Map.Entry<BlockVector3, FaweCompoundTag> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final Map<BlockVector3, FaweCompoundTag> tiles = new HashMap<>();
|
||||
private final Set<FaweCompoundTag> 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<Tag> 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<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
@SuppressWarnings("unchecked")
|
||||
BukkitImplAdapter<Tag> 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<CompoundTag> getEntities() {
|
||||
public Collection<FaweCompoundTag> 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<Holder<Biome>> 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<Biome>) 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<BlockVector3, FaweCompoundTag> tiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FaweCompoundTag tile(final int x, final int y, final int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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<ServerLeve
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z);
|
||||
PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
|
@ -1,175 +1,76 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.StaticCache2D;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.util.ProgressListener;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.chunk.status.ChunkPyramid;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraft.core.registries.Registries.BIOME;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
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<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.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<ChunkAccess, ProtoChunk, Level
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
private WorldGenContext worldGenContext;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
public PaperweightRegen(
|
||||
World originalBukkitWorld,
|
||||
Region region,
|
||||
Extent target,
|
||||
RegenOptions options
|
||||
) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
if (PaperLib.isPaper()) {
|
||||
throw new UnsupportedOperationException("Regeneration currently not support on Paper due to the new generation system");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTasks(final BooleanSupplier shouldKeepTicking) {
|
||||
while (shouldKeepTicking.getAsBoolean()) {
|
||||
if (!this.freshWorld.getChunkSource().pollTask()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
@ -263,8 +154,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
|
||||
.getOrThrow(levelStemResourceKey),
|
||||
new LevelStem(
|
||||
originalServerWorld.dimensionTypeRegistration(),
|
||||
originalServerWorld.getChunkSource().getGenerator()
|
||||
),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
@ -281,20 +174,32 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(@NotNull BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
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<ChunkAccess, ProtoChunk, Level
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>)
|
||||
generatorSettingBaseSupplierField.get(noiseBasedChunkGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
biomeSource = new FixedBiomeSource(
|
||||
DedicatedServer.getServer().registryAccess()
|
||||
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, @NotNull ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(
|
||||
originalChunkProvider.chunkMap);
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
|
||||
origPositions);
|
||||
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(
|
||||
freshChunkProvider.chunkMap);
|
||||
ringPositionsField.set(newState, copy);
|
||||
hasGeneratedPositionsField.setBoolean(newState, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
this.worldGenContext = new WorldGenContext(
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureTemplateManager,
|
||||
threadedLevelLightEngine,
|
||||
originalChunkProvider.chunkMap.worldGenContext.mainThreadMailBox()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -407,7 +221,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
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<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(BIOME), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> {
|
||||
final CraftWorld world = freshWorld.getWorld();
|
||||
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
|
||||
blockPopulator.populate(world, random, chunk);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld()));
|
||||
}
|
||||
|
||||
//util
|
||||
@ -525,99 +299,4 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
@SuppressWarnings("unused") // compatibility with spigot
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public @NotNull List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FULL).getAccumulatedRadiusOf(chunkStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
||||
ChunkAccess chunkAccess = accessibleChunks.get(accessibleChunks.size() / 2);
|
||||
int chunkX = chunkAccess.getPos().x;
|
||||
int chunkZ = chunkAccess.getPos().z;
|
||||
getProtoChunkAt(chunkX, chunkZ);
|
||||
StaticCache2D<GenerationChunkHolder> neighbours = StaticCache2D
|
||||
.create(
|
||||
chunkX,
|
||||
chunkZ,
|
||||
requiredNeighborChunkRadius(),
|
||||
(final int nx, final int nz) -> new ChunkHolder(new ChunkPos(nx, nz),
|
||||
ChunkHolderManager.MAX_TICKET_LEVEL,
|
||||
freshWorld,
|
||||
threadedLevelLightEngine,
|
||||
null,
|
||||
freshChunkProvider.chunkMap
|
||||
)
|
||||
);
|
||||
return ChunkPyramid.GENERATION_PYRAMID.getStepTo(chunkStatus).apply(
|
||||
worldGenContext,
|
||||
neighbours,
|
||||
chunkAccess
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {
|
||||
}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {
|
||||
});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<ChunkAccess> lightChunk(final @NotNull ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
package com.fastasyncworldedit.bukkit;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.bukkit.util.WorldUnloadedException;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class FaweBukkitWorld extends BukkitWorld {
|
||||
|
||||
private static final Map<World, FaweBukkitWorld> CACHE = Collections.synchronizedMap(new WeakHashMap<>());
|
||||
|
||||
private final ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> SENDING_CHUNKS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
*
|
||||
* @param world the world
|
||||
*/
|
||||
private FaweBukkitWorld(final World world) {
|
||||
super(world);
|
||||
}
|
||||
|
||||
public static FaweBukkitWorld of(World world) {
|
||||
return CACHE.compute(world, (__, val) -> {
|
||||
if (val == null) {
|
||||
return new FaweBukkitWorld(world);
|
||||
}
|
||||
val.updateReference();
|
||||
return val;
|
||||
});
|
||||
}
|
||||
|
||||
public static FaweBukkitWorld of(String worldName) {
|
||||
World world = Bukkit.getWorld(worldName);
|
||||
if (world == null) {
|
||||
throw new UnsupportedOperationException("Unable to find org.bukkit.World instance for " + worldName + ". Is it loaded?");
|
||||
}
|
||||
return of(world);
|
||||
}
|
||||
|
||||
public static ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> getWorldSendingChunksMap(FaweBukkitWorld world) {
|
||||
return world.SENDING_CHUNKS;
|
||||
}
|
||||
|
||||
public static ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> getWorldSendingChunksMap(String worldName) {
|
||||
return of(worldName).SENDING_CHUNKS;
|
||||
}
|
||||
|
||||
private void updateReference() {
|
||||
World world = getWorld();
|
||||
World bukkitWorld = Bukkit.getWorld(worldNameRef);
|
||||
if (bukkitWorld == null) {
|
||||
throw new WorldUnloadedException(worldNameRef);
|
||||
} else if (bukkitWorld != world) {
|
||||
worldRef = new WeakReference<>(bukkitWorld);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* Acquires a (FAWE-implemented only) write-lock on the chunk packet lock, waiting if required before writing, then freeing
|
||||
* the lock. Also sets a boolean to indicate a write is waiting and therefore reads should not occur.
|
||||
* <p>
|
||||
* Utilises ConcurrentHashMap#compute for easy synchronisation for all of the above. Only tryWriteLock is used in blocks
|
||||
* synchronised using ConcurrentHashMap methods.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
protected static <LevelChunkSection> boolean setSectionAtomic(
|
||||
String worldName,
|
||||
IntPair pair,
|
||||
LevelChunkSection[] sections,
|
||||
LevelChunkSection expected,
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
if (layer < 0 || layer >= sections.length) {
|
||||
return false;
|
||||
}
|
||||
StampLockHolder holder = new StampLockHolder();
|
||||
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
|
||||
chunks.compute(pair, (k, lock) -> {
|
||||
if (lock == null) {
|
||||
lock = new ChunkSendLock();
|
||||
} else if (lock.writeWaiting) {
|
||||
throw new IllegalStateException("Attempting to write chunk section when write is already ongoing?!");
|
||||
}
|
||||
holder.stamp = lock.lock.tryWriteLock();
|
||||
holder.chunkLock = lock;
|
||||
lock.writeWaiting = true;
|
||||
return lock;
|
||||
});
|
||||
try {
|
||||
if (holder.stamp == 0) {
|
||||
holder.stamp = holder.chunkLock.lock.writeLock();
|
||||
}
|
||||
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
|
||||
} finally {
|
||||
chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
|
||||
chunks.computeIfPresent(pair, (k, lock) -> {
|
||||
if (lock != holder.chunkLock) {
|
||||
throw new IllegalStateException("SENDING_CHUNKS stored lock does not equal lock attempted to be unlocked?!");
|
||||
}
|
||||
lock.lock.unlockWrite(holder.stamp);
|
||||
lock.writeWaiting = false;
|
||||
// Keep the lock, etc. in the map as we're going to be accessing again later when sending
|
||||
return lock;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before sending a chunk packet, filling the given stamp and stampedLock arrays' zeroth indices if the chunk packet
|
||||
* send should go ahead.
|
||||
* <p>
|
||||
* Chunk packets should be sent if both of the following are met:
|
||||
* - There is no more than one current packet send ongoing
|
||||
* - There is no chunk section "write" waiting or ongoing,
|
||||
* which are determined by the number of readers currently locking the StampedLock (i.e. the number of sends), if the
|
||||
* stamped lock is currently write-locked and if the boolean for waiting write is true.
|
||||
* <p>
|
||||
* Utilises ConcurrentHashMap#compute for easy synchronisation
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
protected static void beginChunkPacketSend(String worldName, IntPair pair, StampLockHolder stampedLock) {
|
||||
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
|
||||
chunks.compute(pair, (k, lock) -> {
|
||||
if (lock == null) {
|
||||
lock = new ChunkSendLock();
|
||||
}
|
||||
// Allow twice-read-locking, so if the packets have been created but not sent, we can queue another read
|
||||
if (lock.writeWaiting || lock.lock.getReadLockCount() > 1 || lock.lock.isWriteLocked()) {
|
||||
return lock;
|
||||
}
|
||||
stampedLock.stamp = lock.lock.readLock();
|
||||
stampedLock.chunkLock = lock;
|
||||
return lock;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the read lock acquired when sending a chunk packet for a chunk
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
protected static void endChunkPacketSend(String worldName, IntPair pair, StampLockHolder lockHolder) {
|
||||
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
|
||||
chunks.computeIfPresent(pair, (k, lock) -> {
|
||||
if (lock.lock != lockHolder.chunkLock.lock) {
|
||||
throw new IllegalStateException("SENDING_CHUNKS stored lock does not equal lock attempted to be unlocked?!");
|
||||
}
|
||||
lock.lock.unlockRead(lockHolder.stamp);
|
||||
// Do not continue to store the lock if we may not need it (i.e. chunk has been sent, may not be sent again)
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static final class StampLockHolder {
|
||||
public long stamp;
|
||||
public ChunkSendLock chunkLock = null;
|
||||
}
|
||||
|
||||
public static final class ChunkSendLock {
|
||||
|
||||
public final StampedLock lock = new StampedLock();
|
||||
public boolean writeWaiting = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 <IChunkAccess> the type of the {@code IChunkAccess} of the current Minecraft implementation
|
||||
* @param <ProtoChunk> the type of the {@code ProtoChunk} of the current Minecraft implementation
|
||||
* @param <Chunk> the type of the {@code Chunk} of the current Minecraft implementation
|
||||
* @param <ChunkStatus> the type of the {@code ChunkStatusWrapper} wrapping the {@code ChunkStatus} enum
|
||||
*/
|
||||
public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess, Chunk extends IChunkAccess, ChunkStatus extends Regenerator.ChunkStatusWrapper<IChunkAccess>> {
|
||||
|
||||
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<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
protected final RegenOptions options;
|
||||
|
||||
//runtime
|
||||
protected final LinkedHashMap<ChunkStatus, Concurrency> chunkStatuses = new LinkedHashMap<>(); // TODO (j21): use SequencedMap
|
||||
private final Long2ObjectLinkedOpenHashMap<ProtoChunk> protoChunks = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
|
||||
protected boolean generateConcurrent = true;
|
||||
protected long seed;
|
||||
private ExecutorService executor;
|
||||
private SingleThreadQueueExtent source;
|
||||
protected SingleThreadQueueExtent source;
|
||||
|
||||
/**
|
||||
* Initializes an abstract regeneration handler.
|
||||
@ -92,15 +52,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
private static Random getChunkRandom(long worldSeed, int x, int z) {
|
||||
Random random = new Random();
|
||||
random.setSeed(worldSeed);
|
||||
long xRand = random.nextLong() / 2L * 2L + 1L;
|
||||
long zRand = random.nextLong() / 2L * 2L + 1L;
|
||||
random.setSeed((long) x * xRand + (long) z * zRand ^ worldSeed);
|
||||
return random;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerates the selected {@code Region}.
|
||||
*
|
||||
@ -122,16 +73,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!generate()) {
|
||||
cleanup0();
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
cleanup0();
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
copyToWorld();
|
||||
} catch (Exception e) {
|
||||
@ -144,193 +85,26 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code ProtoChunk} at the given chunk coordinates.
|
||||
*
|
||||
* @param x the chunk x coordinate
|
||||
* @param z the chunk z coordinate
|
||||
* @return the {@code ProtoChunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been initialized yet.
|
||||
* Execute tasks on the main thread during regen.
|
||||
*/
|
||||
protected ProtoChunk getProtoChunkAt(int x, int z) {
|
||||
return protoChunks.get(MathMan.pairInt(x, z));
|
||||
}
|
||||
protected abstract void runTasks(BooleanSupplier shouldKeepTicking);
|
||||
|
||||
/**
|
||||
* Returns the {@code Chunk} at the given chunk coordinates.
|
||||
*
|
||||
* @param x the chunk x coordinate
|
||||
* @param z the chunk z coordinate
|
||||
* @return the {@code Chunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been converted yet.
|
||||
*/
|
||||
protected Chunk getChunkAt(int x, int z) {
|
||||
return chunks.get(MathMan.pairInt(x, z));
|
||||
}
|
||||
|
||||
private boolean generate() throws Exception {
|
||||
ThreadFactory factory = new ThreadFactoryBuilder()
|
||||
.setNameFormat("FAWE Regenerator - %d")
|
||||
.build();
|
||||
if (generateConcurrent) {
|
||||
//Using concurrent chunk generation
|
||||
executor = Executors.newFixedThreadPool(Settings.settings().QUEUE.PARALLEL_THREADS, factory);
|
||||
} else { // else using sequential chunk generation, concurrent not supported
|
||||
executor = Executors.newSingleThreadExecutor(factory);
|
||||
}
|
||||
|
||||
//TODO: can we get that required radius down without affecting chunk generation (e.g. strucures, features, ...)?
|
||||
//for now it is working well and fast, if we are bored in the future we could do the research (a lot of it) to reduce the border radius
|
||||
|
||||
// to get the chunks we need to generate in the nth chunk status, we need to know how many chunks
|
||||
// we need to generate in the n + 1 th chunk status. Summing up the margin solves that
|
||||
LinkedHashMap<ChunkStatus, long[]> chunkCoordsForChunkStatus = new LinkedHashMap<>();
|
||||
int borderSum = 1;
|
||||
// TODO (j21): use SequencedMap#sequencedKeySet().reversed()
|
||||
final List<ChunkStatus> reversedKeys = Lists.reverse(new ArrayList<>(chunkStatuses.keySet()));
|
||||
for (final ChunkStatus status : reversedKeys) {
|
||||
chunkCoordsForChunkStatus.put(status, getChunkCoordsRegen(region, borderSum));
|
||||
borderSum += status.requiredNeighborChunkRadius();
|
||||
}
|
||||
|
||||
//create chunks
|
||||
// TODO (j21): use SequencedMap#firstEntry().getKey()
|
||||
for (long xz : chunkCoordsForChunkStatus.get(chunkStatuses.keySet().iterator().next())) {
|
||||
ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz));
|
||||
protoChunks.put(xz, chunk);
|
||||
}
|
||||
|
||||
// a memory-efficient, lightweight "list" that calculates index -> ChunkAccess
|
||||
// as needed when accessed
|
||||
class LazyChunkList extends AbstractList<IChunkAccess> {
|
||||
private final int size;
|
||||
private final int minX;
|
||||
private final int minZ;
|
||||
private final int sizeSqrt;
|
||||
|
||||
LazyChunkList(int radius, int centerX, int centerZ) {
|
||||
this.sizeSqrt = radius + 1 + radius; // length of one side
|
||||
this.size = this.sizeSqrt * this.sizeSqrt;
|
||||
this.minX = centerX - radius;
|
||||
this.minZ = centerZ - radius;
|
||||
}
|
||||
@Override
|
||||
public IChunkAccess get(final int index) {
|
||||
Objects.checkIndex(index, size);
|
||||
int absX = (index % sizeSqrt) + minX;
|
||||
int absZ = (index / sizeSqrt) + minZ;
|
||||
return protoChunks.get(MathMan.pairInt(absX, absZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
@Label("Regeneration")
|
||||
@Category("FAWE")
|
||||
@Name("fawe.regen")
|
||||
class RegenerationEvent extends Event {
|
||||
private String chunkStatus;
|
||||
private int chunksToProcess;
|
||||
}
|
||||
|
||||
//run generation tasks excluding FULL chunk status
|
||||
for (Map.Entry<ChunkStatus, Concurrency> entry : chunkStatuses.entrySet()) {
|
||||
ChunkStatus chunkStatus = entry.getKey();
|
||||
final RegenerationEvent event = new RegenerationEvent();
|
||||
event.begin();
|
||||
event.chunkStatus = chunkStatus.name();
|
||||
int radius = Math.max(1, chunkStatus.requiredNeighborChunkRadius0());
|
||||
|
||||
long[] coords = chunkCoordsForChunkStatus.get(chunkStatus);
|
||||
event.chunksToProcess = coords.length;
|
||||
if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) {
|
||||
SequentialTasks<ConcurrentTasks<LongList>> tasks = getChunkStatusTaskRows(coords, radius);
|
||||
for (ConcurrentTasks<LongList> para : tasks) {
|
||||
List<Runnable> scheduled = new ArrayList<>(tasks.size());
|
||||
for (LongList row : para) {
|
||||
scheduled.add(() -> {
|
||||
for (long xz : row) {
|
||||
chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz),
|
||||
MathMan.unpairIntY(xz)));
|
||||
}
|
||||
});
|
||||
}
|
||||
runAndWait(scheduled);
|
||||
}
|
||||
} else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) {
|
||||
// every chunk can be processed individually
|
||||
List<Runnable> scheduled = new ArrayList<>(coords.length);
|
||||
for (long xz : coords) {
|
||||
scheduled.add(() -> chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz),
|
||||
MathMan.unpairIntY(xz))));
|
||||
}
|
||||
runAndWait(scheduled);
|
||||
} else { // Concurrency.NONE or generateConcurrent == false
|
||||
// run sequential but submit to different thread
|
||||
// running regen on the main thread otherwise triggers async-only events on the main thread
|
||||
executor.submit(() -> {
|
||||
for (long xz : coords) {
|
||||
chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz),
|
||||
MathMan.unpairIntY(xz)));
|
||||
}
|
||||
}).get(); // wait until finished this step
|
||||
}
|
||||
event.commit();
|
||||
}
|
||||
|
||||
//convert to proper chunks
|
||||
// TODO (j21): use SequencedMap#firstEntry().getValue()
|
||||
for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) {
|
||||
ProtoChunk proto = protoChunks.get(xz);
|
||||
chunks.put(xz, createChunk(proto));
|
||||
}
|
||||
|
||||
//final chunkstatus
|
||||
ChunkStatus FULL = getFullChunkStatus();
|
||||
// TODO (j21): use SequencedMap#firstEntry().getValue()
|
||||
for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) { //FULL.requiredNeighbourChunkRadius() == 0!
|
||||
Chunk chunk = chunks.get(xz);
|
||||
FULL.processChunkSave(xz, List.of(chunk));
|
||||
}
|
||||
|
||||
//populate
|
||||
List<BlockPopulator> populators = getBlockPopulators();
|
||||
// TODO (j21): use SequencedMap#firstEntry().getValue()
|
||||
for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) {
|
||||
int x = MathMan.unpairIntX(xz);
|
||||
int z = MathMan.unpairIntY(xz);
|
||||
|
||||
//prepare chunk seed
|
||||
Random random = getChunkRandom(seed, x, z);
|
||||
|
||||
//actually populate
|
||||
Chunk c = chunks.get(xz);
|
||||
populators.forEach(pop -> {
|
||||
populate(c, random, pop);
|
||||
});
|
||||
}
|
||||
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<Runnable> tasks) {
|
||||
try {
|
||||
List<Future<?>> futures = new ArrayList<>();
|
||||
tasks.forEach(task -> futures.add(executor.submit(task)));
|
||||
for (Future<?> future : futures) {
|
||||
future.get();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.catching(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyToWorld() {
|
||||
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<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
} else if (genbiomes) {
|
||||
target.setBlocks(region, new WithBiomePlacementPattern(vec -> source.getBiome(vec)));
|
||||
}
|
||||
TaskManager.taskManager().cancel(taskId);
|
||||
}
|
||||
|
||||
private class PlacementPattern implements Pattern {
|
||||
@ -382,9 +157,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
|
||||
//functions to be implemented by sub class
|
||||
private void cleanup0() {
|
||||
if (executor != null) {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@ -416,47 +188,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
*/
|
||||
protected abstract void cleanup();
|
||||
|
||||
/**
|
||||
* Implement the initialization of a {@code ProtoChunk} here.
|
||||
*
|
||||
* @param x the x coorinate of the {@code ProtoChunk} to create
|
||||
* @param z the z coorinate of the {@code ProtoChunk} to create
|
||||
* @return an initialized {@code ProtoChunk}
|
||||
*/
|
||||
protected abstract ProtoChunk createProtoChunk(int x, int z);
|
||||
|
||||
/**
|
||||
* Implement the convertion of a {@code ProtoChunk} to a {@code Chunk} here.
|
||||
*
|
||||
* @param protoChunk the {@code ProtoChunk} to be converted to a {@code Chunk}
|
||||
* @return the converted {@code Chunk}
|
||||
*/
|
||||
protected abstract Chunk createChunk(ProtoChunk protoChunk);
|
||||
|
||||
/**
|
||||
* Return the {@code ChunkStatus.FULL} here.
|
||||
* ChunkStatus.FULL is the last step of vanilla chunk generation.
|
||||
*
|
||||
* @return {@code ChunkStatus.FULL}
|
||||
*/
|
||||
protected abstract ChunkStatus getFullChunkStatus();
|
||||
|
||||
/**
|
||||
* Return a list of {@code BlockPopulator} used to populate the original world here.
|
||||
*
|
||||
* @return {@code ChunkStatus.FULL}
|
||||
*/
|
||||
protected abstract List<BlockPopulator> getBlockPopulators();
|
||||
|
||||
/**
|
||||
* Implement the population of the {@code Chunk} with the given chunk random and {@code BlockPopulator} here.
|
||||
*
|
||||
* @param chunk the {@code Chunk} to populate
|
||||
* @param random the chunk random to use for population
|
||||
* @param pop the {@code BlockPopulator} to use
|
||||
*/
|
||||
protected abstract void populate(Chunk chunk, Random random, BlockPopulator pop);
|
||||
|
||||
/**
|
||||
* Implement the initialization an {@code IChunkCache<IChunkGet>} here. Use will need the {@code getChunkAt} function
|
||||
*
|
||||
@ -464,106 +195,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
*/
|
||||
protected abstract IChunkCache<IChunkGet> initSourceQueueCache();
|
||||
|
||||
//algorithms
|
||||
private long[] getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks
|
||||
BlockVector3 oldMin = region.getMinimumPoint();
|
||||
BlockVector3 newMin = BlockVector3.at(
|
||||
(oldMin.x() >> 4 << 4) - border * 16,
|
||||
oldMin.y(),
|
||||
(oldMin.z() >> 4 << 4) - border * 16
|
||||
);
|
||||
BlockVector3 oldMax = region.getMaximumPoint();
|
||||
BlockVector3 newMax = BlockVector3.at(
|
||||
(oldMax.x() >> 4 << 4) + (border + 1) * 16 - 1,
|
||||
oldMax.y(),
|
||||
(oldMax.z() >> 4 << 4) + (border + 1) * 16 - 1
|
||||
);
|
||||
Region adjustedRegion = new CuboidRegion(newMin, newMax);
|
||||
return adjustedRegion.getChunks().stream()
|
||||
.sorted(Comparator
|
||||
.comparingInt(BlockVector2::z)
|
||||
.thenComparingInt(BlockVector2::x)) //needed for RegionLimitedWorldAccess
|
||||
.mapToLong(c -> MathMan.pairInt(c.x(), c.z()))
|
||||
.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of chunkcoord rows that may be executed concurrently
|
||||
*
|
||||
* @param allCoords the coords that should be sorted into rows, must be sorted by z and x
|
||||
* @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to concurrently (ChunkStatus
|
||||
* .requiredNeighborRadius)
|
||||
* @return a list of chunkcoords rows that may be executed concurrently
|
||||
*/
|
||||
private SequentialTasks<ConcurrentTasks<LongList>> getChunkStatusTaskRows(
|
||||
long[] allCoords,
|
||||
int requiredNeighborChunkRadius
|
||||
) {
|
||||
int requiredNeighbors = Math.max(0, requiredNeighborChunkRadius);
|
||||
|
||||
final int coordsCount = allCoords.length;
|
||||
long first = coordsCount == 0 ? 0 : allCoords[0];
|
||||
long last = coordsCount == 0 ? 0 : allCoords[coordsCount - 1];
|
||||
int minX = MathMan.unpairIntX(first);
|
||||
int maxX = MathMan.unpairIntX(last);
|
||||
int minZ = MathMan.unpairIntY(first);
|
||||
int maxZ = MathMan.unpairIntY(last);
|
||||
SequentialTasks<ConcurrentTasks<LongList>> tasks;
|
||||
if (maxZ - minZ > maxX - minX) {
|
||||
int numlists = Math.min(requiredNeighbors * 2 + 1, maxX - minX + 1);
|
||||
|
||||
Int2ObjectOpenHashMap<LongList> byX = new Int2ObjectOpenHashMap<>();
|
||||
int expectedListLength = (coordsCount + 1) / (maxX - minX);
|
||||
|
||||
//init lists
|
||||
for (int i = minX; i <= maxX; i++) {
|
||||
byX.put(i, new LongArrayList(expectedListLength));
|
||||
}
|
||||
|
||||
//sort into lists by x coord
|
||||
for (long allCoord : allCoords) {
|
||||
byX.get(MathMan.unpairIntX(allCoord)).add(allCoord);
|
||||
}
|
||||
|
||||
//create parallel tasks
|
||||
tasks = new SequentialTasks<>(numlists);
|
||||
for (int offset = 0; offset < numlists; offset++) {
|
||||
ConcurrentTasks<LongList> para = new ConcurrentTasks<>((maxZ - minZ + 1) / numlists + 1);
|
||||
for (int i = 0; minX + i * numlists + offset <= maxX; i++) {
|
||||
para.add(byX.get(minX + i * numlists + offset));
|
||||
}
|
||||
tasks.add(para);
|
||||
}
|
||||
} else {
|
||||
int numlists = Math.min(requiredNeighbors * 2 + 1, maxZ - minZ + 1);
|
||||
|
||||
Int2ObjectOpenHashMap<LongList> byZ = new Int2ObjectOpenHashMap<>();
|
||||
int expectedListLength = (coordsCount + 1) / (maxZ - minZ + 2);
|
||||
|
||||
//init lists
|
||||
for (int i = minZ; i <= maxZ; i++) {
|
||||
byZ.put(i, new LongArrayList(expectedListLength));
|
||||
}
|
||||
|
||||
//sort into lists by x coord
|
||||
for (long allCoord : allCoords) {
|
||||
byZ.get(MathMan.unpairIntY(allCoord)).add(allCoord);
|
||||
}
|
||||
|
||||
//create parallel tasks
|
||||
tasks = new SequentialTasks<>(numlists);
|
||||
for (int offset = 0; offset < numlists; offset++) {
|
||||
ConcurrentTasks<LongList> para = new ConcurrentTasks<>((maxX - minX + 1) / numlists + 1);
|
||||
for (int i = 0; minZ + i * numlists + offset <= maxZ; i++) {
|
||||
para.add(byZ.get(minZ + i * numlists + offset));
|
||||
}
|
||||
tasks.add(para);
|
||||
}
|
||||
}
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
protected BiomeProvider getBiomeProvider() {
|
||||
if (options.hasBiomeType()) {
|
||||
return new SingleBiomeProvider();
|
||||
@ -579,103 +210,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
NONE
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to wrap the ChunkStatus of the current Minecraft implementation and as the implementation to execute a chunk generation step.
|
||||
*
|
||||
* @param <IChunkAccess> the IChunkAccess class of the current Minecraft implementation
|
||||
*/
|
||||
public static abstract class ChunkStatusWrapper<IChunkAccess> {
|
||||
|
||||
/**
|
||||
* Return the required neighbor chunk radius the wrapped {@code ChunkStatus} requires.
|
||||
*
|
||||
* @return the radius of required neighbor chunks
|
||||
*/
|
||||
public abstract int requiredNeighborChunkRadius();
|
||||
|
||||
int requiredNeighborChunkRadius0() {
|
||||
return Math.max(0, requiredNeighborChunkRadius());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the wrapped {@code ChunkStatus}.
|
||||
*
|
||||
* @return the radius of required neighbor chunks
|
||||
*/
|
||||
public abstract String name();
|
||||
|
||||
/**
|
||||
* Return the name of the wrapped {@code ChunkStatus}.
|
||||
*
|
||||
* @param accessibleChunks a list of chunks that will be used during the execution of the wrapped {@code ChunkStatus}.
|
||||
* This list is order in the correct order required by the {@code ChunkStatus}, unless Mojang suddenly decides to do things differently.
|
||||
*/
|
||||
public abstract CompletableFuture<?> processChunk(List<IChunkAccess> accessibleChunks);
|
||||
|
||||
void processChunkSave(long xz, List<IChunkAccess> accessibleChunks) {
|
||||
try {
|
||||
processChunk(accessibleChunks).get();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error while running {} on chunk {}/{}",
|
||||
name(), MathMan.unpairIntX(xz), MathMan.unpairIntY(xz), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SequentialTasks<T> extends Tasks<T> {
|
||||
|
||||
public SequentialTasks(int expectedSize) {
|
||||
super(expectedSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ConcurrentTasks<T> extends Tasks<T> {
|
||||
|
||||
public ConcurrentTasks(int expectedSize) {
|
||||
super(expectedSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Tasks<T> implements Iterable<T> {
|
||||
|
||||
private final List<T> tasks;
|
||||
|
||||
public Tasks(int expectedSize) {
|
||||
tasks = new ArrayList<>(expectedSize);
|
||||
}
|
||||
|
||||
public void add(T task) {
|
||||
tasks.add(task);
|
||||
}
|
||||
|
||||
public List<T> list() {
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return tasks.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return tasks.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return tasks.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SingleBiomeProvider extends BiomeProvider {
|
||||
|
||||
private final org.bukkit.block.Biome biome = BukkitAdapter.adapt(options.getBiomeType());
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<World> worldRef;
|
||||
protected WeakReference<World> 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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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"})
|
||||
|
@ -19,9 +19,9 @@ public class AdjacentMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public class AngleMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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) {
|
||||
|
@ -18,9 +18,9 @@ public class BesideMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(final String argumentInput, final int index) {
|
||||
protected Stream<String> getSuggestions(final String argumentInput, final int index, ParserContext context) {
|
||||
if (index == 0) {
|
||||
return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream();
|
||||
return worldEdit.getMaskFactory().getSuggestions(argumentInput, context).stream();
|
||||
} else if (index == 1 || index == 2) {
|
||||
return SuggestionHelper.suggestPositiveDoubles(argumentInput);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public class ExtremaMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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) {
|
||||
|
@ -22,7 +22,7 @@ public class ROCAngleMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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) {
|
||||
|
@ -18,7 +18,7 @@ public class RadiusMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index == 0 || index == 1) {
|
||||
return SuggestionHelper.suggestPositiveIntegers(argumentInput);
|
||||
}
|
||||
|
@ -24,12 +24,12 @@ public class RichOffsetMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public class SimplexMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index < 3) {
|
||||
return SuggestionHelper.suggestPositiveDoubles(argumentInput);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public class SurfaceAngleMaskParser extends RichParser<Mask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index <= 2) {
|
||||
return SuggestionHelper.suggestPositiveDoubles(argumentInput);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class AngleColorPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index != 0) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class AverageColorPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index > 4) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public class BiomePatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index == 0) {
|
||||
return BiomeType.REGISTRY.getSuggestions(argumentInput);
|
||||
}
|
||||
|
@ -26,9 +26,9 @@ public class BufferedPattern2DParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -26,9 +26,9 @@ public class BufferedPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public class ColorPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index > 4) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class DesaturatePatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index == 0) {
|
||||
return SuggestionHelper.suggestPositiveDoubles(argumentInput);
|
||||
}
|
||||
|
@ -28,9 +28,9 @@ public class Linear2DPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
};
|
||||
|
@ -28,9 +28,9 @@ public class Linear3DPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
};
|
||||
|
@ -28,9 +28,9 @@ public class LinearPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
};
|
||||
|
@ -25,10 +25,10 @@ public class MaskedPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
};
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ public class NoXPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> 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();
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ public class NoYPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> 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();
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ public class NoZPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> 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();
|
||||
}
|
||||
|
@ -36,12 +36,12 @@ public abstract class NoisePatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -25,9 +25,9 @@ public class OffsetPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
};
|
||||
|
@ -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<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
if (argumentInput.equals("#") || argumentInput.equals("#c")) {
|
||||
|
@ -25,9 +25,9 @@ public class RandomOffsetPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
};
|
||||
|
@ -24,9 +24,9 @@ public class RelativePatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> 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();
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class SaturatePatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index > 3) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
@ -25,9 +25,9 @@ public class SolidRandomOffsetPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
};
|
||||
|
@ -25,9 +25,9 @@ public class SurfaceRandomOffsetPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
};
|
||||
|
@ -28,7 +28,7 @@ public class TypeSwapPatternParser extends RichParser<Pattern> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
public Stream<String> getSuggestions(String argumentInput, int index, ParserContext context) {
|
||||
if (index > 2) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ public class Linear3DTransformParser extends RichParser<ResettableExtent> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ public class LinearTransformParser extends RichParser<ResettableExtent> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -26,11 +26,11 @@ public class OffsetTransformParser extends RichParser<ResettableExtent> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -24,11 +24,11 @@ public class PatternTransformParser extends RichParser<ResettableExtent> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -25,12 +25,12 @@ public class RotateTransformParser extends RichParser<ResettableExtent> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -24,11 +24,11 @@ public class ScaleTransformParser extends RichParser<ResettableExtent> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -24,11 +24,11 @@ public class SpreadTransformParser extends RichParser<ResettableExtent> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<String> getSuggestions(String argumentInput, int index) {
|
||||
protected Stream<String> 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();
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.fastasyncworldedit.core.extension.platform.binding;
|
||||
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
|
||||
public record EditSessionHolder(EditSession session) {
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Component> 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<Component> onErrorMessage) {
|
||||
this(extent, limit, onErrorMessage, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param extent the extent
|
||||
* @param limit the limit
|
||||
* @param onErrorMessage consumer to handle a component generated by exceptions
|
||||
* @param processing if this limit extent is expected to be processing
|
||||
* @since TODO
|
||||
*/
|
||||
public LimitExtent(
|
||||
Extent extent,
|
||||
FaweLimit limit,
|
||||
Consumer<Component> onErrorMessage,
|
||||
boolean processing
|
||||
) {
|
||||
super(extent);
|
||||
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<? extends Entity> 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<? extends Entity> 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<ClipboardHolder> clipboards, int rarity, boolean rotate) throws
|
||||
WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getVolume());
|
||||
limit.THROW_MAX_CHANGES(region.getVolume());
|
||||
super.addSchems(region, mask, clipboards, rarity, rotate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getVolume());
|
||||
limit.THROW_MAX_CHANGES(region.getVolume());
|
||||
super.spawnResource(region, gen, rarity, frequency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws
|
||||
WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getVolume());
|
||||
limit.THROW_MAX_CHANGES(region.getVolume());
|
||||
super.addOre(region, mask, material, size, frequency, rarity, minY, maxY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOres(Region region, Mask mask) throws WorldEditException {
|
||||
limit.THROW_MAX_CHECKS(region.getVolume());
|
||||
limit.THROW_MAX_CHANGES(region.getVolume());
|
||||
super.addOres(region, mask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Countable<BlockType>> getBlockDistribution(Region region) {
|
||||
limit.THROW_MAX_CHECKS(region.getVolume());
|
||||
return super.getBlockDistribution(region);
|
||||
return extent.getBlockDistribution(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Countable<BlockState>> getBlockDistributionWithData(Region region) {
|
||||
limit.THROW_MAX_CHECKS(region.getVolume());
|
||||
return super.getBlockDistributionWithData(region);
|
||||
return extent.getBlockDistributionWithData(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countBlocks(Region region, Set<BaseBlock> 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 <B extends BlockStateHolder<B>> 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<BaseBlock> 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<BlockVector3> vset, Pattern pattern) {
|
||||
limit.THROW_MAX_CHANGES(vset.size());
|
||||
return super.setBlocks(vset, pattern);
|
||||
return extent.setBlocks(vset, pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Filter> 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<Extent>(this).setNext(child);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<String, Tag<?, ?>> 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 extends NbtValued> T stripEntityNBT(T entity) {
|
||||
if (!entity.hasNbtData()) {
|
||||
LinCompoundTag nbt = entity.getNbt();
|
||||
if (nbt == null) {
|
||||
return entity;
|
||||
}
|
||||
CompoundTag nbt = entity.getNbtData();
|
||||
Map<String, Tag<?, ?>> 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<BlockVector3, CompoundTag> tiles = set.getTiles();
|
||||
Set<CompoundTag> entities = set.getEntities();
|
||||
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
|
||||
Collection<FaweCompoundTag> entities = set.entities();
|
||||
if (tiles.isEmpty() && entities.isEmpty()) {
|
||||
return set;
|
||||
}
|
||||
boolean isBv3ChunkMap = tiles instanceof BlockVector3ChunkMap;
|
||||
for (final Map.Entry<BlockVector3, CompoundTag> entry : tiles.entrySet()) {
|
||||
ImmutableMap.Builder<String, Tag<?, ?>> 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<CompoundTag> stripped = new HashSet<>();
|
||||
Iterator<CompoundTag> iterator = entities.iterator();
|
||||
Set<FaweCompoundTag> stripped = new HashSet<>();
|
||||
Iterator<FaweCompoundTag> iterator = entities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
CompoundTag entity = iterator.next();
|
||||
ImmutableMap.Builder<String, Tag<?, ?>> 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) {
|
||||
|
@ -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<String, Tag<?, ?>> values = new HashMap<>(tag.getValue());
|
||||
values.remove("x");
|
||||
|
@ -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<String, LockHolder> LOCK_HOLDER_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private final HashMap<IntTriple, CompoundTag> nbtMap;
|
||||
private final HashMap<IntTriple, FaweCompoundTag> nbtMap;
|
||||
private final File file;
|
||||
private final int headerSize;
|
||||
|
||||
@ -248,12 +253,12 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
||||
try (NBTInputStream nbtIS = new NBTInputStream(MainUtil.getCompressedIS(new ByteBufferInputStream(tmp)))) {
|
||||
Iterator<CompoundTag> 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<CompoundTag> 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<IntTriple, CompoundTag> entry : nbtMap.entrySet()) {
|
||||
for (Map.Entry<IntTriple, FaweCompoundTag> 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<String, Tag<?, ?>> 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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<CompoundTag> getTileEntities();
|
||||
|
||||
@Override
|
||||
|
@ -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 <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) {
|
||||
return setBlock(getIndex(x, y, z), block);
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user